Documentation
¶
Index ¶
- Constants
- func Checksum(data []byte) uint32
- func CompressLZ77(data []byte) []byte
- func DecodeChunk(buf []byte) ([]byte, error)
- func DecodeChunkBuffer(data []byte)
- func DecompressLZ77(compressed []byte, decompressedSize int) ([]byte, error)
- func DecryptBuffer(key uint8, seed uint8, data []byte)
- func EncodeChunkBuffer(data []byte)
- func EncryptInPlace(key uint8, startOffset int64, data []byte)
- func TransformHeaderKey(headerKey uint8) uint8
- type ChunkHeader
- type Entry
- type Header
Constants ¶
const ( // HeaderMarker is the magic number identifying HPI files ("HAPI"). HeaderMarker = 0x49504148 // ChunkMarker is the magic number for compressed chunks ("SQSH"). ChunkMarker = 0x48535153 // HeaderSize is the size of the HPI v1 header in bytes. HeaderSize = 20 // DirectoryEntrySize is the size of a v1 directory entry. DirectoryEntrySize = 9 // FileEntrySize is the size of a v1 file entry. FileEntrySize = 9 // ChunkHeaderSize is the size of a v1 chunk header. ChunkHeaderSize = 9 // SQSHHeaderSize is the on-disk size of a SQSH chunk header // (uint32 marker + 3 uint8s + 3 uint32s = 19 bytes, packed). SQSHHeaderSize = 19 )
const ( VersionV1 uint32 = 0x00010000 // Total Annihilation VersionV2 uint32 = 0x00020000 // TA: Kingdoms )
HPI archive versions.
const ( CompressionNone = 0 // No compression CompressionLZ77 = 1 // LZ77 compression CompressionZLib = 2 // ZLib compression )
Compression types stored in chunk headers and v1 file records.
const ( EntryTypeDirectory = 1 EntryTypeFile = 0 )
Entry types used by v1 directory records.
const DecryptKey = ^byte(0)
DecryptKey XORs every byte with 0xFF for encrypted HPIs.
const DefaultHeaderKey uint8 = 0xBF
DefaultHeaderKey is the HeaderKey value Total Annihilation writes into the HPI header for the main game archives shipped on disk (totala1/2/4.hpi). A value of 0 disables encryption entirely.
const DefaultTrailer = "Copyright 1997 Cavedog Entertainment"
DefaultTrailer is the 36-byte ASCII signature written at the end of every retail TA HPI archive. The game's loader appears to expect it, so the writer emits it by default to keep custom archives compatible.
Variables ¶
This section is empty.
Functions ¶
func CompressLZ77 ¶
CompressLZ77 compresses data using the HPI sliding-window LZ77 format.
Stream layout, per group of up to 8 items:
- 1 tag byte (8 flag bits, LSB first)
- For each bit (0 = literal, 1 = match): literal: 1 raw byte match: 2 bytes encoding (windowOffset << 4) | (matchLen - 2) giving 12-bit offset (1–4095) and 4-bit length (2–17)
A match with windowOffset == 0 terminates the stream.
Matching uses a 2-byte hash chain over the input data. The decompressor reconstructs bytes by reading from a 4096-byte sliding window, so the emitted window offset is computed against a tracked windowPos that mirrors the decompressor's state — the window content itself isn't needed for matching since it always equals the corresponding bytes of the input.
func DecodeChunk ¶
DecodeChunk parses a single SQSH chunk whose header starts at the front of buf and returns the decompressed payload. The bytes in buf are expected to be already decrypted (v2 stores chunks plaintext; v1 callers decrypt the region before invoking this). The verified checksum is computed on the stored payload before the optional add/XOR transform is reversed.
func DecodeChunkBuffer ¶
func DecodeChunkBuffer(data []byte)
DecodeChunkBuffer reverses the per-chunk add/XOR transform: for each byte at position i it computes (b - i) ^ i using uint8 arithmetic.
func DecompressLZ77 ¶
DecompressLZ77 decompresses HPI LZ77 data into a buffer of decompressedSize bytes using the 4096-byte sliding window scheme.
func DecryptBuffer ¶
DecryptBuffer decrypts data in place using HPI's position-dependent XOR. seed is the low byte of the file offset the buffer was read from. A key of 0 leaves the data untouched (unencrypted archive).
func EncodeChunkBuffer ¶
func EncodeChunkBuffer(data []byte)
EncodeChunkBuffer applies the per-chunk add/XOR transform: for each byte at position i it computes (b ^ i) + i. It is the inverse of DecodeChunkBuffer.
func EncryptInPlace ¶
EncryptInPlace XORs each byte of data with (uint8(startOffset+i) ^ key). This is the symmetric inverse of DecryptBuffer; applying it again with the same key and offset yields the original bytes.
func TransformHeaderKey ¶
TransformHeaderKey converts the raw HeaderKey value stored in the HPI header into the per-byte XOR key used to (de)scramble the directory and chunk regions. A HeaderKey of 0 disables encryption.
Types ¶
type ChunkHeader ¶
type ChunkHeader struct {
Magic uint32
Version uint8
CompressionType uint8
Encoded uint8
CompressedSize uint32
DecompressedSize uint32
Checksum uint32
}
ChunkHeader describes the 19-byte SQSH header that precedes each compressed chunk payload.
type Entry ¶
type Entry struct {
Name string
IsDir bool
Offset uint32
Size uint32
CompressedSize uint32
CompType uint8
Children []*Entry
Parent *Entry
}
Entry represents a file or directory in an HPI archive. The same node type is produced by both the v1 and v2 readers so callers can walk either tree without caring about the on-disk version.
For v1 archives, Size is the file's decompressed size and CompType selects the per-file compression scheme used by the multi-chunk file payload.
For v2 archives (TA: Kingdoms), Size is the decompressed size and CompressedSize is the on-disk SQSH chunk size; CompressedSize == 0 means the payload is stored uncompressed.
type Header ¶
type Header struct {
Marker uint32 // Should be 0x49504148 ("HAPI")
Version uint32 // HPI version (VersionV1 or VersionV2)
DirectorySize uint32 // Size of directory section in bytes
DecryptKey uint32 // Decryption key
Offset uint32 // Offset to directory start
}
Header represents the fixed 20-byte HPI v1 header. The v2 reader populates only Marker and Version (the supplemental v2 header is read separately), so the remaining fields are zero for TA: Kingdoms archives.
func ReadHeader ¶
ReadHeader reads and validates an HPI header from a reader.