mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-02-04 05:36:12 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
120de4e49f | ||
|
|
354a51769b | ||
|
|
a9f937baa3 | ||
|
|
1790d82a6e | ||
|
|
261c20e95a | ||
|
|
ed6556b1f0 | ||
|
|
a86af8c32a | ||
|
|
1670ab45a0 | ||
|
|
7dc4750f3b | ||
|
|
b5f366680d | ||
|
|
fa9e9a0b2b | ||
|
|
2239b82a4b | ||
|
|
3b631678f5 | ||
|
|
2b111a9688 | ||
|
|
0bda1f4f88 | ||
|
|
7d50e0e1c5 | ||
|
|
224a4caab0 | ||
|
|
b4689da404 | ||
|
|
af66657399 | ||
|
|
0f3e2d8275 | ||
|
|
d664b6defc | ||
|
|
adbf74a6e0 | ||
|
|
7eb401efed | ||
|
|
ba97381b99 | ||
|
|
3de92de225 |
4
.github/workflows/build_and_test.yml
vendored
4
.github/workflows/build_and_test.yml
vendored
@@ -28,13 +28,13 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: '*.nupkg'
|
||||
path: "*.nupkg,*.snupkg"
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: "*.nupkg,*.zip"
|
||||
artifacts: "*.nupkg,*.snupkg,*.zip"
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
|
||||
2
.github/workflows/check_pr.yml
vendored
2
.github/workflows/check_pr.yml
vendored
@@ -17,4 +17,4 @@ jobs:
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
run: dotnet test
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.8.2</Version>
|
||||
<Version>1.8.4</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
@@ -31,7 +31,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.IO" Version="1.6.1" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Deserializers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Deserializers
|
||||
{
|
||||
public class AppPkgHeaderTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var deserializer = new AppPkgHeader();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
SabreTools.Serialization.Test/Deserializers/LZKWAJTests.cs
Normal file
73
SabreTools.Serialization.Test/Deserializers/LZKWAJTests.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Deserializers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Deserializers
|
||||
{
|
||||
public class LZKWAJTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var deserializer = new LZKWAJ();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
SabreTools.Serialization.Test/Deserializers/LZQBasicTests.cs
Normal file
74
SabreTools.Serialization.Test/Deserializers/LZQBasicTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Deserializers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Deserializers
|
||||
{
|
||||
public class LZQBasicTests
|
||||
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var deserializer = new LZQBasic();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
SabreTools.Serialization.Test/Deserializers/LZSZDDTests.cs
Normal file
73
SabreTools.Serialization.Test/Deserializers/LZSZDDTests.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Deserializers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Deserializers
|
||||
{
|
||||
public class LZSZDDTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var deserializer = new LZSZDD();
|
||||
|
||||
var actual = deserializer.Deserialize(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,10 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.6" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.8" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Wrappers
|
||||
{
|
||||
public class InstallShieldArchiveV3Tests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var actual = InstallShieldArchiveV3.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var actual = InstallShieldArchiveV3.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var actual = InstallShieldArchiveV3.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var actual = InstallShieldArchiveV3.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var actual = InstallShieldArchiveV3.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var actual = InstallShieldArchiveV3.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
SabreTools.Serialization.Test/Wrappers/LZKWAJTests.cs
Normal file
61
SabreTools.Serialization.Test/Wrappers/LZKWAJTests.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Wrappers
|
||||
{
|
||||
public class LZKWAJTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var actual = LZKWAJ.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var actual = LZKWAJ.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var actual = LZKWAJ.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var actual = LZKWAJ.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var actual = LZKWAJ.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var actual = LZKWAJ.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
SabreTools.Serialization.Test/Wrappers/LZQBasicTests.cs
Normal file
61
SabreTools.Serialization.Test/Wrappers/LZQBasicTests.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Wrappers
|
||||
{
|
||||
public class LZQBasicTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var actual = LZQBasic.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var actual = LZQBasic.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var actual = LZQBasic.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var actual = LZQBasic.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var actual = LZQBasic.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var actual = LZQBasic.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
SabreTools.Serialization.Test/Wrappers/LZSZDDTests.cs
Normal file
61
SabreTools.Serialization.Test/Wrappers/LZSZDDTests.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Test.Wrappers
|
||||
{
|
||||
public class LZSZDDTests
|
||||
{
|
||||
[Fact]
|
||||
public void NullArray_Null()
|
||||
{
|
||||
byte[]? data = null;
|
||||
int offset = 0;
|
||||
var actual = LZSZDD.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyArray_Null()
|
||||
{
|
||||
byte[]? data = [];
|
||||
int offset = 0;
|
||||
var actual = LZSZDD.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidArray_Null()
|
||||
{
|
||||
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
|
||||
int offset = 0;
|
||||
var actual = LZSZDD.Create(data, offset);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullStream_Null()
|
||||
{
|
||||
Stream? data = null;
|
||||
var actual = LZSZDD.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([]);
|
||||
var actual = LZSZDD.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidStream_Null()
|
||||
{
|
||||
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
|
||||
var actual = LZSZDD.Create(data);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace SabreTools.Serialization.CrossModel
|
||||
var romSizes = Array.ConvertAll(roms, r => r.ReadLong(Models.Metadata.Rom.SizeKey) ?? -1);
|
||||
game.RomSize = Array.Find(romSizes, s => s > -1).ToString();
|
||||
|
||||
var romCRCs = Array.ConvertAll(roms, ConvertFromInternalModel);;
|
||||
var romCRCs = Array.ConvertAll(roms, ConvertFromInternalModel);
|
||||
game.Files = new Files { RomCRC = romCRCs };
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new media key block to fill
|
||||
var mediaKeyBlock = new MediaKeyBlock();
|
||||
|
||||
#region Records
|
||||
|
||||
// Create the records list
|
||||
@@ -49,7 +46,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Set the records
|
||||
if (records.Count > 0)
|
||||
return new MediaKeyBlock { Records = [.. records ]};
|
||||
return new MediaKeyBlock { Records = [.. records] };
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -61,29 +58,29 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a record
|
||||
/// Parse a Stream into a Record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled record on success, null on error</returns>
|
||||
/// <returns>Filled Record on success, null on error</returns>
|
||||
private static Record? ParseRecord(Stream data)
|
||||
{
|
||||
// The first 4 bytes make up the type and length
|
||||
// The first byte is the type
|
||||
RecordType type = (RecordType)data.ReadByteValue();
|
||||
uint length = data.ReadUInt24();
|
||||
data.Seek(-1, SeekOrigin.Current);
|
||||
|
||||
// Create a record based on the type
|
||||
return type switch
|
||||
{
|
||||
// Known record types
|
||||
RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data, type, length),
|
||||
RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data, type, length),
|
||||
RecordType.MediaKeyData => ParseMediaKeyDataRecord(data, type, length),
|
||||
RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data, type, length),
|
||||
RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data, type, length),
|
||||
RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data, type, length),
|
||||
RecordType.HostRevocationList => ParseHostRevocationListRecord(data, type, length),
|
||||
RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data, type, length),
|
||||
RecordType.Copyright => ParseCopyrightRecord(data, type, length),
|
||||
RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data),
|
||||
RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data),
|
||||
RecordType.MediaKeyData => ParseMediaKeyDataRecord(data),
|
||||
RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data),
|
||||
RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data),
|
||||
RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data),
|
||||
RecordType.HostRevocationList => ParseHostRevocationListRecord(data),
|
||||
RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data),
|
||||
RecordType.Copyright => ParseCopyrightRecord(data),
|
||||
|
||||
// Unknown record type
|
||||
_ => null,
|
||||
@@ -91,324 +88,327 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an end of media key block record
|
||||
/// Parse a Stream into a CopyrightRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled end of media key block record on success, null on error</returns>
|
||||
private static EndOfMediaKeyBlockRecord? ParseEndOfMediaKeyBlockRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled CopyrightRecord on success, null on error</returns>
|
||||
public static CopyrightRecord ParseCopyrightRecord(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.EndOfMediaKeyBlock)
|
||||
return null;
|
||||
var obj = new CopyrightRecord();
|
||||
|
||||
var record = new EndOfMediaKeyBlockRecord();
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
if (obj.RecordLength > 4)
|
||||
{
|
||||
byte[] copyright = data.ReadBytes((int)(obj.RecordLength - 4));
|
||||
obj.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0');
|
||||
}
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
if (length > 4)
|
||||
record.SignatureData = data.ReadBytes((int)(length - 4));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DriveRevocationListEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DriveRevocationListEntry on success, null on error</returns>
|
||||
public static DriveRevocationListEntry ParseDriveRevocationListEntry(Stream data)
|
||||
{
|
||||
var obj = new DriveRevocationListEntry();
|
||||
|
||||
return record;
|
||||
obj.Range = data.ReadUInt16BigEndian();
|
||||
obj.DriveID = data.ReadBytes(6);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an explicit subset-difference record
|
||||
/// Parse a Stream into a DriveRevocationListRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled explicit subset-difference record on success, null on error</returns>
|
||||
private static ExplicitSubsetDifferenceRecord? ParseExplicitSubsetDifferenceRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled DriveRevocationListRecord on success, null on error</returns>
|
||||
public static DriveRevocationListRecord ParseDriveRevocationListRecord(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.ExplicitSubsetDifference)
|
||||
return null;
|
||||
|
||||
var record = new ExplicitSubsetDifferenceRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create the subset difference list
|
||||
var subsetDifferences = new List<SubsetDifference>();
|
||||
var obj = new DriveRevocationListRecord();
|
||||
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
obj.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
var blocks = new List<DriveRevocationSignatureBlock>();
|
||||
uint entryCount = 0;
|
||||
while (entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + obj.RecordLength)
|
||||
{
|
||||
var block = ParseDriveRevocationSignatureBlock(data);
|
||||
entryCount += block.NumberOfEntries;
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
obj.SignatureBlocks = [.. blocks];
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + obj.RecordLength)
|
||||
_ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DriveRevocationSignatureBlock
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DriveRevocationSignatureBlock on success, null on error</returns>
|
||||
public static DriveRevocationSignatureBlock ParseDriveRevocationSignatureBlock(Stream data)
|
||||
{
|
||||
var obj = new DriveRevocationSignatureBlock();
|
||||
|
||||
obj.NumberOfEntries = data.ReadUInt32BigEndian();
|
||||
obj.EntryFields = new DriveRevocationListEntry[obj.NumberOfEntries];
|
||||
for (int i = 0; i < obj.EntryFields.Length; i++)
|
||||
{
|
||||
obj.EntryFields[i] = ParseDriveRevocationListEntry(data);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a EndOfMediaKeyBlockRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled EndOfMediaKeyBlockRecord on success, null on error</returns>
|
||||
public static EndOfMediaKeyBlockRecord ParseEndOfMediaKeyBlockRecord(Stream data)
|
||||
{
|
||||
var obj = new EndOfMediaKeyBlockRecord();
|
||||
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
if (obj.RecordLength > 4)
|
||||
obj.SignatureData = data.ReadBytes((int)(obj.RecordLength - 4));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ExplicitSubsetDifferenceRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExplicitSubsetDifferenceRecord on success, null on error</returns>
|
||||
public static ExplicitSubsetDifferenceRecord ParseExplicitSubsetDifferenceRecord(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
var obj = new ExplicitSubsetDifferenceRecord();
|
||||
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
|
||||
// Try to parse the subset differences
|
||||
while (data.Position < initialOffset + length - 5)
|
||||
var subsetDifferences = new List<SubsetDifference>();
|
||||
while (data.Position < initialOffset + obj.RecordLength - 5)
|
||||
{
|
||||
var subsetDifference = new SubsetDifference();
|
||||
|
||||
subsetDifference.Mask = data.ReadByteValue();
|
||||
subsetDifference.Number = data.ReadUInt32BigEndian();
|
||||
|
||||
var subsetDifference = ParseSubsetDifference(data);
|
||||
subsetDifferences.Add(subsetDifference);
|
||||
}
|
||||
|
||||
// Set the subset differences
|
||||
record.SubsetDifferences = [.. subsetDifferences];
|
||||
obj.SubsetDifferences = [.. subsetDifferences];
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
if (data.Position < initialOffset + obj.RecordLength)
|
||||
_ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position));
|
||||
|
||||
return record;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a media key data record
|
||||
/// Parse a Stream into a HostRevocationListEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled media key data record on success, null on error</returns>
|
||||
private static MediaKeyDataRecord? ParseMediaKeyDataRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled HostRevocationListEntry on success, null on error</returns>
|
||||
public static HostRevocationListEntry ParseHostRevocationListEntry(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.MediaKeyData)
|
||||
return null;
|
||||
var obj = new HostRevocationListEntry();
|
||||
|
||||
var record = new MediaKeyDataRecord();
|
||||
obj.Range = data.ReadUInt16BigEndian();
|
||||
obj.HostID = data.ReadBytes(6);
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HostRevocationListRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HostRevocationListRecord on success, null on error</returns>
|
||||
public static HostRevocationListRecord ParseHostRevocationListRecord(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Create the media key list
|
||||
var mediaKeys = new List<byte[]>();
|
||||
var obj = new HostRevocationListRecord();
|
||||
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
obj.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
var blocks = new List<HostRevocationSignatureBlock>();
|
||||
for (uint entryCount = 0; entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + obj.RecordLength;)
|
||||
{
|
||||
var block = ParseHostRevocationSignatureBlock(data);
|
||||
entryCount += block.NumberOfEntries;
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
obj.SignatureBlocks = [.. blocks];
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + obj.RecordLength)
|
||||
_ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HostRevocationSignatureBlock
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HostRevocationSignatureBlock on success, null on error</returns>
|
||||
public static HostRevocationSignatureBlock ParseHostRevocationSignatureBlock(Stream data)
|
||||
{
|
||||
var obj = new HostRevocationSignatureBlock();
|
||||
|
||||
obj.NumberOfEntries = data.ReadUInt32BigEndian();
|
||||
obj.EntryFields = new HostRevocationListEntry[obj.NumberOfEntries];
|
||||
for (int i = 0; i < obj.EntryFields.Length; i++)
|
||||
{
|
||||
obj.EntryFields[i] = ParseHostRevocationListEntry(data);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a MediaKeyDataRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled MediaKeyDataRecord on success, null on error</returns>
|
||||
public static MediaKeyDataRecord ParseMediaKeyDataRecord(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
var obj = new MediaKeyDataRecord();
|
||||
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
|
||||
// Try to parse the media keys
|
||||
while (data.Position < initialOffset + length)
|
||||
var mediaKeys = new List<byte[]>();
|
||||
while (data.Position < initialOffset + obj.RecordLength)
|
||||
{
|
||||
byte[] mediaKey = data.ReadBytes(0x10);
|
||||
mediaKeys.Add(mediaKey);
|
||||
}
|
||||
|
||||
// Set the media keys
|
||||
record.MediaKeyData = [.. mediaKeys];
|
||||
obj.MediaKeyData = [.. mediaKeys];
|
||||
|
||||
return record;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a subset-difference index record
|
||||
/// Parse a Stream into a SubsetDifference
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled subset-difference index record on success, null on error</returns>
|
||||
private static SubsetDifferenceIndexRecord? ParseSubsetDifferenceIndexRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled SubsetDifference on success, null on error</returns>
|
||||
public static SubsetDifference ParseSubsetDifference(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.SubsetDifferenceIndex)
|
||||
return null;
|
||||
var obj = new SubsetDifference();
|
||||
|
||||
var record = new SubsetDifferenceIndexRecord();
|
||||
obj.Mask = data.ReadByteValue();
|
||||
obj.Number = data.ReadUInt32BigEndian();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a SubsetDifferenceIndexRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SubsetDifferenceIndexRecord on success, null on error</returns>
|
||||
public static SubsetDifferenceIndexRecord ParseSubsetDifferenceIndexRecord(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
long initialOffset = data.Position;
|
||||
|
||||
record.Span = data.ReadUInt32BigEndian();
|
||||
var obj = new SubsetDifferenceIndexRecord();
|
||||
|
||||
// Create the offset list
|
||||
var offsets = new List<uint>();
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
obj.Span = data.ReadUInt32BigEndian();
|
||||
|
||||
// Try to parse the offsets
|
||||
while (data.Position < initialOffset + length)
|
||||
var offsets = new List<uint>();
|
||||
while (data.Position < initialOffset + obj.RecordLength)
|
||||
{
|
||||
uint offset = data.ReadUInt32BigEndian();
|
||||
offsets.Add(offset);
|
||||
}
|
||||
|
||||
// Set the offsets
|
||||
record.Offsets = [.. offsets];
|
||||
obj.Offsets = [.. offsets];
|
||||
|
||||
return record;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a type and version record
|
||||
/// Parse a Stream into a TypeAndVersionRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled type and version record on success, null on error</returns>
|
||||
private static TypeAndVersionRecord? ParseTypeAndVersionRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled TypeAndVersionRecord on success, null on error</returns>
|
||||
public static TypeAndVersionRecord ParseTypeAndVersionRecord(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.TypeAndVersion)
|
||||
return null;
|
||||
var obj = new TypeAndVersionRecord();
|
||||
|
||||
var record = new TypeAndVersionRecord();
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
obj.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BigEndian();
|
||||
obj.VersionNumber = data.ReadUInt32BigEndian();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
record.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BigEndian();
|
||||
record.VersionNumber = data.ReadUInt32BigEndian();
|
||||
|
||||
return record;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a drive revocation list record
|
||||
/// Parse a Stream into a VerifyMediaKeyRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled drive revocation list record on success, null on error</returns>
|
||||
private static DriveRevocationListRecord? ParseDriveRevocationListRecord(Stream data, RecordType type, uint length)
|
||||
/// <returns>Filled VerifyMediaKeyRecord on success, null on error</returns>
|
||||
public static VerifyMediaKeyRecord ParseVerifyMediaKeyRecord(Stream data)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.DriveRevocationList)
|
||||
return null;
|
||||
var obj = new VerifyMediaKeyRecord();
|
||||
|
||||
var record = new DriveRevocationListRecord();
|
||||
obj.RecordType = (RecordType)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadUInt24LittleEndian();
|
||||
obj.CiphertextValue = data.ReadBytes(0x10);
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
record.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
||||
|
||||
// Create the signature blocks list
|
||||
var blocks = new List<DriveRevocationSignatureBlock>();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
int entryCount = 0;
|
||||
while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length)
|
||||
{
|
||||
var block = new DriveRevocationSignatureBlock();
|
||||
|
||||
block.NumberOfEntries = data.ReadUInt32BigEndian();
|
||||
block.EntryFields = new DriveRevocationListEntry[block.NumberOfEntries];
|
||||
for (int i = 0; i < block.EntryFields.Length; i++)
|
||||
{
|
||||
var entry = new DriveRevocationListEntry();
|
||||
|
||||
entry.Range = data.ReadUInt16BigEndian();
|
||||
entry.DriveID = data.ReadBytes(6);
|
||||
|
||||
block.EntryFields[i] = entry;
|
||||
entryCount++;
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
record.SignatureBlocks = [.. blocks];
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a host revocation list record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled host revocation list record on success, null on error</returns>
|
||||
private static HostRevocationListRecord? ParseHostRevocationListRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.HostRevocationList)
|
||||
return null;
|
||||
|
||||
var record = new HostRevocationListRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
record.TotalNumberOfEntries = data.ReadUInt32BigEndian();
|
||||
|
||||
// Create the signature blocks list
|
||||
var blocks = new List<HostRevocationSignatureBlock>();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
int entryCount = 0;
|
||||
while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length)
|
||||
{
|
||||
var block = new HostRevocationSignatureBlock();
|
||||
|
||||
block.NumberOfEntries = data.ReadUInt32BigEndian();
|
||||
block.EntryFields = new HostRevocationListEntry[block.NumberOfEntries];
|
||||
for (int i = 0; i < block.EntryFields.Length; i++)
|
||||
{
|
||||
var entry = new HostRevocationListEntry();
|
||||
|
||||
entry.Range = data.ReadUInt16BigEndian();
|
||||
entry.HostID = data.ReadBytes(6);
|
||||
|
||||
block.EntryFields[i] = entry;
|
||||
entryCount++;
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
record.SignatureBlocks = [.. blocks];
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a verify media key record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled verify media key record on success, null on error</returns>
|
||||
private static VerifyMediaKeyRecord? ParseVerifyMediaKeyRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.VerifyMediaKey)
|
||||
return null;
|
||||
|
||||
var record = new VerifyMediaKeyRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
record.CiphertextValue = data.ReadBytes(0x10);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a copyright record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled copyright record on success, null on error</returns>
|
||||
private static CopyrightRecord? ParseCopyrightRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.Copyright)
|
||||
return null;
|
||||
|
||||
var record = new CopyrightRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
if (length > 4)
|
||||
{
|
||||
byte[] copyright = data.ReadBytes((int)(length - 4));
|
||||
record.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return record;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
SabreTools.Serialization/Deserializers/AppPkgHeader.cs
Normal file
79
SabreTools.Serialization/Deserializers/AppPkgHeader.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using static SabreTools.Models.PlayStation4.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class AppPkgHeader : BaseBinaryDeserializer<Models.PlayStation4.AppPkgHeader>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Models.PlayStation4.AppPkgHeader? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new app.pkg header to fill
|
||||
var appPkgHeader = new Models.PlayStation4.AppPkgHeader();
|
||||
|
||||
appPkgHeader.Magic = data.ReadUInt32BigEndian();
|
||||
if (appPkgHeader.Magic != AppPkgMagic)
|
||||
return null;
|
||||
|
||||
appPkgHeader.Type = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PKGUnknown = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.FileCount = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.EntryCount = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.SCEntryCount = data.ReadUInt16BigEndian();
|
||||
appPkgHeader.EntryCount2 = data.ReadUInt16BigEndian();
|
||||
appPkgHeader.TableOffset = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.EntryDataSize = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.BodyOffset = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.BodySize = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.ContentOffset = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.ContentSize = data.ReadUInt64BigEndian();
|
||||
byte[] contentID = data.ReadBytes(0x24);
|
||||
appPkgHeader.ContentID = Encoding.ASCII.GetString(contentID).TrimEnd('\0');
|
||||
appPkgHeader.ContentZeroes = data.ReadBytes(0xC);
|
||||
appPkgHeader.DRMType = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.ContentType = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.ContentFlags = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PromoteSize = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.VersionDate = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.VersionHash = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.Zeroes1 = data.ReadBytes(0x78);
|
||||
appPkgHeader.MainEntry1SHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.MainEntry2SHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.DigestTableSHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.MainTableSHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.Zeroes2 = data.ReadBytes(0x280);
|
||||
appPkgHeader.PFSUnknown = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PFSImageCount = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PFSImageFlags = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.PFSImageOffset = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.PFSImageSize = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.MountImageOffset = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.MountImageSize = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.PKGSize = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.PKGSignedSize = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PKGCacheSize = data.ReadUInt32BigEndian();
|
||||
appPkgHeader.PFSImageSHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.PFSSignedSHA256 = data.ReadBytes(0x20);
|
||||
appPkgHeader.PFSSplitSize0 = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.PFSSplitSize1 = data.ReadUInt64BigEndian();
|
||||
appPkgHeader.Zeroes3 = data.ReadBytes(0xB50);
|
||||
appPkgHeader.PKGSHA256 = data.ReadBytes(0x20);
|
||||
|
||||
return appPkgHeader;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (svm.Day < 1 || svm.Day > 31)
|
||||
return null;
|
||||
|
||||
svm.Unknown2 = data.ReadUInt32();
|
||||
svm.Length = data.ReadUInt32();
|
||||
svm.Unknown2 = data.ReadUInt32LittleEndian();
|
||||
svm.Length = data.ReadUInt32LittleEndian();
|
||||
if (svm.Length > 0)
|
||||
svm.Data = data.ReadBytes((int)svm.Length);
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Magic != SignatureString)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Magic != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
@@ -58,32 +58,49 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file entry
|
||||
/// Parse a Stream into a FileEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled file entry on success, null on error</returns>
|
||||
private static FileEntry ParseFileEntry(Stream data)
|
||||
/// <returns>Filled FileEntry on success, null on error</returns>
|
||||
public static FileEntry ParseFileEntry(Stream data)
|
||||
{
|
||||
var fileEntry = new FileEntry();
|
||||
|
||||
fileEntry.NameSize = data.ReadInt32();
|
||||
fileEntry.NameSize = data.ReadInt32LittleEndian();
|
||||
if (fileEntry.NameSize > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(fileEntry.NameSize);
|
||||
fileEntry.Name = Encoding.ASCII.GetString(name);
|
||||
}
|
||||
|
||||
fileEntry.UncompressedSize = data.ReadInt32();
|
||||
fileEntry.Offset = data.ReadInt32();
|
||||
fileEntry.UncompressedSize = data.ReadInt32LittleEndian();
|
||||
fileEntry.Offset = data.ReadInt32LittleEndian();
|
||||
if (fileEntry.Offset > 0)
|
||||
{
|
||||
long currentOffset = data.Position;
|
||||
data.Seek(fileEntry.Offset, SeekOrigin.Begin);
|
||||
fileEntry.CompressedSize = data.ReadInt32();
|
||||
fileEntry.CompressedSize = data.ReadInt32LittleEndian();
|
||||
data.Seek(currentOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
return fileEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
byte[] magic = data.ReadBytes(4);
|
||||
obj.Magic = Encoding.ASCII.GetString(magic);
|
||||
obj.Version = data.ReadInt32LittleEndian();
|
||||
obj.Files = data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<BspHeader>();
|
||||
if (header?.Lumps == null || header.Lumps.Length != BSP_HEADER_LUMPS)
|
||||
return null;
|
||||
var header = ParseBspHeader(data);
|
||||
if (header.Version < 29 || header.Version > 30)
|
||||
return null;
|
||||
|
||||
@@ -41,7 +39,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
for (int l = 0; l < BSP_HEADER_LUMPS; l++)
|
||||
{
|
||||
// Get the next lump entry
|
||||
var lumpEntry = header.Lumps[l];
|
||||
var lumpEntry = header.Lumps![l];
|
||||
if (lumpEntry == null)
|
||||
continue;
|
||||
if (lumpEntry.Offset == 0 || lumpEntry.Length == 0)
|
||||
@@ -115,6 +113,272 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspFace
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspFace on success, null on error</returns>
|
||||
public static BspFace ParseBspFace(Stream data)
|
||||
{
|
||||
var obj = new BspFace();
|
||||
|
||||
obj.PlaneIndex = data.ReadUInt16LittleEndian();
|
||||
obj.PlaneSideCount = data.ReadUInt16LittleEndian();
|
||||
obj.FirstEdgeIndex = data.ReadUInt32LittleEndian();
|
||||
obj.NumberOfEdges = data.ReadUInt16LittleEndian();
|
||||
obj.TextureInfoIndex = data.ReadUInt16LittleEndian();
|
||||
obj.LightingStyles = data.ReadBytes(4);
|
||||
obj.LightmapOffset = data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspHeader on success, null on error</returns>
|
||||
public static BspHeader ParseBspHeader(Stream data)
|
||||
{
|
||||
var obj = new BspHeader();
|
||||
|
||||
obj.Version = data.ReadInt32LittleEndian();
|
||||
obj.Lumps = new BspLumpEntry[BSP_HEADER_LUMPS];
|
||||
for (int i = 0; i < BSP_HEADER_LUMPS; i++)
|
||||
{
|
||||
obj.Lumps[i] = ParseBspLumpEntry(data);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspLeaf
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspLeaf on success, null on error</returns>
|
||||
public static BspLeaf ParseBspLeaf(Stream data)
|
||||
{
|
||||
var obj = new BspLeaf();
|
||||
|
||||
obj.Contents = (BspContents)data.ReadInt32LittleEndian();
|
||||
obj.VisOffset = data.ReadInt32LittleEndian();
|
||||
obj.Mins = new short[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.Mins[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
obj.Maxs = new short[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.Maxs[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
obj.FirstMarkSurfaceIndex = data.ReadUInt16LittleEndian();
|
||||
obj.MarkSurfacesCount = data.ReadUInt16LittleEndian();
|
||||
obj.AmbientLevels = data.ReadBytes(4);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspLumpEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspLumpEntry on success, null on error</returns>
|
||||
public static BspLumpEntry ParseBspLumpEntry(Stream data)
|
||||
{
|
||||
var obj = new BspLumpEntry();
|
||||
|
||||
obj.Offset = data.ReadInt32LittleEndian();
|
||||
obj.Length = data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspModel
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspModel on success, null on error</returns>
|
||||
public static BspModel ParseBspModel(Stream data)
|
||||
{
|
||||
var obj = new BspModel();
|
||||
|
||||
obj.Mins = ParseVector3D(data);
|
||||
obj.Maxs = ParseVector3D(data);
|
||||
obj.OriginVector = ParseVector3D(data);
|
||||
obj.HeadnodesIndex = new int[MAX_MAP_HULLS];
|
||||
for (int i = 0; i < MAX_MAP_HULLS; i++)
|
||||
{
|
||||
obj.HeadnodesIndex[i] = data.ReadInt32LittleEndian();
|
||||
}
|
||||
obj.VisLeafsCount = data.ReadInt32LittleEndian();
|
||||
obj.FirstFaceIndex = data.ReadInt32LittleEndian();
|
||||
obj.FacesCount = data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspNode
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspNode on success, null on error</returns>
|
||||
public static BspNode ParseBspNode(Stream data)
|
||||
{
|
||||
var obj = new BspNode();
|
||||
|
||||
obj.PlaneIndex = data.ReadUInt32LittleEndian();
|
||||
obj.Children = new ushort[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
obj.Children[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
obj.Mins = new ushort[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.Mins[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
obj.Maxs = new ushort[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.Maxs[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
obj.FirstFace = data.ReadUInt16LittleEndian();
|
||||
obj.FaceCount = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into BspTexinfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BspTexinfo on success, null on error</returns>
|
||||
public static BspTexinfo ParseBspTexinfo(Stream data)
|
||||
{
|
||||
var obj = new BspTexinfo();
|
||||
|
||||
obj.SVector = ParseVector3D(data);
|
||||
obj.TextureSShift = data.ReadSingle();
|
||||
obj.TVector = ParseVector3D(data);
|
||||
obj.TextureTShift = data.ReadSingle();
|
||||
obj.MiptexIndex = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (TextureFlag)data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into Clipnode
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Clipnode on success, null on error</returns>
|
||||
public static Clipnode ParseClipnode(Stream data)
|
||||
{
|
||||
var obj = new Clipnode();
|
||||
|
||||
obj.PlaneIndex = data.ReadInt32LittleEndian();
|
||||
obj.ChildrenIndices = new short[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
obj.ChildrenIndices[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into Edge
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Edge on success, null on error</returns>
|
||||
public static Edge ParseEdge(Stream data)
|
||||
{
|
||||
var obj = new Edge();
|
||||
|
||||
obj.VertexIndices = new ushort[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
obj.VertexIndices[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into MipTexture
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled MipTexture on success, null on error</returns>
|
||||
public static MipTexture ParseMipTexture(Stream data)
|
||||
{
|
||||
var obj = new MipTexture();
|
||||
|
||||
byte[] name = data.ReadBytes(MAXTEXTURENAME);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
obj.Width = data.ReadUInt32LittleEndian();
|
||||
obj.Height = data.ReadUInt32LittleEndian();
|
||||
obj.Offsets = new uint[MIPLEVELS];
|
||||
for (int i = 0; i < MIPLEVELS; i++)
|
||||
{
|
||||
obj.Offsets[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into Plane
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Plane on success, null on error</returns>
|
||||
public static Plane ParsePlane(Stream data)
|
||||
{
|
||||
var obj = new Plane();
|
||||
|
||||
obj.NormalVector = ParseVector3D(data);
|
||||
obj.Distance = data.ReadSingle();
|
||||
obj.PlaneType = (PlaneType)data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a TextureHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled TextureHeader on success, null on error</returns>
|
||||
public static TextureHeader ParseTextureHeader(Stream data)
|
||||
{
|
||||
var obj = new TextureHeader();
|
||||
|
||||
obj.MipTextureCount = data.ReadUInt32LittleEndian();
|
||||
obj.Offsets = new int[obj.MipTextureCount];
|
||||
for (int i = 0; i < obj.Offsets.Length; i++)
|
||||
{
|
||||
obj.Offsets[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into Vector3D
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Vector3D on success, null on error</returns>
|
||||
public static Vector3D ParseVector3D(Stream data)
|
||||
{
|
||||
var obj = new Vector3D();
|
||||
|
||||
obj.X = data.ReadSingle();
|
||||
obj.Y = data.ReadSingle();
|
||||
obj.Z = data.ReadSingle();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into LUMP_ENTITIES
|
||||
/// </summary>
|
||||
@@ -167,9 +431,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var planes = new List<Plane>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var plane = data.ReadType<Plane>();
|
||||
if (plane != null)
|
||||
planes.Add(plane);
|
||||
var plane = ParsePlane(data);
|
||||
planes.Add(plane);
|
||||
}
|
||||
|
||||
return new PlanesLump { Planes = [.. planes] };
|
||||
@@ -188,34 +451,14 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var textures = new List<MipTexture>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var texture = data.ReadType<MipTexture>();
|
||||
if (texture != null)
|
||||
textures.Add(texture);
|
||||
var texture = ParseMipTexture(data);
|
||||
textures.Add(texture);
|
||||
}
|
||||
|
||||
lump.Textures = [.. textures];
|
||||
return lump;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Level texture header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Level texture header on success, null on error</returns>
|
||||
private static TextureHeader ParseTextureHeader(Stream data)
|
||||
{
|
||||
var textureHeader = new TextureHeader();
|
||||
|
||||
textureHeader.MipTextureCount = data.ReadUInt32();
|
||||
textureHeader.Offsets = new int[textureHeader.MipTextureCount];
|
||||
for (int i = 0; i < textureHeader.Offsets.Length; i++)
|
||||
{
|
||||
textureHeader.Offsets[i] = data.ReadInt32();
|
||||
}
|
||||
|
||||
return textureHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into LUMP_VERTICES
|
||||
/// </summary>
|
||||
@@ -226,10 +469,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var vertices = new List<Vector3D>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var vertex = data.ReadType<Vector3D>();
|
||||
if (vertex == null)
|
||||
break;
|
||||
|
||||
var vertex = ParseVector3D(data);
|
||||
vertices.Add(vertex);
|
||||
}
|
||||
|
||||
@@ -245,7 +485,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var lump = new VisibilityLump();
|
||||
|
||||
lump.NumClusters = data.ReadInt32();
|
||||
lump.NumClusters = data.ReadInt32LittleEndian();
|
||||
|
||||
// BSP29 has an incompatible value here
|
||||
int bytesNeeded = lump.NumClusters * 8;
|
||||
@@ -258,7 +498,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
lump.ByteOffsets[i] = new int[2];
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
lump.ByteOffsets[i][j] = data.ReadInt32();
|
||||
lump.ByteOffsets[i][j] = data.ReadInt32LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,9 +515,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var nodes = new List<BspNode>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var node = data.ReadType<BspNode>();
|
||||
if (node != null)
|
||||
nodes.Add(node);
|
||||
var node = ParseBspNode(data);
|
||||
nodes.Add(node);
|
||||
}
|
||||
|
||||
return new BspNodesLump { Nodes = [.. nodes] };
|
||||
@@ -293,9 +532,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var texinfos = new List<BspTexinfo>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var texinfo = data.ReadType<BspTexinfo>();
|
||||
if (texinfo != null)
|
||||
texinfos.Add(texinfo);
|
||||
var texinfo = ParseBspTexinfo(data);
|
||||
texinfos.Add(texinfo);
|
||||
}
|
||||
|
||||
return new BspTexinfoLump { Texinfos = [.. texinfos] };
|
||||
@@ -311,9 +549,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var faces = new List<BspFace>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var face = data.ReadType<BspFace>();
|
||||
if (face != null)
|
||||
faces.Add(face);
|
||||
var face = ParseBspFace(data);
|
||||
faces.Add(face);
|
||||
}
|
||||
|
||||
return new BspFacesLump { Faces = [.. faces] };
|
||||
@@ -347,9 +584,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var clipnodes = new List<Clipnode>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var clipnode = data.ReadType<Clipnode>();
|
||||
if (clipnode != null)
|
||||
clipnodes.Add(clipnode);
|
||||
var clipnode = ParseClipnode(data);
|
||||
clipnodes.Add(clipnode);
|
||||
}
|
||||
|
||||
return new ClipnodesLump { Clipnodes = [.. clipnodes] };
|
||||
@@ -365,9 +601,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var leaves = new List<BspLeaf>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var leaf = data.ReadType<BspLeaf>();
|
||||
if (leaf != null)
|
||||
leaves.Add(leaf);
|
||||
var leaf = ParseBspLeaf(data);
|
||||
leaves.Add(leaf);
|
||||
}
|
||||
|
||||
return new BspLeavesLump { Leaves = [.. leaves] };
|
||||
@@ -383,7 +618,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var marksurfaces = new List<ushort>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
marksurfaces.Add(data.ReadUInt16());
|
||||
marksurfaces.Add(data.ReadUInt16LittleEndian());
|
||||
}
|
||||
|
||||
return new MarksurfacesLump { Marksurfaces = [.. marksurfaces] };
|
||||
@@ -399,9 +634,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var edges = new List<Edge>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var edge = data.ReadType<Edge>();
|
||||
if (edge != null)
|
||||
edges.Add(edge);
|
||||
var edge = ParseEdge(data);
|
||||
edges.Add(edge);
|
||||
}
|
||||
|
||||
return new EdgesLump { Edges = [.. edges] };
|
||||
@@ -417,7 +651,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var surfedges = new List<int>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
surfedges.Add(data.ReadInt32());
|
||||
surfedges.Add(data.ReadInt32LittleEndian());
|
||||
}
|
||||
|
||||
return new SurfedgesLump { Surfedges = [.. surfedges] };
|
||||
@@ -433,9 +667,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var models = new List<BspModel>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
var model = data.ReadType<BspModel>();
|
||||
if (model != null)
|
||||
models.Add(model);
|
||||
var model = ParseBspModel(data);
|
||||
models.Add(model);
|
||||
}
|
||||
|
||||
return new BspModelsLump { Models = [.. models] };
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.CFB;
|
||||
using static SabreTools.Models.CFB.Constants;
|
||||
@@ -26,7 +27,17 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the file header
|
||||
var fileHeader = ParseFileHeader(data);
|
||||
if (fileHeader == null)
|
||||
if (fileHeader?.Signature != SignatureUInt64)
|
||||
return null;
|
||||
if (fileHeader.ByteOrder != 0xFFFE)
|
||||
return null;
|
||||
if (fileHeader.MajorVersion == 3 && fileHeader.SectorShift != 0x0009)
|
||||
return null;
|
||||
else if (fileHeader.MajorVersion == 4 && fileHeader.SectorShift != 0x000C)
|
||||
return null;
|
||||
if (fileHeader.MajorVersion == 3 && fileHeader.NumberOfDirectorySectors != 0)
|
||||
return null;
|
||||
if (fileHeader.MiniStreamCutoffSize != 0x00001000)
|
||||
return null;
|
||||
|
||||
// Set the file header
|
||||
@@ -228,32 +239,69 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file header
|
||||
/// Parse a Stream into a DirectoryEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled file header on success, null on error</returns>
|
||||
private static FileHeader? ParseFileHeader(Stream data)
|
||||
/// <returns>Filled DirectoryEntry on success, null on error</returns>
|
||||
public static DirectoryEntry ParseDirectoryEntry(Stream data)
|
||||
{
|
||||
var header = data.ReadType<FileHeader>();
|
||||
var obj = new DirectoryEntry();
|
||||
|
||||
if (header?.Signature != SignatureUInt64)
|
||||
return null;
|
||||
if (header.ByteOrder != 0xFFFE)
|
||||
return null;
|
||||
if (header.MajorVersion == 3 && header.SectorShift != 0x0009)
|
||||
return null;
|
||||
else if (header.MajorVersion == 4 && header.SectorShift != 0x000C)
|
||||
return null;
|
||||
if (header.MajorVersion == 3 && header.NumberOfDirectorySectors != 0)
|
||||
return null;
|
||||
if (header.MiniStreamCutoffSize != 0x00001000)
|
||||
return null;
|
||||
byte[] name = data.ReadBytes(32);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
obj.NameLength = data.ReadUInt16LittleEndian();
|
||||
obj.ObjectType = (ObjectType)data.ReadByteValue();
|
||||
obj.ColorFlag = (ColorFlag)data.ReadByteValue();
|
||||
obj.LeftSiblingID = (StreamID)data.ReadUInt32LittleEndian();
|
||||
obj.RightSiblingID = (StreamID)data.ReadUInt32LittleEndian();
|
||||
obj.ChildID = (StreamID)data.ReadUInt32LittleEndian();
|
||||
obj.CLSID = data.ReadGuid();
|
||||
obj.StateBits = data.ReadUInt32LittleEndian();
|
||||
obj.CreationTime = data.ReadUInt64LittleEndian();
|
||||
obj.ModifiedTime = data.ReadUInt64LittleEndian();
|
||||
obj.StartingSectorLocation = data.ReadUInt32LittleEndian();
|
||||
obj.StreamSize = data.ReadUInt64LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FileHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FileHeader on success, null on error</returns>
|
||||
public static FileHeader ParseFileHeader(Stream data)
|
||||
{
|
||||
var obj = new FileHeader();
|
||||
|
||||
obj.Signature = data.ReadUInt64LittleEndian();
|
||||
obj.CLSID = data.ReadGuid();
|
||||
obj.MinorVersion = data.ReadUInt16LittleEndian();
|
||||
obj.MajorVersion = data.ReadUInt16LittleEndian();
|
||||
obj.ByteOrder = data.ReadUInt16LittleEndian();
|
||||
obj.SectorShift = data.ReadUInt16LittleEndian();
|
||||
obj.MiniSectorShift = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved = data.ReadBytes(6);
|
||||
obj.NumberOfDirectorySectors = data.ReadUInt32LittleEndian();
|
||||
obj.NumberOfFATSectors = data.ReadUInt32LittleEndian();
|
||||
obj.FirstDirectorySectorLocation = data.ReadUInt32LittleEndian();
|
||||
obj.TransactionSignatureNumber = data.ReadUInt32LittleEndian();
|
||||
obj.MiniStreamCutoffSize = data.ReadUInt32LittleEndian();
|
||||
obj.FirstMiniFATSectorLocation = data.ReadUInt32LittleEndian();
|
||||
obj.NumberOfMiniFATSectors = data.ReadUInt32LittleEndian();
|
||||
obj.FirstDIFATSectorLocation = data.ReadUInt32LittleEndian();
|
||||
obj.NumberOfDIFATSectors = data.ReadUInt32LittleEndian();
|
||||
obj.DIFAT = new SectorNumber[109];
|
||||
for (int i = 0; i < 109; i++)
|
||||
{
|
||||
obj.DIFAT[i] = (SectorNumber)data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
// Skip rest of sector for version 4
|
||||
if (header.MajorVersion == 4)
|
||||
if (obj.MajorVersion == 4)
|
||||
_ = data.ReadBytes(3584);
|
||||
|
||||
return header;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -269,7 +317,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
for (int i = 0; i < sectorNumbers.Length; i++)
|
||||
{
|
||||
sectorNumbers[i] = (SectorNumber)data.ReadUInt32();
|
||||
sectorNumbers[i] = (SectorNumber)data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
return sectorNumbers;
|
||||
@@ -294,9 +342,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
for (int i = 0; i < directoryEntries.Length; i++)
|
||||
{
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
return null;
|
||||
var directoryEntry = ParseDirectoryEntry(data);
|
||||
|
||||
// Handle version 3 entries
|
||||
if (majorVersion == 3)
|
||||
|
||||
@@ -22,15 +22,70 @@ namespace SabreTools.Serialization.Deserializers
|
||||
uint version = GetVersion(data);
|
||||
|
||||
// Read and return the current CHD
|
||||
return version switch
|
||||
switch (version)
|
||||
{
|
||||
1 => ParseHeaderV1(data),
|
||||
2 => ParseHeaderV2(data),
|
||||
3 => ParseHeaderV3(data),
|
||||
4 => ParseHeaderV4(data),
|
||||
5 => ParseHeaderV5(data),
|
||||
_ => null,
|
||||
};
|
||||
case 1:
|
||||
var headerV1 = ParseHeaderV1(data);
|
||||
|
||||
if (headerV1.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (headerV1.Length != Constants.HeaderV1Size)
|
||||
return null;
|
||||
if (headerV1.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
|
||||
return null;
|
||||
|
||||
return headerV1;
|
||||
|
||||
case 2:
|
||||
var headerV2 = ParseHeaderV2(data);
|
||||
|
||||
if (headerV2.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (headerV2.Length != Constants.HeaderV2Size)
|
||||
return null;
|
||||
if (headerV2.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
|
||||
return null;
|
||||
|
||||
return headerV2;
|
||||
|
||||
case 3:
|
||||
var headerV3 = ParseHeaderV3(data);
|
||||
|
||||
if (headerV3.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (headerV3.Length != Constants.HeaderV3Size)
|
||||
return null;
|
||||
if (headerV3.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS)
|
||||
return null;
|
||||
|
||||
return headerV3;
|
||||
|
||||
case 4:
|
||||
var headerV4 = ParseHeaderV1(data);
|
||||
|
||||
if (headerV4.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (headerV4.Length != Constants.HeaderV4Size)
|
||||
return null;
|
||||
if (headerV4.Compression > CompressionType.CHDCOMPRESSION_AV)
|
||||
return null;
|
||||
|
||||
return headerV4;
|
||||
|
||||
case 5:
|
||||
var headerV5 = ParseHeaderV1(data);
|
||||
|
||||
if (headerV5.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (headerV5.Length != Constants.HeaderV5Size)
|
||||
return null;
|
||||
|
||||
return headerV5;
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -83,81 +138,129 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a V1 header
|
||||
/// Parse a Stream into a HeaderV1
|
||||
/// </summary>
|
||||
private static HeaderV1? ParseHeaderV1(Stream data)
|
||||
public static HeaderV1 ParseHeaderV1(Stream data)
|
||||
{
|
||||
var header = data.ReadType<HeaderV1>();
|
||||
if (header?.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (header.Length != Constants.HeaderV1Size)
|
||||
return null;
|
||||
if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
|
||||
return null;
|
||||
var obj = new HeaderV1();
|
||||
|
||||
return header;
|
||||
byte[] tag = data.ReadBytes(8);
|
||||
obj.Tag = Encoding.ASCII.GetString(tag);
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (Flags)data.ReadUInt32LittleEndian();
|
||||
obj.Compression = (CompressionType)data.ReadUInt32LittleEndian();
|
||||
obj.HunkSize = data.ReadUInt32LittleEndian();
|
||||
obj.TotalHunks = data.ReadUInt32LittleEndian();
|
||||
obj.Cylinders = data.ReadUInt32LittleEndian();
|
||||
obj.Heads = data.ReadUInt32LittleEndian();
|
||||
obj.Sectors = data.ReadUInt32LittleEndian();
|
||||
obj.MD5 = data.ReadBytes(16);
|
||||
obj.ParentMD5 = data.ReadBytes(16);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a V2 header
|
||||
/// </summary>
|
||||
private static HeaderV2? ParseHeaderV2(Stream data)
|
||||
public static HeaderV2 ParseHeaderV2(Stream data)
|
||||
{
|
||||
var header = data.ReadType<HeaderV2>();
|
||||
if (header?.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (header.Length != Constants.HeaderV2Size)
|
||||
return null;
|
||||
if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
|
||||
return null;
|
||||
var obj = new HeaderV2();
|
||||
|
||||
return header;
|
||||
byte[] tag = data.ReadBytes(8);
|
||||
obj.Tag = Encoding.ASCII.GetString(tag);
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (Flags)data.ReadUInt32LittleEndian();
|
||||
obj.Compression = (CompressionType)data.ReadUInt32LittleEndian();
|
||||
obj.HunkSize = data.ReadUInt32LittleEndian();
|
||||
obj.TotalHunks = data.ReadUInt32LittleEndian();
|
||||
obj.Cylinders = data.ReadUInt32LittleEndian();
|
||||
obj.Heads = data.ReadUInt32LittleEndian();
|
||||
obj.Sectors = data.ReadUInt32LittleEndian();
|
||||
obj.MD5 = data.ReadBytes(16);
|
||||
obj.ParentMD5 = data.ReadBytes(16);
|
||||
obj.BytesPerSector = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a V3 header
|
||||
/// </summary>
|
||||
private static HeaderV3? ParseHeaderV3(Stream data)
|
||||
public static HeaderV3 ParseHeaderV3(Stream data)
|
||||
{
|
||||
var header = data.ReadType<HeaderV3>();
|
||||
if (header?.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (header.Length != Constants.HeaderV3Size)
|
||||
return null;
|
||||
if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS)
|
||||
return null;
|
||||
var obj = new HeaderV3();
|
||||
|
||||
return header;
|
||||
byte[] tag = data.ReadBytes(8);
|
||||
obj.Tag = Encoding.ASCII.GetString(tag);
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (Flags)data.ReadUInt32LittleEndian();
|
||||
obj.Compression = (CompressionType)data.ReadUInt32LittleEndian();
|
||||
obj.TotalHunks = data.ReadUInt32LittleEndian();
|
||||
obj.LogicalBytes = data.ReadUInt64LittleEndian();
|
||||
obj.MetaOffset = data.ReadUInt64LittleEndian();
|
||||
obj.MD5 = data.ReadBytes(16);
|
||||
obj.ParentMD5 = data.ReadBytes(16);
|
||||
obj.HunkBytes = data.ReadUInt32LittleEndian();
|
||||
obj.SHA1 = data.ReadBytes(20);
|
||||
obj.ParentSHA1 = data.ReadBytes(20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a V4 header
|
||||
/// </summary>
|
||||
private static HeaderV4? ParseHeaderV4(Stream data)
|
||||
public static HeaderV4? ParseHeaderV4(Stream data)
|
||||
{
|
||||
var header = data.ReadType<HeaderV4>();
|
||||
if (header?.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (header.Length != Constants.HeaderV4Size)
|
||||
return null;
|
||||
if (header.Compression > CompressionType.CHDCOMPRESSION_AV)
|
||||
return null;
|
||||
var obj = new HeaderV4();
|
||||
|
||||
return header;
|
||||
byte[] tag = data.ReadBytes(8);
|
||||
obj.Tag = Encoding.ASCII.GetString(tag);
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (Flags)data.ReadUInt32LittleEndian();
|
||||
obj.Compression = (CompressionType)data.ReadUInt32LittleEndian();
|
||||
obj.TotalHunks = data.ReadUInt32LittleEndian();
|
||||
obj.LogicalBytes = data.ReadUInt64LittleEndian();
|
||||
obj.MetaOffset = data.ReadUInt64LittleEndian();
|
||||
obj.HunkBytes = data.ReadUInt32LittleEndian();
|
||||
obj.SHA1 = data.ReadBytes(20);
|
||||
obj.ParentSHA1 = data.ReadBytes(20);
|
||||
obj.RawSHA1 = data.ReadBytes(20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a V5 header
|
||||
/// </summary>
|
||||
private static HeaderV5? ParseHeaderV5(Stream data)
|
||||
public static HeaderV5 ParseHeaderV5(Stream data)
|
||||
{
|
||||
var header = data.ReadType<HeaderV5>();
|
||||
if (header?.Tag != Constants.SignatureString)
|
||||
return null;
|
||||
if (header.Length != Constants.HeaderV5Size)
|
||||
return null;
|
||||
var obj = new HeaderV5();
|
||||
|
||||
return header;
|
||||
byte[] tag = data.ReadBytes(8);
|
||||
obj.Tag = Encoding.ASCII.GetString(tag);
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.Compressors = new CodecType[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
obj.Compressors[i] = (CodecType)data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.LogicalBytes = data.ReadUInt64LittleEndian();
|
||||
obj.MapOffset = data.ReadUInt64LittleEndian();
|
||||
obj.MetaOffset = data.ReadUInt64LittleEndian();
|
||||
obj.HunkBytes = data.ReadUInt32LittleEndian();
|
||||
obj.UnitBytes = data.ReadUInt32LittleEndian();
|
||||
obj.RawSHA1 = data.ReadBytes(20);
|
||||
obj.SHA1 = data.ReadBytes(20);
|
||||
obj.ParentSHA1 = data.ReadBytes(20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region CIA Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<CIAHeader>();
|
||||
if (header == null)
|
||||
return null;
|
||||
var header = ParseCIAHeader(data);
|
||||
if (header.CertificateChainSize > data.Length)
|
||||
return null;
|
||||
if (header.TicketSize > data.Length)
|
||||
@@ -119,15 +117,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// If we have a meta data
|
||||
if (header.MetaSize > 0)
|
||||
{
|
||||
// Try to parse the meta
|
||||
var meta = data.ReadType<MetaData>();
|
||||
if (meta == null)
|
||||
return null;
|
||||
|
||||
// Set the meta
|
||||
cia.MetaData = meta;
|
||||
}
|
||||
cia.MetaData = ParseMetaData(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -141,161 +131,235 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a certificate
|
||||
/// Parse a Stream into a Certificate
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled certificate on success, null on error</returns>
|
||||
/// <returns>Filled Certificate on success, null on error</returns>
|
||||
public static Certificate? ParseCertificate(Stream data)
|
||||
{
|
||||
var certificate = new Certificate();
|
||||
var obj = new Certificate();
|
||||
|
||||
certificate.SignatureType = (SignatureType)data.ReadUInt32();
|
||||
switch (certificate.SignatureType)
|
||||
obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian();
|
||||
switch (obj.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
certificate.SignatureSize = 0x200;
|
||||
certificate.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x200;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
certificate.SignatureSize = 0x100;
|
||||
certificate.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x100;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
certificate.SignatureSize = 0x3C;
|
||||
certificate.PaddingSize = 0x40;
|
||||
obj.SignatureSize = 0x3C;
|
||||
obj.PaddingSize = 0x40;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
certificate.Signature = data.ReadBytes(certificate.SignatureSize);
|
||||
certificate.Padding = data.ReadBytes(certificate.PaddingSize);
|
||||
obj.Signature = data.ReadBytes(obj.SignatureSize);
|
||||
obj.Padding = data.ReadBytes(obj.PaddingSize);
|
||||
byte[] issuer = data.ReadBytes(0x40);
|
||||
certificate.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
certificate.KeyType = (PublicKeyType)data.ReadUInt32();
|
||||
obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
obj.KeyType = (PublicKeyType)data.ReadUInt32LittleEndian();
|
||||
byte[] name = data.ReadBytes(0x40);
|
||||
certificate.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
certificate.ExpirationTime = data.ReadUInt32();
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
obj.ExpirationTime = data.ReadUInt32LittleEndian();
|
||||
|
||||
switch (certificate.KeyType)
|
||||
switch (obj.KeyType)
|
||||
{
|
||||
case PublicKeyType.RSA_4096:
|
||||
certificate.RSAModulus = data.ReadBytes(0x200);
|
||||
certificate.RSAPublicExponent = data.ReadUInt32();
|
||||
certificate.RSAPadding = data.ReadBytes(0x34);
|
||||
obj.RSAModulus = data.ReadBytes(0x200);
|
||||
obj.RSAPublicExponent = data.ReadUInt32LittleEndian();
|
||||
obj.RSAPadding = data.ReadBytes(0x34);
|
||||
break;
|
||||
case PublicKeyType.RSA_2048:
|
||||
certificate.RSAModulus = data.ReadBytes(0x100);
|
||||
certificate.RSAPublicExponent = data.ReadUInt32();
|
||||
certificate.RSAPadding = data.ReadBytes(0x34);
|
||||
obj.RSAModulus = data.ReadBytes(0x100);
|
||||
obj.RSAPublicExponent = data.ReadUInt32LittleEndian();
|
||||
obj.RSAPadding = data.ReadBytes(0x34);
|
||||
break;
|
||||
case PublicKeyType.EllipticCurve:
|
||||
certificate.ECCPublicKey = data.ReadBytes(0x3C);
|
||||
certificate.ECCPadding = data.ReadBytes(0x3C);
|
||||
obj.ECCPublicKey = data.ReadBytes(0x3C);
|
||||
obj.ECCPadding = data.ReadBytes(0x3C);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return certificate;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ticket
|
||||
/// Parse a Stream into a CIAHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CIAHeader on success, null on error</returns>
|
||||
public static CIAHeader ParseCIAHeader(Stream data)
|
||||
{
|
||||
var obj = new CIAHeader();
|
||||
|
||||
obj.HeaderSize = data.ReadUInt32LittleEndian();
|
||||
obj.Type = data.ReadUInt16LittleEndian();
|
||||
obj.Version = data.ReadUInt16LittleEndian();
|
||||
obj.CertificateChainSize = data.ReadUInt32LittleEndian();
|
||||
obj.TicketSize = data.ReadUInt32LittleEndian();
|
||||
obj.TMDFileSize = data.ReadUInt32LittleEndian();
|
||||
obj.MetaSize = data.ReadUInt32LittleEndian();
|
||||
obj.ContentSize = data.ReadUInt64LittleEndian();
|
||||
obj.ContentIndex = data.ReadBytes(0x2000);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ContentChunkRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ContentChunkRecord on success, null on error</returns>
|
||||
public static ContentChunkRecord ParseContentChunkRecord(Stream data)
|
||||
{
|
||||
var obj = new ContentChunkRecord();
|
||||
|
||||
obj.ContentId = data.ReadUInt32LittleEndian();
|
||||
obj.ContentIndex = (ContentIndex)data.ReadUInt16LittleEndian();
|
||||
obj.ContentType = (TMDContentType)data.ReadUInt16LittleEndian();
|
||||
obj.ContentSize = data.ReadUInt64LittleEndian();
|
||||
obj.SHA256Hash = data.ReadBytes(0x20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ContentInfoRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ContentInfoRecord on success, null on error</returns>
|
||||
public static ContentInfoRecord ParseContentInfoRecord(Stream data)
|
||||
{
|
||||
var obj = new ContentInfoRecord();
|
||||
|
||||
obj.ContentIndexOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ContentCommandCount = data.ReadUInt16LittleEndian();
|
||||
obj.UnhashedContentRecordsSHA256Hash = data.ReadBytes(0x20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a MetaData
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled MetaData on success, null on error</returns>
|
||||
public static MetaData ParseMetaData(Stream data)
|
||||
{
|
||||
var obj = new MetaData();
|
||||
|
||||
obj.TitleIDDependencyList = data.ReadBytes(0x180);
|
||||
obj.Reserved1 = data.ReadBytes(0x180);
|
||||
obj.CoreVersion = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(0xFC);
|
||||
obj.IconData = data.ReadBytes(0x36C0);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Ticket
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="fromCdn">Indicates if the ticket is from CDN</param>
|
||||
/// <returns>Filled ticket on success, null on error</returns>
|
||||
/// <returns>Filled Ticket on success, null on error</returns>
|
||||
public static Ticket? ParseTicket(Stream data, bool fromCdn = false)
|
||||
{
|
||||
var ticket = new Ticket();
|
||||
var obj = new Ticket();
|
||||
|
||||
ticket.SignatureType = (SignatureType)data.ReadUInt32();
|
||||
switch (ticket.SignatureType)
|
||||
obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian();
|
||||
switch (obj.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
ticket.SignatureSize = 0x200;
|
||||
ticket.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x200;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
ticket.SignatureSize = 0x100;
|
||||
ticket.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x100;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
ticket.SignatureSize = 0x3C;
|
||||
ticket.PaddingSize = 0x40;
|
||||
obj.SignatureSize = 0x3C;
|
||||
obj.PaddingSize = 0x40;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
ticket.Signature = data.ReadBytes(ticket.SignatureSize);
|
||||
ticket.Padding = data.ReadBytes(ticket.PaddingSize);
|
||||
obj.Signature = data.ReadBytes(obj.SignatureSize);
|
||||
obj.Padding = data.ReadBytes(obj.PaddingSize);
|
||||
byte[] issuer = data.ReadBytes(0x40);
|
||||
ticket.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
ticket.ECCPublicKey = data.ReadBytes(0x3C);
|
||||
ticket.Version = data.ReadByteValue();
|
||||
ticket.CaCrlVersion = data.ReadByteValue();
|
||||
ticket.SignerCrlVersion = data.ReadByteValue();
|
||||
ticket.TitleKey = data.ReadBytes(0x10);
|
||||
ticket.Reserved1 = data.ReadByteValue();
|
||||
ticket.TicketID = data.ReadUInt64();
|
||||
ticket.ConsoleID = data.ReadUInt32();
|
||||
ticket.TitleID = data.ReadUInt64();
|
||||
ticket.Reserved2 = data.ReadBytes(2);
|
||||
ticket.TicketTitleVersion = data.ReadUInt16();
|
||||
ticket.Reserved3 = data.ReadBytes(8);
|
||||
ticket.LicenseType = data.ReadByteValue();
|
||||
ticket.CommonKeyYIndex = data.ReadByteValue();
|
||||
ticket.Reserved4 = data.ReadBytes(0x2A);
|
||||
ticket.eShopAccountID = data.ReadUInt32();
|
||||
ticket.Reserved5 = data.ReadByteValue();
|
||||
ticket.Audit = data.ReadByteValue();
|
||||
ticket.Reserved6 = data.ReadBytes(0x42);
|
||||
ticket.Limits = new uint[0x10];
|
||||
for (int i = 0; i < ticket.Limits.Length; i++)
|
||||
obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
obj.ECCPublicKey = data.ReadBytes(0x3C);
|
||||
obj.Version = data.ReadByteValue();
|
||||
obj.CaCrlVersion = data.ReadByteValue();
|
||||
obj.SignerCrlVersion = data.ReadByteValue();
|
||||
obj.TitleKey = data.ReadBytes(0x10);
|
||||
obj.Reserved1 = data.ReadByteValue();
|
||||
obj.TicketID = data.ReadUInt64LittleEndian();
|
||||
obj.ConsoleID = data.ReadUInt32LittleEndian();
|
||||
obj.TitleID = data.ReadUInt64LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(2);
|
||||
obj.TicketTitleVersion = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved3 = data.ReadBytes(8);
|
||||
obj.LicenseType = data.ReadByteValue();
|
||||
obj.CommonKeyYIndex = data.ReadByteValue();
|
||||
obj.Reserved4 = data.ReadBytes(0x2A);
|
||||
obj.eShopAccountID = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved5 = data.ReadByteValue();
|
||||
obj.Audit = data.ReadByteValue();
|
||||
obj.Reserved6 = data.ReadBytes(0x42);
|
||||
obj.Limits = new uint[0x10];
|
||||
for (int i = 0; i < obj.Limits.Length; i++)
|
||||
{
|
||||
ticket.Limits[i] = data.ReadUInt32();
|
||||
obj.Limits[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
// Seek to the content index size
|
||||
data.Seek(4, SeekOrigin.Current);
|
||||
|
||||
// Read the size (big-endian)
|
||||
ticket.ContentIndexSize = data.ReadUInt32BigEndian();
|
||||
obj.ContentIndexSize = data.ReadUInt32BigEndian();
|
||||
|
||||
// Seek back to the start of the content index
|
||||
data.Seek(-8, SeekOrigin.Current);
|
||||
|
||||
ticket.ContentIndex = data.ReadBytes((int)ticket.ContentIndexSize);
|
||||
obj.ContentIndex = data.ReadBytes((int)obj.ContentIndexSize);
|
||||
|
||||
// Certificates only exist in standalone CETK files
|
||||
if (fromCdn)
|
||||
{
|
||||
ticket.CertificateChain = new Certificate[2];
|
||||
obj.CertificateChain = new Certificate[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var certificate = ParseCertificate(data);
|
||||
if (certificate == null)
|
||||
return null;
|
||||
|
||||
ticket.CertificateChain[i] = certificate;
|
||||
obj.CertificateChain[i] = certificate;
|
||||
}
|
||||
}
|
||||
|
||||
return ticket;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -306,90 +370,84 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <returns>Filled title metadata on success, null on error</returns>
|
||||
public static TitleMetadata? ParseTitleMetadata(Stream data, bool fromCdn = false)
|
||||
{
|
||||
var titleMetadata = new TitleMetadata();
|
||||
var obj = new TitleMetadata();
|
||||
|
||||
titleMetadata.SignatureType = (SignatureType)data.ReadUInt32();
|
||||
switch (titleMetadata.SignatureType)
|
||||
obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian();
|
||||
switch (obj.SignatureType)
|
||||
{
|
||||
case SignatureType.RSA_4096_SHA1:
|
||||
case SignatureType.RSA_4096_SHA256:
|
||||
titleMetadata.SignatureSize = 0x200;
|
||||
titleMetadata.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x200;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.RSA_2048_SHA1:
|
||||
case SignatureType.RSA_2048_SHA256:
|
||||
titleMetadata.SignatureSize = 0x100;
|
||||
titleMetadata.PaddingSize = 0x3C;
|
||||
obj.SignatureSize = 0x100;
|
||||
obj.PaddingSize = 0x3C;
|
||||
break;
|
||||
|
||||
case SignatureType.ECDSA_SHA1:
|
||||
case SignatureType.ECDSA_SHA256:
|
||||
titleMetadata.SignatureSize = 0x3C;
|
||||
titleMetadata.PaddingSize = 0x40;
|
||||
obj.SignatureSize = 0x3C;
|
||||
obj.PaddingSize = 0x40;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
titleMetadata.Signature = data.ReadBytes(titleMetadata.SignatureSize);
|
||||
titleMetadata.Padding1 = data.ReadBytes(titleMetadata.PaddingSize);
|
||||
obj.Signature = data.ReadBytes(obj.SignatureSize);
|
||||
obj.Padding1 = data.ReadBytes(obj.PaddingSize);
|
||||
byte[] issuer = data.ReadBytes(0x40);
|
||||
titleMetadata.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
titleMetadata.Version = data.ReadByteValue();
|
||||
titleMetadata.CaCrlVersion = data.ReadByteValue();
|
||||
titleMetadata.SignerCrlVersion = data.ReadByteValue();
|
||||
titleMetadata.Reserved1 = data.ReadByteValue();
|
||||
titleMetadata.SystemVersion = data.ReadUInt64();
|
||||
titleMetadata.TitleID = data.ReadUInt64();
|
||||
titleMetadata.TitleType = data.ReadUInt32();
|
||||
titleMetadata.GroupID = data.ReadUInt16();
|
||||
titleMetadata.SaveDataSize = data.ReadUInt32();
|
||||
titleMetadata.SRLPrivateSaveDataSize = data.ReadUInt32();
|
||||
titleMetadata.Reserved2 = data.ReadBytes(4);
|
||||
titleMetadata.SRLFlag = data.ReadByteValue();
|
||||
titleMetadata.Reserved3 = data.ReadBytes(0x31);
|
||||
titleMetadata.AccessRights = data.ReadUInt32();
|
||||
titleMetadata.TitleVersion = data.ReadUInt16();
|
||||
titleMetadata.ContentCount = data.ReadUInt16BigEndian();
|
||||
titleMetadata.BootContent = data.ReadUInt16();
|
||||
titleMetadata.Padding2 = data.ReadBytes(2);
|
||||
titleMetadata.SHA256HashContentInfoRecords = data.ReadBytes(0x20);
|
||||
titleMetadata.ContentInfoRecords = new ContentInfoRecord[64];
|
||||
obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
|
||||
obj.Version = data.ReadByteValue();
|
||||
obj.CaCrlVersion = data.ReadByteValue();
|
||||
obj.SignerCrlVersion = data.ReadByteValue();
|
||||
obj.Reserved1 = data.ReadByteValue();
|
||||
obj.SystemVersion = data.ReadUInt64LittleEndian();
|
||||
obj.TitleID = data.ReadUInt64LittleEndian();
|
||||
obj.TitleType = data.ReadUInt32LittleEndian();
|
||||
obj.GroupID = data.ReadUInt16LittleEndian();
|
||||
obj.SaveDataSize = data.ReadUInt32LittleEndian();
|
||||
obj.SRLPrivateSaveDataSize = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(4);
|
||||
obj.SRLFlag = data.ReadByteValue();
|
||||
obj.Reserved3 = data.ReadBytes(0x31);
|
||||
obj.AccessRights = data.ReadUInt32LittleEndian();
|
||||
obj.TitleVersion = data.ReadUInt16LittleEndian();
|
||||
obj.ContentCount = data.ReadUInt16BigEndian();
|
||||
obj.BootContent = data.ReadUInt16LittleEndian();
|
||||
obj.Padding2 = data.ReadBytes(2);
|
||||
obj.SHA256HashContentInfoRecords = data.ReadBytes(0x20);
|
||||
obj.ContentInfoRecords = new ContentInfoRecord[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
var contentInfoRecord = data.ReadType<ContentInfoRecord>();
|
||||
if (contentInfoRecord == null)
|
||||
return null;
|
||||
|
||||
titleMetadata.ContentInfoRecords[i] = contentInfoRecord;
|
||||
var contentInfoRecord = ParseContentInfoRecord(data);
|
||||
obj.ContentInfoRecords[i] = contentInfoRecord;
|
||||
}
|
||||
titleMetadata.ContentChunkRecords = new ContentChunkRecord[titleMetadata.ContentCount];
|
||||
for (int i = 0; i < titleMetadata.ContentCount; i++)
|
||||
obj.ContentChunkRecords = new ContentChunkRecord[obj.ContentCount];
|
||||
for (int i = 0; i < obj.ContentCount; i++)
|
||||
{
|
||||
var contentChunkRecord = data.ReadType<ContentChunkRecord>();
|
||||
if (contentChunkRecord == null)
|
||||
return null;
|
||||
|
||||
titleMetadata.ContentChunkRecords[i] = contentChunkRecord;
|
||||
var contentChunkRecord = ParseContentChunkRecord(data);
|
||||
obj.ContentChunkRecords[i] = contentChunkRecord;
|
||||
}
|
||||
|
||||
// Certificates only exist in standalone TMD files
|
||||
if (fromCdn)
|
||||
{
|
||||
titleMetadata.CertificateChain = new Certificate[2];
|
||||
obj.CertificateChain = new Certificate[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var certificate = ParseCertificate(data);
|
||||
if (certificate == null)
|
||||
return null;
|
||||
|
||||
titleMetadata.CertificateChain[i] = certificate;
|
||||
obj.CertificateChain[i] = certificate;
|
||||
}
|
||||
}
|
||||
|
||||
return titleMetadata;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Dummy0 != 0x00000001)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
if (header?.MajorVersion != 0x00000001)
|
||||
if (header.MajorVersion != 0x00000001)
|
||||
return null;
|
||||
if (header.MinorVersion != 3 && header.MinorVersion != 5 && header.MinorVersion != 6)
|
||||
return null;
|
||||
@@ -37,74 +37,49 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Block Entry Header
|
||||
|
||||
// Try to parse the block entry header
|
||||
var blockEntryHeader = data.ReadType<BlockEntryHeader>();
|
||||
if (blockEntryHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the game cache block entry header
|
||||
file.BlockEntryHeader = blockEntryHeader;
|
||||
file.BlockEntryHeader = ParseBlockEntryHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Block Entries
|
||||
|
||||
// Create the block entry array
|
||||
file.BlockEntries = new BlockEntry[blockEntryHeader.BlockCount];
|
||||
file.BlockEntries = new BlockEntry[file.BlockEntryHeader.BlockCount];
|
||||
|
||||
// Try to parse the block entries
|
||||
for (int i = 0; i < blockEntryHeader.BlockCount; i++)
|
||||
for (int i = 0; i < file.BlockEntryHeader.BlockCount; i++)
|
||||
{
|
||||
var blockEntry = data.ReadType<BlockEntry>();
|
||||
if (blockEntry == null)
|
||||
return null;
|
||||
|
||||
file.BlockEntries[i] = blockEntry;
|
||||
file.BlockEntries[i] = ParseBlockEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fragmentation Map Header
|
||||
|
||||
// Try to parse the fragmentation map header
|
||||
var fragmentationMapHeader = data.ReadType<FragmentationMapHeader>();
|
||||
if (fragmentationMapHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the game cache fragmentation map header
|
||||
file.FragmentationMapHeader = fragmentationMapHeader;
|
||||
file.FragmentationMapHeader = ParseFragmentationMapHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fragmentation Maps
|
||||
|
||||
// Create the fragmentation map array
|
||||
file.FragmentationMaps = new FragmentationMap[fragmentationMapHeader.BlockCount];
|
||||
file.FragmentationMaps = new FragmentationMap[file.FragmentationMapHeader.BlockCount];
|
||||
|
||||
// Try to parse the fragmentation maps
|
||||
for (int i = 0; i < fragmentationMapHeader.BlockCount; i++)
|
||||
for (int i = 0; i < file.FragmentationMapHeader.BlockCount; i++)
|
||||
{
|
||||
var fragmentationMap = data.ReadType<FragmentationMap>();
|
||||
if (fragmentationMap == null)
|
||||
return null;
|
||||
|
||||
file.FragmentationMaps[i] = fragmentationMap;
|
||||
file.FragmentationMaps[i] = ParseFragmentationMap(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Block Entry Map Header
|
||||
|
||||
// Set the game cache block entry map header
|
||||
if (header.MinorVersion < 6)
|
||||
{
|
||||
// Try to parse the block entry map header
|
||||
var blockEntryMapHeader = data.ReadType<BlockEntryMapHeader>();
|
||||
if (blockEntryMapHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the game cache block entry map header
|
||||
file.BlockEntryMapHeader = blockEntryMapHeader;
|
||||
}
|
||||
file.BlockEntryMapHeader = ParseBlockEntryMapHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -118,11 +93,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the block entry maps
|
||||
for (int i = 0; i < file.BlockEntryMapHeader.BlockCount; i++)
|
||||
{
|
||||
var blockEntryMap = data.ReadType<BlockEntryMap>();
|
||||
if (blockEntryMap == null)
|
||||
return null;
|
||||
|
||||
file.BlockEntryMaps[i] = blockEntryMap;
|
||||
file.BlockEntryMaps[i] = ParseBlockEntryMap(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,42 +104,33 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Directory Header
|
||||
|
||||
// Try to parse the directory header
|
||||
var directoryHeader = data.ReadType<DirectoryHeader>();
|
||||
if (directoryHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the game cache directory header
|
||||
file.DirectoryHeader = directoryHeader;
|
||||
file.DirectoryHeader = ParseDirectoryHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Directory Entries
|
||||
|
||||
// Create the directory entry array
|
||||
file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount];
|
||||
file.DirectoryEntries = new DirectoryEntry[file.DirectoryHeader.ItemCount];
|
||||
|
||||
// Try to parse the directory entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.ItemCount; i++)
|
||||
{
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryEntries[i] = directoryEntry;
|
||||
file.DirectoryEntries[i] = ParseDirectoryEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Directory Names
|
||||
|
||||
if (directoryHeader.NameSize > 0)
|
||||
if (file.DirectoryHeader.NameSize > 0)
|
||||
{
|
||||
// Get the current offset for adjustment
|
||||
long directoryNamesStart = data.Position;
|
||||
|
||||
// Get the ending offset
|
||||
long directoryNamesEnd = data.Position + directoryHeader.NameSize;
|
||||
long directoryNamesEnd = data.Position + file.DirectoryHeader.NameSize;
|
||||
|
||||
// Create the string dictionary
|
||||
file.DirectoryNames = [];
|
||||
@@ -194,16 +156,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Info 1 Entries
|
||||
|
||||
// Create the directory info 1 entry array
|
||||
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count];
|
||||
file.DirectoryInfo1Entries = new DirectoryInfo1Entry[file.DirectoryHeader.Info1Count];
|
||||
|
||||
// Try to parse the directory info 1 entries
|
||||
for (int i = 0; i < directoryHeader.Info1Count; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.Info1Count; i++)
|
||||
{
|
||||
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
|
||||
if (directoryInfo1Entry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
|
||||
file.DirectoryInfo1Entries[i] = ParseDirectoryInfo1Entry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -211,16 +169,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Info 2 Entries
|
||||
|
||||
// Create the directory info 2 entry array
|
||||
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount];
|
||||
file.DirectoryInfo2Entries = new DirectoryInfo2Entry[file.DirectoryHeader.ItemCount];
|
||||
|
||||
// Try to parse the directory info 2 entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.ItemCount; i++)
|
||||
{
|
||||
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
|
||||
if (directoryInfo2Entry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
|
||||
file.DirectoryInfo2Entries[i] = ParseDirectoryInfo2Entry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -228,16 +182,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Copy Entries
|
||||
|
||||
// Create the directory copy entry array
|
||||
file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount];
|
||||
file.DirectoryCopyEntries = new DirectoryCopyEntry[file.DirectoryHeader.CopyCount];
|
||||
|
||||
// Try to parse the directory copy entries
|
||||
for (int i = 0; i < directoryHeader.CopyCount; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.CopyCount; i++)
|
||||
{
|
||||
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
|
||||
if (directoryCopyEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryCopyEntries[i] = directoryCopyEntry;
|
||||
file.DirectoryCopyEntries[i] = ParseDirectoryCopyEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -245,32 +195,28 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Local Entries
|
||||
|
||||
// Create the directory local entry array
|
||||
file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount];
|
||||
file.DirectoryLocalEntries = new DirectoryLocalEntry[file.DirectoryHeader.LocalCount];
|
||||
|
||||
// Try to parse the directory local entries
|
||||
for (int i = 0; i < directoryHeader.LocalCount; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.LocalCount; i++)
|
||||
{
|
||||
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
|
||||
if (directoryLocalEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryLocalEntries[i] = directoryLocalEntry;
|
||||
file.DirectoryLocalEntries[i] = ParseDirectoryLocalEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Seek to end of directory section, just in case
|
||||
data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin);
|
||||
data.Seek(initialOffset + file.DirectoryHeader.DirectorySize, SeekOrigin.Begin);
|
||||
|
||||
#region Directory Map Header
|
||||
|
||||
if (header.MinorVersion >= 5)
|
||||
{
|
||||
// Try to parse the directory map header
|
||||
var directoryMapHeader = data.ReadType<DirectoryMapHeader>();
|
||||
if (directoryMapHeader?.Dummy0 != 0x00000001)
|
||||
var directoryMapHeader = ParseDirectoryMapHeader(data);
|
||||
if (directoryMapHeader.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
if (directoryMapHeader?.Dummy1 != 0x00000000)
|
||||
if (directoryMapHeader.Dummy1 != 0x00000000)
|
||||
return null;
|
||||
|
||||
// Set the game cache directory map header
|
||||
@@ -282,16 +228,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Map Entries
|
||||
|
||||
// Create the directory map entry array
|
||||
file.DirectoryMapEntries = new DirectoryMapEntry[directoryHeader.ItemCount];
|
||||
file.DirectoryMapEntries = new DirectoryMapEntry[file.DirectoryHeader.ItemCount];
|
||||
|
||||
// Try to parse the directory map entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
for (int i = 0; i < file.DirectoryHeader.ItemCount; i++)
|
||||
{
|
||||
var directoryMapEntry = data.ReadType<DirectoryMapEntry>();
|
||||
if (directoryMapEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryMapEntries[i] = directoryMapEntry;
|
||||
file.DirectoryMapEntries[i] = ParseDirectoryMapEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -299,7 +241,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Checksum Header
|
||||
|
||||
// Try to parse the checksum header
|
||||
var checksumHeader = data.ReadType<ChecksumHeader>();
|
||||
var checksumHeader = ParseChecksumHeader(data);
|
||||
if (checksumHeader?.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
|
||||
@@ -314,7 +256,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Checksum Map Header
|
||||
|
||||
// Try to parse the checksum map header
|
||||
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
|
||||
var checksumMapHeader = ParseChecksumMapHeader(data);
|
||||
if (checksumMapHeader?.Dummy0 != 0x14893721)
|
||||
return null;
|
||||
if (checksumMapHeader?.Dummy1 != 0x00000001)
|
||||
@@ -333,11 +275,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the checksum map entries
|
||||
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
|
||||
{
|
||||
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
|
||||
if (checksumMapEntry == null)
|
||||
return null;
|
||||
|
||||
file.ChecksumMapEntries[i] = checksumMapEntry;
|
||||
file.ChecksumMapEntries[i] = ParseChecksumMapEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -350,11 +288,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the checksum entries
|
||||
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
|
||||
{
|
||||
var checksumEntry = data.ReadType<ChecksumEntry>();
|
||||
if (checksumEntry == null)
|
||||
return null;
|
||||
|
||||
file.ChecksumEntries[i] = checksumEntry;
|
||||
file.ChecksumEntries[i] = ParseChecksumEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -384,26 +318,348 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Game Cache data block header
|
||||
/// Parse a Stream into a BlockEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BlockEntry on success, null on error</returns>
|
||||
public static BlockEntry ParseBlockEntry(Stream data)
|
||||
{
|
||||
var obj = new BlockEntry();
|
||||
|
||||
obj.EntryFlags = data.ReadUInt32LittleEndian();
|
||||
obj.FileDataOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileDataSize = data.ReadUInt32LittleEndian();
|
||||
obj.FirstDataBlockIndex = data.ReadUInt32LittleEndian();
|
||||
obj.NextBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.PreviousBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a BlockEntryHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BlockEntryHeader on success, null on error</returns>
|
||||
public static BlockEntryHeader ParseBlockEntryHeader(Stream data)
|
||||
{
|
||||
var obj = new BlockEntryHeader();
|
||||
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.BlocksUsed = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy2 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy3 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy4 = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a BlockEntryMap
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BlockEntryMap on success, null on error</returns>
|
||||
public static BlockEntryMap ParseBlockEntryMap(Stream data)
|
||||
{
|
||||
var obj = new BlockEntryMap();
|
||||
|
||||
obj.PreviousBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.NextBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a BlockEntryMapHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BlockEntryMapHeader on success, null on error</returns>
|
||||
public static BlockEntryMapHeader ParseBlockEntryMapHeader(Stream data)
|
||||
{
|
||||
var obj = new BlockEntryMapHeader();
|
||||
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.FirstBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.LastBlockEntryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumEntry on success, null on error</returns>
|
||||
public static ChecksumEntry ParseChecksumEntry(Stream data)
|
||||
{
|
||||
var obj = new ChecksumEntry();
|
||||
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumHeader on success, null on error</returns>
|
||||
public static ChecksumHeader ParseChecksumHeader(Stream data)
|
||||
{
|
||||
var obj = new ChecksumHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumMapEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumMapEntry on success, null on error</returns>
|
||||
public static ChecksumMapEntry ParseChecksumMapEntry(Stream data)
|
||||
{
|
||||
var obj = new ChecksumMapEntry();
|
||||
|
||||
obj.ChecksumCount = data.ReadUInt32LittleEndian();
|
||||
obj.FirstChecksumIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumMapHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumMapHeader on success, null on error</returns>
|
||||
public static ChecksumMapHeader ParseChecksumMapHeader(Stream data)
|
||||
{
|
||||
var obj = new ChecksumMapHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.ItemCount = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DataBlockHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="minorVersion">Minor version field from the header</param>
|
||||
/// <returns>Filled Half-Life Game Cache data block header on success, null on error</returns>
|
||||
private static DataBlockHeader? ParseDataBlockHeader(Stream data, uint minorVersion)
|
||||
/// <returns>Filled DataBlockHeader on success, null on error</returns>
|
||||
public static DataBlockHeader ParseDataBlockHeader(Stream data, uint minorVersion)
|
||||
{
|
||||
var dataBlockHeader = new DataBlockHeader();
|
||||
var obj = new DataBlockHeader();
|
||||
|
||||
// In version 3 the DataBlockHeader is missing the LastVersionPlayed field.
|
||||
if (minorVersion >= 5)
|
||||
dataBlockHeader.LastVersionPlayed = data.ReadUInt32();
|
||||
obj.LastVersionPlayed = data.ReadUInt32LittleEndian();
|
||||
|
||||
dataBlockHeader.BlockCount = data.ReadUInt32();
|
||||
dataBlockHeader.BlockSize = data.ReadUInt32();
|
||||
dataBlockHeader.FirstBlockOffset = data.ReadUInt32();
|
||||
dataBlockHeader.BlocksUsed = data.ReadUInt32();
|
||||
dataBlockHeader.Checksum = data.ReadUInt32();
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.BlockSize = data.ReadUInt32LittleEndian();
|
||||
obj.FirstBlockOffset = data.ReadUInt32LittleEndian();
|
||||
obj.BlocksUsed = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return dataBlockHeader;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryCopyEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryCopyEntry on success, null on error</returns>
|
||||
public static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryCopyEntry();
|
||||
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryEntry on success, null on error</returns>
|
||||
public static DirectoryEntry ParseDirectoryEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryEntry();
|
||||
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ItemSize = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumIndex = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryFlags = (HL_GCF_FLAG)data.ReadUInt32LittleEndian();
|
||||
obj.ParentIndex = data.ReadUInt32LittleEndian();
|
||||
obj.NextIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FirstIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryHeader on success, null on error</returns>
|
||||
public static DirectoryHeader ParseDirectoryHeader(Stream data)
|
||||
{
|
||||
var obj = new DirectoryHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.CacheID = data.ReadUInt32LittleEndian();
|
||||
obj.LastVersionPlayed = data.ReadUInt32LittleEndian();
|
||||
obj.ItemCount = data.ReadUInt32LittleEndian();
|
||||
obj.FileCount = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.DirectorySize = data.ReadUInt32LittleEndian();
|
||||
obj.NameSize = data.ReadUInt32LittleEndian();
|
||||
obj.Info1Count = data.ReadUInt32LittleEndian();
|
||||
obj.CopyCount = data.ReadUInt32LittleEndian();
|
||||
obj.LocalCount = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy2 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy3 = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryInfo1Entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryInfo1Entry on success, null on error</returns>
|
||||
public static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryInfo1Entry();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryInfo2Entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryInfo2Entry on success, null on error</returns>
|
||||
public static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryInfo2Entry();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryLocalEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryLocalEntry on success, null on error</returns>
|
||||
public static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryLocalEntry();
|
||||
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryMapEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryMapEntry on success, null on error</returns>
|
||||
public static DirectoryMapEntry ParseDirectoryMapEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryMapEntry();
|
||||
|
||||
obj.FirstBlockIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryMapHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryMapHeader on success, null on error</returns>
|
||||
public static DirectoryMapHeader ParseDirectoryMapHeader(Stream data)
|
||||
{
|
||||
var obj = new DirectoryMapHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FragmentationMap
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FragmentationMap on success, null on error</returns>
|
||||
public static FragmentationMap ParseFragmentationMap(Stream data)
|
||||
{
|
||||
var obj = new FragmentationMap();
|
||||
|
||||
obj.NextDataBlockIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FragmentationMapHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FragmentationMapHeader on success, null on error</returns>
|
||||
public static FragmentationMapHeader ParseFragmentationMapHeader(Stream data)
|
||||
{
|
||||
var obj = new FragmentationMapHeader();
|
||||
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.FirstUnusedEntry = data.ReadUInt32LittleEndian();
|
||||
obj.Terminator = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.MajorVersion = data.ReadUInt32LittleEndian();
|
||||
obj.MinorVersion = data.ReadUInt32LittleEndian();
|
||||
obj.CacheID = data.ReadUInt32LittleEndian();
|
||||
obj.LastVersionPlayed = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy2 = data.ReadUInt32LittleEndian();
|
||||
obj.FileSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy3 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new IRD to fill
|
||||
// Deserialize the IRD
|
||||
var ird = new Models.IRD.File();
|
||||
|
||||
ird.Magic = data.ReadBytes(4);
|
||||
@@ -44,7 +44,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
ird.AppVersion = Encoding.ASCII.GetString(appVersion);
|
||||
|
||||
if (ird.Version == 7)
|
||||
ird.UID = data.ReadUInt32();
|
||||
ird.UID = data.ReadUInt32LittleEndian();
|
||||
|
||||
ird.HeaderLength = data.ReadByteValue();
|
||||
ird.Header = data.ReadBytes((int)ird.HeaderLength);
|
||||
@@ -63,12 +63,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
ird.FileHashes = new byte[ird.FileCount][];
|
||||
for (int i = 0; i < ird.FileCount; i++)
|
||||
{
|
||||
ird.FileKeys[i] = data.ReadUInt64();
|
||||
ird.FileKeys[i] = data.ReadUInt64LittleEndian();
|
||||
ird.FileHashes[i] = data.ReadBytes(16) ?? [];
|
||||
}
|
||||
|
||||
ird.ExtraConfig = data.ReadUInt16();
|
||||
ird.Attachments = data.ReadUInt16();
|
||||
ird.ExtraConfig = data.ReadUInt16LittleEndian();
|
||||
ird.Attachments = data.ReadUInt16LittleEndian();
|
||||
|
||||
if (ird.Version >= 9)
|
||||
ird.PIC = data.ReadBytes(115);
|
||||
@@ -80,9 +80,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
ird.PIC = data.ReadBytes(115);
|
||||
|
||||
if (ird.Version > 7)
|
||||
ird.UID = data.ReadUInt32();
|
||||
ird.UID = data.ReadUInt32LittleEndian();
|
||||
|
||||
ird.CRC = data.ReadUInt32();
|
||||
ird.CRC = data.ReadUInt32LittleEndian();
|
||||
|
||||
return ird;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature1 != Constants.HeaderSignature)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature1 != Constants.HeaderSignature)
|
||||
return null;
|
||||
if (header.TocAddress >= data.Length)
|
||||
return null;
|
||||
@@ -48,11 +48,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
for (int i = 0; i < header.DirCount; i++)
|
||||
{
|
||||
var directory = ParseDirectory(data);
|
||||
if (directory?.Name == null)
|
||||
return null;
|
||||
|
||||
directories.Add(directory);
|
||||
data.Seek(directory.ChunkSize - directory.Name.Length - 6, SeekOrigin.Current);
|
||||
data.Seek(directory.ChunkSize - directory.Name!.Length - 6, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// Set the directories
|
||||
@@ -69,12 +66,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var directory = archive.Directories[i];
|
||||
for (int j = 0; j < directory.FileCount; j++)
|
||||
{
|
||||
var file = data.ReadType<Models.InstallShieldArchiveV3.File>();
|
||||
if (file?.Name == null)
|
||||
return null;
|
||||
|
||||
var file = ParseFile(data);
|
||||
files.Add(file);
|
||||
data.Seek(file.ChunkSize - file.Name.Length - 30, SeekOrigin.Current);
|
||||
data.Seek(file.ChunkSize - file.Name!.Length - 30, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,22 +87,80 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a directory
|
||||
/// Parse a Stream into a Directory
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled directory on success, null on error</returns>
|
||||
public static Models.InstallShieldArchiveV3.Directory? ParseDirectory(Stream data)
|
||||
/// <returns>Filled Directory on success, null on error</returns>
|
||||
public static Models.InstallShieldArchiveV3.Directory ParseDirectory(Stream data)
|
||||
{
|
||||
var directory = new Models.InstallShieldArchiveV3.Directory();
|
||||
var obj = new Models.InstallShieldArchiveV3.Directory();
|
||||
|
||||
directory.FileCount = data.ReadUInt16();
|
||||
directory.ChunkSize = data.ReadUInt16();
|
||||
obj.FileCount = data.ReadUInt16LittleEndian();
|
||||
obj.ChunkSize = data.ReadUInt16LittleEndian();
|
||||
|
||||
ushort nameLength = data.ReadUInt16();
|
||||
ushort nameLength = data.ReadUInt16LittleEndian();
|
||||
byte[] nameBytes = data.ReadBytes(nameLength);
|
||||
directory.Name = Encoding.ASCII.GetString(nameBytes);
|
||||
obj.Name = Encoding.ASCII.GetString(nameBytes);
|
||||
|
||||
return directory;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a File
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled File on success, null on error</returns>
|
||||
public static Models.InstallShieldArchiveV3.File ParseFile(Stream data)
|
||||
{
|
||||
var obj = new Models.InstallShieldArchiveV3.File();
|
||||
|
||||
obj.VolumeEnd = data.ReadByteValue();
|
||||
obj.Index = data.ReadUInt16LittleEndian();
|
||||
obj.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.Offset = data.ReadUInt32LittleEndian();
|
||||
obj.DateTime = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved0 = data.ReadUInt32LittleEndian();
|
||||
obj.ChunkSize = data.ReadUInt16LittleEndian();
|
||||
obj.Attrib = (Attributes)data.ReadByteValue();
|
||||
obj.IsSplit = data.ReadByteValue();
|
||||
obj.Reserved1 = data.ReadByteValue();
|
||||
obj.VolumeStart = data.ReadByteValue();
|
||||
obj.Name = data.ReadPrefixedAnsiString();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
obj.Signature1 = data.ReadUInt32LittleEndian();
|
||||
obj.Signature2 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved0 = data.ReadUInt16LittleEndian();
|
||||
obj.IsMultivolume = data.ReadUInt16LittleEndian();
|
||||
obj.FileCount = data.ReadUInt16LittleEndian();
|
||||
obj.DateTime = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved1 = data.ReadUInt32LittleEndian();
|
||||
obj.VolumeTotal = data.ReadByteValue();
|
||||
obj.VolumeNumber = data.ReadByteValue();
|
||||
obj.Reserved2 = data.ReadByteValue();
|
||||
obj.SplitBeginAddress = data.ReadUInt32LittleEndian();
|
||||
obj.SplitEndAddress = data.ReadUInt32LittleEndian();
|
||||
obj.TocAddress = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved3 = data.ReadUInt32LittleEndian();
|
||||
obj.DirCount = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved4 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved5 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using static SabreTools.Models.InstallShieldCabinet.Constants;
|
||||
@@ -25,8 +26,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Common Header
|
||||
|
||||
// Try to parse the cabinet header
|
||||
var commonHeader = data.ReadType<CommonHeader>();
|
||||
if (commonHeader?.Signature != SignatureString)
|
||||
var commonHeader = ParseCommonHeader(data);
|
||||
if (commonHeader.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the cabinet header
|
||||
@@ -34,15 +35,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#endregion
|
||||
|
||||
// Get the major version
|
||||
int majorVersion = GetMajorVersion(commonHeader);
|
||||
|
||||
#region Volume Header
|
||||
|
||||
// Try to parse the volume header
|
||||
var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader));
|
||||
if (volumeHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the volume header
|
||||
cabinet.VolumeHeader = volumeHeader;
|
||||
cabinet.VolumeHeader = ParseVolumeHeader(data, majorVersion);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -56,20 +55,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Seek to the descriptor
|
||||
data.Seek(descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the descriptor
|
||||
var descriptor = data.ReadType<Descriptor>();
|
||||
if (descriptor == null)
|
||||
return null;
|
||||
|
||||
// Set the descriptor
|
||||
cabinet.Descriptor = descriptor;
|
||||
cabinet.Descriptor = ParseDescriptor(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Descriptor Offsets
|
||||
|
||||
// Get the file table offset
|
||||
uint fileTableOffset = commonHeader.DescriptorOffset + descriptor.FileTableOffset;
|
||||
uint fileTableOffset = commonHeader.DescriptorOffset + cabinet.Descriptor.FileTableOffset;
|
||||
if (fileTableOffset < 0 || fileTableOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
@@ -78,16 +72,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Get the number of file table items
|
||||
uint fileTableItems;
|
||||
if (GetMajorVersion(commonHeader) <= 5)
|
||||
fileTableItems = descriptor.DirectoryCount + descriptor.FileCount;
|
||||
if (majorVersion <= 5)
|
||||
fileTableItems = cabinet.Descriptor.DirectoryCount + cabinet.Descriptor.FileCount;
|
||||
else
|
||||
fileTableItems = descriptor.DirectoryCount;
|
||||
fileTableItems = cabinet.Descriptor.DirectoryCount;
|
||||
|
||||
// Create and fill the file table
|
||||
cabinet.FileDescriptorOffsets = new uint[fileTableItems];
|
||||
for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++)
|
||||
{
|
||||
cabinet.FileDescriptorOffsets[i] = data.ReadUInt32();
|
||||
cabinet.FileDescriptorOffsets[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -95,12 +89,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Descriptors
|
||||
|
||||
// Create and fill the directory descriptors
|
||||
cabinet.DirectoryNames = new string[descriptor.DirectoryCount];
|
||||
for (int i = 0; i < descriptor.DirectoryCount; i++)
|
||||
cabinet.DirectoryNames = new string[cabinet.Descriptor.DirectoryCount];
|
||||
for (int i = 0; i < cabinet.Descriptor.DirectoryCount; i++)
|
||||
{
|
||||
// Get the directory descriptor offset
|
||||
uint offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ cabinet.Descriptor.FileTableOffset
|
||||
+ cabinet.FileDescriptorOffsets[i];
|
||||
|
||||
// If we have an invalid offset
|
||||
@@ -111,7 +105,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the file descriptor
|
||||
string? directoryName = ParseDirectoryName(data, GetMajorVersion(commonHeader));
|
||||
string? directoryName = ParseDirectoryName(data, majorVersion);
|
||||
if (directoryName != null)
|
||||
cabinet.DirectoryNames[i] = directoryName;
|
||||
}
|
||||
@@ -121,22 +115,22 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region File Descriptors
|
||||
|
||||
// Create and fill the file descriptors
|
||||
cabinet.FileDescriptors = new FileDescriptor[descriptor.FileCount];
|
||||
for (int i = 0; i < descriptor.FileCount; i++)
|
||||
cabinet.FileDescriptors = new FileDescriptor[cabinet.Descriptor.FileCount];
|
||||
for (int i = 0; i < cabinet.Descriptor.FileCount; i++)
|
||||
{
|
||||
// Get the file descriptor offset
|
||||
uint offset;
|
||||
if (GetMajorVersion(commonHeader) <= 5)
|
||||
if (majorVersion <= 5)
|
||||
{
|
||||
offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ cabinet.FileDescriptorOffsets[descriptor.DirectoryCount + i];
|
||||
+ cabinet.Descriptor.FileTableOffset
|
||||
+ cabinet.FileDescriptorOffsets[cabinet.Descriptor.DirectoryCount + i];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ descriptor.FileTableOffset2
|
||||
+ cabinet.Descriptor.FileTableOffset
|
||||
+ cabinet.Descriptor.FileTableOffset2
|
||||
+ (uint)(i * 0x57);
|
||||
}
|
||||
|
||||
@@ -148,8 +142,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the file descriptor
|
||||
FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), descriptorOffset + descriptor.FileTableOffset);
|
||||
cabinet.FileDescriptors[i] = fileDescriptor;
|
||||
cabinet.FileDescriptors[i] = ParseFileDescriptor(data,
|
||||
majorVersion,
|
||||
descriptorOffset + cabinet.Descriptor.FileTableOffset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -158,10 +153,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Create and fill the file group offsets
|
||||
cabinet.FileGroupOffsets = new Dictionary<long, OffsetList?>();
|
||||
for (int i = 0; i < (descriptor.FileGroupOffsets?.Length ?? 0); i++)
|
||||
for (int i = 0; i < (cabinet.Descriptor.FileGroupOffsets?.Length ?? 0); i++)
|
||||
{
|
||||
// Get the file group offset
|
||||
uint offset = descriptor.FileGroupOffsets![i];
|
||||
uint offset = cabinet.Descriptor.FileGroupOffsets![i];
|
||||
if (offset == 0)
|
||||
continue;
|
||||
|
||||
@@ -174,8 +169,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.FileGroupOffsets[descriptor.FileGroupOffsets[i]] = offsetList;
|
||||
OffsetList offsetList = ParseOffsetList(data, majorVersion, descriptorOffset);
|
||||
cabinet.FileGroupOffsets[offset] = offsetList;
|
||||
|
||||
// If we have a nonzero next offset
|
||||
uint nextOffset = offsetList.NextOffset;
|
||||
@@ -188,7 +183,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(internalOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
offsetList = ParseOffsetList(data, majorVersion, descriptorOffset);
|
||||
cabinet.FileGroupOffsets[nextOffset] = offsetList;
|
||||
|
||||
// Set the next offset
|
||||
@@ -225,13 +220,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// Seek to the file group
|
||||
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the file group
|
||||
var fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
if (fileGroup == null)
|
||||
return null;
|
||||
|
||||
// Add the file group
|
||||
cabinet.FileGroups[fileGroupId++] = fileGroup;
|
||||
cabinet.FileGroups[fileGroupId++] = ParseFileGroup(data, majorVersion, descriptorOffset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -240,10 +230,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Create and fill the component offsets
|
||||
cabinet.ComponentOffsets = new Dictionary<long, OffsetList?>();
|
||||
for (int i = 0; i < (descriptor.ComponentOffsets?.Length ?? 0); i++)
|
||||
for (int i = 0; i < (cabinet.Descriptor.ComponentOffsets?.Length ?? 0); i++)
|
||||
{
|
||||
// Get the component offset
|
||||
uint offset = descriptor.ComponentOffsets![i];
|
||||
uint offset = cabinet.Descriptor.ComponentOffsets![i];
|
||||
if (offset == 0)
|
||||
continue;
|
||||
|
||||
@@ -256,8 +246,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.ComponentOffsets[descriptor.ComponentOffsets[i]] = offsetList;
|
||||
OffsetList offsetList = ParseOffsetList(data, majorVersion, descriptorOffset);
|
||||
cabinet.ComponentOffsets[cabinet.Descriptor.ComponentOffsets[i]] = offsetList;
|
||||
|
||||
// If we have a nonzero next offset
|
||||
uint nextOffset = offsetList.NextOffset;
|
||||
@@ -270,7 +260,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(internalOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
offsetList = ParseOffsetList(data, majorVersion, descriptorOffset);
|
||||
cabinet.ComponentOffsets[nextOffset] = offsetList;
|
||||
|
||||
// Set the next offset
|
||||
@@ -307,13 +297,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Seek to the component
|
||||
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the component
|
||||
var component = ParseComponent(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
if (component == null)
|
||||
return null;
|
||||
|
||||
// Add the component
|
||||
cabinet.Components[componentId++] = component;
|
||||
cabinet.Components[componentId++] = ParseComponent(data, majorVersion, descriptorOffset);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -330,251 +315,132 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a volume header
|
||||
/// Parse a Stream into a CommonHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled volume header on success, null on error</returns>
|
||||
public static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion)
|
||||
/// <returns>Filled CommonHeader on success, null on error</returns>
|
||||
public static CommonHeader ParseCommonHeader(Stream data)
|
||||
{
|
||||
var volumeHeader = new VolumeHeader();
|
||||
var obj = new CommonHeader();
|
||||
|
||||
// Read the descriptor based on version
|
||||
if (majorVersion <= 5)
|
||||
{
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
_ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data?
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
volumeHeader.DataOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32();
|
||||
}
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.VolumeInfo = data.ReadUInt32LittleEndian();
|
||||
obj.DescriptorOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DescriptorSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return volumeHeader;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an offset list
|
||||
/// Parse a Stream into a Component
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled offset list on success, null on error</returns>
|
||||
public static OffsetList ParseOffsetList(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var offsetList = new OffsetList();
|
||||
|
||||
offsetList.NameOffset = data.ReadUInt32();
|
||||
offsetList.DescriptorOffset = data.ReadUInt32();
|
||||
offsetList.NextOffset = data.ReadUInt32();
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = data.Position;
|
||||
|
||||
// Seek to the name offset
|
||||
data.Seek(offsetList.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
offsetList.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
offsetList.Name = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentOffset, SeekOrigin.Begin);
|
||||
|
||||
return offsetList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file group
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled file group on success, null on error</returns>
|
||||
public static FileGroup ParseFileGroup(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var fileGroup = new FileGroup();
|
||||
|
||||
fileGroup.NameOffset = data.ReadUInt32();
|
||||
fileGroup.ExpandedSize = data.ReadUInt32();
|
||||
fileGroup.CompressedSize = data.ReadUInt32();
|
||||
fileGroup.Attributes = (FileGroupAttributes)data.ReadUInt16();
|
||||
|
||||
// TODO: Figure out what data lives in this area for V5 and below
|
||||
if (majorVersion <= 5)
|
||||
data.Seek(0x36, SeekOrigin.Current);
|
||||
|
||||
fileGroup.FirstFile = data.ReadUInt32();
|
||||
fileGroup.LastFile = data.ReadUInt32();
|
||||
fileGroup.UnknownStringOffset = data.ReadUInt32();
|
||||
fileGroup.OperatingSystemOffset = data.ReadUInt32();
|
||||
fileGroup.LanguageOffset = data.ReadUInt32();
|
||||
fileGroup.HTTPLocationOffset = data.ReadUInt32();
|
||||
fileGroup.FTPLocationOffset = data.ReadUInt32();
|
||||
fileGroup.MiscOffset = data.ReadUInt32();
|
||||
fileGroup.TargetDirectoryOffset = data.ReadUInt32();
|
||||
fileGroup.OverwriteFlags = (FileGroupFlags)data.ReadUInt32();
|
||||
fileGroup.Reserved = new uint[4];
|
||||
for (int i = 0; i < fileGroup.Reserved.Length; i++)
|
||||
{
|
||||
fileGroup.Reserved[i] = data.ReadUInt32();
|
||||
}
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Read the name, if possible
|
||||
if (fileGroup.NameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(fileGroup.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
fileGroup.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
fileGroup.Name = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentPosition, SeekOrigin.Begin);
|
||||
|
||||
return fileGroup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a component
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled component on success, null on error</returns>
|
||||
/// <returns>Filled Component on success, null on error</returns>
|
||||
public static Component ParseComponent(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var component = new Component();
|
||||
var obj = new Component();
|
||||
|
||||
component.IdentifierOffset = data.ReadUInt32();
|
||||
component.DescriptorOffset = data.ReadUInt32();
|
||||
component.DisplayNameOffset = data.ReadUInt32();
|
||||
component.Status = (ComponentStatus)data.ReadUInt16();
|
||||
component.PasswordOffset = data.ReadUInt32();
|
||||
component.MiscOffset = data.ReadUInt32();
|
||||
component.ComponentIndex = data.ReadUInt16();
|
||||
component.NameOffset = data.ReadUInt32();
|
||||
component.CDRomFolderOffset = data.ReadUInt32();
|
||||
component.HTTPLocationOffset = data.ReadUInt32();
|
||||
component.FTPLocationOffset = data.ReadUInt32();
|
||||
component.Guid = new Guid[2];
|
||||
for (int i = 0; i < component.Guid.Length; i++)
|
||||
obj.IdentifierOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DescriptorOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DisplayNameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Status = (ComponentStatus)data.ReadUInt16LittleEndian();
|
||||
obj.PasswordOffset = data.ReadUInt32LittleEndian();
|
||||
obj.MiscOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ComponentIndex = data.ReadUInt16LittleEndian();
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.CDRomFolderOffset = data.ReadUInt32LittleEndian();
|
||||
obj.HTTPLocationOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FTPLocationOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Guid = new Guid[2];
|
||||
for (int i = 0; i < obj.Guid.Length; i++)
|
||||
{
|
||||
component.Guid[i] = data.ReadGuid();
|
||||
obj.Guid[i] = data.ReadGuid();
|
||||
}
|
||||
component.CLSIDOffset = data.ReadUInt32();
|
||||
component.Reserved2 = data.ReadBytes(28);
|
||||
component.Reserved3 = data.ReadBytes(majorVersion <= 5 ? 2 : 1);
|
||||
component.DependsCount = data.ReadUInt16();
|
||||
component.DependsOffset = data.ReadUInt32();
|
||||
component.FileGroupCount = data.ReadUInt16();
|
||||
component.FileGroupNamesOffset = data.ReadUInt32();
|
||||
component.X3Count = data.ReadUInt16();
|
||||
component.X3Offset = data.ReadUInt32();
|
||||
component.SubComponentsCount = data.ReadUInt16();
|
||||
component.SubComponentsOffset = data.ReadUInt32();
|
||||
component.NextComponentOffset = data.ReadUInt32();
|
||||
component.OnInstallingOffset = data.ReadUInt32();
|
||||
component.OnInstalledOffset = data.ReadUInt32();
|
||||
component.OnUninstallingOffset = data.ReadUInt32();
|
||||
component.OnUninstalledOffset = data.ReadUInt32();
|
||||
obj.CLSIDOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(28);
|
||||
obj.Reserved3 = data.ReadBytes(majorVersion <= 5 ? 2 : 1);
|
||||
obj.DependsCount = data.ReadUInt16LittleEndian();
|
||||
obj.DependsOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileGroupCount = data.ReadUInt16LittleEndian();
|
||||
obj.FileGroupNamesOffset = data.ReadUInt32LittleEndian();
|
||||
obj.X3Count = data.ReadUInt16LittleEndian();
|
||||
obj.X3Offset = data.ReadUInt32LittleEndian();
|
||||
obj.SubComponentsCount = data.ReadUInt16LittleEndian();
|
||||
obj.SubComponentsOffset = data.ReadUInt32LittleEndian();
|
||||
obj.NextComponentOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OnInstallingOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OnInstalledOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OnUninstallingOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OnUninstalledOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Read the identifier, if possible
|
||||
if (component.IdentifierOffset != 0)
|
||||
if (obj.IdentifierOffset != 0)
|
||||
{
|
||||
// Seek to the identifier
|
||||
data.Seek(component.IdentifierOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.IdentifierOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
component.Identifier = data.ReadNullTerminatedUnicodeString();
|
||||
obj.Identifier = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
component.Identifier = data.ReadNullTerminatedAnsiString();
|
||||
obj.Identifier = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Read the display name, if possible
|
||||
if (component.DisplayNameOffset != 0)
|
||||
if (obj.DisplayNameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(component.DisplayNameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.DisplayNameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
component.DisplayName = data.ReadNullTerminatedUnicodeString();
|
||||
obj.DisplayName = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
component.DisplayName = data.ReadNullTerminatedAnsiString();
|
||||
obj.DisplayName = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Read the name, if possible
|
||||
if (component.NameOffset != 0)
|
||||
if (obj.NameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(component.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
component.Name = data.ReadNullTerminatedUnicodeString();
|
||||
obj.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
component.Name = data.ReadNullTerminatedAnsiString();
|
||||
obj.Name = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Read the CLSID, if possible
|
||||
if (component.CLSIDOffset != 0)
|
||||
if (obj.CLSIDOffset != 0)
|
||||
{
|
||||
// Seek to the CLSID
|
||||
data.Seek(component.CLSIDOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.CLSIDOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the GUID
|
||||
component.CLSID = data.ReadGuid();
|
||||
obj.CLSID = data.ReadGuid();
|
||||
}
|
||||
|
||||
// Read the file group names, if possible
|
||||
if (component.FileGroupCount != 0 && component.FileGroupNamesOffset != 0)
|
||||
if (obj.FileGroupCount != 0 && obj.FileGroupNamesOffset != 0)
|
||||
{
|
||||
// Seek to the file group table offset
|
||||
data.Seek(component.FileGroupNamesOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.FileGroupNamesOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the file group names table
|
||||
component.FileGroupNames = new string[component.FileGroupCount];
|
||||
for (int j = 0; j < component.FileGroupCount; j++)
|
||||
obj.FileGroupNames = new string[obj.FileGroupCount];
|
||||
for (int j = 0; j < obj.FileGroupCount; j++)
|
||||
{
|
||||
// Get the name offset
|
||||
uint nameOffset = data.ReadUInt32();
|
||||
uint nameOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Cache the current offset
|
||||
long preNameOffset = data.Position;
|
||||
@@ -583,9 +449,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(nameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
if (majorVersion >= 17)
|
||||
component.FileGroupNames[j] = data.ReadNullTerminatedUnicodeString() ?? string.Empty;
|
||||
obj.FileGroupNames[j] = data.ReadNullTerminatedUnicodeString() ?? string.Empty;
|
||||
else
|
||||
component.FileGroupNames[j] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
|
||||
obj.FileGroupNames[j] = data.ReadNullTerminatedAnsiString() ?? string.Empty;
|
||||
|
||||
// Seek back to the original position
|
||||
data.Seek(preNameOffset, SeekOrigin.Begin);
|
||||
@@ -595,7 +461,51 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentPosition, SeekOrigin.Begin);
|
||||
|
||||
return component;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Descriptor on success, null on error</returns>
|
||||
public static Descriptor ParseDescriptor(Stream data)
|
||||
{
|
||||
var obj = new Descriptor();
|
||||
|
||||
obj.StringsOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved0 = data.ReadUInt32LittleEndian();
|
||||
obj.ComponentListOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved1 = data.ReadUInt32LittleEndian();
|
||||
obj.FileTableSize = data.ReadUInt32LittleEndian();
|
||||
obj.FileTableSize2 = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryCount = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved2 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved3 = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved4 = data.ReadUInt32LittleEndian();
|
||||
obj.FileCount = data.ReadUInt32LittleEndian();
|
||||
obj.FileTableOffset2 = data.ReadUInt32LittleEndian();
|
||||
obj.ComponentTableInfoCount = data.ReadUInt16LittleEndian();
|
||||
obj.ComponentTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved5 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved6 = data.ReadUInt32LittleEndian();
|
||||
obj.FileGroupOffsets = new uint[71];
|
||||
for (int i = 0; i < 71; i++)
|
||||
{
|
||||
obj.FileGroupOffsets[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.ComponentOffsets = new uint[71];
|
||||
for (int i = 0; i < 71; i++)
|
||||
{
|
||||
obj.ComponentOffsets[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.SetupTypesOffset = data.ReadUInt32LittleEndian();
|
||||
obj.SetupTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved7 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved8 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -614,68 +524,206 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file descriptor
|
||||
/// Parse a Stream into a FileDescriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled file descriptor on success, null on error</returns>
|
||||
/// <returns>Filled FileDescriptor on success, null on error</returns>
|
||||
public static FileDescriptor ParseFileDescriptor(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var fileDescriptor = new FileDescriptor();
|
||||
var obj = new FileDescriptor();
|
||||
|
||||
// Read the descriptor based on version
|
||||
if (majorVersion <= 5)
|
||||
{
|
||||
fileDescriptor.Volume = 0xFFFF; // Set by the header index
|
||||
fileDescriptor.NameOffset = data.ReadUInt32();
|
||||
fileDescriptor.DirectoryIndex = data.ReadUInt32();
|
||||
fileDescriptor.Flags = (FileFlags)data.ReadUInt16();
|
||||
fileDescriptor.ExpandedSize = data.ReadUInt32();
|
||||
fileDescriptor.CompressedSize = data.ReadUInt32();
|
||||
obj.Volume = 0xFFFF; // Set by the header index
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (FileFlags)data.ReadUInt16LittleEndian();
|
||||
obj.ExpandedSize = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
_ = data.ReadBytes(0x14); // Skip 0x14 bytes, unknown data?
|
||||
fileDescriptor.DataOffset = data.ReadUInt32();
|
||||
obj.DataOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
if (majorVersion == 5)
|
||||
fileDescriptor.MD5 = data.ReadBytes(0x10);
|
||||
obj.MD5 = data.ReadBytes(0x10);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileDescriptor.Flags = (FileFlags)data.ReadUInt16();
|
||||
fileDescriptor.ExpandedSize = data.ReadUInt64();
|
||||
fileDescriptor.CompressedSize = data.ReadUInt64();
|
||||
fileDescriptor.DataOffset = data.ReadUInt64();
|
||||
fileDescriptor.MD5 = data.ReadBytes(0x10);
|
||||
obj.Flags = (FileFlags)data.ReadUInt16LittleEndian();
|
||||
obj.ExpandedSize = data.ReadUInt64LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt64LittleEndian();
|
||||
obj.DataOffset = data.ReadUInt64LittleEndian();
|
||||
obj.MD5 = data.ReadBytes(0x10);
|
||||
_ = data.ReadBytes(0x10); // Skip 0x10 bytes, unknown data?
|
||||
fileDescriptor.NameOffset = data.ReadUInt32();
|
||||
fileDescriptor.DirectoryIndex = data.ReadUInt16();
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryIndex = data.ReadUInt16LittleEndian();
|
||||
_ = data.ReadBytes(0x0C); // Skip 0x0C bytes, unknown data?
|
||||
fileDescriptor.LinkPrevious = data.ReadUInt32();
|
||||
fileDescriptor.LinkNext = data.ReadUInt32();
|
||||
fileDescriptor.LinkFlags = (LinkFlags)data.ReadByteValue();
|
||||
fileDescriptor.Volume = data.ReadUInt16();
|
||||
obj.LinkPrevious = data.ReadUInt32LittleEndian();
|
||||
obj.LinkNext = data.ReadUInt32LittleEndian();
|
||||
obj.LinkFlags = (LinkFlags)data.ReadByteValue();
|
||||
obj.Volume = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Read the name, if possible
|
||||
if (fileDescriptor.NameOffset != 0)
|
||||
if (obj.NameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(fileDescriptor.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
fileDescriptor.Name = data.ReadNullTerminatedUnicodeString();
|
||||
obj.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
fileDescriptor.Name = data.ReadNullTerminatedAnsiString();
|
||||
obj.Name = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentPosition, SeekOrigin.Begin);
|
||||
|
||||
return fileDescriptor;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FileGroup
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled FileGroup on success, null on error</returns>
|
||||
public static FileGroup ParseFileGroup(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var obj = new FileGroup();
|
||||
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ExpandedSize = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.Attributes = (FileGroupAttributes)data.ReadUInt16LittleEndian();
|
||||
|
||||
// TODO: Figure out what data lives in this area for V5 and below
|
||||
if (majorVersion <= 5)
|
||||
data.Seek(0x36, SeekOrigin.Current);
|
||||
|
||||
obj.FirstFile = data.ReadUInt32LittleEndian();
|
||||
obj.LastFile = data.ReadUInt32LittleEndian();
|
||||
obj.UnknownStringOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OperatingSystemOffset = data.ReadUInt32LittleEndian();
|
||||
obj.LanguageOffset = data.ReadUInt32LittleEndian();
|
||||
obj.HTTPLocationOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FTPLocationOffset = data.ReadUInt32LittleEndian();
|
||||
obj.MiscOffset = data.ReadUInt32LittleEndian();
|
||||
obj.TargetDirectoryOffset = data.ReadUInt32LittleEndian();
|
||||
obj.OverwriteFlags = (FileGroupFlags)data.ReadUInt32LittleEndian();
|
||||
obj.Reserved = new uint[4];
|
||||
for (int i = 0; i < obj.Reserved.Length; i++)
|
||||
{
|
||||
obj.Reserved[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Read the name, if possible
|
||||
if (obj.NameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
obj.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
obj.Name = data.ReadNullTerminatedAnsiString();
|
||||
}
|
||||
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentPosition, SeekOrigin.Begin);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an OffsetList
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled OffsetList on success, null on error</returns>
|
||||
public static OffsetList ParseOffsetList(Stream data, int majorVersion, uint descriptorOffset)
|
||||
{
|
||||
var obj = new OffsetList();
|
||||
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DescriptorOffset = data.ReadUInt32LittleEndian();
|
||||
obj.NextOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = data.Position;
|
||||
|
||||
// Seek to the name offset
|
||||
data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
obj.Name = data.ReadNullTerminatedUnicodeString();
|
||||
else
|
||||
obj.Name = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
// Seek back to the correct offset
|
||||
data.Seek(currentOffset, SeekOrigin.Begin);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a VolumeHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled VolumeHeader on success, null on error</returns>
|
||||
public static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion)
|
||||
{
|
||||
var obj = new VolumeHeader();
|
||||
|
||||
// Read the descriptor based on version
|
||||
if (majorVersion <= 5)
|
||||
{
|
||||
obj.DataOffset = data.ReadUInt32LittleEndian();
|
||||
_ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data?
|
||||
obj.FirstFileIndex = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeExpanded = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeCompressed = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileOffset = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeExpanded = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeCompressed = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.DataOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DataOffsetHigh = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileIndex = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileOffsetHigh = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeExpanded = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeExpandedHigh = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeCompressed = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileSizeCompressedHigh = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileOffset = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileOffsetHigh = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeExpanded = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeExpandedHigh = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeCompressed = data.ReadUInt32LittleEndian();
|
||||
obj.LastFileSizeCompressedHigh = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
121
SabreTools.Serialization/Deserializers/LZKWAJ.cs
Normal file
121
SabreTools.Serialization/Deserializers/LZKWAJ.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.LZ;
|
||||
using static SabreTools.Models.LZ.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class LZKWAJ : BaseBinaryDeserializer<KWAJFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override KWAJFile? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new file to fill
|
||||
var file = new KWAJFile();
|
||||
|
||||
#region File Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the header
|
||||
file.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extended Header
|
||||
|
||||
if (header.HeaderFlags != 0)
|
||||
{
|
||||
var extensions = new KWAJHeaderExtensions();
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasDecompressedLength) != 0)
|
||||
extensions.DecompressedLength = data.ReadUInt32LittleEndian();
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasUnknownFlag) != 0)
|
||||
extensions.UnknownPurpose = data.ReadUInt16LittleEndian();
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasPrefixedData) != 0)
|
||||
{
|
||||
extensions.UnknownDataLength = data.ReadUInt16LittleEndian();
|
||||
extensions.UnknownData = data.ReadBytes((int)extensions.UnknownDataLength);
|
||||
}
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasFileName) != 0)
|
||||
extensions.FileName = data.ReadNullTerminatedAnsiString();
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasFileExtension) != 0)
|
||||
extensions.FileExtension = data.ReadNullTerminatedAnsiString();
|
||||
if ((header.HeaderFlags & KWAJHeaderFlags.HasPrefixedData) != 0)
|
||||
{
|
||||
extensions.ArbitraryTextLength = data.ReadUInt16LittleEndian();
|
||||
extensions.ArbitraryText = data.ReadBytes((int)extensions.ArbitraryTextLength);
|
||||
}
|
||||
#else
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasDecompressedLength))
|
||||
extensions.DecompressedLength = data.ReadUInt32LittleEndian();
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasUnknownFlag))
|
||||
extensions.UnknownPurpose = data.ReadUInt16LittleEndian();
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasPrefixedData))
|
||||
{
|
||||
extensions.UnknownDataLength = data.ReadUInt16LittleEndian();
|
||||
extensions.UnknownData = data.ReadBytes((int)extensions.UnknownDataLength);
|
||||
}
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasFileName))
|
||||
extensions.FileName = data.ReadNullTerminatedAnsiString();
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasFileExtension))
|
||||
extensions.FileExtension = data.ReadNullTerminatedAnsiString();
|
||||
if (header.HeaderFlags.HasFlag(KWAJHeaderFlags.HasPrefixedData))
|
||||
{
|
||||
extensions.ArbitraryTextLength = data.ReadUInt16LittleEndian();
|
||||
extensions.ArbitraryText = data.ReadBytes((int)extensions.ArbitraryTextLength);
|
||||
}
|
||||
#endif
|
||||
|
||||
file.HeaderExtensions = extensions;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return file;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled header on success, null on error</returns>
|
||||
private static KWAJHeader? ParseHeader(Stream data)
|
||||
{
|
||||
var header = new KWAJHeader();
|
||||
|
||||
header.Magic = data.ReadBytes(8);
|
||||
if (Encoding.ASCII.GetString(header.Magic) != Encoding.ASCII.GetString(KWAJSignatureBytes))
|
||||
return null;
|
||||
|
||||
header.CompressionType = (KWAJCompressionType)data.ReadUInt16LittleEndian();
|
||||
if (header.CompressionType > KWAJCompressionType.MSZIP)
|
||||
return null;
|
||||
|
||||
header.DataOffset = data.ReadUInt16LittleEndian();
|
||||
header.HeaderFlags = (KWAJHeaderFlags)data.ReadUInt16LittleEndian();
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
SabreTools.Serialization/Deserializers/LZQBasic.cs
Normal file
65
SabreTools.Serialization/Deserializers/LZQBasic.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.LZ;
|
||||
using static SabreTools.Models.LZ.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class LZQBasic : BaseBinaryDeserializer<QBasicFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override QBasicFile? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new file to fill
|
||||
var file = new QBasicFile();
|
||||
|
||||
#region File Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the header
|
||||
file.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
return file;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled header on success, null on error</returns>
|
||||
private static QBasicHeader? ParseHeader(Stream data)
|
||||
{
|
||||
var header = new QBasicHeader();
|
||||
|
||||
header.Magic = data.ReadBytes(8);
|
||||
if (Encoding.ASCII.GetString(header.Magic) != Encoding.ASCII.GetString(QBasicSignatureBytes))
|
||||
return null;
|
||||
|
||||
header.RealLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
SabreTools.Serialization/Deserializers/LZSZDD.cs
Normal file
70
SabreTools.Serialization/Deserializers/LZSZDD.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.LZ;
|
||||
using static SabreTools.Models.LZ.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class LZSZDD : BaseBinaryDeserializer<SZDDFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override SZDDFile? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new file to fill
|
||||
var file = new SZDDFile();
|
||||
|
||||
#region File Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the header
|
||||
file.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
return file;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled header on success, null on error</returns>
|
||||
private static SZDDHeader? ParseHeader(Stream data)
|
||||
{
|
||||
var header = new SZDDHeader();
|
||||
|
||||
header.Magic = data.ReadBytes(8);
|
||||
if (Encoding.ASCII.GetString(header.Magic) != Encoding.ASCII.GetString(SZDDSignatureBytes))
|
||||
return null;
|
||||
|
||||
header.CompressionType = (ExpandCompressionType)data.ReadByteValue();
|
||||
if (header.CompressionType != ExpandCompressionType.A)
|
||||
return null;
|
||||
|
||||
header.LastChar = (char)data.ReadByteValue();
|
||||
header.RealLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the executable header
|
||||
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
|
||||
var informationBlock = data.ReadType<InformationBlock>();
|
||||
if (informationBlock?.Signature != LESignatureString && informationBlock?.Signature != LXSignatureString)
|
||||
var informationBlock = ParseInformationBlock(data);
|
||||
if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString)
|
||||
return null;
|
||||
|
||||
// Set the executable header
|
||||
@@ -64,11 +64,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the object table
|
||||
for (int i = 0; i < executable.ObjectTable.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<ObjectTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ObjectTable[i] = entry;
|
||||
executable.ObjectTable[i] = ParseObjectTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,11 +85,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the object page map
|
||||
for (int i = 0; i < executable.ObjectPageMap.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<ObjectPageMapEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ObjectPageMap[i] = entry;
|
||||
executable.ObjectPageMap[i] = ParseObjectPageMapEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,11 +121,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the resource table
|
||||
for (int i = 0; i < executable.ResourceTable.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<ResourceTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ResourceTable[i] = entry;
|
||||
executable.ResourceTable[i] = ParseResourceTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,11 +201,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the module format directives table
|
||||
for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<ModuleFormatDirectivesTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ModuleFormatDirectivesTable[i] = entry;
|
||||
executable.ModuleFormatDirectivesTable[i] = ParseModuleFormatDirectivesTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,11 +230,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the fix-up page table
|
||||
for (int i = 0; i < executable.FixupPageTable.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<FixupPageTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.FixupPageTable[i] = entry;
|
||||
executable.FixupPageTable[i] = ParseFixupPageTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,11 +276,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the imported module name table
|
||||
for (int i = 0; i < executable.ImportModuleNameTable.Length; i++)
|
||||
{
|
||||
var entry = ParseImportModuleNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ImportModuleNameTable[i] = entry;
|
||||
executable.ImportModuleNameTable[i] = ParseImportModuleNameTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,9 +303,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
while (data.Position < offset + tableSize)
|
||||
{
|
||||
var entry = ParseImportModuleProcedureNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
importModuleProcedureNameTable.Add(entry);
|
||||
}
|
||||
|
||||
@@ -354,11 +327,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the per-page checksum name table
|
||||
for (int i = 0; i < executable.PerPageChecksumTable.Length; i++)
|
||||
{
|
||||
var entry = data.ReadType<PerPageChecksumTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.PerPageChecksumTable[i] = entry;
|
||||
executable.PerPageChecksumTable[i] = ParsePerPageChecksumTableEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +373,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the debug information
|
||||
var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength);
|
||||
if (debugInformation == null)
|
||||
if (debugInformation.Signature != DebugInformationSignatureString)
|
||||
return null;
|
||||
|
||||
// Set the debug information
|
||||
@@ -423,276 +392,288 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resident names table entry
|
||||
/// Parse a Stream into a DebugInformation
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled resident names table entry on success, null on error</returns>
|
||||
public static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data)
|
||||
/// <param name="size">Total size of the debug information</param>
|
||||
/// <returns>Filled DebugInformation on success, null on error</returns>
|
||||
public static DebugInformation ParseDebugInformation(Stream data, long size)
|
||||
{
|
||||
var entry = new ResidentNamesTableEntry();
|
||||
var obj = new DebugInformation();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
byte[] signature = data.ReadBytes(3);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.FormatType = (DebugFormatType)data.ReadByteValue();
|
||||
obj.DebuggerData = data.ReadBytes((int)(size - 4));
|
||||
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an entry table bundle
|
||||
/// Parse a Stream into an EntryTableBundle
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled entry table bundle on success, null on error</returns>
|
||||
/// <returns>Filled EntryTableBundle on success, null on error</returns>
|
||||
public static EntryTableBundle? ParseEntryTableBundle(Stream data)
|
||||
{
|
||||
var bundle = new EntryTableBundle();
|
||||
var obj = new EntryTableBundle();
|
||||
|
||||
bundle.Entries = data.ReadByteValue();
|
||||
if (bundle.Entries == 0)
|
||||
return bundle;
|
||||
obj.Entries = data.ReadByteValue();
|
||||
if (obj.Entries == 0)
|
||||
return obj;
|
||||
|
||||
bundle.BundleType = (BundleType)data.ReadByteValue();
|
||||
bundle.TableEntries = new EntryTableEntry[bundle.Entries];
|
||||
for (int i = 0; i < bundle.Entries; i++)
|
||||
obj.BundleType = (BundleType)data.ReadByteValue();
|
||||
obj.TableEntries = new EntryTableEntry[obj.Entries];
|
||||
for (int i = 0; i < obj.Entries; i++)
|
||||
{
|
||||
var entry = new EntryTableEntry();
|
||||
|
||||
switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent)
|
||||
switch (obj.BundleType & ~BundleType.ParameterTypingInformationPresent)
|
||||
{
|
||||
case BundleType.UnusedEntry:
|
||||
// Empty entry with no information
|
||||
break;
|
||||
|
||||
case BundleType.SixteenBitEntry:
|
||||
entry.SixteenBitObjectNumber = data.ReadUInt16();
|
||||
entry.SixteenBitObjectNumber = data.ReadUInt16LittleEndian();
|
||||
entry.SixteenBitEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.SixteenBitOffset = data.ReadUInt16();
|
||||
entry.SixteenBitOffset = data.ReadUInt16LittleEndian();
|
||||
break;
|
||||
|
||||
case BundleType.TwoEightySixCallGateEntry:
|
||||
entry.TwoEightySixObjectNumber = data.ReadUInt16();
|
||||
entry.TwoEightySixObjectNumber = data.ReadUInt16LittleEndian();
|
||||
entry.TwoEightySixEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.TwoEightySixOffset = data.ReadUInt16();
|
||||
entry.TwoEightySixCallgate = data.ReadUInt16();
|
||||
entry.TwoEightySixOffset = data.ReadUInt16LittleEndian();
|
||||
entry.TwoEightySixCallgate = data.ReadUInt16LittleEndian();
|
||||
break;
|
||||
|
||||
case BundleType.ThirtyTwoBitEntry:
|
||||
entry.ThirtyTwoBitObjectNumber = data.ReadUInt16();
|
||||
entry.ThirtyTwoBitObjectNumber = data.ReadUInt16LittleEndian();
|
||||
entry.ThirtyTwoBitEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.ThirtyTwoBitOffset = data.ReadUInt32();
|
||||
entry.ThirtyTwoBitOffset = data.ReadUInt32LittleEndian();
|
||||
break;
|
||||
|
||||
case BundleType.ForwarderEntry:
|
||||
entry.ForwarderReserved = data.ReadUInt16();
|
||||
entry.ForwarderReserved = data.ReadUInt16LittleEndian();
|
||||
entry.ForwarderFlags = (ForwarderFlags)data.ReadByteValue();
|
||||
entry.ForwarderModuleOrdinalNumber = data.ReadUInt16();
|
||||
entry.ProcedureNameOffset = data.ReadUInt32();
|
||||
entry.ImportOrdinalNumber = data.ReadUInt32();
|
||||
entry.ForwarderModuleOrdinalNumber = data.ReadUInt16LittleEndian();
|
||||
entry.ProcedureNameOffset = data.ReadUInt32LittleEndian();
|
||||
entry.ImportOrdinalNumber = data.ReadUInt32LittleEndian();
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
bundle.TableEntries[i] = entry;
|
||||
obj.TableEntries[i] = entry;
|
||||
}
|
||||
|
||||
return bundle;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a fix-up record table entry
|
||||
/// Parse a Stream into an FixupPageTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled fix-up record table entry on success, null on error</returns>
|
||||
/// <returns>Filled FixupPageTableEntry on success, null on error</returns>
|
||||
public static FixupPageTableEntry ParseFixupPageTableEntry(Stream data)
|
||||
{
|
||||
var obj = new FixupPageTableEntry();
|
||||
|
||||
obj.Offset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FixupRecordTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FixupRecordTableEntry on success, null on error</returns>
|
||||
public static FixupRecordTableEntry? ParseFixupRecordTableEntry(Stream data)
|
||||
{
|
||||
var entry = new FixupRecordTableEntry();
|
||||
var obj = new FixupRecordTableEntry();
|
||||
|
||||
entry.SourceType = (FixupRecordSourceType)data.ReadByteValue();
|
||||
entry.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue();
|
||||
obj.SourceType = (FixupRecordSourceType)data.ReadByteValue();
|
||||
obj.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue();
|
||||
|
||||
// Source list flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.SourceType & FixupRecordSourceType.SourceListFlag) != 0)
|
||||
if ((obj.SourceType & FixupRecordSourceType.SourceListFlag) != 0)
|
||||
#else
|
||||
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
if (obj.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
#endif
|
||||
entry.SourceOffsetListCount = data.ReadByteValue();
|
||||
obj.SourceOffsetListCount = data.ReadByteValue();
|
||||
else
|
||||
entry.SourceOffset = data.ReadUInt16();
|
||||
obj.SourceOffset = data.ReadUInt16LittleEndian();
|
||||
|
||||
// OBJECT / TRGOFF
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReference) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.InternalReference) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference))
|
||||
#endif
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
#endif
|
||||
entry.TargetObjectNumberWORD = data.ReadUInt16();
|
||||
obj.TargetObjectNumberWORD = data.ReadUInt16LittleEndian();
|
||||
else
|
||||
entry.TargetObjectNumberByte = data.ReadByteValue();
|
||||
obj.TargetObjectNumberByte = data.ReadByteValue();
|
||||
|
||||
// 16-bit Selector fixup
|
||||
#if NET20 || NET35
|
||||
if ((entry.SourceType & FixupRecordSourceType.SixteenBitSelectorFixup) == 0)
|
||||
if ((obj.SourceType & FixupRecordSourceType.SixteenBitSelectorFixup) == 0)
|
||||
#else
|
||||
if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup))
|
||||
if (!obj.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup))
|
||||
#endif
|
||||
{
|
||||
// 32-bit Target Offset Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
#endif
|
||||
entry.TargetOffsetDWORD = data.ReadUInt32();
|
||||
obj.TargetOffsetDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.TargetOffsetWORD = data.ReadUInt16();
|
||||
obj.TargetOffsetWORD = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
// MOD ORD# / IMPORT ORD / ADDITIVE
|
||||
#if NET20 || NET35
|
||||
else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByOrdinal) != 0)
|
||||
else if ((obj.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByOrdinal) != 0)
|
||||
#else
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal))
|
||||
else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal))
|
||||
#endif
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
#endif
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// 8-bit Ordinal Flag & 32-bit Target Offset Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.EightBitOrdinalFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.EightBitOrdinalFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag))
|
||||
#endif
|
||||
entry.ImportedOrdinalNumberByte = data.ReadByteValue();
|
||||
obj.ImportedOrdinalNumberByte = data.ReadByteValue();
|
||||
#if NET20 || NET35
|
||||
else if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
else if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
#else
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
#endif
|
||||
entry.ImportedOrdinalNumberDWORD = data.ReadUInt32();
|
||||
obj.ImportedOrdinalNumberDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.ImportedOrdinalNumberWORD = data.ReadUInt16();
|
||||
obj.ImportedOrdinalNumberWORD = data.ReadUInt16LittleEndian();
|
||||
|
||||
// Additive Fixup Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
#endif
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
#endif
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
// MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
|
||||
#if NET20 || NET35
|
||||
else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByName) != 0)
|
||||
else if ((obj.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByName) != 0)
|
||||
#else
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName))
|
||||
else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName))
|
||||
#endif
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
#endif
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// 32-bit Target Offset Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
#endif
|
||||
entry.OffsetImportProcedureNameTableDWORD = data.ReadUInt32();
|
||||
obj.OffsetImportProcedureNameTableDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.OffsetImportProcedureNameTableWORD = data.ReadUInt16();
|
||||
obj.OffsetImportProcedureNameTableWORD = data.ReadUInt16LittleEndian();
|
||||
|
||||
// Additive Fixup Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
#endif
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
#endif
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
// ORD # / ADDITIVE
|
||||
#if NET20 || NET35
|
||||
else if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReferenceViaEntryTable) != 0)
|
||||
else if ((obj.TargetFlags & FixupRecordTargetFlags.InternalReferenceViaEntryTable) != 0)
|
||||
#else
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable))
|
||||
else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable))
|
||||
#endif
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
#endif
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// Additive Fixup Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
#endif
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
#if NET20 || NET35
|
||||
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
|
||||
#else
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
#endif
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,59 +686,135 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region SCROFFn
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((entry.SourceType & FixupRecordSourceType.SourceListFlag) != 0)
|
||||
if ((obj.SourceType & FixupRecordSourceType.SourceListFlag) != 0)
|
||||
#else
|
||||
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
if (obj.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
#endif
|
||||
{
|
||||
entry.SourceOffsetList = new ushort[entry.SourceOffsetListCount];
|
||||
for (int i = 0; i < entry.SourceOffsetList.Length; i++)
|
||||
obj.SourceOffsetList = new ushort[obj.SourceOffsetListCount];
|
||||
for (int i = 0; i < obj.SourceOffsetList.Length; i++)
|
||||
{
|
||||
entry.SourceOffsetList[i] = data.ReadUInt16();
|
||||
obj.SourceOffsetList[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a import module name table entry
|
||||
/// Parse a Stream into an ImportModuleNameTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled import module name table entry on success, null on error</returns>
|
||||
/// <returns>Filled ImportModuleNameTableEntry on success, null on error</returns>
|
||||
public static ImportModuleNameTableEntry ParseImportModuleNameTableEntry(Stream data)
|
||||
{
|
||||
var entry = new ImportModuleNameTableEntry();
|
||||
var obj = new ImportModuleNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
obj.Length = data.ReadByteValue();
|
||||
if (obj.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
byte[] name = data.ReadBytes(obj.Length);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a import module name table entry
|
||||
/// Parse a Stream into an ImportModuleProcedureNameTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled import module name table entry on success, null on error</returns>
|
||||
/// <returns>Filled ImportModuleProcedureNameTableEntry on success, null on error</returns>
|
||||
public static ImportModuleProcedureNameTableEntry ParseImportModuleProcedureNameTableEntry(Stream data)
|
||||
{
|
||||
var entry = new ImportModuleProcedureNameTableEntry();
|
||||
var obj = new ImportModuleProcedureNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
obj.Length = data.ReadByteValue();
|
||||
if (obj.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
byte[] name = data.ReadBytes(obj.Length);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a InformationBlock
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled InformationBlock on success, null on error</returns>
|
||||
public static InformationBlock ParseInformationBlock(Stream data)
|
||||
{
|
||||
var obj = new InformationBlock();
|
||||
|
||||
byte[] signature = data.ReadBytes(2);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.ByteOrder = (ByteOrder)data.ReadByteValue();
|
||||
obj.WordOrder = (WordOrder)data.ReadByteValue();
|
||||
obj.ExecutableFormatLevel = data.ReadUInt32LittleEndian();
|
||||
obj.CPUType = (CPUType)data.ReadUInt16LittleEndian();
|
||||
obj.ModuleOS = (OperatingSystem)data.ReadUInt16LittleEndian();
|
||||
obj.ModuleVersion = data.ReadUInt32LittleEndian();
|
||||
obj.ModuleTypeFlags = (ModuleFlags)data.ReadUInt32LittleEndian();
|
||||
obj.ModuleNumberPages = data.ReadUInt32LittleEndian();
|
||||
obj.InitialObjectCS = data.ReadUInt32LittleEndian();
|
||||
obj.InitialEIP = data.ReadUInt32LittleEndian();
|
||||
obj.InitialObjectSS = data.ReadUInt32LittleEndian();
|
||||
obj.InitialESP = data.ReadUInt32LittleEndian();
|
||||
obj.MemoryPageSize = data.ReadUInt32LittleEndian();
|
||||
obj.BytesOnLastPage = data.ReadUInt32LittleEndian();
|
||||
obj.FixupSectionSize = data.ReadUInt32LittleEndian();
|
||||
obj.FixupSectionChecksum = data.ReadUInt32LittleEndian();
|
||||
obj.LoaderSectionSize = data.ReadUInt32LittleEndian();
|
||||
obj.LoaderSectionChecksum = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectTableCount = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectPageMapOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectIterateDataMapOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ResourceTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ResourceTableCount = data.ReadUInt32LittleEndian();
|
||||
obj.ResidentNamesTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.EntryTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ModuleDirectivesTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ModuleDirectivesCount = data.ReadUInt32LittleEndian();
|
||||
obj.FixupPageTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FixupRecordTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ImportedModulesNameTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ImportedModulesCount = data.ReadUInt32LittleEndian();
|
||||
obj.ImportProcedureNameTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.PerPageChecksumTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DataPagesOffset = data.ReadUInt32LittleEndian();
|
||||
obj.PreloadPageCount = data.ReadUInt32LittleEndian();
|
||||
obj.NonResidentNamesTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.NonResidentNamesTableLength = data.ReadUInt32LittleEndian();
|
||||
obj.NonResidentNamesTableChecksum = data.ReadUInt32LittleEndian();
|
||||
obj.AutomaticDataObject = data.ReadUInt32LittleEndian();
|
||||
obj.DebugInformationOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DebugInformationLength = data.ReadUInt32LittleEndian();
|
||||
obj.PreloadInstancePagesNumber = data.ReadUInt32LittleEndian();
|
||||
obj.DemandInstancePagesNumber = data.ReadUInt32LittleEndian();
|
||||
obj.ExtraHeapAllocation = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ModuleFormatDirectivesTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ModuleFormatDirectivesTableEntry on success, null on error</returns>
|
||||
public static ModuleFormatDirectivesTableEntry ParseModuleFormatDirectivesTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ModuleFormatDirectivesTableEntry();
|
||||
|
||||
obj.DirectiveNumber = (DirectiveNumber)data.ReadUInt16LittleEndian();
|
||||
obj.DirectiveDataLength = data.ReadUInt16LittleEndian();
|
||||
obj.DirectiveDataOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -767,38 +824,105 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <returns>Filled non-resident names table entry on success, null on error</returns>
|
||||
public static NonResidentNamesTableEntry ParseNonResidentNameTableEntry(Stream data)
|
||||
{
|
||||
var entry = new NonResidentNamesTableEntry();
|
||||
var obj = new NonResidentNamesTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
obj.Length = data.ReadByteValue();
|
||||
if (obj.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
byte[] name = data.ReadBytes(obj.Length);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
return entry;
|
||||
obj.OrdinalNumber = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a debug information
|
||||
/// Parse a Stream into a ObjectPageMapEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="size">Total size of the debug information</param>
|
||||
/// <returns>Filled debug information on success, null on error</returns>
|
||||
public static DebugInformation? ParseDebugInformation(Stream data, long size)
|
||||
/// <returns>Filled ObjectPageMapEntry on success, null on error</returns>
|
||||
public static ObjectPageMapEntry ParseObjectPageMapEntry(Stream data)
|
||||
{
|
||||
var debugInformation = new DebugInformation();
|
||||
var obj = new ObjectPageMapEntry();
|
||||
|
||||
byte[] signature = data.ReadBytes(3);
|
||||
debugInformation.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (debugInformation.Signature != DebugInformationSignatureString)
|
||||
return null;
|
||||
obj.PageDataOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DataSize = data.ReadUInt16LittleEndian();
|
||||
obj.Flags = (ObjectPageFlags)data.ReadUInt16LittleEndian();
|
||||
|
||||
debugInformation.FormatType = (DebugFormatType)data.ReadByteValue();
|
||||
debugInformation.DebuggerData = data.ReadBytes((int)(size - 4));
|
||||
return obj;
|
||||
}
|
||||
|
||||
return debugInformation;
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ObjectTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ObjectTableEntry on success, null on error</returns>
|
||||
public static ObjectTableEntry ParseObjectTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ObjectTableEntry();
|
||||
|
||||
obj.RelocationBaseAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectFlags = (ObjectFlags)data.ReadUInt16LittleEndian();
|
||||
obj.PageTableIndex = data.ReadUInt32LittleEndian();
|
||||
obj.PageTableEntries = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a PerPageChecksumTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled PerPageChecksumTableEntry on success, null on error</returns>
|
||||
public static PerPageChecksumTableEntry ParsePerPageChecksumTableEntry(Stream data)
|
||||
{
|
||||
var obj = new PerPageChecksumTableEntry();
|
||||
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ResourceTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ResourceTableEntry on success, null on error</returns>
|
||||
public static ResourceTableEntry ParseResourceTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ResourceTableEntry();
|
||||
|
||||
obj.TypeID = (ResourceTableEntryType)data.ReadUInt32LittleEndian();
|
||||
obj.NameID = data.ReadUInt16LittleEndian();
|
||||
obj.ResourceSize = data.ReadUInt32LittleEndian();
|
||||
obj.ObjectNumber = data.ReadUInt16LittleEndian();
|
||||
obj.Offset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ResidentNamesTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ResidentNamesTableEntry on success, null on error</returns>
|
||||
public static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ResidentNamesTableEntry();
|
||||
|
||||
obj.Length = data.ReadByteValue();
|
||||
if (obj.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(obj.Length);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
obj.OrdinalNumber = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the executable header
|
||||
var executableHeader = ParseExecutableHeader(data);
|
||||
if (executableHeader == null)
|
||||
if (executableHeader.Magic != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the executable header
|
||||
@@ -44,12 +44,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the relocation table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems);
|
||||
if (relocationTable == null)
|
||||
return null;
|
||||
|
||||
// Set the relocation table
|
||||
executable.RelocationTable = relocationTable;
|
||||
executable.RelocationTable = new RelocationEntry[executableHeader.RelocationItems];
|
||||
for (int i = 0; i < executableHeader.RelocationItems; i++)
|
||||
{
|
||||
executable.RelocationTable[i] = ParseRelocationEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -64,82 +65,72 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an MS-DOS executable header
|
||||
/// Parse a Stream into an ExecutableHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled executable header on success, null on error</returns>
|
||||
private static ExecutableHeader? ParseExecutableHeader(Stream data)
|
||||
/// <returns>Filled ExecutableHeader on success, null on error</returns>
|
||||
public static ExecutableHeader ParseExecutableHeader(Stream data)
|
||||
{
|
||||
var header = new ExecutableHeader();
|
||||
var obj = new ExecutableHeader();
|
||||
|
||||
#region Standard Fields
|
||||
|
||||
byte[] magic = data.ReadBytes(2);
|
||||
header.Magic = Encoding.ASCII.GetString(magic);
|
||||
if (header.Magic != SignatureString)
|
||||
return null;
|
||||
|
||||
header.LastPageBytes = data.ReadUInt16();
|
||||
header.Pages = data.ReadUInt16();
|
||||
header.RelocationItems = data.ReadUInt16();
|
||||
header.HeaderParagraphSize = data.ReadUInt16();
|
||||
header.MinimumExtraParagraphs = data.ReadUInt16();
|
||||
header.MaximumExtraParagraphs = data.ReadUInt16();
|
||||
header.InitialSSValue = data.ReadUInt16();
|
||||
header.InitialSPValue = data.ReadUInt16();
|
||||
header.Checksum = data.ReadUInt16();
|
||||
header.InitialIPValue = data.ReadUInt16();
|
||||
header.InitialCSValue = data.ReadUInt16();
|
||||
header.RelocationTableAddr = data.ReadUInt16();
|
||||
header.OverlayNumber = data.ReadUInt16();
|
||||
obj.Magic = Encoding.ASCII.GetString(magic);
|
||||
obj.LastPageBytes = data.ReadUInt16LittleEndian();
|
||||
obj.Pages = data.ReadUInt16LittleEndian();
|
||||
obj.RelocationItems = data.ReadUInt16LittleEndian();
|
||||
obj.HeaderParagraphSize = data.ReadUInt16LittleEndian();
|
||||
obj.MinimumExtraParagraphs = data.ReadUInt16LittleEndian();
|
||||
obj.MaximumExtraParagraphs = data.ReadUInt16LittleEndian();
|
||||
obj.InitialSSValue = data.ReadUInt16LittleEndian();
|
||||
obj.InitialSPValue = data.ReadUInt16LittleEndian();
|
||||
obj.Checksum = data.ReadUInt16LittleEndian();
|
||||
obj.InitialIPValue = data.ReadUInt16LittleEndian();
|
||||
obj.InitialCSValue = data.ReadUInt16LittleEndian();
|
||||
obj.RelocationTableAddr = data.ReadUInt16LittleEndian();
|
||||
obj.OverlayNumber = data.ReadUInt16LittleEndian();
|
||||
|
||||
#endregion
|
||||
|
||||
// If we don't have enough data for PE extensions
|
||||
if (data.Position >= data.Length || data.Length - data.Position < 36)
|
||||
return header;
|
||||
return obj;
|
||||
|
||||
#region PE Extensions
|
||||
|
||||
header.Reserved1 = new ushort[4];
|
||||
for (int i = 0; i < header.Reserved1.Length; i++)
|
||||
obj.Reserved1 = new ushort[4];
|
||||
for (int i = 0; i < obj.Reserved1.Length; i++)
|
||||
{
|
||||
header.Reserved1[i] = data.ReadUInt16();
|
||||
obj.Reserved1[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
header.OEMIdentifier = data.ReadUInt16();
|
||||
header.OEMInformation = data.ReadUInt16();
|
||||
header.Reserved2 = new ushort[10];
|
||||
for (int i = 0; i < header.Reserved2.Length; i++)
|
||||
obj.OEMIdentifier = data.ReadUInt16LittleEndian();
|
||||
obj.OEMInformation = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved2 = new ushort[10];
|
||||
for (int i = 0; i < obj.Reserved2.Length; i++)
|
||||
{
|
||||
header.Reserved2[i] = data.ReadUInt16();
|
||||
obj.Reserved2[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
header.NewExeHeaderAddr = data.ReadUInt32();
|
||||
obj.NewExeHeaderAddr = data.ReadUInt32LittleEndian();
|
||||
|
||||
#endregion
|
||||
|
||||
return header;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a relocation table
|
||||
/// Parse a Stream into an ExecutableHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of relocation table entries to read</param>
|
||||
/// <returns>Filled relocation table on success, null on error</returns>
|
||||
private static RelocationEntry[]? ParseRelocationTable(Stream data, int count)
|
||||
/// <returns>Filled ExecutableHeader on success, null on error</returns>
|
||||
public static RelocationEntry ParseRelocationEntry(Stream data)
|
||||
{
|
||||
var relocationTable = new RelocationEntry[count];
|
||||
var obj = new RelocationEntry();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entry = data.ReadType<RelocationEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
obj.Offset = data.ReadUInt16LittleEndian();
|
||||
obj.Segment = data.ReadUInt16LittleEndian();
|
||||
|
||||
relocationTable[i] = entry;
|
||||
}
|
||||
|
||||
return relocationTable;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,18 +103,18 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
header.Reserved1 = data.ReadUInt32();
|
||||
header.CabinetSize = data.ReadUInt32();
|
||||
header.Reserved2 = data.ReadUInt32();
|
||||
header.FilesOffset = data.ReadUInt32();
|
||||
header.Reserved3 = data.ReadUInt32();
|
||||
header.Reserved1 = data.ReadUInt32LittleEndian();
|
||||
header.CabinetSize = data.ReadUInt32LittleEndian();
|
||||
header.Reserved2 = data.ReadUInt32LittleEndian();
|
||||
header.FilesOffset = data.ReadUInt32LittleEndian();
|
||||
header.Reserved3 = data.ReadUInt32LittleEndian();
|
||||
header.VersionMinor = data.ReadByteValue();
|
||||
header.VersionMajor = data.ReadByteValue();
|
||||
header.FolderCount = data.ReadUInt16();
|
||||
header.FileCount = data.ReadUInt16();
|
||||
header.Flags = (HeaderFlags)data.ReadUInt16();
|
||||
header.SetID = data.ReadUInt16();
|
||||
header.CabinetIndex = data.ReadUInt16();
|
||||
header.FolderCount = data.ReadUInt16LittleEndian();
|
||||
header.FileCount = data.ReadUInt16LittleEndian();
|
||||
header.Flags = (HeaderFlags)data.ReadUInt16LittleEndian();
|
||||
header.SetID = data.ReadUInt16LittleEndian();
|
||||
header.CabinetIndex = data.ReadUInt16LittleEndian();
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((header.Flags & HeaderFlags.RESERVE_PRESENT) != 0)
|
||||
@@ -122,7 +122,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
|
||||
#endif
|
||||
{
|
||||
header.HeaderReservedSize = data.ReadUInt16();
|
||||
header.HeaderReservedSize = data.ReadUInt16LittleEndian();
|
||||
if (header.HeaderReservedSize > 60_000)
|
||||
return null;
|
||||
|
||||
@@ -166,9 +166,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var folder = new CFFOLDER();
|
||||
|
||||
folder.CabStartOffset = data.ReadUInt32();
|
||||
folder.DataCount = data.ReadUInt16();
|
||||
folder.CompressionType = (CompressionType)data.ReadUInt16();
|
||||
folder.CabStartOffset = data.ReadUInt32LittleEndian();
|
||||
folder.DataCount = data.ReadUInt16LittleEndian();
|
||||
folder.CompressionType = (CompressionType)data.ReadUInt16LittleEndian();
|
||||
|
||||
if (header.FolderReservedSize > 0)
|
||||
folder.ReservedData = data.ReadBytes(header.FolderReservedSize);
|
||||
@@ -201,9 +201,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var dataBlock = new CFDATA();
|
||||
|
||||
dataBlock.Checksum = data.ReadUInt32();
|
||||
dataBlock.CompressedSize = data.ReadUInt16();
|
||||
dataBlock.UncompressedSize = data.ReadUInt16();
|
||||
dataBlock.Checksum = data.ReadUInt32LittleEndian();
|
||||
dataBlock.CompressedSize = data.ReadUInt16LittleEndian();
|
||||
dataBlock.UncompressedSize = data.ReadUInt16LittleEndian();
|
||||
|
||||
if (dataReservedSize > 0)
|
||||
dataBlock.ReservedData = data.ReadBytes(dataReservedSize);
|
||||
@@ -223,12 +223,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var file = new CFFILE();
|
||||
|
||||
file.FileSize = data.ReadUInt32();
|
||||
file.FolderStartOffset = data.ReadUInt32();
|
||||
file.FolderIndex = (FolderIndex)data.ReadUInt16();
|
||||
file.Date = data.ReadUInt16();
|
||||
file.Time = data.ReadUInt16();
|
||||
file.Attributes = (Models.MicrosoftCabinet.FileAttributes)data.ReadUInt16();
|
||||
file.FileSize = data.ReadUInt32LittleEndian();
|
||||
file.FolderStartOffset = data.ReadUInt32LittleEndian();
|
||||
file.FolderIndex = (FolderIndex)data.ReadUInt16LittleEndian();
|
||||
file.Date = data.ReadUInt16LittleEndian();
|
||||
file.Time = data.ReadUInt16LittleEndian();
|
||||
file.Attributes = (Models.MicrosoftCabinet.FileAttributes)data.ReadUInt16LittleEndian();
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((file.Attributes & Models.MicrosoftCabinet.FileAttributes.NAME_IS_UTF) != 0)
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region User Data
|
||||
|
||||
// Check for User Data
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
uint possibleSignature = data.ReadUInt32LittleEndian();
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
if (possibleSignature == UserDataSignatureUInt32)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
long basePtr = data.Position;
|
||||
|
||||
// Deserialize the user data, returning null if invalid
|
||||
var userData = data.ReadType<UserData>();
|
||||
var userData = ParseUserData(data);
|
||||
if (userData?.Signature != UserDataSignatureString)
|
||||
return null;
|
||||
|
||||
@@ -49,13 +49,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Archive Header
|
||||
|
||||
// Check for the Header
|
||||
possibleSignature = data.ReadUInt32();
|
||||
possibleSignature = data.ReadUInt32LittleEndian();
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
if (possibleSignature == ArchiveHeaderSignatureUInt32)
|
||||
{
|
||||
// Try to parse the archive header
|
||||
var archiveHeader = ParseArchiveHeader(data);
|
||||
if (archiveHeader == null)
|
||||
if (archiveHeader.Signature != ArchiveHeaderSignatureString)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
@@ -90,10 +90,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < hashTableEnd)
|
||||
{
|
||||
var hashEntry = data.ReadType<HashEntry>();
|
||||
if (hashEntry == null)
|
||||
return null;
|
||||
|
||||
var hashEntry = ParseHashEntry(data);
|
||||
hashTable.Add(hashEntry);
|
||||
}
|
||||
|
||||
@@ -120,10 +117,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < hashTableEnd)
|
||||
{
|
||||
var hashEntry = data.ReadType<HashEntry>();
|
||||
if (hashEntry == null)
|
||||
return null;
|
||||
|
||||
var hashEntry = ParseHashEntry(data);
|
||||
hashTable.Add(hashEntry);
|
||||
}
|
||||
|
||||
@@ -149,10 +143,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < hashTableEnd)
|
||||
{
|
||||
var hashEntry = data.ReadType<HashEntry>();
|
||||
if (hashEntry == null)
|
||||
return null;
|
||||
|
||||
var hashEntry = ParseHashEntry(data);
|
||||
hashTable.Add(hashEntry);
|
||||
}
|
||||
|
||||
@@ -182,10 +173,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < blockTableEnd)
|
||||
{
|
||||
var blockEntry = data.ReadType<BlockEntry>();
|
||||
if (blockEntry == null)
|
||||
return null;
|
||||
|
||||
var blockEntry = ParseBlockEntry(data);
|
||||
blockTable.Add(blockEntry);
|
||||
}
|
||||
|
||||
@@ -212,10 +200,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < blockTableEnd)
|
||||
{
|
||||
var blockEntry = data.ReadType<BlockEntry>();
|
||||
if (blockEntry == null)
|
||||
return null;
|
||||
|
||||
var blockEntry = ParseBlockEntry(data);
|
||||
blockTable.Add(blockEntry);
|
||||
}
|
||||
|
||||
@@ -241,10 +226,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
while (data.Position < blockTableEnd)
|
||||
{
|
||||
var blockEntry = data.ReadType<BlockEntry>();
|
||||
if (blockEntry == null)
|
||||
return null;
|
||||
|
||||
var blockEntry = ParseBlockEntry(data);
|
||||
blockTable.Add(blockEntry);
|
||||
}
|
||||
|
||||
@@ -271,7 +253,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
for (int i = 0; i < (archive.BlockTable?.Length ?? 0); i++)
|
||||
{
|
||||
short hiBlockEntry = data.ReadInt16();
|
||||
short hiBlockEntry = data.ReadInt16LittleEndian();
|
||||
hiBlockTable.Add(hiBlockEntry);
|
||||
}
|
||||
|
||||
@@ -295,7 +277,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Read in the BET table
|
||||
var betTable = ParseBetTable(data);
|
||||
if (betTable != null)
|
||||
if (betTable.Signature != BetTableSignatureString)
|
||||
return null;
|
||||
|
||||
archive.BetTable = betTable;
|
||||
@@ -318,7 +300,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Read in the HET table
|
||||
var hetTable = ParseHetTable(data);
|
||||
if (hetTable != null)
|
||||
if (hetTable.Signature != HetTableSignatureString)
|
||||
return null;
|
||||
|
||||
archive.HetTable = hetTable;
|
||||
@@ -337,150 +319,195 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a archive header
|
||||
/// Parse a Stream into an ArchiveHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled archive header on success, null on error</returns>
|
||||
private static ArchiveHeader? ParseArchiveHeader(Stream data)
|
||||
/// <returns>Filled ArchiveHeader on success, null on error</returns>
|
||||
public static ArchiveHeader ParseArchiveHeader(Stream data)
|
||||
{
|
||||
ArchiveHeader archiveHeader = new ArchiveHeader();
|
||||
var obj = new ArchiveHeader();
|
||||
|
||||
// V1 - Common
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
archiveHeader.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (archiveHeader.Signature != ArchiveHeaderSignatureString)
|
||||
return null;
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
|
||||
archiveHeader.HeaderSize = data.ReadUInt32();
|
||||
archiveHeader.ArchiveSize = data.ReadUInt32();
|
||||
archiveHeader.FormatVersion = (FormatVersion)data.ReadUInt16();
|
||||
archiveHeader.BlockSize = data.ReadUInt16();
|
||||
archiveHeader.HashTablePosition = data.ReadUInt32();
|
||||
archiveHeader.BlockTablePosition = data.ReadUInt32();
|
||||
archiveHeader.HashTableSize = data.ReadUInt32();
|
||||
archiveHeader.BlockTableSize = data.ReadUInt32();
|
||||
obj.HeaderSize = data.ReadUInt32LittleEndian();
|
||||
obj.ArchiveSize = data.ReadUInt32LittleEndian();
|
||||
obj.FormatVersion = (FormatVersion)data.ReadUInt16LittleEndian();
|
||||
obj.BlockSize = data.ReadUInt16LittleEndian();
|
||||
obj.HashTablePosition = data.ReadUInt32LittleEndian();
|
||||
obj.BlockTablePosition = data.ReadUInt32LittleEndian();
|
||||
obj.HashTableSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockTableSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// V2
|
||||
if (archiveHeader.FormatVersion >= FormatVersion.Format2)
|
||||
if (obj.FormatVersion >= FormatVersion.Format2)
|
||||
{
|
||||
archiveHeader.HiBlockTablePosition = data.ReadUInt64();
|
||||
archiveHeader.HashTablePositionHi = data.ReadUInt16();
|
||||
archiveHeader.BlockTablePositionHi = data.ReadUInt16();
|
||||
obj.HiBlockTablePosition = data.ReadUInt64LittleEndian();
|
||||
obj.HashTablePositionHi = data.ReadUInt16LittleEndian();
|
||||
obj.BlockTablePositionHi = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
|
||||
// V3
|
||||
if (archiveHeader.FormatVersion >= FormatVersion.Format3)
|
||||
if (obj.FormatVersion >= FormatVersion.Format3)
|
||||
{
|
||||
archiveHeader.ArchiveSizeLong = data.ReadUInt64();
|
||||
archiveHeader.BetTablePosition = data.ReadUInt64();
|
||||
archiveHeader.HetTablePosition = data.ReadUInt64();
|
||||
obj.ArchiveSizeLong = data.ReadUInt64LittleEndian();
|
||||
obj.BetTablePosition = data.ReadUInt64LittleEndian();
|
||||
obj.HetTablePosition = data.ReadUInt64LittleEndian();
|
||||
}
|
||||
|
||||
// V4
|
||||
if (archiveHeader.FormatVersion >= FormatVersion.Format4)
|
||||
if (obj.FormatVersion >= FormatVersion.Format4)
|
||||
{
|
||||
archiveHeader.HashTableSizeLong = data.ReadUInt64();
|
||||
archiveHeader.BlockTableSizeLong = data.ReadUInt64();
|
||||
archiveHeader.HiBlockTableSize = data.ReadUInt64();
|
||||
archiveHeader.HetTableSize = data.ReadUInt64();
|
||||
archiveHeader.BetTablesize = data.ReadUInt64();
|
||||
archiveHeader.RawChunkSize = data.ReadUInt32();
|
||||
obj.HashTableSizeLong = data.ReadUInt64LittleEndian();
|
||||
obj.BlockTableSizeLong = data.ReadUInt64LittleEndian();
|
||||
obj.HiBlockTableSize = data.ReadUInt64LittleEndian();
|
||||
obj.HetTableSize = data.ReadUInt64LittleEndian();
|
||||
obj.BetTablesize = data.ReadUInt64LittleEndian();
|
||||
obj.RawChunkSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
archiveHeader.BlockTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HashTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HiBlockTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.BetTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(0x10);
|
||||
archiveHeader.HetTableMD5 = data.ReadBytes(0x10);
|
||||
obj.BlockTableMD5 = data.ReadBytes(0x10);
|
||||
obj.HashTableMD5 = data.ReadBytes(0x10);
|
||||
obj.HiBlockTableMD5 = data.ReadBytes(0x10);
|
||||
obj.BetTableMD5 = data.ReadBytes(0x10);
|
||||
obj.HetTableMD5 = data.ReadBytes(0x10);
|
||||
obj.HetTableMD5 = data.ReadBytes(0x10);
|
||||
}
|
||||
|
||||
return archiveHeader;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HET table
|
||||
/// Parse a Stream into a BetTable
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HET table on success, null on error</returns>
|
||||
private static HetTable? ParseHetTable(Stream data)
|
||||
/// <returns>Filled BetTable on success, null on error</returns>
|
||||
public static BetTable ParseBetTable(Stream data)
|
||||
{
|
||||
var hetTable = new HetTable();
|
||||
var obj = new BetTable();
|
||||
|
||||
// Common Headers
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
hetTable.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (hetTable.Signature != HetTableSignatureString)
|
||||
return null;
|
||||
|
||||
hetTable.Version = data.ReadUInt32();
|
||||
hetTable.DataSize = data.ReadUInt32();
|
||||
|
||||
// HET-Specific
|
||||
hetTable.TableSize = data.ReadUInt32();
|
||||
hetTable.MaxFileCount = data.ReadUInt32();
|
||||
hetTable.HashTableSize = data.ReadUInt32();
|
||||
hetTable.TotalIndexSize = data.ReadUInt32();
|
||||
hetTable.IndexSizeExtra = data.ReadUInt32();
|
||||
hetTable.IndexSize = data.ReadUInt32();
|
||||
hetTable.BlockTableSize = data.ReadUInt32();
|
||||
hetTable.HashTable = data.ReadBytes((int)hetTable.HashTableSize);
|
||||
|
||||
// TODO: Populate the file indexes array
|
||||
hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
|
||||
|
||||
return hetTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a BET table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BET table on success, null on error</returns>
|
||||
private static BetTable? ParseBetTable(Stream data)
|
||||
{
|
||||
var betTable = new BetTable();
|
||||
|
||||
// Common Headers
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
betTable.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (betTable.Signature != BetTableSignatureString)
|
||||
return null;
|
||||
|
||||
betTable.Version = data.ReadUInt32();
|
||||
betTable.DataSize = data.ReadUInt32();
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.DataSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// BET-Specific
|
||||
betTable.TableSize = data.ReadUInt32();
|
||||
betTable.FileCount = data.ReadUInt32();
|
||||
betTable.Unknown = data.ReadUInt32();
|
||||
betTable.TableEntrySize = data.ReadUInt32();
|
||||
obj.TableSize = data.ReadUInt32LittleEndian();
|
||||
obj.FileCount = data.ReadUInt32LittleEndian();
|
||||
obj.Unknown = data.ReadUInt32LittleEndian();
|
||||
obj.TableEntrySize = data.ReadUInt32LittleEndian();
|
||||
|
||||
betTable.FilePositionBitIndex = data.ReadUInt32();
|
||||
betTable.FileSizeBitIndex = data.ReadUInt32();
|
||||
betTable.CompressedSizeBitIndex = data.ReadUInt32();
|
||||
betTable.FlagIndexBitIndex = data.ReadUInt32();
|
||||
betTable.UnknownBitIndex = data.ReadUInt32();
|
||||
obj.FilePositionBitIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FileSizeBitIndex = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSizeBitIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FlagIndexBitIndex = data.ReadUInt32LittleEndian();
|
||||
obj.UnknownBitIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
betTable.FilePositionBitCount = data.ReadUInt32();
|
||||
betTable.FileSizeBitCount = data.ReadUInt32();
|
||||
betTable.CompressedSizeBitCount = data.ReadUInt32();
|
||||
betTable.FlagIndexBitCount = data.ReadUInt32();
|
||||
betTable.UnknownBitCount = data.ReadUInt32();
|
||||
obj.FilePositionBitCount = data.ReadUInt32LittleEndian();
|
||||
obj.FileSizeBitCount = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSizeBitCount = data.ReadUInt32LittleEndian();
|
||||
obj.FlagIndexBitCount = data.ReadUInt32LittleEndian();
|
||||
obj.UnknownBitCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
betTable.TotalBetHashSize = data.ReadUInt32();
|
||||
betTable.BetHashSizeExtra = data.ReadUInt32();
|
||||
betTable.BetHashSize = data.ReadUInt32();
|
||||
betTable.BetHashArraySize = data.ReadUInt32();
|
||||
betTable.FlagCount = data.ReadUInt32();
|
||||
obj.TotalBetHashSize = data.ReadUInt32LittleEndian();
|
||||
obj.BetHashSizeExtra = data.ReadUInt32LittleEndian();
|
||||
obj.BetHashSize = data.ReadUInt32LittleEndian();
|
||||
obj.BetHashArraySize = data.ReadUInt32LittleEndian();
|
||||
obj.FlagCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
betTable.FlagsArray = new uint[betTable.FlagCount];
|
||||
byte[] flagsArray = data.ReadBytes((int)betTable.FlagCount * 4);
|
||||
Buffer.BlockCopy(flagsArray, 0, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
|
||||
obj.FlagsArray = new uint[obj.FlagCount];
|
||||
byte[] flagsArray = data.ReadBytes((int)obj.FlagCount * 4);
|
||||
Buffer.BlockCopy(flagsArray, 0, obj.FlagsArray, 0, (int)obj.FlagCount * 4);
|
||||
|
||||
// TODO: Populate the file table
|
||||
// TODO: Populate the hash table
|
||||
|
||||
return betTable;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an BlockEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BlockEntry on success, null on error</returns>
|
||||
public static BlockEntry ParseBlockEntry(Stream data)
|
||||
{
|
||||
var obj = new BlockEntry();
|
||||
|
||||
obj.FilePosition = data.ReadUInt32LittleEndian();
|
||||
obj.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = (FileFlags)data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an HashEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HashEntry on success, null on error</returns>
|
||||
public static HashEntry ParseHashEntry(Stream data)
|
||||
{
|
||||
var obj = new HashEntry();
|
||||
|
||||
obj.NameHashPartA = data.ReadUInt32LittleEndian();
|
||||
obj.NameHashPartB = data.ReadUInt32LittleEndian();
|
||||
obj.Locale = (Locale)data.ReadInt16LittleEndian();
|
||||
obj.Platform = data.ReadUInt16LittleEndian();
|
||||
obj.BlockIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HetTable
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled HetTable on success, null on error</returns>
|
||||
public static HetTable ParseHetTable(Stream data)
|
||||
{
|
||||
var obj = new HetTable();
|
||||
|
||||
// Common Headers
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.DataSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// HET-Specific
|
||||
obj.TableSize = data.ReadUInt32LittleEndian();
|
||||
obj.MaxFileCount = data.ReadUInt32LittleEndian();
|
||||
obj.HashTableSize = data.ReadUInt32LittleEndian();
|
||||
obj.TotalIndexSize = data.ReadUInt32LittleEndian();
|
||||
obj.IndexSizeExtra = data.ReadUInt32LittleEndian();
|
||||
obj.IndexSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockTableSize = data.ReadUInt32LittleEndian();
|
||||
obj.HashTable = data.ReadBytes((int)obj.HashTableSize);
|
||||
|
||||
// TODO: Populate the file indexes array
|
||||
obj.FileIndexes = new byte[(int)obj.HashTableSize][];
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a UserData
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled UserData on success, null on error</returns>
|
||||
public static UserData ParseUserData(Stream data)
|
||||
{
|
||||
var obj = new UserData();
|
||||
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.UserDataSize = data.ReadUInt32LittleEndian();
|
||||
obj.HeaderOffset = data.ReadUInt32LittleEndian();
|
||||
obj.UserDataHeaderSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
@@ -488,7 +515,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <summary>
|
||||
/// Buffer for encryption and decryption
|
||||
/// </summary>
|
||||
private uint[] _stormBuffer = new uint[STORM_BUFFER_SIZE];
|
||||
private readonly uint[] _stormBuffer = new uint[STORM_BUFFER_SIZE];
|
||||
|
||||
/// <summary>
|
||||
/// Prepare the encryption table
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseNCSDHeader(data);
|
||||
if (header == null)
|
||||
if (header.MagicNumber != NCSDMagicNumber)
|
||||
return null;
|
||||
|
||||
// Set the cart image header
|
||||
@@ -35,25 +35,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Card Info Header
|
||||
|
||||
// Try to parse the card info header
|
||||
var cardInfoHeader = ParseCardInfoHeader(data);
|
||||
if (cardInfoHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the card info header
|
||||
cart.CardInfoHeader = cardInfoHeader;
|
||||
cart.CardInfoHeader = ParseCardInfoHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Development Card Info Header
|
||||
|
||||
// Try to parse the development card info header
|
||||
var developmentCardInfoHeader = data.ReadType<DevelopmentCardInfoHeader>();
|
||||
if (developmentCardInfoHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the development card info header
|
||||
cart.DevelopmentCardInfoHeader = developmentCardInfoHeader;
|
||||
cart.DevelopmentCardInfoHeader = ParseDevelopmentCardInfoHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -92,11 +82,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Handle the extended header, if it exists
|
||||
if (partition.ExtendedHeaderSizeInBytes > 0)
|
||||
{
|
||||
var extendedHeader = data.ReadType<NCCHExtendedHeader>();
|
||||
if (extendedHeader != null)
|
||||
cart.ExtendedHeaders[i] = extendedHeader;
|
||||
}
|
||||
cart.ExtendedHeaders[i] = ParseNCCHExtendedHeader(data);
|
||||
|
||||
// Handle the ExeFS, if it exists
|
||||
if (partition.ExeFSSizeInMediaUnits > 0)
|
||||
@@ -117,10 +103,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
long offset = partition.RomFSOffsetInMediaUnits * mediaUnitSize;
|
||||
data.Seek(partitionOffset + offset, SeekOrigin.Begin);
|
||||
|
||||
var romFsHeader = data.ReadType<RomFSHeader>();
|
||||
if (romFsHeader?.MagicString != RomFSMagicNumber)
|
||||
var romFsHeader = ParseRomFSHeader(data);
|
||||
if (romFsHeader.MagicString != RomFSMagicNumber)
|
||||
continue;
|
||||
if (romFsHeader?.MagicNumber != RomFSSecondMagicNumber)
|
||||
if (romFsHeader.MagicNumber != RomFSSecondMagicNumber)
|
||||
continue;
|
||||
|
||||
cart.RomFSHeaders[i] = romFsHeader;
|
||||
@@ -139,85 +125,193 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an NCSD header
|
||||
/// Parse a Stream into a AccessControlInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled NCSD header on success, null on error</returns>
|
||||
public static NCSDHeader? ParseNCSDHeader(Stream data)
|
||||
/// <returns>Filled AccessControlInfo on success, null on error</returns>
|
||||
public static AccessControlInfo ParseAccessControlInfo(Stream data)
|
||||
{
|
||||
var header = new NCSDHeader();
|
||||
var obj = new AccessControlInfo();
|
||||
|
||||
header.RSA2048Signature = data.ReadBytes(0x100);
|
||||
byte[] magicNumber = data.ReadBytes(4);
|
||||
header.MagicNumber = Encoding.ASCII.GetString(magicNumber).TrimEnd('\0'); ;
|
||||
if (header.MagicNumber != NCSDMagicNumber)
|
||||
return null;
|
||||
obj.ARM11LocalSystemCapabilities = ParseARM11LocalSystemCapabilities(data);
|
||||
obj.ARM11KernelCapabilities = ParseARM11KernelCapabilities(data);
|
||||
obj.ARM9AccessControl = ParseARM9AccessControl(data);
|
||||
|
||||
header.ImageSizeInMediaUnits = data.ReadUInt32();
|
||||
header.MediaId = data.ReadBytes(8);
|
||||
header.PartitionsFSType = (FilesystemType)data.ReadUInt64();
|
||||
header.PartitionsCryptType = data.ReadBytes(8);
|
||||
|
||||
header.PartitionsTable = new PartitionTableEntry[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var partitionTableEntry = data.ReadType<PartitionTableEntry>();
|
||||
if (partitionTableEntry == null)
|
||||
return null;
|
||||
|
||||
header.PartitionsTable[i] = partitionTableEntry;
|
||||
}
|
||||
|
||||
if (header.PartitionsFSType == FilesystemType.Normal || header.PartitionsFSType == FilesystemType.None)
|
||||
{
|
||||
header.ExheaderHash = data.ReadBytes(0x20);
|
||||
header.AdditionalHeaderSize = data.ReadUInt32();
|
||||
header.SectorZeroOffset = data.ReadUInt32();
|
||||
header.PartitionFlags = data.ReadBytes(8);
|
||||
|
||||
header.PartitionIdTable = new ulong[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
header.PartitionIdTable[i] = data.ReadUInt64();
|
||||
}
|
||||
|
||||
header.Reserved1 = data.ReadBytes(0x20);
|
||||
header.Reserved2 = data.ReadBytes(0x0E);
|
||||
header.FirmUpdateByte1 = data.ReadByteValue();
|
||||
header.FirmUpdateByte2 = data.ReadByteValue();
|
||||
}
|
||||
else if (header.PartitionsFSType == FilesystemType.FIRM)
|
||||
{
|
||||
header.Unknown = data.ReadBytes(0x5E);
|
||||
header.EncryptedMBR = data.ReadBytes(0x42);
|
||||
}
|
||||
|
||||
return header;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a card info header
|
||||
/// Parse a Stream into a ARM9AccessControl
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled card info header on success, null on error</returns>
|
||||
public static CardInfoHeader? ParseCardInfoHeader(Stream data)
|
||||
/// <returns>Filled ARM9AccessControl on success, null on error</returns>
|
||||
public static ARM9AccessControl ParseARM9AccessControl(Stream data)
|
||||
{
|
||||
var header = new CardInfoHeader();
|
||||
var obj = new ARM9AccessControl();
|
||||
|
||||
header.WritableAddressMediaUnits = data.ReadUInt32();
|
||||
header.CardInfoBitmask = data.ReadUInt32();
|
||||
header.Reserved1 = data.ReadBytes(0xF8);
|
||||
header.FilledSize = data.ReadUInt32();
|
||||
header.Reserved2 = data.ReadBytes(0x0C);
|
||||
header.TitleVersion = data.ReadUInt16();
|
||||
header.CardRevision = data.ReadUInt16();
|
||||
header.Reserved3 = data.ReadBytes(0x0C);
|
||||
header.CVerTitleID = data.ReadBytes(0x08);
|
||||
header.CVerVersionNumber = data.ReadUInt16();
|
||||
header.Reserved4 = data.ReadBytes(0xCD6);
|
||||
header.InitialData = ParseInitialData(data);
|
||||
obj.Descriptors = new ARM9AccessControlDescriptors[15];
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
obj.Descriptors[i] = (ARM9AccessControlDescriptors)data.ReadByteValue();
|
||||
}
|
||||
obj.DescriptorVersion = data.ReadByteValue();
|
||||
|
||||
return header;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ARM11KernelCapabilities
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ARM11KernelCapabilities on success, null on error</returns>
|
||||
public static ARM11KernelCapabilities ParseARM11KernelCapabilities(Stream data)
|
||||
{
|
||||
var obj = new ARM11KernelCapabilities();
|
||||
|
||||
obj.Descriptors = new uint[28];
|
||||
for (int i = 0; i < 28; i++)
|
||||
{
|
||||
obj.Descriptors[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.Reserved = data.ReadBytes(0x10);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ARM11LocalSystemCapabilities
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ARM11LocalSystemCapabilities on success, null on error</returns>
|
||||
public static ARM11LocalSystemCapabilities ParseARM11LocalSystemCapabilities(Stream data)
|
||||
{
|
||||
var obj = new ARM11LocalSystemCapabilities();
|
||||
|
||||
obj.ProgramID = data.ReadUInt64LittleEndian();
|
||||
obj.CoreVersion = data.ReadUInt32LittleEndian();
|
||||
obj.Flag1 = (ARM11LSCFlag1)data.ReadByteValue();
|
||||
obj.Flag2 = (ARM11LSCFlag2)data.ReadByteValue();
|
||||
obj.Flag0 = (ARM11LSCFlag0)data.ReadByteValue();
|
||||
obj.Priority = data.ReadByteValue();
|
||||
obj.ResourceLimitDescriptors = new ushort[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
obj.ResourceLimitDescriptors[i] = data.ReadUInt16LittleEndian();
|
||||
}
|
||||
obj.StorageInfo = ParseStorageInfo(data);
|
||||
obj.ServiceAccessControl = new ulong[32];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
obj.ServiceAccessControl[i] = data.ReadUInt64LittleEndian();
|
||||
}
|
||||
obj.ExtendedServiceAccessControl = new ulong[2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
obj.ExtendedServiceAccessControl[i] = data.ReadUInt64LittleEndian();
|
||||
}
|
||||
obj.Reserved = data.ReadBytes(0x0F);
|
||||
obj.ResourceLimitCategory = (ResourceLimitCategory)data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CardInfoHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CardInfoHeader on success, null on error</returns>
|
||||
public static CardInfoHeader ParseCardInfoHeader(Stream data)
|
||||
{
|
||||
var obj = new CardInfoHeader();
|
||||
|
||||
obj.WritableAddressMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.CardInfoBitmask = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved1 = data.ReadBytes(0xF8);
|
||||
obj.FilledSize = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(0x0C);
|
||||
obj.TitleVersion = data.ReadUInt16LittleEndian();
|
||||
obj.CardRevision = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved3 = data.ReadBytes(0x0C);
|
||||
obj.CVerTitleID = data.ReadBytes(0x08);
|
||||
obj.CVerVersionNumber = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved4 = data.ReadBytes(0xCD6);
|
||||
obj.InitialData = ParseInitialData(data);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CodeSetInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CodeSetInfo on success, null on error</returns>
|
||||
public static CodeSetInfo ParseCodeSetInfo(Stream data)
|
||||
{
|
||||
var obj = new CodeSetInfo();
|
||||
|
||||
obj.Address = data.ReadUInt32LittleEndian();
|
||||
obj.PhysicalRegionSizeInPages = data.ReadUInt32LittleEndian();
|
||||
obj.SizeInBytes = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DevelopmentCardInfoHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DevelopmentCardInfoHeader on success, null on error</returns>
|
||||
public static DevelopmentCardInfoHeader ParseDevelopmentCardInfoHeader(Stream data)
|
||||
{
|
||||
var obj = new DevelopmentCardInfoHeader();
|
||||
|
||||
obj.CardDeviceReserved1 = data.ReadBytes(0x200);
|
||||
obj.TitleKey = data.ReadBytes(0x10);
|
||||
obj.CardDeviceReserved2 = data.ReadBytes(0x1BF0);
|
||||
obj.TestData = ParseTestData(data);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ExeFSFileHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExeFSFileHeader on success, null on error</returns>
|
||||
public static ExeFSFileHeader ParseExeFSFileHeader(Stream data)
|
||||
{
|
||||
var obj = new ExeFSFileHeader();
|
||||
|
||||
byte[] fileName = data.ReadBytes(8);
|
||||
obj.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0');
|
||||
obj.FileOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an ExeFSHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExeFSHeader on success, null on error</returns>
|
||||
public static ExeFSHeader ParseExeFSHeader(Stream data)
|
||||
{
|
||||
var obj = new ExeFSHeader();
|
||||
|
||||
obj.FileHeaders = new ExeFSFileHeader[10];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
obj.FileHeaders[i] = ParseExeFSFileHeader(data);
|
||||
}
|
||||
obj.Reserved = data.ReadBytes(0x20);
|
||||
obj.FileHashes = new byte[10][];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
obj.FileHashes[i] = data.ReadBytes(0x20);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -227,92 +321,285 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <returns>Filled initial data on success, null on error</returns>
|
||||
public static InitialData? ParseInitialData(Stream data)
|
||||
{
|
||||
var id = new InitialData();
|
||||
var obj = new InitialData();
|
||||
|
||||
id.CardSeedKeyY = data.ReadBytes(0x10);
|
||||
id.EncryptedCardSeed = data.ReadBytes(0x10);
|
||||
id.CardSeedAESMAC = data.ReadBytes(0x10);
|
||||
id.CardSeedNonce = data.ReadBytes(0x0C);
|
||||
id.Reserved = data.ReadBytes(0xC4);
|
||||
id.BackupHeader = ParseNCCHHeader(data, skipSignature: true);
|
||||
obj.CardSeedKeyY = data.ReadBytes(0x10);
|
||||
obj.EncryptedCardSeed = data.ReadBytes(0x10);
|
||||
obj.CardSeedAESMAC = data.ReadBytes(0x10);
|
||||
obj.CardSeedNonce = data.ReadBytes(0x0C);
|
||||
obj.Reserved = data.ReadBytes(0xC4);
|
||||
obj.BackupHeader = ParseNCCHHeader(data, skipSignature: true);
|
||||
|
||||
return id;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an NCCH header
|
||||
/// Parse a Stream into a NCCHExtendedHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled NCCHExtendedHeader on success, null on error</returns>
|
||||
public static NCCHExtendedHeader ParseNCCHExtendedHeader(Stream data)
|
||||
{
|
||||
var obj = new NCCHExtendedHeader();
|
||||
|
||||
obj.SCI = ParseSystemControlInfo(data);
|
||||
obj.ACI = ParseAccessControlInfo(data);
|
||||
obj.AccessDescSignature = data.ReadBytes(0x100);
|
||||
obj.NCCHHDRPublicKey = data.ReadBytes(0x100);
|
||||
obj.ACIForLimitations = ParseAccessControlInfo(data);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an NCCHHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="skipSignature">Indicates if the signature should be skipped</param>
|
||||
/// <returns>Filled NCCH header on success, null on error</returns>
|
||||
/// <returns>Filled NCCHHeader on success, null on error</returns>
|
||||
public static NCCHHeader ParseNCCHHeader(Stream data, bool skipSignature = false)
|
||||
{
|
||||
var header = new NCCHHeader();
|
||||
var obj = new NCCHHeader();
|
||||
|
||||
if (!skipSignature)
|
||||
header.RSA2048Signature = data.ReadBytes(0x100);
|
||||
obj.RSA2048Signature = data.ReadBytes(0x100);
|
||||
|
||||
byte[] magicId = data.ReadBytes(4);
|
||||
header.MagicID = Encoding.ASCII.GetString(magicId).TrimEnd('\0');
|
||||
header.ContentSizeInMediaUnits = data.ReadUInt32();
|
||||
header.PartitionId = data.ReadUInt64();
|
||||
header.MakerCode = data.ReadUInt16();
|
||||
header.Version = data.ReadUInt16();
|
||||
header.VerificationHash = data.ReadUInt32();
|
||||
header.ProgramId = data.ReadBytes(8);
|
||||
header.Reserved1 = data.ReadBytes(0x10);
|
||||
header.LogoRegionHash = data.ReadBytes(0x20);
|
||||
obj.MagicID = Encoding.ASCII.GetString(magicId).TrimEnd('\0');
|
||||
obj.ContentSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.PartitionId = data.ReadUInt64LittleEndian();
|
||||
obj.MakerCode = data.ReadUInt16LittleEndian();
|
||||
obj.Version = data.ReadUInt16LittleEndian();
|
||||
obj.VerificationHash = data.ReadUInt32LittleEndian();
|
||||
obj.ProgramId = data.ReadBytes(8);
|
||||
obj.Reserved1 = data.ReadBytes(0x10);
|
||||
obj.LogoRegionHash = data.ReadBytes(0x20);
|
||||
byte[] productCode = data.ReadBytes(0x10);
|
||||
header.ProductCode = Encoding.ASCII.GetString(productCode).TrimEnd('\0');
|
||||
header.ExtendedHeaderHash = data.ReadBytes(0x20);
|
||||
header.ExtendedHeaderSizeInBytes = data.ReadUInt32();
|
||||
header.Reserved2 = data.ReadUInt32();
|
||||
header.Flags = data.ReadType<NCCHHeaderFlags>();
|
||||
header.PlainRegionOffsetInMediaUnits = data.ReadUInt32();
|
||||
header.PlainRegionSizeInMediaUnits = data.ReadUInt32();
|
||||
header.LogoRegionOffsetInMediaUnits = data.ReadUInt32();
|
||||
header.LogoRegionSizeInMediaUnits = data.ReadUInt32();
|
||||
header.ExeFSOffsetInMediaUnits = data.ReadUInt32();
|
||||
header.ExeFSSizeInMediaUnits = data.ReadUInt32();
|
||||
header.ExeFSHashRegionSizeInMediaUnits = data.ReadUInt32();
|
||||
header.Reserved3 = data.ReadUInt32();
|
||||
header.RomFSOffsetInMediaUnits = data.ReadUInt32();
|
||||
header.RomFSSizeInMediaUnits = data.ReadUInt32();
|
||||
header.RomFSHashRegionSizeInMediaUnits = data.ReadUInt32();
|
||||
header.Reserved4 = data.ReadUInt32();
|
||||
header.ExeFSSuperblockHash = data.ReadBytes(0x20);
|
||||
header.RomFSSuperblockHash = data.ReadBytes(0x20);
|
||||
obj.ProductCode = Encoding.ASCII.GetString(productCode).TrimEnd('\0');
|
||||
obj.ExtendedHeaderHash = data.ReadBytes(0x20);
|
||||
obj.ExtendedHeaderSizeInBytes = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadUInt32LittleEndian();
|
||||
obj.Flags = ParseNCCHHeaderFlags(data);
|
||||
obj.PlainRegionOffsetInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.PlainRegionSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.LogoRegionOffsetInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.LogoRegionSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.ExeFSOffsetInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.ExeFSSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.ExeFSHashRegionSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved3 = data.ReadUInt32LittleEndian();
|
||||
obj.RomFSOffsetInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.RomFSSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.RomFSHashRegionSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved4 = data.ReadUInt32LittleEndian();
|
||||
obj.ExeFSSuperblockHash = data.ReadBytes(0x20);
|
||||
obj.RomFSSuperblockHash = data.ReadBytes(0x20);
|
||||
|
||||
return header;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an ExeFS header
|
||||
/// Parse a Stream into an NCCHHeaderFlags
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExeFS header on success, null on error</returns>
|
||||
public static ExeFSHeader? ParseExeFSHeader(Stream data)
|
||||
/// <returns>Filled NCCHHeaderFlags on success, null on error</returns>
|
||||
public static NCCHHeaderFlags ParseNCCHHeaderFlags(Stream data)
|
||||
{
|
||||
var exeFSHeader = new ExeFSHeader();
|
||||
var obj = new NCCHHeaderFlags();
|
||||
|
||||
exeFSHeader.FileHeaders = new ExeFSFileHeader[10];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var exeFsFileHeader = data.ReadType<ExeFSFileHeader>();
|
||||
if (exeFsFileHeader == null)
|
||||
return null;
|
||||
obj.Reserved0 = data.ReadByteValue();
|
||||
obj.Reserved1 = data.ReadByteValue();
|
||||
obj.Reserved2 = data.ReadByteValue();
|
||||
obj.CryptoMethod = (CryptoMethod)data.ReadByteValue();
|
||||
obj.ContentPlatform = (ContentPlatform)data.ReadByteValue();
|
||||
obj.MediaPlatformIndex = (ContentType)data.ReadByteValue();
|
||||
obj.ContentUnitSize = data.ReadByteValue();
|
||||
obj.BitMasks = (BitMasks)data.ReadByteValue();
|
||||
|
||||
exeFsFileHeader.FileName = exeFsFileHeader.FileName?.TrimEnd('\0');
|
||||
exeFSHeader.FileHeaders[i] = exeFsFileHeader;
|
||||
}
|
||||
exeFSHeader.Reserved = data.ReadBytes(0x20);
|
||||
exeFSHeader.FileHashes = new byte[10][];
|
||||
for (int i = 0; i < 10; i++)
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an NCSDHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled NCSDHeader on success, null on error</returns>
|
||||
public static NCSDHeader ParseNCSDHeader(Stream data)
|
||||
{
|
||||
var obj = new NCSDHeader();
|
||||
|
||||
obj.RSA2048Signature = data.ReadBytes(0x100);
|
||||
byte[] magicNumber = data.ReadBytes(4);
|
||||
obj.MagicNumber = Encoding.ASCII.GetString(magicNumber).TrimEnd('\0');
|
||||
obj.ImageSizeInMediaUnits = data.ReadUInt32LittleEndian();
|
||||
obj.MediaId = data.ReadBytes(8);
|
||||
obj.PartitionsFSType = (FilesystemType)data.ReadUInt64LittleEndian();
|
||||
obj.PartitionsCryptType = data.ReadBytes(8);
|
||||
|
||||
obj.PartitionsTable = new PartitionTableEntry[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
exeFSHeader.FileHashes[i] = data.ReadBytes(0x20) ?? [];
|
||||
obj.PartitionsTable[i] = ParsePartitionTableEntry(data);
|
||||
}
|
||||
|
||||
return exeFSHeader;
|
||||
if (obj.PartitionsFSType == FilesystemType.Normal || obj.PartitionsFSType == FilesystemType.None)
|
||||
{
|
||||
obj.ExheaderHash = data.ReadBytes(0x20);
|
||||
obj.AdditionalHeaderSize = data.ReadUInt32LittleEndian();
|
||||
obj.SectorZeroOffset = data.ReadUInt32LittleEndian();
|
||||
obj.PartitionFlags = data.ReadBytes(8);
|
||||
|
||||
obj.PartitionIdTable = new ulong[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
obj.PartitionIdTable[i] = data.ReadUInt64LittleEndian();
|
||||
}
|
||||
|
||||
obj.Reserved1 = data.ReadBytes(0x20);
|
||||
obj.Reserved2 = data.ReadBytes(0x0E);
|
||||
obj.FirmUpdateByte1 = data.ReadByteValue();
|
||||
obj.FirmUpdateByte2 = data.ReadByteValue();
|
||||
}
|
||||
else if (obj.PartitionsFSType == FilesystemType.FIRM)
|
||||
{
|
||||
obj.Unknown = data.ReadBytes(0x5E);
|
||||
obj.EncryptedMBR = data.ReadBytes(0x42);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an PartitionTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled PartitionTableEntry on success, null on error</returns>
|
||||
public static PartitionTableEntry ParsePartitionTableEntry(Stream data)
|
||||
{
|
||||
var obj = new PartitionTableEntry();
|
||||
|
||||
obj.Offset = data.ReadUInt32LittleEndian();
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an RomFSHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled RomFSHeader on success, null on error</returns>
|
||||
public static RomFSHeader ParseRomFSHeader(Stream data)
|
||||
{
|
||||
var obj = new RomFSHeader();
|
||||
|
||||
byte[] magicString = data.ReadBytes(4);
|
||||
obj.MagicString = Encoding.ASCII.GetString(magicString);
|
||||
obj.MagicNumber = data.ReadUInt32LittleEndian();
|
||||
obj.MasterHashSize = data.ReadUInt32LittleEndian();
|
||||
obj.Level1LogicalOffset = data.ReadUInt64LittleEndian();
|
||||
obj.Level1HashdataSize = data.ReadUInt64LittleEndian();
|
||||
obj.Level1BlockSizeLog2 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved1 = data.ReadUInt32LittleEndian();
|
||||
obj.Level2LogicalOffset = data.ReadUInt64LittleEndian();
|
||||
obj.Level2HashdataSize = data.ReadUInt64LittleEndian();
|
||||
obj.Level2BlockSizeLog2 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadUInt32LittleEndian();
|
||||
obj.Level3LogicalOffset = data.ReadUInt64LittleEndian();
|
||||
obj.Level3HashdataSize = data.ReadUInt64LittleEndian();
|
||||
obj.Level3BlockSizeLog2 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved3 = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved4 = data.ReadUInt32LittleEndian();
|
||||
obj.OptionalInfoSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an StorageInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled StorageInfo on success, null on error</returns>
|
||||
public static StorageInfo ParseStorageInfo(Stream data)
|
||||
{
|
||||
var obj = new StorageInfo();
|
||||
|
||||
obj.ExtdataID = data.ReadUInt64LittleEndian();
|
||||
obj.SystemSavedataIDs = data.ReadBytes(8);
|
||||
obj.StorageAccessibleUniqueIDs = data.ReadBytes(8);
|
||||
obj.FileSystemAccessInfo = data.ReadBytes(7);
|
||||
obj.OtherAttributes = (StorageInfoOtherAttributes)data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an SystemControlInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SystemControlInfo on success, null on error</returns>
|
||||
public static SystemControlInfo ParseSystemControlInfo(Stream data)
|
||||
{
|
||||
var obj = new SystemControlInfo();
|
||||
|
||||
byte[] applicationTitle = data.ReadBytes(8);
|
||||
obj.ApplicationTitle = Encoding.ASCII.GetString(applicationTitle).TrimEnd('\0');
|
||||
obj.Reserved1 = data.ReadBytes(5);
|
||||
obj.Flag = data.ReadByteValue();
|
||||
obj.RemasterVersion = data.ReadUInt16LittleEndian();
|
||||
obj.TextCodeSetInfo = ParseCodeSetInfo(data);
|
||||
obj.StackSize = data.ReadUInt32LittleEndian();
|
||||
obj.ReadOnlyCodeSetInfo = ParseCodeSetInfo(data);
|
||||
obj.Reserved2 = data.ReadUInt32LittleEndian();
|
||||
obj.DataCodeSetInfo = ParseCodeSetInfo(data);
|
||||
obj.BSSSize = data.ReadUInt32LittleEndian();
|
||||
obj.DependencyModuleList = new ulong[48];
|
||||
for (int i = 0; i < 48; i++)
|
||||
{
|
||||
obj.DependencyModuleList[i] = data.ReadUInt64LittleEndian();
|
||||
}
|
||||
obj.SystemInfo = ParseSystemInfo(data);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an SystemInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SystemInfo on success, null on error</returns>
|
||||
public static SystemInfo ParseSystemInfo(Stream data)
|
||||
{
|
||||
var obj = new SystemInfo();
|
||||
|
||||
obj.SaveDataSize = data.ReadUInt64LittleEndian();
|
||||
obj.JumpID = data.ReadUInt64LittleEndian();
|
||||
obj.Reserved = data.ReadBytes(0x30);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an TestData
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled TestData on success, null on error</returns>
|
||||
public static TestData ParseTestData(Stream data)
|
||||
{
|
||||
var obj = new TestData();
|
||||
|
||||
obj.Signature = data.ReadBytes(8);
|
||||
obj.AscendingByteSequence = data.ReadBytes(0x1F8);
|
||||
obj.DescendingByteSequence = data.ReadBytes(0x200);
|
||||
obj.Filled00 = data.ReadBytes(0x200);
|
||||
obj.FilledFF = data.ReadBytes(0x200);
|
||||
obj.Filled0F = data.ReadBytes(0x200);
|
||||
obj.FilledF0 = data.ReadBytes(0x200);
|
||||
obj.Filled55 = data.ReadBytes(0x200);
|
||||
obj.FilledAA = data.ReadBytes(0x1FF);
|
||||
obj.FinalByte = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Dummy0 != 0x00000001)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
if (header?.MajorVersion != 0x00000002)
|
||||
if (header.MajorVersion != 0x00000002)
|
||||
return null;
|
||||
if (header?.MinorVersion != 1)
|
||||
if (header.MinorVersion != 1)
|
||||
return null;
|
||||
|
||||
// Set the no cache header
|
||||
@@ -42,8 +42,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Directory Header
|
||||
|
||||
// Try to parse the directory header
|
||||
var directoryHeader = data.ReadType<DirectoryHeader>();
|
||||
if (directoryHeader?.Dummy0 != 0x00000004)
|
||||
var directoryHeader = ParseDirectoryHeader(data);
|
||||
if (directoryHeader.Dummy0 != 0x00000004)
|
||||
return null;
|
||||
|
||||
// Set the game cache directory header
|
||||
@@ -59,11 +59,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
{
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryEntries[i] = directoryEntry;
|
||||
file.DirectoryEntries[i] = ParseDirectoryEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -107,11 +103,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory info 1 entries
|
||||
for (int i = 0; i < directoryHeader.Info1Count; i++)
|
||||
{
|
||||
var directoryInfo1Entry = data.ReadType<DirectoryInfo1Entry>();
|
||||
if (directoryInfo1Entry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryInfo1Entries[i] = directoryInfo1Entry;
|
||||
file.DirectoryInfo1Entries[i] = ParseDirectoryInfo1Entry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -124,11 +116,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory info 2 entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
{
|
||||
var directoryInfo2Entry = data.ReadType<DirectoryInfo2Entry>();
|
||||
if (directoryInfo2Entry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryInfo2Entries[i] = directoryInfo2Entry;
|
||||
file.DirectoryInfo2Entries[i] = ParseDirectoryInfo2Entry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -141,11 +129,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory copy entries
|
||||
for (int i = 0; i < directoryHeader.CopyCount; i++)
|
||||
{
|
||||
var directoryCopyEntry = data.ReadType<DirectoryCopyEntry>();
|
||||
if (directoryCopyEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryCopyEntries[i] = directoryCopyEntry;
|
||||
file.DirectoryCopyEntries[i] = ParseDirectoryCopyEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -158,11 +142,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory local entries
|
||||
for (int i = 0; i < directoryHeader.LocalCount; i++)
|
||||
{
|
||||
var directoryLocalEntry = data.ReadType<DirectoryLocalEntry>();
|
||||
if (directoryLocalEntry == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryLocalEntries[i] = directoryLocalEntry;
|
||||
file.DirectoryLocalEntries[i] = ParseDirectoryLocalEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -173,10 +153,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Unknown Header
|
||||
|
||||
// Try to parse the unknown header
|
||||
var unknownHeader = data.ReadType<UnknownHeader>();
|
||||
if (unknownHeader?.Dummy0 != 0x00000001)
|
||||
var unknownHeader = ParseUnknownHeader(data);
|
||||
if (unknownHeader.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
if (unknownHeader?.Dummy1 != 0x00000000)
|
||||
if (unknownHeader.Dummy1 != 0x00000000)
|
||||
return null;
|
||||
|
||||
// Set the game cache unknown header
|
||||
@@ -192,11 +172,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the unknown entries
|
||||
for (int i = 0; i < directoryHeader.ItemCount; i++)
|
||||
{
|
||||
var unknownEntry = data.ReadType<UnknownEntry>();
|
||||
if (unknownEntry == null)
|
||||
return null;
|
||||
|
||||
file.UnknownEntries[i] = unknownEntry;
|
||||
file.UnknownEntries[i] = ParseUnknownEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -204,8 +180,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Checksum Header
|
||||
|
||||
// Try to parse the checksum header
|
||||
var checksumHeader = data.ReadType<ChecksumHeader>();
|
||||
if (checksumHeader?.Dummy0 != 0x00000001)
|
||||
var checksumHeader = ParseChecksumHeader(data);
|
||||
if (checksumHeader.Dummy0 != 0x00000001)
|
||||
return null;
|
||||
|
||||
// Set the game cache checksum header
|
||||
@@ -219,10 +195,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Checksum Map Header
|
||||
|
||||
// Try to parse the checksum map header
|
||||
var checksumMapHeader = data.ReadType<ChecksumMapHeader>();
|
||||
if (checksumMapHeader?.Dummy0 != 0x14893721)
|
||||
var checksumMapHeader = ParseChecksumMapHeader(data);
|
||||
if (checksumMapHeader.Dummy0 != 0x14893721)
|
||||
return null;
|
||||
if (checksumMapHeader?.Dummy1 != 0x00000001)
|
||||
if (checksumMapHeader.Dummy1 != 0x00000001)
|
||||
return null;
|
||||
|
||||
// Set the game cache checksum map header
|
||||
@@ -238,11 +214,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the checksum map entries
|
||||
for (int i = 0; i < checksumMapHeader.ItemCount; i++)
|
||||
{
|
||||
var checksumMapEntry = data.ReadType<ChecksumMapEntry>();
|
||||
if (checksumMapEntry == null)
|
||||
return null;
|
||||
|
||||
file.ChecksumMapEntries[i] = checksumMapEntry;
|
||||
file.ChecksumMapEntries[i] = ParseChecksumMapEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -255,11 +227,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the checksum entries
|
||||
for (int i = 0; i < checksumMapHeader.ChecksumCount; i++)
|
||||
{
|
||||
var checksumEntry = data.ReadType<ChecksumEntry>();
|
||||
if (checksumEntry == null)
|
||||
return null;
|
||||
|
||||
file.ChecksumEntries[i] = checksumEntry;
|
||||
file.ChecksumEntries[i] = ParseChecksumEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -275,5 +243,222 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumEntry on success, null on error</returns>
|
||||
public static ChecksumEntry ParseChecksumEntry(Stream data)
|
||||
{
|
||||
var obj = new ChecksumEntry();
|
||||
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumHeader on success, null on error</returns>
|
||||
public static ChecksumHeader ParseChecksumHeader(Stream data)
|
||||
{
|
||||
var obj = new ChecksumHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumMapEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumMapEntry on success, null on error</returns>
|
||||
public static ChecksumMapEntry ParseChecksumMapEntry(Stream data)
|
||||
{
|
||||
var obj = new ChecksumMapEntry();
|
||||
|
||||
obj.ChecksumCount = data.ReadUInt32LittleEndian();
|
||||
obj.FirstChecksumIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ChecksumMapHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ChecksumMapHeader on success, null on error</returns>
|
||||
public static ChecksumMapHeader ParseChecksumMapHeader(Stream data)
|
||||
{
|
||||
var obj = new ChecksumMapHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.ItemCount = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryCopyEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryCopyEntry on success, null on error</returns>
|
||||
public static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryCopyEntry();
|
||||
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryEntry on success, null on error</returns>
|
||||
public static DirectoryEntry ParseDirectoryEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryEntry();
|
||||
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ItemSize = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumIndex = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryFlags = (HL_NCF_FLAG)data.ReadUInt32LittleEndian();
|
||||
obj.ParentIndex = data.ReadUInt32LittleEndian();
|
||||
obj.NextIndex = data.ReadUInt32LittleEndian();
|
||||
obj.FirstIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryHeader on success, null on error</returns>
|
||||
public static DirectoryHeader ParseDirectoryHeader(Stream data)
|
||||
{
|
||||
var obj = new DirectoryHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.CacheID = data.ReadUInt32LittleEndian();
|
||||
obj.LastVersionPlayed = data.ReadUInt32LittleEndian();
|
||||
obj.ItemCount = data.ReadUInt32LittleEndian();
|
||||
obj.FileCount = data.ReadUInt32LittleEndian();
|
||||
obj.ChecksumDataLength = data.ReadUInt32LittleEndian();
|
||||
obj.DirectorySize = data.ReadUInt32LittleEndian();
|
||||
obj.NameSize = data.ReadUInt32LittleEndian();
|
||||
obj.Info1Count = data.ReadUInt32LittleEndian();
|
||||
obj.CopyCount = data.ReadUInt32LittleEndian();
|
||||
obj.LocalCount = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy2 = data.ReadUInt32LittleEndian();
|
||||
obj.Checksum = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryInfo1Entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryInfo1Entry on success, null on error</returns>
|
||||
public static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryInfo1Entry();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryInfo2Entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryInfo2Entry on success, null on error</returns>
|
||||
public static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryInfo2Entry();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryLocalEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryLocalEntry on success, null on error</returns>
|
||||
public static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryLocalEntry();
|
||||
|
||||
obj.DirectoryIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.MajorVersion = data.ReadUInt32LittleEndian();
|
||||
obj.MinorVersion = data.ReadUInt32LittleEndian();
|
||||
obj.CacheID = data.ReadUInt32LittleEndian();
|
||||
obj.LastVersionPlayed = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy2 = data.ReadUInt32LittleEndian();
|
||||
obj.FileSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockSize = data.ReadUInt32LittleEndian();
|
||||
obj.BlockCount = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy3 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a UnknownEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled UnknownEntry on success, null on error</returns>
|
||||
public static UnknownEntry ParseUnknownEntry(Stream data)
|
||||
{
|
||||
var obj = new UnknownEntry();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a UnknownHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled UnknownHeader on success, null on error</returns>
|
||||
public static UnknownHeader ParseUnknownHeader(Stream data)
|
||||
{
|
||||
var obj = new UnknownHeader();
|
||||
|
||||
obj.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy1 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.NewExecutable;
|
||||
using static SabreTools.Models.NewExecutable.Constants;
|
||||
@@ -39,8 +40,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the executable header
|
||||
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
|
||||
var header = data.ReadType<ExecutableHeader>();
|
||||
if (header?.Magic != SignatureString)
|
||||
var header = ParseExecutableHeader(data);
|
||||
if (header.Magic != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the executable header
|
||||
@@ -57,14 +58,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the segment table
|
||||
// Seek to the segment table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var segmentTable = ParseSegmentTable(data, header.FileSegmentCount);
|
||||
if (segmentTable == null)
|
||||
return null;
|
||||
|
||||
// Set the segment table
|
||||
executable.SegmentTable = segmentTable;
|
||||
executable.SegmentTable = new SegmentTableEntry[header.FileSegmentCount];
|
||||
for (int i = 0; i < header.FileSegmentCount; i++)
|
||||
{
|
||||
executable.SegmentTable[i] = ParseSegmentTableEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -77,14 +79,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the resource table
|
||||
// Seek to the resource table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var resourceTable = ParseResourceTable(data, header.ResourceEntriesCount);
|
||||
if (resourceTable == null)
|
||||
return null;
|
||||
|
||||
// Set the resource table
|
||||
executable.ResourceTable = resourceTable;
|
||||
executable.ResourceTable = ParseResourceTable(data, header.ResourceEntriesCount);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -100,14 +99,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the resident-name table
|
||||
// Seek to the resident-name table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var residentNameTable = ParseResidentNameTable(data, endOffset);
|
||||
if (residentNameTable == null)
|
||||
return null;
|
||||
|
||||
// Set the resident-name table
|
||||
executable.ResidentNameTable = residentNameTable;
|
||||
executable.ResidentNameTable = ParseResidentNameTable(data, endOffset);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -120,14 +116,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the module-reference table
|
||||
// Seek to the module-reference table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var moduleReferenceTable = ParseModuleReferenceTable(data, header.ModuleReferenceTableSize);
|
||||
if (moduleReferenceTable == null)
|
||||
return null;
|
||||
|
||||
// Set the module-reference table
|
||||
executable.ModuleReferenceTable = moduleReferenceTable;
|
||||
executable.ModuleReferenceTable = new ModuleReferenceTableEntry[header.ModuleReferenceTableSize];
|
||||
for (int i = 0; i < header.ModuleReferenceTableSize; i++)
|
||||
{
|
||||
executable.ModuleReferenceTable[i] = ParseModuleReferenceTableEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -143,14 +140,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the imported-name table
|
||||
// Seek to the imported-name table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var importedNameTable = ParseImportedNameTable(data, endOffset);
|
||||
if (importedNameTable == null)
|
||||
return null;
|
||||
|
||||
// Set the imported-name table
|
||||
executable.ImportedNameTable = importedNameTable;
|
||||
executable.ImportedNameTable = ParseImportedNameTable(data, endOffset);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -167,14 +161,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the imported-name table
|
||||
// Seek to the imported-name table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var entryTable = ParseEntryTable(data, endOffset);
|
||||
if (entryTable == null)
|
||||
return null;
|
||||
|
||||
// Set the entry table
|
||||
executable.EntryTable = entryTable;
|
||||
executable.EntryTable = ParseEntryTable(data, endOffset);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -189,14 +180,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
// Try to parse the nonresident-name table
|
||||
// Seek to the nonresident-name table
|
||||
data.Seek(tableAddress, SeekOrigin.Begin);
|
||||
var nonResidentNameTable = ParseNonResidentNameTable(data, endOffset);
|
||||
if (nonResidentNameTable == null)
|
||||
return null;
|
||||
|
||||
// Set the nonresident-name table
|
||||
executable.NonResidentNameTable = nonResidentNameTable;
|
||||
executable.NonResidentNameTable = ParseNonResidentNameTable(data, endOffset);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -210,68 +198,229 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a segment table
|
||||
/// Parse a Stream into an entry table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of segment table entries to read</param>
|
||||
/// <returns>Filled segment table on success, null on error</returns>
|
||||
public static SegmentTableEntry[]? ParseSegmentTable(Stream data, int count)
|
||||
/// <param name="endOffset">First address not part of the entry table</param>
|
||||
/// <returns>Filled entry table on success, null on error</returns>
|
||||
public static EntryTableBundle[] ParseEntryTable(Stream data, int endOffset)
|
||||
{
|
||||
var segmentTable = new SegmentTableEntry[count];
|
||||
var entryTable = new List<EntryTableBundle>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = data.ReadType<SegmentTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
var entry = new EntryTableBundle();
|
||||
entry.EntryCount = data.ReadByteValue();
|
||||
entry.SegmentIndicator = data.ReadByteValue();
|
||||
switch (entry.GetEntryType())
|
||||
{
|
||||
case SegmentEntryType.Unused:
|
||||
break;
|
||||
|
||||
segmentTable[i] = entry;
|
||||
case SegmentEntryType.FixedSegment:
|
||||
entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByteValue();
|
||||
entry.FixedOffset = data.ReadUInt16LittleEndian();
|
||||
break;
|
||||
|
||||
case SegmentEntryType.MoveableSegment:
|
||||
entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByteValue();
|
||||
entry.MoveableReserved = data.ReadUInt16LittleEndian();
|
||||
entry.MoveableSegmentNumber = data.ReadByteValue();
|
||||
entry.MoveableOffset = data.ReadUInt16LittleEndian();
|
||||
break;
|
||||
}
|
||||
entryTable.Add(entry);
|
||||
}
|
||||
|
||||
return segmentTable;
|
||||
return [.. entryTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource table
|
||||
/// Parse a Stream into an ExecutableHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExecutableHeader on success, null on error</returns>
|
||||
public static ExecutableHeader ParseExecutableHeader(Stream data)
|
||||
{
|
||||
var obj = new ExecutableHeader();
|
||||
|
||||
byte[] magic = data.ReadBytes(2);
|
||||
obj.Magic = Encoding.ASCII.GetString(magic);
|
||||
obj.LinkerVersion = data.ReadByteValue();
|
||||
obj.LinkerRevision = data.ReadByteValue();
|
||||
obj.EntryTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.EntryTableSize = data.ReadUInt16LittleEndian();
|
||||
obj.CrcChecksum = data.ReadUInt32LittleEndian();
|
||||
obj.FlagWord = (HeaderFlag)data.ReadUInt16LittleEndian();
|
||||
obj.AutomaticDataSegmentNumber = data.ReadUInt16LittleEndian();
|
||||
obj.InitialHeapAlloc = data.ReadUInt16LittleEndian();
|
||||
obj.InitialStackAlloc = data.ReadUInt16LittleEndian();
|
||||
obj.InitialCSIPSetting = data.ReadUInt32LittleEndian();
|
||||
obj.InitialSSSPSetting = data.ReadUInt32LittleEndian();
|
||||
obj.FileSegmentCount = data.ReadUInt16LittleEndian();
|
||||
obj.ModuleReferenceTableSize = data.ReadUInt16LittleEndian();
|
||||
obj.NonResidentNameTableSize = data.ReadUInt16LittleEndian();
|
||||
obj.SegmentTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ResourceTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ResidentNameTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ModuleReferenceTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ImportedNamesTableOffset = data.ReadUInt16LittleEndian();
|
||||
obj.NonResidentNamesTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.MovableEntriesCount = data.ReadUInt16LittleEndian();
|
||||
obj.SegmentAlignmentShiftCount = data.ReadUInt16LittleEndian();
|
||||
obj.ResourceEntriesCount = data.ReadUInt16LittleEndian();
|
||||
obj.TargetOperatingSystem = (Models.NewExecutable.OperatingSystem)data.ReadByteValue();
|
||||
obj.AdditionalFlags = (OS2Flag)data.ReadByteValue();
|
||||
obj.ReturnThunkOffset = data.ReadUInt16LittleEndian();
|
||||
obj.SegmentReferenceThunkOffset = data.ReadUInt16LittleEndian();
|
||||
obj.MinCodeSwapAreaSize = data.ReadUInt16LittleEndian();
|
||||
obj.WindowsSDKRevision = data.ReadByteValue();
|
||||
obj.WindowsSDKVersion = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an imported-name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the imported-name table</param>
|
||||
/// <returns>Filled imported-name table on success, null on error</returns>
|
||||
public static Dictionary<ushort, ImportedNameTableEntry> ParseImportedNameTable(Stream data, int endOffset)
|
||||
{
|
||||
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
ushort currentOffset = (ushort)data.Position;
|
||||
importedNameTable[currentOffset] = ParseImportedNameTableEntry(data);
|
||||
}
|
||||
|
||||
return importedNameTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an ImportedNameTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ImportedNameTableEntry on success, null on error</returns>
|
||||
public static ImportedNameTableEntry ParseImportedNameTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ImportedNameTableEntry();
|
||||
|
||||
obj.Length = data.ReadByteValue();
|
||||
obj.NameString = data.ReadBytes(obj.Length);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an ModuleReferenceTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ModuleReferenceTableEntry on success, null on error</returns>
|
||||
public static ModuleReferenceTableEntry ParseModuleReferenceTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ModuleReferenceTableEntry();
|
||||
|
||||
obj.Offset = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a nonresident-name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the nonresident-name table</param>
|
||||
/// <returns>Filled nonresident-name table on success, null on error</returns>
|
||||
public static NonResidentNameTableEntry[] ParseNonResidentNameTable(Stream data, int endOffset)
|
||||
{
|
||||
var residentNameTable = new List<NonResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = ParseNonResidentNameTableEntry(data);
|
||||
residentNameTable.Add(entry);
|
||||
}
|
||||
|
||||
return [.. residentNameTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a NonResidentNameTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled NonResidentNameTableEntry on success, null on error</returns>
|
||||
public static NonResidentNameTableEntry ParseNonResidentNameTableEntry(Stream data)
|
||||
{
|
||||
var obj = new NonResidentNameTableEntry();
|
||||
|
||||
obj.Length = data.ReadByteValue();
|
||||
obj.NameString = data.ReadBytes(obj.Length);
|
||||
obj.OrdinalNumber = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resident-name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the resident-name table</param>
|
||||
/// <returns>Filled resident-name table on success, null on error</returns>
|
||||
public static ResidentNameTableEntry[] ParseResidentNameTable(Stream data, int endOffset)
|
||||
{
|
||||
var residentNameTable = new List<ResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = ParseResidentNameTableEntry(data);
|
||||
residentNameTable.Add(entry);
|
||||
}
|
||||
|
||||
return [.. residentNameTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ResidentNameTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ResidentNameTableEntry on success, null on error</returns>
|
||||
public static ResidentNameTableEntry ParseResidentNameTableEntry(Stream data)
|
||||
{
|
||||
var obj = new ResidentNameTableEntry();
|
||||
|
||||
obj.Length = data.ReadByteValue();
|
||||
obj.NameString = data.ReadBytes(obj.Length);
|
||||
obj.OrdinalNumber = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ResourceTable
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of resource table entries to read</param>
|
||||
/// <returns>Filled resource table on success, null on error</returns>
|
||||
public static ResourceTable? ParseResourceTable(Stream data, ushort count)
|
||||
/// <returns>Filled ResourceTable on success, null on error</returns>
|
||||
public static ResourceTable ParseResourceTable(Stream data, ushort count)
|
||||
{
|
||||
long initialOffset = data.Position;
|
||||
|
||||
var resourceTable = new ResourceTable();
|
||||
|
||||
resourceTable.AlignmentShiftCount = data.ReadUInt16();
|
||||
resourceTable.AlignmentShiftCount = data.ReadUInt16LittleEndian();
|
||||
var resourceTypes = new List<ResourceTypeInformationEntry>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry();
|
||||
|
||||
entry.TypeID = data.ReadUInt16();
|
||||
entry.ResourceCount = data.ReadUInt16();
|
||||
entry.Reserved = data.ReadUInt32();
|
||||
var entry = ParseResourceTypeInformationEntry(data);
|
||||
resourceTypes.Add(entry);
|
||||
|
||||
// A zero type ID marks the end of the resource type information blocks.
|
||||
if (entry.TypeID == 0)
|
||||
{
|
||||
resourceTypes.Add(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
|
||||
for (int j = 0; j < entry.ResourceCount; j++)
|
||||
{
|
||||
// TODO: Should we read and store the resource data?
|
||||
var resource = data.ReadType<ResourceTypeResourceEntry>();
|
||||
if (resource == null)
|
||||
return null;
|
||||
|
||||
entry.Resources[j] = resource;
|
||||
}
|
||||
resourceTypes.Add(entry);
|
||||
}
|
||||
|
||||
resourceTable.ResourceTypes = [.. resourceTypes];
|
||||
@@ -314,9 +463,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(stringOffset, SeekOrigin.Begin);
|
||||
|
||||
var str = ParseResourceTypeAndNameString(data);
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
resourceTable.TypeAndNameStrings[stringOffsets[i]] = str;
|
||||
}
|
||||
|
||||
@@ -324,192 +470,80 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource type and name string
|
||||
/// Parse a Stream into an ResourceTypeInformationEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled resource type and name string on success, null on error</returns>
|
||||
public static ResourceTypeAndNameString? ParseResourceTypeAndNameString(Stream data)
|
||||
/// <returns>Filled ResourceTypeInformationEntry on success, null on error</returns>
|
||||
public static ResourceTypeInformationEntry ParseResourceTypeInformationEntry(Stream data)
|
||||
{
|
||||
var str = new ResourceTypeAndNameString();
|
||||
var obj = new ResourceTypeInformationEntry();
|
||||
|
||||
str.Length = data.ReadByteValue();
|
||||
str.Text = data.ReadBytes(str.Length);
|
||||
obj.TypeID = data.ReadUInt16LittleEndian();
|
||||
obj.ResourceCount = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved = data.ReadUInt32LittleEndian();
|
||||
|
||||
return str;
|
||||
}
|
||||
// A zero type ID marks the end of the resource type information blocks.
|
||||
if (obj.TypeID == 0)
|
||||
return obj;
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resident-name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the resident-name table</param>
|
||||
/// <returns>Filled resident-name table on success, null on error</returns>
|
||||
public static ResidentNameTableEntry[]? ParseResidentNameTable(Stream data, int endOffset)
|
||||
{
|
||||
var residentNameTable = new List<ResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
obj.Resources = new ResourceTypeResourceEntry[obj.ResourceCount];
|
||||
for (int i = 0; i < obj.ResourceCount; i++)
|
||||
{
|
||||
var entry = ParseResidentNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
residentNameTable.Add(entry);
|
||||
// TODO: Should we read and store the resource data?
|
||||
obj.Resources[i] = ParseResourceTypeResourceEntry(data);
|
||||
}
|
||||
|
||||
return [.. residentNameTable];
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resident-name table entry
|
||||
/// Parse a Stream into a ResourceTypeAndNameString
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled resident-name table entry on success, null on error</returns>
|
||||
public static ResidentNameTableEntry? ParseResidentNameTableEntry(Stream data)
|
||||
/// <returns>Filled rResourceTypeAndNameString on success, null on error</returns>
|
||||
public static ResourceTypeAndNameString ParseResourceTypeAndNameString(Stream data)
|
||||
{
|
||||
var entry = new ResidentNameTableEntry();
|
||||
var obj = new ResourceTypeAndNameString();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
entry.NameString = data.ReadBytes(entry.Length);
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
obj.Length = data.ReadByteValue();
|
||||
obj.Text = data.ReadBytes(obj.Length);
|
||||
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a module-reference table
|
||||
/// Parse a Stream into an ResourceTypeResourceEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of module-reference table entries to read</param>
|
||||
/// <returns>Filled module-reference table on success, null on error</returns>
|
||||
public static ModuleReferenceTableEntry[]? ParseModuleReferenceTable(Stream data, int count)
|
||||
/// <returns>Filled ResourceTypeResourceEntry on success, null on error</returns>
|
||||
public static ResourceTypeResourceEntry ParseResourceTypeResourceEntry(Stream data)
|
||||
{
|
||||
var moduleReferenceTable = new ModuleReferenceTableEntry[count];
|
||||
var obj = new ResourceTypeResourceEntry();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entry = data.ReadType<ModuleReferenceTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
obj.Offset = data.ReadUInt16LittleEndian();
|
||||
obj.Length = data.ReadUInt16LittleEndian();
|
||||
obj.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16LittleEndian();
|
||||
obj.ResourceID = data.ReadUInt16LittleEndian();
|
||||
obj.Reserved = data.ReadUInt32LittleEndian();
|
||||
|
||||
moduleReferenceTable[i] = entry;
|
||||
}
|
||||
|
||||
return moduleReferenceTable;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an imported-name table
|
||||
/// Parse a Stream into an SegmentTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the imported-name table</param>
|
||||
/// <returns>Filled imported-name table on success, null on error</returns>
|
||||
public static Dictionary<ushort, ImportedNameTableEntry>? ParseImportedNameTable(Stream data, int endOffset)
|
||||
/// <returns>Filled SegmentTableEntry on success, null on error</returns>
|
||||
public static SegmentTableEntry ParseSegmentTableEntry(Stream data)
|
||||
{
|
||||
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
|
||||
var obj = new SegmentTableEntry();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
ushort currentOffset = (ushort)data.Position;
|
||||
var entry = ParseImportedNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
obj.Offset = data.ReadUInt16LittleEndian();
|
||||
obj.Length = data.ReadUInt16LittleEndian();
|
||||
obj.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16LittleEndian();
|
||||
obj.MinimumAllocationSize = data.ReadUInt16LittleEndian();
|
||||
|
||||
importedNameTable[currentOffset] = entry;
|
||||
}
|
||||
|
||||
return importedNameTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an imported-name table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled imported-name table entry on success, null on error</returns>
|
||||
public static ImportedNameTableEntry? ParseImportedNameTableEntry(Stream data)
|
||||
{
|
||||
var entry = new ImportedNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
entry.NameString = data.ReadBytes(entry.Length);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an entry table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the entry table</param>
|
||||
/// <returns>Filled entry table on success, null on error</returns>
|
||||
public static EntryTableBundle[] ParseEntryTable(Stream data, int endOffset)
|
||||
{
|
||||
var entryTable = new List<EntryTableBundle>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = new EntryTableBundle();
|
||||
entry.EntryCount = data.ReadByteValue();
|
||||
entry.SegmentIndicator = data.ReadByteValue();
|
||||
switch (entry.GetEntryType())
|
||||
{
|
||||
case SegmentEntryType.Unused:
|
||||
break;
|
||||
|
||||
case SegmentEntryType.FixedSegment:
|
||||
entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByteValue();
|
||||
entry.FixedOffset = data.ReadUInt16();
|
||||
break;
|
||||
|
||||
case SegmentEntryType.MoveableSegment:
|
||||
entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByteValue();
|
||||
entry.MoveableReserved = data.ReadUInt16();
|
||||
entry.MoveableSegmentNumber = data.ReadByteValue();
|
||||
entry.MoveableOffset = data.ReadUInt16();
|
||||
break;
|
||||
}
|
||||
entryTable.Add(entry);
|
||||
}
|
||||
|
||||
return [.. entryTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a nonresident-name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="endOffset">First address not part of the nonresident-name table</param>
|
||||
/// <returns>Filled nonresident-name table on success, null on error</returns>
|
||||
public static NonResidentNameTableEntry[]? ParseNonResidentNameTable(Stream data, int endOffset)
|
||||
{
|
||||
var residentNameTable = new List<NonResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = ParseNonResidentNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
residentNameTable.Add(entry);
|
||||
}
|
||||
|
||||
return [.. residentNameTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a nonresident-name table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled nonresident-name table entry on success, null on error</returns>
|
||||
public static NonResidentNameTableEntry? ParseNonResidentNameTableEntry(Stream data)
|
||||
{
|
||||
var entry = new NonResidentNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
entry.NameString = data.ReadBytes(entry.Length);
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,27 +22,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<CommonHeader>();
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the cart image header
|
||||
cart.CommonHeader = header;
|
||||
cart.CommonHeader = ParseCommonHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extended DSi Header
|
||||
|
||||
// If we have a DSi-compatible cartridge
|
||||
if (header.UnitCode == Unitcode.NDSPlusDSi || header.UnitCode == Unitcode.DSi)
|
||||
{
|
||||
var extendedDSiHeader = data.ReadType<ExtendedDSiHeader>();
|
||||
if (extendedDSiHeader == null)
|
||||
return null;
|
||||
|
||||
cart.ExtendedDSiHeader = extendedDSiHeader;
|
||||
}
|
||||
if (cart.CommonHeader.UnitCode == Unitcode.NDSPlusDSi || cart.CommonHeader.UnitCode == Unitcode.DSi)
|
||||
cart.ExtendedDSiHeader = ParseExtendedDSiHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -64,27 +53,22 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Name Table
|
||||
|
||||
// Try to get the name table offset
|
||||
long nameTableOffset = header.FileNameTableOffset;
|
||||
long nameTableOffset = cart.CommonHeader.FileNameTableOffset;
|
||||
if (nameTableOffset < 0 || nameTableOffset > data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the name table
|
||||
data.Seek(nameTableOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the name table
|
||||
var nameTable = ParseNameTable(data);
|
||||
if (nameTable == null)
|
||||
return null;
|
||||
|
||||
// Set the name table
|
||||
cart.NameTable = nameTable;
|
||||
cart.NameTable = ParseNameTable(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Allocation Table
|
||||
|
||||
// Try to get the file allocation table offset
|
||||
long fileAllocationTableOffset = header.FileAllocationTableOffset;
|
||||
long fileAllocationTableOffset = cart.CommonHeader.FileAllocationTableOffset;
|
||||
if (fileAllocationTableOffset < 0 || fileAllocationTableOffset > data.Length)
|
||||
return null;
|
||||
|
||||
@@ -95,12 +79,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var fileAllocationTable = new List<FileAllocationTableEntry>();
|
||||
|
||||
// Try to parse the file allocation table
|
||||
while (data.Position - fileAllocationTableOffset < header.FileAllocationTableLength)
|
||||
while (data.Position - fileAllocationTableOffset < cart.CommonHeader.FileAllocationTableLength)
|
||||
{
|
||||
var entry = data.ReadType<FileAllocationTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
var entry = ParseFileAllocationTableEntry(data);
|
||||
fileAllocationTable.Add(entry);
|
||||
}
|
||||
|
||||
@@ -121,12 +102,203 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CommonHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CommonHeader on success, null on error</returns>
|
||||
public static CommonHeader ParseCommonHeader(Stream data)
|
||||
{
|
||||
var obj = new CommonHeader();
|
||||
|
||||
byte[] gameTitle = data.ReadBytes(12);
|
||||
obj.GameTitle = Encoding.ASCII.GetString(gameTitle).TrimEnd('\0');
|
||||
obj.GameCode = data.ReadUInt32LittleEndian();
|
||||
byte[] makerCode = data.ReadBytes(2);
|
||||
obj.MakerCode = Encoding.ASCII.GetString(makerCode);
|
||||
obj.UnitCode = (Unitcode)data.ReadByteValue();
|
||||
obj.EncryptionSeedSelect = data.ReadByteValue();
|
||||
obj.DeviceCapacity = data.ReadByteValue();
|
||||
obj.Reserved1 = data.ReadBytes(7);
|
||||
obj.GameRevision = data.ReadUInt16LittleEndian();
|
||||
obj.RomVersion = data.ReadByteValue();
|
||||
obj.InternalFlags = data.ReadByteValue();
|
||||
obj.ARM9RomOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9EntryAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9LoadAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9Size = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7RomOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7EntryAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7LoadAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7Size = data.ReadUInt32LittleEndian();
|
||||
obj.FileNameTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileNameTableLength = data.ReadUInt32LittleEndian();
|
||||
obj.FileAllocationTableOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FileAllocationTableLength = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9OverlayOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9OverlayLength = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7OverlayOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7OverlayLength = data.ReadUInt32LittleEndian();
|
||||
obj.NormalCardControlRegisterSettings = data.ReadUInt32LittleEndian();
|
||||
obj.SecureCardControlRegisterSettings = data.ReadUInt32LittleEndian();
|
||||
obj.IconBannerOffset = data.ReadUInt32LittleEndian();
|
||||
obj.SecureAreaCRC = data.ReadUInt16LittleEndian();
|
||||
obj.SecureTransferTimeout = data.ReadUInt16LittleEndian();
|
||||
obj.ARM9Autoload = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7Autoload = data.ReadUInt32LittleEndian();
|
||||
obj.SecureDisable = data.ReadBytes(8);
|
||||
obj.NTRRegionRomSize = data.ReadUInt32LittleEndian();
|
||||
obj.HeaderSize = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved2 = data.ReadBytes(56);
|
||||
obj.NintendoLogo = data.ReadBytes(156);
|
||||
obj.NintendoLogoCRC = data.ReadUInt16LittleEndian();
|
||||
obj.HeaderCRC = data.ReadUInt16LittleEndian();
|
||||
obj.DebuggerReserved = data.ReadBytes(0x20);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ExtendedDSiHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ExtendedDSiHeader on success, null on error</returns>
|
||||
public static ExtendedDSiHeader ParseExtendedDSiHeader(Stream data)
|
||||
{
|
||||
var obj = new ExtendedDSiHeader();
|
||||
|
||||
obj.GlobalMBK15Settings = new uint[5];
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
obj.GlobalMBK15Settings[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.LocalMBK68SettingsARM9 = new uint[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.LocalMBK68SettingsARM9[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.LocalMBK68SettingsARM7 = new uint[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
obj.LocalMBK68SettingsARM7[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
obj.GlobalMBK9Setting = data.ReadUInt32LittleEndian();
|
||||
obj.RegionFlags = data.ReadUInt32LittleEndian();
|
||||
obj.AccessControl = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7SCFGEXTMask = data.ReadUInt32LittleEndian();
|
||||
obj.ReservedFlags = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9iRomOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved3 = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9iLoadAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM9iSize = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7iRomOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved4 = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7iLoadAddress = data.ReadUInt32LittleEndian();
|
||||
obj.ARM7iSize = data.ReadUInt32LittleEndian();
|
||||
obj.DigestNTRRegionOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DigestNTRRegionLength = data.ReadUInt32LittleEndian();
|
||||
obj.DigestTWLRegionOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DigestTWLRegionLength = data.ReadUInt32LittleEndian();
|
||||
obj.DigestSectorHashtableRegionOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DigestSectorHashtableRegionLength = data.ReadUInt32LittleEndian();
|
||||
obj.DigestBlockHashtableRegionOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DigestBlockHashtableRegionLength = data.ReadUInt32LittleEndian();
|
||||
obj.DigestSectorSize = data.ReadUInt32LittleEndian();
|
||||
obj.DigestBlockSectorCount = data.ReadUInt32LittleEndian();
|
||||
obj.IconBannerSize = data.ReadUInt32LittleEndian();
|
||||
obj.Unknown1 = data.ReadUInt32LittleEndian();
|
||||
obj.NTRTWLRegionRomSize = data.ReadUInt32LittleEndian();
|
||||
obj.Unknown2 = data.ReadBytes(12);
|
||||
obj.ModcryptArea1Offset = data.ReadUInt32LittleEndian();
|
||||
obj.ModcryptArea1Size = data.ReadUInt32LittleEndian();
|
||||
obj.ModcryptArea2Offset = data.ReadUInt32LittleEndian();
|
||||
obj.ModcryptArea2Size = data.ReadUInt32LittleEndian();
|
||||
obj.TitleID = data.ReadBytes(8);
|
||||
obj.DSiWarePublicSavSize = data.ReadUInt32LittleEndian();
|
||||
obj.DSiWarePrivateSavSize = data.ReadUInt32LittleEndian();
|
||||
obj.ReservedZero = data.ReadBytes(176);
|
||||
obj.Unknown3 = data.ReadBytes(16);
|
||||
obj.ARM9WithSecureAreaSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.ARM7SHA1HMACHash = data.ReadBytes(20);
|
||||
obj.DigestMasterSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.BannerSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.ARM9iDecryptedSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.ARM7iDecryptedSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.Reserved5 = data.ReadBytes(40);
|
||||
obj.ARM9NoSecureAreaSHA1HMACHash = data.ReadBytes(20);
|
||||
obj.Reserved6 = data.ReadBytes(2636);
|
||||
obj.ReservedAndUnchecked = data.ReadBytes(0x180);
|
||||
obj.RSASignature = data.ReadBytes(0x80);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FileAllocationTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FileAllocationTableEntry on success, null on error</returns>
|
||||
public static FileAllocationTableEntry ParseFileAllocationTableEntry(Stream data)
|
||||
{
|
||||
var obj = new FileAllocationTableEntry();
|
||||
|
||||
obj.StartOffset = data.ReadUInt32LittleEndian();
|
||||
obj.EndOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FolderAllocationTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FolderAllocationTableEntry on success, null on error</returns>
|
||||
public static FolderAllocationTableEntry ParseFolderAllocationTableEntry(Stream data)
|
||||
{
|
||||
var obj = new FolderAllocationTableEntry();
|
||||
|
||||
obj.StartOffset = data.ReadUInt32LittleEndian();
|
||||
obj.FirstFileIndex = data.ReadUInt16LittleEndian();
|
||||
obj.ParentFolderIndex = data.ReadByteValue();
|
||||
obj.Unknown = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a NameListEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled NameListEntry on success, null on error</returns>
|
||||
public static NameListEntry? ParseNameListEntry(Stream data)
|
||||
{
|
||||
var entry = new NameListEntry();
|
||||
|
||||
byte flagAndSize = data.ReadByteValue();
|
||||
if (flagAndSize == 0xFF)
|
||||
return null;
|
||||
|
||||
entry.Folder = (flagAndSize & 0x80) != 0;
|
||||
|
||||
byte size = (byte)(flagAndSize & ~0x80);
|
||||
if (size > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(size);
|
||||
entry.Name = Encoding.UTF8.GetString(name);
|
||||
}
|
||||
|
||||
if (entry.Folder)
|
||||
entry.Index = data.ReadUInt16LittleEndian();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled name table on success, null on error</returns>
|
||||
private static NameTable? ParseNameTable(Stream data)
|
||||
public static NameTable ParseNameTable(Stream data)
|
||||
{
|
||||
var nameTable = new NameTable();
|
||||
|
||||
@@ -135,10 +307,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
int entryCount = int.MaxValue;
|
||||
while (entryCount > 0)
|
||||
{
|
||||
var entry = data.ReadType<FolderAllocationTableEntry>();
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
var entry = ParseFolderAllocationTableEntry(data);
|
||||
folderAllocationTable.Add(entry);
|
||||
|
||||
// If we have the root entry
|
||||
@@ -168,33 +337,5 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
return nameTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a name list entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled name list entry on success, null on error</returns>
|
||||
private static NameListEntry? ParseNameListEntry(Stream data)
|
||||
{
|
||||
var entry = new NameListEntry();
|
||||
|
||||
byte flagAndSize = data.ReadByteValue();
|
||||
if (flagAndSize == 0xFF)
|
||||
return null;
|
||||
|
||||
entry.Folder = (flagAndSize & 0x80) != 0;
|
||||
|
||||
byte size = (byte)(flagAndSize & ~0x80);
|
||||
if (size > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(size);
|
||||
entry.Name = Encoding.UTF8.GetString(name);
|
||||
}
|
||||
|
||||
if (entry.Folder)
|
||||
entry.Index = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PAK;
|
||||
using static SabreTools.Models.PAK.Constants;
|
||||
@@ -22,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature != SignatureString)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the package header
|
||||
@@ -47,11 +48,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory items
|
||||
for (int i = 0; i < file.DirectoryItems.Length; i++)
|
||||
{
|
||||
var directoryItem = data.ReadType<DirectoryItem>();
|
||||
if (directoryItem == null)
|
||||
return null;
|
||||
|
||||
file.DirectoryItems[i] = directoryItem;
|
||||
file.DirectoryItems[i] = ParseDirectoryItem(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -64,5 +61,39 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryItem
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryItem on success, null on error</returns>
|
||||
public static DirectoryItem ParseDirectoryItem(Stream data)
|
||||
{
|
||||
var obj = new DirectoryItem();
|
||||
|
||||
byte[] itemName = data.ReadBytes(56);
|
||||
obj.ItemName = Encoding.ASCII.GetString(itemName).TrimEnd('\0');
|
||||
obj.ItemOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ItemLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.DirectoryOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,33 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
if (header.Signature == Version0SignatureString)
|
||||
{
|
||||
if (header.FileSegmentSize != Version0HSegmentSize)
|
||||
return null;
|
||||
}
|
||||
else if (header.Signature == Version2SignatureString)
|
||||
{
|
||||
if (header.FileSegmentSize != Version2SegmentSize)
|
||||
return null;
|
||||
}
|
||||
else if (header.Signature == Version3SignatureString)
|
||||
{
|
||||
if (header.FileSegmentSize != Version2SegmentSize
|
||||
&& header.FileSegmentSize != Version3SegmentSize)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (header.Signature == Version4SignatureString)
|
||||
{
|
||||
if (header.FileSegmentSize != Version4SegmentSize)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the archive header
|
||||
archive.Header = header;
|
||||
@@ -48,11 +73,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Read all segments in turn
|
||||
for (int i = 0; i < header.NumberOfFiles; i++)
|
||||
{
|
||||
var file = ParseSegment(data, header.FileSegmentSize);
|
||||
if (file == null)
|
||||
continue;
|
||||
|
||||
archive.Segments[i] = file;
|
||||
archive.Segments[i] = ParseSegment(data, header.FileSegmentSize);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -67,13 +88,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Seek to the footer
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the footer
|
||||
var footer = data.ReadType<Footer>();
|
||||
if (footer == null)
|
||||
return null;
|
||||
|
||||
// Set the archive footer
|
||||
archive.Footer = footer;
|
||||
archive.Footer = ParseFooter(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -87,54 +103,63 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a header
|
||||
/// Parse a Stream into a Footer
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled header on success, null on error</returns>
|
||||
private static Header? ParseHeader(Stream data)
|
||||
/// <returns>Filled Footer on success, null on error</returns>
|
||||
public static Footer ParseFooter(Stream data)
|
||||
{
|
||||
var header = data.ReadType<Header>();
|
||||
return header?.Signature switch
|
||||
{
|
||||
Version0SignatureString when header.FileSegmentSize != Version0HSegmentSize => null,
|
||||
Version0SignatureString => header,
|
||||
var obj = new Footer();
|
||||
|
||||
Version2SignatureString when header.FileSegmentSize != Version2SegmentSize => null,
|
||||
Version2SignatureString => header,
|
||||
obj.SystemIP = data.ReadUInt32LittleEndian();
|
||||
obj.Reserved = data.ReadUInt32LittleEndian();
|
||||
byte[] kingTag = data.ReadBytes(4);
|
||||
obj.KingTag = Encoding.ASCII.GetString(kingTag);
|
||||
|
||||
Version3SignatureString when header.FileSegmentSize != Version2SegmentSize
|
||||
&& header.FileSegmentSize != Version3SegmentSize => null,
|
||||
Version3SignatureString => header,
|
||||
|
||||
Version4SignatureString when header.FileSegmentSize != Version4SegmentSize => null,
|
||||
Version4SignatureString => header,
|
||||
|
||||
_ => null,
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file entry
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
obj.HeaderSize = data.ReadUInt32LittleEndian();
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.NumberOfFiles = data.ReadUInt32LittleEndian();
|
||||
obj.FileSegmentSize = data.ReadUInt32LittleEndian();
|
||||
obj.FileListOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Segment
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="segmentSize">PFF segment size</param>
|
||||
/// <returns>Filled file entry on success, null on error</returns>
|
||||
private static Segment ParseSegment(Stream data, uint segmentSize)
|
||||
/// <returns>Filled Segment on success, null on error</returns>
|
||||
public static Segment ParseSegment(Stream data, uint segmentSize)
|
||||
{
|
||||
var segment = new Segment();
|
||||
var obj = new Segment();
|
||||
|
||||
segment.Deleted = data.ReadUInt32();
|
||||
segment.FileLocation = data.ReadUInt32();
|
||||
segment.FileSize = data.ReadUInt32();
|
||||
segment.PackedDate = data.ReadUInt32();
|
||||
obj.Deleted = data.ReadUInt32LittleEndian();
|
||||
obj.FileLocation = data.ReadUInt32LittleEndian();
|
||||
obj.FileSize = data.ReadUInt32LittleEndian();
|
||||
obj.PackedDate = data.ReadUInt32LittleEndian();
|
||||
byte[] fileName = data.ReadBytes(0x10);
|
||||
segment.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0');
|
||||
obj.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0');
|
||||
if (segmentSize > Version2SegmentSize)
|
||||
segment.ModifiedDate = data.ReadUInt32();
|
||||
obj.ModifiedDate = data.ReadUInt32LittleEndian();
|
||||
if (segmentSize > Version3SegmentSize)
|
||||
segment.CompressionLevel = data.ReadUInt32();
|
||||
obj.CompressionLevel = data.ReadUInt32LittleEndian();
|
||||
|
||||
return segment;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class PIC : BaseBinaryDeserializer<DiscInformation>
|
||||
{
|
||||
#region IStreamDeserializer
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override DiscInformation? Deserialize(Stream? data)
|
||||
{
|
||||
@@ -37,9 +35,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
var unit = ParseDiscInformationUnit(data);
|
||||
if (unit == null)
|
||||
continue;
|
||||
|
||||
diUnits.Add(unit);
|
||||
}
|
||||
|
||||
@@ -55,54 +50,39 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a disc information unit
|
||||
/// Parse a Stream into a DiscInformationUnit
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled disc information unit on success, null on error</returns>
|
||||
private static DiscInformationUnit? ParseDiscInformationUnit(Stream data)
|
||||
/// <returns>Filled DiscInformationUnit on success, null on error</returns>
|
||||
public static DiscInformationUnit ParseDiscInformationUnit(Stream data)
|
||||
{
|
||||
var unit = new DiscInformationUnit();
|
||||
var obj = new DiscInformationUnit();
|
||||
|
||||
#region Header
|
||||
|
||||
// We only accept Disc Information units, not Emergency Brake or other
|
||||
var header = data.ReadType<DiscInformationUnitHeader>();
|
||||
if (header?.DiscInformationIdentifier != "DI")
|
||||
return null;
|
||||
|
||||
// Set the information unit header
|
||||
unit.Header = header;
|
||||
obj.Header = ParseDiscInformationUnitHeader(data);
|
||||
if (obj.Header.DiscInformationIdentifier != "DI")
|
||||
return obj;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Body
|
||||
|
||||
// Try to parse the body
|
||||
var body = ParseDiscInformationUnitBody(data);
|
||||
if (body == null)
|
||||
return null;
|
||||
|
||||
// Set the information unit body
|
||||
unit.Body = body;
|
||||
obj.Body = ParseDiscInformationUnitBody(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Trailer
|
||||
|
||||
if (unit.Body.DiscTypeIdentifier == DiscTypeIdentifierReWritable || unit.Body.DiscTypeIdentifier == DiscTypeIdentifierRecordable)
|
||||
{
|
||||
// Try to parse the trailer
|
||||
var trailer = data.ReadType<DiscInformationUnitTrailer>();
|
||||
if (trailer == null)
|
||||
return null;
|
||||
|
||||
// Set the information unit trailer
|
||||
unit.Trailer = trailer;
|
||||
}
|
||||
// Set the information unit trailer
|
||||
if (obj.Body.DiscTypeIdentifier == DiscTypeIdentifierReWritable || obj.Body.DiscTypeIdentifier == DiscTypeIdentifierRecordable)
|
||||
obj.Trailer = ParseDiscInformationUnitTrailer(data);
|
||||
|
||||
#endregion
|
||||
|
||||
return unit;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,29 +90,68 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled disc information unit body on success, null on error</returns>
|
||||
private static DiscInformationUnitBody? ParseDiscInformationUnitBody(Stream data)
|
||||
private static DiscInformationUnitBody ParseDiscInformationUnitBody(Stream data)
|
||||
{
|
||||
var body = new DiscInformationUnitBody();
|
||||
var obj = new DiscInformationUnitBody();
|
||||
|
||||
byte[] dti = data.ReadBytes(3);
|
||||
body.DiscTypeIdentifier = Encoding.ASCII.GetString(dti);
|
||||
body.DiscSizeClassVersion = data.ReadByteValue();
|
||||
switch (body.DiscTypeIdentifier)
|
||||
obj.DiscTypeIdentifier = Encoding.ASCII.GetString(dti);
|
||||
obj.DiscSizeClassVersion = data.ReadByteValue();
|
||||
switch (obj.DiscTypeIdentifier)
|
||||
{
|
||||
case DiscTypeIdentifierROM:
|
||||
case DiscTypeIdentifierROMUltra:
|
||||
case DiscTypeIdentifierXGD4:
|
||||
body.FormatDependentContents = data.ReadBytes(52);
|
||||
obj.FormatDependentContents = data.ReadBytes(52);
|
||||
break;
|
||||
case DiscTypeIdentifierReWritable:
|
||||
case DiscTypeIdentifierRecordable:
|
||||
body.FormatDependentContents = data.ReadBytes(100);
|
||||
obj.FormatDependentContents = data.ReadBytes(100);
|
||||
break;
|
||||
}
|
||||
|
||||
return body;
|
||||
return obj;
|
||||
}
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DiscInformationUnitHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>DiscInformationUnitHeader on success, null on error</returns>
|
||||
public static DiscInformationUnitHeader ParseDiscInformationUnitHeader(Stream data)
|
||||
{
|
||||
var obj = new DiscInformationUnitHeader();
|
||||
|
||||
byte[] discInformationIdentifier = data.ReadBytes(2);
|
||||
obj.DiscInformationIdentifier = Encoding.ASCII.GetString(discInformationIdentifier);
|
||||
if (obj.DiscInformationIdentifier != "DI")
|
||||
return obj;
|
||||
|
||||
obj.DiscInformationFormat = data.ReadByteValue();
|
||||
obj.NumberOfUnitsInBlock = data.ReadByteValue();
|
||||
obj.Reserved0 = data.ReadByteValue();
|
||||
obj.SequenceNumber = data.ReadByteValue();
|
||||
obj.BytesInUse = data.ReadByteValue();
|
||||
obj.Reserved1 = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DiscInformationUnitTrailer
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>DiscInformationUnitTrailer on success, null on error</returns>
|
||||
public static DiscInformationUnitTrailer ParseDiscInformationUnitTrailer(Stream data)
|
||||
{
|
||||
var obj = new DiscInformationUnitTrailer();
|
||||
|
||||
obj.DiscManufacturerID = data.ReadBytes(6);
|
||||
obj.MediaTypeID = data.ReadBytes(3);
|
||||
obj.TimeStamp = data.ReadUInt16LittleEndian();
|
||||
obj.ProductRevisionNumber = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PKZIP;
|
||||
using static SabreTools.Models.PKZIP.Constants;
|
||||
|
||||
// TODO: Finish replacing ReadType
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class PKZIP : BaseBinaryDeserializer<Archive>
|
||||
@@ -320,7 +321,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
uint possibleSignature = data.ReadUInt32LittleEndian();
|
||||
if (possibleSignature == EndOfCentralDirectoryRecordSignature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
@@ -360,17 +361,17 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var record = new EndOfCentralDirectoryRecord();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
record.Signature = data.ReadUInt32LittleEndian();
|
||||
if (record.Signature != EndOfCentralDirectoryRecordSignature)
|
||||
return null;
|
||||
|
||||
record.DiskNumber = data.ReadUInt16();
|
||||
record.StartDiskNumber = data.ReadUInt16();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt16();
|
||||
record.TotalEntries = data.ReadUInt16();
|
||||
record.CentralDirectorySize = data.ReadUInt32();
|
||||
record.CentralDirectoryOffset = data.ReadUInt32();
|
||||
record.FileCommentLength = data.ReadUInt16();
|
||||
record.DiskNumber = data.ReadUInt16LittleEndian();
|
||||
record.StartDiskNumber = data.ReadUInt16LittleEndian();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt16LittleEndian();
|
||||
record.TotalEntries = data.ReadUInt16LittleEndian();
|
||||
record.CentralDirectorySize = data.ReadUInt32LittleEndian();
|
||||
record.CentralDirectoryOffset = data.ReadUInt32LittleEndian();
|
||||
record.FileCommentLength = data.ReadUInt16LittleEndian();
|
||||
if (record.FileCommentLength > 0 && data.Position + record.FileCommentLength <= data.Length)
|
||||
{
|
||||
byte[] commentBytes = data.ReadBytes(record.FileCommentLength);
|
||||
@@ -401,7 +402,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
uint possibleSignature = data.ReadUInt32LittleEndian();
|
||||
if (possibleSignature == EndOfCentralDirectoryLocator64Signature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
@@ -441,20 +442,20 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var record = new EndOfCentralDirectoryRecord64();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
record.Signature = data.ReadUInt32LittleEndian();
|
||||
if (record.Signature != EndOfCentralDirectoryRecord64Signature)
|
||||
return null;
|
||||
|
||||
record.DirectoryRecordSize = data.ReadUInt64();
|
||||
record.DirectoryRecordSize = data.ReadUInt64LittleEndian();
|
||||
record.HostSystem = (HostSystem)data.ReadByteValue();
|
||||
record.VersionMadeBy = data.ReadByteValue();
|
||||
record.VersionNeededToExtract = data.ReadUInt16();
|
||||
record.DiskNumber = data.ReadUInt32();
|
||||
record.StartDiskNumber = data.ReadUInt32();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt64();
|
||||
record.TotalEntries = data.ReadUInt64();
|
||||
record.CentralDirectorySize = data.ReadUInt64();
|
||||
record.CentralDirectoryOffset = data.ReadUInt64();
|
||||
record.VersionNeededToExtract = data.ReadUInt16LittleEndian();
|
||||
record.DiskNumber = data.ReadUInt32LittleEndian();
|
||||
record.StartDiskNumber = data.ReadUInt32LittleEndian();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt64LittleEndian();
|
||||
record.TotalEntries = data.ReadUInt64LittleEndian();
|
||||
record.CentralDirectorySize = data.ReadUInt64LittleEndian();
|
||||
record.CentralDirectoryOffset = data.ReadUInt64LittleEndian();
|
||||
|
||||
// TODO: Handle the ExtensibleDataSector -- How to detect if exists?
|
||||
|
||||
@@ -470,27 +471,27 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var header = new CentralDirectoryFileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt32();
|
||||
header.Signature = data.ReadUInt32LittleEndian();
|
||||
if (header.Signature != CentralDirectoryFileHeaderSignature)
|
||||
return null;
|
||||
|
||||
header.HostSystem = (HostSystem)data.ReadByteValue();
|
||||
header.VersionMadeBy = data.ReadByteValue();
|
||||
header.VersionNeededToExtract = data.ReadUInt16();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16();
|
||||
header.LastModifedFileTime = data.ReadUInt16();
|
||||
header.LastModifiedFileDate = data.ReadUInt16();
|
||||
header.CRC32 = data.ReadUInt32();
|
||||
header.CompressedSize = data.ReadUInt32();
|
||||
header.UncompressedSize = data.ReadUInt32();
|
||||
header.FileNameLength = data.ReadUInt16();
|
||||
header.ExtraFieldLength = data.ReadUInt16();
|
||||
header.FileCommentLength = data.ReadUInt16();
|
||||
header.DiskNumberStart = data.ReadUInt16();
|
||||
header.InternalFileAttributes = (InternalFileAttributes)data.ReadUInt16();
|
||||
header.ExternalFileAttributes = data.ReadUInt32();
|
||||
header.RelativeOffsetOfLocalHeader = data.ReadUInt32();
|
||||
header.VersionNeededToExtract = data.ReadUInt16LittleEndian();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16LittleEndian();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16LittleEndian();
|
||||
header.LastModifedFileTime = data.ReadUInt16LittleEndian();
|
||||
header.LastModifiedFileDate = data.ReadUInt16LittleEndian();
|
||||
header.CRC32 = data.ReadUInt32LittleEndian();
|
||||
header.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
header.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
header.FileNameLength = data.ReadUInt16LittleEndian();
|
||||
header.ExtraFieldLength = data.ReadUInt16LittleEndian();
|
||||
header.FileCommentLength = data.ReadUInt16LittleEndian();
|
||||
header.DiskNumberStart = data.ReadUInt16LittleEndian();
|
||||
header.InternalFileAttributes = (InternalFileAttributes)data.ReadUInt16LittleEndian();
|
||||
header.ExternalFileAttributes = data.ReadUInt32LittleEndian();
|
||||
header.RelativeOffsetOfLocalHeader = data.ReadUInt32LittleEndian();
|
||||
|
||||
if (header.FileNameLength > 0 && data.Position + header.FileNameLength <= data.Length)
|
||||
{
|
||||
@@ -538,7 +539,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
uint possibleSignature = data.ReadUInt32LittleEndian();
|
||||
if (possibleSignature == ArchiveExtraDataRecordSignature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
@@ -578,11 +579,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var record = new ArchiveExtraDataRecord();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
record.Signature = data.ReadUInt32LittleEndian();
|
||||
if (record.Signature != ArchiveExtraDataRecordSignature)
|
||||
return null;
|
||||
|
||||
record.ExtraFieldLength = data.ReadUInt32();
|
||||
record.ExtraFieldLength = data.ReadUInt32LittleEndian();
|
||||
if (record.ExtraFieldLength > 0 && data.Position + record.ExtraFieldLength <= data.Length)
|
||||
{
|
||||
byte[] extraBytes = data.ReadBytes((int)record.ExtraFieldLength);
|
||||
@@ -604,20 +605,20 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var header = new LocalFileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt32();
|
||||
header.Signature = data.ReadUInt32LittleEndian();
|
||||
if (header.Signature != LocalFileHeaderSignature)
|
||||
return null;
|
||||
|
||||
header.Version = data.ReadUInt16();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16();
|
||||
header.LastModifedFileTime = data.ReadUInt16();
|
||||
header.LastModifiedFileDate = data.ReadUInt16();
|
||||
header.CRC32 = data.ReadUInt32();
|
||||
header.CompressedSize = data.ReadUInt32();
|
||||
header.UncompressedSize = data.ReadUInt32();
|
||||
header.FileNameLength = data.ReadUInt16();
|
||||
header.ExtraFieldLength = data.ReadUInt16();
|
||||
header.Version = data.ReadUInt16LittleEndian();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16LittleEndian();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16LittleEndian();
|
||||
header.LastModifedFileTime = data.ReadUInt16LittleEndian();
|
||||
header.LastModifiedFileDate = data.ReadUInt16LittleEndian();
|
||||
header.CRC32 = data.ReadUInt32LittleEndian();
|
||||
header.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
header.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
header.FileNameLength = data.ReadUInt16LittleEndian();
|
||||
header.ExtraFieldLength = data.ReadUInt16LittleEndian();
|
||||
|
||||
if (header.FileNameLength > 0 && data.Position + header.FileNameLength <= data.Length)
|
||||
{
|
||||
@@ -649,13 +650,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var dataDescriptor = new DataDescriptor();
|
||||
|
||||
// Signatures are expected but not required
|
||||
dataDescriptor.Signature = data.ReadUInt32();
|
||||
dataDescriptor.Signature = data.ReadUInt32LittleEndian();
|
||||
if (dataDescriptor.Signature != DataDescriptorSignature)
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
|
||||
dataDescriptor.CRC32 = data.ReadUInt32();
|
||||
dataDescriptor.CompressedSize = data.ReadUInt32();
|
||||
dataDescriptor.UncompressedSize = data.ReadUInt32();
|
||||
dataDescriptor.CRC32 = data.ReadUInt32LittleEndian();
|
||||
dataDescriptor.CompressedSize = data.ReadUInt32LittleEndian();
|
||||
dataDescriptor.UncompressedSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return dataDescriptor;
|
||||
}
|
||||
@@ -670,13 +671,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var zip64DataDescriptor = new DataDescriptor64();
|
||||
|
||||
// Signatures are expected but not required
|
||||
zip64DataDescriptor.Signature = data.ReadUInt32();
|
||||
zip64DataDescriptor.Signature = data.ReadUInt32LittleEndian();
|
||||
if (zip64DataDescriptor.Signature != DataDescriptorSignature)
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
|
||||
zip64DataDescriptor.CRC32 = data.ReadUInt32();
|
||||
zip64DataDescriptor.CompressedSize = data.ReadUInt64();
|
||||
zip64DataDescriptor.UncompressedSize = data.ReadUInt64();
|
||||
zip64DataDescriptor.CRC32 = data.ReadUInt32LittleEndian();
|
||||
zip64DataDescriptor.CompressedSize = data.ReadUInt64LittleEndian();
|
||||
zip64DataDescriptor.UncompressedSize = data.ReadUInt64LittleEndian();
|
||||
|
||||
return zip64DataDescriptor;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
// Set the unknown value 2
|
||||
audioFile.UnknownValue2 = data.ReadUInt32();
|
||||
audioFile.UnknownValue2 = data.ReadUInt32LittleEndian();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Data Files Count
|
||||
|
||||
// Set the data files count
|
||||
audioFile.DataFilesCount = data.ReadUInt32();
|
||||
audioFile.DataFilesCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -183,11 +183,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
AudioHeader audioHeader;
|
||||
|
||||
// Get the common header pieces
|
||||
uint signature = data.ReadUInt32();
|
||||
uint signature = data.ReadUInt32LittleEndian();
|
||||
if (signature != SignatureUInt32)
|
||||
return null;
|
||||
|
||||
uint version = data.ReadUInt32();
|
||||
uint version = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Build the header according to version
|
||||
uint unknownOffset1;
|
||||
@@ -199,16 +199,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
v1.Signature = signature;
|
||||
v1.Version = version;
|
||||
v1.TrackID = data.ReadUInt32();
|
||||
v1.UnknownOffset1 = data.ReadUInt32();
|
||||
v1.UnknownOffset2 = data.ReadUInt32();
|
||||
v1.UnknownOffset3 = data.ReadUInt32();
|
||||
v1.Unknown1 = data.ReadUInt32();
|
||||
v1.Unknown2 = data.ReadUInt32();
|
||||
v1.Year = data.ReadUInt32();
|
||||
v1.TrackID = data.ReadUInt32LittleEndian();
|
||||
v1.UnknownOffset1 = data.ReadUInt32LittleEndian();
|
||||
v1.UnknownOffset2 = data.ReadUInt32LittleEndian();
|
||||
v1.UnknownOffset3 = data.ReadUInt32LittleEndian();
|
||||
v1.Unknown1 = data.ReadUInt32LittleEndian();
|
||||
v1.Unknown2 = data.ReadUInt32LittleEndian();
|
||||
v1.Year = data.ReadUInt32LittleEndian();
|
||||
v1.TrackNumber = data.ReadByteValue();
|
||||
v1.Subgenre = (Subgenre)data.ReadByteValue();
|
||||
v1.Duration = data.ReadUInt32();
|
||||
v1.Duration = data.ReadUInt32LittleEndian();
|
||||
|
||||
audioHeader = v1;
|
||||
unknownOffset1 = v1.UnknownOffset1;
|
||||
@@ -220,29 +220,29 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
v2.Signature = signature;
|
||||
v2.Version = version;
|
||||
v2.Unknown1 = data.ReadUInt32();
|
||||
v2.Unknown2 = data.ReadUInt32();
|
||||
v2.Unknown3 = data.ReadUInt32();
|
||||
v2.Unknown4 = data.ReadUInt32();
|
||||
v2.Unknown5 = data.ReadUInt32();
|
||||
v2.Unknown6 = data.ReadUInt32();
|
||||
v2.UnknownOffset1 = data.ReadUInt32();
|
||||
v2.Unknown7 = data.ReadUInt32();
|
||||
v2.Unknown8 = data.ReadUInt32();
|
||||
v2.Unknown9 = data.ReadUInt32();
|
||||
v2.UnknownOffset2 = data.ReadUInt32();
|
||||
v2.Unknown10 = data.ReadUInt32();
|
||||
v2.Unknown11 = data.ReadUInt32();
|
||||
v2.Unknown12 = data.ReadUInt32();
|
||||
v2.Unknown13 = data.ReadUInt32();
|
||||
v2.Unknown14 = data.ReadUInt32();
|
||||
v2.Unknown15 = data.ReadUInt32();
|
||||
v2.Unknown16 = data.ReadUInt32();
|
||||
v2.Unknown17 = data.ReadUInt32();
|
||||
v2.TrackID = data.ReadUInt32();
|
||||
v2.Year = data.ReadUInt32();
|
||||
v2.TrackNumber = data.ReadUInt32();
|
||||
v2.Unknown18 = data.ReadUInt32();
|
||||
v2.Unknown1 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown2 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown3 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown4 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown5 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown6 = data.ReadUInt32LittleEndian();
|
||||
v2.UnknownOffset1 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown7 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown8 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown9 = data.ReadUInt32LittleEndian();
|
||||
v2.UnknownOffset2 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown10 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown11 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown12 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown13 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown14 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown15 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown16 = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown17 = data.ReadUInt32LittleEndian();
|
||||
v2.TrackID = data.ReadUInt32LittleEndian();
|
||||
v2.Year = data.ReadUInt32LittleEndian();
|
||||
v2.TrackNumber = data.ReadUInt32LittleEndian();
|
||||
v2.Unknown18 = data.ReadUInt32LittleEndian();
|
||||
|
||||
audioHeader = v2;
|
||||
unknownOffset1 = v2.UnknownOffset1 + 0x54;
|
||||
@@ -253,33 +253,33 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return null;
|
||||
}
|
||||
|
||||
audioHeader.TrackLength = data.ReadUInt16();
|
||||
audioHeader.TrackLength = data.ReadUInt16LittleEndian();
|
||||
byte[] track = data.ReadBytes(audioHeader.TrackLength);
|
||||
audioHeader.Track = Encoding.ASCII.GetString(track);
|
||||
|
||||
audioHeader.ArtistLength = data.ReadUInt16();
|
||||
audioHeader.ArtistLength = data.ReadUInt16LittleEndian();
|
||||
byte[] artist = data.ReadBytes(audioHeader.ArtistLength);
|
||||
audioHeader.Artist = Encoding.ASCII.GetString(artist);
|
||||
|
||||
audioHeader.AlbumLength = data.ReadUInt16();
|
||||
audioHeader.AlbumLength = data.ReadUInt16LittleEndian();
|
||||
byte[] album = data.ReadBytes(audioHeader.AlbumLength);
|
||||
audioHeader.Album = Encoding.ASCII.GetString(album);
|
||||
|
||||
audioHeader.WriterLength = data.ReadUInt16();
|
||||
audioHeader.WriterLength = data.ReadUInt16LittleEndian();
|
||||
byte[] writer = data.ReadBytes(audioHeader.WriterLength);
|
||||
audioHeader.Writer = Encoding.ASCII.GetString(writer);
|
||||
|
||||
audioHeader.PublisherLength = data.ReadUInt16();
|
||||
audioHeader.PublisherLength = data.ReadUInt16LittleEndian();
|
||||
byte[] publisher = data.ReadBytes(audioHeader.PublisherLength);
|
||||
audioHeader.Publisher = Encoding.ASCII.GetString(publisher);
|
||||
|
||||
audioHeader.LabelLength = data.ReadUInt16();
|
||||
audioHeader.LabelLength = data.ReadUInt16LittleEndian();
|
||||
byte[] label = data.ReadBytes(audioHeader.LabelLength);
|
||||
audioHeader.Label = Encoding.ASCII.GetString(label);
|
||||
|
||||
if (data.Position - initialOffset < unknownOffset1)
|
||||
{
|
||||
audioHeader.CommentsLength = data.ReadUInt16();
|
||||
audioHeader.CommentsLength = data.ReadUInt16LittleEndian();
|
||||
byte[] comments = data.ReadBytes(audioHeader.CommentsLength);
|
||||
audioHeader.Comments = Encoding.ASCII.GetString(comments);
|
||||
}
|
||||
@@ -296,7 +296,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var unknownBlock1 = new UnknownBlock1();
|
||||
|
||||
unknownBlock1.Length = data.ReadUInt32();
|
||||
unknownBlock1.Length = data.ReadUInt32LittleEndian();
|
||||
unknownBlock1.Data = data.ReadBytes((int)unknownBlock1.Length);
|
||||
|
||||
return unknownBlock1;
|
||||
@@ -325,10 +325,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var dataFile = new DataFile();
|
||||
|
||||
dataFile.FileNameLength = data.ReadUInt16();
|
||||
dataFile.FileNameLength = data.ReadUInt16LittleEndian();
|
||||
byte[] fileName = data.ReadBytes(dataFile.FileNameLength);
|
||||
dataFile.FileName = Encoding.ASCII.GetString(fileName);
|
||||
dataFile.DataLength = data.ReadUInt32();
|
||||
dataFile.DataLength = data.ReadUInt32LittleEndian();
|
||||
dataFile.Data = data.ReadBytes((int)dataFile.DataLength);
|
||||
|
||||
return dataFile;
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var playlistHeader = new PlaylistHeader();
|
||||
|
||||
playlistHeader.TrackCount = data.ReadUInt32();
|
||||
playlistHeader.TrackCount = data.ReadUInt32LittleEndian();
|
||||
if (playlistHeader.TrackCount > int.MaxValue)
|
||||
return null;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature != SignatureString)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
@@ -40,11 +40,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Read all entries in turn
|
||||
for (int i = 0; i < header.FileCount; i++)
|
||||
{
|
||||
var file = ParseFileDescriptor(data, header.MinorVersion);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
fileDescriptors[i] = file;
|
||||
fileDescriptors[i] = ParseFileDescriptor(data, header.MinorVersion);
|
||||
}
|
||||
|
||||
// Set the file list
|
||||
@@ -65,38 +61,58 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file descriptor
|
||||
/// Parse a Stream into a FileDescriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="minorVersion">Minor version of the archive</param>
|
||||
/// <returns>Filled file descriptor on success, null on error</returns>
|
||||
private static FileDescriptor ParseFileDescriptor(Stream data, byte minorVersion)
|
||||
/// <returns>Filled FileDescriptor on success, null on error</returns>
|
||||
public static FileDescriptor ParseFileDescriptor(Stream data, byte minorVersion)
|
||||
{
|
||||
var fileDescriptor = new FileDescriptor();
|
||||
var obj = new FileDescriptor();
|
||||
|
||||
fileDescriptor.FileNameSize = ReadVariableLength(data);
|
||||
if (fileDescriptor.FileNameSize > 0)
|
||||
obj.FileNameSize = ReadVariableLength(data);
|
||||
if (obj.FileNameSize > 0)
|
||||
{
|
||||
byte[] fileName = data.ReadBytes(fileDescriptor.FileNameSize);
|
||||
fileDescriptor.FileName = Encoding.ASCII.GetString(fileName);
|
||||
byte[] fileName = data.ReadBytes(obj.FileNameSize);
|
||||
obj.FileName = Encoding.ASCII.GetString(fileName);
|
||||
}
|
||||
|
||||
fileDescriptor.CommentFieldSize = ReadVariableLength(data);
|
||||
if (fileDescriptor.CommentFieldSize > 0)
|
||||
obj.CommentFieldSize = ReadVariableLength(data);
|
||||
if (obj.CommentFieldSize > 0)
|
||||
{
|
||||
byte[] commentField = data.ReadBytes(fileDescriptor.CommentFieldSize);
|
||||
fileDescriptor.CommentField = Encoding.ASCII.GetString(commentField);
|
||||
byte[] commentField = data.ReadBytes(obj.CommentFieldSize);
|
||||
obj.CommentField = Encoding.ASCII.GetString(commentField);
|
||||
}
|
||||
|
||||
fileDescriptor.ExpandedFileSize = data.ReadUInt32();
|
||||
fileDescriptor.FileTime = data.ReadUInt16();
|
||||
fileDescriptor.FileDate = data.ReadUInt16();
|
||||
obj.ExpandedFileSize = data.ReadUInt32LittleEndian();
|
||||
obj.FileTime = data.ReadUInt16LittleEndian();
|
||||
obj.FileDate = data.ReadUInt16LittleEndian();
|
||||
|
||||
// Hack for unknown format data
|
||||
if (minorVersion == 22)
|
||||
fileDescriptor.Unknown = data.ReadUInt16();
|
||||
obj.Unknown = data.ReadUInt16LittleEndian();
|
||||
|
||||
return fileDescriptor;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
byte[] signature = data.ReadBytes(2);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.MajorVersion = data.ReadByteValue();
|
||||
obj.MinorVersion = data.ReadByteValue();
|
||||
obj.FileCount = data.ReadUInt16LittleEndian();
|
||||
obj.TableSize = data.ReadByteValue();
|
||||
obj.CompressionFlags = data.ReadByteValue();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using static SabreTools.Models.PlayStation3.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
@@ -16,13 +17,32 @@ namespace SabreTools.Serialization.Deserializers
|
||||
try
|
||||
{
|
||||
// Deserialize the SFB
|
||||
var sfb = data.ReadType<Models.PlayStation3.SFB>();
|
||||
if (sfb?.Magic == null)
|
||||
var sfb = new Models.PlayStation3.SFB();
|
||||
|
||||
sfb.Magic = data.ReadUInt32BigEndian();
|
||||
if (sfb.Magic != SFBMagic)
|
||||
return null;
|
||||
|
||||
string magic = Encoding.ASCII.GetString(sfb.Magic);
|
||||
if (magic != ".SFB")
|
||||
return null;
|
||||
sfb.FileVersion = data.ReadUInt32BigEndian();
|
||||
sfb.Reserved1 = data.ReadBytes(0x18);
|
||||
byte[] flagsType = data.ReadBytes(0x10);
|
||||
sfb.FlagsType = Encoding.ASCII.GetString(flagsType).TrimEnd('\0');
|
||||
sfb.DiscContentDataOffset = data.ReadUInt32BigEndian();
|
||||
sfb.DiscContentDataLength = data.ReadUInt32BigEndian();
|
||||
sfb.Reserved2 = data.ReadBytes(0x08);
|
||||
byte[] discTitleName = data.ReadBytes(0x08);
|
||||
sfb.DiscTitleName = Encoding.ASCII.GetString(discTitleName).TrimEnd('\0');
|
||||
sfb.Reserved3 = data.ReadBytes(0x08);
|
||||
sfb.DiscVersionDataOffset = data.ReadUInt32BigEndian();
|
||||
sfb.DiscVersionDataLength = data.ReadUInt32BigEndian();
|
||||
sfb.Reserved4 = data.ReadBytes(0x188);
|
||||
byte[] discContent = data.ReadBytes(0x20);
|
||||
sfb.DiscContent = Encoding.ASCII.GetString(discContent).TrimEnd('\0');
|
||||
byte[] discTitle = data.ReadBytes(0x10);
|
||||
sfb.DiscTitle = Encoding.ASCII.GetString(discTitle).TrimEnd('\0');
|
||||
byte[] discVersion = data.ReadBytes(0x10);
|
||||
sfb.DiscVersion = Encoding.ASCII.GetString(discVersion).TrimEnd('\0');
|
||||
sfb.Reserved5 = data.ReadBytes(0x3C0);
|
||||
|
||||
return sfb;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PlayStation3;
|
||||
using static SabreTools.Models.PlayStation3.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
@@ -21,8 +22,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
var header = ParseSFOHeader(data);
|
||||
if (header.Magic != SFOMagic)
|
||||
return null;
|
||||
|
||||
// Assign the header
|
||||
@@ -61,31 +62,39 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an SFO header
|
||||
/// Parse a Stream into an SFOHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SFO header on success, null on error</returns>
|
||||
public static Models.PlayStation3.SFOHeader? ParseHeader(Stream data)
|
||||
/// <returns>Filled SFOHeader on success, null on error</returns>
|
||||
public static SFOHeader ParseSFOHeader(Stream data)
|
||||
{
|
||||
var sfoHeader = data.ReadType<Models.PlayStation3.SFOHeader>();
|
||||
if (sfoHeader == null)
|
||||
return null;
|
||||
var obj = new SFOHeader();
|
||||
|
||||
string magic = Encoding.ASCII.GetString(sfoHeader!.Magic!);
|
||||
if (magic != "\0PSF")
|
||||
return null;
|
||||
obj.Magic = data.ReadUInt32BigEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.KeyTableStart = data.ReadUInt32LittleEndian();
|
||||
obj.DataTableStart = data.ReadUInt32LittleEndian();
|
||||
obj.TablesEntries = data.ReadUInt32LittleEndian();
|
||||
|
||||
return sfoHeader;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an SFO index table entry
|
||||
/// Parse a Stream into an SFOIndexTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SFO index table entry on success, null on error</returns>
|
||||
public static Models.PlayStation3.SFOIndexTableEntry? ParseIndexTableEntry(Stream data)
|
||||
/// <returns>Filled SFOIndexTableEntry on success, null on error</returns>
|
||||
public static SFOIndexTableEntry ParseIndexTableEntry(Stream data)
|
||||
{
|
||||
return data.ReadType<Models.PlayStation3.SFOIndexTableEntry>();
|
||||
var obj = new SFOIndexTableEntry();
|
||||
|
||||
obj.KeyOffset = data.ReadUInt16LittleEndian();
|
||||
obj.DataFormat = (DataFormat)data.ReadUInt16LittleEndian();
|
||||
obj.DataLength = data.ReadUInt32LittleEndian();
|
||||
obj.DataMaxLength = data.ReadUInt32LittleEndian();
|
||||
obj.DataOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (signature != SignatureString)
|
||||
return null;
|
||||
|
||||
ushort majorVersion = data.ReadUInt16();
|
||||
ushort minorVersion = data.ReadUInt16();
|
||||
ushort majorVersion = data.ReadUInt16LittleEndian();
|
||||
ushort minorVersion = data.ReadUInt16LittleEndian();
|
||||
if (minorVersion != 0)
|
||||
return null;
|
||||
|
||||
@@ -85,9 +85,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
byte[] header4Name = data.ReadBytes(count: 128);
|
||||
header4.Name = Encoding.Unicode.GetString(header4Name).TrimEnd('\0');
|
||||
header4.HeaderMD5 = data.ReadBytes(0x10);
|
||||
header4.HeaderLength = data.ReadUInt32();
|
||||
header4.FileDataOffset = data.ReadUInt32();
|
||||
header4.Dummy0 = data.ReadUInt32();
|
||||
header4.HeaderLength = data.ReadUInt32LittleEndian();
|
||||
header4.FileDataOffset = data.ReadUInt32LittleEndian();
|
||||
header4.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return header4;
|
||||
|
||||
@@ -101,9 +101,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
header6.MinorVersion = minorVersion;
|
||||
byte[] header6Name = data.ReadBytes(count: 128);
|
||||
header6.Name = Encoding.Unicode.GetString(header6Name).TrimEnd('\0');
|
||||
header6.HeaderLength = data.ReadUInt32();
|
||||
header6.FileDataOffset = data.ReadUInt32();
|
||||
header6.Dummy0 = data.ReadUInt32();
|
||||
header6.HeaderLength = data.ReadUInt32LittleEndian();
|
||||
header6.FileDataOffset = data.ReadUInt32LittleEndian();
|
||||
header6.Dummy0 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return header6;
|
||||
|
||||
@@ -743,14 +743,14 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var directoryHeader4 = new DirectoryHeader4();
|
||||
|
||||
directoryHeader4.SectionOffset = data.ReadUInt32();
|
||||
directoryHeader4.SectionCount = data.ReadUInt16();
|
||||
directoryHeader4.FolderOffset = data.ReadUInt32();
|
||||
directoryHeader4.FolderCount = data.ReadUInt16();
|
||||
directoryHeader4.FileOffset = data.ReadUInt32();
|
||||
directoryHeader4.FileCount = data.ReadUInt16();
|
||||
directoryHeader4.StringTableOffset = data.ReadUInt32();
|
||||
directoryHeader4.StringTableCount = data.ReadUInt16();
|
||||
directoryHeader4.SectionOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader4.SectionCount = data.ReadUInt16LittleEndian();
|
||||
directoryHeader4.FolderOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader4.FolderCount = data.ReadUInt16LittleEndian();
|
||||
directoryHeader4.FileOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader4.FileCount = data.ReadUInt16LittleEndian();
|
||||
directoryHeader4.StringTableOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader4.StringTableCount = data.ReadUInt16LittleEndian();
|
||||
|
||||
return directoryHeader4;
|
||||
}
|
||||
@@ -764,14 +764,14 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var directoryHeader5 = new DirectoryHeader5();
|
||||
|
||||
directoryHeader5.SectionOffset = data.ReadUInt32();
|
||||
directoryHeader5.SectionCount = data.ReadUInt32();
|
||||
directoryHeader5.FolderOffset = data.ReadUInt32();
|
||||
directoryHeader5.FolderCount = data.ReadUInt32();
|
||||
directoryHeader5.FileOffset = data.ReadUInt32();
|
||||
directoryHeader5.FileCount = data.ReadUInt32();
|
||||
directoryHeader5.StringTableOffset = data.ReadUInt32();
|
||||
directoryHeader5.StringTableCount = data.ReadUInt32();
|
||||
directoryHeader5.SectionOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.SectionCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.FolderOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.FolderCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.FileOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.FileCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.StringTableOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader5.StringTableCount = data.ReadUInt32LittleEndian();
|
||||
|
||||
return directoryHeader5;
|
||||
}
|
||||
@@ -785,16 +785,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var directoryHeader7 = new DirectoryHeader7();
|
||||
|
||||
directoryHeader7.SectionOffset = data.ReadUInt32();
|
||||
directoryHeader7.SectionCount = data.ReadUInt32();
|
||||
directoryHeader7.FolderOffset = data.ReadUInt32();
|
||||
directoryHeader7.FolderCount = data.ReadUInt32();
|
||||
directoryHeader7.FileOffset = data.ReadUInt32();
|
||||
directoryHeader7.FileCount = data.ReadUInt32();
|
||||
directoryHeader7.StringTableOffset = data.ReadUInt32();
|
||||
directoryHeader7.StringTableCount = data.ReadUInt32();
|
||||
directoryHeader7.HashTableOffset = data.ReadUInt32();
|
||||
directoryHeader7.BlockSize = data.ReadUInt32();
|
||||
directoryHeader7.SectionOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.SectionCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.FolderOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.FolderCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.FileOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.FileCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.StringTableOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.StringTableCount = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.HashTableOffset = data.ReadUInt32LittleEndian();
|
||||
directoryHeader7.BlockSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return directoryHeader7;
|
||||
}
|
||||
@@ -813,11 +813,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
section4.Alias = Encoding.ASCII.GetString(section4Alias).TrimEnd('\0');
|
||||
byte[] section4Name = data.ReadBytes(64);
|
||||
section4.Name = Encoding.ASCII.GetString(section4Name).TrimEnd('\0');
|
||||
section4.FolderStartIndex = data.ReadUInt16();
|
||||
section4.FolderEndIndex = data.ReadUInt16();
|
||||
section4.FileStartIndex = data.ReadUInt16();
|
||||
section4.FileEndIndex = data.ReadUInt16();
|
||||
section4.FolderRootIndex = data.ReadUInt16();
|
||||
section4.FolderStartIndex = data.ReadUInt16LittleEndian();
|
||||
section4.FolderEndIndex = data.ReadUInt16LittleEndian();
|
||||
section4.FileStartIndex = data.ReadUInt16LittleEndian();
|
||||
section4.FileEndIndex = data.ReadUInt16LittleEndian();
|
||||
section4.FolderRootIndex = data.ReadUInt16LittleEndian();
|
||||
|
||||
return section4;
|
||||
}
|
||||
@@ -836,11 +836,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
section5.Alias = Encoding.ASCII.GetString(section5Alias).TrimEnd('\0');
|
||||
byte[] section5Name = data.ReadBytes(64);
|
||||
section5.Name = Encoding.ASCII.GetString(section5Name).TrimEnd('\0');
|
||||
section5.FolderStartIndex = data.ReadUInt32();
|
||||
section5.FolderEndIndex = data.ReadUInt32();
|
||||
section5.FileStartIndex = data.ReadUInt32();
|
||||
section5.FileEndIndex = data.ReadUInt32();
|
||||
section5.FolderRootIndex = data.ReadUInt32();
|
||||
section5.FolderStartIndex = data.ReadUInt32LittleEndian();
|
||||
section5.FolderEndIndex = data.ReadUInt32LittleEndian();
|
||||
section5.FileStartIndex = data.ReadUInt32LittleEndian();
|
||||
section5.FileEndIndex = data.ReadUInt32LittleEndian();
|
||||
section5.FolderRootIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return section5;
|
||||
}
|
||||
@@ -855,12 +855,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var folder4 = new Folder4();
|
||||
|
||||
folder4.NameOffset = data.ReadUInt32();
|
||||
folder4.NameOffset = data.ReadUInt32LittleEndian();
|
||||
folder4.Name = null; // Read from string table
|
||||
folder4.FolderStartIndex = data.ReadUInt16();
|
||||
folder4.FolderEndIndex = data.ReadUInt16();
|
||||
folder4.FileStartIndex = data.ReadUInt16();
|
||||
folder4.FileEndIndex = data.ReadUInt16();
|
||||
folder4.FolderStartIndex = data.ReadUInt16LittleEndian();
|
||||
folder4.FolderEndIndex = data.ReadUInt16LittleEndian();
|
||||
folder4.FileStartIndex = data.ReadUInt16LittleEndian();
|
||||
folder4.FileEndIndex = data.ReadUInt16LittleEndian();
|
||||
|
||||
return folder4;
|
||||
}
|
||||
@@ -875,12 +875,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var folder5 = new Folder5();
|
||||
|
||||
folder5.NameOffset = data.ReadUInt32();
|
||||
folder5.NameOffset = data.ReadUInt32LittleEndian();
|
||||
folder5.Name = null; // Read from string table
|
||||
folder5.FolderStartIndex = data.ReadUInt32();
|
||||
folder5.FolderEndIndex = data.ReadUInt32();
|
||||
folder5.FileStartIndex = data.ReadUInt32();
|
||||
folder5.FileEndIndex = data.ReadUInt32();
|
||||
folder5.FolderStartIndex = data.ReadUInt32LittleEndian();
|
||||
folder5.FolderEndIndex = data.ReadUInt32LittleEndian();
|
||||
folder5.FileStartIndex = data.ReadUInt32LittleEndian();
|
||||
folder5.FileEndIndex = data.ReadUInt32LittleEndian();
|
||||
|
||||
return folder5;
|
||||
}
|
||||
@@ -895,12 +895,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var file4 = new File4();
|
||||
|
||||
file4.NameOffset = data.ReadUInt32();
|
||||
file4.NameOffset = data.ReadUInt32LittleEndian();
|
||||
file4.Name = null; // Read from string table
|
||||
file4.Offset = data.ReadUInt32();
|
||||
file4.SizeOnDisk = data.ReadUInt32();
|
||||
file4.Size = data.ReadUInt32();
|
||||
file4.TimeModified = data.ReadUInt32();
|
||||
file4.Offset = data.ReadUInt32LittleEndian();
|
||||
file4.SizeOnDisk = data.ReadUInt32LittleEndian();
|
||||
file4.Size = data.ReadUInt32LittleEndian();
|
||||
file4.TimeModified = data.ReadUInt32LittleEndian();
|
||||
file4.Dummy0 = data.ReadByteValue();
|
||||
file4.Type = data.ReadByteValue();
|
||||
|
||||
@@ -917,15 +917,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var file6 = new File6();
|
||||
|
||||
file6.NameOffset = data.ReadUInt32();
|
||||
file6.NameOffset = data.ReadUInt32LittleEndian();
|
||||
file6.Name = null; // Read from string table
|
||||
file6.Offset = data.ReadUInt32();
|
||||
file6.SizeOnDisk = data.ReadUInt32();
|
||||
file6.Size = data.ReadUInt32();
|
||||
file6.TimeModified = data.ReadUInt32();
|
||||
file6.Offset = data.ReadUInt32LittleEndian();
|
||||
file6.SizeOnDisk = data.ReadUInt32LittleEndian();
|
||||
file6.Size = data.ReadUInt32LittleEndian();
|
||||
file6.TimeModified = data.ReadUInt32LittleEndian();
|
||||
file6.Dummy0 = data.ReadByteValue();
|
||||
file6.Type = data.ReadByteValue();
|
||||
file6.CRC32 = data.ReadUInt32();
|
||||
file6.CRC32 = data.ReadUInt32LittleEndian();
|
||||
|
||||
return file6;
|
||||
}
|
||||
@@ -940,16 +940,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var file7 = new File7();
|
||||
|
||||
file7.NameOffset = data.ReadUInt32();
|
||||
file7.NameOffset = data.ReadUInt32LittleEndian();
|
||||
file7.Name = null; // Read from string table
|
||||
file7.Offset = data.ReadUInt32();
|
||||
file7.SizeOnDisk = data.ReadUInt32();
|
||||
file7.Size = data.ReadUInt32();
|
||||
file7.TimeModified = data.ReadUInt32();
|
||||
file7.Offset = data.ReadUInt32LittleEndian();
|
||||
file7.SizeOnDisk = data.ReadUInt32LittleEndian();
|
||||
file7.Size = data.ReadUInt32LittleEndian();
|
||||
file7.TimeModified = data.ReadUInt32LittleEndian();
|
||||
file7.Dummy0 = data.ReadByteValue();
|
||||
file7.Type = data.ReadByteValue();
|
||||
file7.CRC32 = data.ReadUInt32();
|
||||
file7.HashOffset = data.ReadUInt32();
|
||||
file7.CRC32 = data.ReadUInt32LittleEndian();
|
||||
file7.HashOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return file7;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.BSP;
|
||||
using static SabreTools.Models.BSP.Constants;
|
||||
|
||||
// TODO: Finish replacing ReadType
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class VBSP : BaseBinaryDeserializer<VbspFile>
|
||||
@@ -25,8 +26,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<VbspHeader>();
|
||||
if (header?.Signature != SignatureString)
|
||||
var header = ParseVbspHeader(data);
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
if (Array.IndexOf([17, 18, 19, 20, 21, 22, 23, 25, 27, 29, 0x00040014], header.Version) > -1)
|
||||
return null;
|
||||
@@ -265,6 +266,43 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into VbspHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled VbspHeader on success, null on error</returns>
|
||||
public static VbspHeader ParseVbspHeader(Stream data)
|
||||
{
|
||||
var obj = new VbspHeader();
|
||||
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.Version = data.ReadInt32LittleEndian();
|
||||
obj.Lumps = new VbspLumpEntry[VBSP_HEADER_LUMPS];
|
||||
for (int i = 0; i < VBSP_HEADER_LUMPS; i++)
|
||||
{
|
||||
obj.Lumps[i] = ParseVbspLumpEntry(data);
|
||||
}
|
||||
obj.MapRevision = data.ReadInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into VbspLumpEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled VbspLumpEntry on success, null on error</returns>
|
||||
public static VbspLumpEntry ParseVbspLumpEntry(Stream data)
|
||||
{
|
||||
var obj = new VbspLumpEntry();
|
||||
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.FourCC = data.ReadBytes(4);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into LUMP_ENTITIES
|
||||
/// </summary>
|
||||
@@ -372,14 +410,14 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var lump = new VisibilityLump();
|
||||
|
||||
lump.NumClusters = data.ReadInt32();
|
||||
lump.NumClusters = data.ReadInt32LittleEndian();
|
||||
lump.ByteOffsets = new int[lump.NumClusters][];
|
||||
for (int i = 0; i < lump.NumClusters; i++)
|
||||
{
|
||||
lump.ByteOffsets[i] = new int[2];
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
lump.ByteOffsets[i][j] = data.ReadInt32();
|
||||
lump.ByteOffsets[i][j] = data.ReadInt32LittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,10 +505,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var model = new PhysModel();
|
||||
|
||||
model.ModelIndex = data.ReadInt32();
|
||||
model.DataSize = data.ReadInt32();
|
||||
model.KeydataSize = data.ReadInt32();
|
||||
model.SolidCount = data.ReadInt32();
|
||||
model.ModelIndex = data.ReadInt32LittleEndian();
|
||||
model.DataSize = data.ReadInt32LittleEndian();
|
||||
model.KeydataSize = data.ReadInt32LittleEndian();
|
||||
model.SolidCount = data.ReadInt32LittleEndian();
|
||||
model.Solids = new PhysSolid[model.SolidCount];
|
||||
for (int i = 0; i < model.Solids.Length; i++)
|
||||
{
|
||||
@@ -491,7 +529,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var solid = new PhysSolid();
|
||||
|
||||
solid.Size = data.ReadInt32();
|
||||
solid.Size = data.ReadInt32LittleEndian();
|
||||
if (solid.Size > 0)
|
||||
solid.CollisionData = data.ReadBytes(solid.Size);
|
||||
|
||||
@@ -525,7 +563,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var lump = new OcclusionLump();
|
||||
|
||||
lump.Count = data.ReadInt32();
|
||||
lump.Count = data.ReadInt32LittleEndian();
|
||||
lump.Data = new OccluderData[lump.Count];
|
||||
for (int i = 0; i < lump.Count; i++)
|
||||
{
|
||||
@@ -533,7 +571,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (occluderData != null)
|
||||
lump.Data[i] = occluderData;
|
||||
}
|
||||
lump.PolyDataCount = data.ReadInt32();
|
||||
lump.PolyDataCount = data.ReadInt32LittleEndian();
|
||||
lump.PolyData = new OccluderPolyData[lump.Count];
|
||||
for (int i = 0; i < lump.Count; i++)
|
||||
{
|
||||
@@ -541,11 +579,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (polyData != null)
|
||||
lump.PolyData[i] = polyData;
|
||||
}
|
||||
lump.VertexIndexCount = data.ReadInt32();
|
||||
lump.VertexIndexCount = data.ReadInt32LittleEndian();
|
||||
lump.VertexIndicies = new int[lump.VertexIndexCount];
|
||||
for (int i = 0; i < lump.VertexIndexCount; i++)
|
||||
{
|
||||
lump.VertexIndicies[i] = data.ReadInt32();
|
||||
lump.VertexIndicies[i] = data.ReadInt32LittleEndian();
|
||||
}
|
||||
|
||||
return lump;
|
||||
@@ -578,29 +616,29 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var leaf = new VbspLeaf();
|
||||
|
||||
leaf.Contents = (VbspContents)data.ReadUInt32();
|
||||
leaf.Cluster = data.ReadInt16();
|
||||
leaf.AreaFlags = data.ReadInt16();
|
||||
leaf.Contents = (VbspContents)data.ReadUInt32LittleEndian();
|
||||
leaf.Cluster = data.ReadInt16LittleEndian();
|
||||
leaf.AreaFlags = data.ReadInt16LittleEndian();
|
||||
leaf.Mins = new short[3];
|
||||
for (int i = 0; i < leaf.Mins.Length; i++)
|
||||
{
|
||||
leaf.Mins[i] = data.ReadInt16();
|
||||
leaf.Mins[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
leaf.Maxs = new short[3];
|
||||
for (int i = 0; i < leaf.Maxs.Length; i++)
|
||||
{
|
||||
leaf.Maxs[i] = data.ReadInt16();
|
||||
leaf.Maxs[i] = data.ReadInt16LittleEndian();
|
||||
}
|
||||
leaf.FirstLeafFace = data.ReadUInt16();
|
||||
leaf.NumLeafFaces = data.ReadUInt16();
|
||||
leaf.FirstLeafBrush = data.ReadUInt16();
|
||||
leaf.NumLeafBrushes = data.ReadUInt16();
|
||||
leaf.LeafWaterDataID = data.ReadInt16();
|
||||
leaf.FirstLeafFace = data.ReadUInt16LittleEndian();
|
||||
leaf.NumLeafFaces = data.ReadUInt16LittleEndian();
|
||||
leaf.FirstLeafBrush = data.ReadUInt16LittleEndian();
|
||||
leaf.NumLeafBrushes = data.ReadUInt16LittleEndian();
|
||||
leaf.LeafWaterDataID = data.ReadInt16LittleEndian();
|
||||
|
||||
if (version == 1)
|
||||
leaf.AmbientLighting = data.ReadType<CompressedLightCube>();
|
||||
else
|
||||
leaf.Padding = data.ReadInt16();
|
||||
leaf.Padding = data.ReadInt16LittleEndian();
|
||||
|
||||
return leaf;
|
||||
}
|
||||
@@ -615,7 +653,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var marksurfaces = new List<ushort>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
marksurfaces.Add(data.ReadUInt16());
|
||||
marksurfaces.Add(data.ReadUInt16LittleEndian());
|
||||
}
|
||||
|
||||
return new MarksurfacesLump { Marksurfaces = [.. marksurfaces] };
|
||||
@@ -649,7 +687,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var surfedges = new List<int>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
surfedges.Add(data.ReadInt32());
|
||||
surfedges.Add(data.ReadInt32LittleEndian());
|
||||
}
|
||||
|
||||
return new SurfedgesLump { Surfedges = [.. surfedges] };
|
||||
@@ -701,7 +739,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var map = new List<ushort>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
map.Add(data.ReadUInt16());
|
||||
map.Add(data.ReadUInt16LittleEndian());
|
||||
}
|
||||
|
||||
return new LeafFacesLump { Map = [.. map] };
|
||||
@@ -717,7 +755,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var map = new List<ushort>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
map.Add(data.ReadUInt16());
|
||||
map.Add(data.ReadUInt16LittleEndian());
|
||||
}
|
||||
|
||||
return new LeafBrushesLump { Map = [.. map] };
|
||||
@@ -804,7 +842,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var lump = new GameLump();
|
||||
|
||||
lump.LumpCount = data.ReadInt32();
|
||||
lump.LumpCount = data.ReadInt32LittleEndian();
|
||||
lump.Directories = new GameLumpDirectory[lump.LumpCount];
|
||||
for (int i = 0; i < lump.LumpCount; i++)
|
||||
{
|
||||
@@ -876,7 +914,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var offsets = new List<int>();
|
||||
while (data.Position < offset + length)
|
||||
{
|
||||
offsets.Add(data.ReadInt32());
|
||||
offsets.Add(data.ReadInt32LittleEndian());
|
||||
}
|
||||
|
||||
return new TexdataStringTable { Offsets = [.. offsets] };
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// The original version had no signature.
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature != SignatureUInt32)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature != SignatureUInt32)
|
||||
return null;
|
||||
if (header.Version > 2)
|
||||
return null;
|
||||
@@ -36,28 +36,16 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Extended Header
|
||||
|
||||
// Set the package extended header
|
||||
if (header.Version == 2)
|
||||
{
|
||||
// Try to parse the extended header
|
||||
var extendedHeader = data.ReadType<ExtendedHeader>();
|
||||
if (extendedHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the package extended header
|
||||
file.ExtendedHeader = extendedHeader;
|
||||
}
|
||||
file.ExtendedHeader = ParseExtendedHeader(data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Directory Items
|
||||
|
||||
// Create the directory items tree
|
||||
var directoryItems = ParseDirectoryItemTree(data);
|
||||
if (directoryItems == null)
|
||||
return null;
|
||||
|
||||
// Set the directory items
|
||||
file.DirectoryItems = directoryItems;
|
||||
file.DirectoryItems = ParseDirectoryItemTree(data);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -77,10 +65,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory items
|
||||
while (data.Position < initialOffset + file.ExtendedHeader.ArchiveMD5SectionSize)
|
||||
{
|
||||
var archiveHash = data.ReadType<ArchiveHash>();
|
||||
if (archiveHash == null)
|
||||
return null;
|
||||
|
||||
var archiveHash = ParseArchiveHash(data);
|
||||
archiveHashes.Add(archiveHash);
|
||||
}
|
||||
|
||||
@@ -98,12 +83,104 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ArchiveHash
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ArchiveHash on success, null on error</returns>
|
||||
public static ArchiveHash ParseArchiveHash(Stream data)
|
||||
{
|
||||
var obj = new ArchiveHash();
|
||||
|
||||
obj.ArchiveIndex = data.ReadUInt32LittleEndian();
|
||||
obj.ArchiveOffset = data.ReadUInt32LittleEndian();
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Hash = data.ReadBytes(0x10);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryEntry on success, null on error</returns>
|
||||
public static DirectoryEntry ParseDirectoryEntry(Stream data)
|
||||
{
|
||||
var obj = new DirectoryEntry();
|
||||
|
||||
obj.CRC = data.ReadUInt32LittleEndian();
|
||||
obj.PreloadBytes = data.ReadUInt16LittleEndian();
|
||||
obj.ArchiveIndex = data.ReadUInt16LittleEndian();
|
||||
obj.EntryOffset = data.ReadUInt32LittleEndian();
|
||||
obj.EntryLength = data.ReadUInt32LittleEndian();
|
||||
obj.Dummy0 = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Valve Package directory item
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Valve Package directory item on success, null on error</returns>
|
||||
public static DirectoryItem ParseDirectoryItem(Stream data, string extension, string path, string name)
|
||||
{
|
||||
var obj = new DirectoryItem();
|
||||
|
||||
obj.Extension = extension;
|
||||
obj.Path = path;
|
||||
obj.Name = name;
|
||||
|
||||
// Set the directory entry
|
||||
obj.DirectoryEntry = ParseDirectoryEntry(data);
|
||||
|
||||
// Get the preload data pointer
|
||||
long preloadDataPointer = -1; int preloadDataLength = -1;
|
||||
if (obj.DirectoryEntry.ArchiveIndex == HL_VPK_NO_ARCHIVE
|
||||
&& obj.DirectoryEntry.EntryLength > 0
|
||||
&& data.Position + obj.DirectoryEntry.EntryLength <= data.Length)
|
||||
{
|
||||
preloadDataPointer = obj.DirectoryEntry.EntryOffset;
|
||||
preloadDataLength = (int)obj.DirectoryEntry.EntryLength;
|
||||
}
|
||||
else if (obj.DirectoryEntry.PreloadBytes > 0)
|
||||
{
|
||||
preloadDataPointer = data.Position;
|
||||
preloadDataLength = obj.DirectoryEntry.PreloadBytes;
|
||||
}
|
||||
|
||||
// If we had a valid preload data pointer
|
||||
byte[]? preloadData = null;
|
||||
if (preloadDataPointer >= 0
|
||||
&& preloadDataLength > 0
|
||||
&& data.Position + preloadDataLength <= data.Length)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Seek to the preload data offset
|
||||
data.Seek(preloadDataPointer, SeekOrigin.Begin);
|
||||
|
||||
// Read the preload data
|
||||
preloadData = data.ReadBytes(preloadDataLength);
|
||||
|
||||
// Seek back to the original offset
|
||||
data.Seek(initialOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Set the preload data
|
||||
obj.PreloadData = preloadData;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Valve Package directory item tree
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Valve Package directory item tree on success, null on error</returns>
|
||||
private static DirectoryItem[]? ParseDirectoryItemTree(Stream data)
|
||||
public static DirectoryItem[] ParseDirectoryItemTree(Stream data)
|
||||
{
|
||||
// Create the directory items list
|
||||
var directoryItems = new List<DirectoryItem>();
|
||||
@@ -149,8 +226,6 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Get the directory item
|
||||
var directoryItem = ParseDirectoryItem(data, extensionString!, pathString!, nameString!);
|
||||
if (directoryItem == null)
|
||||
return null;
|
||||
|
||||
// Add the directory item
|
||||
directoryItems.Add(directoryItem);
|
||||
@@ -162,64 +237,36 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Valve Package directory item
|
||||
/// Parse a Stream into a ExtendedHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Valve Package directory item on success, null on error</returns>
|
||||
private static DirectoryItem? ParseDirectoryItem(Stream data, string extension, string path, string name)
|
||||
/// <returns>Filled ExtendedHeader on success, null on error</returns>
|
||||
public static ExtendedHeader ParseExtendedHeader(Stream data)
|
||||
{
|
||||
var directoryItem = new DirectoryItem();
|
||||
var obj = new ExtendedHeader();
|
||||
|
||||
directoryItem.Extension = extension;
|
||||
directoryItem.Path = path;
|
||||
directoryItem.Name = name;
|
||||
obj.FileDataSectionSize = data.ReadUInt32LittleEndian();
|
||||
obj.ArchiveMD5SectionSize = data.ReadUInt32LittleEndian();
|
||||
obj.OtherMD5SectionSize = data.ReadUInt32LittleEndian();
|
||||
obj.SignatureSectionSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Get the directory entry
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
return null;
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Set the directory entry
|
||||
directoryItem.DirectoryEntry = directoryEntry;
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
// Get the preload data pointer
|
||||
long preloadDataPointer = -1; int preloadDataLength = -1;
|
||||
if (directoryEntry.ArchiveIndex == HL_VPK_NO_ARCHIVE
|
||||
&& directoryEntry.EntryLength > 0
|
||||
&& data.Position + directoryEntry.EntryLength <= data.Length)
|
||||
{
|
||||
preloadDataPointer = directoryEntry.EntryOffset;
|
||||
preloadDataLength = (int)directoryEntry.EntryLength;
|
||||
}
|
||||
else if (directoryEntry.PreloadBytes > 0)
|
||||
{
|
||||
preloadDataPointer = data.Position;
|
||||
preloadDataLength = directoryEntry.PreloadBytes;
|
||||
}
|
||||
obj.Signature = data.ReadUInt32LittleEndian();
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.TreeSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// If we had a valid preload data pointer
|
||||
byte[]? preloadData = null;
|
||||
if (preloadDataPointer >= 0
|
||||
&& preloadDataLength > 0
|
||||
&& data.Position + preloadDataLength <= data.Length)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// Seek to the preload data offset
|
||||
data.Seek(preloadDataPointer, SeekOrigin.Begin);
|
||||
|
||||
// Read the preload data
|
||||
preloadData = data.ReadBytes(preloadDataLength);
|
||||
|
||||
// Seek back to the original offset
|
||||
data.Seek(initialOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Set the preload data
|
||||
directoryItem.PreloadData = preloadData;
|
||||
|
||||
return directoryItem;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature != SignatureString)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
// Set the package header
|
||||
@@ -46,11 +46,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
file.DirEntries = new DirEntry[header.NumDirs];
|
||||
for (int i = 0; i < header.NumDirs; i++)
|
||||
{
|
||||
var lump = data.ReadType<DirEntry>();
|
||||
if (lump == null)
|
||||
return null;
|
||||
|
||||
file.DirEntries[i] = lump;
|
||||
file.DirEntries[i] = ParseDirEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -95,12 +91,48 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package file entry
|
||||
/// Parse a Stream into a CharInfo
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CharInfo on success, null on error</returns>
|
||||
public static CharInfo ParseCharInfo(Stream data)
|
||||
{
|
||||
var obj = new CharInfo();
|
||||
|
||||
obj.StartOffset = data.ReadUInt16LittleEndian();
|
||||
obj.CharWidth = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirEntry on success, null on error</returns>
|
||||
public static DirEntry ParseDirEntry(Stream data)
|
||||
{
|
||||
var obj = new DirEntry();
|
||||
|
||||
obj.Offset = data.ReadUInt32LittleEndian();
|
||||
obj.DiskLength = data.ReadUInt32LittleEndian();
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.Type = (FileType)data.ReadByteValue();
|
||||
obj.Compression = data.ReadByteValue();
|
||||
obj.Padding = data.ReadUInt16LittleEndian();
|
||||
byte[] name = data.ReadBytes(16);
|
||||
obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FileEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="type">File entry type</param>
|
||||
/// <returns>Filled Half-Life Texture Package file entry on success, null on error</returns>
|
||||
private static FileEntry? ParseFileEntry(Stream data, FileType type)
|
||||
/// <returns>Filled FileEntry on success, null on error</returns>
|
||||
public static FileEntry? ParseFileEntry(Stream data, FileType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
@@ -113,115 +145,130 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package MipTex
|
||||
/// Parse a Stream into a Font
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package MipTex on success, null on error</returns>
|
||||
private static MipTex ParseMipTex(Stream data)
|
||||
/// <returns>Filled Font on success, null on error</returns>
|
||||
public static Font ParseFont(Stream data)
|
||||
{
|
||||
var miptex = new MipTex();
|
||||
var obj = new Font();
|
||||
|
||||
byte[] nameBytes = data.ReadBytes(16);
|
||||
miptex.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0');
|
||||
miptex.Width = data.ReadUInt32();
|
||||
miptex.Height = data.ReadUInt32();
|
||||
miptex.MipOffsets = new uint[4];
|
||||
for (int i = 0; i < miptex.MipOffsets.Length; i++)
|
||||
obj.Width = data.ReadUInt32LittleEndian();
|
||||
obj.Height = data.ReadUInt32LittleEndian();
|
||||
obj.RowCount = data.ReadUInt32LittleEndian();
|
||||
obj.RowHeight = data.ReadUInt32LittleEndian();
|
||||
obj.FontInfo = new CharInfo[256];
|
||||
for (int i = 0; i < obj.FontInfo.Length; i++)
|
||||
{
|
||||
miptex.MipOffsets[i] = data.ReadUInt32();
|
||||
obj.FontInfo[i] = ParseCharInfo(data);
|
||||
}
|
||||
miptex.MipImages = new MipMap[4];
|
||||
for (int i = 0; i < miptex.MipImages.Length; i++)
|
||||
obj.Data = new byte[obj.Height][];
|
||||
for (int i = 0; i < obj.Height; i++)
|
||||
{
|
||||
miptex.MipImages[i] = ParseMipMap(data, miptex.Width, miptex.Height);
|
||||
obj.Data[i] = data.ReadBytes((int)obj.Width);
|
||||
}
|
||||
miptex.ColorsUsed = data.ReadUInt16();
|
||||
miptex.Palette = new byte[miptex.ColorsUsed][];
|
||||
for (int i = 0; i < miptex.ColorsUsed; i++)
|
||||
obj.ColorsUsed = data.ReadUInt16LittleEndian();
|
||||
obj.Palette = new byte[obj.ColorsUsed][];
|
||||
for (int i = 0; i < obj.ColorsUsed; i++)
|
||||
{
|
||||
miptex.Palette[i] = data.ReadBytes(3);
|
||||
obj.Palette[i] = data.ReadBytes(3);
|
||||
}
|
||||
|
||||
return miptex;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package MipMap
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package MipMap on success, null on error</returns>
|
||||
private static MipMap ParseMipMap(Stream data, uint width, uint height)
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var mipmap = new MipMap();
|
||||
var obj = new Header();
|
||||
|
||||
mipmap.Data = new byte[width][];
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.NumDirs = data.ReadUInt32LittleEndian();
|
||||
obj.DirOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a MipMap
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled MipMap on success, null on error</returns>
|
||||
public static MipMap ParseMipMap(Stream data, uint width, uint height)
|
||||
{
|
||||
var obj = new MipMap();
|
||||
|
||||
obj.Data = new byte[width][];
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
mipmap.Data[i] = data.ReadBytes((int)height);
|
||||
obj.Data[i] = data.ReadBytes((int)height);
|
||||
}
|
||||
|
||||
return mipmap;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package Qpic image
|
||||
/// Parse a Stream into a MipTex
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package Qpic image on success, null on error</returns>
|
||||
private static QpicImage ParseQpicImage(Stream data)
|
||||
/// <returns>Filled MipTex on success, null on error</returns>
|
||||
public static MipTex ParseMipTex(Stream data)
|
||||
{
|
||||
var qpic = new QpicImage();
|
||||
var obj = new MipTex();
|
||||
|
||||
qpic.Width = data.ReadUInt32();
|
||||
qpic.Height = data.ReadUInt32();
|
||||
qpic.Data = new byte[qpic.Height][];
|
||||
for (int i = 0; i < qpic.Height; i++)
|
||||
byte[] nameBytes = data.ReadBytes(16);
|
||||
obj.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0');
|
||||
obj.Width = data.ReadUInt32LittleEndian();
|
||||
obj.Height = data.ReadUInt32LittleEndian();
|
||||
obj.MipOffsets = new uint[4];
|
||||
for (int i = 0; i < obj.MipOffsets.Length; i++)
|
||||
{
|
||||
qpic.Data[i] = data.ReadBytes((int)qpic.Width);
|
||||
obj.MipOffsets[i] = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
qpic.ColorsUsed = data.ReadUInt16();
|
||||
qpic.Palette = new byte[qpic.ColorsUsed][];
|
||||
for (int i = 0; i < qpic.ColorsUsed; i++)
|
||||
obj.MipImages = new MipMap[4];
|
||||
for (int i = 0; i < obj.MipImages.Length; i++)
|
||||
{
|
||||
qpic.Palette[i] = data.ReadBytes(3);
|
||||
obj.MipImages[i] = ParseMipMap(data, obj.Width, obj.Height);
|
||||
}
|
||||
obj.ColorsUsed = data.ReadUInt16LittleEndian();
|
||||
obj.Palette = new byte[obj.ColorsUsed][];
|
||||
for (int i = 0; i < obj.ColorsUsed; i++)
|
||||
{
|
||||
obj.Palette[i] = data.ReadBytes(3);
|
||||
}
|
||||
|
||||
return qpic;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Half-Life Texture Package font
|
||||
/// Parse a Stream into a QpicImage
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Half-Life Texture Package font on success, null on error</returns>
|
||||
private static Font ParseFont(Stream data)
|
||||
/// <returns>Filled QpicImage on success, null on error</returns>
|
||||
public static QpicImage ParseQpicImage(Stream data)
|
||||
{
|
||||
var font = new Font();
|
||||
var obj = new QpicImage();
|
||||
|
||||
font.Width = data.ReadUInt32();
|
||||
font.Height = data.ReadUInt32();
|
||||
font.RowCount = data.ReadUInt32();
|
||||
font.RowHeight = data.ReadUInt32();
|
||||
font.FontInfo = new CharInfo[256];
|
||||
for (int i = 0; i < font.FontInfo.Length; i++)
|
||||
obj.Width = data.ReadUInt32LittleEndian();
|
||||
obj.Height = data.ReadUInt32LittleEndian();
|
||||
obj.Data = new byte[obj.Height][];
|
||||
for (int i = 0; i < obj.Height; i++)
|
||||
{
|
||||
var fontInfo = data.ReadType<CharInfo>();
|
||||
if (fontInfo != null)
|
||||
font.FontInfo[i] = fontInfo;
|
||||
obj.Data[i] = data.ReadBytes((int)obj.Width);
|
||||
}
|
||||
font.Data = new byte[font.Height][];
|
||||
for (int i = 0; i < font.Height; i++)
|
||||
obj.ColorsUsed = data.ReadUInt16LittleEndian();
|
||||
obj.Palette = new byte[obj.ColorsUsed][];
|
||||
for (int i = 0; i < obj.ColorsUsed; i++)
|
||||
{
|
||||
font.Data[i] = data.ReadBytes((int)font.Width);
|
||||
}
|
||||
font.ColorsUsed = data.ReadUInt16();
|
||||
font.Palette = new byte[font.ColorsUsed][];
|
||||
for (int i = 0; i < font.ColorsUsed; i++)
|
||||
{
|
||||
font.Palette[i] = data.ReadBytes(3);
|
||||
obj.Palette[i] = data.ReadBytes(3);
|
||||
}
|
||||
|
||||
return font;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.XZP;
|
||||
using static SabreTools.Models.XZP.Constants;
|
||||
@@ -22,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = data.ReadType<Header>();
|
||||
if (header?.Signature != HeaderSignatureString)
|
||||
var header = ParseHeader(data);
|
||||
if (header.Signature != HeaderSignatureString)
|
||||
return null;
|
||||
if (header.Version != 6)
|
||||
return null;
|
||||
@@ -41,11 +42,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory entries
|
||||
for (int i = 0; i < file.DirectoryEntries.Length; i++)
|
||||
{
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
continue;
|
||||
|
||||
file.DirectoryEntries[i] = directoryEntry;
|
||||
file.DirectoryEntries[i] = ParseDirectoryEntry(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -60,11 +57,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the preload directory entries
|
||||
for (int i = 0; i < file.PreloadDirectoryEntries.Length; i++)
|
||||
{
|
||||
var directoryEntry = data.ReadType<DirectoryEntry>();
|
||||
if (directoryEntry == null)
|
||||
continue;
|
||||
|
||||
file.PreloadDirectoryEntries[i] = directoryEntry;
|
||||
file.PreloadDirectoryEntries[i] = ParseDirectoryEntry(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +73,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the preload directory mappings
|
||||
for (int i = 0; i < file.PreloadDirectoryMappings.Length; i++)
|
||||
{
|
||||
var directoryMapping = data.ReadType<DirectoryMapping>();
|
||||
if (directoryMapping == null)
|
||||
continue;
|
||||
|
||||
file.PreloadDirectoryMappings[i] = directoryMapping;
|
||||
file.PreloadDirectoryMappings[i] = ParseDirectoryMapping(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +97,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the directory items
|
||||
for (int i = 0; i < file.DirectoryItems.Length; i++)
|
||||
{
|
||||
var directoryItem = ParseDirectoryItem(data);
|
||||
file.DirectoryItems[i] = directoryItem;
|
||||
file.DirectoryItems[i] = ParseDirectoryItem(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +109,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
data.Seek(-8, SeekOrigin.End);
|
||||
|
||||
// Try to parse the footer
|
||||
var footer = data.ReadType<Footer>();
|
||||
if (footer?.Signature != FooterSignatureString)
|
||||
var footer = ParseFooter(data);
|
||||
if (footer.Signature != FooterSignatureString)
|
||||
return null;
|
||||
|
||||
// Set the package footer
|
||||
@@ -140,31 +128,100 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a XBox Package File directory item
|
||||
/// Parse a Stream into a DirectoryEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled XBox Package File directory item on success, null on error</returns>
|
||||
private static DirectoryItem ParseDirectoryItem(Stream data)
|
||||
/// <returns>Filled DirectoryEntry on success, null on error</returns>
|
||||
public static DirectoryEntry ParseDirectoryEntry(Stream data)
|
||||
{
|
||||
var directoryItem = new DirectoryItem();
|
||||
var obj = new DirectoryEntry();
|
||||
|
||||
directoryItem.FileNameCRC = data.ReadUInt32();
|
||||
directoryItem.NameOffset = data.ReadUInt32();
|
||||
directoryItem.TimeCreated = data.ReadUInt32();
|
||||
obj.FileNameCRC = data.ReadUInt32LittleEndian();
|
||||
obj.EntryLength = data.ReadUInt32LittleEndian();
|
||||
obj.EntryOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryItem
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryItem on success, null on error</returns>
|
||||
public static DirectoryItem ParseDirectoryItem(Stream data)
|
||||
{
|
||||
var obj = new DirectoryItem();
|
||||
|
||||
obj.FileNameCRC = data.ReadUInt32LittleEndian();
|
||||
obj.NameOffset = data.ReadUInt32LittleEndian();
|
||||
obj.TimeCreated = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Cache the current offset
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Seek to the name offset
|
||||
data.Seek(directoryItem.NameOffset, SeekOrigin.Begin);
|
||||
data.Seek(obj.NameOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the name
|
||||
directoryItem.Name = data.ReadNullTerminatedAnsiString();
|
||||
obj.Name = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
// Seek back to the right position
|
||||
data.Seek(currentPosition, SeekOrigin.Begin);
|
||||
|
||||
return directoryItem;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryMapping
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled DirectoryMapping on success, null on error</returns>
|
||||
public static DirectoryMapping ParseDirectoryMapping(Stream data)
|
||||
{
|
||||
var obj = new DirectoryMapping();
|
||||
|
||||
obj.PreloadDirectoryEntryIndex = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Footer
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Footer on success, null on error</returns>
|
||||
public static Footer ParseFooter(Stream data)
|
||||
{
|
||||
var obj = new Footer();
|
||||
|
||||
obj.FileLength = data.ReadUInt32LittleEndian();
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Header on success, null on error</returns>
|
||||
public static Header ParseHeader(Stream data)
|
||||
{
|
||||
var obj = new Header();
|
||||
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
obj.Signature = Encoding.ASCII.GetString(signature);
|
||||
obj.Version = data.ReadUInt32LittleEndian();
|
||||
obj.PreloadDirectoryEntryCount = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryEntryCount = data.ReadUInt32LittleEndian();
|
||||
obj.PreloadBytes = data.ReadUInt32LittleEndian();
|
||||
obj.HeaderLength = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryItemCount = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryItemOffset = data.ReadUInt32LittleEndian();
|
||||
obj.DirectoryItemLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,13 @@ namespace SabreTools.Serialization
|
||||
Wrapper.CHD item => item.PrettyPrint(),
|
||||
Wrapper.CIA item => item.PrettyPrint(),
|
||||
Wrapper.GCF item => item.PrettyPrint(),
|
||||
Wrapper.InstallShieldArchiveV3 item => item.PrettyPrint(),
|
||||
Wrapper.InstallShieldCabinet item => item.PrettyPrint(),
|
||||
Wrapper.IRD item => item.PrettyPrint(),
|
||||
Wrapper.LinearExecutable item => item.PrettyPrint(),
|
||||
Wrapper.LZKWAJ item => item.PrettyPrint(),
|
||||
Wrapper.LZQBasic item => item.PrettyPrint(),
|
||||
Wrapper.LZSZDD item => item.PrettyPrint(),
|
||||
Wrapper.MicrosoftCabinet item => item.PrettyPrint(),
|
||||
Wrapper.MoPaQ item => item.PrettyPrint(),
|
||||
Wrapper.MSDOS item => item.PrettyPrint(),
|
||||
@@ -87,9 +91,13 @@ namespace SabreTools.Serialization
|
||||
Wrapper.CHD item => item.ExportJSON(),
|
||||
Wrapper.CIA item => item.ExportJSON(),
|
||||
Wrapper.GCF item => item.ExportJSON(),
|
||||
Wrapper.InstallShieldArchiveV3 item => item.ExportJSON(),
|
||||
Wrapper.InstallShieldCabinet item => item.ExportJSON(),
|
||||
Wrapper.IRD item => item.ExportJSON(),
|
||||
Wrapper.LinearExecutable item => item.ExportJSON(),
|
||||
Wrapper.LZKWAJ item => item.ExportJSON(),
|
||||
Wrapper.LZQBasic item => item.ExportJSON(),
|
||||
Wrapper.LZSZDD item => item.ExportJSON(),
|
||||
Wrapper.MicrosoftCabinet item => item.ExportJSON(),
|
||||
Wrapper.MoPaQ item => item.ExportJSON(),
|
||||
Wrapper.MSDOS item => item.ExportJSON(),
|
||||
@@ -199,6 +207,16 @@ namespace SabreTools.Serialization
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.InstallShieldArchiveV3 item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
InstallShieldArchiveV3.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
@@ -229,6 +247,36 @@ namespace SabreTools.Serialization
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.LZKWAJ item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
LZKWAJ.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.LZQBasic item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
LZQBasic.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.LZSZDD item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
LZSZDD.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
|
||||
114
SabreTools.Serialization/Printers/InstallShieldArchiveV3.cs
Normal file
114
SabreTools.Serialization/Printers/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.InstallShieldArchiveV3;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class InstallShieldArchiveV3 : IPrinter<Archive>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, Archive model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, Archive archive)
|
||||
{
|
||||
builder.AppendLine("InstallShield Archive V3 Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, archive.Header);
|
||||
Print(builder, archive.Directories);
|
||||
Print(builder, archive.Files);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Header? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Signature1, " Signature 1");
|
||||
builder.AppendLine(header.Signature2, " Signature 2");
|
||||
builder.AppendLine(header.Reserved0, " Reserved 0");
|
||||
builder.AppendLine(header.IsMultivolume, " Is multivolume");
|
||||
builder.AppendLine(header.FileCount, " File count");
|
||||
builder.AppendLine(header.DateTime, " Datetime");
|
||||
builder.AppendLine(header.CompressedSize, " Compressed size");
|
||||
builder.AppendLine(header.UncompressedSize, " Uncompressed size");
|
||||
builder.AppendLine(header.Reserved1, " Reserved 1");
|
||||
builder.AppendLine(header.VolumeTotal, " Volume total");
|
||||
builder.AppendLine(header.VolumeNumber, " Volume number");
|
||||
builder.AppendLine(header.Reserved2, " Reserved 2");
|
||||
builder.AppendLine(header.SplitBeginAddress, " Split begin address");
|
||||
builder.AppendLine(header.SplitEndAddress, " Split end address");
|
||||
builder.AppendLine(header.TocAddress, " TOC address");
|
||||
builder.AppendLine(header.Reserved3, " Reserved 3");
|
||||
builder.AppendLine(header.DirCount, " Dir count");
|
||||
builder.AppendLine(header.Reserved4, " Reserved 4");
|
||||
builder.AppendLine(header.Reserved5, " Reserved 5");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Directory[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Directories:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No directories");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" Directory {i}");
|
||||
builder.AppendLine(entry.FileCount, " File count");
|
||||
builder.AppendLine(entry.ChunkSize, " Chunk size");
|
||||
builder.AppendLine(entry.NameLength, " Name length");
|
||||
builder.AppendLine(entry.Name, " Name");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, File[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Files:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No files");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
builder.AppendLine($" File {i}");
|
||||
builder.AppendLine(entry.VolumeEnd, " Volume end");
|
||||
builder.AppendLine(entry.Index, " Index");
|
||||
builder.AppendLine(entry.UncompressedSize, " Uncompressed size");
|
||||
builder.AppendLine(entry.CompressedSize, " Compressed size");
|
||||
builder.AppendLine(entry.Offset, " Offset");
|
||||
builder.AppendLine(entry.DateTime, " Datetime");
|
||||
builder.AppendLine(entry.Reserved0, " Reserved 0");
|
||||
builder.AppendLine(entry.ChunkSize, " Chunk size");
|
||||
builder.AppendLine($" Attrib: {entry.Attrib} (0x{entry.Attrib:X})");
|
||||
builder.AppendLine(entry.IsSplit, " Is split");
|
||||
builder.AppendLine(entry.Reserved1, " Reserved 1");
|
||||
builder.AppendLine(entry.VolumeStart, " Volume start");
|
||||
builder.AppendLine(entry.Name, " Name");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
63
SabreTools.Serialization/Printers/LZKWAJ.cs
Normal file
63
SabreTools.Serialization/Printers/LZKWAJ.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.LZ;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class LZKWAJ : IPrinter<KWAJFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, KWAJFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, KWAJFile file)
|
||||
{
|
||||
builder.AppendLine("LZ-compressed File, KWAJ Variant Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
Print(builder, file.HeaderExtensions);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, KWAJHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Magic, " Magic number");
|
||||
builder.AppendLine($" Compression type: {header.CompressionType} (0x{header.CompressionType:X})");
|
||||
builder.AppendLine(header.DataOffset, " Data offset");
|
||||
builder.AppendLine($" Header flags: {header.HeaderFlags} (0x{header.HeaderFlags:X})");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, KWAJHeaderExtensions? header)
|
||||
{
|
||||
builder.AppendLine(" Header Extensions Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header extensions");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.DecompressedLength, " Decompressed length");
|
||||
builder.AppendLine(header.UnknownPurpose, " Unknown purpose");
|
||||
builder.AppendLine(header.UnknownDataLength, " Unknown data length");
|
||||
builder.AppendLine(header.UnknownData, " Unknown data");
|
||||
builder.AppendLine(header.FileName, " File name");
|
||||
builder.AppendLine(header.FileExtension, " File extension");
|
||||
builder.AppendLine(header.ArbitraryTextLength, " Arbitrary text length");
|
||||
builder.AppendLine(header.ArbitraryText, " Arbitrary text");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
SabreTools.Serialization/Printers/LZQBasic.cs
Normal file
38
SabreTools.Serialization/Printers/LZQBasic.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.LZ;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class LZQBasic : IPrinter<QBasicFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, QBasicFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, QBasicFile file)
|
||||
{
|
||||
builder.AppendLine("LZ-compressed File, QBasic Variant Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, QBasicHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Magic, " Magic number");
|
||||
builder.AppendLine(header.RealLength, " Real length");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
SabreTools.Serialization/Printers/LZSZDD.cs
Normal file
40
SabreTools.Serialization/Printers/LZSZDD.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.LZ;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class LZSZDD : IPrinter<SZDDFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, SZDDFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, SZDDFile file)
|
||||
{
|
||||
builder.AppendLine("LZ-compressed File, SZDD Variant Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, file.Header);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SZDDHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Magic, " Magic number");
|
||||
builder.AppendLine($" Compression type: {header.CompressionType} (0x{header.CompressionType:X})");
|
||||
builder.AppendLine(header.LastChar, " Last char");
|
||||
builder.AppendLine(header.RealLength, " Real length");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using SabreTools.ASN1;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.PortableExecutable;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
@@ -1346,32 +1347,36 @@ namespace SabreTools.Serialization.Printers
|
||||
else
|
||||
{
|
||||
int offset = 0;
|
||||
byte[]? magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16));
|
||||
byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16));
|
||||
|
||||
if (magic == null)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
else if (magic[0] == 0x4D && magic[1] == 0x5A)
|
||||
if (magic.StartsWith([0x4D, 0x5A]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54)
|
||||
else if (magic.StartsWith([0x4D, 0x53, 0x46, 0x54]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x03 && magic[3] == 0x04)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x03, 0x04]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x05 && magic[3] == 0x06)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x05, 0x06]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded empty PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x07 && magic[3] == 0x08)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x07, 0x08]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded spanned PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded RAR file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded RAR5 file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(magic, $"{padding}Data");
|
||||
@@ -1830,26 +1835,34 @@ namespace SabreTools.Serialization.Printers
|
||||
int offset = 0;
|
||||
|
||||
byte[] magic = entry.Data.ReadBytes(ref offset, Math.Min(entry.Data.Length, 16));
|
||||
if (magic[0] == 0x4D && magic[1] == 0x5A)
|
||||
if (magic.StartsWith([0x4D, 0x5A]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded Executable File]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x4D && magic[1] == 0x53 && magic[2] == 0x46 && magic[3] == 0x54)
|
||||
else if (magic.StartsWith([0x4D, 0x53, 0x46, 0x54]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded OLE Library File]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x03 && magic[3] == 0x04)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x03, 0x04]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x05 && magic[3] == 0x06)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x05, 0x06]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded empty PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic[0] == 0x50 && magic[1] == 0x4B && magic[2] == 0x07 && magic[3] == 0x08)
|
||||
else if (magic.StartsWith([0x50, 0x4B, 0x07, 0x08]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded spanned PKZIP file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded RAR file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00]))
|
||||
{
|
||||
builder.AppendLine($"{padding}Data: [Embedded RAR5 file]"); // TODO: Parse this out and print separately
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(magic, $"{padding}Data");
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.8.2</Version>
|
||||
<Version>1.8.4</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
@@ -30,10 +32,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.ASN1" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.6.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.6" />
|
||||
<PackageReference Include="SabreTools.ASN1" Version="1.5.1" />
|
||||
<PackageReference Include="SabreTools.Compression" Version="0.6.3" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.4.1" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.6.2" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.5.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.IO;
|
||||
using SabreTools.Compression.Deflate;
|
||||
using SabreTools.Models.BFPK;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class BFPK : WrapperBase<Models.BFPK.Archive>
|
||||
public class BFPK : WrapperBase<Archive>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
@@ -11,17 +13,24 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="Archive.Files"/>
|
||||
public FileEntry[] Files => Model.Files ?? [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BFPK(Models.BFPK.Archive? model, byte[]? data, int offset)
|
||||
public BFPK(Archive? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BFPK(Models.BFPK.Archive? model, Stream? data)
|
||||
public BFPK(Archive? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
@@ -74,5 +83,96 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the BFPK to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public bool ExtractAll(string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (Files == null || Files.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < Files.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the BFPK to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public bool ExtractFile(int index, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (Files == null || Files.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (index < 0 || index >= Files.Length)
|
||||
return false;
|
||||
|
||||
// Get the file information
|
||||
var file = Files[index];
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Get the read index and length
|
||||
int offset = file.Offset + 4;
|
||||
int compressedSize = file.CompressedSize;
|
||||
|
||||
// Some files can lack the length prefix
|
||||
if (compressedSize > GetEndOfFile())
|
||||
{
|
||||
offset -= 4;
|
||||
compressedSize = file.UncompressedSize;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Ensure the output directory exists
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
|
||||
// Create the output path
|
||||
string filePath = Path.Combine(outputDirectory, file.Name ?? $"file{index}");
|
||||
using FileStream fs = File.OpenWrite(filePath);
|
||||
|
||||
// Read the data block
|
||||
var data = ReadFromDataSource(offset, compressedSize);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// If we have uncompressed data
|
||||
if (compressedSize == file.UncompressedSize)
|
||||
{
|
||||
fs.Write(data, 0, compressedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
ZlibStream zs = new ZlibStream(ms, CompressionMode.Decompress);
|
||||
zs.CopyTo(fs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
287
SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs
Normal file
287
SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,287 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Compression.Blast;
|
||||
using SabreTools.Models.InstallShieldArchiveV3;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
/// <remarks>
|
||||
/// Reference (de)compressor: https://www.sac.sk/download/pack/icomp95.zip
|
||||
/// </remarks>
|
||||
/// <see href="https://github.com/wfr/unshieldv3"/>
|
||||
public partial class InstallShieldArchiveV3 : WrapperBase<Archive>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "InstallShield Archive V3";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="Header.DirCount"/>
|
||||
public ushort DirCount => Model.Header?.DirCount ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Header.FileCount"/>
|
||||
public ushort FileCount => Model.Header?.FileCount ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Archive.Directories"/>
|
||||
public Models.InstallShieldArchiveV3.Directory[] Directories => Model.Directories ?? [];
|
||||
|
||||
/// <inheritdoc cref="Archive.Files"/>
|
||||
public Models.InstallShieldArchiveV3.File[] Files => Model.Files ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Map of all files to their parent directories by index
|
||||
/// </summary>
|
||||
public Dictionary<int, int> FileDirMap
|
||||
{
|
||||
get
|
||||
{
|
||||
// Return the prebuilt map
|
||||
if (_fileDirMap != null)
|
||||
return _fileDirMap;
|
||||
|
||||
// Build the file map
|
||||
_fileDirMap = [];
|
||||
|
||||
int fileId = 0;
|
||||
for (int i = 0; i < Directories.Length; i++)
|
||||
{
|
||||
var dir = Directories[i];
|
||||
for (int j = 0; j < dir.FileCount; j++)
|
||||
{
|
||||
_fileDirMap[fileId++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
return _fileDirMap;
|
||||
}
|
||||
}
|
||||
private Dictionary<int, int>? _fileDirMap = null;
|
||||
|
||||
/// <summary>
|
||||
/// Map of all files found in the archive
|
||||
/// </summary>
|
||||
public Dictionary<string, Models.InstallShieldArchiveV3.File> FileNameMap
|
||||
{
|
||||
get
|
||||
{
|
||||
// Return the prebuilt map
|
||||
if (_fileNameMap != null)
|
||||
return _fileNameMap;
|
||||
|
||||
// Build the file map
|
||||
_fileNameMap = [];
|
||||
for (int fileIndex = 0; fileIndex < Files.Length; fileIndex++)
|
||||
{
|
||||
// Get the current file
|
||||
var file = Files[fileIndex];
|
||||
|
||||
// Get the parent directory
|
||||
int dirIndex = FileDirMap[fileIndex];
|
||||
if (dirIndex < 0 || dirIndex >= DirCount)
|
||||
continue;
|
||||
|
||||
// Create the filename
|
||||
string filename = Path.Combine(
|
||||
Directories[dirIndex]?.Name ?? $"dir_{dirIndex}",
|
||||
file.Name ?? $"file_{fileIndex}"
|
||||
);
|
||||
|
||||
// Add to the map
|
||||
_fileNameMap[filename] = file;
|
||||
}
|
||||
|
||||
return _fileNameMap;
|
||||
}
|
||||
}
|
||||
private Dictionary<string, Models.InstallShieldArchiveV3.File>? _fileNameMap = null;
|
||||
|
||||
/// <summary>
|
||||
/// Data offset for all archives
|
||||
/// </summary>
|
||||
private const uint DataStart = 255;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public InstallShieldArchiveV3(Archive? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public InstallShieldArchiveV3(Archive? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an InstallShield Archive V3 from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the archive</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A archive wrapper on success, null on failure</returns>
|
||||
public static InstallShieldArchiveV3? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a InstallShield Archive V3 from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the archive</param>
|
||||
/// <returns>A archive wrapper on success, null on failure</returns>
|
||||
public static InstallShieldArchiveV3? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var archive = Deserializers.InstallShieldArchiveV3.DeserializeStream(data);
|
||||
if (archive == null)
|
||||
return null;
|
||||
|
||||
return new InstallShieldArchiveV3(archive, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the ISAv3 to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public bool ExtractAll(string outputDirectory)
|
||||
{
|
||||
// Get the file count
|
||||
int fileCount = Files.Length;
|
||||
if (fileCount == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the ISAv3 to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public bool ExtractFile(int index, string outputDirectory)
|
||||
{
|
||||
// If the files index is invalid
|
||||
if (index < 0 || index >= FileCount)
|
||||
return false;
|
||||
|
||||
// Get the file
|
||||
var file = Files[index];
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = file.Name;
|
||||
if (filename == null)
|
||||
return false;
|
||||
|
||||
// Get the directory index
|
||||
int dirIndex = FileDirMap[index];
|
||||
if (dirIndex < 0 || dirIndex > DirCount)
|
||||
return false;
|
||||
|
||||
// Get the directory name
|
||||
var dirName = Directories[dirIndex].Name;
|
||||
if (dirName != null)
|
||||
filename = Path.Combine(dirName, filename);
|
||||
|
||||
// Get and adjust the file offset
|
||||
long fileOffset = file.Offset + DataStart;
|
||||
if (fileOffset < 0 || fileOffset >= Length)
|
||||
return false;
|
||||
|
||||
// Get the file sizes
|
||||
long fileSize = file.CompressedSize;
|
||||
long outputFileSize = file.UncompressedSize;
|
||||
|
||||
// Read the compressed data directly
|
||||
var compressedData = ReadFromDataSource((int)fileOffset, (int)fileSize);
|
||||
if (compressedData == null)
|
||||
return false;
|
||||
|
||||
// If the compressed and uncompressed sizes match
|
||||
byte[] data;
|
||||
if (fileSize == outputFileSize)
|
||||
{
|
||||
data = compressedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decompress the data
|
||||
var decomp = Decompressor.Create();
|
||||
var outData = new MemoryStream();
|
||||
decomp.CopyTo(compressedData, outData);
|
||||
data = outData.ToArray();
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
System.IO.Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = System.IO.File.OpenWrite(filename);
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
139
SabreTools.Serialization/Wrappers/LZKWAJ.cs
Normal file
139
SabreTools.Serialization/Wrappers/LZKWAJ.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System.IO;
|
||||
using SabreTools.Compression.SZDD;
|
||||
using SabreTools.Models.LZ;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class LZKWAJ : WrapperBase<KWAJFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "LZ-compressed file, KWAJ variant";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZKWAJ(KWAJFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZKWAJ(KWAJFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an LZ (KWAJ variant) from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the LZ (KWAJ variant)</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>An LZ (KWAJ variant) wrapper on success, null on failure</returns>
|
||||
public static LZKWAJ? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a LZ (KWAJ variant) from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the LZ (KWAJ variant)</param>
|
||||
/// <returns>An LZ (KWAJ variant) wrapper on success, null on failure</returns>
|
||||
public static LZKWAJ? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var file = Deserializers.LZKWAJ.DeserializeStream(data);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
return new LZKWAJ(file, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract the contents to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the contents extracted, false otherwise</returns>
|
||||
public bool Extract(string outputDirectory)
|
||||
{
|
||||
// Get the length of the compressed data
|
||||
long compressedSize = Length - Model.Header!.DataOffset;
|
||||
if (compressedSize < Model.Header.DataOffset)
|
||||
return false;
|
||||
|
||||
// Read in the data as an array
|
||||
byte[]? contents = ReadFromDataSource(Model.Header.DataOffset, (int)compressedSize);
|
||||
if (contents == null)
|
||||
return false;
|
||||
|
||||
// Get the decompressor
|
||||
var decompressor = Decompressor.CreateKWAJ(contents, Model.Header!.CompressionType);
|
||||
if (decompressor == null)
|
||||
return false;
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
string filename = "tempfile";
|
||||
if (Model.HeaderExtensions?.FileName != null)
|
||||
filename = Model.HeaderExtensions.FileName;
|
||||
if (Model.HeaderExtensions?.FileExtension != null)
|
||||
filename += $".{Model.HeaderExtensions.FileExtension}";
|
||||
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = File.OpenWrite(filename);
|
||||
decompressor.CopyTo(fs);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
134
SabreTools.Serialization/Wrappers/LZQBasic.cs
Normal file
134
SabreTools.Serialization/Wrappers/LZQBasic.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System.IO;
|
||||
using SabreTools.Compression.SZDD;
|
||||
using SabreTools.Models.LZ;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class LZQBasic : WrapperBase<QBasicFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "LZ-compressed file, QBasic variant";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZQBasic(QBasicFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZQBasic(QBasicFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an LZ (QBasic variant) from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the LZ (QBasic variant)</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>An LZ (QBasic variant) wrapper on success, null on failure</returns>
|
||||
public static LZQBasic? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a LZ (QBasic variant) from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the LZ (QBasic variant)</param>
|
||||
/// <returns>An LZ (QBasic variant) wrapper on success, null on failure</returns>
|
||||
public static LZQBasic? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var file = Deserializers.LZQBasic.DeserializeStream(data);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
return new LZQBasic(file, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract the contents to an output directory
|
||||
/// </summary>
|
||||
/// <param name="filename">Original filename to use as a base</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the contents extracted, false otherwise</returns>
|
||||
public bool Extract(string outputDirectory)
|
||||
{
|
||||
// Get the length of the compressed data
|
||||
long compressedSize = Length - 12;
|
||||
if (compressedSize < 12)
|
||||
return false;
|
||||
|
||||
// Read in the data as an array
|
||||
byte[]? contents = ReadFromDataSource(12, (int)compressedSize);
|
||||
if (contents == null)
|
||||
return false;
|
||||
|
||||
// Get the decompressor
|
||||
var decompressor = Decompressor.CreateQBasic(contents);
|
||||
if (decompressor == null)
|
||||
return false;
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
string filename = Path.Combine(outputDirectory, "tempfile.bin");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = File.OpenWrite(filename);
|
||||
decompressor.CopyTo(fs);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
166
SabreTools.Serialization/Wrappers/LZSZDD.cs
Normal file
166
SabreTools.Serialization/Wrappers/LZSZDD.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System.IO;
|
||||
using SabreTools.Compression.SZDD;
|
||||
using SabreTools.Models.LZ;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class LZSZDD : WrapperBase<SZDDFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "LZ-compressed file, SZDD variant";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZSZDD(SZDDFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LZSZDD(SZDDFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an LZ (SZDD variant) from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the LZ (SZDD variant)</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>An LZ (SZDD variant) wrapper on success, null on failure</returns>
|
||||
public static LZSZDD? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a LZ (SZDD variant) from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the LZ (SZDD variant)</param>
|
||||
/// <returns>An LZ (SZDD variant) wrapper on success, null on failure</returns>
|
||||
public static LZSZDD? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var file = Deserializers.LZSZDD.DeserializeStream(data);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
return new LZSZDD(file, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract the contents to an output directory
|
||||
/// </summary>
|
||||
/// <param name="filename">Original filename to use as a base</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the contents extracted, false otherwise</returns>
|
||||
public bool Extract(string filename, string outputDirectory)
|
||||
{
|
||||
// Get the length of the compressed data
|
||||
long compressedSize = Length - 14;
|
||||
if (compressedSize < 14)
|
||||
return false;
|
||||
|
||||
// Read in the data as an array
|
||||
byte[]? contents = ReadFromDataSource(14, (int)compressedSize);
|
||||
if (contents == null)
|
||||
return false;
|
||||
|
||||
// Get the decompressor
|
||||
var decompressor = Decompressor.CreateSZDD(contents);
|
||||
if (decompressor == null)
|
||||
return false;
|
||||
|
||||
// Create the output file
|
||||
filename = GetExpandedName(filename).TrimEnd('\0');
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = File.OpenWrite(filename);
|
||||
decompressor.CopyTo(fs);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full name of the input file
|
||||
/// </summary>
|
||||
private string GetExpandedName(string input)
|
||||
{
|
||||
// If the extension is missing
|
||||
string extension = Path.GetExtension(input).TrimStart('.');
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
return Path.GetFileNameWithoutExtension(input);
|
||||
|
||||
// If the extension is a single character
|
||||
if (extension.Length == 1)
|
||||
{
|
||||
if (extension == "_" || extension == "$")
|
||||
return $"{Path.GetFileNameWithoutExtension(input)}.{char.ToLower(Model.Header!.LastChar)}";
|
||||
|
||||
return Path.GetFileNameWithoutExtension(input);
|
||||
}
|
||||
|
||||
// If the extension isn't formatted
|
||||
if (!extension.EndsWith("_"))
|
||||
return Path.GetFileNameWithoutExtension(input);
|
||||
|
||||
// Handle replacing characters
|
||||
char c = (char.IsUpper(input[0]) ? char.ToLower(Model.Header!.LastChar) : char.ToUpper(Model.Header!.LastChar));
|
||||
string text2 = extension.Substring(0, extension.Length - 1) + c;
|
||||
return Path.GetFileNameWithoutExtension(input) + "." + text2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -376,8 +376,11 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return _overlayStrings;
|
||||
}
|
||||
|
||||
// TODO: Revisit the 16 MiB limit
|
||||
// Cap the check for overlay strings to 16 MiB (arbitrary)
|
||||
int overlayLength = Math.Min(endOfFile - endOfSectionData, 16 * 1024 * 1024);
|
||||
|
||||
// Otherwise, cache and return the strings
|
||||
int overlayLength = endOfFile - endOfSectionData;
|
||||
_overlayStrings = ReadStringsFromDataSource(endOfSectionData, overlayLength, charLimit: 3);
|
||||
return _overlayStrings;
|
||||
}
|
||||
@@ -746,6 +749,11 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// </summary>
|
||||
private Models.PortableExecutable.VersionInfo? _versionInfo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Cached version info strings data
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, string?> _versionInfoStrings = [];
|
||||
|
||||
/// <summary>
|
||||
/// Cached assembly manifest data
|
||||
/// </summary>
|
||||
@@ -842,6 +850,10 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return null;
|
||||
|
||||
// If we have the value cached
|
||||
if (_versionInfoStrings.ContainsKey(key))
|
||||
return _versionInfoStrings[key];
|
||||
|
||||
// Ensure that we have the resource data cached
|
||||
if (ResourceData == null)
|
||||
return null;
|
||||
@@ -862,17 +874,22 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// Return the match if found
|
||||
match = Array.Find(st.Children, sd => sd != null && key.Equals(sd.Key, StringComparison.OrdinalIgnoreCase));
|
||||
if (match != null)
|
||||
return match.Value?.TrimEnd('\0');
|
||||
{
|
||||
_versionInfoStrings[key] = match.Value?.TrimEnd('\0');
|
||||
return _versionInfoStrings[key];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
_versionInfoStrings[key] = null;
|
||||
return _versionInfoStrings[key];
|
||||
#else
|
||||
var match = stringTable
|
||||
.SelectMany(st => st?.Children ?? [])
|
||||
.FirstOrDefault(sd => sd != null && key.Equals(sd.Key, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Return either the match or null
|
||||
return match?.Value?.TrimEnd('\0');
|
||||
_versionInfoStrings[key] = match?.Value?.TrimEnd('\0');
|
||||
return _versionInfoStrings[key];
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1028,7 +1045,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
// Read the signature
|
||||
int offset = 0;
|
||||
uint signature = entryData.ReadUInt32(ref offset);
|
||||
uint signature = entryData.ReadUInt32LittleEndian(ref offset);
|
||||
|
||||
// Reset the offset
|
||||
offset = 0;
|
||||
@@ -1036,7 +1053,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// NB10
|
||||
if (signature == 0x3031424E)
|
||||
{
|
||||
var nb10ProgramDatabase = entryData.AsNB10ProgramDatabase(ref offset);
|
||||
var nb10ProgramDatabase = entryData.ParseNB10ProgramDatabase(ref offset);
|
||||
if (nb10ProgramDatabase != null)
|
||||
{
|
||||
_debugData[i] = nb10ProgramDatabase;
|
||||
@@ -1047,7 +1064,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// RSDS
|
||||
else if (signature == 0x53445352)
|
||||
{
|
||||
var rsdsProgramDatabase = entryData.AsRSDSProgramDatabase(ref offset);
|
||||
var rsdsProgramDatabase = entryData.ParseRSDSProgramDatabase(ref offset);
|
||||
if (rsdsProgramDatabase != null)
|
||||
{
|
||||
_debugData[i] = rsdsProgramDatabase;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.IO;
|
||||
using SabreTools.Models.Quantum;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class Quantum : WrapperBase<Models.Quantum.Archive>
|
||||
public class Quantum : WrapperBase<Archive>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
@@ -14,14 +15,14 @@ namespace SabreTools.Serialization.Wrappers
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Quantum(Models.Quantum.Archive? model, byte[]? data, int offset)
|
||||
public Quantum(Archive? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Quantum(Models.Quantum.Archive? model, Stream? data)
|
||||
public Quantum(Archive? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
@@ -74,5 +75,130 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="Archive.CompressedDataOffset"/>
|
||||
public long CompressedDataOffset => Model.CompressedDataOffset;
|
||||
|
||||
/// <inheritdoc cref="Header.FileCount"/>
|
||||
public ushort FileCount => Model.Header?.FileCount ?? 0;
|
||||
|
||||
/// <inheritdoc cref="Archive.FileList"/>
|
||||
public FileDescriptor[] FileList => Model.FileList ?? [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the Quantum archive to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public bool ExtractAll(string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (FileList == null || FileList.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < FileList.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the Quantum archive to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public bool ExtractFile(int index, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (Model.Header == null || FileCount == 0 || FileList == null || FileList.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (index < 0 || index >= FileList.Length)
|
||||
return false;
|
||||
|
||||
// Get the file information
|
||||
var fileDescriptor = FileList[index];
|
||||
|
||||
// Read the entire compressed data
|
||||
int compressedDataOffset = (int)CompressedDataOffset;
|
||||
int compressedDataLength = GetEndOfFile() - compressedDataOffset;
|
||||
var compressedData = ReadFromDataSource(compressedDataOffset, compressedDataLength);
|
||||
|
||||
// TODO: Figure out decompression
|
||||
// - Single-file archives seem to work
|
||||
// - Single-file archives with files that span a window boundary seem to work
|
||||
// - The first files in each archive seem to work
|
||||
return false;
|
||||
|
||||
// // Setup the decompression state
|
||||
// State state = new State();
|
||||
// Decompressor.InitState(state, TableSize, CompressionFlags);
|
||||
|
||||
// // Decompress the entire array
|
||||
// int decompressedDataLength = (int)FileList.Sum(fd => fd.ExpandedFileSize);
|
||||
// byte[] decompressedData = new byte[decompressedDataLength];
|
||||
// Decompressor.Decompress(state, compressedData.Length, compressedData, decompressedData.Length, decompressedData);
|
||||
|
||||
// // Read the data
|
||||
// int offset = (int)FileList.Take(index).Sum(fd => fd.ExpandedFileSize);
|
||||
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
|
||||
// Array.Copy(decompressedData, offset, data, 0, data.Length);
|
||||
|
||||
// // Loop through all files before the current
|
||||
// for (int i = 0; i < index; i++)
|
||||
// {
|
||||
// // Decompress the next block of data
|
||||
// byte[] tempData = new byte[FileList[i].ExpandedFileSize];
|
||||
// int lastRead = Decompressor.Decompress(state, compressedData.Length, compressedData, tempData.Length, tempData);
|
||||
// compressedData = new ReadOnlySpan<byte>(compressedData, (lastRead), compressedData.Length - (lastRead)).ToArray();
|
||||
// }
|
||||
|
||||
// // Read the data
|
||||
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
|
||||
// _ = Decompressor.Decompress(state, compressedData.Length, compressedData, data.Length, data);
|
||||
|
||||
// // Create the filename
|
||||
// string filename = fileDescriptor.FileName;
|
||||
|
||||
// // If we have an invalid output directory
|
||||
// if (string.IsNullOrEmpty(outputDirectory))
|
||||
// return false;
|
||||
|
||||
// // Create the full output path
|
||||
// filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// // Ensure the output directory is created
|
||||
// Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
|
||||
// // Try to write the data
|
||||
// try
|
||||
// {
|
||||
// // Open the output file for writing
|
||||
// using (Stream fs = File.OpenWrite(filename))
|
||||
// {
|
||||
// fs.Write(data, 0, data.Length);
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Compression.zlib;
|
||||
using SabreTools.Models.SGA;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
@@ -120,6 +122,148 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the SGA to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public bool ExtractAll(string outputDirectory)
|
||||
{
|
||||
// Get the file count
|
||||
int fileCount = FileCount;
|
||||
if (fileCount == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the SGA to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public bool ExtractFile(int index, string outputDirectory)
|
||||
{
|
||||
// Get the file count
|
||||
int fileCount = FileCount;
|
||||
if (fileCount == 0)
|
||||
return false;
|
||||
|
||||
// If the files index is invalid
|
||||
if (index < 0 || index >= fileCount)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = GetFileName(index);
|
||||
if (filename == null)
|
||||
return false;
|
||||
|
||||
// Loop through and get all parent directories
|
||||
var parentNames = new List<string> { filename };
|
||||
|
||||
// Get the parent directory
|
||||
string? folderName = GetParentName(index);
|
||||
if (folderName != null)
|
||||
parentNames.Add(folderName);
|
||||
|
||||
// TODO: Should the section name/alias be used in the path as well?
|
||||
|
||||
// Reverse and assemble the filename
|
||||
parentNames.Reverse();
|
||||
#if NET20 || NET35
|
||||
filename = parentNames[0];
|
||||
for (int i = 1; i < parentNames.Count; i++)
|
||||
{
|
||||
filename = Path.Combine(filename, parentNames[i]);
|
||||
}
|
||||
#else
|
||||
filename = Path.Combine([.. parentNames]);
|
||||
#endif
|
||||
|
||||
// Get and adjust the file offset
|
||||
long fileOffset = GetFileOffset(index);
|
||||
fileOffset += FileDataOffset;
|
||||
if (fileOffset < 0)
|
||||
return false;
|
||||
|
||||
// Get the file sizes
|
||||
long fileSize = GetCompressedSize(index);
|
||||
long outputFileSize = GetUncompressedSize(index);
|
||||
|
||||
// Read the compressed data directly
|
||||
var compressedData = ReadFromDataSource((int)fileOffset, (int)fileSize);
|
||||
if (compressedData == null)
|
||||
return false;
|
||||
|
||||
// If the compressed and uncompressed sizes match
|
||||
byte[] data;
|
||||
if (fileSize == outputFileSize)
|
||||
{
|
||||
data = compressedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inflate the data into the buffer
|
||||
var zstream = new ZLib.z_stream_s();
|
||||
data = new byte[outputFileSize];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* payloadPtr = compressedData)
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
zstream.next_in = payloadPtr;
|
||||
zstream.avail_in = (uint)compressedData.Length;
|
||||
zstream.total_in = (uint)compressedData.Length;
|
||||
zstream.next_out = dataPtr;
|
||||
zstream.avail_out = (uint)data.Length;
|
||||
zstream.total_out = 0;
|
||||
|
||||
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), compressedData.Length);
|
||||
int zret = ZLib.inflate(zstream, 1);
|
||||
ZLib.inflateEnd(zstream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
System.IO.Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = System.IO.File.OpenWrite(filename);
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -45,6 +45,24 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// </summary>
|
||||
public T Model { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the underlying data
|
||||
/// </summary>
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dataSource switch
|
||||
{
|
||||
DataSource.ByteArray => _byteArrayData!.Length - _byteArrayOffset,
|
||||
DataSource.Stream => _streamData!.Length,
|
||||
|
||||
// Everything else is invalid
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instance Variables
|
||||
|
||||
@@ -28,11 +28,13 @@ namespace SabreTools.Serialization.Wrappers
|
||||
WrapperType.GCF => GCF.Create(data),
|
||||
WrapperType.GZIP => null,// TODO: Implement wrapper
|
||||
WrapperType.IniFile => null,// TODO: Implement wrapper
|
||||
WrapperType.InstallShieldArchiveV3 => null,// TODO: Implement wrapper
|
||||
WrapperType.InstallShieldArchiveV3 => InstallShieldArchiveV3.Create(data),
|
||||
WrapperType.InstallShieldCAB => InstallShieldCabinet.Create(data),
|
||||
WrapperType.LDSCRYPT => null,// TODO: Implement wrapper
|
||||
WrapperType.LZKWAJ => LZKWAJ.Create(data),
|
||||
WrapperType.LZQBasic => LZQBasic.Create(data),
|
||||
WrapperType.LZSZDD => LZSZDD.Create(data),
|
||||
WrapperType.MicrosoftCAB => MicrosoftCabinet.Create(data),
|
||||
WrapperType.MicrosoftLZ => null,// TODO: Implement wrapper
|
||||
WrapperType.MoPaQ => MoPaQ.Create(data),
|
||||
WrapperType.N3DS => N3DS.Create(data),
|
||||
WrapperType.NCF => NCF.Create(data),
|
||||
@@ -330,6 +332,19 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#endregion
|
||||
|
||||
#region LZ
|
||||
|
||||
if (magic.StartsWith([0x4B, 0x57, 0x41, 0x4A, 0x88, 0xF0, 0x27, 0xD1]))
|
||||
return WrapperType.LZKWAJ;
|
||||
|
||||
if (magic.StartsWith([0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1]))
|
||||
return WrapperType.LZQBasic;
|
||||
|
||||
if (magic.StartsWith([0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33]))
|
||||
return WrapperType.LZSZDD;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MicrosoftCAB
|
||||
|
||||
if (magic.StartsWith([0x4d, 0x53, 0x43, 0x46]))
|
||||
@@ -339,13 +354,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#endregion
|
||||
|
||||
#region MicrosoftLZ
|
||||
|
||||
if (magic.StartsWith([0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33]))
|
||||
return WrapperType.MicrosoftLZ;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MoPaQ
|
||||
|
||||
if (magic.StartsWith([0x4d, 0x50, 0x51, 0x1a]))
|
||||
|
||||
@@ -77,7 +77,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// InstallShield archive v3
|
||||
/// </summary>
|
||||
/// <remarks>Currently has no IWrapper implementation</remarks>
|
||||
InstallShieldArchiveV3,
|
||||
|
||||
/// <summary>
|
||||
@@ -91,17 +90,26 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <remarks>Currently has no IWrapper implementation</remarks>
|
||||
LDSCRYPT,
|
||||
|
||||
/// <summary>
|
||||
/// LZ-compressed file, KWAJ variant
|
||||
/// </summary>
|
||||
LZKWAJ,
|
||||
|
||||
/// <summary>
|
||||
/// LZ-compressed file, QBasic variant
|
||||
/// </summary>
|
||||
LZQBasic,
|
||||
|
||||
/// <summary>
|
||||
/// LZ-compressed file, SZDD variant
|
||||
/// </summary>
|
||||
LZSZDD,
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft cabinet file
|
||||
/// </summary>
|
||||
MicrosoftCAB,
|
||||
|
||||
/// <summary>
|
||||
/// Microsoft LZ-compressed file
|
||||
/// </summary>
|
||||
/// <remarks>Currently has no IWrapper implementation</remarks>
|
||||
MicrosoftLZ,
|
||||
|
||||
/// <summary>
|
||||
/// MPQ game data archive
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user