Formats/Common:LOC
LOC File (Localization/Language File)
Overview
An LOC file contains most of the text used in the games. They're consistent across games, other than the LOCT section and the footer at the end of the file.
An LOC file is divide into three sections, one of them only available in some games
LOCH (Unofficial name: LOC Header)
This section lives on top of the file. It doesn't do much other than hold the offset to the LOCL section.
LOCT (Unofficial name: LOC Table)
LOCT stores a hash table for UI elements.
This section appears in the games SSX 3, SSX OnTour, and SSX Blur. The games SSX 2000, and SSX Tricky ignore this section and proceed as if LOCL was right after LOCH.
LOCL (Unofficial name: LOC Language)
This section holds almost all text data. It keeps a count of how many UTF16 strings there are, and an array of offsets to them.
The Strings are UTF16, null terminated with 2 null characters. There are also empty strings, which are strings that only have 1 null character and no null termination.
Structure
#include <std/mem.pat>
struct LOCH {
char magic[4]; // "LOCH"
le u32 lochSize; // Always 20
u32 unk[2];
le u32 loclOffset;
};
struct HashData {
le u32 hash;
le u32 id; // The index of the text entry in LOCL
};
struct LOCT {
char magic[4]; // "LOCT"
le u32 headerSize; // Always 16
u32 unk[2];
// Keep reading hashes till the cursor hits the LOCL signature.
HashData hashTable[while(std::mem::read_string($, 4) != "LOCL")];
};
// Returns true if the next 2 UTF16 characters are null.
fn null_terminated() {
if (std::mem::read_unsigned($, 4) == 0) {
return true;
}
return false;
};
struct String16 {
// If the text is empty (1 null character),
// then just read one null character
// without reading null termination.
if (std::mem::read_unsigned($, 2) == 0) {
le char16 text;
} else {
le char16 text[while(!null_terminated())];
padding[4];
}
};
struct LOCL {
char magic[4]; // "LOCL"
le u32 size;
le u32 unk;
le u32 textEntryCount;
// Offsets are relative to the start of LOCL.
le u32 textOffsets[textEntryCount];
String16 text[textEntryCount];
};
struct LOC {
LOCH loch;
str loctSignature = std::mem::read_string($, 4);
if (loctSignature == "LOCT") {
LOCT loct;
}
LOCL locl;
if (!std::mem::eof()) {
u8 footer[2]; // Inconsistant
}
};
LOC loc @ 0x0;