The trailer is located at the end of an APACK archive and provides archive summary information. APACK supports two trailer types depending on the archive mode.
| Mode | Trailer | Size | Purpose |
|---|---|---|---|
| Container | Trailer |
64 + N×40 bytes | Full TOC for random access |
| Stream | StreamTrailer |
32 bytes | Summary only, no TOC |
The container trailer contains the Table of Contents (TOC) enabling O(1) entry lookup by ID or name hash.
| Offset | Size | Field | Type | Description |
|---|---|---|---|---|
| 0x00 | 4 | magic | bytes | Magic number "ATRL" (ASCII) |
| 0x04 | 4 | trailerVersion | int32 | Trailer format version (current: 1) |
| 0x08 | 8 | tocOffset | int64 | Offset to TOC relative to trailer start |
| 0x10 | 8 | tocSize | int64 | Size of TOC in bytes |
| 0x18 | 8 | entryCount | int64 | Number of entries in TOC |
| 0x20 | 8 | totalOriginalSize | int64 | Sum of all entry original sizes |
| 0x28 | 8 | totalStoredSize | int64 | Sum of all entry stored sizes |
| 0x30 | 4 | tocChecksum | uint32 | CRC32 of entire TOC |
| 0x34 | 4 | trailerChecksum | uint32 | CRC32 of trailer header (bytes 0x00-0x33) |
| 0x38 | 8 | fileSize | int64 | Total file size for verification |
| 0x40 | N×40 | tocEntries | TocEntry[] | Table of Contents entries |
Header Size: 64 bytes (0x40) Total Size: 64 + (entryCount × 40) bytes
Offset 0x00 0x04 0x08 0x0C
┌─────────┬─────────┬─────────┬─────────┐
0x00 │ magic │ trailer │ tocOffset │
│ "ATRL" │ Version │ │
│ (4) │ (4) │ (8) │
├─────────┴─────────┼───────────────────┤
0x10 │ tocSize │ entryCount │
│ (8) │ (8) │
├───────────────────┼───────────────────┤
0x20 │ totalOriginalSize │ totalStoredSize │
│ (8) │ (8) │
├───────────────────┼───────────────────┤
0x30 │ toc │trailer│ fileSize │
│ csum│ csum │ (8) │
│ (4) │ (4) │ │
├─────┴───────┴─────────────────────────┤
0x40 │ TOC Entry 1 (40 bytes) │
├───────────────────────────────────────┤
0x68 │ TOC Entry 2 (40 bytes) │
├───────────────────────────────────────┤
│ ... │
└───────────────────────────────────────┘
Must be exactly "ATRL" (bytes: 0x41 0x54 0x52 0x4C).
Version of the trailer format. Current version is 1.
Byte offset from the start of the trailer to the first TOC entry. Typically equals the header size (64 bytes).
Total size of all TOC entries in bytes.
tocSize = entryCount * 40;Number of entries in the archive and TOC.
Sum of all entry original (uncompressed) sizes. Used for overall compression ratio:
double ratio = (double) totalStoredSize / totalOriginalSize * 100;Sum of all entry stored (compressed) sizes.
CRC32 checksum of the entire TOC (all TOC entries).
CRC32 checksum of the trailer header (bytes 0x00-0x33, excluding the checksum itself).
Total archive file size. Used to verify the archive is complete and not truncated.
Each TOC entry is a fixed 40-byte structure enabling random access to entries.
| Offset | Size | Field | Type | Description |
|---|---|---|---|---|
| 0x00 | 8 | entryId | int64 | Unique entry identifier |
| 0x08 | 8 | entryOffset | int64 | Absolute file offset to entry header |
| 0x10 | 8 | originalSize | int64 | Original (uncompressed) size |
| 0x18 | 8 | storedSize | int64 | Stored (compressed) size |
| 0x20 | 4 | nameHash | int32 | XXH3-32 hash of entry name |
| 0x24 | 4 | entryChecksum | int32 | CRC32 of entry header |
Total Size: 40 bytes (0x28)
Offset 0x00 0x04 0x08 0x0C
┌─────────┬─────────┬─────────┬─────────┐
0x00 │ entryId │ entryOffset │
│ (8) │ (8) │
├───────────────────┼───────────────────┤
0x10 │ originalSize │ storedSize │
│ (8) │ (8) │
├───────────────────┼───────────────────┤
0x20 │ nameHash │ entryChecksum │
│ (4) │ (4) │
└───────────────────┴───────────────────┘
0x28 End of TOC entry
Unique identifier for this entry. Matches EntryHeader.entryId.
Absolute file offset to the entry header. Seek to this position to read the entry.
Uncompressed size of entry data. Matches EntryHeader.originalSize.
Compressed/encrypted size of entry data. Matches EntryHeader.storedSize.
XXH3-32 hash of the entry name (UTF-8 bytes). Enables fast name-based lookup without reading entry headers.
Lookup Process:
- Compute XXH3-32 of desired entry name
- Scan TOC entries for matching
nameHash - For matches, read actual entry name from
entryOffset - Compare names to confirm (handles hash collisions)
CRC32 of the entry header. Used for early integrity validation.
The stream trailer is a simplified 32-byte structure for stream-mode archives (single entry, no random access).
| Offset | Size | Field | Type | Description |
|---|---|---|---|---|
| 0x00 | 4 | magic | bytes | Magic number "STRL" (ASCII) |
| 0x04 | 4 | reserved | int32 | Reserved for future use |
| 0x08 | 8 | originalSize | int64 | Original (uncompressed) size |
| 0x10 | 8 | storedSize | int64 | Stored (compressed) size |
| 0x18 | 4 | chunkCount | int32 | Total number of chunks |
| 0x1C | 4 | trailerChecksum | uint32 | CRC32 of trailer (bytes 0x00-0x1B) |
Total Size: 32 bytes (0x20)
Offset 0x00 0x04 0x08 0x0C
┌─────────┬─────────┬─────────┬─────────┐
0x00 │ magic │reserved │ originalSize │
│ "STRL" │ (4) │ (8) │
│ (4) │ │ │
├─────────┴─────────┼───────────────────┤
0x10 │ storedSize │ chunk │ trailer │
│ (8) │ Count │ Checksum │
│ │ (4) │ (4) │
└───────────────────┴───────┴───────────┘
0x20 End of stream trailer
Must be exactly "STRL" (bytes: 0x53 0x54 0x52 0x4C).
Reserved for future use. Must be zero.
Total uncompressed size of the stream data.
Total compressed/encrypted size of the stream data.
Total number of chunks written to the stream.
CRC32 of the trailer (bytes 0x00-0x1B, excluding the checksum itself).
public Trailer readTrailer(SeekableByteChannel channel) throws ApackException {
// Seek to trailer (offset from file header)
channel.position(fileHeader.trailerOffset());
BinaryReader reader = new BinaryReader(channel);
// Validate magic
byte[] magic = reader.readBytes(4);
if (!Arrays.equals(magic, "ATRL".getBytes())) {
throw new FormatException("Invalid trailer magic");
}
int trailerVersion = reader.readInt32();
long tocOffset = reader.readInt64();
long tocSize = reader.readInt64();
long entryCount = reader.readInt64();
long totalOriginalSize = reader.readInt64();
long totalStoredSize = reader.readInt64();
int tocChecksum = reader.readInt32();
int trailerChecksum = reader.readInt32();
long fileSize = reader.readInt64();
// Read TOC entries
List<TocEntry> tocEntries = new ArrayList<>();
for (int i = 0; i < entryCount; i++) {
tocEntries.add(readTocEntry(reader));
}
// Verify TOC checksum
int computedTocChecksum = computeTocChecksum(tocEntries);
if (computedTocChecksum != tocChecksum) {
throw new ChecksumException("TOC checksum mismatch");
}
return new Trailer(
trailerVersion, tocOffset, tocSize, entryCount,
totalOriginalSize, totalStoredSize,
tocChecksum, trailerChecksum, fileSize, tocEntries
);
}
private TocEntry readTocEntry(BinaryReader reader) throws IOException {
return TocEntry.builder()
.entryId(reader.readInt64())
.entryOffset(reader.readInt64())
.originalSize(reader.readInt64())
.storedSize(reader.readInt64())
.nameHash(reader.readInt32())
.entryChecksum(reader.readInt32())
.build();
}public Optional<PackEntry> findEntry(String name) {
// Compute name hash
int targetHash = computeXxh3Hash(name);
// Search TOC
for (TocEntry tocEntry : trailer.tocEntries()) {
if (tocEntry.nameHash() == targetHash) {
// Read actual entry header to confirm name
EntryHeader header = readEntryHeaderAt(tocEntry.entryOffset());
if (header.name().equals(name)) {
return Optional.of(new PackEntry(header, tocEntry));
}
}
}
return Optional.empty();
}- Magic must be exactly "ATRL"
- Trailer version must be supported (≤ current version)
- TOC size must equal
entryCount × 40 - TOC checksum must match computed CRC32
- Trailer checksum must match computed CRC32
- File size must match actual file size
- Entry offsets must be within file bounds
- Magic must be exactly "STRL"
- Reserved field must be zero
- Chunk count must be positive for non-empty streams
- Trailer checksum must match computed CRC32
- Original size must be non-negative
- Stored size must be non-negative
Next: Encryption Block | Previous: Chunk Format