diff --git a/Maple2.File.Generator/Maple2.File.Generator.csproj b/Maple2.File.Generator/Maple2.File.Generator.csproj
index c7c3adc..7884773 100644
--- a/Maple2.File.Generator/Maple2.File.Generator.csproj
+++ b/Maple2.File.Generator/Maple2.File.Generator.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/Maple2.File.IO/Crypto/CryptoManager.cs b/Maple2.File.IO/Crypto/CryptoManager.cs
index c63b62d..e79c0d9 100644
--- a/Maple2.File.IO/Crypto/CryptoManager.cs
+++ b/Maple2.File.IO/Crypto/CryptoManager.cs
@@ -1,144 +1,183 @@
-using System.IO.MemoryMappedFiles;
+using System.IO.Compression;
+using System.IO.MemoryMappedFiles;
using System.Text;
-using Ionic.Zlib;
using Maple2.File.IO.Crypto.Common;
using Maple2.File.IO.Crypto.Keys;
using Maple2.File.IO.Crypto.Stream;
-namespace Maple2.File.IO.Crypto {
- public static class CryptoManager {
- public static byte[] DecryptFileString(IPackStream stream, System.IO.Stream buffer) {
- if (stream.CompressedHeaderSize > 0 && stream.EncodedHeaderSize > 0 &&
- stream.HeaderSize > 0) {
- byte[] src = new byte[stream.EncodedHeaderSize];
-
- if ((ulong) buffer.Read(src, 0, (int) stream.EncodedHeaderSize) ==
- stream.EncodedHeaderSize) {
- return Decrypt(stream.Version, (uint) stream.EncodedHeaderSize,
- (uint) stream.CompressedHeaderSize, Encryption.Aes | Encryption.Zlib, src);
- }
- }
+namespace Maple2.File.IO.Crypto;
+
+public static class CryptoManager {
+ public static byte[] DecryptFileString(IPackStream stream, System.IO.Stream buffer) {
+ if (stream.CompressedHeaderSize > 0 && stream.EncodedHeaderSize > 0 && stream.HeaderSize > 0) {
+ byte[] src = new byte[stream.EncodedHeaderSize];
- throw new Exception("ERROR decrypting file list: the size of the list is invalid.");
+ if ((ulong) buffer.Read(src, 0, (int) stream.EncodedHeaderSize) == stream.EncodedHeaderSize) {
+ return Decrypt(stream.Version, (uint) stream.EncodedHeaderSize, (uint) stream.CompressedHeaderSize, Encryption.Aes | Encryption.Zlib, src);
+ }
}
- public static byte[] DecryptFileTable(IPackStream stream, System.IO.Stream buffer) {
- if (stream.CompressedDataSize > 0 && stream.EncodedDataSize > 0 && stream.DataSize > 0) {
- byte[] src = new byte[stream.EncodedDataSize];
+ throw new Exception("ERROR decrypting file list: the size of the list is invalid.");
+ }
+
+ public static byte[] DecryptFileTable(IPackStream stream, System.IO.Stream buffer) {
+ if (stream.CompressedDataSize > 0 && stream.EncodedDataSize > 0 && stream.DataSize > 0) {
+ byte[] src = new byte[stream.EncodedDataSize];
- if ((ulong) buffer.Read(src, 0, (int) stream.EncodedDataSize) == stream.EncodedDataSize) {
- return Decrypt(stream.Version, (uint) stream.EncodedDataSize,
- (uint) stream.CompressedDataSize, Encryption.Aes | Encryption.Zlib, src);
- }
+ if ((ulong) buffer.Read(src, 0, (int) stream.EncodedDataSize) == stream.EncodedDataSize) {
+ return Decrypt(stream.Version, (uint) stream.EncodedDataSize, (uint) stream.CompressedDataSize, Encryption.Aes | Encryption.Zlib, src);
}
-
- throw new Exception("ERROR decrypting file table: the size of the table is invalid.");
}
- public static byte[] DecryptData(IPackFileHeader pHeader, MemoryMappedFile data) {
- if (pHeader.CompressedFileSize > 0 && pHeader.EncodedFileSize > 0 && pHeader.FileSize > 0) {
- using MemoryMappedViewStream buffer =
- data.CreateViewStream((long) pHeader.Offset, pHeader.EncodedFileSize);
- byte[] src = new byte[pHeader.EncodedFileSize];
+ throw new Exception("ERROR decrypting file table: the size of the table is invalid.");
+ }
+
+ public static byte[] DecryptData(IPackFileHeader pHeader, MemoryMappedFile data) {
+ if (pHeader.CompressedFileSize > 0 && pHeader.EncodedFileSize > 0 && pHeader.FileSize > 0) {
+ using MemoryMappedViewStream buffer = data.CreateViewStream((long) pHeader.Offset, pHeader.EncodedFileSize);
+ byte[] src = new byte[pHeader.EncodedFileSize];
- if (buffer.Read(src, 0, (int) pHeader.EncodedFileSize) == pHeader.EncodedFileSize) {
- return Decrypt(pHeader.Version, pHeader.EncodedFileSize,
- (uint) pHeader.CompressedFileSize, pHeader.BufferFlag, src);
- }
+ if (buffer.Read(src, 0, (int) pHeader.EncodedFileSize) == pHeader.EncodedFileSize) {
+ return Decrypt(pHeader.Version, pHeader.EncodedFileSize, (uint) pHeader.CompressedFileSize, pHeader.BufferFlag, src);
}
+ }
+
+ throw new Exception("ERROR decrypting data file segment: the size of the block is invalid.");
+ }
- throw new Exception("ERROR decrypting data file segment: the size of the block is invalid.");
+ // Decryption Routine: Base64 -> AES -> Zlib
+ private static byte[] Decrypt(PackVersion version, uint size, uint sizeCompressed, Encryption flag, byte[] src) {
+ if (flag.HasFlag(Encryption.Aes)) {
+ // Get the AES Key/IV for transformation
+ CipherKeys.GetKeyAndIV(version, sizeCompressed, out byte[] key, out byte[] iv);
+
+ // Decode the base64 encoded string
+ src = Convert.FromBase64String(Encoding.UTF8.GetString(src));
+
+ // Decrypt the AES encrypted block
+ var pCipher = new AESCipher(key, iv);
+ pCipher.TransformBlock(src, 0, size, src, 0);
+ } else if (flag.HasFlag(Encryption.Xor)) {
+ // Decrypt the XOR encrypted block
+ src = EncryptXor(version, src, size, sizeCompressed);
}
- // Decryption Routine: Base64 -> AES -> Zlib
- private static byte[] Decrypt(PackVersion version, uint size, uint sizeCompressed, Encryption flag,
- byte[] src) {
- if (flag.HasFlag(Encryption.Aes)) {
- // Get the AES Key/IV for transformation
- CipherKeys.GetKeyAndIV(version, sizeCompressed, out byte[] key, out byte[] iv);
-
- // Decode the base64 encoded string
- src = Convert.FromBase64String(Encoding.UTF8.GetString(src));
-
- // Decrypt the AES encrypted block
- var pCipher = new AESCipher(key, iv);
- pCipher.TransformBlock(src, 0, size, src, 0);
- } else if (flag.HasFlag(Encryption.Xor)) {
- // Decrypt the XOR encrypted block
- src = EncryptXor(version, src, size, sizeCompressed);
- }
+ return flag.HasFlag(Encryption.Zlib) ? UncompressBuffer(src) : src;
+ }
- return flag.HasFlag(Encryption.Zlib) ? ZlibStream.UncompressBuffer(src) : src;
+ // Encryption Routine: Zlib -> AES -> Base64
+ public static byte[] Encrypt(PackVersion version, byte[] src, Encryption flag, out uint size, out uint sizeCompressed, out uint sizeEncoded) {
+ byte[] pEncrypted;
+ if (flag.HasFlag(Encryption.Zlib)) {
+ pEncrypted = CompressBuffer(src);
+ } else {
+ pEncrypted = new byte[src.Length];
+ Buffer.BlockCopy(src, 0, pEncrypted, 0, src.Length);
}
- // Encryption Routine: Zlib -> AES -> Base64
- public static byte[] Encrypt(PackVersion version, byte[] src, Encryption flag, out uint size,
- out uint sizeCompressed, out uint sizeEncoded) {
- byte[] pEncrypted;
- if (flag.HasFlag(Encryption.Zlib)) {
- pEncrypted = ZlibStream.CompressBuffer(src);
- } else {
- pEncrypted = new byte[src.Length];
- Buffer.BlockCopy(src, 0, pEncrypted, 0, src.Length);
- }
+ size = (uint) src.Length;
+ sizeCompressed = (uint) pEncrypted.Length;
- size = (uint) src.Length;
- sizeCompressed = (uint) pEncrypted.Length;
+ if (flag.HasFlag(Encryption.Aes)) {
+ // Get the AES Key/IV for transformation
+ CipherKeys.GetKeyAndIV(version, sizeCompressed, out byte[] key, out byte[] iv);
- if (flag.HasFlag(Encryption.Aes)) {
- // Get the AES Key/IV for transformation
- CipherKeys.GetKeyAndIV(version, sizeCompressed, out byte[] key, out byte[] iv);
+ // Perform AES block encryption
+ var pCipher = new AESCipher(key, iv);
+ pCipher.TransformBlock(pEncrypted, 0, size, pEncrypted, 0);
- // Perform AES block encryption
- var pCipher = new AESCipher(key, iv);
- pCipher.TransformBlock(pEncrypted, 0, size, pEncrypted, 0);
+ // Encode the encrypted data into a base64 encoded string
+ pEncrypted = Encoding.UTF8.GetBytes(Convert.ToBase64String(pEncrypted));
+ } else if (flag.HasFlag(Encryption.Xor)) {
+ // Perform XOR block encryption
+ pEncrypted = EncryptXor(version, pEncrypted, size, sizeCompressed);
+ }
- // Encode the encrypted data into a base64 encoded string
- pEncrypted = Encoding.UTF8.GetBytes(Convert.ToBase64String(pEncrypted));
- } else if (flag.HasFlag(Encryption.Xor)) {
- // Perform XOR block encryption
- pEncrypted = EncryptXor(version, pEncrypted, size, sizeCompressed);
- }
+ sizeEncoded = (uint) pEncrypted.Length;
- sizeEncoded = (uint) pEncrypted.Length;
+ return pEncrypted;
+ }
+
+ private static byte[] EncryptXor(PackVersion version, byte[] src, uint size, uint sizeCompressed) {
+ CipherKeys.GetXorKey(version, out byte[] key);
+
+ uint uBlock = size >> 2;
+ uint uBlockOffset = 0;
+ int nKeyOffset = 0;
+
+ if (uBlock != 0) {
+ while (uBlockOffset < uBlock) {
+ uint pBlockData = BitConverter.ToUInt32(src, (int) (4 * uBlockOffset)) ^
+ BitConverter.ToUInt32(key, 4 * nKeyOffset);
+ Buffer.BlockCopy(BitConverter.GetBytes(pBlockData), 0, src, (int) (4 * uBlockOffset),
+ sizeof(uint));
- return pEncrypted;
+ nKeyOffset = ((ushort) nKeyOffset + 1) & 0x1FF;
+ uBlockOffset++;
+ }
}
- private static byte[] EncryptXor(PackVersion version, byte[] src, uint size, uint sizeCompressed) {
- CipherKeys.GetXorKey(version, out byte[] key);
+ uBlock = (size & 3);
+ if (uBlock != 0) {
+ int nStart = (int) (4 * uBlockOffset);
- uint uBlock = size >> 2;
- uint uBlockOffset = 0;
- int nKeyOffset = 0;
+ uBlockOffset = 0;
+ nKeyOffset = 0;
- if (uBlock != 0) {
- while (uBlockOffset < uBlock) {
- uint pBlockData = BitConverter.ToUInt32(src, (int) (4 * uBlockOffset)) ^
- BitConverter.ToUInt32(key, 4 * nKeyOffset);
- Buffer.BlockCopy(BitConverter.GetBytes(pBlockData), 0, src, (int) (4 * uBlockOffset),
- sizeof(uint));
+ while (uBlockOffset < uBlock) {
+ src[nStart + uBlockOffset++] ^= (byte) (key[nKeyOffset]);
- nKeyOffset = ((ushort) nKeyOffset + 1) & 0x1FF;
- uBlockOffset++;
- }
+ nKeyOffset = ((ushort) nKeyOffset + 1) & 0x7FF;
}
+ }
+
+ return src;
+ }
- uBlock = (size & 3);
- if (uBlock != 0) {
- int nStart = (int) (4 * uBlockOffset);
+ private static byte[] UncompressBuffer(byte[] src) {
+ using var compressedStream = new MemoryStream(src, 2, src.Length - 6);
+ using var decompressStream = new DeflateStream(compressedStream, CompressionMode.Decompress);
+ using var resultStream = new MemoryStream();
- uBlockOffset = 0;
- nKeyOffset = 0;
+ decompressStream.CopyTo(resultStream);
+ return resultStream.ToArray();
+ }
- while (uBlockOffset < uBlock) {
- src[nStart + uBlockOffset++] ^= (byte) (key[nKeyOffset]);
+ private static byte[] CompressBuffer(byte[] src) {
+ using var outputStream = new MemoryStream();
- nKeyOffset = ((ushort) nKeyOffset + 1) & 0x7FF;
- }
- }
+ // Add Zlib header bytes
+ outputStream.WriteByte(0x78); // CMF byte (compression method and flags)
+ outputStream.WriteByte(0x9C); // Additional flags byte (default compression)
+
+ using (var compressionStream = new DeflateStream(outputStream, CompressionMode.Compress, true)) {
+ compressionStream.Write(src, 0, src.Length);
+ }
- return src;
+ // Calculate and append Adler-32 checksum
+ uint adler32 = CalculateAdler32(src);
+ byte[] checksumBytes = [
+ (byte) ((adler32 >> 24) & 0xFF),
+ (byte) ((adler32 >> 16) & 0xFF),
+ (byte) ((adler32 >> 8) & 0xFF),
+ (byte) (adler32 & 0xFF),
+ ];
+ outputStream.Write(checksumBytes, 0, 4);
+
+ return outputStream.ToArray();
+ }
+
+ // Adler-32 checksum calculation (needed for Zlib format)
+ private static uint CalculateAdler32(byte[] data) {
+ // ReSharper disable once InconsistentNaming
+ const uint BASE = 65521;
+ uint a = 1, b = 0;
+
+ foreach (byte byteData in data) {
+ a = (a + byteData) % BASE;
+ b = (b + a) % BASE;
}
+
+ return (b << 16) | a;
}
}
diff --git a/Maple2.File.IO/Maple2.File.IO.csproj b/Maple2.File.IO/Maple2.File.IO.csproj
index de68901..88686d4 100644
--- a/Maple2.File.IO/Maple2.File.IO.csproj
+++ b/Maple2.File.IO/Maple2.File.IO.csproj
@@ -8,8 +8,4 @@
enable
-
-
-
-
diff --git a/Maple2.File.Parser/Maple2.File.Parser.csproj b/Maple2.File.Parser/Maple2.File.Parser.csproj
index 0008299..54e2c09 100644
--- a/Maple2.File.Parser/Maple2.File.Parser.csproj
+++ b/Maple2.File.Parser/Maple2.File.Parser.csproj
@@ -13,7 +13,7 @@
MapleStory2, File, Parser, m2d, xml
true
- 2.1.33
+ 2.2.0
net8.0
README.md
enable
@@ -36,7 +36,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Maple2.File.Tests/Maple2.File.Tests.csproj b/Maple2.File.Tests/Maple2.File.Tests.csproj
index 746c634..48425f0 100644
--- a/Maple2.File.Tests/Maple2.File.Tests.csproj
+++ b/Maple2.File.Tests/Maple2.File.Tests.csproj
@@ -15,9 +15,9 @@
-
-
-
+
+
+