Engine services
Cross-cutting runtime services used by every module: archive/file I/O, frame timing, and input. Complements Engine startup and main loop. All addresses are for FF8_EN.exe (2000 PC release), image base 0x400000.
Archive I/O (FS/FI/FL)
Every file access goes through a virtual file layer (FILE_HANDLE_TABLE): Archive_OpenVirtualFile resolves a path either to a loose file on disk (CRT _open) or to an entry inside an FS archive, and read/seek/close operate on the virtual handle.
- An opened archive (
struc_flfifs) holds the.flpath list (loaded as a token list), the open.fsdata file and the.fiindex buffer. An FI record is 12 bytes:{u32 uncompressedSize, u32 fsOffset, u32 compression}. FL lines are matched case-insensitively. - Compression 1 = LZSS (
Archive_LZSSDecompress, 12-bit window with base offset 0xFEE, run length 3–18, out-of-window bytes read as zero, stream prefixed by a u32 size). A compressed file is decompressed whole on first read and cached on the handle. - A static configuration table (800-byte stride, kind ids in
ARCHIVE_KIND_TABLE) maps path substrings to archives. Kind 0 (FIELD) is special: field sub-archives are nested — the requested sub-archive’s.fi/.fl/.fsare extracted fromfield.fsinto<CurrentDir>\temp.fi/.fl/.fs(cached per subfolder) and then opened as a normal archive. Other kinds open their archive directly. - Path table:
CopyDataPathsStringsbuilds 260-byte-stride entries — app root,Data\, then per-folder paths (Sound, Music, Menu, Battle, Field, world Data rootPATH_DATA_DIR). World paths pass throughgetWorldFsPath, which implements the 2-character;xelision used to strip language tags from stored paths.
| Function | Address |
|---|---|
Archive_OpenVirtualFile / Archive_SeekVirtualFile / Archive_ReadVirtualFile / Archive_CloseVirtualFile | 0x51B4E0 / 0x51BDC0 / 0x51BE40 / 0x51BF50 |
Archive_OpenFsArchive | 0x40EFAA |
Archive_FindFileEntry (FL match → FI record) | 0x40F06B |
Archive_ReadFileData (raw or LZSS) | 0x40F52C |
Archive_LZSSDecompress | 0x40F852 |
Archive_LoadFlPathList / Archive_FL_GetPathByIndex | 0x428B6E / 0x428C1A |
IO_OpenFile (disk-or-archive resolution) | 0x40E33A |
File_WriteBufferToDisk (temp.* extraction) | 0x40EB71 |
Frame timing
The engine has two timer backends selected by USE_TIME_RDTSC: the raw CPU RDTSC counter, or timeGetTime (1 kHz). RDTSC is only enabled when CPU_DetectProcessor (full CPUID identification at startup) reports an Intel P5/P6 or AMD K6/K7 class CPU; its frequency is calibrated against QueryPerformanceCounter during a 100 ms busy-wait at realtime priority (CPU_MeasureTscFrequency) and stored in game_obj->countspersecond.
Frame pacing:
Time_SetTargetFrameRate(fps)— each module sets its own rate (world = 30.5 fps, field/battle = via their init) asFRAME_PERIOD_TICKS = countspersecond / fps.Time_WaitNextFrame— the limiter: sleeps off the remainder of the period when the frame was fast, or returns “over budget” carrying the overshoot (clamped to one period) and amortizing it over the next two frames.- The menu/title modules bypass the limiter and busy-wait on a fixed 60 fps period (
FRAME_PERIOD_TICKS_60FPS).
Input
Input_ProcessInput (pumped every frame from IsWindowNOTActive) polls DirectInput keyboard (256 DIK bytes), mouse, and up to two joysticks, then builds a 32-bit state per pad through the remap table at 0x1CD0208 (112 bytes per pad = 3 alternative binding banks × 32 codes; code < 0xDE = DIK scancode, 0x6A–0x6C = mouse buttons, 0xE0+n = joystick button, 0xFC–0xFF = stick Y−/Y+/X−/X+). Rising edges are computed as cur & ~(prev & cur), with a keyboard-style auto-repeat layer on top.
The resulting 16-bit game pad mask is the PSX digital pad layout (verified by Input_WritePsxPadButtons, which converts it active-low into an emulated PSX pad buffer):
| Bit | Button | Bit | Button |
|---|---|---|---|
| 0x0001 | L2 | 0x0100 | Select |
| 0x0002 | R2 | 0x0800 | Start |
| 0x0004 | L1 | 0x1000 | Up |
| 0x0008 | R1 | 0x2000 | Right |
| 0x0010 | Triangle (Cancel) | 0x4000 | Down |
| 0x0020 | Circle (Menu) | 0x8000 | Left |
| 0x0040 | Cross (Confirm) | ||
| 0x0080 | Square (Examine) |
remap_pad_input further remaps bits 0–11 through the player’s button config in the savemap settings; the D-pad nibble 0xF000 passes through unchanged. The world map keeps its own double-buffered copy (world_input_states[2] + world_input_frame_parity) with hold-to-repeat after 30 time units.
Debug key combos (raw DIK state, held 5 frames): Ctrl+R = soft reset, Ctrl+Q = quit. The bounds-check of that code still contains the developer string “William please check this”.
| Function | Address |
|---|---|
Input_ProcessInput | 0x467D10 |
Input_PollKeyboard / Input_PollMouse / Input_PollJoysticks | 0x468D80 / 0x468B60 / 0x4692B0 |
Input_GetPadState / Input_UpdateAutoRepeat | 0x468500 / 0x468540 |
Input_WritePsxPadButtons | 0x56D990 |
World_PollInputBeginFrame | 0x559240 |
InputKeyboardResetCombination (Ctrl+R / Ctrl+Q) | 0x4A2E50 |
Time_SetTargetFrameRate / Time_WaitNextFrame | 0x4020C0 / 0x4020F0 |
CPU_DetectProcessor / CPU_MeasureTscFrequency | 0x569E70 / 0x56AD8E |