common

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
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
)
View Source
const (
	VersionV1 uint32 = 0x00010000 // Total Annihilation
	VersionV2 uint32 = 0x00020000 // TA: Kingdoms
)

HPI archive versions.

View Source
const (
	CompressionNone = 0 // No compression
	CompressionLZ77 = 1 // LZ77 compression
	CompressionZLib = 2 // ZLib compression
)

Compression types stored in chunk headers and v1 file records.

View Source
const (
	EntryTypeDirectory = 1
	EntryTypeFile      = 0
)

Entry types used by v1 directory records.

View Source
const DecryptKey = ^byte(0)

DecryptKey XORs every byte with 0xFF for encrypted HPIs.

View Source
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.

View Source
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 Checksum

func Checksum(data []byte) uint32

Checksum computes the HPI chunk checksum (the 32-bit sum of all bytes).

func CompressLZ77

func CompressLZ77(data []byte) []byte

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

func DecodeChunk(buf []byte) ([]byte, error)

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

func DecompressLZ77(compressed []byte, decompressedSize int) ([]byte, error)

DecompressLZ77 decompresses HPI LZ77 data into a buffer of decompressedSize bytes using the 4096-byte sliding window scheme.

func DecryptBuffer

func DecryptBuffer(key uint8, seed uint8, data []byte)

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

func EncryptInPlace(key uint8, startOffset int64, data []byte)

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

func TransformHeaderKey(headerKey uint8) uint8

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.

func (*Entry) Find

func (e *Entry) Find(path string) *Entry

Find locates an entry by path, matching component names case-insensitively.

func (*Entry) FullPath

func (e *Entry) FullPath() string

FullPath returns the full path of this entry.

func (*Entry) Walk

func (e *Entry) Walk(fn func(*Entry) error) error

Walk traverses the entry tree depth-first, calling fn for each node.

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

func ReadHeader(r io.Reader) (*Header, error)

ReadHeader reads and validates an HPI header from a reader.

func (*Header) WriteHeader

func (h *Header) WriteHeader(w io.Writer) error

WriteHeader writes an HPI header to a writer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL