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

Decryption and decompression fixes and changes #6

Merged
merged 3 commits into from
Sep 2, 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
9 changes: 9 additions & 0 deletions Galanthus/GameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,13 @@ public static string GetPath(string inName)
}
return s_toc.GetData(file);
}

public static List<string>? GetAssetListing()
{
if (s_toc?.Assets == null)
{
return null;
}
return s_toc.Assets;
}
}
173 changes: 110 additions & 63 deletions Galanthus/SdfToc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Galanthus.Structs;
using Galanthus.Utils;
Expand All @@ -11,7 +12,7 @@ namespace Galanthus;

public class SdfToc : IDisposable
{
public IEnumerable<Asset> Assets => m_assets.Values;
public List<string> Assets => m_assets.Keys.ToList();

private TocHeader m_header;
private List<Locale> m_locales;
Expand Down Expand Up @@ -207,34 +208,16 @@ public SdfToc(TocHeader inHeader, List<Locale> inLocales, List<Block<byte>> inDd

if (isEncrypted)
{
if (KeyManager.Key is null || KeyManager.Iv is null)
Block<byte> tempBuffer = new(compressedFileTable.Ptr, compressedFileTable.Size);
tempBuffer.MarkMemoryAsFragile();

if (!DecryptBlock(header.Version, compressedFileTable, ref tempBuffer))
{
Console.WriteLine($"Toc failed to decrypt!");
return null;
}

if (header.Version >= 0x29 && compressedFileTable.Size >= 8)
{
// first they use XTEA encryption, then they use des encryption
XTEA((uint*)compressedFileTable.Ptr, 32);

// DES-PCBC
// Problem is c# doesnt have native support for it
if (Crypto.DecryptDes((nuint)compressedFileTable.Ptr, (compressedFileTable.Size >> 3) << 3, (nuint)compressedFileTable.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return null;
}
}
else if (compressedFileTable.Size >= 0x100)
{
// AES-192-OFB
// Problem is c# doesnt have native support for it
if (Crypto.DecryptAes((nuint)compressedFileTable.Ptr, 0x100, (nuint)compressedFileTable.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return null;
}
}
tempBuffer.Dispose();
}

Block<byte> fileTable = new(header.FileTableDecompressedSize);
Expand Down Expand Up @@ -359,17 +342,18 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
path = GameManager.GetPath($"sdf{s}{part}{s}{inSlice.Index:D4}{s}{locale}.sdfdata");
}

return true;
return File.Exists(path);
}


public unsafe Block<byte>? GetData(Asset inAsset)
{
// get final file size
int outBufferSize = 0;
int ddsHeaderSize = 0;
if (inAsset.DdsIndex != -1)
{
outBufferSize += m_ddsHeaders[inAsset.DdsIndex].Size;
ddsHeaderSize = m_ddsHeaders[inAsset.DdsIndex].Size;
}

foreach (DataSlice dataSlice in inAsset.DataSlices)
Expand All @@ -380,8 +364,14 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
}

outBufferSize += (int)dataSlice.DecompressedSize;
}

// probably a localised file that isn't installed so just return null before we do anything
if (outBufferSize - ddsHeaderSize <= 0)
{
return null;
}

Block<byte> outBuffer = new(outBufferSize);

// add dds header data
Expand All @@ -405,39 +395,6 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
Block<byte> compressedBuffer = new((int)dataSlice.CompressedSize);
stream.ReadExactly(compressedBuffer);

// decrypt slice
if (dataSlice.IsEncrypted)
{
if (KeyManager.Key is null || KeyManager.Iv is null)
{
return null;
}

if (m_header.Version >= 0x29 && compressedBuffer.Size >= 8)
{
// first they use XTEA encryption, then they use des encryption
XTEA((uint*)compressedBuffer.Ptr, 32);

// DES-PCBC
// Problem is c# doesnt have native support for it
if (Crypto.DecryptDes((nuint)compressedBuffer.Ptr, (compressedBuffer.Size >> 3) << 3, (nuint)compressedBuffer.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return null;
}
}
else if (compressedBuffer.Size >= 0x100)
{
// AES-192-OFB
// Problem is c# doesnt have native support for it
if (Crypto.DecryptAes((nuint)compressedBuffer.Ptr, 0x100, (nuint)compressedBuffer.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return null;
}
}
}

// decompress slice if needed
if (dataSlice.IsCompressed)
{
Expand All @@ -448,11 +405,33 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
for (int i = 0; i < dataSlice.PageSizes!.Count; i++)
{
int decompressedSize = (int)Math.Min(dataSlice.DecompressedSize - decompressedOffset, pageSize);
if (dataSlice.PageSizes.Count == 1)
{
decompressedSize = (int)dataSlice.DecompressedSize;
}

if (dataSlice.PageSizes[i] == 0 || decompressedSize == dataSlice.PageSizes[i])
{
// uncompressed page
compressedBuffer.CopyTo(outBuffer, decompressedSize);
// set up temp buffer with only the page data
Block<byte> tempBuffer = new(compressedBuffer.Ptr, decompressedSize);
tempBuffer.MarkMemoryAsFragile();

// decrypt page
if (dataSlice.IsEncrypted)
{
if (!DecryptBlock(m_header.Version, tempBuffer, ref outBuffer))
{
Console.WriteLine("Page failed to decrypt!");
return null;
}
}
else
{
tempBuffer.CopyTo(outBuffer, decompressedSize);
}

tempBuffer.Dispose();
compressedBuffer.Shift(decompressedSize);
}
else
Expand All @@ -462,9 +441,30 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
Block<byte> tempBuffer = new(compressedBuffer.Ptr, dataSlice.PageSizes[i]);
tempBuffer.MarkMemoryAsFragile();

// decrypt page
if (dataSlice.IsEncrypted)
{
if (!DecryptBlock(m_header.Version, tempBuffer, ref tempBuffer))
{
Console.WriteLine("Page failed to decrypt!");
return null;
}
}

if (!dataSlice.IsOodle)
{
ZStd.Decompress(tempBuffer, ref outBuffer);
switch (GameManager.CompressionMethod)
{
case CompressionMethod.ZLib:
ZLib.Decompress(tempBuffer, ref outBuffer);
break;
case CompressionMethod.ZStd:
ZStd.Decompress(tempBuffer, ref outBuffer);
break;
case CompressionMethod.Lz4:
Lz4.Decompress(tempBuffer, ref outBuffer);
break;
}
}
else
{
Expand All @@ -484,7 +484,20 @@ public bool TryGetDataFile(DataSlice inSlice, [NotNullWhen(true)] out string? pa
}
else
{
compressedBuffer.CopyTo(outBuffer, (int)dataSlice.DecompressedSize);
// decrypt whole slice
if (dataSlice.IsEncrypted)
{
if (!DecryptBlock(m_header.Version, compressedBuffer, ref outBuffer))
{
Console.WriteLine("Slice failed to decrypt!");
return null;
}
}
else
{
compressedBuffer.CopyTo(outBuffer, (int)dataSlice.DecompressedSize);
}

outBuffer.Shift((int)dataSlice.DecompressedSize);
}

Expand Down Expand Up @@ -644,6 +657,40 @@ private static void ParseEntry(DataStream inStream, bool isSigned, string name,
}
}

public static unsafe bool DecryptBlock(uint inVersion, Block<Byte> inBuf, ref Block<Byte> outBuf)
{
if (KeyManager.Key is null || KeyManager.Iv is null)
{
return false;
}

if (inVersion >= 0x29 && inBuf.Size >= 8)
{
// first they use XTEA encryption, then they use des encryption
XTEA((uint*)inBuf.Ptr, 32);

// DES-PCBC
// Problem is c# doesnt have native support for it
if (Crypto.DecryptDes((nuint)inBuf.Ptr, (inBuf.Size >> 3) << 3, (nuint)outBuf.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return false;
}
}
else if (inBuf.Size >= 0x100)
{
// AES-192-OFB
// Problem is c# doesnt have native support for it
if (Crypto.DecryptAes((nuint)inBuf.Ptr, 0x100, (nuint)outBuf.Ptr, (nuint)KeyManager.Key.Ptr,
(nuint)KeyManager.Iv.Ptr) != 0)
{
return false;
}
}

return true;
}

public static unsafe void XTEA(uint* v, uint numRounds)
{
uint[] key = [0xb, 0x11, 0x17, 0x1f];
Expand Down
Loading