Compare commits

..

27 Commits
1.5.6 ... 1.6.2

Author SHA1 Message Date
Matt Nadareski
3f7b71e9a5 Bump version 2024-05-06 22:23:45 -04:00
Matt Nadareski
95baaf8603 Update SabreTools.IO 2024-05-06 22:12:14 -04:00
Matt Nadareski
3673264bab Bump version 2024-04-28 19:37:10 -04:00
Matt Nadareski
64fb5a6b63 Update SabreTools.IO 2024-04-28 19:32:06 -04:00
Matt Nadareski
e9c959ccdb Update SabreTools.IO 2024-04-28 17:39:30 -04:00
Matt Nadareski
4b7487e92e More rudimentary ZIP64 fixes 2024-04-28 00:24:35 -04:00
Matt Nadareski
52dbcffd8e Add shortcut if any other valid PKZIP blocks found 2024-04-27 23:57:32 -04:00
Matt Nadareski
24ae354bc2 Fix an indicator for ZIP64 2024-04-27 23:50:03 -04:00
Matt Nadareski
b30b91fd91 Remove redunant fix in StringBuilderExtensions 2024-04-27 23:48:55 -04:00
Matt Nadareski
efb63afc74 Fix PKZIP data printing 2024-04-27 23:45:33 -04:00
Matt Nadareski
16706f7169 Force writing values with proper width 2024-04-27 23:42:37 -04:00
Matt Nadareski
d7c32676b5 Add PKZIP printer implementation 2024-04-27 23:40:02 -04:00
Matt Nadareski
c8c45446bc Add PKZIP archive extra data record parsing 2024-04-27 23:01:50 -04:00
Matt Nadareski
f4de2e27d7 Notes cleanup 2024-04-27 22:49:09 -04:00
Matt Nadareski
970fcbd93b Add PKZIP shell wrapper 2024-04-27 22:45:49 -04:00
Matt Nadareski
57d1cd7f1e Initial code for PKZIP deserialization 2024-04-27 22:41:22 -04:00
Matt Nadareski
522fc372fa Fix instance of wrong extension 2024-04-27 22:23:33 -04:00
Matt Nadareski
7141690fcb Add override for compression handling 2024-04-27 22:04:52 -04:00
Matt Nadareski
c7d9177e68 Allow decompression to be skipped 2024-04-27 22:04:38 -04:00
Matt Nadareski
00b3ea40d9 Bump version 2024-04-26 21:33:56 -04:00
Matt Nadareski
c9afe939dc Update packages 2024-04-26 21:32:55 -04:00
Matt Nadareski
4171ae6516 Update build scripts, action 2024-04-26 21:27:58 -04:00
Matt Nadareski
35a42c49d5 Uncomment large amount of items but return null explicitly 2024-04-24 19:18:38 -04:00
Matt Nadareski
e4dbf56b49 Integrate test program data into main library 2024-04-24 19:15:28 -04:00
Matt Nadareski
76eeb10c47 Update README 2024-04-24 19:04:05 -04:00
Matt Nadareski
9fa73ad54f Rename PrintExtensions to Printer 2024-04-24 19:04:01 -04:00
Matt Nadareski
6034a4fd06 Port Printing into Serialization 2024-04-24 18:53:10 -04:00
50 changed files with 12032 additions and 15 deletions

53
.github/workflows/build_test.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Build Test
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [Test]
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Release, Debug]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -6,7 +6,7 @@ Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTo
## Interfaces
Below is a table representing the various interfaces that are implemented within this library.
Below is a table representing the various conversion interfaces that are implemented within this library.
| Interface Name | Source Type | Destination Type |
| --- | --- | --- |
@@ -19,7 +19,13 @@ Below is a table representing the various interfaces that are implemented within
| `IStreamSerializer` | Model | `Stream?` |
| `IStringDeserializer` | `string?` representation | Model |
| `IStringSerializer` | Model | `string?` representation |
| `IWrapper` | N/A | N/A |
Below is a table representing the various non-conversion interfaces that are implemented within this library.
| Interface Name | Purpose |
| --- | --- |
| `IPrinter` | Provides a formatted output for a model |
| `IWrapper` / `IWrapper<T>` | Wraps a model or set of models to provide additional functionality |
## Namespaces
@@ -29,5 +35,6 @@ Below is a table of all namespaces within the library and what they represent
| --- | --- |
| `SabreTools.Serialization.CrossModel` | Convert between models; mainly used for metadata files converting to and from a common, `Dictionary`-based model |
| `SabreTools.Serialization.Deserializers` | Convert from external sources to models |
| `SabreTools.Serialization.Printers` | Export model information in a formatted manner |
| `SabreTools.Serialization.Serializers` | Convert from models to external sources |
| `SabreTools.Serialization.Wrappers` | Classes that wrap serialization and models to allow for including extension properties |

View File

@@ -28,8 +28,8 @@
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="SabreTools.Models" Version="1.4.5" />
<PackageReference Include="xunit" Version="2.7.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@@ -17,6 +17,11 @@ namespace SabreTools.Serialization.Deserializers
IFileDeserializer<TModel>,
IStreamDeserializer<TModel>
{
/// <summary>
/// Indicates if compressed files should be decompressed before processing
/// </summary>
protected virtual bool SkipCompression => false;
#region IByteDeserializer
/// <inheritdoc/>
@@ -42,7 +47,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public virtual TModel? Deserialize(string? path)
{
using var stream = PathProcessor.OpenStream(path);
using var stream = PathProcessor.OpenStream(path, SkipCompression);
return DeserializeStream(stream);
}

View File

@@ -0,0 +1,697 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.PKZIP;
using static SabreTools.Models.PKZIP.Constants;
namespace SabreTools.Serialization.Deserializers
{
public class PKZIP : BaseBinaryDeserializer<Archive>
{
/// <inheritdoc/>
protected override bool SkipCompression => true;
/// <inheritdoc/>
public override Archive? Deserialize(Stream? data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
var archive = new Archive();
#region End of Central Directory Record
// Find the end of central directory record
long eocdrOffset = SearchForEndOfCentralDirectoryRecord(data);
if (eocdrOffset < 0 || eocdrOffset >= data.Length)
return null;
// Seek to the end of central directory record
data.Seek(eocdrOffset, SeekOrigin.Begin);
// Read the end of central directory record
var eocdr = ParseEndOfCentralDirectoryRecord(data);
if (eocdr == null)
return null;
// Assign the end of central directory record
archive.EndOfCentralDirectoryRecord = eocdr;
#endregion
#region ZIP64 End of Central Directory Locator and Record
// Set a flag for ZIP64 not found by default
bool zip64 = false;
// Process ZIP64 if any fields are set to max value
if (eocdr.DiskNumber == 0xFFFF
|| eocdr.StartDiskNumber == 0xFFFF
|| eocdr.TotalEntriesOnDisk == 0xFFFF
|| eocdr.TotalEntries == 0xFFFF
|| eocdr.CentralDirectorySize == 0xFFFFFFFF
|| eocdr.CentralDirectoryOffset == 0xFFFFFFFF)
{
// Set the ZIP64 flag
zip64 = true;
// Find the ZIP64 end of central directory locator
long eocdlOffset = SearchForZIP64EndOfCentralDirectoryLocator(data);
if (eocdlOffset < 0 || eocdlOffset >= data.Length)
return null;
// Seek to the ZIP64 end of central directory locator
data.Seek(eocdlOffset, SeekOrigin.Begin);
// Read the ZIP64 end of central directory locator
var eocdl64 = ParseEndOfCentralDirectoryLocator64(data);
if (eocdl64 == null)
return null;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryLocator = eocdl64;
// Try to get the ZIP64 end of central directory record offset
if ((long)eocdl64.CentralDirectoryOffset < 0 || (long)eocdl64.CentralDirectoryOffset >= data.Length)
return null;
// Seek to the ZIP64 end of central directory record
data.Seek((long)eocdl64.CentralDirectoryOffset, SeekOrigin.Begin);
// Read the ZIP64 end of central directory record
var eocdr64 = ParseEndOfCentralDirectoryRecord64(data);
if (eocdr64 == null)
return null;
// Assign the ZIP64 end of central directory record
archive.ZIP64EndOfCentralDirectoryRecord = eocdr64;
}
#endregion
#region Central Directory Records
// Try to get the central directory record offset
long cdrOffset, cdrSize;
if (zip64 && archive.ZIP64EndOfCentralDirectoryRecord != null)
{
cdrOffset = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else if (archive.EndOfCentralDirectoryRecord != null)
{
cdrOffset = archive.EndOfCentralDirectoryRecord.CentralDirectoryOffset;
cdrSize = archive.EndOfCentralDirectoryRecord.CentralDirectorySize;
}
else
{
return null;
}
// Try to get the central directory record offset
if (cdrOffset < 0 || cdrOffset >= data.Length)
return null;
// Seek to the first central directory record
data.Seek(cdrOffset, SeekOrigin.Begin);
// Cache the current offset
long currentOffset = data.Position;
// Read the central directory records
var cdrs = new List<CentralDirectoryFileHeader>();
while (data.Position < currentOffset + cdrSize)
{
// Read the central directory record
var cdr = ParseCentralDirectoryFileHeader(data);
if (cdr == null)
return null;
// Add the central directory record
cdrs.Add(cdr);
}
// Assign the central directory records
archive.CentralDirectoryHeaders = [.. cdrs];
#endregion
// TODO: Handle digital signature -- immediately following central directory records
#region Archive Extra Data Record
// Find the archive extra data record
long aedrOffset = SearchForArchiveExtraDataRecord(data, cdrOffset);
if (aedrOffset >= 0 && aedrOffset < data.Length)
{
// Seek to the archive extra data record
data.Seek(aedrOffset, SeekOrigin.Begin);
// Read the archive extra data record
var aedr = ParseArchiveExtraDataRecord(data);
if (aedr == null)
return null;
// Assign the archive extra data record
archive.ArchiveExtraDataRecord = aedr;
}
#endregion
#region Local File
// Setup all of the collections
var localFileHeaders = new List<LocalFileHeader?>();
var encryptionHeaders = new List<byte[]?>();
var fileData = new List<byte[]>(); // TODO: Should this data be read here?
var dataDescriptors = new List<DataDescriptor?>();
var zip64DataDescriptors = new List<DataDescriptor64?>();
// Read the local file headers
for (int i = 0; i < archive.CentralDirectoryHeaders.Length; i++)
{
var header = archive.CentralDirectoryHeaders[i];
// Get the local file header offset
long headerOffset = header.RelativeOffsetOfLocalHeader;
if (headerOffset == 0xFFFFFFFF && header.ExtraField != null)
{
// TODO: Parse into a proper structure instead of this
byte[] extraData = header.ExtraField;
if (BitConverter.ToUInt16(extraData, 0) == 0x0001)
headerOffset = BitConverter.ToInt64(extraData, 4);
}
if (headerOffset < 0 || headerOffset >= data.Length)
return null;
// Seek to the local file header
data.Seek(headerOffset, SeekOrigin.Begin);
// Try to parse the local header
var localFileHeader = ParseLocalFileHeader(data);
if (localFileHeader == null)
{
// Add a placeholder null item
localFileHeaders.Add(null);
encryptionHeaders.Add(null);
fileData.Add([]);
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
continue;
}
// Add the local file header
localFileHeaders.Add(localFileHeader);
// Only read the encryption header if necessary
#if NET20 || NET35
if ((header.Flags & GeneralPurposeBitFlags.FileEncrypted) != 0)
#else
if (header.Flags.HasFlag(GeneralPurposeBitFlags.FileEncrypted))
#endif
{
// Try to read the encryption header data -- TODO: Verify amount to read
byte[] encryptionHeader = data.ReadBytes(12);
if (encryptionHeader.Length != 12)
return null;
// Add the encryption header
encryptionHeaders.Add(encryptionHeader);
}
else
{
// Add the null encryption header
encryptionHeaders.Add(null);
}
// Try to read the file data
byte[] fileDatum = data.ReadBytes((int)header.CompressedSize);
if (fileDatum.Length < header.CompressedSize)
return null;
// Add the file data
fileData.Add(fileDatum);
// Only read the data descriptor if necessary
#if NET20 || NET35
if ((header.Flags & GeneralPurposeBitFlags.NoCRC) != 0)
#else
if (header.Flags.HasFlag(GeneralPurposeBitFlags.NoCRC))
#endif
{
// Select the data descriptor that is being used
if (zip64)
{
// Try to parse the data descriptor
var dataDescriptor64 = ParseDataDescriptor64(data);
if (dataDescriptor64 == null)
return null;
// Add the data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(dataDescriptor64);
}
else
{
// Try to parse the data descriptor
var dataDescriptor = ParseDataDescriptor(data);
if (dataDescriptor == null)
return null;
// Add the data descriptor
dataDescriptors.Add(dataDescriptor);
zip64DataDescriptors.Add(null);
}
}
else
{
// Add the null data descriptor
dataDescriptors.Add(null);
zip64DataDescriptors.Add(null);
}
}
// Assign the local file headers
archive.LocalFileHeaders = [.. localFileHeaders];
// Assign the encryption headers
archive.EncryptionHeaders = [.. encryptionHeaders];
// Assign the file data
archive.FileData = [.. fileData];
// Assign the data descriptors
archive.DataDescriptors = [.. dataDescriptors];
archive.ZIP64DataDescriptors = [.. zip64DataDescriptors];
#endregion
// TODO: Handle archive decryption header
return archive;
}
/// <summary>
/// Search for the end of central directory record
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Position of the end of central directory record, -1 on error</returns>
public static long SearchForEndOfCentralDirectoryRecord(Stream data)
{
// Cache the current offset
long current = data.Position;
// Seek to the minimum size of the record from the end
data.Seek(-22, SeekOrigin.End);
// Attempt to find the end of central directory signature
while (data.Position > 0)
{
// Read the potential signature
uint possibleSignature = data.ReadUInt32();
if (possibleSignature == EndOfCentralDirectoryRecordSignature)
{
long signaturePosition = data.Position - 4;
data.Seek(current, SeekOrigin.Begin);
return signaturePosition;
}
// If we find any other signature
switch (possibleSignature)
{
case ArchiveExtraDataRecordSignature:
case CentralDirectoryFileHeaderSignature:
case DataDescriptorSignature:
case DigitalSignatureSignature:
case EndOfCentralDirectoryLocator64Signature:
case EndOfCentralDirectoryRecord64Signature:
case LocalFileHeaderSignature:
data.Seek(current, SeekOrigin.Begin);
return -1;
}
// Seek backward 5 bytes, if possible
data.Seek(-5, SeekOrigin.Current);
}
// No signature was found
data.Seek(current, SeekOrigin.Begin);
return -1;
}
/// <summary>
/// Parse a Stream into an end of central directory record
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled end of central directory record on success, null on error</returns>
public static EndOfCentralDirectoryRecord? ParseEndOfCentralDirectoryRecord(Stream data)
{
// TODO: Use marshalling here instead of building
var record = new EndOfCentralDirectoryRecord();
record.Signature = data.ReadUInt32();
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();
if (record.FileCommentLength > 0)
{
byte[] commentBytes = data.ReadBytes(record.FileCommentLength);
if (commentBytes.Length != record.FileCommentLength)
return null;
record.FileComment = Encoding.ASCII.GetString(commentBytes);
}
return record;
}
/// <summary>
/// Search for the ZIP64 end of central directory locator
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Position of the ZIP64 end of central directory locator, -1 on error</returns>
public static long SearchForZIP64EndOfCentralDirectoryLocator(Stream data)
{
// Cache the current offset
long current = data.Position;
// Seek to the minimum size of the record from the minimum start
// of theend of central directory record
data.Seek(-22 + -20, SeekOrigin.Current);
// Attempt to find the ZIP64 end of central directory locator signature
while (data.Position > 0)
{
// Read the potential signature
uint possibleSignature = data.ReadUInt32();
if (possibleSignature == EndOfCentralDirectoryLocator64Signature)
{
long signaturePosition = data.Position - 4;
data.Seek(current, SeekOrigin.Begin);
return signaturePosition;
}
// If we find any other signature
switch (possibleSignature)
{
case ArchiveExtraDataRecordSignature:
case CentralDirectoryFileHeaderSignature:
case DataDescriptorSignature:
case DigitalSignatureSignature:
case EndOfCentralDirectoryRecordSignature:
case EndOfCentralDirectoryRecord64Signature:
case LocalFileHeaderSignature:
data.Seek(current, SeekOrigin.Begin);
return -1;
}
// Seek backward 5 bytes, if possible
data.Seek(-5, SeekOrigin.Current);
}
// No signature was found
data.Seek(current, SeekOrigin.Begin);
return -1;
}
/// <summary>
/// Parse a Stream into a ZIP64 end of central directory locator
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ZIP64 end of central directory locator on success, null on error</returns>
public static EndOfCentralDirectoryLocator64? ParseEndOfCentralDirectoryLocator64(Stream data)
{
return data.ReadType<EndOfCentralDirectoryLocator64>();
}
/// <summary>
/// Parse a Stream into a ZIP64 end of central directory record
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ZIP64 end of central directory record on success, null on error</returns>
public static EndOfCentralDirectoryRecord64? ParseEndOfCentralDirectoryRecord64(Stream data)
{
// TODO: Use marshalling here instead of building
var record = new EndOfCentralDirectoryRecord64();
record.Signature = data.ReadUInt32();
if (record.Signature != EndOfCentralDirectoryRecord64Signature)
return null;
record.DirectoryRecordSize = data.ReadUInt64();
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();
// TODO: Handle the ExtensibleDataSector -- How to detect if exists?
return record;
}
/// <summary>
/// Parse a Stream into a central directory file header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled central directory file header on success, null on error</returns>
public static CentralDirectoryFileHeader? ParseCentralDirectoryFileHeader(Stream data)
{
// TODO: Use marshalling here instead of building
var header = new CentralDirectoryFileHeader();
header.Signature = data.ReadUInt32();
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();
if (header.FileNameLength > 0)
{
byte[] filenameBytes = data.ReadBytes(header.FileNameLength);
if (filenameBytes.Length != header.FileNameLength)
return null;
header.FileName = Encoding.ASCII.GetString(filenameBytes);
}
if (header.ExtraFieldLength > 0)
{
byte[] extraBytes = data.ReadBytes(header.ExtraFieldLength);
if (extraBytes.Length != header.ExtraFieldLength)
return null;
header.ExtraField = extraBytes;
}
if (header.FileCommentLength > 0)
{
byte[] commentBytes = data.ReadBytes(header.FileCommentLength);
if (commentBytes.Length != header.FileCommentLength)
return null;
header.FileComment = Encoding.ASCII.GetString(commentBytes);
}
return header;
}
/// <summary>
/// Search for the archive extra data record
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="centralDirectoryoffset">Offset to the first central directory record</param>
/// <returns>Position of the archive extra data record, -1 on error</returns>
public static long SearchForArchiveExtraDataRecord(Stream data, long centralDirectoryoffset)
{
// Cache the current offset
long current = data.Position;
// Seek to the minimum size of the record from the central directory
data.Seek(centralDirectoryoffset - 8, SeekOrigin.Begin);
// Attempt to find the end of central directory signature
while (data.Position > 0)
{
// Read the potential signature
uint possibleSignature = data.ReadUInt32();
if (possibleSignature == ArchiveExtraDataRecordSignature)
{
long signaturePosition = data.Position - 4;
data.Seek(current, SeekOrigin.Begin);
return signaturePosition;
}
// If we find any other signature
switch (possibleSignature)
{
case CentralDirectoryFileHeaderSignature:
case DataDescriptorSignature:
case DigitalSignatureSignature:
case EndOfCentralDirectoryLocator64Signature:
case EndOfCentralDirectoryRecordSignature:
case EndOfCentralDirectoryRecord64Signature:
case LocalFileHeaderSignature:
data.Seek(current, SeekOrigin.Begin);
return -1;
}
// Seek backward 5 bytes, if possible
data.Seek(-5, SeekOrigin.Current);
}
// No signature was found
data.Seek(current, SeekOrigin.Begin);
return -1;
}
/// <summary>
/// Parse a Stream into an archive extra data record
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled archive extra data record on success, null on error</returns>
public static ArchiveExtraDataRecord? ParseArchiveExtraDataRecord(Stream data)
{
// TODO: Use marshalling here instead of building
var record = new ArchiveExtraDataRecord();
record.Signature = data.ReadUInt32();
if (record.Signature != ArchiveExtraDataRecordSignature)
return null;
record.ExtraFieldLength = data.ReadUInt32();
if (record.ExtraFieldLength > 0)
{
byte[] extraBytes = data.ReadBytes((int)record.ExtraFieldLength);
if (extraBytes.Length != record.ExtraFieldLength)
return null;
record.ExtraFieldData = extraBytes;
}
return record;
}
/// <summary>
/// Parse a Stream into a local file header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled local file header on success, null on error</returns>
public static LocalFileHeader? ParseLocalFileHeader(Stream data)
{
// TODO: Use marshalling here instead of building
var header = new LocalFileHeader();
header.Signature = data.ReadUInt32();
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();
if (header.FileNameLength > 0)
{
byte[] filenameBytes = data.ReadBytes(header.FileNameLength);
if (filenameBytes.Length != header.FileNameLength)
return null;
header.FileName = Encoding.ASCII.GetString(filenameBytes);
}
if (header.ExtraFieldLength > 0)
{
byte[] extraBytes = data.ReadBytes(header.ExtraFieldLength);
if (extraBytes.Length != header.ExtraFieldLength)
return null;
header.ExtraField = extraBytes;
}
return header;
}
/// <summary>
/// Parse a Stream into a data descriptor
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled data descriptor on success, null on error</returns>
public static DataDescriptor? ParseDataDescriptor(Stream data)
{
// TODO: Use marshalling here instead of building
var dataDescriptor = new DataDescriptor();
// Signatures are expected but not required
dataDescriptor.Signature = data.ReadUInt32();
if (dataDescriptor.Signature != DataDescriptorSignature)
data.Seek(-4, SeekOrigin.Current);
dataDescriptor.CRC32 = data.ReadUInt32();
dataDescriptor.CompressedSize = data.ReadUInt32();
dataDescriptor.UncompressedSize = data.ReadUInt32();
return dataDescriptor;
}
/// <summary>
/// Parse a Stream into a ZIP64 data descriptor
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ZIP64 data descriptor on success, null on error</returns>
public static DataDescriptor64? ParseDataDescriptor64(Stream data)
{
// TODO: Use marshalling here instead of building
var zip64DataDescriptor = new DataDescriptor64();
// Signatures are expected but not required
zip64DataDescriptor.Signature = data.ReadUInt32();
if (zip64DataDescriptor.Signature != DataDescriptorSignature)
data.Seek(-4, SeekOrigin.Current);
zip64DataDescriptor.CRC32 = data.ReadUInt32();
zip64DataDescriptor.CompressedSize = data.ReadUInt64();
zip64DataDescriptor.UncompressedSize = data.ReadUInt64();
return zip64DataDescriptor;
}
}
}

View File

@@ -495,7 +495,7 @@ namespace SabreTools.Serialization.Deserializers
entry.Value = data.ReadUInt32();
entry.SectionNumber = data.ReadUInt16();
entry.SymbolType = (SymbolType)data.ReadUInt16();
entry.StorageClass = (StorageClass)data.ReadByte();
entry.StorageClass = (StorageClass)data.ReadByteValue();
entry.NumberOfAuxSymbols = data.ReadByteValue();
coffSymbolTable[i] = entry;

View File

@@ -0,0 +1,18 @@
using System.Text;
namespace SabreTools.Serialization.Interfaces
{
/// <summary>
/// Marks a class as a printer associated with a model
/// </summary>
/// <typeparam name="TModel">Type of the top-level model</typeparam>
public interface IPrinter<TModel>
{
/// <summary>
/// Print information associated with a model
/// </summary>
/// <param name="builder">StringBuilder to append information to</param>
/// <param name="model">Model to print</param>
void PrintInformation(StringBuilder builder, TModel model);
}
}

View File

@@ -11,7 +11,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="path">Path to open as a stream</param>
/// <returns>Stream representing the file, null on error</returns>
public static Stream? OpenStream(string? path)
public static Stream? OpenStream(string? path, bool skipCompression = false)
{
try
{
@@ -26,11 +26,11 @@ namespace SabreTools.Serialization
string ext = Path.GetExtension(path).TrimStart('.');
// Determine what we do based on the extension
if (string.Equals(ext, "gz", StringComparison.OrdinalIgnoreCase))
if (!skipCompression && string.Equals(ext, "gz", StringComparison.OrdinalIgnoreCase))
{
return new GZipStream(stream, CompressionMode.Decompress);
}
else if (string.Equals(ext, "zip", StringComparison.OrdinalIgnoreCase))
else if (!skipCompression && string.Equals(ext, "zip", StringComparison.OrdinalIgnoreCase))
{
// TODO: Support zip-compressed files
return null;

View File

@@ -0,0 +1,442 @@
using System;
using System.Text;
using SabreTools.Serialization.Interfaces;
using SabreTools.Serialization.Printers;
using Wrapper = SabreTools.Serialization.Wrappers;
namespace SabreTools.Serialization
{
/// <summary>
/// Generic wrapper around printing methods
/// </summary>
public static class Printer
{
/// <summary>
/// Print the item information from a wrapper to console as
/// pretty-printed text
/// </summary>
public static void PrintToConsole(this IWrapper wrapper)
{
var sb = wrapper.ExportStringBuilder();
if (sb == null)
{
Console.WriteLine("No item information could be generated");
return;
}
Console.WriteLine(sb.ToString());
}
/// <summary>
/// Export the item information as a StringBuilder
/// </summary>
public static StringBuilder? ExportStringBuilder(this IWrapper wrapper)
{
return wrapper switch
{
Wrapper.AACSMediaKeyBlock item => item.PrettyPrint(),
Wrapper.BDPlusSVM item => item.PrettyPrint(),
Wrapper.BFPK item => item.PrettyPrint(),
Wrapper.BSP item => item.PrettyPrint(),
Wrapper.CFB item => item.PrettyPrint(),
Wrapper.CIA item => item.PrettyPrint(),
Wrapper.GCF item => item.PrettyPrint(),
Wrapper.InstallShieldCabinet item => item.PrettyPrint(),
Wrapper.IRD item => item.PrettyPrint(),
Wrapper.LinearExecutable item => item.PrettyPrint(),
Wrapper.MicrosoftCabinet item => item.PrettyPrint(),
Wrapper.MoPaQ item => item.PrettyPrint(),
Wrapper.MSDOS item => item.PrettyPrint(),
Wrapper.N3DS item => item.PrettyPrint(),
Wrapper.NCF item => item.PrettyPrint(),
Wrapper.NewExecutable item => item.PrettyPrint(),
Wrapper.Nitro item => item.PrettyPrint(),
Wrapper.PAK item => item.PrettyPrint(),
Wrapper.PFF item => item.PrettyPrint(),
Wrapper.PIC item => item.PrettyPrint(),
Wrapper.PKZIP item => item.PrettyPrint(),
Wrapper.PlayJAudioFile item => item.PrettyPrint(),
Wrapper.PlayJPlaylist item => item.PrettyPrint(),
Wrapper.PortableExecutable item => item.PrettyPrint(),
Wrapper.Quantum item => item.PrettyPrint(),
Wrapper.SGA item => item.PrettyPrint(),
Wrapper.VBSP item => item.PrettyPrint(),
Wrapper.VPK item => item.PrettyPrint(),
Wrapper.WAD item => item.PrettyPrint(),
Wrapper.XeMID item => item.PrettyPrint(),
Wrapper.XMID item => item.PrettyPrint(),
Wrapper.XZP item => item.PrettyPrint(),
_ => null,
};
}
#if NET6_0_OR_GREATER
/// <summary>
/// Export the item information as JSON
/// </summary>
public static string ExportJSON(this IWrapper wrapper)
{
return wrapper switch
{
Wrapper.AACSMediaKeyBlock item => item.ExportJSON(),
Wrapper.BDPlusSVM item => item.ExportJSON(),
Wrapper.BFPK item => item.ExportJSON(),
Wrapper.BSP item => item.ExportJSON(),
Wrapper.CFB item => item.ExportJSON(),
Wrapper.CIA item => item.ExportJSON(),
Wrapper.GCF item => item.ExportJSON(),
Wrapper.InstallShieldCabinet item => item.ExportJSON(),
Wrapper.IRD item => item.ExportJSON(),
Wrapper.LinearExecutable item => item.ExportJSON(),
Wrapper.MicrosoftCabinet item => item.ExportJSON(),
Wrapper.MoPaQ item => item.ExportJSON(),
Wrapper.MSDOS item => item.ExportJSON(),
Wrapper.N3DS item => item.ExportJSON(),
Wrapper.NCF item => item.ExportJSON(),
Wrapper.NewExecutable item => item.ExportJSON(),
Wrapper.Nitro item => item.ExportJSON(),
Wrapper.PAK item => item.ExportJSON(),
Wrapper.PFF item => item.ExportJSON(),
Wrapper.PIC item => item.ExportJSON(),
Wrapper.PKZIP item => item.ExportJSON(),
Wrapper.PlayJAudioFile item => item.ExportJSON(),
Wrapper.PlayJPlaylist item => item.ExportJSON(),
Wrapper.PortableExecutable item => item.ExportJSON(),
Wrapper.Quantum item => item.ExportJSON(),
Wrapper.SGA item => item.ExportJSON(),
Wrapper.VBSP item => item.ExportJSON(),
Wrapper.VPK item => item.ExportJSON(),
Wrapper.WAD item => item.ExportJSON(),
Wrapper.XeMID item => item.ExportJSON(),
Wrapper.XMID item => item.ExportJSON(),
Wrapper.XZP item => item.ExportJSON(),
_ => string.Empty,
};
}
#endif
#region Static Printing Implementations
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.AACSMediaKeyBlock item)
{
var builder = new StringBuilder();
AACSMediaKeyBlock.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.BDPlusSVM item)
{
var builder = new StringBuilder();
BDPlusSVM.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.BFPK item)
{
var builder = new StringBuilder();
BFPK.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.BSP item)
{
var builder = new StringBuilder();
BSP.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.CFB item)
{
var builder = new StringBuilder();
CFB.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.CIA item)
{
var builder = new StringBuilder();
CIA.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.GCF item)
{
var builder = new StringBuilder();
GCF.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.InstallShieldCabinet item)
{
var builder = new StringBuilder();
InstallShieldCabinet.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.IRD item)
{
var builder = new StringBuilder();
IRD.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.LinearExecutable item)
{
var builder = new StringBuilder();
LinearExecutable.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.MicrosoftCabinet item)
{
var builder = new StringBuilder();
MicrosoftCabinet.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.MoPaQ item)
{
var builder = new StringBuilder();
MoPaQ.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.MSDOS item)
{
var builder = new StringBuilder();
MSDOS.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.N3DS item)
{
var builder = new StringBuilder();
N3DS.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.NCF item)
{
var builder = new StringBuilder();
NCF.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.NewExecutable item)
{
var builder = new StringBuilder();
NewExecutable.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.Nitro item)
{
var builder = new StringBuilder();
Nitro.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PAK item)
{
var builder = new StringBuilder();
PAK.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PFF item)
{
var builder = new StringBuilder();
PFF.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PIC item)
{
var builder = new StringBuilder();
PIC.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PKZIP item)
{
var builder = new StringBuilder();
PKZIP.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PlayJAudioFile item)
{
var builder = new StringBuilder();
PlayJAudioFile.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PlayJPlaylist item)
{
var builder = new StringBuilder();
PlayJPlaylist.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.PortableExecutable item)
{
var builder = new StringBuilder();
PortableExecutable.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.Quantum item)
{
var builder = new StringBuilder();
Quantum.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.SGA item)
{
var builder = new StringBuilder();
SGA.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.VBSP item)
{
var builder = new StringBuilder();
VBSP.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.VPK item)
{
var builder = new StringBuilder();
VPK.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.WAD item)
{
var builder = new StringBuilder();
WAD.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.XeMID item)
{
var builder = new StringBuilder();
XeMID.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.XMID item)
{
var builder = new StringBuilder();
XMID.Print(builder, item.Model);
return builder;
}
/// <summary>
/// Export the item information as pretty-printed text
/// </summary>
private static StringBuilder PrettyPrint(this Wrapper.XZP item)
{
var builder = new StringBuilder();
XZP.Print(builder, item.Model);
return builder;
}
#endregion
}
}

View File

@@ -0,0 +1,290 @@
using System.Text;
using SabreTools.Models.AACS;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class AACSMediaKeyBlock : IPrinter<MediaKeyBlock>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, MediaKeyBlock model)
=> Print(builder, model);
public static void Print(StringBuilder builder, MediaKeyBlock mediaKeyBlock)
{
builder.AppendLine("AACS Media Key Block Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, mediaKeyBlock.Records);
}
private static void Print(StringBuilder builder, Record?[]? records)
{
builder.AppendLine(" Records Information:");
builder.AppendLine(" -------------------------");
if (records == null || records.Length == 0)
{
builder.AppendLine(" No records");
builder.AppendLine();
return;
}
for (int i = 0; i < records.Length; i++)
{
var record = records[i];
Print(builder, record, i);
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Record? record, int index)
{
builder.AppendLine($" Record Entry {index}");
if (record == null)
{
builder.AppendLine(" [NULL]");
return;
}
builder.AppendLine($" Record type: {record.RecordType} (0x{record.RecordType:X})");
builder.AppendLine(record.RecordLength, " Record length");
switch (record)
{
case EndOfMediaKeyBlockRecord eomkb:
Print(builder, eomkb);
break;
case ExplicitSubsetDifferenceRecord esd:
Print(builder, esd);
break;
case MediaKeyDataRecord mkd:
Print(builder, mkd);
break;
case SubsetDifferenceIndexRecord sdi:
Print(builder, sdi);
break;
case TypeAndVersionRecord tav:
Print(builder, tav);
break;
case DriveRevocationListRecord drl:
Print(builder, drl);
break;
case HostRevocationListRecord hrl:
Print(builder, hrl);
break;
case VerifyMediaKeyRecord vmk:
Print(builder, vmk);
break;
case CopyrightRecord c:
Print(builder, c);
break;
}
}
private static void Print(StringBuilder builder, EndOfMediaKeyBlockRecord record)
{
if (record == null)
return;
builder.AppendLine(record.SignatureData, " Signature data");
}
private static void Print(StringBuilder builder, ExplicitSubsetDifferenceRecord? record)
{
if (record == null)
return;
builder.AppendLine(" Subset Differences:");
builder.AppendLine(" -------------------------");
if (record?.SubsetDifferences == null || record.SubsetDifferences.Length == 0)
{
builder.AppendLine(" No subset differences");
return;
}
for (int j = 0; j < record.SubsetDifferences.Length; j++)
{
var sd = record.SubsetDifferences[j];
builder.AppendLine($" Subset Difference {j}");
if (sd == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(sd.Mask, " Mask");
builder.AppendLine(sd.Number, " Number");
}
}
}
private static void Print(StringBuilder builder, MediaKeyDataRecord? record)
{
if (record == null)
return;
builder.AppendLine(" Media Keys:");
builder.AppendLine(" -------------------------");
if (record?.MediaKeyData == null || record.MediaKeyData.Length == 0)
{
builder.AppendLine(" No media keys");
return;
}
for (int j = 0; j < record.MediaKeyData.Length; j++)
{
var mk = record.MediaKeyData[j];
builder.AppendLine(mk, $" Media key {j}");
}
}
private static void Print(StringBuilder builder, SubsetDifferenceIndexRecord? record)
{
if (record == null)
return;
builder.AppendLine($" Span: {record.Span} (0x{record.Span:X})");
builder.AppendLine(" Offsets:");
builder.AppendLine(" -------------------------");
if (record.Offsets == null || record.Offsets.Length == 0)
{
builder.AppendLine(" No offsets");
return;
}
for (int j = 0; j < record.Offsets.Length; j++)
{
var offset = record.Offsets[j];
builder.AppendLine(offset, $" Offset {j}");
}
}
private static void Print(StringBuilder builder, TypeAndVersionRecord? record)
{
if (record == null)
return;
builder.AppendLine($" Media key block type: {record.MediaKeyBlockType} (0x{record.MediaKeyBlockType:X})");
builder.AppendLine(record.VersionNumber, " Version number");
}
private static void Print(StringBuilder builder, DriveRevocationListRecord? record)
{
if (record == null)
return;
builder.AppendLine(record.TotalNumberOfEntries, " Total number of entries");
builder.AppendLine(" Signature Blocks:");
builder.AppendLine(" -------------------------");
if (record.SignatureBlocks == null || record.SignatureBlocks.Length == 0)
{
builder.AppendLine(" No signature blocks");
return;
}
for (int j = 0; j < record.SignatureBlocks.Length; j++)
{
var block = record.SignatureBlocks[j];
builder.AppendLine($" Signature Block {j}");
if (block == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(block.NumberOfEntries, " Number of entries");
builder.AppendLine(" Entry Fields:");
builder.AppendLine(" -------------------------");
if (block.EntryFields == null || block.EntryFields.Length == 0)
{
builder.AppendLine(" No entry fields");
}
else
{
for (int k = 0; k < block.EntryFields.Length; k++)
{
var ef = block.EntryFields[k];
builder.AppendLine($" Entry {k}");
if (ef == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(ef.Range, " Range");
builder.AppendLine(ef.DriveID, " Drive ID");
}
}
}
}
}
private static void Print(StringBuilder builder, HostRevocationListRecord? record)
{
if (record == null)
return;
builder.AppendLine($" Total number of entries: {record.TotalNumberOfEntries} (0x{record.TotalNumberOfEntries:X})");
builder.AppendLine(" Signature Blocks:");
builder.AppendLine(" -------------------------");
if (record.SignatureBlocks == null || record.SignatureBlocks.Length == 0)
{
builder.AppendLine(" No signature blocks");
return;
}
for (int j = 0; j < record.SignatureBlocks.Length; j++)
{
builder.AppendLine($" Signature Block {j}");
var block = record.SignatureBlocks[j];
if (block == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(block.NumberOfEntries, " Number of entries");
builder.AppendLine(" Entry Fields:");
builder.AppendLine(" -------------------------");
if (block.EntryFields == null || block.EntryFields.Length == 0)
{
builder.AppendLine(" No entry fields");
continue;
}
for (int k = 0; k < block.EntryFields.Length; k++)
{
var ef = block.EntryFields[k];
builder.AppendLine($" Entry {k}");
if (ef == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(ef.Range, " Range");
builder.AppendLine(ef.HostID, " Host ID");
}
}
}
}
private static void Print(StringBuilder builder, VerifyMediaKeyRecord? record)
{
if (record == null)
return;
builder.AppendLine(record.CiphertextValue, " Ciphertext value");
}
private static void Print(StringBuilder builder, CopyrightRecord? record)
{
if (record == null)
return;
builder.AppendLine(record.Copyright, " Copyright");
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Text;
using SabreTools.Models.BDPlus;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class BDPlusSVM : IPrinter<SVM>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, SVM model)
=> Print(builder, model);
public static void Print(StringBuilder builder, SVM svm)
{
builder.AppendLine("BD+ SVM Information:");
builder.AppendLine("-------------------------");
builder.AppendLine(svm.Signature, "Signature");
builder.AppendLine(svm.Unknown1, "Unknown 1");
builder.AppendLine(svm.Year, "Year");
builder.AppendLine(svm.Month, "Month");
builder.AppendLine(svm.Day, "Day");
builder.AppendLine(svm.Unknown2, "Unknown 2");
builder.AppendLine(svm.Length, "Length");
//builder.AppendLine(svm.Data, "Data");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,68 @@
using System.Text;
using SabreTools.Models.BFPK;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class BFPK : IPrinter<Archive>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Archive model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Archive archive)
{
builder.AppendLine("BFPK Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, archive.Header);
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.Magic, " Magic");
builder.AppendLine(header.Version, " Version");
builder.AppendLine(header.Files, " Files");
builder.AppendLine();
}
private static void Print(StringBuilder builder, FileEntry?[]? files)
{
builder.AppendLine(" File Table Information:");
builder.AppendLine(" -------------------------");
if (files == null || files.Length == 0)
{
builder.AppendLine(" No file table items");
return;
}
for (int i = 0; i < files.Length; i++)
{
var entry = files[i];
builder.AppendLine($" File Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NameSize, " Name size");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.UncompressedSize, " Uncompressed size");
builder.AppendLine(entry.Offset, " Offset");
builder.AppendLine(entry.CompressedSize, " Compressed size");
}
}
}
}

View File

@@ -0,0 +1,150 @@
using System.Text;
using SabreTools.Models.BSP;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Models.BSP.Constants;
namespace SabreTools.Serialization.Printers
{
public class BSP : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("BSP Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
Print(builder, file.Lumps);
Print(builder, file.TextureHeader);
Print(builder, file.Textures);
}
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.Version, " Version");
builder.AppendLine();
}
private static void Print(StringBuilder builder, Lump?[]? lumps)
{
builder.AppendLine(" Lumps Information:");
builder.AppendLine(" -------------------------");
if (lumps == null || lumps.Length == 0)
{
builder.AppendLine(" No lumps");
builder.AppendLine();
return;
}
for (int i = 0; i < lumps.Length; i++)
{
var lump = lumps[i];
string specialLumpName = string.Empty;
switch (i)
{
case HL_BSP_LUMP_ENTITIES:
specialLumpName = " (entities)";
break;
case HL_BSP_LUMP_TEXTUREDATA:
specialLumpName = " (texture data)";
break;
}
builder.AppendLine($" Lump {i}{specialLumpName}");
if (lump == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(lump.Offset, " Offset");
builder.AppendLine(lump.Length, " Length");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, TextureHeader? header)
{
builder.AppendLine(" Texture Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No texture header");
builder.AppendLine();
return;
}
builder.AppendLine(header.TextureCount, " Texture count");
builder.AppendLine(" Offsets:");
if (header.Offsets == null || header.Offsets.Length == 0)
{
builder.AppendLine(" No offsets");
builder.AppendLine();
return;
}
for (int i = 0; i < header.Offsets.Length; i++)
{
builder.AppendLine(header.Offsets[i], $" Offset {i}");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Texture?[]? textures)
{
builder.AppendLine(" Textures Information:");
builder.AppendLine(" -------------------------");
if (textures == null || textures.Length == 0)
{
builder.AppendLine(" No textures");
builder.AppendLine();
return;
}
for (int i = 0; i < textures.Length; i++)
{
var texture = textures[i];
builder.AppendLine($" Texture {i}");
if (texture == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(texture.Name, " Name");
builder.AppendLine(texture.Width, " Width");
builder.AppendLine(texture.Height, " Height");
builder.AppendLine(" Offsets:");
if (texture.Offsets == null || texture.Offsets.Length == 0)
{
builder.AppendLine(" No offsets");
continue;
}
else
{
for (int j = 0; j < texture.Offsets.Length; j++)
{
builder.AppendLine(texture.Offsets[i], $" Offset {j}");
}
}
// Skip texture data
builder.AppendLine(texture.PaletteSize, " Palette size");
// Skip palette data
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Text;
using SabreTools.Models.CFB;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class CFB : IPrinter<Binary>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Binary model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Binary binary)
{
builder.AppendLine("Compound File Binary Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, binary.Header);
Print(builder, binary.FATSectorNumbers, "FAT");
Print(builder, binary.MiniFATSectorNumbers, "Mini FAT");
Print(builder, binary.DIFATSectorNumbers, "DIFAT");
Print(builder, binary.DirectoryEntries);
}
private static void Print(StringBuilder builder, FileHeader? header)
{
builder.AppendLine(" File Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No file header");
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.CLSID, " CLSID");
builder.AppendLine(header.MinorVersion, " Minor version");
builder.AppendLine(header.MajorVersion, " Major version");
builder.AppendLine(header.ByteOrder, " Byte order");
builder.AppendLine($" Sector shift: {header.SectorShift} (0x{header.SectorShift:X}) => {Math.Pow(2, header.SectorShift)}");
builder.AppendLine($" Mini sector shift: {header.MiniSectorShift} (0x{header.MiniSectorShift:X}) => {Math.Pow(2, header.MiniSectorShift)}");
builder.AppendLine(header.Reserved, " Reserved");
builder.AppendLine(header.NumberOfDirectorySectors, " Number of directory sectors");
builder.AppendLine(header.NumberOfFATSectors, " Number of FAT sectors");
builder.AppendLine(header.FirstDirectorySectorLocation, " First directory sector location");
builder.AppendLine(header.TransactionSignatureNumber, " Transaction signature number");
builder.AppendLine(header.MiniStreamCutoffSize, " Mini stream cutoff size");
builder.AppendLine(header.FirstMiniFATSectorLocation, " First mini FAT sector location");
builder.AppendLine(header.NumberOfMiniFATSectors, " Number of mini FAT sectors");
builder.AppendLine(header.FirstDIFATSectorLocation, " First DIFAT sector location");
builder.AppendLine(header.NumberOfDIFATSectors, " Number of DIFAT sectors");
builder.AppendLine(" DIFAT:");
if (header.DIFAT == null || header.DIFAT.Length == 0)
{
builder.AppendLine(" No DIFAT entries");
builder.AppendLine();
return;
}
for (int i = 0; i < header.DIFAT.Length; i++)
{
builder.AppendLine($" DIFAT Entry {i}: {header.DIFAT[i]} (0x{header.DIFAT[i]:X})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, SectorNumber[]? sectorNumbers, string name)
{
builder.AppendLine($" {name} Sectors Information:");
builder.AppendLine(" -------------------------");
if (sectorNumbers == null || sectorNumbers.Length == 0)
{
builder.AppendLine($" No {name} sectors");
builder.AppendLine();
return;
}
for (int i = 0; i < sectorNumbers.Length; i++)
{
builder.AppendLine($" {name} Sector Entry {i}: {sectorNumbers[i]} (0x{sectorNumbers[i]:X})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryEntry?[]? directoryEntries)
{
builder.AppendLine(" Directory Entries Information:");
builder.AppendLine(" -------------------------");
if (directoryEntries == null || directoryEntries.Length == 0)
{
builder.AppendLine(" No directory entries");
builder.AppendLine();
return;
}
for (int i = 0; i < directoryEntries.Length; i++)
{
var directoryEntry = directoryEntries[i];
builder.AppendLine($" Directory Entry {i}");
if (directoryEntry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(directoryEntry.Name, " Name");
builder.AppendLine(directoryEntry.NameLength, " Name length");
builder.AppendLine($" Object type: {directoryEntry.ObjectType} (0x{directoryEntry.ObjectType:X})");
builder.AppendLine($" Color flag: {directoryEntry.ColorFlag} (0x{directoryEntry.ColorFlag:X})");
builder.AppendLine($" Left sibling ID: {directoryEntry.LeftSiblingID} (0x{directoryEntry.LeftSiblingID:X})");
builder.AppendLine($" Right sibling ID: {directoryEntry.RightSiblingID} (0x{directoryEntry.RightSiblingID:X})");
builder.AppendLine($" Child ID: {directoryEntry.ChildID} (0x{directoryEntry.ChildID:X})");
builder.AppendLine(directoryEntry.CLSID, " CLSID");
builder.AppendLine(directoryEntry.StateBits, " State bits");
builder.AppendLine(directoryEntry.CreationTime, " Creation time");
builder.AppendLine(directoryEntry.ModifiedTime, " Modification time");
builder.AppendLine(directoryEntry.StartingSectorLocation, " Staring sector location");
builder.AppendLine(directoryEntry.StreamSize, " Stream size");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,454 @@
using System.Text;
using SabreTools.Models.N3DS;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class CIA : IPrinter<Models.N3DS.CIA>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Models.N3DS.CIA model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Models.N3DS.CIA cia)
{
builder.AppendLine("CIA Archive Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, cia.Header);
Print(builder, cia.CertificateChain);
Print(builder, cia.Ticket);
Print(builder, cia.TMDFileData);
Print(builder, cia.Partitions);
Print(builder, cia.MetaData);
}
private static void Print(StringBuilder builder, CIAHeader? header)
{
builder.AppendLine(" CIA Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No CIA header");
builder.AppendLine();
return;
}
builder.AppendLine(header.HeaderSize, " Header size");
builder.AppendLine(header.Type, " Type");
builder.AppendLine(header.Version, " Version");
builder.AppendLine(header.CertificateChainSize, " Certificate chain size");
builder.AppendLine(header.TicketSize, " Ticket size");
builder.AppendLine(header.TMDFileSize, " TMD file size");
builder.AppendLine(header.MetaSize, " Meta size");
builder.AppendLine(header.ContentSize, " Content size");
builder.AppendLine(header.ContentIndex, " Content index");
builder.AppendLine();
}
private static void Print(StringBuilder builder, Certificate?[]? certificateChain)
{
builder.AppendLine(" Certificate Chain Information:");
builder.AppendLine(" -------------------------");
if (certificateChain == null || certificateChain.Length == 0)
{
builder.AppendLine(" No certificates, expected 3");
builder.AppendLine();
return;
}
for (int i = 0; i < certificateChain.Length; i++)
{
var certificate = certificateChain[i];
string certificateName = string.Empty;
switch (i)
{
case 0: certificateName = " (CA)"; break;
case 1: certificateName = " (Ticket)"; break;
case 2: certificateName = " (TMD)"; break;
}
builder.AppendLine($" Certificate {i}{certificateName}");
if (certificate == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})");
builder.AppendLine(certificate.SignatureSize, " Signature size");
builder.AppendLine(certificate.PaddingSize, " Padding size");
builder.AppendLine(certificate.Signature, " Signature");
builder.AppendLine(certificate.Padding, " Padding");
builder.AppendLine(certificate.Issuer, " Issuer");
builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})");
builder.AppendLine(certificate.Name, " Name");
builder.AppendLine(certificate.ExpirationTime, " Expiration time");
switch (certificate.KeyType)
{
case PublicKeyType.RSA_4096:
case PublicKeyType.RSA_2048:
builder.AppendLine(certificate.RSAModulus, " Modulus");
builder.AppendLine(certificate.RSAPublicExponent, " Public exponent");
builder.AppendLine(certificate.RSAPadding, " Padding");
break;
case PublicKeyType.EllipticCurve:
builder.AppendLine(certificate.ECCPublicKey, " Public key");
builder.AppendLine(certificate.ECCPadding, " Padding");
break;
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Ticket? ticket)
{
builder.AppendLine(" Ticket Information:");
builder.AppendLine(" -------------------------");
if (ticket == null)
{
builder.AppendLine(" No ticket");
builder.AppendLine();
return;
}
builder.AppendLine($" Signature type: {ticket.SignatureType} (0x{ticket.SignatureType:X})");
builder.AppendLine(ticket.SignatureSize, " Signature size");
builder.AppendLine(ticket.PaddingSize, " Padding size");
builder.AppendLine(ticket.Signature, " Signature");
builder.AppendLine(ticket.Padding, " Padding");
builder.AppendLine(ticket.Issuer, " Issuer");
builder.AppendLine(ticket.ECCPublicKey, " ECC public key");
builder.AppendLine(ticket.Version, " Version");
builder.AppendLine(ticket.CaCrlVersion, " CaCrlVersion");
builder.AppendLine(ticket.SignerCrlVersion, " SignerCrlVersion");
builder.AppendLine(ticket.TitleKey, " Title key");
builder.AppendLine(ticket.Reserved1, " Reserved 1");
builder.AppendLine(ticket.TicketID, " Ticket ID");
builder.AppendLine(ticket.ConsoleID, " Console ID");
builder.AppendLine(ticket.TitleID, " Title ID");
builder.AppendLine(ticket.Reserved2, " Reserved 2");
builder.AppendLine(ticket.TicketTitleVersion, " Ticket title version");
builder.AppendLine(ticket.Reserved3, " Reserved 3");
builder.AppendLine(ticket.LicenseType, " License type");
builder.AppendLine(ticket.CommonKeyYIndex, " Common key Y index");
builder.AppendLine(ticket.Reserved4, " Reserved 4");
builder.AppendLine(ticket.eShopAccountID, " eShop Account ID");
builder.AppendLine(ticket.Reserved5, " Reserved 5");
builder.AppendLine(ticket.Audit, " Audit");
builder.AppendLine(ticket.Reserved6, " Reserved 6");
builder.AppendLine(" Limits:");
if (ticket.Limits == null || ticket.Limits.Length == 0)
{
builder.AppendLine(" No limits");
}
else
{
for (int i = 0; i < ticket.Limits.Length; i++)
{
builder.AppendLine(ticket.Limits[i], $" Limit {i}");
}
}
builder.AppendLine(ticket.ContentIndexSize, " Content index size");
builder.AppendLine(ticket.ContentIndex, " Content index");
builder.AppendLine();
builder.AppendLine(" Ticket Certificate Chain Information:");
builder.AppendLine(" -------------------------");
if (ticket.CertificateChain == null || ticket.CertificateChain.Length == 0)
{
builder.AppendLine(" No certificates, expected 2");
}
else
{
for (int i = 0; i < ticket.CertificateChain.Length; i++)
{
var certificate = ticket.CertificateChain[i];
string certificateName = string.Empty;
switch (i)
{
case 0: certificateName = " (Ticket)"; break;
case 1: certificateName = " (CA)"; break;
}
builder.AppendLine($" Certificate {i}{certificateName}");
if (certificate == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})");
builder.AppendLine(certificate.SignatureSize, " Signature size");
builder.AppendLine(certificate.PaddingSize, " Padding size");
builder.AppendLine(certificate.Signature, " Signature");
builder.AppendLine(certificate.Padding, " Padding");
builder.AppendLine(certificate.Issuer, " Issuer");
builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})");
builder.AppendLine(certificate.Name, " Name");
builder.AppendLine(certificate.ExpirationTime, " Expiration time");
switch (certificate.KeyType)
{
case PublicKeyType.RSA_4096:
case PublicKeyType.RSA_2048:
builder.AppendLine(certificate.RSAModulus, " Modulus");
builder.AppendLine(certificate.RSAPublicExponent, " Public exponent");
builder.AppendLine(certificate.RSAPadding, " Padding");
break;
case PublicKeyType.EllipticCurve:
builder.AppendLine(certificate.ECCPublicKey, " Public key");
builder.AppendLine(certificate.ECCPadding, " Padding");
break;
}
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, TitleMetadata? tmd)
{
builder.AppendLine(" Title Metadata Information:");
builder.AppendLine(" -------------------------");
if (tmd == null)
{
builder.AppendLine(" No title metadata");
builder.AppendLine();
return;
}
builder.AppendLine($" Signature type: {tmd.SignatureType} (0x{tmd.SignatureType:X})");
builder.AppendLine(tmd.SignatureSize, " Signature size");
builder.AppendLine(tmd.PaddingSize, " Padding size");
builder.AppendLine(tmd.Signature, " Signature");
builder.AppendLine(tmd.Padding1, " Padding 1");
builder.AppendLine(tmd.Issuer, " Issuer");
builder.AppendLine(tmd.Version, " Version");
builder.AppendLine(tmd.CaCrlVersion, " CaCrlVersion");
builder.AppendLine(tmd.SignerCrlVersion, " SignerCrlVersion");
builder.AppendLine(tmd.Reserved1, " Reserved 1");
builder.AppendLine(tmd.SystemVersion, " System version");
builder.AppendLine(tmd.TitleID, " Title ID");
builder.AppendLine(tmd.TitleType, " Title type");
builder.AppendLine(tmd.GroupID, " Group ID");
builder.AppendLine(tmd.SaveDataSize, " Save data size");
builder.AppendLine(tmd.SRLPrivateSaveDataSize, " SRL private save data size");
builder.AppendLine(tmd.Reserved2, " Reserved 2");
builder.AppendLine(tmd.SRLFlag, " SRL flag");
builder.AppendLine(tmd.Reserved3, " Reserved 3");
builder.AppendLine(tmd.AccessRights, " Access rights");
builder.AppendLine(tmd.TitleVersion, " Title version");
builder.AppendLine(tmd.ContentCount, " Content count");
builder.AppendLine(tmd.BootContent, " Boot content");
builder.AppendLine(tmd.Padding2, " Padding 2");
builder.AppendLine(tmd.SHA256HashContentInfoRecords, " SHA-256 hash of the content info records");
builder.AppendLine();
builder.AppendLine(" Ticket Content Info Records Information:");
builder.AppendLine(" -------------------------");
if (tmd.ContentInfoRecords == null || tmd.ContentInfoRecords.Length == 0)
{
builder.AppendLine(" No content info records, expected 64");
}
else
{
for (int i = 0; i < tmd.ContentInfoRecords.Length; i++)
{
var contentInfoRecord = tmd.ContentInfoRecords[i];
builder.AppendLine($" Content Info Record {i}");
if (contentInfoRecord == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(contentInfoRecord.ContentIndexOffset, " Content index offset");
builder.AppendLine(contentInfoRecord.ContentCommandCount, " Content command count");
builder.AppendLine(contentInfoRecord.UnhashedContentRecordsSHA256Hash, $" SHA-256 hash of the next {contentInfoRecord.ContentCommandCount} records not hashed");
}
}
builder.AppendLine();
builder.AppendLine(" Ticket Content Chunk Records Information:");
builder.AppendLine(" -------------------------");
if (tmd.ContentChunkRecords == null || tmd.ContentChunkRecords.Length == 0)
{
builder.AppendLine($" No content chunk records, expected {tmd.ContentCount}");
}
else
{
for (int i = 0; i < tmd.ContentChunkRecords.Length; i++)
{
var contentChunkRecord = tmd.ContentChunkRecords[i];
builder.AppendLine($" Content Chunk Record {i}");
if (contentChunkRecord == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(contentChunkRecord.ContentId, " Content ID");
builder.AppendLine($" Content index: {contentChunkRecord.ContentIndex} (0x{contentChunkRecord.ContentIndex:X})");
builder.AppendLine($" Content type: {contentChunkRecord.ContentType} (0x{contentChunkRecord.ContentType:X})");
builder.AppendLine(contentChunkRecord.ContentSize, " Content size");
builder.AppendLine(contentChunkRecord.SHA256Hash, " SHA-256 hash");
}
}
builder.AppendLine();
builder.AppendLine(" Ticket Certificate Chain Information:");
builder.AppendLine(" -------------------------");
if (tmd.CertificateChain == null || tmd.CertificateChain.Length == 0)
{
builder.AppendLine(" No certificates, expected 2");
}
else
{
for (int i = 0; i < tmd.CertificateChain.Length; i++)
{
var certificate = tmd.CertificateChain[i];
string certificateName = string.Empty;
switch (i)
{
case 0: certificateName = " (TMD)"; break;
case 1: certificateName = " (CA)"; break;
}
builder.AppendLine($" Certificate {i}{certificateName}");
if (certificate == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Signature type: {certificate.SignatureType} (0x{certificate.SignatureType:X})");
builder.AppendLine(certificate.SignatureSize, " Signature size");
builder.AppendLine(certificate.PaddingSize, " Padding size");
builder.AppendLine(certificate.Signature, " Signature");
builder.AppendLine(certificate.Padding, " Padding");
builder.AppendLine(certificate.Issuer, " Issuer");
builder.AppendLine($" Key type: {certificate.KeyType} (0x{certificate.KeyType:X})");
builder.AppendLine(certificate.Name, " Name");
builder.AppendLine(certificate.ExpirationTime, " Expiration time");
switch (certificate.KeyType)
{
case PublicKeyType.RSA_4096:
case PublicKeyType.RSA_2048:
builder.AppendLine(certificate.RSAModulus, " Modulus");
builder.AppendLine(certificate.RSAPublicExponent, " Public exponent");
builder.AppendLine(certificate.RSAPadding, " Padding");
break;
case PublicKeyType.EllipticCurve:
builder.AppendLine(certificate.ECCPublicKey, " Public key");
builder.AppendLine(certificate.ECCPadding, " Padding");
break;
}
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NCCHHeader?[]? partitions)
{
builder.AppendLine(" NCCH Partition Header Information:");
builder.AppendLine(" -------------------------");
if (partitions == null || partitions.Length == 0)
{
builder.AppendLine(" No NCCH partition headers");
builder.AppendLine();
return;
}
for (int i = 0; i < partitions.Length; i++)
{
var partitionHeader = partitions[i];
builder.AppendLine($" NCCH Partition Header {i}");
if (partitionHeader == null)
{
builder.AppendLine(" [NULL]");
continue;
}
if (partitionHeader.MagicID == string.Empty)
{
builder.AppendLine(" Empty partition, no data can be parsed");
continue;
}
else if (partitionHeader.MagicID != Constants.NCCHMagicNumber)
{
builder.AppendLine(" Unrecognized partition data, no data can be parsed");
continue;
}
builder.AppendLine(partitionHeader.RSA2048Signature, " RSA-2048 SHA-256 signature");
builder.AppendLine(partitionHeader.MagicID, " Magic ID");
builder.AppendLine(partitionHeader.ContentSizeInMediaUnits, " Content size in media units");
builder.AppendLine(partitionHeader.PartitionId, " Partition ID");
builder.AppendLine(partitionHeader.MakerCode, " Maker code");
builder.AppendLine(partitionHeader.Version, " Version");
builder.AppendLine(partitionHeader.VerificationHash, " Verification hash");
builder.AppendLine(partitionHeader.ProgramId, " Program ID");
builder.AppendLine(partitionHeader.Reserved1, " Reserved 1");
builder.AppendLine(partitionHeader.LogoRegionHash, " Logo region SHA-256 hash");
builder.AppendLine(partitionHeader.ProductCode, " Product code");
builder.AppendLine(partitionHeader.ExtendedHeaderHash, " Extended header SHA-256 hash");
builder.AppendLine(partitionHeader.ExtendedHeaderSizeInBytes, " Extended header size in bytes");
builder.AppendLine(partitionHeader.Reserved2, " Reserved 2");
builder.AppendLine(" Flags:");
if (partitionHeader.Flags == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(partitionHeader.Flags.Reserved0, " Reserved 0");
builder.AppendLine(partitionHeader.Flags.Reserved1, " Reserved 1");
builder.AppendLine(partitionHeader.Flags.Reserved2, " Reserved 2");
builder.AppendLine($" Crypto method: {partitionHeader.Flags.CryptoMethod} (0x{partitionHeader.Flags.CryptoMethod:X})");
builder.AppendLine($" Content platform: {partitionHeader.Flags.ContentPlatform} (0x{partitionHeader.Flags.ContentPlatform:X})");
builder.AppendLine($" Content type: {partitionHeader.Flags.MediaPlatformIndex} (0x{partitionHeader.Flags.MediaPlatformIndex:X})");
builder.AppendLine(partitionHeader.Flags.ContentUnitSize, " Content unit size");
builder.AppendLine($" Bitmasks: {partitionHeader.Flags.BitMasks} (0x{partitionHeader.Flags.BitMasks:X})");
}
builder.AppendLine(partitionHeader.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units");
builder.AppendLine(partitionHeader.PlainRegionSizeInMediaUnits, " Plain region size, in media units");
builder.AppendLine(partitionHeader.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units");
builder.AppendLine(partitionHeader.LogoRegionSizeInMediaUnits, " Logo region size, in media units");
builder.AppendLine(partitionHeader.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units");
builder.AppendLine(partitionHeader.ExeFSSizeInMediaUnits, " ExeFS size, in media units");
builder.AppendLine(partitionHeader.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region offset, in media units");
builder.AppendLine(partitionHeader.Reserved3, " Reserved 3");
builder.AppendLine(partitionHeader.RomFSOffsetInMediaUnits, " RomFS offset, in media units");
builder.AppendLine(partitionHeader.RomFSSizeInMediaUnits, " RomFS size, in media units");
builder.AppendLine(partitionHeader.RomFSHashRegionSizeInMediaUnits, " RomFS hash region offset, in media units");
builder.AppendLine(partitionHeader.Reserved4, " Reserved 4");
builder.AppendLine(partitionHeader.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash");
builder.AppendLine(partitionHeader.RomFSSuperblockHash, " RomFS superblock SHA-256 hash");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, MetaData? metaData)
{
builder.AppendLine(" Meta Data Information:");
builder.AppendLine(" -------------------------");
if (metaData == null)
{
builder.AppendLine(" No meta file data");
builder.AppendLine();
return;
}
builder.AppendLine(metaData.TitleIDDependencyList, " Title ID dependency list");
builder.AppendLine(metaData.Reserved1, " Reserved 1");
builder.AppendLine(metaData.CoreVersion, " Core version");
builder.AppendLine(metaData.Reserved2, " Reserved 2");
builder.AppendLine(metaData.IconData, " Icon data");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,539 @@
using System.Collections.Generic;
using System.Text;
using SabreTools.Models.GCF;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class GCF : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("GCF Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Header
Print(builder, file.Header);
// Block Entries
Print(builder, file.BlockEntryHeader);
Print(builder, file.BlockEntries);
// Fragmentation Maps
Print(builder, file.FragmentationMapHeader);
Print(builder, file.FragmentationMaps);
// Block Entry Maps
Print(builder, file.BlockEntryMapHeader);
Print(builder, file.BlockEntryMaps);
// Directory and Directory Maps
Print(builder, file.DirectoryHeader);
Print(builder, file.DirectoryEntries, file.DirectoryNames);
// TODO: Should we print out the entire string table?
Print(builder, file.DirectoryInfo1Entries);
Print(builder, file.DirectoryInfo2Entries);
Print(builder, file.DirectoryCopyEntries);
Print(builder, file.DirectoryLocalEntries);
Print(builder, file.DirectoryMapHeader);
Print(builder, file.DirectoryMapEntries);
// Checksums and Checksum Maps
Print(builder, file.ChecksumHeader);
Print(builder, file.ChecksumMapHeader);
Print(builder, file.ChecksumMapEntries);
Print(builder, file.ChecksumEntries);
// Data Blocks
Print(builder, file.DataBlockHeader);
}
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.Dummy0, " Dummy 0");
builder.AppendLine(header.MajorVersion, " Major version");
builder.AppendLine(header.MinorVersion, " Minor version");
builder.AppendLine(header.CacheID, " Cache ID");
builder.AppendLine(header.LastVersionPlayed, " Last version played");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.Dummy2, " Dummy 2");
builder.AppendLine(header.FileSize, " File size");
builder.AppendLine(header.BlockSize, " Block size");
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.Dummy3, " Dummy 3");
builder.AppendLine();
}
private static void Print(StringBuilder builder, BlockEntryHeader? header)
{
builder.AppendLine(" Block Entry Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No block entry header");
builder.AppendLine();
return;
}
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.BlocksUsed, " Blocks used");
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.Dummy2, " Dummy 2");
builder.AppendLine(header.Dummy3, " Dummy 3");
builder.AppendLine(header.Dummy4, " Dummy 4");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
private static void Print(StringBuilder builder, BlockEntry?[]? entries)
{
builder.AppendLine(" Block Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No block entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Block Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.EntryFlags, " Entry flags");
builder.AppendLine(entry.FileDataOffset, " File data offset");
builder.AppendLine(entry.FileDataSize, " File data size");
builder.AppendLine(entry.FirstDataBlockIndex, " First data block index");
builder.AppendLine(entry.NextBlockEntryIndex, " Next block entry index");
builder.AppendLine(entry.PreviousBlockEntryIndex, " Previous block entry index");
builder.AppendLine(entry.DirectoryIndex, " Directory index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FragmentationMapHeader? header)
{
builder.AppendLine(" Fragmentation Map Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No fragmentation map header");
builder.AppendLine();
return;
}
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.FirstUnusedEntry, " First unused entry");
builder.AppendLine(header.Terminator, " Terminator");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
private static void Print(StringBuilder builder, FragmentationMap?[]? entries)
{
builder.AppendLine(" Fragmentation Maps Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No fragmentation maps");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Fragmentation Map {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NextDataBlockIndex, " Next data block index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, BlockEntryMapHeader? header)
{
builder.AppendLine(" Block Entry Map Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No block entry map header");
builder.AppendLine();
return;
}
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.FirstBlockEntryIndex, " First block entry index");
builder.AppendLine(header.LastBlockEntryIndex, " Last block entry index");
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
private static void Print(StringBuilder builder, BlockEntryMap?[]? entries)
{
builder.AppendLine(" Block Entry Maps Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No block entry maps");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Block Entry Map {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.PreviousBlockEntryIndex, " Previous data block index");
builder.AppendLine(entry.NextBlockEntryIndex, " Next data block index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryHeader? header)
{
builder.AppendLine(" Directory Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.CacheID, " Cache ID");
builder.AppendLine(header.LastVersionPlayed, " Last version played");
builder.AppendLine(header.ItemCount, " Item count");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.DirectorySize, " Directory size");
builder.AppendLine(header.NameSize, " Name size");
builder.AppendLine(header.Info1Count, " Info 1 count");
builder.AppendLine(header.CopyCount, " Copy count");
builder.AppendLine(header.LocalCount, " Local count");
builder.AppendLine(header.Dummy2, " Dummy 2");
builder.AppendLine(header.Dummy3, " Dummy 3");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryEntry?[]? entries, Dictionary<long, string?>? entryNames)
{
builder.AppendLine(" Directory Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NameOffset, " Name offset");
builder.AppendLine(entryNames![entry.NameOffset], " Name");
builder.AppendLine(entry.ItemSize, " Item size");
builder.AppendLine(entry.ChecksumIndex, " Checksum index");
builder.AppendLine($" Directory flags: {entry.DirectoryFlags} (0x{entry.DirectoryFlags:X})");
builder.AppendLine(entry.ParentIndex, " Parent index");
builder.AppendLine(entry.NextIndex, " Next index");
builder.AppendLine(entry.FirstIndex, " First index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryInfo1Entry?[]? entries)
{
builder.AppendLine(" Directory Info 1 Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory info 1 entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Info 1 Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryInfo2Entry?[]? entries)
{
builder.AppendLine(" Directory Info 2 Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory info 2 entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Info 2 Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryCopyEntry?[]? entries)
{
builder.AppendLine(" Directory Copy Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory copy entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Copy Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.DirectoryIndex, " Directory index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryLocalEntry?[]? entries)
{
builder.AppendLine(" Directory Local Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory local entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Local Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.DirectoryIndex, " Directory index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryMapHeader? header)
{
builder.AppendLine(" Directory Map Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory map header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryMapEntry?[]? entries)
{
builder.AppendLine(" Directory Map Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory map entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Map Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FirstBlockIndex, " First block index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumHeader? header)
{
builder.AppendLine(" Checksum Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No checksum header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.ChecksumSize, " Checksum size");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumMapHeader? header)
{
builder.AppendLine(" Checksum Map Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No checksum map header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.ItemCount, " Item count");
builder.AppendLine(header.ChecksumCount, " Checksum count");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumMapEntry?[]? entries)
{
builder.AppendLine(" Checksum Map Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No checksum map entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Checksum Map Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.ChecksumCount, " Checksum count");
builder.AppendLine(entry.FirstChecksumIndex, " First checksum index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumEntry?[]? entries)
{
builder.AppendLine(" Checksum Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No checksum entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Checksum Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Checksum, " Checksum");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DataBlockHeader? header)
{
builder.AppendLine(" Data Block Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No data block header");
builder.AppendLine();
return;
}
builder.AppendLine(header.LastVersionPlayed, " Last version played");
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.BlockSize, " Block size");
builder.AppendLine(header.FirstBlockOffset, " First block offset");
builder.AppendLine(header.BlocksUsed, " Blocks used");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Text;
using SabreTools.Models.IRD;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class IRD : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File ird)
{
builder.AppendLine("IRD Information:");
builder.AppendLine("-------------------------");
builder.AppendLine(ird.Magic, "Magic", Encoding.ASCII);
builder.AppendLine(ird.Version, "Version");
builder.AppendLine(ird.TitleID, "Title ID");
builder.AppendLine(ird.TitleLength, "Title length");
builder.AppendLine(ird.Title, "Title");
builder.AppendLine(ird.SystemVersion, "System version");
builder.AppendLine(ird.GameVersion, "Game version");
builder.AppendLine(ird.AppVersion, "App version");
builder.AppendLine(ird.HeaderLength, "Header length");
builder.AppendLine(ird.Header, "Header");
builder.AppendLine(ird.FooterLength, "Footer length");
builder.AppendLine(ird.Footer, "Footer");
builder.AppendLine(ird.RegionCount, "Region count");
if (ird.RegionCount != 0 && ird.RegionHashes != null && ird.RegionHashes.Length != 0)
{
for (int i = 0; i < ird.RegionCount; i++)
{
builder.AppendLine(ird.RegionHashes[i], $"Region {i} hash");
}
}
builder.AppendLine(ird.FileCount, "File count");
for (int i = 0; i < ird.FileCount; i++)
{
if (ird.FileKeys != null)
builder.AppendLine(ird.FileKeys[i], $"File {i} key");
if (ird.FileHashes != null)
builder.AppendLine(ird.FileHashes[i], $"File {i} hash");
}
builder.AppendLine(ird.ExtraConfig, "Extra config");
builder.AppendLine(ird.Attachments, "Attachments");
builder.AppendLine(ird.Data1Key, "Data 1 key");
builder.AppendLine(ird.Data2Key, "Data 2 key");
builder.AppendLine(ird.PIC, "PIC");
builder.AppendLine(ird.UID, "UID");
builder.AppendLine(ird.CRC, "CRC");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,417 @@
using System.Collections.Generic;
using System.Text;
using SabreTools.Models.InstallShieldCabinet;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class InstallShieldCabinet : IPrinter<Cabinet>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Cabinet model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Cabinet cabinet)
{
builder.AppendLine("InstallShield Cabinet Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Major Version
int majorVersion = GetMajorVersion(cabinet.CommonHeader);
// Headers
Print(builder, cabinet.CommonHeader, majorVersion);
Print(builder, cabinet.VolumeHeader, majorVersion);
Print(builder, cabinet.Descriptor);
// File Descriptors
Print(builder, cabinet.FileDescriptorOffsets);
Print(builder, cabinet.DirectoryNames);
Print(builder, cabinet.FileDescriptors);
// File Groups
Print(builder, cabinet.FileGroupOffsets, "File Group");
Print(builder, cabinet.FileGroups);
// Components
Print(builder, cabinet.ComponentOffsets, "Component");
Print(builder, cabinet.Components);
}
private static int GetMajorVersion(CommonHeader? header)
{
uint majorVersion = header?.Version ?? 0;
if (majorVersion >> 24 == 1)
{
majorVersion = (majorVersion >> 12) & 0x0F;
}
else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4)
{
majorVersion = majorVersion & 0xFFFF;
if (majorVersion != 0)
majorVersion /= 100;
}
return (int)majorVersion;
}
private static void Print(StringBuilder builder, CommonHeader? header, int majorVersion)
{
builder.AppendLine(" Common Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(value: " No common header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine($" Version: {header.Version} (0x{header.Version:X}) [{majorVersion}]");
builder.AppendLine(header.VolumeInfo, " Volume info");
builder.AppendLine(header.DescriptorOffset, " Descriptor offset");
builder.AppendLine(header.DescriptorSize, " Descriptor size");
builder.AppendLine();
}
private static void Print(StringBuilder builder, VolumeHeader? header, int majorVersion)
{
builder.AppendLine(" Volume Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(value: " No volume header");
builder.AppendLine();
return;
}
if (majorVersion <= 5)
{
builder.AppendLine(header.DataOffset, " Data offset");
builder.AppendLine(header.FirstFileIndex, " First file index");
builder.AppendLine(header.LastFileIndex, " Last file index");
builder.AppendLine(header.FirstFileOffset, " First file offset");
builder.AppendLine(header.FirstFileSizeExpanded, " First file size expanded");
builder.AppendLine(header.FirstFileSizeCompressed, " First file size compressed");
builder.AppendLine(header.LastFileOffset, " Last file offset");
builder.AppendLine(header.LastFileSizeExpanded, " Last file size expanded");
builder.AppendLine(header.LastFileSizeCompressed, " Last file size compressed");
}
else
{
builder.AppendLine(header.DataOffset, " Data offset");
builder.AppendLine(header.DataOffsetHigh, " Data offset high");
builder.AppendLine(header.FirstFileIndex, " First file index");
builder.AppendLine(header.LastFileIndex, " Last file index");
builder.AppendLine(header.FirstFileOffset, " First file offset");
builder.AppendLine(header.FirstFileOffsetHigh, " First file offset high");
builder.AppendLine(header.FirstFileSizeExpanded, " First file size expanded");
builder.AppendLine(header.FirstFileSizeExpandedHigh, " First file size expanded high");
builder.AppendLine(header.FirstFileSizeCompressed, " First file size compressed");
builder.AppendLine(header.FirstFileSizeCompressedHigh, " First file size compressed high");
builder.AppendLine(header.LastFileOffset, " Last file offset");
builder.AppendLine(header.LastFileOffsetHigh, " Last file offset high");
builder.AppendLine(header.LastFileSizeExpanded, " Last file size expanded");
builder.AppendLine(header.LastFileSizeExpandedHigh, " Last file size expanded high");
builder.AppendLine(header.LastFileSizeCompressed, " Last file size compressed");
builder.AppendLine(header.LastFileSizeCompressedHigh, " Last file size compressed high");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Descriptor? descriptor)
{
builder.AppendLine(" Descriptor Information:");
builder.AppendLine(" -------------------------");
if (descriptor == null)
{
builder.AppendLine(" No descriptor");
builder.AppendLine();
return;
}
builder.AppendLine(descriptor.StringsOffset, " Strings offset");
builder.AppendLine(descriptor.Reserved0, " Reserved 0");
builder.AppendLine(descriptor.ComponentListOffset, " Component list offset");
builder.AppendLine(descriptor.FileTableOffset, " File table offset");
builder.AppendLine(descriptor.Reserved1, " Reserved 1");
builder.AppendLine(descriptor.FileTableSize, " File table size");
builder.AppendLine(descriptor.FileTableSize2, " File table size 2");
builder.AppendLine(descriptor.DirectoryCount, " Directory count");
builder.AppendLine(descriptor.Reserved2, " Reserved 2");
builder.AppendLine(descriptor.Reserved3, " Reserved 3");
builder.AppendLine(descriptor.Reserved4, " Reserved 4");
builder.AppendLine(descriptor.FileCount, " File count");
builder.AppendLine(descriptor.FileTableOffset2, " File table offset 2");
builder.AppendLine(descriptor.ComponentTableInfoCount, " Component table info count");
builder.AppendLine(descriptor.ComponentTableOffset, " Component table offset");
builder.AppendLine(descriptor.Reserved5, " Reserved 5");
builder.AppendLine(descriptor.Reserved6, " Reserved 6");
builder.AppendLine();
builder.AppendLine(" File group offsets:");
builder.AppendLine(" -------------------------");
if (descriptor.FileGroupOffsets == null || descriptor.FileGroupOffsets.Length == 0)
{
builder.AppendLine(" No file group offsets");
}
else
{
for (int i = 0; i < descriptor.FileGroupOffsets.Length; i++)
{
builder.AppendLine(descriptor.FileGroupOffsets[i], $" File Group Offset {i}");
}
}
builder.AppendLine();
builder.AppendLine(" Component offsets:");
builder.AppendLine(" -------------------------");
if (descriptor.ComponentOffsets == null || descriptor.ComponentOffsets.Length == 0)
{
builder.AppendLine(" No component offsets");
}
else
{
for (int i = 0; i < descriptor.ComponentOffsets.Length; i++)
{
builder.AppendLine(descriptor.ComponentOffsets[i], $" Component Offset {i}");
}
}
builder.AppendLine();
builder.AppendLine(descriptor.SetupTypesOffset, " Setup types offset");
builder.AppendLine(descriptor.SetupTableOffset, " Setup table offset");
builder.AppendLine(descriptor.Reserved7, " Reserved 7");
builder.AppendLine(descriptor.Reserved8, " Reserved 8");
builder.AppendLine();
}
private static void Print(StringBuilder builder, uint[]? entries)
{
builder.AppendLine(" File Descriptor Offsets:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No file descriptor offsets");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
builder.AppendLine(entries[i], $" File Descriptor Offset {i}");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, string?[]? entries)
{
builder.AppendLine(" Directory Names:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory names");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
builder.AppendLine(entries[i], $" Directory Name {i}");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FileDescriptor?[]? entries)
{
builder.AppendLine(" File Descriptors:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No file descriptors");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" File Descriptor {i}:");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NameOffset, " Name offset");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.DirectoryIndex, " Directory index");
builder.AppendLine($" Flags: {entry.Flags} (0x{entry.Flags:X})");
builder.AppendLine(entry.ExpandedSize, " Expanded size");
builder.AppendLine(entry.CompressedSize, " Compressed size");
builder.AppendLine(entry.DataOffset, " Data offset");
builder.AppendLine(entry.MD5, " MD5");
builder.AppendLine(entry.Volume, " Volume");
builder.AppendLine(entry.LinkPrevious, " Link previous");
builder.AppendLine(entry.LinkNext, " Link next");
builder.AppendLine($" Link flags: {entry.LinkFlags} (0x{entry.LinkFlags:X})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Dictionary<long, OffsetList?>? entries, string name)
{
builder.AppendLine($" {name} Offsets:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Count == 0)
{
builder.AppendLine($" No {name.ToLowerInvariant()} offsets");
builder.AppendLine();
return;
}
foreach (var kvp in entries)
{
long offset = kvp.Key;
var value = kvp.Value;
builder.AppendLine($" {name} Offset {offset}:");
if (value == null)
{
builder.AppendLine($" Unassigned {name.ToLowerInvariant()}");
continue;
}
builder.AppendLine(value.NameOffset, " Name offset");
builder.AppendLine(value.Name, " Name");
builder.AppendLine(value.DescriptorOffset, " Descriptor offset");
builder.AppendLine(value.NextOffset, " Next offset");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FileGroup?[]? entries)
{
builder.AppendLine(" File Groups:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No file groups");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var fileGroup = entries[i];
builder.AppendLine($" File Group {i}:");
if (fileGroup == null)
{
builder.AppendLine(" Unassigned file group");
continue;
}
builder.AppendLine(fileGroup.NameOffset, " Name offset");
builder.AppendLine(fileGroup.Name, " Name");
builder.AppendLine(fileGroup.ExpandedSize, " Expanded size");
builder.AppendLine(fileGroup.Reserved0, " Reserved 0");
builder.AppendLine(fileGroup.CompressedSize, " Compressed size");
builder.AppendLine(fileGroup.Reserved1, " Reserved 1");
builder.AppendLine(fileGroup.Reserved2, " Reserved 2");
builder.AppendLine(fileGroup.Attribute1, " Attribute 1");
builder.AppendLine(fileGroup.Attribute2, " Attribute 2");
builder.AppendLine(fileGroup.FirstFile, " First file");
builder.AppendLine(fileGroup.LastFile, " Last file");
builder.AppendLine(fileGroup.UnknownOffset, " Unknown offset");
builder.AppendLine(fileGroup.Var4Offset, " Var 4 offset");
builder.AppendLine(fileGroup.Var1Offset, " Var 1 offset");
builder.AppendLine(fileGroup.HTTPLocationOffset, " HTTP location offset");
builder.AppendLine(fileGroup.FTPLocationOffset, " FTP location offset");
builder.AppendLine(fileGroup.MiscOffset, " Misc. offset");
builder.AppendLine(fileGroup.Var2Offset, " Var 2 offset");
builder.AppendLine(fileGroup.TargetDirectoryOffset, " Target directory offset");
builder.AppendLine(fileGroup.Reserved3, " Reserved 3");
builder.AppendLine(fileGroup.Reserved4, " Reserved 4");
builder.AppendLine(fileGroup.Reserved5, " Reserved 5");
builder.AppendLine(fileGroup.Reserved6, " Reserved 6");
builder.AppendLine(fileGroup.Reserved7, " Reserved 7");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Component?[]? entries)
{
builder.AppendLine(" Components:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No components");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var component = entries[i];
builder.AppendLine($" Component {i}:");
if (component == null)
{
builder.AppendLine(" Unassigned component");
continue;
}
builder.AppendLine(component.IdentifierOffset, " Identifier offset");
builder.AppendLine(component.Identifier, " Identifier");
builder.AppendLine(component.DescriptorOffset, " Descriptor offset");
builder.AppendLine(component.DisplayNameOffset, " Display name offset");
builder.AppendLine(component.DisplayName, " Display name");
builder.AppendLine(component.Reserved0, " Reserved 0");
builder.AppendLine(component.ReservedOffset0, " Reserved offset 0");
builder.AppendLine(component.ReservedOffset1, " Reserved offset 1");
builder.AppendLine(component.ComponentIndex, " Component index");
builder.AppendLine(component.NameOffset, " Name offset");
builder.AppendLine(component.Name, " Name");
builder.AppendLine(component.ReservedOffset2, " Reserved offset 2");
builder.AppendLine(component.ReservedOffset3, " Reserved offset 3");
builder.AppendLine(component.ReservedOffset4, " Reserved offset 4");
builder.AppendLine(component.Reserved1, " Reserved 1");
builder.AppendLine(component.CLSIDOffset, " CLSID offset");
builder.AppendLine(component.CLSID, " CLSID");
builder.AppendLine(component.Reserved2, " Reserved 2");
builder.AppendLine(component.Reserved3, " Reserved 3");
builder.AppendLine(component.DependsCount, " Depends count");
builder.AppendLine(component.DependsOffset, " Depends offset");
builder.AppendLine(component.FileGroupCount, " File group count");
builder.AppendLine(component.FileGroupNamesOffset, " File group names offset");
builder.AppendLine();
builder.AppendLine(" File group names:");
builder.AppendLine(" -------------------------");
if (component.FileGroupNames == null || component.FileGroupNames.Length == 0)
{
builder.AppendLine(" No file group names");
}
else
{
for (int j = 0; j < component.FileGroupNames.Length; j++)
{
builder.AppendLine(component.FileGroupNames[j], $" File Group Name {j}");
}
}
builder.AppendLine();
builder.AppendLine(component.X3Count, " X3 count");
builder.AppendLine(component.X3Offset, " X3 offset");
builder.AppendLine(component.SubComponentsCount, " Sub-components count");
builder.AppendLine(component.SubComponentsOffset, " Sub-components offset");
builder.AppendLine(component.NextComponentOffset, " Next component offset");
builder.AppendLine(component.ReservedOffset5, " Reserved offset 5");
builder.AppendLine(component.ReservedOffset6, " Reserved offset 6");
builder.AppendLine(component.ReservedOffset7, " Reserved offset 7");
builder.AppendLine(component.ReservedOffset8, " Reserved offset 8");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,783 @@
using System.Text;
using SabreTools.Models.LinearExecutable;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class LinearExecutable : IPrinter<Executable>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Executable model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Executable executable)
{
builder.AppendLine("New Executable Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Stub
Print(builder, executable.Stub?.Header);
// Information Block
Print(builder, executable.InformationBlock);
// Tables
Print(builder, executable.ObjectTable);
Print(builder, executable.ObjectPageMap);
Print(builder, executable.ResourceTable);
Print(builder, executable.ResidentNamesTable);
Print(builder, executable.EntryTable);
Print(builder, executable.ModuleFormatDirectivesTable);
Print(builder, executable.VerifyRecordDirectiveTable);
Print(builder, executable.FixupPageTable);
Print(builder, executable.FixupRecordTable);
Print(builder, executable.ImportModuleNameTable);
Print(builder, executable.ImportModuleProcedureNameTable);
Print(builder, executable.PerPageChecksumTable);
Print(builder, executable.NonResidentNamesTable);
// Debug
Print(builder, executable.DebugInformation);
}
private static void Print(StringBuilder builder, Models.MSDOS.ExecutableHeader? header)
{
builder.AppendLine(" MS-DOS Stub Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No MS-DOS stub header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Magic, " Magic number");
builder.AppendLine(header.LastPageBytes, " Last page bytes");
builder.AppendLine(header.Pages, " Pages");
builder.AppendLine(header.RelocationItems, " Relocation items");
builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size");
builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs");
builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs");
builder.AppendLine(header.InitialSSValue, " Initial SS value");
builder.AppendLine(header.InitialSPValue, " Initial SP value");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine(header.InitialIPValue, " Initial IP value");
builder.AppendLine(header.InitialCSValue, " Initial CS value");
builder.AppendLine(header.RelocationTableAddr, " Relocation table address");
builder.AppendLine(header.OverlayNumber, " Overlay number");
builder.AppendLine();
builder.AppendLine(" MS-DOS Stub Extended Header Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine(header.Reserved1, " Reserved words");
builder.AppendLine(header.OEMIdentifier, " OEM identifier");
builder.AppendLine(header.OEMInformation, " OEM information");
builder.AppendLine(header.Reserved2, " Reserved words");
builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address");
builder.AppendLine();
}
private static void Print(StringBuilder builder, InformationBlock? block)
{
builder.AppendLine(" Information Block Information:");
builder.AppendLine(" -------------------------");
if (block == null)
{
builder.AppendLine(" No information block");
builder.AppendLine();
return;
}
builder.AppendLine(block.Signature, " Signature");
builder.AppendLine($" Byte order: {block.ByteOrder} (0x{block.ByteOrder:X})");
builder.AppendLine($" Word order: {block.WordOrder} (0x{block.WordOrder:X})");
builder.AppendLine(block.ExecutableFormatLevel, " Executable format level");
builder.AppendLine($" CPU type: {block.CPUType} (0x{block.CPUType:X})");
builder.AppendLine($" Module OS: {block.ModuleOS} (0x{block.ModuleOS:X})");
builder.AppendLine(block.ModuleVersion, " Module version");
builder.AppendLine($" Module type flags: {block.ModuleTypeFlags} (0x{block.ModuleTypeFlags:X})");
builder.AppendLine(block.ModuleNumberPages, " Module number pages");
builder.AppendLine(block.InitialObjectCS, " Initial object CS");
builder.AppendLine(block.InitialEIP, " Initial EIP");
builder.AppendLine(block.InitialObjectSS, " Initial object SS");
builder.AppendLine(block.InitialESP, " Initial ESP");
builder.AppendLine(block.MemoryPageSize, " Memory page size");
builder.AppendLine(block.BytesOnLastPage, " Bytes on last page");
builder.AppendLine(block.FixupSectionSize, " Fix-up section size");
builder.AppendLine(block.FixupSectionChecksum, " Fix-up section checksum");
builder.AppendLine(block.LoaderSectionSize, " Loader section size");
builder.AppendLine(block.LoaderSectionChecksum, " Loader section checksum");
builder.AppendLine(block.ObjectTableOffset, " Object table offset");
builder.AppendLine(block.ObjectTableCount, " Object table count");
builder.AppendLine(block.ObjectPageMapOffset, " Object page map offset");
builder.AppendLine(block.ObjectIterateDataMapOffset, " Object iterate data map offset");
builder.AppendLine(block.ResourceTableOffset, " Resource table offset");
builder.AppendLine(block.ResourceTableCount, " Resource table count");
builder.AppendLine(block.ResidentNamesTableOffset, " Resident names table offset");
builder.AppendLine(block.EntryTableOffset, " Entry table offset");
builder.AppendLine(block.ModuleDirectivesTableOffset, " Module directives table offset");
builder.AppendLine(block.ModuleDirectivesCount, " Module directives table count");
builder.AppendLine(block.FixupPageTableOffset, " Fix-up page table offset");
builder.AppendLine(block.FixupRecordTableOffset, " Fix-up record table offset");
builder.AppendLine(block.ImportedModulesNameTableOffset, " Imported modules name table offset");
builder.AppendLine(block.ImportedModulesCount, " Imported modules count");
builder.AppendLine(block.ImportProcedureNameTableOffset, " Imported procedure name table count");
builder.AppendLine(block.PerPageChecksumTableOffset, " Per-page checksum table offset");
builder.AppendLine(block.DataPagesOffset, " Data pages offset");
builder.AppendLine(block.PreloadPageCount, " Preload page count");
builder.AppendLine(block.NonResidentNamesTableOffset, " Non-resident names table offset");
builder.AppendLine(block.NonResidentNamesTableLength, " Non-resident names table length");
builder.AppendLine(block.NonResidentNamesTableChecksum, " Non-resident names table checksum");
builder.AppendLine(block.AutomaticDataObject, " Automatic data object");
builder.AppendLine(block.DebugInformationOffset, " Debug information offset");
builder.AppendLine(block.DebugInformationLength, " Debug information length");
builder.AppendLine(block.PreloadInstancePagesNumber, " Preload instance pages number");
builder.AppendLine(block.DemandInstancePagesNumber, " Demand instance pages number");
builder.AppendLine(block.ExtraHeapAllocation, " Extra heap allocation");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ObjectTableEntry?[]? entries)
{
builder.AppendLine(" Object Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No object table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Object Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.VirtualSegmentSize, " Virtual segment size");
builder.AppendLine(entry.RelocationBaseAddress, " Relocation base address");
builder.AppendLine($" Object flags: {entry.ObjectFlags} (0x{entry.ObjectFlags:X})");
builder.AppendLine(entry.PageTableIndex, " Page table index");
builder.AppendLine(entry.PageTableEntries, " Page table entries");
builder.AppendLine(entry.Reserved, " Reserved");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ObjectPageMapEntry?[]? entries)
{
builder.AppendLine(" Object Page Map Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No object page map entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Object Page Map Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.PageDataOffset, " Page data offset");
builder.AppendLine(entry.DataSize, " Data size");
builder.AppendLine($" Flags: {entry.Flags} (0x{entry.Flags:X})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ResourceTableEntry?[]? entries)
{
builder.AppendLine(" Resource Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No resource table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Resource Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Type ID: {entry.TypeID} (0x{entry.TypeID:X})");
builder.AppendLine(entry.NameID, " Name ID");
builder.AppendLine(entry.ResourceSize, " Resource size");
builder.AppendLine(entry.ObjectNumber, " Object number");
builder.AppendLine(entry.Offset, " Offset");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ResidentNamesTableEntry?[]? entries)
{
builder.AppendLine(" Resident Names Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No resident names table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Resident Names Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, EntryTableBundle?[]? bundles)
{
builder.AppendLine(" Entry Table Information:");
builder.AppendLine(" -------------------------");
if (bundles == null || bundles.Length == 0)
{
builder.AppendLine(" No entry table bundles");
builder.AppendLine();
return;
}
for (int i = 0; i < bundles.Length; i++)
{
var bundle = bundles[i];
builder.AppendLine($" Entry Table Bundle {i}");
if (bundle == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(bundle.Entries, " Entries");
builder.AppendLine($" Bundle type: {bundle.BundleType} (0x{bundle.BundleType:X})");
builder.AppendLine();
builder.AppendLine(" Entry Table Entries:");
builder.AppendLine(" -------------------------");
if (bundle.TableEntries == null || bundle.TableEntries.Length == 0)
{
builder.AppendLine(" No entry table entries");
builder.AppendLine();
continue;
}
for (int j = 0; j < bundle.TableEntries.Length; j++)
{
var entry = bundle.TableEntries[j];
builder.AppendLine($" Entry Table Entry {j}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent)
{
case BundleType.UnusedEntry:
builder.AppendLine(" Unused, empty entry");
break;
case BundleType.SixteenBitEntry:
builder.AppendLine(entry.SixteenBitObjectNumber, " Object number");
builder.AppendLine($" Entry flags: {entry.SixteenBitEntryFlags} (0x{entry.SixteenBitEntryFlags:X})");
builder.AppendLine(entry.SixteenBitOffset, " Offset");
break;
case BundleType.TwoEightySixCallGateEntry:
builder.AppendLine(entry.TwoEightySixObjectNumber, " Object number");
builder.AppendLine($" Entry flags: {entry.TwoEightySixEntryFlags} (0x{entry.TwoEightySixEntryFlags:X})");
builder.AppendLine(entry.TwoEightySixOffset, " Offset");
builder.AppendLine(entry.TwoEightySixCallgate, " Callgate");
break;
case BundleType.ThirtyTwoBitEntry:
builder.AppendLine(entry.ThirtyTwoBitObjectNumber, " Object number");
builder.AppendLine($" Entry flags: {entry.ThirtyTwoBitEntryFlags} (0x{entry.ThirtyTwoBitEntryFlags:X})");
builder.AppendLine(entry.ThirtyTwoBitOffset, " Offset");
break;
case BundleType.ForwarderEntry:
builder.AppendLine(entry.ForwarderReserved, " Reserved");
builder.AppendLine($" Forwarder flags: {entry.ForwarderFlags} (0x{entry.ForwarderFlags:X})");
builder.AppendLine(entry.ForwarderModuleOrdinalNumber, " Module ordinal number");
builder.AppendLine(entry.ProcedureNameOffset, " Procedure name offset");
builder.AppendLine(entry.ImportOrdinalNumber, " Import ordinal number");
break;
default:
builder.AppendLine($" Unknown entry type {bundle.BundleType}");
break;
}
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ModuleFormatDirectivesTableEntry?[]? entries)
{
builder.AppendLine(" Module Format Directives Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No module format directives table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Moduile Format Directives Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Directive number: {entry.DirectiveNumber} (0x{entry.DirectiveNumber:X})");
builder.AppendLine(entry.DirectiveDataLength, " Directive data length");
builder.AppendLine(entry.DirectiveDataOffset, " Directive data offset");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, VerifyRecordDirectiveTableEntry?[]? entries)
{
builder.AppendLine(" Verify Record Directive Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No verify record directive table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Verify Record Directive Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.EntryCount, " Entry count");
builder.AppendLine(entry.OrdinalIndex, " Ordinal index");
builder.AppendLine(entry.Version, " Version");
builder.AppendLine(entry.ObjectEntriesCount, " Object entries count");
builder.AppendLine(entry.ObjectNumberInModule, " Object number in module");
builder.AppendLine(entry.ObjectLoadBaseAddress, " Object load base address");
builder.AppendLine(entry.ObjectVirtualAddressSize, " Object virtual address size");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FixupPageTableEntry?[]? entries)
{
builder.AppendLine(" Fix-up Page Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No fix-up page table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Fix-up Page Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Offset, " Offset");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FixupRecordTableEntry?[]? entries)
{
builder.AppendLine(" Fix-up Record Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No fix-up record table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Fix-up Record Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Source type: {entry.SourceType} (0x{entry.SourceType:X})");
builder.AppendLine($" Target flags: {entry.TargetFlags} (0x{entry.TargetFlags:X})");
// Source list flag
#if NET20 || NET35
if ((entry.SourceType & FixupRecordSourceType.SourceListFlag) != 0)
#else
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
#endif
builder.AppendLine(entry.SourceOffsetListCount, " Source offset list count");
else
builder.AppendLine(entry.SourceOffset, " Source offset");
// OBJECT / TRGOFF
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReference) != 0)
#else
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference))
#endif
{
// 16-bit Object Number/Module Ordinal Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
#else
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
#endif
builder.AppendLine(entry.TargetObjectNumberWORD, " Target object number");
else
builder.AppendLine(entry.TargetObjectNumberByte, " Target object number");
// 16-bit Selector fixup
#if NET20 || NET35
if ((entry.SourceType & FixupRecordSourceType.SixteenBitSelectorFixup) != 0)
#else
if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup))
#endif
{
// 32-bit Target Offset Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
#else
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
#endif
builder.AppendLine(entry.TargetOffsetDWORD, " Target offset");
else
builder.AppendLine(entry.TargetOffsetWORD, " Target offset");
}
}
// MOD ORD# / IMPORT ORD / ADDITIVE
#if NET20 || NET35
else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByOrdinal) != 0)
#else
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal))
#endif
{
// 16-bit Object Number/Module Ordinal Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
#endif
builder.AppendLine(entry.OrdinalIndexImportModuleNameTableWORD, " Ordinal index import module name table");
else
builder.AppendLine(entry.OrdinalIndexImportModuleNameTableByte, " Ordinal index import module name table");
// 8-bit Ordinal Flag & 32-bit Target Offset Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.EightBitOrdinalFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag))
#endif
builder.AppendLine(entry.ImportedOrdinalNumberByte, " Imported ordinal number");
#if NET20 || NET35
else if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
#else
else if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
#endif
builder.AppendLine(entry.ImportedOrdinalNumberDWORD, " Imported ordinal number");
else
builder.AppendLine(entry.ImportedOrdinalNumberWORD, " Imported ordinal number");
// Additive Fixup Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
#endif
{
// 32-bit Additive Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
#endif
builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value");
else
builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value");
}
}
// MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
#if NET20 || NET35
else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByName) != 0)
#else
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName))
#endif
{
// 16-bit Object Number/Module Ordinal Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
#endif
builder.AppendLine(entry.OrdinalIndexImportModuleNameTableWORD, " Ordinal index import module name table");
else
builder.AppendLine(entry.OrdinalIndexImportModuleNameTableByte, " Ordinal index import module name table");
// 32-bit Target Offset Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
#endif
builder.AppendLine(entry.OffsetImportProcedureNameTableDWORD, " Offset import procedure name table");
else
builder.AppendLine(entry.OffsetImportProcedureNameTableWORD, " Offset import procedure name table");
// Additive Fixup Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
#endif
{
// 32-bit Additive Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
#endif
builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value");
else
builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value");
}
}
// ORD # / ADDITIVE
#if NET20 || NET35
else if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReferenceViaEntryTable) != 0)
#else
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable))
#endif
{
// 16-bit Object Number/Module Ordinal Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
#endif
builder.AppendLine(entry.TargetObjectNumberWORD, " Target object number");
else
builder.AppendLine(entry.TargetObjectNumberByte, " Target object number");
// Additive Fixup Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
#endif
{
// 32-bit Additive Flag
#if NET20 || NET35
if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0)
#else
if (!entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
#endif
builder.AppendLine(entry.AdditiveFixupValueDWORD, " Additive fixup value");
else
builder.AppendLine(entry.AdditiveFixupValueWORD, " Additive fixup value");
}
}
// No other top-level flags recognized
else
{
builder.AppendLine(" Unknown entry format");
}
builder.AppendLine();
builder.AppendLine(" Source Offset List:");
builder.AppendLine(" -------------------------");
if (entry.SourceOffsetList == null || entry.SourceOffsetList.Length == 0)
{
builder.AppendLine(" No source offset list entries");
}
else
{
for (int j = 0; j < entry.SourceOffsetList.Length; j++)
{
builder.AppendLine(entry.SourceOffsetList[j], $" Source Offset List Entry {j}");
}
}
builder.AppendLine();
}
}
private static void Print(StringBuilder builder, ImportModuleNameTableEntry?[]? entries)
{
builder.AppendLine(" Import Module Name Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No import module name table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Import Module Name Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Name, " Name");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ImportModuleProcedureNameTableEntry?[]? entries)
{
builder.AppendLine(" Import Module Procedure Name Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No import module procedure name table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Import Module Procedure Name Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Name, " Name");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, PerPageChecksumTableEntry?[]? entries)
{
builder.AppendLine(" Per-Page Checksum Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No per-page checksum table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Per-Page Checksum Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Checksum, " Checksum");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NonResidentNamesTableEntry?[]? entries)
{
builder.AppendLine(" Non-Resident Names Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No non-resident names table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Non-Resident Names Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DebugInformation? di)
{
builder.AppendLine(" Debug Information:");
builder.AppendLine(" -------------------------");
if (di == null)
{
builder.AppendLine(" No debug information");
builder.AppendLine();
return;
}
builder.AppendLine(di.Signature, " Signature");
builder.AppendLine($" Format type: {di.FormatType} (0x{di.FormatType:X})");
// Debugger data
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Text;
using SabreTools.Models.MSDOS;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class MSDOS : IPrinter<Executable>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Executable model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Executable executable)
{
builder.AppendLine("MS-DOS Executable Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, executable.Header);
Print(builder, executable.RelocationTable);
}
private static void Print(StringBuilder builder, ExecutableHeader? 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.LastPageBytes, " Last page bytes");
builder.AppendLine(header.Pages, " Pages");
builder.AppendLine(header.RelocationItems, " Relocation items");
builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size");
builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs");
builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs");
builder.AppendLine(header.InitialSSValue, " Initial SS value");
builder.AppendLine(header.InitialSPValue, " Initial SP value");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine(header.InitialIPValue, " Initial IP value");
builder.AppendLine(header.InitialCSValue, " Initial CS value");
builder.AppendLine(header.RelocationTableAddr, " Relocation table address");
builder.AppendLine(header.OverlayNumber, " Overlay number");
builder.AppendLine();
}
private static void Print(StringBuilder builder, RelocationEntry?[]? entries)
{
builder.AppendLine(" Relocation Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No relocation table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Relocation Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Offset, " Offset");
builder.AppendLine(entry.Segment, " Segment");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,172 @@
using System.Text;
using SabreTools.Models.MicrosoftCabinet;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class MicrosoftCabinet : IPrinter<Cabinet>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Cabinet model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Cabinet cabinet)
{
builder.AppendLine("Microsoft Cabinet Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, cabinet.Header);
Print(builder, cabinet.Folders);
Print(builder, cabinet.Files);
}
private static void Print(StringBuilder builder, CFHEADER? header)
{
builder.AppendLine(" Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.Reserved1, " Reserved 1");
builder.AppendLine(header.CabinetSize, " Cabinet size");
builder.AppendLine(header.Reserved2, " Reserved 2");
builder.AppendLine(header.FilesOffset, " Files offset");
builder.AppendLine(header.Reserved3, " Reserved 3");
builder.AppendLine(header.VersionMinor, " Minor version");
builder.AppendLine(header.VersionMajor, " Major version");
builder.AppendLine(header.FolderCount, " Folder count");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine($" Flags: {header.Flags} (0x{header.Flags:X})");
builder.AppendLine(header.SetID, " Set ID");
builder.AppendLine(header.CabinetIndex, " Cabinet index");
#if NET20 || NET35
if ((header.Flags & HeaderFlags.RESERVE_PRESENT) != 0)
#else
if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
#endif
{
builder.AppendLine(header.HeaderReservedSize, " Header reserved size");
builder.AppendLine(header.FolderReservedSize, " Folder reserved size");
builder.AppendLine(header.DataReservedSize, " Data reserved size");
builder.AppendLine(header.ReservedData, " Reserved data");
}
#if NET20 || NET35
if ((header.Flags & HeaderFlags.PREV_CABINET) != 0)
#else
if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET))
#endif
{
builder.AppendLine(header.CabinetPrev, " Previous cabinet");
builder.AppendLine(header.DiskPrev, " Previous disk");
}
#if NET20 || NET35
if ((header.Flags & HeaderFlags.NEXT_CABINET) != 0)
#else
if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET))
#endif
{
builder.AppendLine(header.CabinetNext, " Next cabinet");
builder.AppendLine(header.DiskNext, " Next disk");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, CFFOLDER?[]? entries)
{
builder.AppendLine(" Folders:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No folders");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Folder {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.CabStartOffset, " Cab start offset");
builder.AppendLine(entry.DataCount, " Data count");
builder.AppendLine($" Compression type: {entry.CompressionType} (0x{entry.CompressionType:X})");
builder.AppendLine($" Masked compression type: {entry.CompressionType & CompressionType.MASK_TYPE}");
builder.AppendLine(entry.ReservedData, " Reserved data");
builder.AppendLine();
builder.AppendLine(" Data Blocks");
builder.AppendLine(" -------------------------");
if (entry.DataBlocks == null || entry.DataBlocks.Length == 0)
{
builder.AppendLine(" No data blocks");
continue;
}
for (int j = 0; j < entry.DataBlocks.Length; j++)
{
var dataBlock = entry.DataBlocks[j];
builder.AppendLine($" Data Block {j}");
if (dataBlock == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(dataBlock.Checksum, " Checksum");
builder.AppendLine(dataBlock.CompressedSize, " Compressed size");
builder.AppendLine(dataBlock.UncompressedSize, " Uncompressed size");
builder.AppendLine(dataBlock.ReservedData, " Reserved data");
//builder.AppendLine(dataBlock.CompressedData, " Compressed data");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, CFFILE?[]? 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}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FileSize, " File size");
builder.AppendLine(entry.FolderStartOffset, " Folder start offset");
builder.AppendLine($" Folder index: {entry.FolderIndex} (0x{entry.FolderIndex:X})");
builder.AppendLine(entry.Date, " Date");
builder.AppendLine(entry.Time, " Time");
builder.AppendLine($" Attributes: {entry.Attributes} (0x{entry.Attributes:X})");
builder.AppendLine(entry.Name, " Name");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,243 @@
using System.Text;
using SabreTools.Models.MoPaQ;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class MoPaQ : IPrinter<Archive>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Archive model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Archive archive)
{
builder.AppendLine("MoPaQ Archive Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, archive.UserData);
Print(builder, archive.ArchiveHeader);
Print(builder, archive.HetTable);
Print(builder, archive.BetTable);
Print(builder, archive.HashTable);
Print(builder, archive.BlockTable);
Print(builder, archive.HiBlockTable);
}
private static void Print(StringBuilder builder, UserData? userData)
{
builder.AppendLine(" User Data Information:");
builder.AppendLine(" -------------------------");
if (userData == null)
{
builder.AppendLine(" No user data");
builder.AppendLine();
return;
}
builder.AppendLine(userData.Signature, " Signature");
builder.AppendLine(userData.UserDataSize, " User data size");
builder.AppendLine(userData.HeaderOffset, " Header offset");
builder.AppendLine(userData.UserDataHeaderSize, " User data header size");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ArchiveHeader? header)
{
builder.AppendLine(" Archive Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No archive header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.HeaderSize, " Header size");
builder.AppendLine(header.ArchiveSize, " Archive size");
builder.AppendLine($" Format version: {header.FormatVersion} (0x{header.FormatVersion:X})");
builder.AppendLine(header.BlockSize, " Block size");
builder.AppendLine(header.HashTablePosition, " Hash table position");
builder.AppendLine(header.BlockTablePosition, " Block table position");
builder.AppendLine(header.HashTableSize, " Hash table size");
builder.AppendLine(header.BlockTableSize, " Block table size");
builder.AppendLine(header.HiBlockTablePosition, " Hi-block table position");
builder.AppendLine(header.HashTablePositionHi, " Hash table position hi");
builder.AppendLine(header.BlockTablePositionHi, " Block table position hi");
builder.AppendLine(header.ArchiveSizeLong, " Archive size long");
builder.AppendLine(header.BetTablePosition, " BET table position");
builder.AppendLine(header.HetTablePosition, " HET table position");
builder.AppendLine(header.HashTableSizeLong, " Hash table size long");
builder.AppendLine(header.BlockTableSizeLong, " Block table size long");
builder.AppendLine(header.HiBlockTableSize, " Hi-block table size");
builder.AppendLine(header.HetTableSize, " HET table size");
builder.AppendLine(header.BetTablesize, " BET table size"); // TODO: Fix casing
builder.AppendLine(header.RawChunkSize, " Raw chunk size");
builder.AppendLine(header.BlockTableMD5, " Block table MD5");
builder.AppendLine(header.HashTableMD5, " Hash table MD5");
builder.AppendLine(header.HiBlockTableMD5, " Hi-block table MD5");
builder.AppendLine(header.BetTableMD5, " BET table MD5");
builder.AppendLine(header.HetTableMD5, " HET table MD5");
builder.AppendLine(header.MpqHeaderMD5, " MPQ header MD5");
builder.AppendLine();
}
private static void Print(StringBuilder builder, HetTable? table)
{
builder.AppendLine(" HET Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
{
builder.AppendLine(" No HET table");
builder.AppendLine();
return;
}
builder.AppendLine(table.Signature, " Signature");
builder.AppendLine(table.Version, " Version");
builder.AppendLine(table.DataSize, " Data size");
builder.AppendLine(table.TableSize, " Table size");
builder.AppendLine(table.MaxFileCount, " Max file count");
builder.AppendLine(table.HashTableSize, " Hash table size");
builder.AppendLine(table.HashEntrySize, " Hash entry size");
builder.AppendLine(table.TotalIndexSize, " Total index size");
builder.AppendLine(table.IndexSizeExtra, " Index size extra");
builder.AppendLine(table.IndexSize, " Index size");
builder.AppendLine(table.BlockTableSize, " Block table size");
builder.AppendLine(table.HashTable, " Hash table");
builder.AppendLine(" File indexes:");
builder.AppendLine(" -------------------------");
if (table.FileIndexes == null)
{
builder.AppendLine(" No file indexes ");
}
else
{
for (int i = 0; i < table.FileIndexes.Length; i++)
{
builder.AppendLine(table.FileIndexes[i], $" File index {i}");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, BetTable? table)
{
builder.AppendLine(" BET Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
{
builder.AppendLine(" No BET table");
builder.AppendLine();
return;
}
builder.AppendLine(table.Signature, " Signature");
builder.AppendLine(table.Version, " Version");
builder.AppendLine(table.DataSize, " Data size");
builder.AppendLine(table.TableSize, " Table size");
builder.AppendLine(table.FileCount, " File count");
builder.AppendLine(table.Unknown, " Unknown");
builder.AppendLine(table.TableEntrySize, " Table entry size");
builder.AppendLine(table.FilePositionBitIndex, " File position bit index");
builder.AppendLine(table.FileSizeBitIndex, " File size bit index");
builder.AppendLine(table.CompressedSizeBitIndex, " Compressed size bit index");
builder.AppendLine(table.FlagIndexBitIndex, " Flag index bit index");
builder.AppendLine(table.UnknownBitIndex, " Unknown bit index");
builder.AppendLine(table.FilePositionBitCount, " File position bit count");
builder.AppendLine(table.FileSizeBitCount, " File size bit count");
builder.AppendLine(table.CompressedSizeBitCount, " Compressed size bit count");
builder.AppendLine(table.FlagIndexBitCount, " Flag index bit count");
builder.AppendLine(table.UnknownBitCount, " Unknown bit count");
builder.AppendLine(table.TotalBetHashSize, " Total BET hash size");
builder.AppendLine(table.BetHashSizeExtra, " BET hash size extra");
builder.AppendLine(table.BetHashSize, " BET hash size");
builder.AppendLine(table.BetHashArraySize, " BET hash array size");
builder.AppendLine(table.FlagCount, " Flag count");
builder.AppendLine(table.FlagsArray, " Flags array");
builder.AppendLine();
}
private static void Print(StringBuilder builder, HashEntry?[]? entries)
{
builder.AppendLine(" Hash Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No hash table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Hash Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NameHashPartA, " Name hash, part A");
builder.AppendLine(entry.NameHashPartB, " Name hash, part B");
builder.AppendLine($" Locale: {entry.Locale} (0x{entry.Locale:X})");
builder.AppendLine(entry.Platform, " Platform");
builder.AppendLine(entry.BlockIndex, " BlockIndex");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, BlockEntry?[]? entries)
{
builder.AppendLine(" Block Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No block table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Block Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FilePosition, " File position");
builder.AppendLine(entry.CompressedSize, " Compressed size");
builder.AppendLine(entry.UncompressedSize, " Uncompressed size");
builder.AppendLine($" Flags: {entry.Flags} (0x{entry.Flags:X})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, short[]? entries)
{
builder.AppendLine(" Hi-block Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No hi-block table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Hi-block Table Entry {i}: {entry}");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,646 @@
using System.Text;
using SabreTools.Models.N3DS;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class N3DS : IPrinter<Cart>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Cart model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Cart cart)
{
builder.AppendLine("3DS Cart Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, cart.Header);
Print(builder, cart.CardInfoHeader);
Print(builder, cart.DevelopmentCardInfoHeader);
Print(builder, cart.Partitions);
Print(builder, cart.ExtendedHeaders);
Print(builder, cart.ExeFSHeaders);
Print(builder, cart.RomFSHeaders);
}
private static void Print(StringBuilder builder, NCSDHeader? header)
{
builder.AppendLine(" NCSD Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No NCSD header");
builder.AppendLine();
return;
}
builder.AppendLine(header.RSA2048Signature, " RSA-2048 SHA-256 signature");
builder.AppendLine(header.MagicNumber, " Magic number");
builder.AppendLine(header.ImageSizeInMediaUnits, " Image size in media units");
builder.AppendLine(header.MediaId, " Media ID");
builder.AppendLine($" Partitions filesystem type: {header.PartitionsFSType} (0x{header.PartitionsFSType:X})");
builder.AppendLine(header.PartitionsCryptType, " Partitions crypt type");
builder.AppendLine();
builder.AppendLine(" Partition table:");
builder.AppendLine(" -------------------------");
if (header.PartitionsTable == null || header.PartitionsTable.Length == 0)
{
builder.AppendLine(" No partition table entries");
}
else
{
for (int i = 0; i < header.PartitionsTable.Length; i++)
{
var partitionTableEntry = header.PartitionsTable[i];
builder.AppendLine($" Partition table entry {i}");
if (partitionTableEntry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(partitionTableEntry.Offset, " Offset");
builder.AppendLine(partitionTableEntry.Length, " Length");
}
}
builder.AppendLine();
// If we have a cart image
if (header.PartitionsFSType == FilesystemType.Normal || header.PartitionsFSType == FilesystemType.None)
{
builder.AppendLine(header.ExheaderHash, " Exheader SHA-256 hash");
builder.AppendLine(header.AdditionalHeaderSize, " Additional header size");
builder.AppendLine(header.SectorZeroOffset, " Sector zero offset");
builder.AppendLine(header.PartitionFlags, " Partition flags");
builder.AppendLine();
builder.AppendLine(" Partition ID table:");
builder.AppendLine(" -------------------------");
if (header.PartitionIdTable == null || header.PartitionIdTable.Length == 0)
{
builder.AppendLine(" No partition ID table entries");
}
else
{
for (int i = 0; i < header.PartitionIdTable.Length; i++)
{
builder.AppendLine(header.PartitionIdTable[i], $" Partition {i} ID");
}
}
builder.AppendLine();
builder.AppendLine(header.Reserved1, " Reserved 1");
builder.AppendLine(header.Reserved2, " Reserved 2");
builder.AppendLine(header.FirmUpdateByte1, " Firmware update byte 1");
builder.AppendLine(header.FirmUpdateByte2, " Firmware update byte 2");
}
// If we have a firmware image
else if (header.PartitionsFSType == FilesystemType.FIRM)
{
builder.AppendLine(header.Unknown, " Unknown");
builder.AppendLine(header.EncryptedMBR, " Encrypted MBR");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, CardInfoHeader? header)
{
builder.AppendLine(" Card Info Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No card info header");
builder.AppendLine();
return;
}
builder.AppendLine(header.WritableAddressMediaUnits, " Writable address in media units");
builder.AppendLine(header.CardInfoBitmask, " Card info bitmask");
builder.AppendLine(header.Reserved1, " Reserved 1");
builder.AppendLine(header.FilledSize, " Filled size of cartridge");
builder.AppendLine(header.Reserved2, " Reserved 2");
builder.AppendLine(header.TitleVersion, " Title version");
builder.AppendLine(header.CardRevision, " Card revision");
builder.AppendLine(header.Reserved3, " Reserved 3");
builder.AppendLine(header.CVerTitleID, " Title ID of CVer in included update partition");
builder.AppendLine(header.CVerVersionNumber, " Version number of CVer in included update partition");
builder.AppendLine(header.Reserved4, " Reserved 4");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DevelopmentCardInfoHeader? header)
{
builder.AppendLine(" Development Card Info Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No development card info header");
builder.AppendLine();
return;
}
builder.AppendLine();
builder.AppendLine(" Initial Data:");
builder.AppendLine(" -------------------------");
if (header.InitialData == null)
{
builder.AppendLine(" No initial data");
}
else
{
builder.AppendLine(header.InitialData.CardSeedKeyY, " Card seed keyY");
builder.AppendLine(header.InitialData.EncryptedCardSeed, " Encrypted card seed");
builder.AppendLine(header.InitialData.CardSeedAESMAC, " Card seed AES-MAC");
builder.AppendLine(header.InitialData.CardSeedNonce, " Card seed nonce");
builder.AppendLine(header.InitialData.Reserved, " Reserved");
builder.AppendLine();
builder.AppendLine(" Backup Header:");
builder.AppendLine(" -------------------------");
if (header.InitialData.BackupHeader == null)
{
builder.AppendLine(" No backup header");
}
else
{
builder.AppendLine(header.InitialData.BackupHeader.MagicID, " Magic ID");
builder.AppendLine(header.InitialData.BackupHeader.ContentSizeInMediaUnits, " Content size in media units");
builder.AppendLine(header.InitialData.BackupHeader.PartitionId, " Partition ID");
builder.AppendLine(header.InitialData.BackupHeader.MakerCode, " Maker code");
builder.AppendLine(header.InitialData.BackupHeader.Version, " Version");
builder.AppendLine(header.InitialData.BackupHeader.VerificationHash, " Verification hash");
builder.AppendLine(header.InitialData.BackupHeader.ProgramId, " Program ID");
builder.AppendLine(header.InitialData.BackupHeader.Reserved1, " Reserved 1");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionHash, " Logo region SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.ProductCode, " Product code");
builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderHash, " Extended header SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.ExtendedHeaderSizeInBytes, " Extended header size in bytes");
builder.AppendLine(header.InitialData.BackupHeader.Reserved2, " Reserved 2");
builder.AppendLine($" Flags: {header.InitialData.BackupHeader.Flags} (0x{header.InitialData.BackupHeader.Flags:X})");
builder.AppendLine(header.InitialData.BackupHeader.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.PlainRegionSizeInMediaUnits, " Plain region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.LogoRegionSizeInMediaUnits, " Logo region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSSizeInMediaUnits, " ExeFS size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.Reserved3, " Reserved 3");
builder.AppendLine(header.InitialData.BackupHeader.RomFSOffsetInMediaUnits, " RomFS offset, in media units");
builder.AppendLine(header.InitialData.BackupHeader.RomFSSizeInMediaUnits, " RomFS size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units");
builder.AppendLine(header.InitialData.BackupHeader.Reserved4, " Reserved 4");
builder.AppendLine(header.InitialData.BackupHeader.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash");
builder.AppendLine(header.InitialData.BackupHeader.RomFSSuperblockHash, " RomFS superblock SHA-256 hash");
}
}
builder.AppendLine();
builder.AppendLine(header.CardDeviceReserved1, " Card device reserved 1");
builder.AppendLine(header.TitleKey, " Title key");
builder.AppendLine(header.CardDeviceReserved2, " Card device reserved 2");
builder.AppendLine();
builder.AppendLine(" Test Data:");
builder.AppendLine(" -------------------------");
if (header.TestData == null)
{
builder.AppendLine(" No test data");
}
else
{
builder.AppendLine(header.TestData.Signature, " Signature");
builder.AppendLine(header.TestData.AscendingByteSequence, " Ascending byte sequence");
builder.AppendLine(header.TestData.DescendingByteSequence, " Descending byte sequence");
builder.AppendLine(header.TestData.Filled00, " Filled with 00");
builder.AppendLine(header.TestData.FilledFF, " Filled with FF");
builder.AppendLine(header.TestData.Filled0F, " Filled with 0F");
builder.AppendLine(header.TestData.FilledF0, " Filled with F0");
builder.AppendLine(header.TestData.Filled55, " Filled with 55");
builder.AppendLine(header.TestData.FilledAA, " Filled with AA");
builder.AppendLine(header.TestData.FinalByte, " Final byte");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NCCHHeader?[]? entries)
{
builder.AppendLine(" NCCH Partition Header Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No NCCH partition headers");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" NCCH Partition Header {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
if (entry.MagicID == string.Empty)
{
builder.AppendLine(" Empty partition, no data can be parsed");
}
else if (entry.MagicID != Constants.NCCHMagicNumber)
{
builder.AppendLine(" Unrecognized partition data, no data can be parsed");
}
else
{
builder.AppendLine(entry.RSA2048Signature, " RSA-2048 SHA-256 signature");
builder.AppendLine(entry.MagicID, " Magic ID");
builder.AppendLine(entry.ContentSizeInMediaUnits, " Content size in media units");
builder.AppendLine(entry.PartitionId, " Partition ID");
builder.AppendLine(entry.MakerCode, " Maker code");
builder.AppendLine(entry.Version, " Version");
builder.AppendLine(entry.VerificationHash, " Verification hash");
builder.AppendLine(entry.ProgramId, " Program ID");
builder.AppendLine(entry.Reserved1, " Reserved 1");
builder.AppendLine(entry.LogoRegionHash, " Logo region SHA-256 hash");
builder.AppendLine(entry.ProductCode, " Product code");
builder.AppendLine(entry.ExtendedHeaderHash, " Extended header SHA-256 hash");
builder.AppendLine(entry.ExtendedHeaderSizeInBytes, " Extended header size in bytes");
builder.AppendLine(entry.Reserved2, " Reserved 2");
builder.AppendLine(" Flags:");
if (entry.Flags == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.Flags.Reserved0, " Reserved 0");
builder.AppendLine(entry.Flags.Reserved1, " Reserved 1");
builder.AppendLine(entry.Flags.Reserved2, " Reserved 2");
builder.AppendLine($" Crypto method: {entry.Flags.CryptoMethod} (0x{entry.Flags.CryptoMethod:X})");
builder.AppendLine($" Content platform: {entry.Flags.ContentPlatform} (0x{entry.Flags.ContentPlatform:X})");
builder.AppendLine($" Content type: {entry.Flags.MediaPlatformIndex} (0x{entry.Flags.MediaPlatformIndex:X})");
builder.AppendLine(entry.Flags.ContentUnitSize, " Content unit size");
builder.AppendLine($" Bitmasks: {entry.Flags.BitMasks} (0x{entry.Flags.BitMasks:X})");
}
builder.AppendLine(entry.PlainRegionOffsetInMediaUnits, " Plain region offset, in media units");
builder.AppendLine(entry.PlainRegionSizeInMediaUnits, " Plain region size, in media units");
builder.AppendLine(entry.LogoRegionOffsetInMediaUnits, " Logo region offset, in media units");
builder.AppendLine(entry.LogoRegionSizeInMediaUnits, " Logo region size, in media units");
builder.AppendLine(entry.ExeFSOffsetInMediaUnits, " ExeFS offset, in media units");
builder.AppendLine(entry.ExeFSSizeInMediaUnits, " ExeFS size, in media units");
builder.AppendLine(entry.ExeFSHashRegionSizeInMediaUnits, " ExeFS hash region size, in media units");
builder.AppendLine(entry.Reserved3, " Reserved 3");
builder.AppendLine(entry.RomFSOffsetInMediaUnits, " RomFS offset, in media units");
builder.AppendLine(entry.RomFSSizeInMediaUnits, " RomFS size, in media units");
builder.AppendLine(entry.RomFSHashRegionSizeInMediaUnits, " RomFS hash region size, in media units");
builder.AppendLine(entry.Reserved4, " Reserved 4");
builder.AppendLine(entry.ExeFSSuperblockHash, " ExeFS superblock SHA-256 hash");
builder.AppendLine(entry.RomFSSuperblockHash, " RomFS superblock SHA-256 hash");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NCCHExtendedHeader?[]? entries)
{
builder.AppendLine(" NCCH Extended Header Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No NCCH extended headers");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" NCCH Extended Header {i}");
if (entry == null)
{
builder.AppendLine(" Unrecognized partition data, no data can be parsed");
continue;
}
builder.AppendLine(" System control info:");
if (entry.SCI == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.SCI.ApplicationTitle, " Application title");
builder.AppendLine(entry.SCI.Reserved1, " Reserved 1");
builder.AppendLine(entry.SCI.Flag, " Flag");
builder.AppendLine(entry.SCI.RemasterVersion, " Remaster version");
builder.AppendLine(" Text code set info:");
if (entry.SCI.TextCodeSetInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.SCI.TextCodeSetInfo.Address, " Address");
builder.AppendLine(entry.SCI.TextCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)");
builder.AppendLine(entry.SCI.TextCodeSetInfo.SizeInBytes, " Size (in bytes)");
}
builder.AppendLine(entry.SCI.StackSize, " Stack size");
builder.AppendLine(" Read-only code set info:");
if (entry.SCI.ReadOnlyCodeSetInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.Address, " Address");
builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)");
builder.AppendLine(entry.SCI.ReadOnlyCodeSetInfo.SizeInBytes, " Size (in bytes)");
}
builder.AppendLine(entry.SCI.Reserved2, " Reserved 2");
builder.AppendLine(" Data code set info:");
if (entry.SCI.DataCodeSetInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.SCI.DataCodeSetInfo.Address, " Address");
builder.AppendLine(entry.SCI.DataCodeSetInfo.PhysicalRegionSizeInPages, " Physical region size (in page-multiples)");
builder.AppendLine(entry.SCI.DataCodeSetInfo.SizeInBytes, " Size (in bytes)");
}
builder.AppendLine(entry.SCI.BSSSize, " BSS size");
builder.AppendLine(entry.SCI.DependencyModuleList, " Dependency module list");
builder.AppendLine(" System info:");
if (entry.SCI.SystemInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.SCI.SystemInfo.SaveDataSize, " SaveData size");
builder.AppendLine(entry.SCI.SystemInfo.JumpID, " Jump ID");
builder.AppendLine(entry.SCI.SystemInfo.Reserved, " Reserved");
}
}
builder.AppendLine(" Access control info:");
if (entry.ACI == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(" ARM11 local system capabilities:");
if (entry.ACI.ARM11LocalSystemCapabilities == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ProgramID, " Program ID");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.CoreVersion, " Core version");
builder.AppendLine($" Flag 1: {entry.ACI.ARM11LocalSystemCapabilities.Flag1} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag1:X})");
builder.AppendLine($" Flag 2: {entry.ACI.ARM11LocalSystemCapabilities.Flag2} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag2:X})");
builder.AppendLine($" Flag 0: {entry.ACI.ARM11LocalSystemCapabilities.Flag0} (0x{entry.ACI.ARM11LocalSystemCapabilities.Flag0:X})");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.Priority, " Priority");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitDescriptors, " Resource limit descriptors");
builder.AppendLine(" Storage info:");
if (entry.ACI.ARM11LocalSystemCapabilities.StorageInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.ExtdataID, " Extdata ID");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.SystemSavedataIDs, " System savedata IDs");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.StorageAccessibleUniqueIDs, " Storage accessible unique IDs");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.FileSystemAccessInfo, " File system access info");
builder.AppendLine($" Other attributes: {entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes} (0x{entry.ACI.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes:X})");
}
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ServiceAccessControl, " Service access control");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.ExtendedServiceAccessControl, " Extended service access control");
builder.AppendLine(entry.ACI.ARM11LocalSystemCapabilities.Reserved, " Reserved");
builder.AppendLine($" Resource limit cateogry: {entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitCategory} (0x{entry.ACI.ARM11LocalSystemCapabilities.ResourceLimitCategory:X})");
}
builder.AppendLine(" ARM11 kernel capabilities:");
if (entry.ACI.ARM11KernelCapabilities == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACI.ARM11KernelCapabilities.Descriptors, " Descriptors");
builder.AppendLine(entry.ACI.ARM11KernelCapabilities.Reserved, " Reserved");
}
builder.AppendLine(" ARM9 access control:");
if (entry.ACI.ARM9AccessControl == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACI.ARM9AccessControl.Descriptors, " Descriptors");
builder.AppendLine(entry.ACI.ARM9AccessControl.DescriptorVersion, " Descriptor version");
}
builder.AppendLine(entry.AccessDescSignature, " AccessDec signature (RSA-2048-SHA256)");
builder.AppendLine(entry.NCCHHDRPublicKey, " NCCH HDR RSA-2048 public key");
}
builder.AppendLine(" Access control info (for limitations of first ACI):");
if (entry.ACIForLimitations == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(" ARM11 local system capabilities:");
if (entry.ACIForLimitations.ARM11LocalSystemCapabilities == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ProgramID, " Program ID");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.CoreVersion, " Core version");
builder.AppendLine($" Flag 1: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag1} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag1:X})");
builder.AppendLine($" Flag 2: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag2} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag2:X})");
builder.AppendLine($" Flag 0: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag0} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.Flag0:X})");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.Priority, " Priority");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitDescriptors, " Resource limit descriptors");
builder.AppendLine(" Storage info:");
if (entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.ExtdataID, " Extdata ID");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.SystemSavedataIDs, " System savedata IDs");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.StorageAccessibleUniqueIDs, " Storage accessible unique IDs");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.FileSystemAccessInfo, " File system access info");
builder.AppendLine($" Other attributes: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.StorageInfo.OtherAttributes:X})");
}
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ServiceAccessControl, " Service access control");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.ExtendedServiceAccessControl, " Extended service access control");
builder.AppendLine(entry.ACIForLimitations.ARM11LocalSystemCapabilities.Reserved, " Reserved");
builder.AppendLine($" Resource limit cateogry: {entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitCategory} (0x{entry.ACIForLimitations.ARM11LocalSystemCapabilities.ResourceLimitCategory:X})");
}
builder.AppendLine(" ARM11 kernel capabilities:");
if (entry.ACIForLimitations.ARM11KernelCapabilities == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACIForLimitations.ARM11KernelCapabilities.Descriptors, " Descriptors");
builder.AppendLine(entry.ACIForLimitations.ARM11KernelCapabilities.Reserved, " Reserved");
}
builder.AppendLine(" ARM9 access control:");
if (entry.ACIForLimitations.ARM9AccessControl == null)
{
builder.AppendLine(" [NULL]");
}
else
{
builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.Descriptors, " Descriptors");
builder.AppendLine(entry.ACIForLimitations.ARM9AccessControl.DescriptorVersion, " Descriptor version");
}
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ExeFSHeader?[]? entries)
{
builder.AppendLine(" ExeFS Header Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No ExeFS headers");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" ExeFS Header {i}");
if (entry == null)
{
builder.AppendLine(" Unrecognized partition data, no data can be parsed");
continue;
}
builder.AppendLine(" File headers:");
if (entry.FileHeaders == null || entry.FileHeaders.Length == 0)
{
builder.AppendLine(" No file headers");
}
else
{
for (int j = 0; j < entry.FileHeaders.Length; j++)
{
var fileHeader = entry.FileHeaders[j];
builder.AppendLine($" File Header {j}");
if (fileHeader == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(fileHeader.FileName, " File name");
builder.AppendLine(fileHeader.FileOffset, " File offset");
builder.AppendLine(fileHeader.FileSize, " File size");
}
}
builder.AppendLine(entry.Reserved, " Reserved");
builder.AppendLine(" File hashes:");
if (entry.FileHashes == null || entry.FileHashes.Length == 0)
{
builder.AppendLine(" No file hashes");
}
else
{
for (int j = 0; j < entry.FileHashes.Length; j++)
{
var fileHash = entry.FileHashes[j];
builder.AppendLine($" File Hash {j}");
if (fileHash == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(fileHash, " SHA-256");
}
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, RomFSHeader?[]? entries)
{
builder.AppendLine(" RomFS Header Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No RomFS headers");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var romFSHeader = entries[i];
builder.AppendLine($" RomFS Header {i}");
if (romFSHeader == null)
{
builder.AppendLine(" Unrecognized RomFS data, no data can be parsed");
continue;
}
builder.AppendLine(romFSHeader.MagicString, " Magic string");
builder.AppendLine(romFSHeader.MagicNumber, " Magic number");
builder.AppendLine(romFSHeader.MasterHashSize, " Master hash size");
builder.AppendLine(romFSHeader.Level1LogicalOffset, " Level 1 logical offset");
builder.AppendLine(romFSHeader.Level1HashdataSize, " Level 1 hashdata size");
builder.AppendLine(romFSHeader.Level1BlockSizeLog2, " Level 1 block size");
builder.AppendLine(romFSHeader.Reserved1, " Reserved 1");
builder.AppendLine(romFSHeader.Level2LogicalOffset, " Level 2 logical offset");
builder.AppendLine(romFSHeader.Level2HashdataSize, " Level 2 hashdata size");
builder.AppendLine(romFSHeader.Level2BlockSizeLog2, " Level 2 block size");
builder.AppendLine(romFSHeader.Reserved2, " Reserved 2");
builder.AppendLine(romFSHeader.Level3LogicalOffset, " Level 3 logical offset");
builder.AppendLine(romFSHeader.Level3HashdataSize, " Level 3 hashdata size");
builder.AppendLine(romFSHeader.Level3BlockSizeLog2, " Level 3 block size");
builder.AppendLine(romFSHeader.Reserved3, " Reserved 3");
builder.AppendLine(romFSHeader.Reserved4, " Reserved 4");
builder.AppendLine(romFSHeader.OptionalInfoSize, " Optional info size");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,360 @@
using System.Collections.Generic;
using System.Text;
using SabreTools.Models.NCF;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class NCF : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("NCF Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Header
Print(builder, file.Header);
// Directory and Directory Maps
Print(builder, file.DirectoryHeader);
Print(builder, file.DirectoryEntries, file.DirectoryNames);
// TODO: Should we print out the entire string table?
Print(builder, file.DirectoryInfo1Entries);
Print(builder, file.DirectoryInfo2Entries);
Print(builder, file.DirectoryCopyEntries);
Print(builder, file.DirectoryLocalEntries);
Print(builder, file.UnknownHeader);
Print(builder, file.UnknownEntries);
// Checksums and Checksum Maps
Print(builder, file.ChecksumHeader);
Print(builder, file.ChecksumMapHeader);
Print(builder, file.ChecksumMapEntries);
Print(builder, file.ChecksumEntries);
}
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.Dummy0, " Dummy 0");
builder.AppendLine(header.MajorVersion, " Major version");
builder.AppendLine(header.MinorVersion, " Minor version");
builder.AppendLine(header.CacheID, " Cache ID");
builder.AppendLine(header.LastVersionPlayed, " Last version played");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.Dummy2, " Dummy 2");
builder.AppendLine(header.FileSize, " File size");
builder.AppendLine(header.BlockSize, " Block size");
builder.AppendLine(header.BlockCount, " Block count");
builder.AppendLine(header.Dummy3, " Dummy 3");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryHeader? header)
{
builder.AppendLine(" Directory Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.CacheID, " Cache ID");
builder.AppendLine(header.LastVersionPlayed, " Last version played");
builder.AppendLine(header.ItemCount, " Item count");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.ChecksumDataLength, " Checksum data length");
builder.AppendLine(header.DirectorySize, " Directory size");
builder.AppendLine(header.NameSize, " Name size");
builder.AppendLine(header.Info1Count, " Info 1 count");
builder.AppendLine(header.CopyCount, " Copy count");
builder.AppendLine(header.LocalCount, " Local count");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.Dummy2, " Dummy 2");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryEntry?[]? entries, Dictionary<long, string?>? entryNames)
{
builder.AppendLine(" Directory Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.NameOffset, " Name offset");
builder.AppendLine(entryNames![entry.NameOffset], " Name");
builder.AppendLine(entry.ItemSize, " Item size");
builder.AppendLine(entry.ChecksumIndex, " Checksum index");
builder.AppendLine($" Directory flags: {entry.DirectoryFlags} (0x{entry.DirectoryFlags:X})");
builder.AppendLine(entry.ParentIndex, " Parent index");
builder.AppendLine(entry.NextIndex, " Next index");
builder.AppendLine(entry.FirstIndex, " First index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryInfo1Entry?[]? entries)
{
builder.AppendLine(" Directory Info 1 Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory info 1 entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Info 1 Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryInfo2Entry?[]? entries)
{
builder.AppendLine(" Directory Info 2 Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory info 2 entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Info 2 Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryCopyEntry?[]? entries)
{
builder.AppendLine(" Directory Copy Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory copy entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Copy Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.DirectoryIndex, " Directory index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryLocalEntry?[]? entries)
{
builder.AppendLine(" Directory Local Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory local entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Local Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.DirectoryIndex, " Directory index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownHeader? header)
{
builder.AppendLine(" Unknown Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No unknown header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownEntry?[]? entries)
{
builder.AppendLine(" Unknown Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No unknown entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Unknown Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumHeader? header)
{
builder.AppendLine(" Checksum Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No checksum header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.ChecksumSize, " Checksum size");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumMapHeader? header)
{
builder.AppendLine(" Checksum Map Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No checksum map header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine(header.ItemCount, " Item count");
builder.AppendLine(header.ChecksumCount, " Checksum count");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumMapEntry?[]? entries)
{
builder.AppendLine(" Checksum Map Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No checksum map entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Checksum Map Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.ChecksumCount, " Checksum count");
builder.AppendLine(entry.FirstChecksumIndex, " First checksum index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ChecksumEntry?[]? entries)
{
builder.AppendLine(" Checksum Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No checksum entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Checksum Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Checksum, " Checksum");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,379 @@
using System.Collections.Generic;
using System.Text;
using SabreTools.Models.NewExecutable;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Serialization.Extensions;
namespace SabreTools.Serialization.Printers
{
public class NewExecutable : IPrinter<Executable>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Executable model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Executable executable)
{
builder.AppendLine("New Executable Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Stub
Print(builder, executable.Stub?.Header);
// Header
Print(builder, executable.Header);
// Tables
Print(builder, executable.SegmentTable);
Print(builder, executable.ResourceTable);
Print(builder, executable.ResidentNameTable);
Print(builder, executable.ModuleReferenceTable, executable.Stub?.Header, executable.Header);
Print(builder, executable.ImportedNameTable);
Print(builder, executable.EntryTable);
Print(builder, executable.NonResidentNameTable);
}
private static void Print(StringBuilder builder, Models.MSDOS.ExecutableHeader? header)
{
builder.AppendLine(" MS-DOS Stub Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No MS-DOS stub header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Magic, " Magic number");
builder.AppendLine(header.LastPageBytes, " Last page bytes");
builder.AppendLine(header.Pages, " Pages");
builder.AppendLine(header.RelocationItems, " Relocation items");
builder.AppendLine(header.HeaderParagraphSize, " Header paragraph size");
builder.AppendLine(header.MinimumExtraParagraphs, " Minimum extra paragraphs");
builder.AppendLine(header.MaximumExtraParagraphs, " Maximum extra paragraphs");
builder.AppendLine(header.InitialSSValue, " Initial SS value");
builder.AppendLine(header.InitialSPValue, " Initial SP value");
builder.AppendLine(header.Checksum, " Checksum");
builder.AppendLine(header.InitialIPValue, " Initial IP value");
builder.AppendLine(header.InitialCSValue, " Initial CS value");
builder.AppendLine(header.RelocationTableAddr, " Relocation table address");
builder.AppendLine(header.OverlayNumber, " Overlay number");
builder.AppendLine();
builder.AppendLine(" MS-DOS Stub Extended Header Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine(header.Reserved1, " Reserved words");
builder.AppendLine(header.OEMIdentifier, " OEM identifier");
builder.AppendLine(header.OEMInformation, " OEM information");
builder.AppendLine(header.Reserved2, " Reserved words");
builder.AppendLine(header.NewExeHeaderAddr, " New EXE header address");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ExecutableHeader? 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.LinkerVersion, " Linker version");
builder.AppendLine(header.LinkerRevision, " Linker revision");
builder.AppendLine(header.EntryTableOffset, " Entry table offset");
builder.AppendLine(header.EntryTableSize, " Entry table size");
builder.AppendLine(header.CrcChecksum, " CRC checksum");
builder.AppendLine($" Flag word: {header.FlagWord} (0x{header.FlagWord:X})");
builder.AppendLine(header.AutomaticDataSegmentNumber, " Automatic data segment number");
builder.AppendLine(header.InitialHeapAlloc, " Initial heap allocation");
builder.AppendLine(header.InitialStackAlloc, " Initial stack allocation");
builder.AppendLine(header.InitialCSIPSetting, " Initial CS:IP setting");
builder.AppendLine(header.InitialSSSPSetting, " Initial SS:SP setting");
builder.AppendLine(header.FileSegmentCount, " File segment count");
builder.AppendLine(header.ModuleReferenceTableSize, " Module reference table size");
builder.AppendLine(header.NonResidentNameTableSize, " Non-resident name table size");
builder.AppendLine(header.SegmentTableOffset, " Segment table offset");
builder.AppendLine(header.ResourceTableOffset, " Resource table offset");
builder.AppendLine(header.ResidentNameTableOffset, " Resident name table offset");
builder.AppendLine(header.ModuleReferenceTableOffset, " Module reference table offset");
builder.AppendLine(header.ImportedNamesTableOffset, " Imported names table offset");
builder.AppendLine(header.NonResidentNamesTableOffset, " Non-resident name table offset");
builder.AppendLine(header.MovableEntriesCount, " Moveable entries count");
builder.AppendLine(header.SegmentAlignmentShiftCount, " Segment alignment shift count");
builder.AppendLine(header.ResourceEntriesCount, " Resource entries count");
builder.AppendLine($" Target operating system: {header.TargetOperatingSystem} (0x{header.TargetOperatingSystem:X})");
builder.AppendLine($" Additional flags: {header.AdditionalFlags} (0x{header.AdditionalFlags:X})");
builder.AppendLine(header.ReturnThunkOffset, " Return thunk offset");
builder.AppendLine(header.SegmentReferenceThunkOffset, " Segment reference thunk offset");
builder.AppendLine(header.MinCodeSwapAreaSize, " Minimum code swap area size");
builder.AppendLine(header.WindowsSDKRevision, " Windows SDK revision");
builder.AppendLine(header.WindowsSDKVersion, " Windows SDK version");
builder.AppendLine();
}
private static void Print(StringBuilder builder, SegmentTableEntry?[]? entries)
{
builder.AppendLine(" Segment Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No segment table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Segment Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Offset, " Offset");
builder.AppendLine(entry.Length, " Length");
builder.AppendLine($" Flag word: {entry.FlagWord} (0x{entry.FlagWord:X})");
builder.AppendLine(entry.MinimumAllocationSize, " Minimum allocation size");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ResourceTable? table)
{
builder.AppendLine(" Resource Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
{
builder.AppendLine(" No resource table");
builder.AppendLine();
return;
}
builder.AppendLine(table.AlignmentShiftCount, " Alignment shift count");
if (table.ResourceTypes == null || table.ResourceTypes.Length == 0)
{
builder.AppendLine(" No resource table items");
}
else
{
for (int i = 0; i < table.ResourceTypes.Length; i++)
{
// TODO: If not integer type, print out name
var entry = table.ResourceTypes[i];
builder.AppendLine($" Resource Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.TypeID, " Type ID");
builder.AppendLine(entry.ResourceCount, " Resource count");
builder.AppendLine(entry.Reserved, " Reserved");
builder.AppendLine(" Resources = ");
if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0)
{
builder.AppendLine(" No resource items");
}
else
{
for (int j = 0; j < entry.Resources.Length; j++)
{
// TODO: If not integer type, print out name
var resource = entry.Resources[j];
builder.AppendLine($" Resource Entry {i}");
if (resource == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(resource.Offset, " Offset");
builder.AppendLine(resource.Length, " Length");
builder.AppendLine($" Flag word: {resource.FlagWord} (0x{resource.FlagWord:X})");
builder.AppendLine(resource.ResourceID, " Resource ID");
builder.AppendLine(resource.Reserved, " Reserved");
}
}
}
}
if (table.TypeAndNameStrings == null || table.TypeAndNameStrings.Count == 0)
{
builder.AppendLine(" No resource table type/name strings");
}
else
{
foreach (var typeAndNameString in table.TypeAndNameStrings)
{
builder.AppendLine($" Resource Type/Name Offset {typeAndNameString.Key}");
if (typeAndNameString.Value == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(typeAndNameString.Value.Length, " Length");
builder.AppendLine(typeAndNameString.Value.Text, " Text", Encoding.ASCII);
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ResidentNameTableEntry?[]? entries)
{
builder.AppendLine(" Resident-Name Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No resident-name table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Resident-Name Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.NameString, " Name string", Encoding.ASCII);
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ModuleReferenceTableEntry?[]? entries, Models.MSDOS.ExecutableHeader? stub, ExecutableHeader? header)
{
builder.AppendLine(" Module-Reference Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No module-reference table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
// TODO: Read the imported names table and print value here
var entry = entries[i];
builder.AppendLine($" Module-Reference Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine($" Offset: {entry.Offset} (adjusted to be {entry.Offset + (stub?.NewExeHeaderAddr ?? 0) + (header?.ImportedNamesTableOffset ?? 0)})");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Dictionary<ushort, ImportedNameTableEntry>? entries)
{
builder.AppendLine(" Imported-Name Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Count == 0)
{
builder.AppendLine(" No imported-name table items");
builder.AppendLine();
return;
}
foreach (var entry in entries)
{
builder.AppendLine($" Imported-Name Table at Offset {entry.Key}");
if (entry.Value == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Value.Length, " Length");
builder.AppendLine(entry.Value.NameString, " Name string", Encoding.ASCII);
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, EntryTableBundle?[]? entries)
{
builder.AppendLine(" Entry Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No entry table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Entry Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.EntryCount, " Entry count");
builder.AppendLine(entry.SegmentIndicator, " Segment indicator");
switch (entry.GetEntryType())
{
case SegmentEntryType.FixedSegment:
builder.AppendLine($" Flag word: {entry.FixedFlagWord} (0x{entry.FixedFlagWord:X})");
builder.AppendLine(entry.FixedOffset, " Offset");
break;
case SegmentEntryType.MoveableSegment:
builder.AppendLine($" Flag word: {entry.MoveableFlagWord} (0x{entry.MoveableFlagWord:X})");
builder.AppendLine(entry.MoveableReserved, " Reserved");
builder.AppendLine(entry.MoveableSegmentNumber, " Segment number");
builder.AppendLine(entry.MoveableOffset, " Offset");
break;
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NonResidentNameTableEntry?[]? entries)
{
builder.AppendLine(" Nonresident-Name Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No nonresident-name table items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Nonresident-Name Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.NameString, " Name string", Encoding.ASCII);
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,259 @@
using System.Text;
using SabreTools.Models.Nitro;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class Nitro : IPrinter<Cart>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Cart model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Cart cart)
{
builder.AppendLine("NDS Cart Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, cart.CommonHeader);
Print(builder, cart.ExtendedDSiHeader);
Print(builder, cart.SecureArea);
Print(builder, cart.NameTable);
Print(builder, cart.FileAllocationTable);
}
private static void Print(StringBuilder builder, CommonHeader? header)
{
builder.AppendLine(" Common Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No common header");
builder.AppendLine();
return;
}
builder.AppendLine(header.GameTitle, " Game title");
builder.AppendLine(header.GameCode, " Game code");
builder.AppendLine(header.MakerCode, " Maker code");
builder.AppendLine($" Unit code: {header.UnitCode} (0x{header.UnitCode:X})");
builder.AppendLine(header.EncryptionSeedSelect, " Encryption seed select");
builder.AppendLine(header.DeviceCapacity, " Device capacity");
builder.AppendLine(header.Reserved1, " Reserved 1");
builder.AppendLine(header.GameRevision, " Game revision");
builder.AppendLine(header.RomVersion, " Rom version");
builder.AppendLine(header.ARM9RomOffset, " ARM9 rom offset");
builder.AppendLine(header.ARM9EntryAddress, " ARM9 entry address");
builder.AppendLine(header.ARM9LoadAddress, " ARM9 load address");
builder.AppendLine(header.ARM9Size, " ARM9 size");
builder.AppendLine(header.ARM7RomOffset, " ARM7 rom offset");
builder.AppendLine(header.ARM7EntryAddress, " ARM7 entry address");
builder.AppendLine(header.ARM7LoadAddress, " ARM7 load address");
builder.AppendLine(header.ARM7Size, " ARM7 size");
builder.AppendLine(header.FileNameTableOffset, " File name table offset");
builder.AppendLine(header.FileNameTableLength, " File name table length");
builder.AppendLine(header.FileAllocationTableOffset, " File allocation table offset");
builder.AppendLine(header.FileAllocationTableLength, " File allocation table length");
builder.AppendLine(header.ARM9OverlayOffset, " ARM9 overlay offset");
builder.AppendLine(header.ARM9OverlayLength, " ARM9 overlay length");
builder.AppendLine(header.ARM7OverlayOffset, " ARM7 overlay offset");
builder.AppendLine(header.ARM7OverlayLength, " ARM7 overlay length");
builder.AppendLine(header.NormalCardControlRegisterSettings, " Normal card control register settings");
builder.AppendLine(header.SecureCardControlRegisterSettings, " Secure card control register settings");
builder.AppendLine(header.IconBannerOffset, " Icon banner offset");
builder.AppendLine(header.SecureAreaCRC, " Secure area CRC");
builder.AppendLine(header.SecureTransferTimeout, " Secure transfer timeout");
builder.AppendLine(header.ARM9Autoload, " ARM9 autoload");
builder.AppendLine(header.ARM7Autoload, " ARM7 autoload");
builder.AppendLine(header.SecureDisable, " Secure disable");
builder.AppendLine(header.NTRRegionRomSize, " NTR region rom size");
builder.AppendLine(header.HeaderSize, " Header size");
builder.AppendLine(header.Reserved2, " Reserved 2");
builder.AppendLine(header.NintendoLogo, " Nintendo logo");
builder.AppendLine(header.NintendoLogoCRC, " Nintendo logo CRC");
builder.AppendLine(header.HeaderCRC, " Header CRC");
builder.AppendLine(header.DebuggerReserved, " Debugger reserved");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ExtendedDSiHeader? header)
{
builder.AppendLine(" Extended DSi Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No extended DSi header");
builder.AppendLine();
return;
}
builder.AppendLine(header.GlobalMBK15Settings, " Global MBK1..MBK5 settings");
builder.AppendLine(header.LocalMBK68SettingsARM9, " Local MBK6..MBK8 settings for ARM9");
builder.AppendLine(header.LocalMBK68SettingsARM7, " Local MBK6..MBK8 settings for ARM7");
builder.AppendLine(header.GlobalMBK9Setting, " Global MBK9 setting");
builder.AppendLine(header.RegionFlags, " Region flags");
builder.AppendLine(header.AccessControl, " Access control");
builder.AppendLine(header.ARM7SCFGEXTMask, " ARM7 SCFG EXT mask");
builder.AppendLine(header.ReservedFlags, " Reserved/flags?");
builder.AppendLine(header.ARM9iRomOffset, " ARM9i rom offset");
builder.AppendLine(header.Reserved3, " Reserved 3");
builder.AppendLine(header.ARM9iLoadAddress, " ARM9i load address");
builder.AppendLine(header.ARM9iSize, " ARM9i size");
builder.AppendLine(header.ARM7iRomOffset, " ARM7i rom offset");
builder.AppendLine(header.Reserved4, " Reserved 4");
builder.AppendLine(header.ARM7iLoadAddress, " ARM7i load address");
builder.AppendLine(header.ARM7iSize, " ARM7i size");
builder.AppendLine(header.DigestNTRRegionOffset, " Digest NTR region offset");
builder.AppendLine(header.DigestNTRRegionLength, " Digest NTR region length");
builder.AppendLine(header.DigestTWLRegionOffset, " Digest TWL region offset");
builder.AppendLine(header.DigestTWLRegionLength, " Digest TWL region length");
builder.AppendLine(header.DigestSectorHashtableRegionOffset, " Digest sector hashtable region offset");
builder.AppendLine(header.DigestSectorHashtableRegionLength, " Digest sector hashtable region length");
builder.AppendLine(header.DigestBlockHashtableRegionOffset, " Digest block hashtable region offset");
builder.AppendLine(header.DigestBlockHashtableRegionLength, " Digest block hashtable region length");
builder.AppendLine(header.DigestSectorSize, " Digest sector size");
builder.AppendLine(header.DigestBlockSectorCount, " Digest block sector count");
builder.AppendLine(header.IconBannerSize, " Icon banner size");
builder.AppendLine(header.Unknown1, " Unknown 1");
builder.AppendLine(header.ModcryptArea1Offset, " Modcrypt area 1 offset");
builder.AppendLine(header.ModcryptArea1Size, " Modcrypt area 1 size");
builder.AppendLine(header.ModcryptArea2Offset, " Modcrypt area 2 offset");
builder.AppendLine(header.ModcryptArea2Size, " Modcrypt area 2 size");
builder.AppendLine(header.TitleID, " Title ID");
builder.AppendLine(header.DSiWarePublicSavSize, " DSiWare 'public.sav' size");
builder.AppendLine(header.DSiWarePrivateSavSize, " DSiWare 'private.sav' size");
builder.AppendLine(header.ReservedZero, " Reserved (zero)");
builder.AppendLine(header.Unknown2, " Unknown 2");
builder.AppendLine(header.ARM9WithSecureAreaSHA1HMACHash, " ARM9 (with encrypted secure area) SHA1 HMAC hash");
builder.AppendLine(header.ARM7SHA1HMACHash, " ARM7 SHA1 HMAC hash");
builder.AppendLine(header.DigestMasterSHA1HMACHash, " Digest master SHA1 HMAC hash");
builder.AppendLine(header.BannerSHA1HMACHash, " Banner SHA1 HMAC hash");
builder.AppendLine(header.ARM9iDecryptedSHA1HMACHash, " ARM9i (decrypted) SHA1 HMAC hash");
builder.AppendLine(header.ARM7iDecryptedSHA1HMACHash, " ARM7i (decrypted) SHA1 HMAC hash");
builder.AppendLine(header.Reserved5, " Reserved 5");
builder.AppendLine(header.ARM9NoSecureAreaSHA1HMACHash, " ARM9 (without secure area) SHA1 HMAC hash");
builder.AppendLine(header.Reserved6, " Reserved 6");
builder.AppendLine(header.ReservedAndUnchecked, " Reserved and unchecked region");
builder.AppendLine(header.RSASignature, " RSA signature");
builder.AppendLine();
}
private static void Print(StringBuilder builder, byte[]? secureArea)
{
builder.AppendLine(" Secure Area Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine(secureArea, " Secure Area");
builder.AppendLine();
}
private static void Print(StringBuilder builder, NameTable? table)
{
builder.AppendLine(" Name Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
{
builder.AppendLine(" No name table");
builder.AppendLine();
return;
}
builder.AppendLine();
Print(builder, table.FolderAllocationTable);
Print(builder, table.NameList);
}
private static void Print(StringBuilder builder, FolderAllocationTableEntry?[]? entries)
{
builder.AppendLine(" Folder Allocation Table:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No folder allocation table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Folder Allocation Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.StartOffset, " Start offset");
builder.AppendLine(entry.FirstFileIndex, " First file index");
if (entry.Unknown == 0xF0)
{
builder.AppendLine(entry.ParentFolderIndex, " Parent folder index");
builder.AppendLine(entry.Unknown, " Unknown");
}
else
{
ushort totalEntries = (ushort)((entry.Unknown << 8) | entry.ParentFolderIndex);
builder.AppendLine(totalEntries, " Total entries");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, NameListEntry?[]? entries)
{
builder.AppendLine(" Name List:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No name list entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Name List Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Folder, " Folder");
builder.AppendLine(entry.Name, " Name");
if (entry.Folder)
builder.AppendLine(entry.Index, " Index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, FileAllocationTableEntry?[]? entries)
{
builder.AppendLine(" File Allocation Table:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No file allocation table entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" File Allocation Table Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.StartOffset, " Start offset");
builder.AppendLine(entry.EndOffset, " End offset");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,68 @@
using System.Text;
using SabreTools.Models.PAK;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PAK : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("PAK Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
Print(builder, file.DirectoryItems);
}
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.Signature, " Signature");
builder.AppendLine(header.DirectoryOffset, " Directory offset");
builder.AppendLine(header.DirectoryLength, " Directory length");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryItem?[]? entries)
{
builder.AppendLine(" Directory Items Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Item {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.ItemName, " Item name");
builder.AppendLine(entry.ItemOffset, " Item offset");
builder.AppendLine(entry.ItemLength, " Item length");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,92 @@
using System.Text;
using SabreTools.Models.PFF;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PFF : IPrinter<Archive>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Archive model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Archive archive)
{
builder.AppendLine("PFF Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, archive.Header);
Print(builder, archive.Segments);
Print(builder, archive.Footer);
}
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.HeaderSize, " Header size");
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.NumberOfFiles, " Number of files");
builder.AppendLine(header.FileSegmentSize, " File segment size");
builder.AppendLine(header.FileListOffset, " File list offset");
builder.AppendLine();
}
private static void Print(StringBuilder builder, Segment?[]? segments)
{
builder.AppendLine(" Segments Information:");
builder.AppendLine(" -------------------------");
if (segments == null || segments.Length == 0)
{
builder.AppendLine(" No segments");
builder.AppendLine();
return;
}
for (int i = 0; i < segments.Length; i++)
{
var segment = segments[i];
builder.AppendLine($" Segment {i}");
if (segment == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(segment.Deleted, " Deleted");
builder.AppendLine(segment.FileLocation, " File location");
builder.AppendLine(segment.FileSize, " File size");
builder.AppendLine(segment.PackedDate, " Packed date");
builder.AppendLine(segment.FileName, " File name");
builder.AppendLine(segment.ModifiedDate, " Modified date");
builder.AppendLine(segment.CompressionLevel, " Compression level");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Footer? footer)
{
builder.AppendLine(" Footer Information:");
builder.AppendLine(" -------------------------");
if (footer == null)
{
builder.AppendLine(" No footer");
builder.AppendLine();
return;
}
builder.AppendLine(footer.SystemIP, " System IP");
builder.AppendLine(footer.Reserved, " Reserved");
builder.AppendLine(footer.KingTag, " King tag");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Text;
using SabreTools.Models.PIC;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PIC : IPrinter<DiscInformation>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, DiscInformation model)
=> Print(builder, model);
public static void Print(StringBuilder builder, DiscInformation di)
{
builder.AppendLine("PIC Information:");
builder.AppendLine("-------------------------");
builder.AppendLine(di.DataStructureLength, "Data structure length");
builder.AppendLine(di.Reserved0, "Reserved");
builder.AppendLine(di.Reserved1, "Reserved");
builder.AppendLine();
Print(builder, di.Units);
}
private static void Print(StringBuilder builder, DiscInformationUnit?[]? entries)
{
builder.AppendLine(" Disc Information Units:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No disc information units");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Disc Information Unit {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
if (entry.Header == null)
{
builder.AppendLine(" No header");
}
else
{
var header = entry.Header;
builder.AppendLine(header.DiscInformationIdentifier, " Disc information identifier");
builder.AppendLine(header.DiscInformationFormat, " Disc information format");
builder.AppendLine(header.Reserved0, " Reserved");
builder.AppendLine(header.SequenceNumber, " Sequence number");
builder.AppendLine(header.BytesInUse, " Bytes in use");
builder.AppendLine(header.Reserved1, " Reserved");
}
if (entry.Body == null)
{
builder.AppendLine(" No body");
}
else
{
DiscInformationUnitBody body = entry.Body;
builder.AppendLine(body.DiscTypeIdentifier, " Disc type identifer");
builder.AppendLine(body.DiscSizeClassVersion, " Disc size class version");
builder.AppendLine(body.FormatDependentContents, " Format-dependent contents");
}
if (entry.Trailer == null)
{
builder.AppendLine(" No trailer");
}
else
{
DiscInformationUnitTrailer trailer = entry.Trailer;
builder.AppendLine(trailer.DiscManufacturerID, " Disc manufacturer ID");
builder.AppendLine(trailer.MediaTypeID, " Media type ID");
builder.AppendLine(trailer.TimeStamp, " Timestamp");
builder.AppendLine(trailer.ProductRevisionNumber, " Product revision number");
}
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,279 @@
using System.Text;
using SabreTools.Models.PKZIP;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PKZIP : IPrinter<Archive>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Archive model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Archive archive)
{
builder.AppendLine("PKZIP Archive (or Derived Format) Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, archive.EndOfCentralDirectoryRecord);
Print(builder, archive.ZIP64EndOfCentralDirectoryLocator);
Print(builder, archive.ZIP64EndOfCentralDirectoryRecord);
Print(builder, archive.CentralDirectoryHeaders);
Print(builder, archive.ArchiveExtraDataRecord);
Print(builder,
archive.LocalFileHeaders,
archive.EncryptionHeaders,
archive.FileData,
archive.DataDescriptors,
archive.ZIP64DataDescriptors);
}
private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord? record)
{
builder.AppendLine(" End of Central Directory Record Information:");
builder.AppendLine(" -------------------------");
if (record == null)
{
builder.AppendLine(" No end of central directory record");
builder.AppendLine();
return;
}
builder.AppendLine(record.Signature, " Signature");
builder.AppendLine(record.DiskNumber, " Disk number");
builder.AppendLine(record.StartDiskNumber, " Start disk number");
builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk");
builder.AppendLine(record.TotalEntries, " Total entries");
builder.AppendLine(record.CentralDirectorySize, " Central directory size");
builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset");
builder.AppendLine(record.FileCommentLength, " File comment length");
builder.AppendLine(record.FileComment, " File comment");
builder.AppendLine();
}
private static void Print(StringBuilder builder, EndOfCentralDirectoryLocator64? locator)
{
builder.AppendLine(" ZIP64 End of Central Directory Locator Information:");
builder.AppendLine(" -------------------------");
if (locator == null)
{
builder.AppendLine(" No ZIP64 end of central directory locator");
builder.AppendLine();
return;
}
builder.AppendLine(locator.Signature, " Signature");
builder.AppendLine(locator.StartDiskNumber, " Start disk number");
builder.AppendLine(locator.CentralDirectoryOffset, " Central directory offset");
builder.AppendLine(locator.TotalDisks, " Total disks");
builder.AppendLine();
}
private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord64? record)
{
builder.AppendLine(" ZIP64 End of Central Directory Record Information:");
builder.AppendLine(" -------------------------");
if (record == null)
{
builder.AppendLine(" No ZIP64 end of central directory record");
builder.AppendLine();
return;
}
builder.AppendLine(record.Signature, " Signature");
builder.AppendLine(record.DirectoryRecordSize, " Directory record size");
builder.AppendLine($" Host system: {record.HostSystem} (0x{record.HostSystem:X})");
builder.AppendLine(record.VersionMadeBy, " Version made by");
builder.AppendLine(record.VersionNeededToExtract, " Version needed to extract");
builder.AppendLine(record.DiskNumber, " Disk number");
builder.AppendLine(record.StartDiskNumber, " Start disk number");
builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk");
builder.AppendLine(record.TotalEntries, " Total entries");
builder.AppendLine(record.CentralDirectorySize, " Central directory size");
builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset");
//builder.AppendLine(record.ExtensibleDataSector, " Extensible data sector");
builder.AppendLine();
}
private static void Print(StringBuilder builder, CentralDirectoryFileHeader?[]? headers)
{
builder.AppendLine(" Central Directory File Headers Information:");
builder.AppendLine(" -------------------------");
if (headers == null || headers.Length == 0)
{
builder.AppendLine(" No central directory file headers");
builder.AppendLine();
return;
}
for (int i = 0; i < headers.Length; i++)
{
var record = headers[i];
Print(builder, record, i);
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, CentralDirectoryFileHeader? header, int index)
{
builder.AppendLine($" Central Directory File Header Entry {index}");
if (header == null)
{
builder.AppendLine(" [NULL]");
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine($" Host system: {header.HostSystem} (0x{header.HostSystem:X})");
builder.AppendLine(header.VersionMadeBy, " Version made by");
builder.AppendLine(header.VersionNeededToExtract, " Version needed to extract");
builder.AppendLine($" Flags: {header.Flags} (0x{header.Flags:X})");
builder.AppendLine($" Compression method: {header.CompressionMethod} (0x{header.CompressionMethod:X})");
builder.AppendLine(header.LastModifedFileTime, " Last modified file time"); // TODO: Parse from MS-DOS
builder.AppendLine(header.LastModifiedFileDate, " Last modified file date"); // TODO: Parse from MS-DOS
builder.AppendLine(header.CRC32, " CRC-32");
builder.AppendLine(header.CompressedSize, " Compressed size");
builder.AppendLine(header.UncompressedSize, " Uncompressed size");
builder.AppendLine(header.FileNameLength, " File name length");
builder.AppendLine(header.ExtraFieldLength, " Extra field length");
builder.AppendLine(header.FileCommentLength, " File comment length");
builder.AppendLine(header.DiskNumberStart, " Disk number start");
builder.AppendLine($" Internal file attributes: {header.InternalFileAttributes} (0x{header.InternalFileAttributes:X})");
builder.AppendLine(header.ExternalFileAttributes, " External file attributes");
builder.AppendLine(header.RelativeOffsetOfLocalHeader, " Relative offset of local header");
builder.AppendLine(header.FileName, " File name");
builder.AppendLine(header.ExtraField, " Extra field");
builder.AppendLine(header.FileComment, " File comment");
}
private static void Print(StringBuilder builder, ArchiveExtraDataRecord? record)
{
builder.AppendLine(" Archive Extra Data Record Information:");
builder.AppendLine(" -------------------------");
if (record == null)
{
builder.AppendLine(" No archive extra data record");
builder.AppendLine();
return;
}
builder.AppendLine(record.Signature, " Signature");
builder.AppendLine(record.ExtraFieldLength, " Extra field length");
builder.AppendLine(record.ExtraFieldData, " Extra field data");
builder.AppendLine();
}
private static void Print(StringBuilder builder,
LocalFileHeader[]? localFileHeaders,
byte[]?[]? encryptionHeaders,
byte[][]? fileData,
DataDescriptor?[]? dataDescriptors,
DataDescriptor64?[]? zip64DataDescriptors)
{
builder.AppendLine(" Local File Information:");
builder.AppendLine(" -------------------------");
if (localFileHeaders == null || localFileHeaders.Length == 0)
{
builder.AppendLine(" No local files");
builder.AppendLine();
return;
}
if (encryptionHeaders == null || localFileHeaders.Length > encryptionHeaders.Length
|| fileData == null || localFileHeaders.Length > fileData.Length
|| dataDescriptors == null || localFileHeaders.Length > dataDescriptors.Length
|| zip64DataDescriptors == null || localFileHeaders.Length > zip64DataDescriptors.Length)
{
builder.AppendLine(" Mismatch in local file array values");
builder.AppendLine();
}
for (int i = 0; i < localFileHeaders.Length; i++)
{
var localFileHeader = localFileHeaders[i];
var encryptionHeader = encryptionHeaders != null && i < encryptionHeaders.Length ? encryptionHeaders[i] : null;
var fileDatum = fileData != null && i < fileData.Length ? fileData[i] : null;
var dataDescriptor = dataDescriptors != null && i < dataDescriptors.Length ? dataDescriptors[i] : null;
var zip64DataDescriptor = zip64DataDescriptors != null && i < zip64DataDescriptors.Length ? zip64DataDescriptors[i] : null;
Print(builder, localFileHeader, encryptionHeader, fileDatum, dataDescriptor, zip64DataDescriptor, i);
}
builder.AppendLine();
}
private static void Print(StringBuilder builder,
LocalFileHeader localFileHeader,
byte[]? encryptionHeader,
byte[]? fileData,
DataDescriptor? dataDescriptor,
DataDescriptor64? zip64DataDescriptor,
int index)
{
builder.AppendLine($" Local File Entry {index}");
if (localFileHeader == null)
{
builder.AppendLine(" [NULL]");
return;
}
builder.AppendLine(localFileHeader.Signature, " [Local File Header] Signature");
builder.AppendLine(localFileHeader.Version, " [Local File Header] Version");
builder.AppendLine($" [Local File Header] Flags: {localFileHeader.Flags} (0x{localFileHeader.Flags:X})");
builder.AppendLine($" [Local File Header] Compression method: {localFileHeader.CompressionMethod} (0x{localFileHeader.CompressionMethod:X})");
builder.AppendLine(localFileHeader.LastModifedFileTime, " [Local File Header] Last modified file time"); // TODO: Parse from MS-DOS
builder.AppendLine(localFileHeader.LastModifiedFileDate, " [Local File Header] Last modified file date"); // TODO: Parse from MS-DOS
builder.AppendLine(localFileHeader.CRC32, " [Local File Header] CRC-32");
builder.AppendLine(localFileHeader.CompressedSize, " [Local File Header] Compressed size");
builder.AppendLine(localFileHeader.UncompressedSize, " [Local File Header] Uncompressed size");
builder.AppendLine(localFileHeader.FileNameLength, " [Local File Header] File name length");
builder.AppendLine(localFileHeader.ExtraFieldLength, " [Local File Header] Extra field length");
builder.AppendLine(localFileHeader.FileName, " [Local File Header] File name");
builder.AppendLine(localFileHeader.ExtraField, " [Local File Header] Extra field");
if (encryptionHeader == null)
{
builder.AppendLine(" [Encryption Header]: [NULL]");
}
else
{
builder.AppendLine(encryptionHeader.Length, " [Encryption Header] Length");
builder.AppendLine(encryptionHeader, " [Encryption Header] Data");
}
if (fileData == null)
{
builder.AppendLine(" [File Data]: [NULL]");
}
else
{
builder.AppendLine(fileData.Length, " [File Data] Length");
//builder.AppendLine(fileData, " [File Data] Data");
}
if (dataDescriptor == null)
{
builder.AppendLine(" [Data Descriptor]: [NULL]");
}
else
{
builder.AppendLine(dataDescriptor.Signature, " [Data Descriptor] Signature");
builder.AppendLine(dataDescriptor.CRC32, " [Data Descriptor] CRC-32");
builder.AppendLine(dataDescriptor.CompressedSize, " [Data Descriptor] Compressed size");
builder.AppendLine(dataDescriptor.UncompressedSize, " [Data Descriptor] Uncompressed size");
}
if (zip64DataDescriptor == null)
{
builder.AppendLine(" [ZIP64 Data Descriptor]: [NULL]");
}
else
{
builder.AppendLine(zip64DataDescriptor.Signature, " [ZIP64 Data Descriptor] Signature");
builder.AppendLine(zip64DataDescriptor.CRC32, " [ZIP64 Data Descriptor] CRC-32");
builder.AppendLine(zip64DataDescriptor.CompressedSize, " [ZIP64 Data Descriptor] Compressed size");
builder.AppendLine(zip64DataDescriptor.UncompressedSize, " [ZIP64 Data Descriptor] Uncompressed size");
}
}
}
}

View File

@@ -0,0 +1,183 @@
using System.Text;
using SabreTools.Models.PlayJ;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PlayJAudioFile : IPrinter<AudioFile>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, AudioFile model)
=> Print(builder, model);
public static void Print(StringBuilder builder, AudioFile audio)
{
builder.AppendLine("PlayJ Audio File Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, audio.Header);
Print(builder, audio.UnknownBlock1);
if (audio.Header?.Version == 0x00000000)
{
Print(builder, audio.UnknownValue2);
Print(builder, audio.UnknownBlock3);
}
else if (audio.Header?.Version == 0x0000000A)
{
Print(builder, audio.DataFilesCount, audio.DataFiles);
}
}
private static void Print(StringBuilder builder, AudioHeader? header)
{
builder.AppendLine(" Audio Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No audio header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.Version, " Version");
if (header.Version == 0x00000000 && header is AudioHeaderV1 headerV1)
{
builder.AppendLine(headerV1.TrackID, " Track ID");
builder.AppendLine(headerV1.UnknownOffset1, " Unknown offset 1");
builder.AppendLine(headerV1.UnknownOffset2, " Unknown offset 2");
builder.AppendLine(headerV1.UnknownOffset3, " Unknown offset 3");
builder.AppendLine(headerV1.Unknown1, " Unknown 1");
builder.AppendLine(headerV1.Unknown2, " Unknown 2");
builder.AppendLine(headerV1.Year, " Year");
builder.AppendLine(headerV1.TrackNumber, " Track number");
builder.AppendLine($" Subgenre: {headerV1.Subgenre} (0x{headerV1.Subgenre:X})");
builder.AppendLine(headerV1.Duration, " Duration in seconds");
}
else if (header.Version == 0x0000000A && header is AudioHeaderV2 headerV2)
{
builder.AppendLine(headerV2.Unknown1, " Unknown 1");
builder.AppendLine(headerV2.Unknown2, " Unknown 2");
builder.AppendLine(headerV2.Unknown3, " Unknown 3");
builder.AppendLine(headerV2.Unknown4, " Unknown 4");
builder.AppendLine(headerV2.Unknown5, " Unknown 5");
builder.AppendLine(headerV2.Unknown6, " Unknown 6");
builder.AppendLine(headerV2.UnknownOffset1, " Unknown Offset 1");
builder.AppendLine(headerV2.Unknown7, " Unknown 7");
builder.AppendLine(headerV2.Unknown8, " Unknown 8");
builder.AppendLine(headerV2.Unknown9, " Unknown 9");
builder.AppendLine(headerV2.UnknownOffset2, " Unknown Offset 2");
builder.AppendLine(headerV2.Unknown10, " Unknown 10");
builder.AppendLine(headerV2.Unknown11, " Unknown 11");
builder.AppendLine(headerV2.Unknown12, " Unknown 12");
builder.AppendLine(headerV2.Unknown13, " Unknown 13");
builder.AppendLine(headerV2.Unknown14, " Unknown 14");
builder.AppendLine(headerV2.Unknown15, " Unknown 15");
builder.AppendLine(headerV2.Unknown16, " Unknown 16");
builder.AppendLine(headerV2.Unknown17, " Unknown 17");
builder.AppendLine(headerV2.TrackID, " Track ID");
builder.AppendLine(headerV2.Year, " Year");
builder.AppendLine(headerV2.TrackNumber, " Track number");
builder.AppendLine(headerV2.Unknown18, " Unknown 18");
}
else
{
builder.AppendLine(" Unrecognized version, not parsed...");
}
builder.AppendLine(header.TrackLength, " Track length");
builder.AppendLine(header.Track, " Track");
builder.AppendLine(header.ArtistLength, " Artist length");
builder.AppendLine(header.Artist, " Artist");
builder.AppendLine(header.AlbumLength, " Album length");
builder.AppendLine(header.Album, " Album");
builder.AppendLine(header.WriterLength, " Writer length");
builder.AppendLine(header.Writer, " Writer");
builder.AppendLine(header.PublisherLength, " Publisher length");
builder.AppendLine(header.Publisher, " Publisher");
builder.AppendLine(header.LabelLength, " Label length");
builder.AppendLine(header.Label, " Label");
builder.AppendLine(header.CommentsLength, " Comments length");
builder.AppendLine(header.Comments, " Comments");
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownBlock1? block)
{
builder.AppendLine(" Unknown Block 1 Information:");
builder.AppendLine(" -------------------------");
if (block == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(block.Length, " Length");
builder.AppendLine(block.Data, " Data");
builder.AppendLine();
}
private static void Print(StringBuilder builder, uint? value)
{
builder.AppendLine(" Unknown Value 2 Information:");
builder.AppendLine(" -------------------------");
if (value == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(value, " Value");
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownBlock3? block)
{
builder.AppendLine(" Unknown Block 3 Information:");
builder.AppendLine(" -------------------------");
if (block == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(block.Data, " Data");
builder.AppendLine();
}
private static void Print(StringBuilder builder, uint count, DataFile?[]? entries)
{
builder.AppendLine(" Data Files Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine(count, " Data files count");
if (count == 0 || entries == null || entries.Length == 0)
{
builder.AppendLine(" No data files");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Data File {i}:");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FileNameLength, " File name length");
builder.AppendLine(entry.FileName, " File name");
builder.AppendLine(entry.DataLength, " Data length");
builder.AppendLine(entry.Data, " Data");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,226 @@
using System.Text;
using SabreTools.Models.PlayJ;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class PlayJPlaylist : IPrinter<Playlist>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Playlist model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Playlist playlist)
{
builder.AppendLine("PlayJ Playlist Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, playlist.Header);
Print(builder, playlist.AudioFiles);
}
private static void Print(StringBuilder builder, PlaylistHeader? header)
{
builder.AppendLine(" Playlist Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No playlist header");
builder.AppendLine();
return;
}
builder.AppendLine(header.TrackCount, " Track count");
builder.AppendLine(header.Data, " Data");
builder.AppendLine();
}
private static void Print(StringBuilder builder, AudioFile?[]? entries)
{
builder.AppendLine(" Audio Files Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No audio files");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Audio File {i}:");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
Print(builder, entry.Header);
Print(builder, entry.UnknownBlock1);
if (entry.Header?.Version == 0x00000000)
{
Print(builder, entry.UnknownValue2);
Print(builder, entry.UnknownBlock3);
}
else if (entry.Header?.Version == 0x0000000A)
{
Print(builder, entry.DataFilesCount, entry.DataFiles);
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, AudioHeader? header)
{
builder.AppendLine(" Audio Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No audio header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Signature, " Signature");
builder.AppendLine(header.Version, " Version");
if (header.Version == 0x00000000 && header is AudioHeaderV1 headerV1)
{
builder.AppendLine(headerV1.TrackID, " Track ID");
builder.AppendLine(headerV1.UnknownOffset1, " Unknown offset 1");
builder.AppendLine(headerV1.UnknownOffset2, " Unknown offset 2");
builder.AppendLine(headerV1.UnknownOffset3, " Unknown offset 3");
builder.AppendLine(headerV1.Unknown1, " Unknown 1");
builder.AppendLine(headerV1.Unknown2, " Unknown 2");
builder.AppendLine(headerV1.Year, " Year");
builder.AppendLine(headerV1.TrackNumber, " Track number");
builder.AppendLine($" Subgenre: {headerV1.Subgenre} (0x{headerV1.Subgenre:X})");
builder.AppendLine(headerV1.Duration, " Duration in seconds");
}
else if (header.Version == 0x0000000A && header is AudioHeaderV2 headerV2)
{
builder.AppendLine(headerV2.Unknown1, " Unknown 1");
builder.AppendLine(headerV2.Unknown2, " Unknown 2");
builder.AppendLine(headerV2.Unknown3, " Unknown 3");
builder.AppendLine(headerV2.Unknown4, " Unknown 4");
builder.AppendLine(headerV2.Unknown5, " Unknown 5");
builder.AppendLine(headerV2.Unknown6, " Unknown 6");
builder.AppendLine(headerV2.UnknownOffset1, " Unknown Offset 1");
builder.AppendLine(headerV2.Unknown7, " Unknown 7");
builder.AppendLine(headerV2.Unknown8, " Unknown 8");
builder.AppendLine(headerV2.Unknown9, " Unknown 9");
builder.AppendLine(headerV2.UnknownOffset2, " Unknown Offset 2");
builder.AppendLine(headerV2.Unknown10, " Unknown 10");
builder.AppendLine(headerV2.Unknown11, " Unknown 11");
builder.AppendLine(headerV2.Unknown12, " Unknown 12");
builder.AppendLine(headerV2.Unknown13, " Unknown 13");
builder.AppendLine(headerV2.Unknown14, " Unknown 14");
builder.AppendLine(headerV2.Unknown15, " Unknown 15");
builder.AppendLine(headerV2.Unknown16, " Unknown 16");
builder.AppendLine(headerV2.Unknown17, " Unknown 17");
builder.AppendLine(headerV2.TrackID, " Track ID");
builder.AppendLine(headerV2.Year, " Year");
builder.AppendLine(headerV2.TrackNumber, " Track number");
builder.AppendLine(headerV2.Unknown18, " Unknown 18");
}
else
{
builder.AppendLine(" Unrecognized version, not parsed...");
}
builder.AppendLine(header.TrackLength, " Track length");
builder.AppendLine(header.Track, " Track");
builder.AppendLine(header.ArtistLength, " Artist length");
builder.AppendLine(header.Artist, " Artist");
builder.AppendLine(header.AlbumLength, " Album length");
builder.AppendLine(header.Album, " Album");
builder.AppendLine(header.WriterLength, " Writer length");
builder.AppendLine(header.Writer, " Writer");
builder.AppendLine(header.PublisherLength, " Publisher length");
builder.AppendLine(header.Publisher, " Publisher");
builder.AppendLine(header.LabelLength, " Label length");
builder.AppendLine(header.Label, " Label");
builder.AppendLine(header.CommentsLength, " Comments length");
builder.AppendLine(header.Comments, " Comments");
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownBlock1? block)
{
builder.AppendLine(" Unknown Block 1 Information:");
builder.AppendLine(" -------------------------");
if (block == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(block.Length, " Length");
builder.AppendLine(block.Data, " Data");
builder.AppendLine();
}
private static void Print(StringBuilder builder, uint? value)
{
builder.AppendLine(" Unknown Value 2 Information:");
builder.AppendLine(" -------------------------");
if (value == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(value, " Value");
builder.AppendLine();
}
private static void Print(StringBuilder builder, UnknownBlock3? block)
{
builder.AppendLine(" Unknown Block 3 Information:");
builder.AppendLine(" -------------------------");
if (block == null)
{
builder.AppendLine(" No unknown block 1r");
builder.AppendLine();
return;
}
builder.AppendLine(block.Data, " Data");
builder.AppendLine();
}
private static void Print(StringBuilder builder, uint count, DataFile?[]? entries)
{
builder.AppendLine(" Data Files Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine(count, " Data files count");
if (count == 0 || entries == null || entries.Length == 0)
{
builder.AppendLine(" No data files");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Data File {i}:");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FileNameLength, " File name length");
builder.AppendLine(entry.FileName, " File name");
builder.AppendLine(entry.DataLength, " Data length");
builder.AppendLine(entry.Data, " Data");
}
builder.AppendLine();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
using System.Text;
using SabreTools.Models.Quantum;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class Quantum : IPrinter<Archive>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Archive model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Archive archive)
{
builder.AppendLine("Quantum Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, archive.Header);
Print(builder, archive.FileList);
builder.AppendLine(archive.CompressedDataOffset, " Compressed data offset");
builder.AppendLine();
}
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.Signature, " Signature");
builder.AppendLine(header.MajorVersion, " Major version");
builder.AppendLine(header.MinorVersion, " Minor version");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.TableSize, " Table size");
builder.AppendLine(header.CompressionFlags, " Compression flags");
builder.AppendLine();
}
private static void Print(StringBuilder builder, FileDescriptor?[]? entries)
{
builder.AppendLine(" File List Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No file list items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var fileDescriptor = entries[i];
builder.AppendLine($" File Descriptor {i}");
if (fileDescriptor == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(fileDescriptor.FileNameSize, " File name size");
builder.AppendLine(fileDescriptor.FileName, " File name");
builder.AppendLine(fileDescriptor.CommentFieldSize, " Comment field size");
builder.AppendLine(fileDescriptor.CommentField, " Comment field");
builder.AppendLine(fileDescriptor.ExpandedFileSize, " Expanded file size");
builder.AppendLine(fileDescriptor.FileTime, " File time");
builder.AppendLine(fileDescriptor.FileDate, " File date");
if (fileDescriptor.Unknown != null)
builder.AppendLine(fileDescriptor.Unknown.Value, " Unknown (Checksum?)");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,406 @@
using System.Text;
using SabreTools.Models.SGA;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class SGA : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("SGA Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
// Header
Print(builder, file.Header);
// Directory
Print(builder, file.Directory);
// TODO: Should we print the string table?
}
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.Signature, " Signature");
builder.AppendLine(header.MajorVersion, " Major version");
builder.AppendLine(header.MinorVersion, " Minor version");
switch (header)
{
case Header4 header4:
builder.AppendLine(header4.FileMD5, " File MD5");
builder.AppendLine(header4.Name, " Name");
builder.AppendLine(header4.HeaderMD5, " Header MD5");
builder.AppendLine(header4.HeaderLength, " Header length");
builder.AppendLine(header4.FileDataOffset, " File data offset");
builder.AppendLine(header4.Dummy0, " Dummy 0");
break;
case Header6 header6:
builder.AppendLine(header6.Name, " Name");
builder.AppendLine(header6.HeaderLength, " Header length");
builder.AppendLine(header6.FileDataOffset, " File data offset");
builder.AppendLine(header6.Dummy0, " Dummy 0");
break;
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Directory? directory)
{
builder.AppendLine(" Directory Information:");
builder.AppendLine(" -------------------------");
if (directory == null)
{
builder.AppendLine(" No directory");
builder.AppendLine();
return;
}
switch (directory)
{
case Directory4 directory4:
Print(builder, directory4.DirectoryHeader);
Print(builder, directory4.Sections);
Print(builder, directory4.Folders);
Print(builder, directory4.Files);
break;
case Directory5 directory5:
Print(builder, directory5.DirectoryHeader);
Print(builder, directory5.Sections);
Print(builder, directory5.Folders);
Print(builder, directory5.Files);
break;
case Directory6 directory6:
Print(builder, directory6.DirectoryHeader);
Print(builder, directory6.Sections);
Print(builder, directory6.Folders);
Print(builder, directory6.Files);
break;
case Directory7 directory7:
Print(builder, directory7.DirectoryHeader);
Print(builder, directory7.Sections);
Print(builder, directory7.Folders);
Print(builder, directory7.Files);
break;
default:
builder.AppendLine($" Unrecognized directory type");
break;
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryHeader4? header)
{
builder.AppendLine(" Directory Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory header");
builder.AppendLine();
return;
}
builder.AppendLine(header.SectionOffset, " Section offset");
builder.AppendLine(header.SectionCount, " Section count");
builder.AppendLine(header.FolderOffset, " Folder offset");
builder.AppendLine(header.FolderCount, " Folder count");
builder.AppendLine(header.FileOffset, " File offset");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.StringTableOffset, " String table offset");
builder.AppendLine(header.StringTableCount, " String table count");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryHeader5? header)
{
builder.AppendLine(" Directory Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory header");
builder.AppendLine();
return;
}
builder.AppendLine(header.SectionOffset, " Section offset");
builder.AppendLine(header.SectionCount, " Section count");
builder.AppendLine(header.FolderOffset, " Folder offset");
builder.AppendLine(header.FolderCount, " Folder count");
builder.AppendLine(header.FileOffset, " File offset");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.StringTableOffset, " String table offset");
builder.AppendLine(header.StringTableCount, " String table count");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryHeader7? header)
{
builder.AppendLine(" Directory Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No directory header");
builder.AppendLine();
return;
}
builder.AppendLine(header.SectionOffset, " Section offset");
builder.AppendLine(header.SectionCount, " Section count");
builder.AppendLine(header.FolderOffset, " Folder offset");
builder.AppendLine(header.FolderCount, " Folder count");
builder.AppendLine(header.FileOffset, " File offset");
builder.AppendLine(header.FileCount, " File count");
builder.AppendLine(header.StringTableOffset, " String table offset");
builder.AppendLine(header.StringTableCount, " String table count");
builder.AppendLine(header.HashTableOffset, " Hash table offset");
builder.AppendLine(header.BlockSize, " Block size");
builder.AppendLine();
}
private static void Print(StringBuilder builder, Section4?[]? sections)
{
builder.AppendLine(" Sections Information:");
builder.AppendLine(" -------------------------");
if (sections == null || sections.Length == 0)
{
builder.AppendLine(" No sections");
builder.AppendLine();
return;
}
for (int i = 0; i < sections.Length; i++)
{
builder.AppendLine($" Section {i}");
var section = sections[i];
if (section == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(section.Alias, " Alias");
builder.AppendLine(section.Name, " Name");
builder.AppendLine(section.FolderStartIndex, " Folder start index");
builder.AppendLine(section.FolderEndIndex, " Folder end index");
builder.AppendLine(section.FileStartIndex, " File start index");
builder.AppendLine(section.FileEndIndex, " File end index");
builder.AppendLine(section.FolderRootIndex, " Folder root index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Section5?[]? sections)
{
builder.AppendLine(" Sections Information:");
builder.AppendLine(" -------------------------");
if (sections == null || sections.Length == 0)
{
builder.AppendLine(" No sections");
builder.AppendLine();
return;
}
for (int i = 0; i < sections.Length; i++)
{
builder.AppendLine($" Section {i}");
var section = sections[i];
if (section == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(section.Alias, " Alias");
builder.AppendLine(section.Name, " Name");
builder.AppendLine(section.FolderStartIndex, " Folder start index");
builder.AppendLine(section.FolderEndIndex, " Folder end index");
builder.AppendLine(section.FileStartIndex, " File start index");
builder.AppendLine(section.FileEndIndex, " File end index");
builder.AppendLine(section.FolderRootIndex, " Folder root index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Folder4?[]? folders)
{
builder.AppendLine(" Folders Information:");
builder.AppendLine(" -------------------------");
if (folders == null || folders.Length == 0)
{
builder.AppendLine(" No folders");
builder.AppendLine();
return;
}
for (int i = 0; i < folders.Length; i++)
{
builder.AppendLine($" Folder {i}");
var folder = folders[i];
if (folder == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(folder.NameOffset, " Name offset");
builder.AppendLine(folder.Name, " Name");
builder.AppendLine(folder.FolderStartIndex, " Folder start index");
builder.AppendLine(folder.FolderEndIndex, " Folder end index");
builder.AppendLine(folder.FileStartIndex, " File start index");
builder.AppendLine(folder.FileEndIndex, " File end index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Folder5?[]? folders)
{
builder.AppendLine(" Folders Information:");
builder.AppendLine(" -------------------------");
if (folders == null || folders.Length == 0)
{
builder.AppendLine(" No folders");
builder.AppendLine();
return;
}
for (int i = 0; i < folders.Length; i++)
{
builder.AppendLine($" Folder {i}");
var folder = folders[i] as Folder5;
if (folder == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(folder.NameOffset, " Name offset");
builder.AppendLine(folder.Name, " Name");
builder.AppendLine(folder.FolderStartIndex, " Folder start index");
builder.AppendLine(folder.FolderEndIndex, " Folder end index");
builder.AppendLine(folder.FileStartIndex, " File start index");
builder.AppendLine(folder.FileEndIndex, " File end index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, File4?[]? files)
{
builder.AppendLine(" Files Information:");
builder.AppendLine(" -------------------------");
if (files == null || files.Length == 0)
{
builder.AppendLine(" No files");
builder.AppendLine();
return;
}
for (int i = 0; i < files.Length; i++)
{
builder.AppendLine($" File {i}");
var file = files[i];
if (file == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(file.NameOffset, " Name offset");
builder.AppendLine(file.Name, " Name");
builder.AppendLine(file.Offset, " Offset");
builder.AppendLine(file.SizeOnDisk, " Size on disk");
builder.AppendLine(file.Size, " Size");
builder.AppendLine(file.TimeModified, " Time modified");
builder.AppendLine(file.Dummy0, " Dummy 0");
builder.AppendLine(file.Type, " Type");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, File6?[]? files)
{
builder.AppendLine(" Files Information:");
builder.AppendLine(" -------------------------");
if (files == null || files.Length == 0)
{
builder.AppendLine(" No files");
builder.AppendLine();
return;
}
for (int i = 0; i < files.Length; i++)
{
builder.AppendLine($" File {i}");
var file = files[i];
if (file == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(file.NameOffset, " Name offset");
builder.AppendLine(file.Name, " Name");
builder.AppendLine(file.Offset, " Offset");
builder.AppendLine(file.SizeOnDisk, " Size on disk");
builder.AppendLine(file.Size, " Size");
builder.AppendLine(file.TimeModified, " Time modified");
builder.AppendLine(file.Dummy0, " Dummy 0");
builder.AppendLine(file.Type, " Type");
builder.AppendLine(file.CRC32, " CRC32");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, File7?[]? files)
{
builder.AppendLine(" Files Information:");
builder.AppendLine(" -------------------------");
if (files == null || files.Length == 0)
{
builder.AppendLine(" No files");
builder.AppendLine();
return;
}
for (int i = 0; i < files.Length; i++)
{
builder.AppendLine($" File {i}");
var file = files[i];
if (file == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(file.NameOffset, " Name offset");
builder.AppendLine(file.Name, " Name");
builder.AppendLine(file.Offset, " Offset");
builder.AppendLine(file.SizeOnDisk, " Size on disk");
builder.AppendLine(file.Size, " Size");
builder.AppendLine(file.TimeModified, " Time modified");
builder.AppendLine(file.Dummy0, " Dummy 0");
builder.AppendLine(file.Type, " Type");
builder.AppendLine(file.CRC32, " CRC32");
builder.AppendLine(file.HashOffset, " Hash offset");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,82 @@
using System.Text;
using SabreTools.Models.VBSP;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Models.VBSP.Constants;
namespace SabreTools.Serialization.Printers
{
public class VBSP : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("VBSP Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
}
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.Signature, " Signature");
builder.AppendLine(header.Version, " Version");
builder.AppendLine(header.MapRevision, " Map revision");
builder.AppendLine();
Print(builder, header.Lumps);
}
private static void Print(StringBuilder builder, Lump?[]? lumps)
{
builder.AppendLine(" Lumps Information:");
builder.AppendLine(" -------------------------");
if (lumps == null || lumps.Length == 0)
{
builder.AppendLine(" No lumps");
builder.AppendLine();
return;
}
for (int i = 0; i < lumps.Length; i++)
{
var lump = lumps[i];
string specialLumpName = string.Empty;
switch (i)
{
case HL_VBSP_LUMP_ENTITIES:
specialLumpName = " (entities)";
break;
case HL_VBSP_LUMP_PAKFILE:
specialLumpName = " (pakfile)";
break;
}
builder.AppendLine($" Lump {i}{specialLumpName}");
if (lump == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(lump.Offset, " Offset");
builder.AppendLine(lump.Length, " Length");
builder.AppendLine(lump.Version, " Version");
builder.AppendLine(lump.FourCC, " 4CC");
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,140 @@
using System.Text;
using SabreTools.Models.VPK;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class VPK : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("VPK Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
Print(builder, file.ExtendedHeader);
Print(builder, file.ArchiveHashes);
Print(builder, file.DirectoryItems);
}
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.Signature, " Signature");
builder.AppendLine(header.Version, " Version");
builder.AppendLine(header.DirectoryLength, " Directory length");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ExtendedHeader? header)
{
builder.AppendLine(" Extended Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No extended header");
builder.AppendLine();
return;
}
builder.AppendLine(header.Dummy0, " Dummy 0");
builder.AppendLine(header.ArchiveHashLength, " Archive hash length");
builder.AppendLine(header.ExtraLength, " Extra length");
builder.AppendLine(header.Dummy1, " Dummy 1");
builder.AppendLine();
}
private static void Print(StringBuilder builder, ArchiveHash?[]? entries)
{
builder.AppendLine(" Archive Hashes Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No archive hashes");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Archive Hash {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.ArchiveIndex, " Archive index");
builder.AppendLine(entry.ArchiveOffset, " Archive offset");
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Hash, " Hash");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryItem?[]? entries)
{
builder.AppendLine(" Directory Items Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Item {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
builder.AppendLine();
continue;
}
builder.AppendLine(entry.Extension, " Extension");
builder.AppendLine(entry.Path, " Path");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine();
Print(builder, entry.DirectoryEntry);
// TODO: Print out preload data?
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryEntry? entry)
{
builder.AppendLine(" Directory Entry:");
builder.AppendLine(" -------------------------");
if (entry == null)
{
builder.AppendLine(" [NULL]");
return;
}
builder.AppendLine(entry.CRC, " CRC");
builder.AppendLine(entry.PreloadBytes, " Preload bytes");
builder.AppendLine(entry.ArchiveIndex, " Archive index");
builder.AppendLine(entry.EntryOffset, " Entry offset");
builder.AppendLine(entry.EntryLength, " Entry length");
builder.AppendLine(entry.Dummy0, " Dummy 0");
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Text;
using SabreTools.Models.WAD;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class WAD : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("WAD Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
Print(builder, file.Lumps);
Print(builder, file.LumpInfos);
}
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.Signature, " Signature");
builder.AppendLine(header.LumpCount, " Lump count");
builder.AppendLine(header.LumpOffset, " Lump offset");
builder.AppendLine();
}
private static void Print(StringBuilder builder, Lump?[]? entries)
{
builder.AppendLine(" Lumps Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No lumps");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Lump {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.Offset, " Offset");
builder.AppendLine(entry.DiskLength, " Disk length");
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.Type, " Type");
builder.AppendLine(entry.Compression, " Compression");
builder.AppendLine(entry.Padding0, " Padding 0");
builder.AppendLine(entry.Padding1, " Padding 1");
builder.AppendLine(entry.Name, " Name");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, LumpInfo?[]? entries)
{
builder.AppendLine(" Lump Infos Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No lump infos");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Lump Info {i}");
if (entry == null)
{
builder.AppendLine(" Lump is compressed");
continue;
}
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.Width, " Width");
builder.AppendLine(entry.Height, " Height");
builder.AppendLine(entry.PixelOffset, " Pixel offset");
// TODO: Print unknown data?
// TODO: Print pixel data?
builder.AppendLine(entry.PaletteSize, " Palette size");
// TODO: Print palette data?
}
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Text;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Models.Xbox.Constants;
namespace SabreTools.Serialization.Printers
{
public class XMID : IPrinter<Models.Xbox.XMID>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Models.Xbox.XMID model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Models.Xbox.XMID xmid)
{
builder.AppendLine("Xbox Media Identifier Information:");
builder.AppendLine("-------------------------");
builder.AppendLine(xmid.PublisherIdentifier, "Publisher identifier");
if (!string.IsNullOrEmpty(xmid.PublisherIdentifier) && Publishers.ContainsKey(xmid.PublisherIdentifier ?? string.Empty))
builder.AppendLine(Publishers[xmid.PublisherIdentifier ?? string.Empty], "Publisher");
builder.AppendLine(xmid.GameID, "Game ID");
builder.AppendLine(xmid.VersionNumber, "Version number");
builder.AppendLine(xmid.RegionIdentifier, "Region identifier");
if (Regions.ContainsKey(xmid.RegionIdentifier))
builder.AppendLine(Regions[xmid.RegionIdentifier], "Region");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,149 @@
using System.Text;
using SabreTools.Models.XZP;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
{
public class XZP : IPrinter<File>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, File model)
=> Print(builder, model);
public static void Print(StringBuilder builder, File file)
{
builder.AppendLine("XZP Information:");
builder.AppendLine("-------------------------");
builder.AppendLine();
Print(builder, file.Header);
Print(builder, file.DirectoryEntries, "Directory");
Print(builder, file.PreloadDirectoryEntries, "Preload Directory");
Print(builder, file.PreloadDirectoryMappings);
Print(builder, file.DirectoryItems);
Print(builder, file.Footer);
}
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.Signature, " Signature");
builder.AppendLine(header.Version, " Version");
builder.AppendLine(header.PreloadDirectoryEntryCount, " Preload directory entry count");
builder.AppendLine(header.DirectoryEntryCount, " Directory entry count");
builder.AppendLine(header.PreloadBytes, " Preload bytes");
builder.AppendLine(header.HeaderLength, " Header length");
builder.AppendLine(header.DirectoryItemCount, " Directory item count");
builder.AppendLine(header.DirectoryItemOffset, " Directory item offset");
builder.AppendLine(header.DirectoryItemLength, " Directory item length");
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryEntry?[]? entries, string prefix)
{
builder.AppendLine($" {prefix} Entries Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory entries");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Entry {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FileNameCRC, " File name CRC");
builder.AppendLine(entry.EntryLength, " Entry length");
builder.AppendLine(entry.EntryOffset, " Entry offset");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryMapping?[]? entries)
{
builder.AppendLine(" Preload Directory Mappings Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No preload directory mappings");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Mapping {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.PreloadDirectoryEntryIndex, " Preload directory entry index");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, DirectoryItem?[]? entries)
{
builder.AppendLine(" Directory Items Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No directory items");
builder.AppendLine();
return;
}
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
builder.AppendLine($" Directory Item {i}");
if (entry == null)
{
builder.AppendLine(" [NULL]");
continue;
}
builder.AppendLine(entry.FileNameCRC, " File name CRC");
builder.AppendLine(entry.NameOffset, " Name offset");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.TimeCreated, " Time created");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Footer? footer)
{
builder.AppendLine(" Footer Information:");
builder.AppendLine(" -------------------------");
if (footer == null)
{
builder.AppendLine(" No header");
builder.AppendLine();
return;
}
builder.AppendLine(footer.FileLength, " File length");
builder.AppendLine(footer.Signature, " Signature");
builder.AppendLine();
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Text;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Models.Xbox.Constants;
namespace SabreTools.Serialization.Printers
{
public class XeMID : IPrinter<Models.Xbox.XeMID>
{
/// <inheritdoc/>
public void PrintInformation(StringBuilder builder, Models.Xbox.XeMID model)
=> Print(builder, model);
public static void Print(StringBuilder builder, Models.Xbox.XeMID xemid)
{
builder.AppendLine("Xbox Media Identifier Information:");
builder.AppendLine("-------------------------");
builder.AppendLine(xemid.PublisherIdentifier, "Publisher identifier");
if (!string.IsNullOrEmpty(xemid.PublisherIdentifier) && Publishers.ContainsKey(xemid.PublisherIdentifier ?? string.Empty))
builder.AppendLine(Publishers[xemid.PublisherIdentifier ?? string.Empty], "Publisher");
builder.AppendLine(xemid.PlatformIdentifier, "Platform identifier");
builder.AppendLine(xemid.GameID, "Game ID");
builder.AppendLine(xemid.SKU, "SKU");
builder.AppendLine(xemid.RegionIdentifier, "Region identifier");
if (Regions.ContainsKey(xemid.RegionIdentifier))
builder.AppendLine(Regions[xemid.RegionIdentifier], "Region");
builder.AppendLine(xemid.BaseVersion, "Base version");
builder.AppendLine(xemid.MediaSubtypeIdentifier, "Media subtype identifier");
if (MediaSubtypes.ContainsKey(xemid.MediaSubtypeIdentifier))
builder.AppendLine(MediaSubtypes[xemid.MediaSubtypeIdentifier], "Media subtype");
builder.AppendLine(xemid.DiscNumberIdentifier, "Disc number identifier");
builder.AppendLine(xemid.CertificationSubmissionIdentifier, "Certification submission identifier");
builder.AppendLine();
}
}
}

View File

@@ -8,7 +8,7 @@
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.5.6</Version>
<Version>1.6.2</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
@@ -28,8 +28,9 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.ASN1" Version="1.3.2" />
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
<PackageReference Include="SabreTools.IO" Version="1.4.0" />
<PackageReference Include="SabreTools.IO" Version="1.4.9" />
<PackageReference Include="SabreTools.Models" Version="1.4.5" />
</ItemGroup>

View File

@@ -0,0 +1,208 @@
using System;
using System.Linq;
using System.Text;
namespace SabreTools.Serialization
{
// TODO: Add extension for printing enums, if possible
internal static class StringBuilderExtensions
{
/// <summary>
/// Append a line containing a boolean to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, bool? value, string prefixString)
{
value ??= false;
return sb.AppendLine($"{prefixString}: {value}");
}
/// <summary>
/// Append a line containing a Char to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, char? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : value.Value.ToString());
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int8 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, sbyte? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X2})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt8 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, byte? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X2})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int16 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, short? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X4})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt16 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, ushort? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X4})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int32 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, int? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X8})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt32 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, uint? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X8})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int64 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, long? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X16})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt64 to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, ulong? value, string prefixString)
{
value ??= 0;
string valueString = $"{value} (0x{value:X16})";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a string to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, string? value, string prefixString)
{
string valueString = value ?? "[NULL]";
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Guid to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, Guid? value, string prefixString)
{
value ??= Guid.Empty;
string valueString = value.Value.ToString();
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt8[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, byte[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : BitConverter.ToString(value).Replace('-', ' '));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt8[] value as a string to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, byte[]? value, string prefixString, Encoding encoding)
{
string valueString = (value == null ? "[NULL]" : encoding.GetString(value).Replace("\0", string.Empty));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Char[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, char[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(c => c.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int16[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, short[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(s => s.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt16[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, ushort[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(u => u.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int32[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, int[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(i => i.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt32[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, uint[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(u => u.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a Int64[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, long[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(l => l.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
/// <summary>
/// Append a line containing a UInt64[] value to a StringBuilder
/// </summary>
public static StringBuilder AppendLine(this StringBuilder sb, ulong[]? value, string prefixString)
{
string valueString = (value == null ? "[NULL]" : string.Join(", ", value.Select(u => u.ToString()).ToArray()));
return sb.AppendLine($"{prefixString}: {valueString}");
}
}
}

View File

@@ -0,0 +1,79 @@
using System.IO;
using SabreTools.Models.PKZIP;
namespace SabreTools.Serialization.Wrappers
{
public class PKZIP : WrapperBase<Archive>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "PKZIP Archive (or Derived Format)";
#endregion
#region Constructors
/// <inheritdoc/>
public PKZIP(Archive? model, byte[]? data, int offset)
: base(model, data, offset)
{
// All logic is handled by the base class
}
/// <inheritdoc/>
public PKZIP(Archive? model, Stream? data)
: base(model, data)
{
// All logic is handled by the base class
}
/// <summary>
/// Create a PKZIP archive (or derived format) 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 PKZIP wrapper on success, null on failure</returns>
public static PKZIP? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data == null)
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 PKZIP archive (or derived format) from a Stream
/// </summary>
/// <param name="data">Stream representing the archive</param>
/// <returns>A PKZIP wrapper on success, null on failure</returns>
public static PKZIP? Create(Stream? data)
{
// If the data is invalid
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
return null;
var archive = Deserializers.PKZIP.DeserializeStream(data);
if (archive == null)
return null;
try
{
return new PKZIP(archive, data);
}
catch
{
return null;
}
}
#endregion
}
}

View File

@@ -0,0 +1,922 @@
using System;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Wrappers
{
public static class WrapperFactory
{
/// <summary>
/// Create an instance of a wrapper based on file type
/// </summary>
public static IWrapper? CreateWrapper(WrapperType fileType, Stream? data)
{
switch (fileType)
{
case WrapperType.AACSMediaKeyBlock: return AACSMediaKeyBlock.Create(data);
case WrapperType.BDPlusSVM: return BDPlusSVM.Create(data);
case WrapperType.BFPK: return BFPK.Create(data);
case WrapperType.BSP: return BSP.Create(data);
case WrapperType.BZip2: return null; // TODO: Implement wrapper
case WrapperType.CFB: return CFB.Create(data);
case WrapperType.CIA: return CIA.Create(data);
case WrapperType.Executable: return CreateExecutableWrapper(data);
case WrapperType.GCF: return GCF.Create(data);
case WrapperType.GZIP: return null; // TODO: Implement wrapper
case WrapperType.IniFile: return null; // TODO: Implement wrapper
case WrapperType.InstallShieldArchiveV3: return null; // TODO: Implement wrapper
case WrapperType.InstallShieldCAB: return InstallShieldCabinet.Create(data);
case WrapperType.LDSCRYPT: return null; // TODO: Implement wrapper
case WrapperType.MicrosoftCAB: return MicrosoftCabinet.Create(data);
case WrapperType.MicrosoftLZ: return null; // TODO: Implement wrapper
case WrapperType.MoPaQ: return MoPaQ.Create(data);
case WrapperType.N3DS: return N3DS.Create(data);
case WrapperType.NCF: return NCF.Create(data);
case WrapperType.Nitro: return Nitro.Create(data);
case WrapperType.PAK: return PAK.Create(data);
case WrapperType.PFF: return PFF.Create(data);
case WrapperType.PIC: return PIC.Create(data);
case WrapperType.PKZIP: return PKZIP.Create(data);
case WrapperType.PlayJAudioFile: return PlayJAudioFile.Create(data);
case WrapperType.PlayJPlaylist: return PlayJPlaylist.Create(data);
case WrapperType.Quantum: return Quantum.Create(data);
case WrapperType.RAR: return null; // TODO: Implement wrapper
case WrapperType.RealArcadeInstaller: return null; // TODO: Implement wrapper
case WrapperType.RealArcadeMezzanine: return null; // TODO: Implement wrapper
case WrapperType.SevenZip: return null; // TODO: Implement wrapper
case WrapperType.SFFS: return null; // TODO: Implement wrapper
case WrapperType.SGA: return SGA.Create(data);
case WrapperType.TapeArchive: return null; // TODO: Implement wrapper
case WrapperType.Textfile: return null; // TODO: Implement wrapper
case WrapperType.VBSP: return VBSP.Create(data);
case WrapperType.VPK: return VPK.Create(data);
case WrapperType.WAD: return WAD.Create(data);
case WrapperType.XZ: return null; // TODO: Implement wrapper
case WrapperType.XZP: return XZP.Create(data);
default: return null;
}
}
/// <summary>
/// Create an instance of a wrapper based on the executable type
/// </summary>
/// <param name="stream">Stream data to parse</param>
/// <returns>IWrapper representing the executable, null on error</returns>
public static IWrapper? CreateExecutableWrapper(Stream? stream)
{
// If we have no stream
if (stream == null)
return null;
// Try to get an MS-DOS wrapper first
var wrapper = MSDOS.Create(stream);
if (wrapper == null || wrapper is not MSDOS msdos)
return null;
// Check for a valid new executable address
if (msdos.Model.Header?.NewExeHeaderAddr == null || msdos.Model.Header.NewExeHeaderAddr >= stream.Length)
return wrapper;
// Try to read the executable info
stream.Seek(msdos.Model.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var magic = stream.ReadBytes(4);
// If we didn't get valid data at the offset
if (magic == null)
{
return wrapper;
}
// New Executable
else if (magic.StartsWith(Models.NewExecutable.Constants.SignatureBytes))
{
stream.Seek(0, SeekOrigin.Begin);
return NewExecutable.Create(stream);
}
// Linear Executable
else if (magic.StartsWith(Models.LinearExecutable.Constants.LESignatureBytes)
|| magic.StartsWith(Models.LinearExecutable.Constants.LXSignatureBytes))
{
stream.Seek(0, SeekOrigin.Begin);
return LinearExecutable.Create(stream);
}
// Portable Executable
else if (magic.StartsWith(Models.PortableExecutable.Constants.SignatureBytes))
{
stream.Seek(0, SeekOrigin.Begin);
return PortableExecutable.Create(stream);
}
// Everything else fails
return null;
}
/// <summary>
/// Get the supported file type for a magic string
/// </summary>
/// <remarks>Recommend sending in 16 bytes to check</remarks>
public static WrapperType GetFileType(byte[] magic)
{
// If we have an invalid magic byte array
if (magic == null || magic.Length == 0)
return WrapperType.UNKNOWN;
// TODO: For all modelled types, use the constants instead of hardcoded values here
#region AACSMediaKeyBlock
// Block starting with verify media key record
if (magic.StartsWith(new byte?[] { 0x81, 0x00, 0x00, 0x14 }))
return WrapperType.AACSMediaKeyBlock;
// Block starting with type and version record
if (magic.StartsWith(new byte?[] { 0x10, 0x00, 0x00, 0x0C }))
return WrapperType.AACSMediaKeyBlock;
#endregion
#region BDPlusSVM
if (magic.StartsWith(new byte?[] { 0x42, 0x44, 0x53, 0x56, 0x4D, 0x5F, 0x43, 0x43 }))
return WrapperType.BDPlusSVM;
#endregion
#region BFPK
if (magic.StartsWith(new byte?[] { 0x42, 0x46, 0x50, 0x4b }))
return WrapperType.BFPK;
#endregion
#region BSP
if (magic.StartsWith(new byte?[] { 0x1e, 0x00, 0x00, 0x00 }))
return WrapperType.BSP;
#endregion
#region BZip2
if (magic.StartsWith(new byte?[] { 0x42, 0x52, 0x68 }))
return WrapperType.BZip2;
#endregion
#region CFB
if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }))
return WrapperType.CFB;
#endregion
#region CIA
// No magic checks for CIA
#endregion
#region Executable
// DOS MZ executable file format (and descendants)
if (magic.StartsWith(new byte?[] { 0x4d, 0x5a }))
return WrapperType.Executable;
/*
// None of the following are supported yet
// Executable and Linkable Format
if (magic.StartsWith(new byte?[] { 0x7f, 0x45, 0x4c, 0x46 }))
return FileTypes.Executable;
// Mach-O binary (32-bit)
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xce }))
return FileTypes.Executable;
// Mach-O binary (32-bit, reverse byte ordering scheme)
if (magic.StartsWith(new byte?[] { 0xce, 0xfa, 0xed, 0xfe }))
return FileTypes.Executable;
// Mach-O binary (64-bit)
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xcf }))
return FileTypes.Executable;
// Mach-O binary (64-bit, reverse byte ordering scheme)
if (magic.StartsWith(new byte?[] { 0xcf, 0xfa, 0xed, 0xfe }))
return FileTypes.Executable;
// Prefrred Executable File Format
if (magic.StartsWith(new byte?[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 }))
return FileTypes.Executable;
*/
#endregion
#region GCF
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }))
return WrapperType.GCF;
#endregion
#region GZIP
if (magic.StartsWith(new byte?[] { 0x1f, 0x8b }))
return WrapperType.GZIP;
#endregion
#region IniFile
// No magic checks for IniFile
#endregion
#region InstallShieldArchiveV3
if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C }))
return WrapperType.InstallShieldArchiveV3;
#endregion
#region InstallShieldCAB
if (magic.StartsWith(new byte?[] { 0x49, 0x53, 0x63 }))
return WrapperType.InstallShieldCAB;
#endregion
#region LDSCRYPT
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
return WrapperType.LDSCRYPT;
#endregion
#region MicrosoftCAB
if (magic.StartsWith(new byte?[] { 0x4d, 0x53, 0x43, 0x46 }))
return WrapperType.MicrosoftCAB;
#endregion
#region MicrosoftLZ
if (magic.StartsWith(new byte?[] { 0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33 }))
return WrapperType.MicrosoftLZ;
#endregion
#region MPQ
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
return WrapperType.MoPaQ;
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1b }))
return WrapperType.MoPaQ;
#endregion
#region N3DS
// No magic checks for N3DS
#endregion
#region NCF
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }))
return WrapperType.NCF;
#endregion
#region Nitro
// No magic checks for Nitro
#endregion
#region PAK
if (magic.StartsWith(new byte?[] { 0x50, 0x41, 0x43, 0x4B }))
return WrapperType.PAK;
#endregion
#region PFF
// Version 2
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x32 }))
return WrapperType.PFF;
// Version 3
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x33 }))
return WrapperType.PFF;
// Version 4
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x34 }))
return WrapperType.PFF;
#endregion
#region PKZIP
// PKZIP (Unknown)
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x00, 0x00 }))
return WrapperType.PKZIP;
// PKZIP
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x03, 0x04 }))
return WrapperType.PKZIP;
// PKZIP (Empty Archive)
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x05, 0x06 }))
return WrapperType.PKZIP;
// PKZIP (Spanned Archive)
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x07, 0x08 }))
return WrapperType.PKZIP;
#endregion
#region PLJ
// https://www.iana.org/assignments/media-types/audio/vnd.everad.plj
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
return WrapperType.PlayJAudioFile;
#endregion
#region Quantum
if (magic.StartsWith(new byte?[] { 0x44, 0x53 }))
return WrapperType.Quantum;
#endregion
#region RAR
// RAR archive version 1.50 onwards
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
return WrapperType.RAR;
// RAR archive version 5.0 onwards
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
return WrapperType.RAR;
#endregion
#region RealArcade
// RASGI2.0
// Found in the ".rgs files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith(new byte?[] { 0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30 }))
return WrapperType.RealArcadeInstaller;
// XZip2.0
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith(new byte?[] { 0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30 }))
return WrapperType.RealArcadeMezzanine;
#endregion
#region SevenZip
if (magic.StartsWith(new byte?[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
return WrapperType.SevenZip;
#endregion
#region SFFS
// Found in Redump entry 81756, confirmed to be "StarForce Filesystem" by PiD.
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
return WrapperType.SFFS;
#endregion
#region SGA
if (magic.StartsWith(new byte?[] { 0x5F, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45 }))
return WrapperType.SGA;
#endregion
#region TapeArchive
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
return WrapperType.TapeArchive;
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
return WrapperType.TapeArchive;
#endregion
#region Textfile
// Not all textfiles can be determined through magic number
// HTML
if (magic.StartsWith(new byte?[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
return WrapperType.Textfile;
// HTML and XML
if (magic.StartsWith(new byte?[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
return WrapperType.Textfile;
// InstallShield Compiled Rules
if (magic.StartsWith(new byte?[] { 0x61, 0x4C, 0x75, 0x5A }))
return WrapperType.Textfile;
// Microsoft Office File (old)
if (magic.StartsWith(new byte?[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
return WrapperType.Textfile;
// Rich Text File
if (magic.StartsWith(new byte?[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
return WrapperType.Textfile;
// Windows Help File
if (magic.StartsWith(new byte?[] { 0x3F, 0x5F, 0x03, 0x00 }))
return WrapperType.Textfile;
// XML
// "<?xml"
if (magic.StartsWith(new byte?[] { 0x3C, 0x3F, 0x78, 0x6D, 0x6C }))
return WrapperType.Textfile;
#endregion
#region VBSP
if (magic.StartsWith(new byte?[] { 0x56, 0x42, 0x53, 0x50 }))
return WrapperType.VBSP;
#endregion
#region VPK
if (magic.StartsWith(new byte?[] { 0x34, 0x12, 0xaa, 0x55 }))
return WrapperType.VPK;
#endregion
#region WAD
if (magic.StartsWith(new byte?[] { 0x57, 0x41, 0x44, 0x33 }))
return WrapperType.WAD;
#endregion
#region XZ
if (magic.StartsWith(new byte?[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
return WrapperType.XZ;
#endregion
#region XZP
if (magic.StartsWith(new byte?[] { 0x70, 0x69, 0x5A, 0x78 }))
return WrapperType.XZP;
#endregion
// We couldn't find a supported match
return WrapperType.UNKNOWN;
}
/// <summary>
/// Get the supported file type for an extension
/// </summary>
/// <remarks>This is less accurate than a magic string match</remarks>
public static WrapperType GetFileType(string extension)
{
// If we have an invalid extension
if (string.IsNullOrEmpty(extension))
return WrapperType.UNKNOWN;
// Normalize the extension
extension = extension.TrimStart('.').Trim();
#region AACSMediaKeyBlock
// Shares an extension with INF setup information so it can't be used accurately
// Blu-ray
// if (extension.Equals("inf", StringComparison.OrdinalIgnoreCase))
// return WrapperType.AACSMediaKeyBlock;
// HD-DVD
if (extension.Equals("aacs", StringComparison.OrdinalIgnoreCase))
return WrapperType.AACSMediaKeyBlock;
#endregion
#region BDPlusSVM
if (extension.Equals("svm", StringComparison.OrdinalIgnoreCase))
return WrapperType.BDPlusSVM;
#endregion
#region BFPK
// No extensions registered for BFPK
#endregion
#region BSP
// Shares an extension with VBSP so it can't be used accurately
// if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
// return WrapperType.BSP;
#endregion
#region BZip2
if (extension.Equals("bz2", StringComparison.OrdinalIgnoreCase))
return WrapperType.BZip2;
#endregion
#region CFB
// Installer package
if (extension.Equals("msi", StringComparison.OrdinalIgnoreCase))
return WrapperType.CFB;
// Merge module
else if (extension.Equals("msm", StringComparison.OrdinalIgnoreCase))
return WrapperType.CFB;
// Patch Package
else if (extension.Equals("msp", StringComparison.OrdinalIgnoreCase))
return WrapperType.CFB;
// Transform
else if (extension.Equals("mst", StringComparison.OrdinalIgnoreCase))
return WrapperType.CFB;
// Patch Creation Properties
else if (extension.Equals("pcp", StringComparison.OrdinalIgnoreCase))
return WrapperType.CFB;
#endregion
#region CIA
if (extension.Equals("cia", StringComparison.OrdinalIgnoreCase))
return WrapperType.CIA;
#endregion
#region Executable
// DOS MZ executable file format (and descendants)
if (extension.Equals("exe", StringComparison.OrdinalIgnoreCase))
return WrapperType.Executable;
// DOS MZ library file format (and descendants)
if (extension.Equals("dll", StringComparison.OrdinalIgnoreCase))
return WrapperType.Executable;
#endregion
#region GCF
if (extension.Equals("gcf", StringComparison.OrdinalIgnoreCase))
return WrapperType.GCF;
#endregion
#region GZIP
if (extension.Equals("gz", StringComparison.OrdinalIgnoreCase))
return WrapperType.GZIP;
#endregion
#region IniFile
if (extension.Equals("ini", StringComparison.OrdinalIgnoreCase))
return WrapperType.IniFile;
#endregion
#region InstallShieldArchiveV3
if (extension.Equals("z", StringComparison.OrdinalIgnoreCase))
return WrapperType.InstallShieldArchiveV3;
#endregion
#region InstallShieldCAB
// No extensions registered for InstallShieldCAB
// Both InstallShieldCAB and MicrosoftCAB share the same extension
#endregion
#region MicrosoftCAB
// No extensions registered for InstallShieldCAB
// Both InstallShieldCAB and MicrosoftCAB share the same extension
#endregion
#region MPQ
if (extension.Equals("mpq", StringComparison.OrdinalIgnoreCase))
return WrapperType.MoPaQ;
#endregion
#region N3DS
// 3DS cart image
if (extension.Equals("3ds", StringComparison.OrdinalIgnoreCase))
return WrapperType.N3DS;
// CIA package -- Not currently supported
// else if (extension.Equals("cia", StringComparison.OrdinalIgnoreCase))
// return WrapperType.N3DS;
#endregion
#region NCF
if (extension.Equals("ncf", StringComparison.OrdinalIgnoreCase))
return WrapperType.NCF;
#endregion
#region Nitro
// DS cart image
if (extension.Equals("nds", StringComparison.OrdinalIgnoreCase))
return WrapperType.Nitro;
// DS development cart image
else if (extension.Equals("srl", StringComparison.OrdinalIgnoreCase))
return WrapperType.Nitro;
// DSi cart image
else if (extension.Equals("dsi", StringComparison.OrdinalIgnoreCase))
return WrapperType.Nitro;
// iQue DS cart image
else if (extension.Equals("ids", StringComparison.OrdinalIgnoreCase))
return WrapperType.Nitro;
#endregion
#region PAK
// No extensions registered for PAK
// Both PAK and Quantum share one extension
// if (extension.Equals("pak", StringComparison.OrdinalIgnoreCase))
// return WrapperType.PAK;
#endregion
#region PFF
if (extension.Equals("pff", StringComparison.OrdinalIgnoreCase))
return WrapperType.PFF;
#endregion
#region PKZIP
// PKZIP
if (extension.Equals("zip", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Android package
if (extension.Equals("apk", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Java archive
if (extension.Equals("jar", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Google Earth saved working session file
if (extension.Equals("kmz", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// KWord document
if (extension.Equals("kwd", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Microsoft Office Open XML Format (OOXML) Document
if (extension.Equals("docx", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Microsoft Office Open XML Format (OOXML) Presentation
if (extension.Equals("pptx", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Microsoft Office Open XML Format (OOXML) Spreadsheet
if (extension.Equals("xlsx", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenDocument text document
if (extension.Equals("odt", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenDocument presentation
if (extension.Equals("odp", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenDocument text document template
if (extension.Equals("ott", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Microsoft Open XML paper specification file
if (extension.Equals("oxps", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenOffice spreadsheet
if (extension.Equals("sxc", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenOffice drawing
if (extension.Equals("sxd", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenOffice presentation
if (extension.Equals("sxi", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// OpenOffice word processing
if (extension.Equals("sxw", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// StarOffice spreadsheet
if (extension.Equals("sxc", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Windows Media compressed skin file
if (extension.Equals("wmz", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// Mozilla Browser Archive
if (extension.Equals("xpi", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// XML paper specification file
if (extension.Equals("xps", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
// eXact Packager Models
if (extension.Equals("xpt", StringComparison.OrdinalIgnoreCase))
return WrapperType.PKZIP;
#endregion
#region PLJ
// https://www.iana.org/assignments/media-types/audio/vnd.everad.plj
if (extension.Equals("plj", StringComparison.OrdinalIgnoreCase))
return WrapperType.PlayJAudioFile;
#endregion
#region Quantum
if (extension.Equals("q", StringComparison.OrdinalIgnoreCase))
return WrapperType.Quantum;
// Both PAK and Quantum share one extension
// if (extension.Equals("pak", StringComparison.OrdinalIgnoreCase))
// return WrapperType.Quantum;
#endregion
#region RAR
if (extension.Equals("rar", StringComparison.OrdinalIgnoreCase))
return WrapperType.RAR;
#endregion
#region SevenZip
if (extension.Equals("7z", StringComparison.OrdinalIgnoreCase))
return WrapperType.SevenZip;
#endregion
#region SGA
if (extension.Equals("sga", StringComparison.OrdinalIgnoreCase))
return WrapperType.SGA;
#endregion
#region TapeArchive
if (extension.Equals("tar", StringComparison.OrdinalIgnoreCase))
return WrapperType.SevenZip;
#endregion
#region Textfile
// "Description in Zip"
if (extension.Equals("diz", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Generic textfile (no header)
if (extension.Equals("txt", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// HTML
if (extension.Equals("htm", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
if (extension.Equals("html", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// InstallShield Script
if (extension.Equals("ins", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Microsoft Office File (old)
if (extension.Equals("doc", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Property list
if (extension.Equals("plist", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Rich Text File
if (extension.Equals("rtf", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Setup information
if (extension.Equals("inf", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// Windows Help File
if (extension.Equals("hlp", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// WZC
if (extension.Equals("wzc", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
// XML
if (extension.Equals("xml", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
#endregion
#region VBSP
// Shares an extension with BSP so it can't be used accurately
// if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
// return WrapperType.VBSP;
#endregion
#region VPK
// Common extension so this cannot be used accurately
// if (extension.Equals("vpk", StringComparison.OrdinalIgnoreCase))
// return WrapperType.VPK;
#endregion
#region WAD
// Common extension so this cannot be used accurately
// if (extension.Equals("wad", StringComparison.OrdinalIgnoreCase))
// return WrapperType.WAD;
#endregion
#region XZ
if (extension.Equals("xz", StringComparison.OrdinalIgnoreCase))
return WrapperType.XZ;
#endregion
#region XZP
if (extension.Equals("xzp", StringComparison.OrdinalIgnoreCase))
return WrapperType.XZP;
#endregion
// We couldn't find a supported match
return WrapperType.UNKNOWN;
}
}
}

View File

@@ -0,0 +1,230 @@
namespace SabreTools.Serialization.Wrappers
{
/// <summary>
/// Represents each of the IWrapper implementations
/// </summary>
public enum WrapperType
{
/// <summary>
/// Unknown or unsupported
/// </summary>
UNKNOWN,
/// <summary>
/// AACS media key block
/// </summary>
AACSMediaKeyBlock,
/// <summary>
/// BD+ SVM
/// </summary>
BDPlusSVM,
/// <summary>
/// BFPK custom archive
/// </summary>
BFPK,
/// <summary>
/// Half-Life Level
/// </summary>
BSP,
/// <summary>
/// bzip2 archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
BZip2,
/// <summary>
/// Compound File Binary
/// </summary>
CFB,
/// <summary>
/// CTR Importable Archive
/// </summary>
CIA,
/// <summary>
/// Executable or library
/// </summary>
/// <remarks>Includes MZ, NE, LE/LX, and PE</remarks>
Executable,
/// <summary>
/// Half-Life Game Cache File
/// </summary>
GCF,
/// <summary>
/// gzip archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
GZIP,
/// <summary>
/// Key-value pair INI file
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
IniFile,
/// <summary>
/// InstallShield archive v3
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
InstallShieldArchiveV3,
/// <summary>
/// InstallShield cabinet file
/// </summary>
InstallShieldCAB,
/// <summary>
/// Link Data Security encrypted file
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
LDSCRYPT,
/// <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>
MoPaQ,
/// <summary>
/// Nintendo 3DS cart image
/// </summary>
N3DS,
/// <summary>
/// Half-Life No Cache File
/// </summary>
NCF,
/// <summary>
/// Nintendo DS/DSi cart image
/// </summary>
Nitro,
/// <summary>
/// Half-Life Package File
/// </summary>
PAK,
/// <summary>
/// NovaLogic Game Archive Format
/// </summary>
PFF,
/// <summary>
/// PIC data object
/// </summary>
/// <remarks>Currently has no detection method</remarks>
PIC,
/// <summary>
/// PKWARE ZIP archive and derivatives
/// </summary>
PKZIP,
/// <summary>
/// PlayJ audio file
/// </summary>
PlayJAudioFile,
/// <summary>
/// PlayJ playlist file
/// </summary>
/// <remarks>Currently has no detection method/remarks>
PlayJPlaylist,
/// <summary>
/// Quantum archive
/// </summary>
Quantum,
/// <summary>
/// RAR archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
RAR,
/// <summary>
/// RealArcade Installer
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
RealArcadeInstaller,
/// <summary>
/// RealArcade Mezzanine
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
RealArcadeMezzanine,
/// <summary>
/// 7-zip archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
SevenZip,
/// <summary>
/// StarForce FileSystem file
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
SFFS,
/// <summary>
/// SGA
/// </summary>
SGA,
/// <summary>
/// Tape archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
TapeArchive,
/// <summary>
/// Various generic textfile formats
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
Textfile,
/// <summary>
/// Half-Life 2 Level
/// </summary>
VBSP,
/// <summary>
/// Valve Package File
/// </summary>
VPK,
/// <summary>
/// Half-Life Texture Package File
/// </summary>
WAD,
/// <summary>
/// xz archive
/// </summary>
/// <remarks>Currently has no IWrapper implementation</remarks>
XZ,
/// <summary>
/// Xbox Package File
/// </summary>
XZP,
}
}

119
Test/Options.cs Normal file
View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
namespace Test
{
/// <summary>
/// Set of options for the test executable
/// </summary>
internal sealed class Options
{
#region Properties
/// <summary>
/// Enable debug output for relevant operations
/// </summary>
public bool Debug { get; private set; } = false;
/// <summary>
/// Set of input paths to use for operations
/// </summary>
public List<string> InputPaths { get; private set; } = [];
#if NETCOREAPP3_1_OR_GREATER
/// <summary>
/// Enable JSON output
/// </summary>
public bool Json { get; private set; } = false;
#endif
#endregion
/// <summary>
/// Parse commandline arguments into an Options object
/// </summary>
public static Options? ParseOptions(string[] args)
{
// If we have invalid arguments
if (args == null || args.Length == 0)
return null;
// Create an Options object
var options = new Options();
// Parse the features
int index = 0;
for (; index < args.Length; index++)
{
string arg = args[index];
bool featureFound = false;
switch (arg)
{
case "-?":
case "-h":
case "--help":
return null;
default:
break;
}
// If the flag wasn't a feature
if (!featureFound)
break;
}
// Parse the options and paths
for (; index < args.Length; index++)
{
string arg = args[index];
switch (arg)
{
case "-d":
case "--debug":
options.Debug = true;
break;
case "-j":
case "--json":
#if NET6_0_OR_GREATER
options.Json = true;
#else
Console.WriteLine("JSON output not available in .NET Framework");
#endif
break;
default:
options.InputPaths.Add(arg);
break;
}
}
// Validate we have any input paths to work on
if (options.InputPaths.Count == 0)
{
Console.WriteLine("At least one path is required!");
return null;
}
return options;
}
/// <summary>
/// Display help text
/// </summary>
public static void DisplayHelp()
{
Console.WriteLine("SabreTools.Serialization Test Program");
Console.WriteLine();
Console.WriteLine("test.exe <options> file|directory ...");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine("-?, -h, --help Display this help text and quit");
Console.WriteLine("-d, --debug Enable debug mode");
#if NET6_0_OR_GREATER
Console.WriteLine("-j, --json Print executable info as JSON");
#endif
}
}
}

View File

@@ -1,12 +1,142 @@
using SabreTools.Serialization;
using System;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Serialization;
using SabreTools.Serialization.Wrappers;
namespace Test
{
class Program
public static class Program
{
public static void Main(string[] args)
{
// DO NOTHING FOR NOW
// Get the options from the arguments
var options = Options.ParseOptions(args);
// If we have an invalid state
if (options == null)
{
Options.DisplayHelp();
Console.WriteLine("Press enter to close the program...");
Console.ReadLine();
return;
}
// Loop through the input paths
foreach (string inputPath in args)
{
#if NETFRAMEWORK
PrintPathInfo(inputPath, false, options.Debug);
#else
PrintPathInfo(inputPath, options.Json, options.Debug);
#endif
}
}
/// <summary>
/// Wrapper to print information for a single path
/// </summary>
/// <param name="path">File or directory path</param>
/// <param name="json">Enable JSON output, if supported</param>
/// <param name="debug">Enable debug output</param>
private static void PrintPathInfo(string path, bool json, bool debug)
{
Console.WriteLine($"Checking possible path: {path}");
// Check if the file or directory exists
if (File.Exists(path))
{
PrintFileInfo(path, json, debug);
}
else if (Directory.Exists(path))
{
#if NET20 || NET35
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
#else
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
#endif
{
PrintFileInfo(file, json, debug);
}
}
else
{
Console.WriteLine($"{path} does not exist, skipping...");
}
}
/// <summary>
/// Print information for a single file, if possible
/// </summary>
private static void PrintFileInfo(string file, bool json, bool debug)
{
Console.WriteLine($"Attempting to print info for {file}");
try
{
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// Read the first 8 bytes
byte[]? magic = stream.ReadBytes(8);
stream.Seek(0, SeekOrigin.Begin);
// Get the file type
WrapperType ft = WrapperFactory.GetFileType(magic ?? []);
if (ft == WrapperType.UNKNOWN)
{
string extension = Path.GetExtension(file).TrimStart('.');
ft = WrapperFactory.GetFileType(extension);
}
// Print out the file format
Console.WriteLine($"File format found: {ft}");
// Setup the wrapper to print
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
// If we don't have a wrapper
if (wrapper == null)
{
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
Console.WriteLine();
return;
}
// Get the base info output name
string filenameBase = $"info-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}";
#if NET6_0_OR_GREATER
// If we have the JSON flag
if (json)
{
// Create the output data
string serializedData = wrapper.ExportJSON();
Console.WriteLine(serializedData);
// Write the output data
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
jsw.WriteLine(serializedData);
}
#endif
// Create the output data
var builder = wrapper.ExportStringBuilder();
if (builder == null)
{
Console.WriteLine("No item information could be generated");
return;
}
// Write the output data
Console.WriteLine(builder);
using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt"));
sw.WriteLine(builder.ToString());
}
catch (Exception ex)
{
Console.WriteLine(debug ? ex : "[Exception opening file, please try again]");
Console.WriteLine();
}
}
}
}

View File

@@ -2,18 +2,28 @@
# This batch file assumes the following:
# - .NET 8.0 (or newer) SDK is installed and in PATH
# - zip is installed and in PATH
# - Git is installed and in PATH
#
# If any of these are not satisfied, the operation may fail
# in an unpredictable way and result in an incomplete output.
# Optional parameters
USE_ALL=false
NO_BUILD=false
NO_ARCHIVE=false
while getopts "uba" OPTION
do
case $OPTION in
u)
USE_ALL=true
;;
b)
NO_BUILD=true
;;
a)
NO_ARCHIVE=true
;;
*)
echo "Invalid option provided"
exit 1
@@ -24,6 +34,25 @@ done
# Set the current directory as a variable
BUILD_FOLDER=$PWD
# Set the current commit hash
COMMIT=`git log --pretty=%H -1`
# Create the build matrix arrays
FRAMEWORKS=("net8.0")
RUNTIMES=("win-x86" "win-x64" "linux-x64" "osx-x64")
# Use expanded lists, if requested
if [ $USE_ALL = true ]
then
FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64")
fi
# Create the filter arrays
SINGLE_FILE_CAPABLE=("net5.0" "net6.0" "net7.0" "net8.0")
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64")
# Only build if requested
if [ $NO_BUILD = false ]
then
@@ -33,4 +62,52 @@ then
# Create Nuget Package
dotnet pack SabreTools.Serialization/SabreTools.Serialization.csproj --output $BUILD_FOLDER
# Build Test
for FRAMEWORK in "${FRAMEWORKS[@]}"
do
for RUNTIME in "${RUNTIMES[@]}"
do
# If we have an invalid combination of framework and runtime
if [ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ] && [ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]
then
continue
fi
# Only .NET 5 and above can publish to a single file
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]
then
dotnet publish Test/Test.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish Test/Test.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
else
dotnet publish Test/Test.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish Test/Test.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
fi
done
done
fi
# Only create archives if requested
if [ $NO_ARCHIVE = false ]
then
# Create Test archives
for FRAMEWORK in "${FRAMEWORKS[@]}"
do
for RUNTIME in "${RUNTIMES[@]}"
do
# If we have an invalid combination of framework and runtime
if [ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ] && [ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]
then
continue
fi
cd $BUILD_FOLDER/Test/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/SabreTools.Serialization_${FRAMEWORK}_${RUNTIME}_debug.zip .
cd $BUILD_FOLDER/Test/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/SabreTools.Serialization_${FRAMEWORK}_${RUNTIME}_release.zip .
done
done
# Reset the directory
cd $BUILD_FOLDER
fi

View File

@@ -1,19 +1,48 @@
# This batch file assumes the following:
# - .NET 8.0 (or newer) SDK is installed and in PATH
# - 7-zip commandline (7z.exe) is installed and in PATH
# - Git for Windows is installed and in PATH
#
# If any of these are not satisfied, the operation may fail
# in an unpredictable way and result in an incomplete output.
# Optional parameters
param(
[Parameter(Mandatory = $false)]
[Alias("UseAll")]
[switch]$USE_ALL,
[Parameter(Mandatory = $false)]
[Alias("NoBuild")]
[switch]$NO_BUILD
[switch]$NO_BUILD,
[Parameter(Mandatory = $false)]
[Alias("NoArchive")]
[switch]$NO_ARCHIVE
)
# Set the current directory as a variable
$BUILD_FOLDER = $PSScriptRoot
# Set the current commit hash
$COMMIT = git log --pretty=format:"%H" -1
# Create the build matrix arrays
$FRAMEWORKS = @('net8.0')
$RUNTIMES = @('win-x86', 'win-x64', 'linux-x64', 'osx-x64')
# Use expanded lists, if requested
if ($USE_ALL.IsPresent)
{
$FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
}
# Create the filter arrays
$SINGLE_FILE_CAPABLE = @('net5.0', 'net6.0', 'net7.0', 'net8.0')
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
# Only build if requested
if (!$NO_BUILD.IsPresent)
{
@@ -23,4 +52,54 @@ if (!$NO_BUILD.IsPresent)
# Create Nuget Package
dotnet pack SabreTools.Serialization\SabreTools.Serialization.csproj --output $BUILD_FOLDER
# Build Test
foreach ($FRAMEWORK in $FRAMEWORKS)
{
foreach ($RUNTIME in $RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish Test\Test.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish Test\Test.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish Test\Test.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish Test\Test.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
}
# Only create archives if requested
if (!$NO_ARCHIVE.IsPresent)
{
# Create Test archives
foreach ($FRAMEWORK in $FRAMEWORKS)
{
foreach ($RUNTIME in $RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
Set-Location -Path $BUILD_FOLDER\Test\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\SabreTools.Serialization_${FRAMEWORK}_${RUNTIME}_debug.zip *
Set-Location -Path $BUILD_FOLDER\Test\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\SabreTools.Serialization_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
# Reset the directory
Set-Location -Path $PSScriptRoot
}