109
edits
Changes
From Final Fantasy Inside
Rewrote the whole article with up to date information
=== LGP Archive format for PC by [[User:Ficedula|Ficedula]] =Introduction ==
==== Section 1: File Header ==Structure Overview ==
{| class="wikitable"! Section !! Size !! Description|-| Header || 16 bytes || Archive metadata and file count|-| Table of Contents || 27 bytes × file count || File entries with names and offsets|-| Hash Table || 3600 bytes (fixed) || Lookup table for fast file access|-| Path Table || Variable || Optional directory paths for files|-| File Data || Variable || Actual file contents with individual headers|-| Footer || 14 bytes || "FINAL FANTASY7" terminator string|} == Header == The first item header is 12 always 16 bytes containing and contains the archive's magic identifier and file creatorcount. This is a standard {| class="wikitable"! Offset !! Size !! Type !! Description|-| 0x00 || 2 bytes || uint16 || Reserved (always 0)|-| 0x02 || 10 bytes || char[10] || Magic string, except it is : "rightalignedSQUARESOFT". In other words the blank space comes before the actual text(ASCII, not after. In FF7 itno null terminator)|-| 0x0C || 2 bytes || uint16 || Number of files in archive|-| 0x0E || 2 bytes || uint16 || Reserved (always 0)|} '''Validation:'''s always The magic string at offset 0x02 must exactly match "SQUARESOFT" preceded by two nulls to make it 12 (ASCII, no null terminator within the 10 bytes). The only other thing you might see is == Table of Contents (TOC) == Immediately follows the header and contains one 27-byte entry for each file in the archive. {| class="wikitable"FICEDULA! Offset !! Size !! Type !! Description|-| 0x00 || 20 bytes || char[20] || Filename (null-LGP"padded, which I use to indicate a no path)|-| 0x14 || 4 bytes || uint32 || Absolute file is an LGP *patch* one of my programs has constructedoffset in archive|-| 0x18 || 1 byte || uint8 || File type (always 0x0E / 14)|-| 0x19 || 2 bytes || uint16 || Path index (0 = no path, not a complete archive.1+ = path table index)|}
The hash table immediately follows the TOC and is always exactly 3600 bytes (900 entries × 4 bytes). It provides fast O(1) lookup of files by filename. === Hash Table Entry === Each entry in the TOC has the following structureis 4 bytes:
{| class="wikitable"
! Offset! Length! Size !! Type !! Description
|-
| 20 0x00 || 2 bytes| Null terminated string| uint16 || Index into TOC (1-indexed, giving filename0 = empty bucket)
|-
| 4 byte integer0x02 || 2 bytes || uint16 || Position Count of consecutive entries in this bucket|} === Hash Function === The hash is computed from the filename (without path or extension) using only the first two characters of the file where data starts for stem: <pre>hash_value = hash(first_char) × 30 + hash(second_char) + 1</pre> For filenames with only one character in the filestem: <pre>hash_value = hash(first_char) × 30</pre> === Character Hash Values === The hash function maps characters to numeric values as follows: {| class="wikitable"! Character !! Hash Value
|-
| 1 byte| style="background: rgba-z (255,255,204case insensitive)" | Some sort of check code. File attributes? Normally seems to be<br />14 but it does vary.| 0-25
|-
| 2 byte short0-9 || 0-9|-| _ (underscore) || 10 (same as 'k')| style="background: rgb-| - (255,255,204hyphen)" | Something to do with duplicate file names. If a name is unique it is 0, otherwise it is assigned a value based on existing duplicates. | 11 (Hard to explainsame as 'l')
|}
'''Note:''' The hash function is case-insensitive, treating 'A' and 'a' identically. ==== Section 2: Section formerly designated as "CRC Code" =Lookup Algorithm ===
The second letter, minus path table immediately follows the value hash table and stores directory paths for ascii ' ' ' (0x60) is the index of the entry within the setfiles. Since the second letter in all of the file names is ascii 'It uses a' or greater, it means variable-length structure organized into "path groups" for files that share the lowest entry index is 1, so the first entry (at index 0) same filename but exist in every group is always zero (0x0000)different directories.
==== Section 3: Actual Data =Path Entry ===
{| class="wikitable"
! style="background: rgb(204,204,204); width: 80px" align="center" | Offset !! Size! style="background: rgb(204,204,204); width: 200px" | ! Type !! Description
|-
| 0x00 || 128 bytes || char[128] || Directory path (null-padded, no trailing slash)|-| 0x80 || 2 bytes || uint16 || TOC index this path belongs to|} '''Notes:'''* Path index in TOC entries is 1-indexed into path groups* Multiple files with the same name but different paths share a path group* Empty path string means root directory* Maximum path length is 127 characters plus null terminator == File Data == File data blocks follow the path table. Each file consists of a 24-byte header followed by the raw file content. === File Header === {| class="wikitable"! Offset !! Size !! Type !! Description|-| 0x00 || 20 bytes|| char[20] || Filename (same as in TOC)| Null terminated string, giving filename-| 0x14 || 4 bytes || uint32 || File size in bytes|} === File Content === Immediately follows the file header. The size is specified in the header's file size field. {| class="wikitable"! Offset !! Size !! Type !! Description
|-
| 4 bytes0x00 || file_size || byte[] || Raw file data|} == Footer == The archive ends with a 14-byte terminator string: {| File lengthclass="wikitable"! Offset !! Size !! Type !! Description
|-
| Varies0x00 | The file data itself| 14 bytes || char[14] || "FINAL FANTASY7" (no null terminator)
|}
==== Section 4: Terminator ==Reading Algorithm ==
The game is remarkably flexible about LGP archives. So long as the TOC and the CRC data is intact it'll accept just about anything.