Last active
August 30, 2019 23:59
-
-
Save Radfordhound/ec23b24251083f76db442406dce7b9e2 to your computer and use it in GitHub Desktop.
Tokyo Olympics 2020 .pac format specification
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Tokyo Olympics 2020 pac format specification 2.0 | |
// By: Radfordhound | |
// Heavily based off of Skyth's Forces pac specification | |
HeaderV4 Header; | |
EmbeddedPAC EmbeddedPACs[]; // These are LZ4 compressed and need to be decompressed first | |
struct HeaderV4 | |
{ | |
char[8] Signature = "PACx402L"; | |
uint PacID; // Random number? Might actually be date created/modified. | |
uint FileSize; | |
uint RootOffset; | |
uint RootCompressedSize; | |
uint RootUncompressedSize; | |
PacType Type = 1; // Always 1? Oh gosh can these giant pacs have splits please no | |
ushort Constant = 0x208; // No idea what this is for | |
uint ChunkCount; | |
Chunk Chunks[ChunkCount]; | |
// Pad to a 0x16 offset | |
} | |
enum PacType : ushort | |
{ | |
// PAC has no splits | |
HasNoSplit = 1, | |
// PAC is a split | |
IsSplit = 2, | |
// PAC has splits | |
HasSplit = 5 | |
} | |
struct Chunk | |
{ | |
// When decompressing the root pac allocate a buffer of | |
// size Header.RootUncompressedSize, then loop through these | |
// chunks and decompress each one, one-by-one, into that buffer. | |
// If you try to decompress all at once instead the data can be corrupted. | |
uint CompressedSize; | |
uint UncompressedSize; | |
} | |
// Once you've decompressed the root PAC it'll look like this: | |
struct EmbeddedPAC | |
{ | |
HeaderV3 PACHeader; | |
NodeTree TypesTree; // Type nodes point to file node trees | |
SplitsInfo SplitInfo; // If there are no splits this isn't present | |
DataEntry DataEntries[]; // If there are no data entries this isn't present | |
} | |
struct HeaderV3 | |
{ | |
char[8] Signature = "PACx402L"; // Yes, it's the same as HeaderV4 even though it's a different format. | |
uint PacID = Header.PacID; // Always same as Header.PacID? | |
uint FileSize; // Size of the pac. | |
uint NodesSize; // Size of the section containing all of the NodeTrees and their Nodes. | |
uint SplitsInfoSize; // Size of the section containing all of the split information. | |
uint DataEntriesSize; // Size of the section containing all of the DataEntries. | |
uint StringTableSize; // Size of the string table. | |
uint DataSize; // Size of the section containing all of the file's data. | |
uint OffsetTableSize; // Size of the offset table. | |
PacType Type; | |
ushort Constant = 0x108; | |
uint SplitCount; | |
} | |
struct NodeTree | |
{ | |
uint NodeCount; | |
uint DataNodeCount; | |
ulong NodesOffset; | |
ulong DataNodeIndicesOffset; | |
Node Nodes[NodeCount]; | |
} | |
struct Node | |
{ | |
// Either the type name or the file name, depending on what type of node this is. See below. | |
ulong NameOffset; | |
// If this node is part of the first NodeTree in the pac, this points to | |
// another NodeTree which contains files of the type described by this node's name. | |
// Otherwise, this points to a DataEntry containing information on the file. | |
ulong DataOffset; | |
ulong ChildIndexTableOffset; | |
int ParentIndex; | |
int GlobalIndex; | |
int DataIndex; | |
ushort ChildCount; | |
bool HasData; | |
byte FullPathSize; // Not counting this node's name. | |
} | |
struct SplitsInfo | |
{ | |
ulong SplitCount; // Could also be a uint with padding but this makes more sense to me | |
ulong SplitEntriesOffset; // Yes, literally points to next 8 bytes. | |
SplitEntry SplitEntries[SplitCount]; | |
Chunk SplitChunks[]; | |
} | |
struct SplitEntry | |
{ | |
ulong SplitName; // E.G. ath10n_trr_cmn.pac.000 | |
uint SplitCompressedSize; | |
uint SplitUncompressedSize; | |
uint SplitOffset; // Where this split is within the big pac that contains this pac | |
uint SplitChunkCount; | |
ulong SplitChunksOffset; | |
} | |
struct DataEntry | |
{ | |
// Random number? Not the same as Header.PacID or PACHeader.PacID, like it was in Forces. | |
// Might actually be date created/modified. | |
uint PacID; | |
ulong DataSize; | |
char Padding1[4]; // Always 0? | |
ulong DataPosition; | |
char Padding2[8]; // Always 0? | |
ulong ExtensionPosition; | |
DataType DataType; | |
} | |
enum DataType : ulong | |
{ | |
RegularFile = 0, | |
NotHere = 1, | |
BINAFile = 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey, np! That's odd; I used lz4 as well actually in my tests and had no issues.
Make sure you're decompressing one "chunk" (the structures in this spec) of data at a time rather than trying to decompress the whole thing at once.