224 lines
9.1 KiB
Markdown
224 lines
9.1 KiB
Markdown
# Megami Ibunroku Persona — Event Script Format Documentation
|
||
|
||
## Overview
|
||
|
||
The game stores event/dialogue scripts inside `ADV\E0.BIN` on the CD.
|
||
This file is a **container** holding 224 sub-files (individual event scripts).
|
||
|
||
---
|
||
|
||
## 1. CD Image (BIN/CUE)
|
||
|
||
- Format: MODE2/2352 (raw CD-ROM XA)
|
||
- Sector size: 2352 bytes
|
||
- User data offset: 24 bytes from sector start
|
||
- User data size: 2048 bytes per sector
|
||
|
||
The game uses internal file tables (not ISO9660) to locate files:
|
||
- **FSECT** (LBA 634): array of uint32_le, each entry = starting LBA of a game file
|
||
- **FSIZE** (LBA 623): array of uint32_le, each entry = size in bytes
|
||
- E0.BIN is at index **273** in both tables
|
||
|
||
---
|
||
|
||
## 2. E0 Container Format
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Pointer Table (variable length) │
|
||
│ uint16_le[N+1] pointers │
|
||
│ Terminated by 0x0000 │
|
||
├─────────────────────────────────────────────┤
|
||
│ Padding to 0x800 alignment │
|
||
├─────────────────────────────────────────────┤
|
||
│ Sub-file 0 (aligned to 0x800) │
|
||
├─────────────────────────────────────────────┤
|
||
│ Sub-file 1 (aligned to 0x800) │
|
||
├─────────────────────────────────────────────┤
|
||
│ ... │
|
||
├─────────────────────────────────────────────┤
|
||
│ Sub-file N-1 │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Pointer Table
|
||
|
||
| Offset | Type | Description |
|
||
|--------|-----------|----------------------------------------------------|
|
||
| 0 | uint16_le | Pointer to sub-file 0 (in 0x800-byte units) |
|
||
| 2 | uint16_le | Pointer to sub-file 1 |
|
||
| ... | ... | ... |
|
||
| N*2 | uint16_le | End-of-data pointer (marks end of last sub-file) |
|
||
| (N+1)*2| uint16_le | 0x0000 (terminator) |
|
||
|
||
- Original E0 has **225 entries** (224 sub-files + 1 end marker)
|
||
- To get byte offset: `pointer_value * 0x800`
|
||
- Sub-file N size: `(ptr[N+1] - ptr[N]) * 0x800`
|
||
|
||
---
|
||
|
||
## 3. Event Script Sub-file Format
|
||
|
||
Each sub-file has:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Sub-file Header (8 bytes typically) │
|
||
├─────────────────────────────────────────────┤
|
||
│ Script Data Section │
|
||
│ ├── Event Command Table (offsets 0x00-0x60+)│
|
||
│ ├── Script Bytecode │
|
||
│ ├── String Pointer Table │
|
||
│ ├── Text Data (strings) │
|
||
│ └── Trailing Data │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Sub-file Header
|
||
|
||
| Offset | Type | Value | Description |
|
||
|--------|-----------|---------|--------------------------|
|
||
| 0 | uint16_le | 8 | Header size in bytes |
|
||
| 2 | uint16_le | 0x8010 | Marker (always 10 80) |
|
||
| 4 | uint16_le | varies | (purpose TBD) |
|
||
| 6 | uint16_le | 0x8010 | Marker (always 10 80) |
|
||
|
||
### Script Data Section
|
||
|
||
The data section starts immediately after the header (`offset = hdr_size`).
|
||
All offsets below are relative to the start of the data section.
|
||
|
||
#### Key Fields
|
||
|
||
| Offset | Type | Name | Description |
|
||
|--------|-----------|------------|--------------------------------------------------|
|
||
| 52 | uint16_le | ett | End of text table / start of text blob area |
|
||
| 96 | uint16_le | table_ptr | Offset to string pointer table within data |
|
||
|
||
If `table_ptr == 0xFFFF`, the sub-file has no dialogue strings.
|
||
|
||
#### String Pointer Table
|
||
|
||
Located at `data[table_ptr]`. Entries follow this pattern:
|
||
|
||
```
|
||
FF 55 00 00 [ptr_lo] [ptr_hi] 10 80
|
||
```
|
||
|
||
- `FF 55 00 00` = prefix marker (4 bytes before the pointer)
|
||
- `ptr_lo, ptr_hi` = uint16_le offset within data section pointing to string start
|
||
- `10 80` = suffix marker
|
||
|
||
The table continues until a different pattern is encountered.
|
||
To find all strings, scan from `table_ptr` looking for `FF 55 00 00 XX XX 10 80` sequences.
|
||
|
||
---
|
||
|
||
## 4. Text Encoding
|
||
|
||
### Single-byte characters (0x01-0x7F)
|
||
|
||
Originally mapped to Japanese kana. Values 0x01-0x7F are indices into the font file.
|
||
|
||
| Range | Original Content |
|
||
|-----------|--------------------------|
|
||
| 0x01-0x2E | Hiragana (あ-ん) |
|
||
| 0x2F-0x5E | Katakana (ア-ン) |
|
||
| 0x5F-0x7F | Extended katakana + marks |
|
||
|
||
For Russian translation, these are remapped to Cyrillic:
|
||
| Range | Content |
|
||
|-----------|--------------------------|
|
||
| 0x01-0x42 | А-я (66 letters + Ё/ё) |
|
||
| 0x43 | Space |
|
||
|
||
### Two-byte characters (0x80xx)
|
||
|
||
High byte 0x80+ triggers a two-byte read. The pair forms a code looked up in the TBL file.
|
||
|
||
Common punctuation codes:
|
||
| Code | Character |
|
||
|--------|-----------|
|
||
| 80 A5 | . (period) |
|
||
| 80 CB | : (colon) |
|
||
| 80 CC | (JP space) |
|
||
| 80 D0 | ? (question)|
|
||
| 80 D1 | ! (exclaim) |
|
||
| 80 D5 | , (comma) |
|
||
| 80 E1 | - (dash) |
|
||
|
||
### Control codes (0xFF xx)
|
||
|
||
| Code | Parameters | Name | Description |
|
||
|-------------|---------------|--------------|------------------------------------|
|
||
| FF 00 | none | [ff00] | Unknown/NOP |
|
||
| FF 01 | none | (terminator) | End of string |
|
||
| FF 02 | none | [end] | End of dialogue sequence |
|
||
| FF 03 | none | [nl] | Newline |
|
||
| FF 04 | none | [clear] | Clear text box |
|
||
| FF 05 XX 00 | 1 byte + 00 | [wait=XX] | Pause for XX frames |
|
||
| FF 06 XX | 1 byte | [color=XX] | Set text color |
|
||
| FF 07 | none | [firstname] | Insert player's first name |
|
||
| FF 08 | none | [nickname] | Insert player's nickname |
|
||
| FF 09 | none | [ff09] | Unknown |
|
||
| FF 0A | none | [ff0a] | Unknown |
|
||
| FF 0B | none | [ff0b] | Unknown |
|
||
| FF 0C | none | [ff0c] | Unknown |
|
||
| FF 0D | none | [ff0d] | Unknown |
|
||
| FF 0E XX | 1 byte | [choice=XX] | Display choice menu (XX = menu ID) |
|
||
| FF 0F | none | [lastname] | Insert player's last name |
|
||
|
||
---
|
||
|
||
## 5. Font File
|
||
|
||
- Located at game file index **5** in FSECT/FSIZE
|
||
- Size: 65536 bytes
|
||
- Glyph size: 32 bytes each (16x16 pixels, 1bpp, 2 bytes per row)
|
||
- Glyph index N is at byte offset `N * 32`
|
||
- Indices 0x01-0x7F correspond to single-byte text codes
|
||
- Indices 0x80+ correspond to two-byte text code second bytes (TBL lookup)
|
||
|
||
---
|
||
|
||
## 6. TBL File (Persona_jap.tbl)
|
||
|
||
Plain text, UTF-8, format: `XXXX=char` per line where XXXX is hex code.
|
||
|
||
Example:
|
||
```
|
||
8090=漢
|
||
80A5=。
|
||
80CB=:
|
||
80CC=
|
||
80D0=?
|
||
80D1=!
|
||
80D5=、
|
||
```
|
||
|
||
The hex code is the 2-byte value as it appears in the script (big-endian in the TBL, but stored little-endian in the binary when the injector tool writes it).
|
||
|
||
---
|
||
|
||
## 7. Workflow for Translation
|
||
|
||
1. **Unpack**: Read ISO → extract E0 → parse pointer table → for each sub-file, parse strings
|
||
2. **Edit**: Modify string text (respecting encoding constraints and control codes)
|
||
3. **Repack**: Encode strings → rebuild sub-file (update string pointers) → rebuild E0 container → patch ISO
|
||
|
||
### Constraints when editing:
|
||
- New text blob must fit within `sub-file_size - ett` bytes (or sub-file must be resized)
|
||
- String pointer table positions are fixed (they're part of the script bytecode)
|
||
- Only the text area (after `ett`) is safe to resize
|
||
- If sub-file grows, the E0 container must be relocated in the ISO
|
||
|
||
---
|
||
|
||
## 8. Statistics (Original Japanese Rev 1)
|
||
|
||
- E0 container: 1,828,864 bytes (893 sectors)
|
||
- Sub-files: 224
|
||
- Sub-file sizes: 6,144 - 34,816 bytes
|
||
- Total dialogue strings: ~2,500 across all sub-files
|
||
- Sub-files with no strings (table_ptr=0xFFFF): ~27
|