archive

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

README

internal/archive

Mid-level MPQ archive engine. This package owns the open archive object, the read/write paths, file creation, table mutation, in-place compaction, and patch-archive support. It is the layer that composes internal/mpq (table parsing) with the codec implementations in internal/compress.

Files

File Responsibility
archive.go Archive type, Open, locale/platform plumbing, listfile + table caches.
read.go Decompression pipeline: sector splitting, key decryption, codec dispatch.
write.go CreateFile / WriteFile / FinishFile: compressed and uncompressed sector emission.
create.go New empty archive layout (V1/V2 header, hash table sizing, signing slots).
compact.go In-place compaction of live blocks (re-emits hash/block/HET/BET if present).
patch.go Patch archive open + bsdiff40/COPY/BSD0 application semantics.
bsdiff40.go BSDIFF40 patch decoding (Blizzard variant of bsdiff).
bzip2_encode.go bzip2 sector encode (uses dsnet/bzip2).
lzma_encode.go LZMA1 sector encode (uses ulikunitz/xz/lzma).

*_test.go files cover round-trip read/write, compact, patch, codec combinations, and several malformed-archive corner cases.

Codec coverage (read path)

The dispatch in read.go currently handles:

Unsupported codec combinations return a typed error rather than panicking; parity tests under tools/parity gate the strict-no-skips bar.

What this package does not do

  • It does not expose the user-facing API surface; that lives in pkg/storm. Imports outside the workspace should target pkg/storm.
  • It does not manage table cryptography directly; key derivation and table decode/encode live in internal/mpq.
  • Sign/verify functionality is currently a stub in pkg/storm and returns ErrUnsupportedFeature.

Documentation

Overview

Package archive implements the mid-level MPQ archive engine: open, read, write, create, compact, and patch operations.

It composes github.com/ldmonster/go-stormlib/internal/mpq (table parsing and cryptography) with the codec packages under github.com/ldmonster/go-stormlib/internal/compress to produce a complete read/write pipeline for MPQ v1 and v2 archives.

This package is internal. External callers should use github.com/ldmonster/go-stormlib/pkg/storm instead.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrIndexOutOfRange  = errors.New("file index out of range")
	ErrFileHashNotFound = errors.New("file hash entry not found")
	ErrDecodeFailed     = errors.New("decode failed")
	// ErrUnsupportedCodec is returned when the sector compression mask uses a codec not implemented yet (pkware, huffman, ...).
	ErrUnsupportedCodec = errors.New("unsupported mpq sector compression codec")
	// ErrEncryptionNeedsPath is returned for MPQ_FILE_ENCRYPTED entries when no internal path was supplied for DecryptFileKey (open-by-hash/index cannot derive the key).
	ErrEncryptionNeedsPath = errors.New(
		"encrypted mpq entry requires opening by internal file path for decryption key",
	)
)
View Source
var (
	ErrWriteInProgress         = errors.New("mpq file write already in progress")
	ErrNoWriteInProgress       = errors.New("mpq file write not started")
	ErrWriteSizeExceeded       = errors.New("mpq file write exceeds declared size")
	ErrWriteSizeIncomplete     = errors.New("mpq file write incomplete")
	ErrWriteFlagsUnsupported   = errors.New("unsupported mpq file write flags")
	ErrArchiveWriteUnsupported = errors.New("archive write shape unsupported")
	ErrRenameCollision         = errors.New("rename target already exists")
	ErrInvalidFileName         = errors.New("invalid mpq file name")
	ErrInternalFileName        = errors.New("internal mpq file name")
	ErrUnsupportedCodecWrite   = errors.New("unsupported mpq codec for write")
)
View Source
var ErrPatchDeltaUnsupported = errors.New("patch-chain delta unsupported")

ErrPatchDeltaUnsupported is returned when a patch-chain entry carries an unrecognised XFRM type that we do not know how to apply.

View Source
var ErrSectorChecksum = errors.New("sector checksum mismatch")

ErrSectorChecksum reports an adler32 mismatch on a stored sector.

Functions

func CreateEmpty

func CreateEmpty(path string, opts CreateOptions) error

CreateEmpty writes a new minimal MPQ without user data preamble, matching StormLib empty-archive layout (0x200 padding, header, encrypted empty hash table, block table size 0).

Types

type Archive

type Archive struct {
	Path       string
	Header     mpq.Header
	HashTable  []mpq.HashEntry
	BlockTable []mpq.BlockEntry
	FileIndex  []mpq.IndexedFileEntry
	// contains filtered or unexported fields
}

func Open

func Open(path string) (*Archive, error)

func OpenWithOptions

func OpenWithOptions(path string, opts OpenOptions) (*Archive, error)

func (*Archive) Compact

func (a *Archive) Compact() error

Compact rewrites the archive in place so that only live block entries remain, reclaiming gaps left behind by removed/replaced files. The on-disk archive layout after compaction is:

[lead padding] [header] [live block payloads concatenated] [hash table] [block table]

HET/BET/Hi-block extension tables are stripped by Compact — StormLib's BuildFileTable transparently falls back to the classic hash/block tables when the HET pointer is zero. v4 archives have their MD5HashTable and MD5BlockTable digests recomputed so VerifyHashTableMD5/VerifyBlockTableMD5 succeed after compaction.

func (*Archive) CreateFile

func (a *Archive) CreateFile(name string, fileSize, flags uint32) error

CreateFile begins a minimal write lifecycle similar to SFileCreateFile. Current parity scope is intentionally narrow: uncompressed single-unit writes.

func (*Archive) CreateFileEx

func (a *Archive) CreateFileEx(name string, fileSize, flags uint32, compression byte) error

CreateFileEx is the explicit-codec variant of CreateFile. The compression argument is the MPQ codec mask byte (e.g. 0x02 zlib, 0x10 bzip2). Zero selects the implementation default when MPQ_FILE_COMPRESS is set.

func (*Archive) FinishFile

func (a *Archive) FinishFile() error

FinishFile persists the pending file and updates MPQ tables/header, similar to SFileFinishFile.

func (*Archive) Flush

func (a *Archive) Flush() error

Flush serialises the (listfile) for the current archive state. It is safe to call multiple times; existing internal entries are first marked deleted so the new payload occupies a fresh slot/block.

func (*Archive) GetAttributesFlags

func (a *Archive) GetAttributesFlags() uint32

GetAttributesFlags returns the configured (attributes) flags, defaulting to CRC32|FILETIME|MD5 when SetAttributesFlags has not been called.

func (*Archive) IsPatchedArchive

func (a *Archive) IsPatchedArchive() bool

func (*Archive) OpenIndexedFile

func (a *Archive) OpenIndexedFile(index int) (FileHandle, error)

func (*Archive) OpenIndexedFileByHash

func (a *Archive) OpenIndexedFileByHash(
	hashA, hashB uint32,
	locale uint16,
	platform uint8,
) (FileHandle, error)

func (*Archive) OpenIndexedFileForDecrypt

func (a *Archive) OpenIndexedFileForDecrypt(
	hashA, hashB uint32,
	locale uint16,
	platform uint8,
	pathForDecryptKey string,
) (FileHandle, error)

OpenIndexedFileForDecrypt opens by hash table entry but supplies the MPQ-internal path string required to derive Storm DecryptFileKey when MPQ_FILE_ENCRYPTED is set.

func (*Archive) OpenPatchArchive

func (a *Archive) OpenPatchArchive(path, prefix string, flags uint32) error

func (*Archive) PersistHeaderAndTablesForTest

func (a *Archive) PersistHeaderAndTablesForTest(
	h mpq.Header,
	hashes []mpq.HashEntry,
	blocks []mpq.BlockEntry,
) error

PersistHeaderAndTablesForTest is an exported wrapper around persistHeaderAndTables for use by storm-package tests that need to inject/mutate tables.

func (*Archive) ReadFile

func (a *Archive) ReadFile(h FileHandle) ([]byte, error)

func (*Archive) ReadPatchedByName

func (a *Archive) ReadPatchedByName(name string, locale uint16, platform uint8) ([]byte, error)

ReadPatchedByName walks the patch chain newest-first to find the newest version, applying any BSDIFF40/COPY patch deltas over the most recent non-delta base. MPQ_FILE_DELETE_MARKER terminates lookup and returns ErrFileHashNotFound.

func (*Archive) RemoveFile

func (a *Archive) RemoveFile(name string, locale uint16, platform uint8) error

RemoveFile marks a hash entry as deleted and clears its block table row.

func (*Archive) RenameFile

func (a *Archive) RenameFile(oldName, newName string, locale uint16, platform uint8) error

RenameFile updates hash A/B for the selected locale/platform variant.

func (*Archive) SetAddFileCallback

func (a *Archive) SetAddFileCallback(cb func(written, total uint32, done bool))

func (*Archive) SetAttributesFlags

func (a *Archive) SetAttributesFlags(flags uint32) uint32

SetAttributesFlags overrides the bitmask used by Flush when emitting an (attributes) file. Returns the previous value. Pass 0 to suppress (attributes) emission entirely. Bits: 0x01=CRC32, 0x02=FILETIME, 0x04=MD5.

func (*Archive) WriteFile

func (a *Archive) WriteFile(data []byte) error

WriteFile appends bytes for the pending write lifecycle, similar to SFileWriteFile.

type CreateOptions

type CreateOptions struct {
	// ArchiveFormat is MPQ_FORMAT_VERSION_*: 0 = classic v1 header, 1 = Burning Crusade v2 header (44-byte).
	ArchiveFormat uint16
	// MaxFileCount is dwMaxFileCount; 0 uses StormLib default hash table sizing.
	MaxFileCount uint32
	// ReservedSlots counts extra hash slots for internal files when MaxFileCount > 0 (listfile/attributes/signature).
	ReservedSlots uint32
	// ReserveListfile mirrors StormLib create reserved-file accounting for (listfile).
	ReserveListfile bool
	// ReserveAttributes mirrors StormLib create reserved-file accounting for (attributes).
	ReserveAttributes bool
	// ReserveSignature mirrors StormLib create reserved-file accounting for (signature).
	ReserveSignature bool
}

CreateOptions controls layout for a new empty archive (read StormLib SFileCreateArchive / SFileCreateArchive2).

type FileHandle

type FileHandle struct {
	Entry            mpqIndexedEntry
	DecryptKeySource string // Storm DecryptFileKey path string; empty when opened by hash/index only.
}

type OpenOptions

type OpenOptions struct {
	ForceMPQV1      bool
	MarkerSignature uint32
}

Jump to

Keyboard shortcuts

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