Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove DotNetZip & Upgrade dependencies #41

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Maple2.File.Generator/Maple2.File.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
</ItemGroup>

<ItemGroup>
Expand Down
247 changes: 143 additions & 104 deletions Maple2.File.IO/Crypto/CryptoManager.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
4 changes: 0 additions & 4 deletions Maple2.File.IO/Maple2.File.IO.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,4 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DotNetZip" Version="1.16.0" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions Maple2.File.Parser/Maple2.File.Parser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageTags>MapleStory2, File, Parser, m2d, xml</PackageTags>
<!-- Use following lines to write the generated files to disk. -->
<EmitCompilerGeneratedFiles Condition=" '$(Configuration)' == 'Debug' ">true</EmitCompilerGeneratedFiles>
<PackageVersion>2.1.33</PackageVersion>
<PackageVersion>2.2.0</PackageVersion>
<TargetFramework>net8.0</TargetFramework>
<PackageReadmeFile>README.md</PackageReadmeFile>
<ImplicitUsings>enable</ImplicitUsings>
Expand All @@ -36,7 +36,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Teronis.MSBuild.Packaging.ProjectBuildInPackage" Version="1.0.0">
<PackageReference Include="Tenekon.MSBuild.Packaging.ProjectBuildInPackage" Version="2.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
6 changes: 3 additions & 3 deletions Maple2.File.Tests/Maple2.File.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.6.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.6.3" />
</ItemGroup>

</Project>
Loading