Skip to content

Commit

Permalink
Packing support
Browse files Browse the repository at this point in the history
  • Loading branch information
Nenkai committed Aug 25, 2021
1 parent 1001cf6 commit e91831c
Show file tree
Hide file tree
Showing 12 changed files with 1,095 additions and 249 deletions.
8 changes: 7 additions & 1 deletion GTPSPUnpacker.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31515.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GTPSPUnpacker", "GTPSPUnpacker\GTPSPUnpacker.csproj", "{635F7BAF-FCAE-4EB7-B2B1-ED8BC060D64C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GTPSPUnpacker", "GTPSPUnpacker\GTPSPUnpacker.csproj", "{635F7BAF-FCAE-4EB7-B2B1-ED8BC060D64C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDTools.Utils", "PDTools.Utils\PDTools.Utils.csproj", "{2E93D12A-139B-46B8-BED3-294E714C6D6A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +17,10 @@ Global
{635F7BAF-FCAE-4EB7-B2B1-ED8BC060D64C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{635F7BAF-FCAE-4EB7-B2B1-ED8BC060D64C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{635F7BAF-FCAE-4EB7-B2B1-ED8BC060D64C}.Release|Any CPU.Build.0 = Release|Any CPU
{2E93D12A-139B-46B8-BED3-294E714C6D6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E93D12A-139B-46B8-BED3-294E714C6D6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E93D12A-139B-46B8-BED3-294E714C6D6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E93D12A-139B-46B8-BED3-294E714C6D6A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
116 changes: 116 additions & 0 deletions GTPSPUnpacker/Compression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Compression;
using System.Buffers;
using System.Buffers.Binary;

using Syroot.BinaryData;
using Syroot.BinaryData.Core;
using Syroot.BinaryData.Memory;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

using System.Security.Cryptography;

namespace GTPSPUnpacker
{
public class Compression
{
/// <summary>
/// Decompresses a file (in memory, unsuited for large files).
/// </summary>
/// <param name="data"></param>
/// <param name="outSize"></param>
/// <param name="deflatedData"></param>
/// <returns></returns>
public unsafe static bool TryInflateInMemory(Span<byte> data, uint outSize, out byte[] deflatedData)
{
deflatedData = Array.Empty<byte>();
if (outSize > uint.MaxValue)
return false;

// Inflated is always little
var sr = new SpanReader(data, Endian.Little);
uint zlibMagic = sr.ReadUInt32();
uint sizeComplement = sr.ReadUInt32();

if ((long)zlibMagic != 0xFFF7EEC5)
return false;

if (outSize + sizeComplement != 0)
return false;

const int headerSize = 8;
if (sr.Length <= headerSize) // Header size, if it's under, data is missing
return false;

deflatedData = new byte[(int)outSize];
fixed (byte* pBuffer = &sr.Span.Slice(headerSize)[0]) // Vol Header Size
{
using var ums = new UnmanagedMemoryStream(pBuffer, sr.Span.Length - headerSize);
using var ds = new DeflateStream(ums, CompressionMode.Decompress);
ds.Read(deflatedData, 0, (int)outSize);
}

return true;
}

/// <summary>
/// Compresses input data (PS2ZIP-like) and encrypts it from one stream to another.
/// </summary>
/// <param name="data"></param>
/// <returns>Length of the compressed data.</returns>
public static uint PS2ZIPCompressEncrypt(Stream input, Stream output)
{
long basePos = output.Position;
Span<byte> header = stackalloc byte[8];
BinaryPrimitives.WriteUInt32BigEndian(header, 0xC5EEF7FFu);
BinaryPrimitives.WriteInt32LittleEndian(header[4..], -(int)input.Length);

VolumeCrypto.EncryptFile((uint)output.Position, header, 8);
output.Write(header);

var buffer = new byte[input.Length];
input.Read(buffer);

var d = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
d.SetInput(buffer);
d.Finish();

int count = d.Deflate(buffer);
VolumeCrypto.EncryptFile((uint)output.Position, buffer, buffer.Length);
output.Write(buffer, 0, count);

return (uint)(output.Position - basePos);
}

/// <summary>
/// Encrypts a file stream to an output stream.
/// </summary>
/// <param name="data"></param>
/// <returns>Length of the compressed data.</returns>
public static void Encrypt(Stream input, Stream output)
{
const int bufferSize = 0x20000;
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);

uint rem = (uint)input.Length;

while (rem > 0)
{
uint size = Math.Min(rem, bufferSize);
input.Read(buffer, 0, (int)size);
VolumeCrypto.EncryptFile((uint)output.Position, buffer, buffer.Length);
output.Write(buffer, 0, (int)size);

rem -= size;
}

ArrayPool<byte>.Shared.Return(buffer);
}
}
}
6 changes: 6 additions & 0 deletions GTPSPUnpacker/GTPSPUnpacker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="SharpZipLib" Version="1.3.2" />
<PackageReference Include="Syroot.BinaryData" Version="5.2.2" />
<PackageReference Include="Syroot.BinaryData.Memory" Version="5.2.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PDTools.Utils\PDTools.Utils.csproj" />
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions GTPSPUnpacker/Packing/IndexWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

using PDTools.Utils;

namespace GTPSPUnpacker.Packing
{
public class IndexWriter
{
public const int BTREE_SEGMENT_SIZE = 0x1000;

public int CurrentDataLength = 0;

public byte SegmentCount = 0;
private List<VolumeEntry> _indices { get; set; } = new();

public bool IsEmpty
=> _indices.Count == 0;

public IndexWriter()
{

}

public void AddIndex(VolumeEntry entry)
{
_indices.Add(entry);
}

public void Write(ref BitStream stream)
{
BitStream entryWriter = new BitStream(BitStreamMode.Write, endian: BitStreamSignificantBitOrder.MSB);
List<int> entryOffsets = new List<int>();

for (int i = 0; i < _indices.Count; i++)
{
if (i != 0)
entryOffsets.Add(entryWriter.Position);

entryWriter.WriteByte((byte)(_indices[i].EntriesLocationSegmentIndex >> 8)); // Major 8 bits
entryWriter.WriteVarPrefixString(_indices[i].Name);
entryWriter.WriteByte((byte)(_indices[i].EntriesLocationSegmentIndex & 0xFF)); // Minor 8 bits
}

stream.WriteBoolBit(true); // Index Block

Debug.Assert((ulong)_indices.Count < Utils.GetMaxValueForBitCount(11),
$"IndexWriter: Index Count was larger that could fit 11 bits ({_indices.Count} < {Utils.GetMaxValueForBitCount(11)})");
stream.WriteBits((ulong)_indices.Count, 11);

for (int i = 0; i < entryOffsets.Count; i++)
{
uint tocSize = (uint)Utils.MeasureBytesTakenByBits(12 + (entryOffsets.Count * 12));

Debug.Assert((ulong)(tocSize + entryOffsets[i]) < Utils.GetMaxValueForBitCount(12),
$"IndexWriter: Index offset was larger that could fit 12 bits ({(ulong)(tocSize + entryOffsets[i])} < {Utils.GetMaxValueForBitCount(12)})");
stream.WriteBits((ulong)(tocSize + entryOffsets[i]), 12);
}

stream.WriteByteData(entryWriter.GetSpan());
stream.Align(0x40);
}

}
}
Loading

0 comments on commit e91831c

Please sign in to comment.