[StructLayout(LayoutKind.Explicit, Size =5)]structSqpkChunk{ [FieldOffset(0x0)] Uint32BE Size; [FieldOffset(0x4)] SqpkOperation Opcode; [FieldOffset(0x5)] SqpkPayload Payload;}enumSqpkOperation:byte{ A,// Add data. Used to overwrite blocks in dat files. Can also delete old data D,// Delete data. Used to delete blocks (overwrite with 0x00) in dat files E,// Expand data. Used to insert empty blocks into dat files F,// File operations. Adding files, deleting files, etc. H,// Header Update. Updates headers of dat or index files I,// Index Add/Delete. Present in patch files but unused X,// Patch Info. Present in patch files but unused T,// Target Info. Present in patch files but only one field is used}
SqpkChunk is a 'container' chunk which executes one of several operations in SqpkOperation. These will be detailed in the following subsections.
Important to note that any fields prepended with "Block" will mean that the number is in blocks, or units of 128-bytes. e.g., BlockOffset means that the offset in bytes would be calculated by BlockOffset << 7 or BlockOffset * 128.
These operations often contain fields indicating which file the operation will apply to. These adhere to the following structure:
This operation writes BlockCount empty (0x00) 128-byte blocks at BlockOffset, with the first of those blocks containing a BlockHeader struct with:Size = 128, Type = FileSize = UsedBlocks = 0, TotalBlocks = BlockCount
If Delete Data encounters EOF while writing or seeking to BlockOffset, it will fail.
Type E - Expand Data
Expand Data follows the same structure as Delete Data, but the behaviour differs. When Expand Data seeks past the end of file or writes past the end of file, it does not fail, since this is its intended use.
If blkHeader.Compressed is true then the payload is a deflated stream.
If blkHeader.Compressed is false then the payload is a raw bytes stream.
Yes, AlignedBlockSize is very dodgy. Apparently they are rounded up to next multiple of 128 except they're not.
There are no good ways to determine how many blocks are actually encoded in SqpkFileBlocks.
Usually the last block won't be compressed but keeping track of payload.remaining() and stop if it's all read will be more robust against a forged file.