Tutorial demo scripts
The tutorial menu (“TUTORIAL” in the main menu) offers demo entries that play back a guided demonstration of a menu screen: the Junction demos, the GF demo, the Squall/Zell/Rinoa limit break demos and the character switch demo. Each demo runs the real menu program on a fake savegame, driven by a small script that fakes controller input and shows caption windows.
All the data lives in mngrp.bin sections (raw mngrphd entries 160-179 and 204-205).
Demo record table
The nine demos come from a table of 12-byte records in the executable (Menu_TutorialDemoRecords):
| Offset | Type | Value | Description |
|---|---|---|---|
| 0 | Byte | demo_id | Tutorial menu entry id (5-13) |
| 1 | Byte | program_id | Menu program invoked: 18 = junction demos, 24 = GF demo, 28 = limit break demos, 29 = character switch |
| 2 | Byte | variant | Sub-mode for the program (limit breaks: 0 = Squall, 1 = Zell, 4 = Rinoa; junction: 0-3) |
| 3 | Byte | mock_gf_file | Raw mngrphd index of the mock GF records (177 or 179) |
| 4 | Byte | mock_save_file | Raw mngrphd index of the mock save data (176 or 178) |
| 5 | Byte | text_file | Raw mngrphd index of the caption text, a string section (160-167, 204) |
| 6 | Byte | script_file | Raw mngrphd index of the demo script (168-175, 205) |
| 7 | Byte | flag | 1 = build the demo party from the real save’s available characters (set only for the character switch demo) |
| 8 | Byte | help_id | Description string id in the tutorial menu (62-70) |
| 9-11 | Bytes | padding |
The four files are loaded to menu VRAM +0x2A000 / +0x29000 / +0x1B000 / +0x1F000 respectively, then the program starts with the demo script active.
Mock save data
Before a demo starts, the tutorial menu backs up the real save into menu VRAM (characters +0x27490, GFs +0x27050, settings +0x27AE0, party block +0x27AF4), then overwrites the live savemap with the mock files:
- Mock characters (raw 176/178): 8 records of 152 bytes, the savemap character record format (Squall…Edea). On load the current HP is forced to 9999 and status effects are cleared.
- Mock GF records (raw 177/179): 16 records of 68 bytes, the savemap GF record format (name in FF8 text, experience, HP, abilities…). HP is forced to 9999 and each GF name is replaced by the name from the real save’s backup, so the development names left in the file (misspellings such as “Quetcoatl”) never show in game.
Variant A (176/177) is used by the junction, magic junction, GF and limit break demos; variant B (178/179) by the elemental junction, status junction and character switch demos. When the demo ends, the backup is copied back over the savemap.
Script format
A demo script is a stream of UInt16 little-endian words. The high nibble is the opcode, the low 12 bits the operand:
| Opcode | Name | Effect |
|---|---|---|
| 0x1 | HOLD_BUTTON | OR 1 << (operand & 0xFF) into the synthesized pad word for this frame |
| 0x2 | WAIT_WINDOW_READY | Wait until the caption window is ready |
| 0x3 | SHOW_TEXT | Open a caption window centered on (X, Y) with string text_index of the caption section |
| 0x4 | HIDE_TEXT | Close the caption window |
| 0x5 | SET_TEXT_INDEX | text_index = operand |
| 0x6 | SET_TEXT_X | Caption center X = operand |
| 0x7 | SET_TEXT_Y | Caption center Y = operand |
| 0x8 | WAIT_CONFIRM | Wait for the player: confirm advances, cancel aborts the whole demo |
| 0x9 | WAIT | Wait operand frames |
| 0xA | WAIT_ANIM | Wait until the window open/close animation finishes |
| 0xF | END | Stop the demo script (input returns to the player) |
Each frame, the interpreter replaces the pad read: the word built by HOLD_BUTTON opcodes is written into the engine’s pad state, so the hosted menu program navigates as if the player pressed those buttons. Ops 0x1 and 0x4-0x7 execute immediately (several per frame); the wait ops yield until their condition is met.
HOLD_BUTTON bit indices
The HOLD_BUTTON operand (low byte) is the bit index ORed into the engine pad word (PAD_PRESSED/PAD_REPEAT). Bits 0-11 are the physical button slots consumed through the button-config remap (sub_4A2D60, identity with the default config); bits 12-15 are the D-pad, passed through the remap unchanged:
| Bit | Button | Menu action |
|---|---|---|
| 0 | L2 | Flee combo half |
| 1 | R2 | Flee combo half |
| 2 | L1 | Previous character/page |
| 3 | R1 | Next character/page |
| 4 | Triangle | Cancel/Back |
| 5 | Circle | Open menu |
| 6 | Cross | Confirm |
| 7 | Square | Examine |
| 8 | Select | |
| 11 | Start | |
| 12 | D-pad | Left |
| 13 | D-pad | Down |
| 14 | D-pad | Right |
| 15 | D-pad | Up |
The vanilla scripts only use bits 4, 6, 7 and 12-15.
Example (start of the Junction demo script, raw section 168)
0000: 60C0 SET_TEXT_X 192
0002: 7068 SET_TEXT_Y 104
0004: 5000 SET_TEXT_INDEX 0
0006: 903C WAIT 60
0008: 3000 SHOW_TEXT
000A: 2000 WAIT_WINDOW_READY
000C: 8000 WAIT_CONFIRM
000E: 4000 HIDE_TEXT
0010: A000 WAIT_ANIM
0012: 903C WAIT 60
0014: 5001 SET_TEXT_INDEX 1
...
Function addresses
| Function | Address | Description |
|---|---|---|
Menu_ReadPad_OrRunDemoScript | 0x4BE100 | Per-frame input hook: runs the script VM when a demo is active, otherwise reads the real pad |
Menu_DemoScript_Start | 0x4BE050 | Arms the VM with a script buffer and a caption text section |
Menu_TutorialDemoRecords | 0xB88360 | The 9 × 12-byte demo records |
Menu_TutorialPageList | 0xB88570 | Tutorial page-browser TIM list {63, 64, 65, -1} |
Menu_DemoScript_IP | 0x1D76BC0 | Current script word pointer |
Menu_DemoScript_TextSection | 0x1D76AB4 | Caption string section pointer |
Menu_DemoScript_Active | 0x1D77078 | 1 while a demo script drives the input |
Menu_DemoScript_Cancelled | 0x1D76B40 | Set when the player cancels the demo |
Menu_Demo_ApplyMockSave | 0x4C99E0 | Copies the mock character/GF records over the live savemap |
Menu_Demo_RestoreRealSave | 0x4CADA0 | Restores the real save backup when the demo ends |
Menu_Demo_UseRealRosterFlag | 0x1D771AC | Record flag byte: demo party from the real roster |