22 Commits
1.1.2 ... 1.1.5

Author SHA1 Message Date
Matt Nadareski
21e22a1476 Bump version 2023-10-25 12:36:33 -04:00
Matt Nadareski
016057a837 Add mapping dictionaries for Xbox 2023-10-24 22:48:41 -04:00
Matt Nadareski
69ca889ac7 Add version guards around LZX.Chunk 2023-10-24 21:45:32 -04:00
Matt Nadareski
cd67a7282b Add version guards to IRD model 2023-10-23 11:33:50 -04:00
Matt Nadareski
948edbad58 Merge pull request #4 from Deterous/Deterous-patch-1
Improve IRD Model
2023-10-23 10:54:21 -04:00
Deterous
d445f02ba6 Specify the reserved attributes 2023-10-23 12:23:55 +13:00
Deterous
835fce7876 Comment on UID 2023-10-23 12:00:05 +13:00
Deterous
97513840e0 ID and UID are the same Property 2023-10-23 11:53:34 +13:00
Matt Nadareski
6112dcb391 Add IRD model 2023-10-22 01:03:34 -04:00
Matt Nadareski
c000e581c8 Bump version 2023-09-28 23:24:58 -04:00
Matt Nadareski
465cef4224 Add XGD4 identifier for PIC 2023-09-28 23:21:44 -04:00
Matt Nadareski
87cadbfd2b Add documentation around Quantum 2023-09-22 21:24:05 -04:00
Matt Nadareski
648ee2eaa5 Add back two properties 2023-09-22 21:15:52 -04:00
Matt Nadareski
daa814728d Simplify the Quantum models for now 2023-09-22 21:13:31 -04:00
Matt Nadareski
68aac36623 Fully create Chunk and ChunkHeader 2023-09-22 21:00:41 -04:00
Matt Nadareski
0c95cfcde4 More LZX cleanup 2023-09-22 20:47:29 -04:00
Matt Nadareski
6d6361c153 Start making LZX models better 2023-09-22 20:40:22 -04:00
Matt Nadareski
e4be402052 Bump version 2023-09-22 16:02:57 -04:00
Matt Nadareski
182c9bc756 Add remark on DeflateBlock 2023-09-22 15:37:49 -04:00
Matt Nadareski
cc62b3ffae This is an array 2023-09-22 15:34:55 -04:00
Matt Nadareski
7d34f486cd Make the MSZIP models better 2023-09-22 15:32:19 -04:00
Matt Nadareski
9c68cfc0c1 Fix issues found during MSZIP research 2023-09-22 11:54:48 -04:00
26 changed files with 762 additions and 261 deletions

View File

@@ -5,17 +5,8 @@ namespace SabreTools.Models.Compression.LZX
/// tree preceding the other trees.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
public class AlignedOffsetBlock
public class AlignedOffsetBlockData : BlockData
{
/// <summary>
/// Generic block header
/// </summary>
#if NET48
public BlockHeader Header { get; set; }
#else
public BlockHeader? Header { get; set; }
#endif
/// <summary>
/// Aligned offset tree
/// </summary>
@@ -86,8 +77,14 @@ namespace SabreTools.Models.Compression.LZX
public int[]? PathLengthsLengthTree { get; set; }
#endif
// Entry Comments Size
// ---------------------------------------------------------------------------------------
// Token sequence (matches and literals) Specified in section 2.6 Variable
/// <summary>
/// Token sequence (matches and literals)
/// </summary>
/// <remarks>Variable</remarks>
#if NET48
public byte[] TokenSequence { get; set; }
#else
public byte[]? TokenSequence { get; set; }
#endif
}
}

32
Compression/LZX/Block.cs Normal file
View File

@@ -0,0 +1,32 @@
namespace SabreTools.Models.Compression.LZX
{
/// <summary>
/// An LZXD block represents a sequence of compressed data that is encoded with the same set of
/// Huffman trees, or a sequence of uncompressed data. There can be one or more LZXD blocks in a
/// compressed stream, each with its own set of Huffman trees. Blocks do not have to start or end on a
/// chunk boundary; blocks can span multiple chunks, or a single chunk can contain multiple blocks. The
/// number of chunks is related to the size of the data being compressed, while the number of blocks is
/// related to how well the data is compressed.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
public class Block
{
/// <summary>
/// Block header
/// </summary>
#if NET48
public BlockHeader Header { get; set; }
#else
public BlockHeader? Header { get; set; }
#endif
/// <summary>
/// Block data
/// </summary>
#if NET48
public BlockData BlockData { get; set; }
#else
public BlockData? BlockData { get; set; }
#endif
}
}

View File

@@ -0,0 +1,8 @@
namespace SabreTools.Models.Compression.LZX
{
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
public abstract class BlockData
{
// No common fields between all block data
}
}

View File

@@ -1,14 +1,9 @@
namespace SabreTools.Models.Compression.LZX
{
/// <summary>
/// An LZXD block represents a sequence of compressed data that is encoded with the same set of
/// Huffman trees, or a sequence of uncompressed data. There can be one or more LZXD blocks in a
/// compressed stream, each with its own set of Huffman trees. Blocks do not have to start or end on a
/// chunk boundary; blocks can span multiple chunks, or a single chunk can contain multiple blocks. The
/// number of chunks is related to the size of the data being compressed, while the number of blocks is
/// related to how well the data is compressed. The Block Type field, as specified in section 2.3.1.1,
/// indicates which type of block follows, and the Block Size field, as specified in section 2.3.1.2,
/// indicates the number of uncompressed bytes represented by the block. Following the generic block
/// The Block Type field, as specified in section 2.3.1.1, indicates which type of block follows,
/// and the Block Size field, as specified in section 2.3.1.2, indicates the number of
/// uncompressed bytes represented by the block. Following the generic block
/// header is a type-specific header that describes the remainder of the block.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>

33
Compression/LZX/Chunk.cs Normal file
View File

@@ -0,0 +1,33 @@
namespace SabreTools.Models.Compression.LZX
{
/// <summary>
/// The LZXD compressor emits chunks of compressed data. A chunk represents exactly 32 KB of
/// uncompressed data until the last chunk in the stream, which can represent less than 32 KB. To
/// ensure that an exact number of input bytes represent an exact number of output bytes for each
/// chunk, after each 32 KB of uncompressed data is represented in the output compressed bitstream, the
/// output bitstream is padded with up to 15 bits of zeros to realign the bitstream on a 16-bit boundary
/// (even byte boundary) for the next 32 KB of data. This results in a compressed chunk of a byte-aligned
/// size. The compressed chunk could be smaller than 32 KB or larger than 32 KB if the data is
/// incompressible when the chunk is not the last one.
/// </summary>
public class Chunk
{
/// <summary>
/// Chunk header
/// </summary>
#if NET48
public ChunkHeader Header { get; set; }
#else
public ChunkHeader? Header { get; set; }
#endif
/// <summary>
/// Block headers and data
/// </summary>
#if NET48
public Block[] Blocks { get; set; }
#else
public Block[]? Blocks { get; set; }
#endif
}
}

View File

@@ -0,0 +1,46 @@
namespace SabreTools.Models.Compression.LZX
{
/// <summary>
/// The LZXD compressor emits chunks of compressed data. A chunk represents exactly 32 KB of
/// uncompressed data until the last chunk in the stream, which can represent less than 32 KB. To
/// ensure that an exact number of input bytes represent an exact number of output bytes for each
/// chunk, after each 32 KB of uncompressed data is represented in the output compressed bitstream, the
/// output bitstream is padded with up to 15 bits of zeros to realign the bitstream on a 16-bit boundary
/// (even byte boundary) for the next 32 KB of data. This results in a compressed chunk of a byte-aligned
/// size. The compressed chunk could be smaller than 32 KB or larger than 32 KB if the data is
/// incompressible when the chunk is not the last one.
/// </summary>
public class ChunkHeader
{
/// <summary>
/// The LZXD engine encodes a compressed, chunk-size prefix field preceding each compressed chunk in
/// the compressed byte stream. The compressed, chunk-size prefix field is a byte aligned, little-endian,
/// 16-bit field. The chunk prefix chain could be followed in the compressed stream without
/// decompressing any data. The next chunk prefix is at a location computed by the absolute byte offset
/// location of this chunk prefix plus 2 (for the size of the chunk-size prefix field) plus the current chunk
/// size.
/// </summary>
public ushort ChunkSize { get; set; }
/// <summary>
/// The first bit in the first chunk in the LZXD bitstream (following the 2-byte, chunk-size prefix described
/// in section 2.2.1) indicates the presence or absence of two 16-bit fields immediately following the
/// single bit. If the bit is set, E8 translation is enabled for all the following chunks in the stream using the
/// 32-bit value derived from the two 16-bit fields as the E8_file_size provided to the compressor when E8
/// translation was enabled. Note that E8_file_size is completely independent of the length of the
/// uncompressed data. E8 call translation is disabled after the 32,768th chunk (after 1 gigabyte (GB) of
/// uncompressed data).
/// </summary>
public byte E8Translation { get; set; }
/// <summary>
/// E8 translation size, high WORD
/// </summary>
public ushort? TranslationSizeHighWord { get; set; }
/// <summary>
/// E8 translation size, low WORD
/// </summary>
public ushort? TranslationSizeLowWord { get; set; }
}
}

View File

@@ -3,44 +3,36 @@ namespace SabreTools.Models.Compression.LZX
public static class Constants
{
/* some constants defined by the LZX specification */
public const int LZX_MIN_MATCH = (2);
public const int LZX_MAX_MATCH = (257);
public const int LZX_NUM_CHARS = (256);
/// <summary>
/// also blocktypes 4-7 invalid
/// </summary>
public const int LZX_BLOCKTYPE_INVALID = (0);
public const int LZX_BLOCKTYPE_VERBATIM = (1);
public const int LZX_BLOCKTYPE_ALIGNED = (2);
public const int LZX_BLOCKTYPE_UNCOMPRESSED = (3);
public const int LZX_PRETREE_NUM_ELEMENTS = (20);
public const int LZX_MIN_MATCH = 2;
public const int LZX_MAX_MATCH = 257;
public const int LZX_NUM_CHARS = 256;
public const int LZX_PRETREE_NUM_ELEMENTS = 20;
/// <summary>
/// aligned offset tree #elements
/// </summary>
public const int LZX_ALIGNED_NUM_ELEMENTS = (8);
public const int LZX_ALIGNED_NUM_ELEMENTS = 8;
/// <summary>
/// this one missing from spec!
/// </summary>
public const int LZX_NUM_PRIMARY_LENGTHS = (7);
public const int LZX_NUM_PRIMARY_LENGTHS = 7;
/// <summary>
/// length tree #elements
/// </summary>
public const int LZX_NUM_SECONDARY_LENGTHS = (249);
public const int LZX_NUM_SECONDARY_LENGTHS = 249;
/* LZX huffman defines: tweak tablebits as desired */
public const int LZX_PRETREE_MAXSYMBOLS = (LZX_PRETREE_NUM_ELEMENTS);
public const int LZX_PRETREE_TABLEBITS = (6);
public const int LZX_MAINTREE_MAXSYMBOLS = (LZX_NUM_CHARS + 50 * 8);
public const int LZX_MAINTREE_TABLEBITS = (12);
public const int LZX_LENGTH_MAXSYMBOLS = (LZX_NUM_SECONDARY_LENGTHS + 1);
public const int LZX_LENGTH_TABLEBITS = (12);
public const int LZX_ALIGNED_MAXSYMBOLS = (LZX_ALIGNED_NUM_ELEMENTS);
public const int LZX_ALIGNED_TABLEBITS = (7);
public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
public const int LZX_PRETREE_TABLEBITS = 6;
public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 50 * 8;
public const int LZX_MAINTREE_TABLEBITS = 12;
public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1;
public const int LZX_LENGTH_TABLEBITS = 12;
public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
public const int LZX_ALIGNED_TABLEBITS = 7;
public const int LZX_LENTABLE_SAFETY = (64); /* we allow length table decoding overruns */
public const int LZX_LENTABLE_SAFETY = 64; /* we allow length table decoding overruns */
}
}

View File

@@ -1,102 +0,0 @@
namespace SabreTools.Models.Compression.LZX
{
public class Header
{
/*
2.2 Header
2.2.1 Chunk Size
The LZXD compressor emits chunks of compressed data. A chunk represents exactly 32 KB of
uncompressed data until the last chunk in the stream, which can represent less than 32 KB. To
ensure that an exact number of input bytes represent an exact number of output bytes for each
chunk, after each 32 KB of uncompressed data is represented in the output compressed bitstream, the
output bitstream is padded with up to 15 bits of zeros to realign the bitstream on a 16-bit boundary
(even byte boundary) for the next 32 KB of data. This results in a compressed chunk of a byte-aligned
size. The compressed chunk could be smaller than 32 KB or larger than 32 KB if the data is
incompressible when the chunk is not the last one.
The LZXD engine encodes a compressed, chunk-size prefix field preceding each compressed chunk in
the compressed byte stream. The compressed, chunk-size prefix field is a byte aligned, little-endian,
16-bit field. The chunk prefix chain could be followed in the compressed stream without
decompressing any data. The next chunk prefix is at a location computed by the absolute byte offset
location of this chunk prefix plus 2 (for the size of the chunk-size prefix field) plus the current chunk
size.
2.2.2 E8 Call Translation
E8 call translation is an optional feature that can be used when the data to compress contains x86
instruction sequences. E8 translation operates as a preprocessing stage before compressing each
chunk, and the compressed stream header contains a bit that indicates whether the decoder shall
reverse the translation as a postprocessing step after decompressing each chunk.
The x86 instruction beginning with a byte value of 0xE8 is followed by a 32-bit, little-endian relative
displacement to the call target. When E8 call translation is enabled, the following preprocessing steps
are performed on the uncompressed input before compression (assuming little-endian byte ordering):
Let chunk_offset refer to the total number of uncompressed bytes preceding this chunk.
Let E8_file_size refer to the caller-specified value given to the compressor or decoded from the header
of the compressed stream during decompression.
The following example shows how E8 translation is performed for each 32-KB chunk of uncompressed
data (or less than 32 KB if last chunk to compress).
if (( chunk_offset < 0x40000000 ) && ( chunk_size > 10 ))
for ( i = 0; i < (chunk_size 10); i++ )
if ( chunk_byte[ i ] == 0xE8 )
long current_pointer = chunk_offset + i;
long displacement = chunk_byte[ i+1 ] |
chunk_byte[ i+2 ] << 8 |
chunk_byte[ i+3 ] << 16 |
chunk_byte[ i+4 ] << 24;
long target = current_pointer + displacement;
if (( target >= 0 ) && ( target < E8_file_size+current_pointer))
if ( target >= E8_file_size )
target = displacement E8_file_size;
endif
chunk_byte[ i+1 ] = (byte)( target );
chunk_byte[ i+2 ] = (byte)( target >> 8 );
chunk_byte[ i+3 ] = (byte)( target >> 16 );
chunk_byte[ i+4 ] = (byte)( target >> 24 );
endif
i += 4;
endif
endfor
endif
After decompression, the E8 scanning algorithm is the same. The following example shows how E8
translation reversal is performed.
long value = chunk_byte[ i+1 ] |
chunk_byte[ i+2 ] << 8 |
chunk_byte[ i+3 ] << 16 |
chunk_byte[ i+4 ] << 24;
if (( value >= -current_pointer ) && ( value < E8_file_size ))
if ( value >= 0 )
displacement = value current_pointer;
else
displacement = value + E8_file_size;
endif
chunk_byte[ i+1 ] = (byte)( displacement );
chunk_byte[ i+2 ] = (byte)( displacement >> 8 );
chunk_byte[ i+3 ] = (byte)( displacement >> 16 );
chunk_byte[ i+4 ] = (byte)( displacement >> 24 );
endif
The first bit in the first chunk in the LZXD bitstream (following the 2-byte, chunk-size prefix described
in section 2.2.1) indicates the presence or absence of two 16-bit fields immediately following the
single bit. If the bit is set, E8 translation is enabled for all the following chunks in the stream using the
32-bit value derived from the two 16-bit fields as the E8_file_size provided to the compressor when E8
translation was enabled. Note that E8_file_size is completely independent of the length of the
uncompressed data. E8 call translation is disabled after the 32,768th chunk (after 1 gigabyte (GB) of
uncompressed data).
Field Comments Size
----------------------------------------------------------------
E8 translation 0-disabled, 1-enabled 1 bit
Translation size high word Only present if enabled 0 or 16 bits
Translation size low word Only present if enabled 0 or 16 bits
*/
}
}

View File

@@ -14,17 +14,8 @@ namespace SabreTools.Models.Compression.LZX
/// subsequent compressed block if present.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
public class UncompressedBlock
public class UncompressedBlockData : BlockData
{
/// <summary>
/// Generic block header
/// </summary>
#if NET48
public BlockHeader Header { get; set; }
#else
public BlockHeader? Header { get; set; }
#endif
/// <summary>
/// Padding to align following field on 16-bit boundary
/// </summary>

View File

@@ -4,17 +4,8 @@ namespace SabreTools.Models.Compression.LZX
/// The fields of a verbatim block that follow the generic block header
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-PATCH/%5bMS-PATCH%5d.pdf"/>
public class VerbatimBlock
public class VerbatimBlockData : BlockData
{
/// <summary>
/// Generic block header
/// </summary>
#if NET48
public BlockHeader Header { get; set; }
#else
public BlockHeader? Header { get; set; }
#endif
/// <summary>
/// Pretree for first 256 elements of main tree
/// </summary>
@@ -75,8 +66,14 @@ namespace SabreTools.Models.Compression.LZX
public int[]? PathLengthsLengthTree { get; set; }
#endif
// Entry Comments Size
// ---------------------------------------------------------------------------------------
// Token sequence (matches and literals) Specified in section 2.6 Variable
/// <summary>
/// Token sequence (matches and literals)
/// </summary>
/// <remarks>Variable</remarks>
#if NET48
public byte[] TokenSequence { get; set; }
#else
public byte[]? TokenSequence { get; set; }
#endif
}
}

View File

@@ -0,0 +1,41 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Each MSZIP block MUST consist of a 2-byte MSZIP signature and one or more RFC 1951 blocks. The
/// 2-byte MSZIP signature MUST consist of the bytes 0x43 and 0x4B. The MSZIP signature MUST be
/// the first 2 bytes in the MSZIP block. The MSZIP signature is shown in the following packet diagram.
///
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
///
/// The maximum compressed size of each MSZIP block is 32 KB + 12 bytes. This enables the MSZIP
/// block to contain 32 KB of data split between two noncompressed RFC 1951 blocks, each of which
/// has a value of BTYPE = 00.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class Block
{
/// <summary>
/// Block header
/// </summary>
#if NET48
public BlockHeader BlockHeader { get; set; }
#else
public BlockHeader? BlockHeader { get; set; }
#endif
/// <summary>
/// Compressed blocks
/// </summary>
#if NET48
public DeflateBlock[] CompressedBlocks { get; set; }
#else
public DeflateBlock[]? CompressedBlocks { get; set; }
#endif
}
}

View File

@@ -4,18 +4,6 @@ namespace SabreTools.Models.Compression.MSZIP
/// Each MSZIP block MUST consist of a 2-byte MSZIP signature and one or more RFC 1951 blocks. The
/// 2-byte MSZIP signature MUST consist of the bytes 0x43 and 0x4B. The MSZIP signature MUST be
/// the first 2 bytes in the MSZIP block. The MSZIP signature is shown in the following packet diagram.
///
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
///
/// The maximum compressed size of each MSZIP block is 32 KB + 12 bytes. This enables the MSZIP
/// block to contain 32 KB of data split between two noncompressed RFC 1951 blocks, each of which
/// has a value of BTYPE = 00.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class BlockHeader

View File

@@ -0,0 +1,28 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Compression with Huffman codes (BTYPE=01 or BTYPE=02)
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public abstract class CompressedDataHeader : DataHeader
{
/// <summary>
/// Huffman code lengths for the literal / length alphabet
/// </summary>
#if NET48
public virtual uint[] LiteralLengths { get; set; }
#else
public virtual uint[]? LiteralLengths { get; set; }
#endif
/// <summary>
/// Huffman distance codes for the literal / length alphabet
/// </summary>
#if NET48
public virtual uint[] DistanceCodes { get; set; }
#else
public virtual uint[]? DistanceCodes { get; set; }
#endif
}
}

View File

@@ -0,0 +1,11 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Base class for all data headers (BTYPE=00, BTYPE=01, or BTYPE=02)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public abstract class DataHeader
{
// No common fields between all data headers
}
}

View File

@@ -0,0 +1,47 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class DeflateBlock
{
/// <summary>
/// Deflate block (RFC-1951) header
/// </summary>
#if NET48
public DeflateBlockHeader Header { get; set; }
#else
public DeflateBlockHeader? Header { get; set; }
#endif
/// <summary>
/// Compression-specific data header
/// </summary>
#if NET48
public DataHeader DataHeader { get; set; }
#else
public DataHeader? DataHeader { get; set; }
#endif
/// <summary>
/// MSZIP data
/// </summary>
/// <remarks>
/// Depending on the implementation of these models, this property could either be
/// compressed or uncompressed data. Keep this in mind when using the built
/// versions of this model.
/// </remarks>
#if NET48
public byte[] Data { get; set; }
#else
public byte[]? Data { get; set; }
#endif
}
}

View File

@@ -0,0 +1,11 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Compression with dynamic Huffman codes (BTYPE=10)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class DynamicCompressedDataHeader : CompressedDataHeader
{
// Codes are provided externally
}
}

View File

@@ -1,27 +0,0 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Compression with dynamic Huffman codes (BTYPE=10)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class DynamicHuffmanCompressedBlockHeader
{
/// <summary>
/// Huffman code lengths for the literal / length alphabet
/// </summary>
#if NET48
public int[] LiteralLengths { get; set; }
#else
public int[]? LiteralLengths { get; set; }
#endif
/// <summary>
/// Huffman distance codes for the literal / length alphabet
/// </summary>
#if NET48
public int[] DistanceCodes { get; set; }
#else
public int[]? DistanceCodes { get; set; }
#endif
}
}

View File

@@ -7,7 +7,7 @@ namespace SabreTools.Models.Compression.MSZIP
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class FixedHuffmanCompressedBlockHeader
public class FixedCompressedDataHeader : CompressedDataHeader
{
#region Properties
@@ -15,9 +15,9 @@ namespace SabreTools.Models.Compression.MSZIP
/// Huffman code lengths for the literal / length alphabet
/// </summary>
#if NET48
public uint[] LiteralLengths
public override uint[] LiteralLengths
#else
public uint[]? LiteralLengths
public override uint[]? LiteralLengths
#endif
{
get
@@ -57,9 +57,9 @@ namespace SabreTools.Models.Compression.MSZIP
/// Huffman distance codes for the literal / length alphabet
/// </summary>
#if NET48
public uint[] DistanceCodes
public override uint[] DistanceCodes
#else
public uint[]? DistanceCodes
public override uint[]? DistanceCodes
#endif
{
get

View File

@@ -4,7 +4,7 @@ namespace SabreTools.Models.Compression.MSZIP
/// Non-compressed blocks (BTYPE=00)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class NonCompressedBlockHeader
public class NonCompressedBlockHeader : DataHeader
{
/// <summary>
/// The number of data bytes in the block

View File

@@ -1,45 +1,50 @@
namespace SabreTools.Models.Compression.Quantum
{
/// <see href="www.russotto.net/quantumcomp.html"/>
public static class Constants
{
/// <summary>
/// Mask for Quantum Compression Level
/// </summary>
public const ushort MASK_QUANTUM_LEVEL = 0x00F0;
public static readonly int[] PositionSlot = new int[]
{
0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00006, 0x00008, 0x0000c,
0x00010, 0x00018, 0x00020, 0x00030, 0x00040, 0x00060, 0x00080, 0x000c0,
0x00100, 0x00180, 0x00200, 0x00300, 0x00400, 0x00600, 0x00800, 0x00c00,
0x01000, 0x01800, 0x02000, 0x03000, 0x04000, 0x06000, 0x08000, 0x0c000,
0x10000, 0x18000, 0x20000, 0x30000, 0x40000, 0x60000, 0x80000, 0xc0000,
0x100000, 0x180000
};
public static readonly int[] PositionExtraBits = new int[]
{
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14,
15, 15, 16, 16, 17, 17, 18, 18,
19, 19
};
public static readonly int[] LengthSlot = new int[]
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
0x0a, 0x0c, 0x0e, 0x12, 0x16, 0x1a, 0x1e, 0x26,
0x2e, 0x36, 0x3e, 0x4e, 0x5e, 0x6e, 0x7e, 0x9e,
0xbe, 0xde, 0xfe
};
public static readonly int[] LengthExtraBits = new int[]
{
0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 2, 2, 2, 2, 3, 3,
3, 3, 4, 4, 4, 4, 5, 5,
5, 5, 0
};
/// <summary>
/// Lowest Quantum Level (1)
/// Number of position slots for (tsize - 10)
/// </summary>
public const ushort QUANTUM_LEVEL_LO = 0x0010;
/// <summary>
/// Highest Quantum Level (7)
/// </summary>
public const ushort QUANTUM_LEVEL_HI = 0x0070;
/// <summary>
/// Amount to shift over to get int
/// </summary>
public const ushort SHIFT_QUANTUM_LEVEL = 4;
/// <summary>
/// Mask for Quantum Compression Memory
/// </summary>
public const ushort MASK_QUANTUM_MEM = 0x1F00;
/// <summary>
/// Lowest Quantum Memory (10)
/// </summary>
public const ushort QUANTUM_MEM_LO = 0x0A00;
/// <summary>
/// Highest Quantum Memory (21)
/// </summary>
public const ushort QUANTUM_MEM_HI = 0x1500;
/// <summary>
/// Amount to shift over to get int
/// </summary>
public const ushort SHIFT_QUANTUM_MEM = 8;
public static readonly int[] NumPositionSlots = new int[]
{
20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42
};
}
}

View File

@@ -1,23 +1,28 @@
namespace SabreTools.Models.Compression.Quantum
{
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
/// <see href="http://www.russotto.net/quantumcomp.html"/>
public sealed class Model
{
public int TimeToReorder { get; set; }
public int Entries { get; set; }
/// <remarks>
/// All the models are initialized with the symbols in symbol
/// order in the table, and with every symbol in the table
/// having a frequency of 1
/// </remarks>
#if NET48
public ModelSymbol[] Symbols { get; set; }
#else
public ModelSymbol?[]? Symbols { get; set; }
#endif
#if NET48
public ushort[] LookupTable { get; set; } = new ushort[256];
#else
public ushort[]? LookupTable { get; set; } = new ushort[256];
#endif
/// <remarks>
/// The initial total frequency is equal to the number of entries
/// in the table
/// </remarks>
public int TotalFrequency { get; set; }
/// <remarks>The initial time_to_reorder value is 4</remarks>
public int TimeToReorder { get; set; }
}
}

View File

@@ -1,11 +1,15 @@
namespace SabreTools.Models.Compression.Quantum
{
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
/// <see href="http://www.russotto.net/quantumcomp.html"/>
public sealed class ModelSymbol
{
public ushort Symbol { get; set; }
/// <summary>
/// The cumulative frequency is the frequency of all the symbols
/// which are at a higher index in the table than that symbol —
/// thus the last entry in the table has a cumulative frequency of 0.
/// </summary>
public ushort CumulativeFrequency { get; set; }
}
}

196
IRD/File.cs Normal file
View File

@@ -0,0 +1,196 @@
namespace SabreTools.Models.IRD
{
/// <see href="https://psdevwiki.com/ps3/Bluray_disc#IRD_file"/>
/// <see href="https://github.com/SabreTools/MPF/files/13062347/IRD.File.Format.pdf"/>
public class IRD
{
/// <summary>
/// "3IRD"
/// </summary>
#if NET48
public byte[] Magic { get; set; }
#else
public byte[]? Magic { get; set; }
#endif
/// <summary>
/// Version
/// </summary>
/// <remarks>Versions 6 - 9 are accepted</remarks>
public byte Version { get; set; }
/// <summary>
/// The same value stored in PARAM.SFO / TITLE_ID
/// </summary>
/// <remarks>9 bytes, ASCII, stored without dashes</remarks>
#if NET48
public string TitleID { get; set; }
#else
public string? TitleID { get; set; }
#endif
/// <summary>
/// Number of bytes that follow containing the title
/// </summary>
public byte TitleLength { get; set; }
/// <summary>
/// The same value stored in PARAM.SFO / TITLE
/// </summary>
/// <remarks><see cref="TitleLength"/> bytes, ASCII</remarks>
#if NET48
public string Title { get; set; }
#else
public string? Title { get; set; }
#endif
/// <summary>
/// The same value stored in PARAM.SFO / PS3_SYSTEM_VER
/// </summary>
/// <remarks>4 bytes, ASCII, missing uses "0000"</remarks>
#if NET48
public string SystemVersion { get; set; }
#else
public string? SystemVersion { get; set; }
#endif
/// <summary>
/// The same value stored in PARAM.SFO / VERSION
/// </summary>
/// <remarks>5 bytes, ASCII</remarks>
#if NET48
public string GameVersion { get; set; }
#else
public string? GameVersion { get; set; }
#endif
/// <summary>
/// The same value stored in PARAM.SFO / APP_VER
/// </summary>
/// <remarks>5 bytes, ASCII</remarks>
#if NET48
public string AppVersion { get; set; }
#else
public string? AppVersion { get; set; }
#endif
/// <summary>
/// Length of the gzip-compressed header data
/// </summary>
public uint HeaderLength { get; set; }
/// <summary>
/// Gzip-compressed header data
/// </summary>
#if NET48
public byte[] Header { get; set; }
#else
public byte[]? Header { get; set; }
#endif
/// <summary>
/// Length of the gzip-compressed footer data
/// </summary>
public uint FooterLength { get; set; }
/// <summary>
/// Gzip-compressed footer data
/// </summary>
#if NET48
public byte[] Footer { get; set; }
#else
public byte[]? Footer { get; set; }
#endif
/// <summary>
/// Number of complete regions in the image
/// </summary>
public byte RegionCount { get; set; }
/// <summary>
/// MD5 hashes for all complete regions in the image
/// </summary>
/// <remarks><see cref="RegionCount"/> regions, 16-bytes per hash</remarks>
#if NET48
public byte[][] RegionHashes { get; set; }
#else
public byte[][]? RegionHashes { get; set; }
#endif
/// <summary>
/// Number of decrypted files in the image
/// </summary>
public uint FileCount { get; set; }
/// <summary>
/// Starting sector for each decrypted file
/// </summary>
/// <remarks><see cref="FileCount"/> files, alternating with each <see cref="FileHashes"/> entry</remarks>
#if NET48
public ulong[] FileKeys { get; set; }
#else
public ulong[]? FileKeys { get; set; }
#endif
/// <summary>
/// MD5 hashes for all decrypted files in the image
/// </summary>
/// <remarks><see cref="FileCount"/> files, 16-bytes per hash, alternating with each <see cref="FileHashes"/> entry</remarks>
#if NET48
public byte[][] FileHashes { get; set; }
#else
public byte[][]? FileHashes { get; set; }
#endif
/// <summary>
/// Extra Config, usually 0x0000
/// </summary>
public ushort ExtraConfig { get; set; }
/// <summary>
/// Attachments, usually 0x0000
/// </summary>
public ushort Attachments { get; set; }
/// <summary>
/// D1 key
/// </summary>
/// <remarks>16 bytes</remarks>
#if NET48
public byte[] Data1Key { get; set; }
#else
public byte[]? Data1Key { get; set; }
#endif
/// <summary>
/// D2 key
/// </summary>
/// <remarks>16 bytes</remarks>
#if NET48
public byte[] Data2Key { get; set; }
#else
public byte[]? Data2Key { get; set; }
#endif
/// <summary>
/// Uncompressed PIC data
/// </summary>
/// <remarks>115 bytes, before D1/D2 keys on version 9</remarks>
#if NET48
public byte[] PIC { get; set; }
#else
public byte[]? PIC { get; set; }
#endif
/// <summary>
/// Unique Identifier
/// </summary>
/// <remarks>Not present on version 6 and prior, after AppVersion on version 7</remarks>
public uint UID { get; set; }
/// <summary>
/// IRD content CRC
/// </summary>
public uint CRC { get; set; }
}
}

View File

@@ -11,5 +11,7 @@ namespace SabreTools.Models.PIC
public const string DiscTypeIdentifierReWritable = "BDW";
public const string DiscTypeIdentifierRecordable = "BDR";
public const string DiscTypeIdentifierXGD4 = "XG4";
}
}

View File

@@ -4,7 +4,7 @@
<!-- Assembly Properties -->
<TargetFrameworks>net48;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Version>1.1.2</Version>
<Version>1.1.5</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>

201
Xbox/Constants.cs Normal file
View File

@@ -0,0 +1,201 @@
using System.Collections.Generic;
namespace SabreTools.Models.Xbox
{
/// <see href="https://xboxdevwiki.net/Xbe"/>
/// <see href="http://wiki.redump.org/index.php?title=Xbox_Title_IDs"/>
/// <see href="https://dbox.tools/publishers/"/>
public static class Constants
{
/// <summary>
/// Mapping of all Xbox 360 media subtypes to long names
/// </summary>
public static readonly Dictionary<char, string> MediaSubtypes = new Dictionary<char, string>
{
{ 'F', "XGD3" },
{ 'X', "XGD2" },
{ 'Z', "Games on Demand / Marketplace Demo" },
};
/// <summary>
/// Mapping of all publisher 2-letter codes to long names
/// </summary>
public static readonly Dictionary<string, string> Publishers = new Dictionary<string, string>
{
{ "AB", "Ambush Reality" },
{ "AC", "Acclaim Entertainment" },
{ "AD", "Andamiro USA Corp." },
{ "AH", "Arush Entertainment" },
{ "AK", "Artdink" },
{ "AP", "Aquaplus" },
{ "AQ", "Aqua System" },
{ "AS", "ASK" },
{ "AT", "Atlus" },
{ "AV", "Activision" },
{ "AW", "Arc System Works" },
{ "AX", "Aksys Games" },
{ "AY", "Aspyr Media" },
{ "BA", "Bandai" },
{ "BB", "BigBen" },
{ "BD", "Bravado" },
{ "BE", "Blueside Inc." },
{ "BF", "Blind Ferret Entertainment" },
{ "BG", "BradyGames" },
{ "BH", "Blackhole" },
{ "BL", "Black Box" },
{ "BM", "BAM! Entertainment" },
{ "BR", "Broccoli Co." },
{ "BS", "Bethesda Softworks" },
{ "BT", "Brash Entertainment" },
{ "BU", "Bunkasha Co." },
{ "BV", "Buena Vista Games" },
{ "BW", "BBC Multimedia" },
{ "BZ", "Blizzard" },
{ "CC", "Capcom" },
{ "CK", "Kemco Corporation" }, // TODO: Confirm
{ "CM", "Codemasters" },
{ "CT", "CTO" },
{ "CV", "Crave Entertainment" },
{ "DC", "DreamCatcher Interactive" },
{ "DE", "Destineer" },
{ "DX", "Davilex" },
{ "EA", "Electronic Arts" },
{ "EC", "Encore Software" },
{ "EF", "E-Frontier" },
{ "EL", "Enlight Software" },
{ "EM", "Empire Interactive" },
{ "ES", "Eidos Interactive" },
{ "EV", "Evolved Games" },
{ "FE", "Focus Entertainment (formerly Focus Home Interactive)" }, // TODO: Confirm
{ "FI", "Fox Interactive" },
{ "FL", "Fluent Entertainment" },
{ "FO", "505 Games" },
{ "FS", "From Software" },
{ "GE", "Genki Co." },
{ "GF", "Gameloft" },
{ "GV", "Groove Games" },
{ "HE", "Tru Blu (Entertainment division of Home Entertainment Suppliers)" },
{ "HP", "Hip Games" },
{ "HU", "Hudson Soft" },
{ "HW", "Highwaystar" },
{ "IA", "Mad Catz Interactive" }, // TODO: Confirm
{ "IF", "Idea Factory" },
{ "IG", "Infogrames" },
{ "IL", "Interlex Corporation" },
{ "IM", "Imagine Media" },
{ "IO", "Ignition Entertainment" },
{ "IP", "Interplay Entertainment" },
{ "IX", "InXile Entertainment" },
{ "JA", "Jaleco Entertainment" },
{ "JW", "JoWooD Entertainment" },
{ "KA", "Konami Osaka / Major A" },
{ "KB", "Kemco" }, // TODO: Confirm
{ "KI", "Kids Station Inc." }, // TODO: Confirm
{ "KN", "Konami" },
{ "KO", "Koei" },
{ "KT", "Konami Tokyo" },
{ "KU", "Kobi and/or GAE (formerly Global A Entertainment)" }, // TODO: Confirm
{ "KY", "Kalypso" },
{ "LA", "LucasArts" },
{ "LS", "Black Bean Games (publishing arm of Leader S.p.A.)" }, // TODO: Confirm
{ "MD", "Metro3D" },
{ "ME", "Medix" },
{ "MI", "Microïds" }, // TODO: Confirm
{ "MJ", "Majesco Entertainment" },
{ "MM", "Myelin Media" },
{ "MP", "MediaQuest" }, // TODO: Confirm
{ "MS", "Microsoft Game Studios" },
{ "MW", "Midway Games" },
{ "MX", "Empire Interactive" }, // TODO: Confirm
{ "NK", "NewKidCo" },
{ "NL", "NovaLogic" },
{ "NM", "Namco" },
{ "OG", "O-Games" },
{ "OX", "Oxygen Interactive" },
{ "PC", "Playlogic Entertainment" },
{ "PL", "Phantagram Co., Ltd. / Playlogic Entertainment" }, // TODO: Confirm
{ "RA", "Rage" },
{ "SA", "Sammy" },
{ "SC", "SCi Games" },
{ "SE", "Sega" },
{ "SN", "SNK" },
{ "SP", "Southpeak Games" },
{ "SQ", "Square Enix" },
{ "SS", "Simon & Schuster" },
{ "ST", "Studio Nine" },
{ "SU", "Success Corporation" },
{ "SW", "Swing! Deutschland" },
{ "TA", "Takara" },
{ "TC", "Tecmo" },
{ "TD", "The 3DO Company" },
{ "TK", "Takuyo" },
{ "TM", "TDK Mediactive" },
{ "TQ", "THQ" },
{ "TS", "Titus Interactive" },
{ "TT", "Take-Two Interactive Software" },
{ "US", "Ubisoft" },
{ "VC", "Victor Interactive Software" },
{ "VG", "Valcon Games" },
{ "VN", "Vivendi Universal Games (Former Interplay)" }, // TODO: Confirm
{ "VU", "Vivendi Universal Games" },
{ "VV", "Vicarious Visions" },
{ "WE", "Wanadoo Edition" },
{ "WR", "Warner Bros. Interactive Entertainment" },
{ "XA", "Xbox Live Arcade" },
{ "XI", "Xicat Interactive / XPEC Entertainment and Idea Factory" }, // TODO: Confirm
{ "XK", "Xbox Kiosk" },
{ "XL", "Xbox Live" },
{ "XM", "Evolved Games" }, // TODO: Confirm
{ "XP", "XPEC Entertainment" },
{ "XR", "Panorama" }, // TODO: Confirm
{ "YB", "YBM Sisa (South-Korea)" },
{ "ZD", "Zushi Games (formerly Zoo Digital Publishing)" },
{ "" + (char)0x00 + (char)0x01, "Microsoft Downloadable Content" },
{ "" + (char)0x05 + (char)0x04, "Zuma's Revenge" },
{ "" + (char)0xAA + (char)0xAA, "Arcania - Gothic 4" },
{ "" + (char)0xFF + (char)0xED, "Microsoft Downloadable Content" },
{ "" + (char)0xFF + (char)0xFD, "Microsoft Downloadable Content" },
{ "" + (char)0xFF + (char)0xFE, "Microsoft Downloadable Content" },
{ "" + (char)0xFF + (char)0xFF, "Microsoft Downloadable Content" },
};
/// <summary>
/// Mapping of all region 1-letter codes to long names
/// </summary>
public static readonly Dictionary<char, string> Regions = new Dictionary<char, string>
{
{ 'A', "USA" },
{ 'E', "Europe" },
{ 'H', "Japan / Europe" },
{ 'J', "Japan / Asia" },
{ 'K', "USA / Japan" },
{ 'L', "USA / Europe" },
{ 'W', "World" },
};
}
}