mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
PC port: Phase 1 boot-to-window (ROM load + map + SDL2 clear loop)
- scripts/gen_romassets.py: emit absolute cart-address asset symbols (0x10000000 + rom_offset) from filelist.<r>.csv + the asset .s files, in file_resource_table.inc.c order. US region (romassets_u.s). - romdata.c: load/validate the GE-specific header (magic, name @0x20, cart ID @0x3C, country @0x3E) and map the .z64 at cart base 0x10000000 so every &asset-symbol is a live host pointer. - libultra.c: osPiStartDma/osPiRawStartDma now service reads as bounds-checked memcpys from the mapped ROM (synchronous completion). - system.c: implement sysResolvePath ($S data dir, $E exe dir). - video.c: real SDL2 window + GL context + clear/swap frame loop with FPS accounting; main.c runs a Phase 1 demo loop (ESC quits) until the software RSP/scheduler can drive mainproc(). - CMakeLists: project(... ASM), wire romassets_<region>.s, drop assetstubs.c (replaced by generated symbols); n64stubs.c keeps only pure-RAM segment markers. - docs: findings D18-D23 (header offsets, BE music table, .rz tracks, scanner-prefixed manifest names, CMake ASM gotcha, absolute-symbol approach). Verified: baserom.u.z64 (12MB) passes validation and maps at 0x10000000; window opens and renders until killed; asset offsets spot-checked against ROM bytes (music bank seqCount=63 BE, first track offset 508).
This commit is contained in:
1 parent
5ea46fabc1
commit
2aad171344
14 files changed
+4462
-971
No files matched your search
@@ -45,7 +45,7 @@ Run `/linkcheck` for this sweep. Record new findings in `docs/PCPortResearch.md`
|
||||
|
||||
- **Phase 0 (scaffolding):** done, committed.
|
||||
- **Compile+link milestone:** all ~235 TUs compile and `ge007.x86_64.exe` links clean (`ninja ge007 -k 0`, 236/236). Findings D7–D17 in `docs/PCPortResearch.md` §11; asset stubs in `port/src/assetstubs.c`, fast3d no-ops in `port/src/gfxstub.c` (delete when real fast3d lands).
|
||||
- **Phase 1 (boot to window):** remaining — ROM loading in `romdata.c` (currently a TODO), run the binary, wire gfx stubs to a real window.
|
||||
- **Phase 1 (boot to window):** done — ROM loads/validates/maps at cart base 0x10000000 (`romdata.c`), SDL2 window + GL clear loop (`video.c`), absolute asset symbols from `scripts/gen_romassets.py` → `port/src/romassets_<r>.s` (findings D18–D23).
|
||||
- **Phase 2 (rendering):** bring in PD fast3d (replaces `port/src/gfxstub.c`), verify custom CC/RM modes against `gmain.s`, port the scheduler; replace asset stubs with ROM-backed data (D16).
|
||||
- **Phase 2 (rendering):** bring in PD fast3d, verify custom CC/RM modes against `gmain.s`, port the scheduler.
|
||||
- **Phase 3 (audio + input):** libaudio → SDL; decide the ASP strategy (C2: `aspMain*` dummies in `ucode.c`).
|
||||
|
||||
+21
-5
@@ -44,7 +44,9 @@ if(APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
project(ge007 C CXX)
|
||||
# ASM is needed for the generated absolute ROM asset symbols
|
||||
# (port/src/romassets_<region>.s, see scripts/gen_romassets.py).
|
||||
project(ge007 C CXX ASM)
|
||||
|
||||
include(TargetArch)
|
||||
include(FindPkgConfig)
|
||||
@@ -99,18 +101,21 @@ message(STATUS "Target platform: ${TARGET_PLATFORM}")
|
||||
if(ROMID STREQUAL "ntsc-final")
|
||||
set(VERSION_IS_PAL 0)
|
||||
set(BIN_REGION "")
|
||||
set(ASSET_REGION "u")
|
||||
set(REGION_DEFS
|
||||
-DVERSION_US -DLANG_US -DREFRESH_NTSC
|
||||
-DLEFTOVERDEBUG -DLEFTOVERSPECTRUM -DBUGFIX_R0 -DBYTEMATCH)
|
||||
elseif(ROMID STREQUAL "pal-final")
|
||||
set(VERSION_IS_PAL 1)
|
||||
set(BIN_REGION ".pal")
|
||||
set(ASSET_REGION "e")
|
||||
set(REGION_DEFS
|
||||
-DVERSION_EU -DLANG_EU -DREFRESH_PAL
|
||||
-DBUGFIX_R1 -DBUGFIX_R2 -DBYTEMATCH)
|
||||
elseif(ROMID STREQUAL "jpn-final")
|
||||
set(VERSION_IS_PAL 0)
|
||||
set(BIN_REGION ".jpn")
|
||||
set(ASSET_REGION "j")
|
||||
set(REGION_DEFS
|
||||
-DVERSION_JP -DLANG_JP -DREFRESH_NTSC
|
||||
-DBUGFIX_R1 -DLEFTOVERDEBUG -DLEFTOVERSPECTRUM -DBYTEMATCH)
|
||||
@@ -255,6 +260,15 @@ file(GLOB_RECURSE SRC_PORT "${CMAKE_SOURCE_DIR}/port/src/*.c")
|
||||
file(GLOB_RECURSE SRC_PORT_CXX "${CMAKE_SOURCE_DIR}/port/src/*.cpp")
|
||||
file(GLOB_RECURSE SRC_FAST3D "${CMAKE_SOURCE_DIR}/port/fast3d/*.cpp")
|
||||
|
||||
# Generated absolute ROM asset symbols for this region (see
|
||||
# scripts/gen_romassets.py). Defines every obseg/ramrom/music label and the
|
||||
# ge007.ld segment markers as 0x10000000 + rom_offset, so `&symbol` / romCopy()
|
||||
# work once romdata.c maps the .z64 at 0x10000000.
|
||||
set(SRC_ROMASSETS "${CMAKE_SOURCE_DIR}/port/src/romassets_${ASSET_REGION}.s")
|
||||
if(NOT EXISTS ${SRC_ROMASSETS})
|
||||
message(FATAL_ERROR "Missing ${SRC_ROMASSETS} — run: python3 scripts/gen_romassets.py ${ASSET_REGION}")
|
||||
endif()
|
||||
|
||||
# Game code — compiled unmodified.
|
||||
file(GLOB_RECURSE SRC_GAME "${CMAKE_SOURCE_DIR}/src/game/*.c")
|
||||
|
||||
@@ -296,10 +310,11 @@ list(PREPEND SRC_LIBULTRARE_LIBC "${CMAKE_SOURCE_DIR}")
|
||||
# `intro`, ...) that the N64 build keeps apart by linking every file into its
|
||||
# own ELF (see assets/obseg/Makefile.*). In a single host link they collide.
|
||||
# The game loads those blobs from ROM at runtime anyway; their top-level
|
||||
# symbols (C*Z, G*Z, P*Z, Tbg_*Z, U*Z, L*E/L*J, bg_*_all_p_seg, ...) are
|
||||
# stubbed in port/src/assetstubs.c until Phase 2 ROM loading. The per-model
|
||||
# data itself (chr/*/Model.c etc.) is gitignored ROM-derived and absent from
|
||||
# this checkout. See docs/PCPortResearch.md §11.
|
||||
# symbols (C*Z, G*Z, P*Z, Tbg_*Z, U*Z, L*E/L*J, bg_*_all_p_seg, ...) are now
|
||||
# absolute cart addresses in the generated romassets_<region>.s (see
|
||||
# scripts/gen_romassets.py), backed by the ROM image that romdata.c maps at
|
||||
# 0x10000000. The per-model data itself (chr/*/Model.c etc.) is gitignored
|
||||
# ROM-derived and absent from this checkout. See docs/PCPortResearch.md §11.
|
||||
set(SRC_ASSETS
|
||||
assets/animationtable_data.c
|
||||
assets/animationtable_entries.c
|
||||
@@ -361,6 +376,7 @@ list(PREPEND SRC_ENGINE "${CMAKE_SOURCE_DIR}")
|
||||
set(SRC
|
||||
${SRC_GAME}
|
||||
${SRC_PORT}
|
||||
${SRC_ROMASSETS}
|
||||
${SRC_PORT_CXX}
|
||||
${SRC_FAST3D}
|
||||
${SRC_LIBAUDIO}
|
||||
|
||||
+63
-5
@@ -665,13 +665,71 @@ in the same change. Where a fix could not be made purely in `port/`, the minimal
|
||||
implementations of every entry point (plus `gfx_current_dimensions`) so
|
||||
video.c links. **Delete it when the real fast3d sources are added** (same
|
||||
symbols → duplicate-definition error if both remain).
|
||||
* **D18 — GE ROM header offsets.** The N64 header is GE-specific
|
||||
(`src/rom_header.s`): magic `0x80371240` @0x00, ROM name "GOLDENEYE" @0x20,
|
||||
cartridge ID "GE" **@0x3C**, country byte **@0x3E** ('E' US / 'P' EU /
|
||||
'J' JP), version @0x3F. The standard-N64 offsets (ID @0x38, country @0x3A)
|
||||
are wrong for GE — verified against `baserom.u.z64` (hexdump: `... 47 45
|
||||
45 00` at 0x3C–0x3F). romdata.c validates all four fields.
|
||||
* **D19 — music table is big-endian in the ROM.** The `.music` section of
|
||||
`assets/music/music.s` (the `RareALSeqBankFile` header + 63 × 8-byte
|
||||
`music_table_entry` records) is stored in the ROM in **big-endian** word
|
||||
order: at 0x419790 the bytes are `00 3f 00 00 | 00 00 01 fc` = BE u16
|
||||
seqCount=63 (= NUM_MUSIC_TRACKS), BE u16 unk=0, then BE u32 offset 508 for
|
||||
the first track. The N64 build evidently assembles that section with
|
||||
big-endian output; the PC port never reassembles it — the CSV offsets are
|
||||
unaffected and `musicSeqPlayerInit()`'s romCopy + `musicSeqFileNew()`
|
||||
patching work on the raw bytes unchanged.
|
||||
* **D20 — US music tracks are RLE-compressed in ROM.** The `music_file`
|
||||
macro incbins `build/u/assets/music/<name>.rz` under `.ifdef VERSION_US`, so
|
||||
every M* track lives in the ROM 1172/RLE-compressed; the filelist sizes are
|
||||
the compressed lengths and per-track `end_` symbols = start + csv_size are
|
||||
correct boundaries. Decompression happens at playback (Phase 3 audio), not
|
||||
at load — no Phase-1/2 action needed.
|
||||
* **D21 — scanner-prefixed manifest names.** `filelist.<r>.csv` contains
|
||||
entries whose file column is prefixed with the scan origin, e.g.
|
||||
`assets/ge007.u.117880.jfont_dl.bin` (real name `jfont_dl.bin`).
|
||||
gen_romassets.py aliases such names to their suffix after the
|
||||
`ge007.<r>.<hexoffset>.` prefix, so font/image/oddtexture segments resolve.
|
||||
* **D22 — CMake silently skips `.s` files without ASM enabled.** The first
|
||||
assembly source in the PC build (`romassets_u.s`) was added to the target
|
||||
but never compiled: `project(ge007 C CXX)` had no ASM language, and CMake
|
||||
does not error on unknown extensions — it just drops them, surfacing only as
|
||||
a wall of undefined asset symbols at link. Fixed with
|
||||
`project(ge007 C CXX ASM)`. Lesson: when adding a new source *extension*,
|
||||
verify the object file actually appears in the build graph.
|
||||
* **D23 — absolute cart-address asset symbols (Phase 1 approach).** Instead of
|
||||
zeroed stubs (D16), every ROM asset symbol is now an ABSOLUTE cart address:
|
||||
`scripts/gen_romassets.py` emits `port/src/romassets_<r>.s` defining all
|
||||
obseg labels (in `file_resource_table.inc.c` order — ob.c computes file
|
||||
sizes as `table[i+1].hw_address - table[i].hw_address`, so order matters),
|
||||
ramrom files, music tracks/markers, and the ge007.ld segment markers as
|
||||
`0x10000000 + rom_offset` from `filelist.<r>.csv`. romdata.c maps the .z64
|
||||
at exactly 0x10000000 (VirtualAlloc preferred address), so `&symbol` is a
|
||||
live host pointer and the PI shims (`osPiStartDma`/`osPiRawStartDma` in
|
||||
libultra.c) service reads as bounds-checked memcpys. This makes both DMA'd
|
||||
assets (models, banks) and direct-read assets (fonts, image display lists)
|
||||
work with unmodified game code. `assetstubs.c` is deleted; n64stubs.c keeps
|
||||
only the pure-RAM segment symbols (`_bssSegmentEnd`, `_csegment*`,
|
||||
`_inflate/_gameSegmentVaddr*`). US-only for now: EU/JPN manifests have
|
||||
naming inconsistencies (trailing-Z add/drop, region subdirs) that need the
|
||||
same treatment before those regions build.
|
||||
|
||||
### E. Compile + link milestone (status)
|
||||
|
||||
All ~235 translation units now compile and the target **links**: clean build
|
||||
from scratch is `236/236` steps, zero errors (`ninja ge007 -k 0`), producing
|
||||
All ~235 translation units compile and the target **links**: clean build from
|
||||
scratch is `236/236` steps, zero errors (`ninja ge007 -k 0`), producing
|
||||
`ge007.x86_64.exe`. Remaining warnings (~4.5k) are expected IDO-leniency noise
|
||||
(int-conversion, implicit declarations, etc.), demoted via the CMake warning
|
||||
flags. The binary is not yet runnable end-to-end: gfx is stubbed (D17), asset
|
||||
model/level data is zeroed (D16), and ROM loading is still a Phase-2 TODO in
|
||||
romdata.c.
|
||||
flags.
|
||||
|
||||
**Phase 1 (boot to window) is done:** `romdata.c` loads and validates the ROM
|
||||
(`baserom.u.z64` at the repo root, or a final .z64 in `data/` / next to the
|
||||
exe), maps it at cart base 0x10000000 (D23); `video.c` opens an SDL2 640×480
|
||||
window with a GL context and clears/presents every frame; `main.c` runs a
|
||||
demo loop (ESC quits) in place of `mainproc()`, which is deferred until the
|
||||
software RSP + scheduler can service real frames. Verified: ROM header passes
|
||||
validation, 12 MB mapped at 0x10000000, window renders for the full test
|
||||
duration. Asset symbol spot-checks against the ROM confirm correct offsets
|
||||
(e.g. music bank BE seqCount=63 @0x419790, first track offset 508 → Mno_music
|
||||
@0x41998C; sfx/instrument banks start with GE's `B1` header).
|
||||
@@ -24,11 +24,18 @@ extern "C" {
|
||||
int romdataInit(void);
|
||||
void romdataDestroy(void);
|
||||
|
||||
/* Pointer to the loaded ROM image (start of the .z64). */
|
||||
/* Pointer to the loaded ROM image (start of the .z64). When the image is
|
||||
* mapped at the N64 cart base this is 0x10000000 itself. */
|
||||
const u8 *romdataGetRom(void);
|
||||
/* Size of the loaded ROM in bytes. */
|
||||
u32 romdataGetRomSize(void);
|
||||
|
||||
/*
|
||||
* True if [addr, addr+size) is a valid cart address (0x10000000+) inside the
|
||||
* mapped image. The PI shims call this before servicing DMA.
|
||||
*/
|
||||
int romdataCartAddrValid(u32 addr, u32 size);
|
||||
|
||||
/*
|
||||
* Map an N64 virtual address (0xA0000000 RDRAM space) to the corresponding
|
||||
* pointer in the loaded image. Used to resolve asset/segment references.
|
||||
|
||||
@@ -26,6 +26,10 @@ void videoStartFrame(void);
|
||||
void videoSubmitCommands(Gfx *cmds); /* runs the software RSP on the list */
|
||||
void videoEndFrame(void);
|
||||
|
||||
/* Pump window events; returns 1 when the app should quit. Used by the
|
||||
* Phase 1 demo loop until the scheduler owns the frame boundary. */
|
||||
int videoHandleEvents(void);
|
||||
|
||||
/* VSync / framerate control. */
|
||||
void videoSetVsync(int enabled);
|
||||
void videoSetFramerateLimit(int fps); /* 0 = unlimited */
|
||||
|
||||
@@ -1,816 +0,0 @@
|
||||
/*
|
||||
* Link stubs for ROM asset data that is NOT present in this checkout.
|
||||
*
|
||||
* The N64 build gets these symbols from assets/obseg/ob_seg.s and the per-
|
||||
* category .s files: each model/level file is compressed to a .rz blob and
|
||||
* .incbin'ed into the ROM at link time (the blobs are gitignored, ROM-
|
||||
* derived). The decompiled C sources for them (chr/<name>/Model.c etc.)
|
||||
* are also absent. ob.c's file_resource_table references every one of these symbols
|
||||
* by address, so they must exist to link.
|
||||
*
|
||||
* Each is a single zero word: just enough to satisfy the linker and any
|
||||
* &symbol reference. The game never meaningfully reads them in Phase 1;
|
||||
* real data arrives with ROM loading in Phase 2 (romdata.c), which will also
|
||||
* replace file_resource_table's address arithmetic. See
|
||||
* docs/PCPortResearch.md section 11.
|
||||
*/
|
||||
|
||||
#include <PR/ultratypes.h>
|
||||
|
||||
/* --- Character models (C*Z) --------------------------------------------- */
|
||||
u32 CarmourguardZ[1] = {0};
|
||||
u32 CbaronsamediZ[1] = {0};
|
||||
u32 CbluecamguardZ[1] = {0};
|
||||
u32 CbluemanZ[1] = {0};
|
||||
u32 CbluewomanZ[1] = {0};
|
||||
u32 CboilerbondZ[1] = {0};
|
||||
u32 CboilertrevZ[1] = {0};
|
||||
u32 CborisZ[1] = {0};
|
||||
u32 CcamguardZ[1] = {0};
|
||||
u32 CcardimanZ[1] = {0};
|
||||
u32 CcheckmanZ[1] = {0};
|
||||
u32 CcommguardZ[1] = {0};
|
||||
u32 CdjbondZ[1] = {0};
|
||||
u32 CfattechwomanZ[1] = {0};
|
||||
u32 Cgreatguard2Z[1] = {0};
|
||||
u32 CgreatguardZ[1] = {0};
|
||||
u32 CgreyguardZ[1] = {0};
|
||||
u32 CgreymanZ[1] = {0};
|
||||
u32 CheadalanZ[1] = {0};
|
||||
u32 CheadbZ[1] = {0};
|
||||
u32 CheadbalaclavaZ[1] = {0};
|
||||
u32 CheadbikeZ[1] = {0};
|
||||
u32 CheadbrosnanZ[1] = {0};
|
||||
u32 CheadbrosnanboilerZ[1] = {0};
|
||||
u32 CheadbrosnansnowZ[1] = {0};
|
||||
u32 CheadbrosnansuitZ[1] = {0};
|
||||
u32 CheadbrosnantimberZ[1] = {0};
|
||||
u32 CheadchrisZ[1] = {0};
|
||||
u32 CheaddaveZ[1] = {0};
|
||||
u32 CheaddesZ[1] = {0};
|
||||
u32 CheadduncanZ[1] = {0};
|
||||
u32 CheaddwayneZ[1] = {0};
|
||||
u32 CheadgrahamZ[1] = {0};
|
||||
u32 CheadgrantZ[1] = {0};
|
||||
u32 CheadjimZ[1] = {0};
|
||||
u32 Cheadjoe2Z[1] = {0};
|
||||
u32 CheadjoeZ[1] = {0};
|
||||
u32 CheadjoelZ[1] = {0};
|
||||
u32 CheadkarlZ[1] = {0};
|
||||
u32 CheadkenZ[1] = {0};
|
||||
u32 CheadleeZ[1] = {0};
|
||||
u32 CheadmandyZ[1] = {0};
|
||||
u32 CheadmarionZ[1] = {0};
|
||||
u32 CheadmarkZ[1] = {0};
|
||||
u32 CheadmartinZ[1] = {0};
|
||||
u32 CheadmishkinZ[1] = {0};
|
||||
u32 CheadneilZ[1] = {0};
|
||||
u32 CheadpeteZ[1] = {0};
|
||||
u32 CheadrobinZ[1] = {0};
|
||||
u32 CheadsallyZ[1] = {0};
|
||||
u32 CheadscottZ[1] = {0};
|
||||
u32 CheadshaunZ[1] = {0};
|
||||
u32 CheadsteveeZ[1] = {0};
|
||||
u32 CheadstevehZ[1] = {0};
|
||||
u32 CheadvivienZ[1] = {0};
|
||||
u32 CjawsZ[1] = {0};
|
||||
u32 CjeanwomanZ[1] = {0};
|
||||
u32 CmaydayZ[1] = {0};
|
||||
u32 CmoonfemaleZ[1] = {0};
|
||||
u32 CmoonguardZ[1] = {0};
|
||||
u32 CnatalyaZ[1] = {0};
|
||||
u32 CnavyguardZ[1] = {0};
|
||||
u32 CoddjobZ[1] = {0};
|
||||
u32 ColiveguardZ[1] = {0};
|
||||
u32 CorumovZ[1] = {0};
|
||||
u32 CpilotZ[1] = {0};
|
||||
u32 CredmanZ[1] = {0};
|
||||
u32 CrusguardZ[1] = {0};
|
||||
u32 CsnowbondZ[1] = {0};
|
||||
u32 CsnowguardZ[1] = {0};
|
||||
u32 CspicebondZ[1] = {0};
|
||||
u32 Csuit_lf_handZ[1] = {0};
|
||||
u32 CsuitbondZ[1] = {0};
|
||||
u32 CtechmanZ[1] = {0};
|
||||
u32 CtechwomanZ[1] = {0};
|
||||
u32 CtimberbondZ[1] = {0};
|
||||
u32 CtrevelyanZ[1] = {0};
|
||||
u32 CtrevguardZ[1] = {0};
|
||||
u32 CvalentinZ[1] = {0};
|
||||
u32 CxeniaZ[1] = {0};
|
||||
|
||||
/* --- Gun / prop-object models (G*Z) ---------------------------------- */
|
||||
u32 Gak47Z[1] = {0};
|
||||
u32 GaudiotapeZ[1] = {0};
|
||||
u32 GautoshotZ[1] = {0};
|
||||
u32 GblackboxZ[1] = {0};
|
||||
u32 GblueprintsZ[1] = {0};
|
||||
u32 GbombcaseZ[1] = {0};
|
||||
u32 GbombdefuserZ[1] = {0};
|
||||
u32 GbriefcaseZ[1] = {0};
|
||||
u32 GbugZ[1] = {0};
|
||||
u32 GbugdetectorZ[1] = {0};
|
||||
u32 GbungeeZ[1] = {0};
|
||||
u32 GcameraZ[1] = {0};
|
||||
u32 GcartblueZ[1] = {0};
|
||||
u32 GcartridgeZ[1] = {0};
|
||||
u32 GcartrifleZ[1] = {0};
|
||||
u32 GcartshellZ[1] = {0};
|
||||
u32 GcircuitboardZ[1] = {0};
|
||||
u32 GclipboardZ[1] = {0};
|
||||
u32 GcreditcardZ[1] = {0};
|
||||
u32 GdarkglassesZ[1] = {0};
|
||||
u32 GdatathiefZ[1] = {0};
|
||||
u32 GdattapeZ[1] = {0};
|
||||
u32 GdoordecoderZ[1] = {0};
|
||||
u32 GdoorexploderZ[1] = {0};
|
||||
u32 GdossierredZ[1] = {0};
|
||||
u32 GdynamiteZ[1] = {0};
|
||||
u32 GexplosivefloppyZ[1] = {0};
|
||||
u32 GexplosivepenZ[1] = {0};
|
||||
u32 GextinguisherZ[1] = {0};
|
||||
u32 GfingergunZ[1] = {0};
|
||||
u32 GfistZ[1] = {0};
|
||||
u32 GflarepistolZ[1] = {0};
|
||||
u32 Gfnp90Z[1] = {0};
|
||||
u32 GgaskeyringZ[1] = {0};
|
||||
u32 GgoldbarZ[1] = {0};
|
||||
u32 GgoldeneyekeyZ[1] = {0};
|
||||
u32 GgoldengunZ[1] = {0};
|
||||
u32 GgoldwppkZ[1] = {0};
|
||||
u32 GgrenadeZ[1] = {0};
|
||||
u32 GgrenadelaunchZ[1] = {0};
|
||||
u32 GheroinZ[1] = {0};
|
||||
u32 GjoypadZ[1] = {0};
|
||||
u32 GkeyanalysercaseZ[1] = {0};
|
||||
u32 GkeyboltZ[1] = {0};
|
||||
u32 GkeycardZ[1] = {0};
|
||||
u32 GkeyyaleZ[1] = {0};
|
||||
u32 GknifeZ[1] = {0};
|
||||
u32 GlaserZ[1] = {0};
|
||||
u32 GlectreZ[1] = {0};
|
||||
u32 GlockexploderZ[1] = {0};
|
||||
u32 Gm16Z[1] = {0};
|
||||
u32 GmapZ[1] = {0};
|
||||
u32 GmicrocameraZ[1] = {0};
|
||||
u32 GmicrocodeZ[1] = {0};
|
||||
u32 GmicrofilmZ[1] = {0};
|
||||
u32 GmoneyZ[1] = {0};
|
||||
u32 Gmp5kZ[1] = {0};
|
||||
u32 Gmp5ksilZ[1] = {0};
|
||||
u32 GpitongunZ[1] = {0};
|
||||
u32 GplansZ[1] = {0};
|
||||
u32 GplastiqueZ[1] = {0};
|
||||
u32 GpolarizedglassesZ[1] = {0};
|
||||
u32 GproximitymineZ[1] = {0};
|
||||
u32 GremotemineZ[1] = {0};
|
||||
u32 GrocketlaunchZ[1] = {0};
|
||||
u32 GrugerZ[1] = {0};
|
||||
u32 GsafecrackercaseZ[1] = {0};
|
||||
u32 GshotgunZ[1] = {0};
|
||||
u32 GsilverwppkZ[1] = {0};
|
||||
u32 GskorpionZ[1] = {0};
|
||||
u32 GsniperrifleZ[1] = {0};
|
||||
u32 GspectreZ[1] = {0};
|
||||
u32 GspooltapeZ[1] = {0};
|
||||
u32 GspyfileZ[1] = {0};
|
||||
u32 GstafflistZ[1] = {0};
|
||||
u32 GtaserZ[1] = {0};
|
||||
u32 GthrowknifeZ[1] = {0};
|
||||
u32 GtimedmineZ[1] = {0};
|
||||
u32 GtriggerZ[1] = {0};
|
||||
u32 Gtt33Z[1] = {0};
|
||||
u32 GuziZ[1] = {0};
|
||||
u32 GvideotapeZ[1] = {0};
|
||||
u32 GwatchcommunicatorZ[1] = {0};
|
||||
u32 GwatchgeigercounterZ[1] = {0};
|
||||
u32 GwatchidentifierZ[1] = {0};
|
||||
u32 GwatchlaserZ[1] = {0};
|
||||
u32 GwatchmagnetattractZ[1] = {0};
|
||||
u32 GwatchmagnetrepelZ[1] = {0};
|
||||
u32 GweaponcaseZ[1] = {0};
|
||||
u32 GwppkZ[1] = {0};
|
||||
u32 GwppksilZ[1] = {0};
|
||||
u32 GwristdartZ[1] = {0};
|
||||
|
||||
/* --- Prop models (P*Z) ------------------------------------------------- */
|
||||
u32 PICBMZ[1] = {0};
|
||||
u32 PICBM_noseZ[1] = {0};
|
||||
u32 Pak47magZ[1] = {0};
|
||||
u32 Palarm1Z[1] = {0};
|
||||
u32 Palarm2Z[1] = {0};
|
||||
u32 Pammo_crate1Z[1] = {0};
|
||||
u32 Pammo_crate2Z[1] = {0};
|
||||
u32 Pammo_crate3Z[1] = {0};
|
||||
u32 Pammo_crate4Z[1] = {0};
|
||||
u32 Pammo_crate5Z[1] = {0};
|
||||
u32 PapcZ[1] = {0};
|
||||
u32 Parchsecdoor1Z[1] = {0};
|
||||
u32 Parchsecdoor2Z[1] = {0};
|
||||
u32 ParticZ[1] = {0};
|
||||
u32 PartictrailerZ[1] = {0};
|
||||
u32 PbarricadeZ[1] = {0};
|
||||
u32 Pbin1Z[1] = {0};
|
||||
u32 Pblotter1Z[1] = {0};
|
||||
u32 PbodyarmourZ[1] = {0};
|
||||
u32 PbodyarmourvestZ[1] = {0};
|
||||
u32 PbollardZ[1] = {0};
|
||||
u32 PbombZ[1] = {0};
|
||||
u32 Pbook1Z[1] = {0};
|
||||
u32 Pbookshelf1Z[1] = {0};
|
||||
u32 Pborg_crateZ[1] = {0};
|
||||
u32 PboxcartridgesZ[1] = {0};
|
||||
u32 Pboxes2x4Z[1] = {0};
|
||||
u32 Pboxes3x4Z[1] = {0};
|
||||
u32 Pboxes4x4Z[1] = {0};
|
||||
u32 PbrakeunitZ[1] = {0};
|
||||
u32 Pbridge_console1aZ[1] = {0};
|
||||
u32 Pbridge_console1bZ[1] = {0};
|
||||
u32 Pbridge_console2aZ[1] = {0};
|
||||
u32 Pbridge_console2bZ[1] = {0};
|
||||
u32 Pbridge_console3aZ[1] = {0};
|
||||
u32 Pbridge_console3bZ[1] = {0};
|
||||
u32 PcarbmwZ[1] = {0};
|
||||
u32 Pcard_box1Z[1] = {0};
|
||||
u32 Pcard_box2Z[1] = {0};
|
||||
u32 Pcard_box3Z[1] = {0};
|
||||
u32 Pcard_box4_lgZ[1] = {0};
|
||||
u32 Pcard_box5_lgZ[1] = {0};
|
||||
u32 Pcard_box6_lgZ[1] = {0};
|
||||
u32 PcarescortZ[1] = {0};
|
||||
u32 PcargolfZ[1] = {0};
|
||||
u32 PcarweirdZ[1] = {0};
|
||||
u32 PcarzilZ[1] = {0};
|
||||
u32 PcctvZ[1] = {0};
|
||||
u32 PchraudiotapeZ[1] = {0};
|
||||
u32 PchrautoshotZ[1] = {0};
|
||||
u32 PchrblackboxZ[1] = {0};
|
||||
u32 PchrblueprintsZ[1] = {0};
|
||||
u32 PchrbombcaseZ[1] = {0};
|
||||
u32 PchrbombdefuserZ[1] = {0};
|
||||
u32 PchrbriefcaseZ[1] = {0};
|
||||
u32 PchrbugZ[1] = {0};
|
||||
u32 PchrbugdetectorZ[1] = {0};
|
||||
u32 PchrbungeeZ[1] = {0};
|
||||
u32 PchrcameraZ[1] = {0};
|
||||
u32 PchrcircuitboardZ[1] = {0};
|
||||
u32 PchrclipboardZ[1] = {0};
|
||||
u32 PchrcreditcardZ[1] = {0};
|
||||
u32 PchrdarkglassesZ[1] = {0};
|
||||
u32 PchrdatathiefZ[1] = {0};
|
||||
u32 PchrdattapeZ[1] = {0};
|
||||
u32 PchrdoordecoderZ[1] = {0};
|
||||
u32 PchrdoorexploderZ[1] = {0};
|
||||
u32 PchrdossierredZ[1] = {0};
|
||||
u32 PchrdynamiteZ[1] = {0};
|
||||
u32 PchrexplosivepenZ[1] = {0};
|
||||
u32 PchrextinguisherZ[1] = {0};
|
||||
u32 PchrfingergunZ[1] = {0};
|
||||
u32 PchrflarepistolZ[1] = {0};
|
||||
u32 Pchrfnp90Z[1] = {0};
|
||||
u32 PchrgaskeyringZ[1] = {0};
|
||||
u32 PchrgoldbarZ[1] = {0};
|
||||
u32 PchrgoldenZ[1] = {0};
|
||||
u32 PchrgoldeneyekeyZ[1] = {0};
|
||||
u32 PchrgoldwppkZ[1] = {0};
|
||||
u32 PchrgrenadeZ[1] = {0};
|
||||
u32 PchrgrenadelaunchZ[1] = {0};
|
||||
u32 PchrgrenaderoundZ[1] = {0};
|
||||
u32 PchrheroinZ[1] = {0};
|
||||
u32 PchrkalashZ[1] = {0};
|
||||
u32 PchrkeyanalysercaseZ[1] = {0};
|
||||
u32 PchrkeyboltZ[1] = {0};
|
||||
u32 PchrkeyyaleZ[1] = {0};
|
||||
u32 PchrknifeZ[1] = {0};
|
||||
u32 PchrlaserZ[1] = {0};
|
||||
u32 PchrlectreZ[1] = {0};
|
||||
u32 PchrlockexploderZ[1] = {0};
|
||||
u32 Pchrm16Z[1] = {0};
|
||||
u32 PchrmapZ[1] = {0};
|
||||
u32 PchrmicrocameraZ[1] = {0};
|
||||
u32 PchrmicrocodeZ[1] = {0};
|
||||
u32 PchrmicrofilmZ[1] = {0};
|
||||
u32 PchrmoneyZ[1] = {0};
|
||||
u32 Pchrmp5kZ[1] = {0};
|
||||
u32 Pchrmp5ksilZ[1] = {0};
|
||||
u32 PchrpitongunZ[1] = {0};
|
||||
u32 PchrplansZ[1] = {0};
|
||||
u32 PchrplastiqueZ[1] = {0};
|
||||
u32 PchrpolarizedglassesZ[1] = {0};
|
||||
u32 PchrproximitymineZ[1] = {0};
|
||||
u32 PchrremotemineZ[1] = {0};
|
||||
u32 PchrrocketZ[1] = {0};
|
||||
u32 PchrrocketlaunchZ[1] = {0};
|
||||
u32 PchrrugerZ[1] = {0};
|
||||
u32 PchrsafecrackercaseZ[1] = {0};
|
||||
u32 PchrshotgunZ[1] = {0};
|
||||
u32 PchrsilverwppkZ[1] = {0};
|
||||
u32 PchrskorpionZ[1] = {0};
|
||||
u32 PchrsniperrifleZ[1] = {0};
|
||||
u32 PchrspectreZ[1] = {0};
|
||||
u32 PchrspooltapeZ[1] = {0};
|
||||
u32 PchrspyfileZ[1] = {0};
|
||||
u32 PchrstafflistZ[1] = {0};
|
||||
u32 PchrtesttubeZ[1] = {0};
|
||||
u32 PchrthrowknifeZ[1] = {0};
|
||||
u32 PchrtimedmineZ[1] = {0};
|
||||
u32 Pchrtt33Z[1] = {0};
|
||||
u32 PchruziZ[1] = {0};
|
||||
u32 PchrvideotapeZ[1] = {0};
|
||||
u32 PchrweaponcaseZ[1] = {0};
|
||||
u32 PchrwppkZ[1] = {0};
|
||||
u32 PchrwppksilZ[1] = {0};
|
||||
u32 PchrwristdartZ[1] = {0};
|
||||
u32 Pconsole1Z[1] = {0};
|
||||
u32 Pconsole2Z[1] = {0};
|
||||
u32 Pconsole3Z[1] = {0};
|
||||
u32 Pconsole_sev2aZ[1] = {0};
|
||||
u32 Pconsole_sev2bZ[1] = {0};
|
||||
u32 Pconsole_sev2cZ[1] = {0};
|
||||
u32 Pconsole_sev2dZ[1] = {0};
|
||||
u32 Pconsole_sev_GEaZ[1] = {0};
|
||||
u32 Pconsole_sev_GEbZ[1] = {0};
|
||||
u32 Pconsole_sevaZ[1] = {0};
|
||||
u32 Pconsole_sevbZ[1] = {0};
|
||||
u32 Pconsole_sevcZ[1] = {0};
|
||||
u32 Pconsole_sevdZ[1] = {0};
|
||||
u32 Pcryptdoor1aZ[1] = {0};
|
||||
u32 Pcryptdoor1bZ[1] = {0};
|
||||
u32 Pcryptdoor2aZ[1] = {0};
|
||||
u32 Pcryptdoor2bZ[1] = {0};
|
||||
u32 Pcryptdoor3Z[1] = {0};
|
||||
u32 Pcryptdoor4Z[1] = {0};
|
||||
u32 PdamchaindoorZ[1] = {0};
|
||||
u32 PdamgatedoorZ[1] = {0};
|
||||
u32 PdamtundoorZ[1] = {0};
|
||||
u32 Pdepot_door_steelZ[1] = {0};
|
||||
u32 Pdepot_gate_entryZ[1] = {0};
|
||||
u32 Pdesk1Z[1] = {0};
|
||||
u32 Pdesk2Z[1] = {0};
|
||||
u32 Pdesk_arecibo1Z[1] = {0};
|
||||
u32 Pdesk_lamp2Z[1] = {0};
|
||||
u32 Pdest_engineZ[1] = {0};
|
||||
u32 Pdest_exocetZ[1] = {0};
|
||||
u32 Pdest_gunZ[1] = {0};
|
||||
u32 Pdest_harpoonZ[1] = {0};
|
||||
u32 Pdest_seawolfZ[1] = {0};
|
||||
u32 Pdisc_readerZ[1] = {0};
|
||||
u32 Pdisk_drive1Z[1] = {0};
|
||||
u32 Pdoor_azt_chairZ[1] = {0};
|
||||
u32 Pdoor_azt_deskZ[1] = {0};
|
||||
u32 Pdoor_azt_desk_topZ[1] = {0};
|
||||
u32 Pdoor_aztecZ[1] = {0};
|
||||
u32 Pdoor_dest1Z[1] = {0};
|
||||
u32 Pdoor_dest2Z[1] = {0};
|
||||
u32 Pdoor_eyelidZ[1] = {0};
|
||||
u32 Pdoor_irisZ[1] = {0};
|
||||
u32 Pdoor_mfZ[1] = {0};
|
||||
u32 Pdoor_roller1Z[1] = {0};
|
||||
u32 Pdoor_roller2Z[1] = {0};
|
||||
u32 Pdoor_roller3Z[1] = {0};
|
||||
u32 Pdoor_roller4Z[1] = {0};
|
||||
u32 Pdoor_rollertrainZ[1] = {0};
|
||||
u32 Pdoor_st_arec1Z[1] = {0};
|
||||
u32 Pdoor_st_arec2Z[1] = {0};
|
||||
u32 Pdoor_winZ[1] = {0};
|
||||
u32 PdoorconsoleZ[1] = {0};
|
||||
u32 PdoorpanelZ[1] = {0};
|
||||
u32 Pdoorprison1Z[1] = {0};
|
||||
u32 PdoorstatgateZ[1] = {0};
|
||||
u32 PexplosionbitZ[1] = {0};
|
||||
u32 Pfiling_cabinet1Z[1] = {0};
|
||||
u32 PflagZ[1] = {0};
|
||||
u32 PfloppyZ[1] = {0};
|
||||
u32 Pfnp90magZ[1] = {0};
|
||||
u32 Pgas_plant_met1_do1Z[1] = {0};
|
||||
u32 Pgas_plant_sw2_do1Z[1] = {0};
|
||||
u32 Pgas_plant_sw3_do1Z[1] = {0};
|
||||
u32 Pgas_plant_sw4_do1Z[1] = {0};
|
||||
u32 Pgas_plant_sw_do1Z[1] = {0};
|
||||
u32 Pgas_plant_wc_cub1Z[1] = {0};
|
||||
u32 PgasbarrelZ[1] = {0};
|
||||
u32 PgasbarrelsZ[1] = {0};
|
||||
u32 Pgasplant_clear_doorZ[1] = {0};
|
||||
u32 PgastankZ[1] = {0};
|
||||
u32 Pglassware1Z[1] = {0};
|
||||
u32 Pglassware2Z[1] = {0};
|
||||
u32 Pglassware3Z[1] = {0};
|
||||
u32 Pglassware4Z[1] = {0};
|
||||
u32 PgoldeneyelogoZ[1] = {0};
|
||||
u32 PgoldenshellsZ[1] = {0};
|
||||
u32 PgroundgunZ[1] = {0};
|
||||
u32 Pgun_runway1Z[1] = {0};
|
||||
u32 PhatberetZ[1] = {0};
|
||||
u32 PhatberetblueZ[1] = {0};
|
||||
u32 PhatberetredZ[1] = {0};
|
||||
u32 PhatchboltZ[1] = {0};
|
||||
u32 PhatchdoorZ[1] = {0};
|
||||
u32 PhatchsevxZ[1] = {0};
|
||||
u32 PhatfurryZ[1] = {0};
|
||||
u32 PhatfurryblackZ[1] = {0};
|
||||
u32 PhatfurrybrownZ[1] = {0};
|
||||
u32 PhathelmetZ[1] = {0};
|
||||
u32 PhathelmetgreyZ[1] = {0};
|
||||
u32 PhatmoonZ[1] = {0};
|
||||
u32 PhatpeakedZ[1] = {0};
|
||||
u32 PhattbirdZ[1] = {0};
|
||||
u32 PhattbirdbrownZ[1] = {0};
|
||||
u32 PhelicopterZ[1] = {0};
|
||||
u32 PhindZ[1] = {0};
|
||||
u32 PjeepZ[1] = {0};
|
||||
u32 Pjerry_can1Z[1] = {0};
|
||||
u32 Pjungle3_treeZ[1] = {0};
|
||||
u32 Pjungle5_treeZ[1] = {0};
|
||||
u32 Pkey_holderZ[1] = {0};
|
||||
u32 Pkeyboard1Z[1] = {0};
|
||||
u32 Pkit_units1Z[1] = {0};
|
||||
u32 PlabbenchZ[1] = {0};
|
||||
u32 PlandmineZ[1] = {0};
|
||||
u32 PlegalpageZ[1] = {0};
|
||||
u32 Pletter_tray1Z[1] = {0};
|
||||
u32 Plocker3Z[1] = {0};
|
||||
u32 Plocker4Z[1] = {0};
|
||||
u32 Pm16magZ[1] = {0};
|
||||
u32 PmagnumshellsZ[1] = {0};
|
||||
u32 Pmainframe1Z[1] = {0};
|
||||
u32 Pmainframe2Z[1] = {0};
|
||||
u32 Pmetal_chair1Z[1] = {0};
|
||||
u32 Pmetal_crate1Z[1] = {0};
|
||||
u32 Pmetal_crate2Z[1] = {0};
|
||||
u32 Pmetal_crate3Z[1] = {0};
|
||||
u32 Pmetal_crate4Z[1] = {0};
|
||||
u32 PmilcopterZ[1] = {0};
|
||||
u32 PmiltruckZ[1] = {0};
|
||||
u32 Pmissile_rack2Z[1] = {0};
|
||||
u32 Pmissile_rackZ[1] = {0};
|
||||
u32 PmodemboxZ[1] = {0};
|
||||
u32 PmotorbikeZ[1] = {0};
|
||||
u32 Pmp5kmagZ[1] = {0};
|
||||
u32 PnintendologoZ[1] = {0};
|
||||
u32 Poil_drum1Z[1] = {0};
|
||||
u32 Poil_drum2Z[1] = {0};
|
||||
u32 Poil_drum3Z[1] = {0};
|
||||
u32 Poil_drum5Z[1] = {0};
|
||||
u32 Poil_drum6Z[1] = {0};
|
||||
u32 Poil_drum7Z[1] = {0};
|
||||
u32 PpadlockZ[1] = {0};
|
||||
u32 PpalmZ[1] = {0};
|
||||
u32 PpalmtreeZ[1] = {0};
|
||||
u32 Pphone1Z[1] = {0};
|
||||
u32 PplaneZ[1] = {0};
|
||||
u32 Pplant11Z[1] = {0};
|
||||
u32 Pplant1Z[1] = {0};
|
||||
u32 Pplant2Z[1] = {0};
|
||||
u32 Pplant2bZ[1] = {0};
|
||||
u32 Pplant3Z[1] = {0};
|
||||
u32 Pradio_unit1Z[1] = {0};
|
||||
u32 Pradio_unit2Z[1] = {0};
|
||||
u32 Pradio_unit3Z[1] = {0};
|
||||
u32 Pradio_unit4Z[1] = {0};
|
||||
u32 ProofgunZ[1] = {0};
|
||||
u32 PsafeZ[1] = {0};
|
||||
u32 PsafedoorZ[1] = {0};
|
||||
u32 Psat1_reflectZ[1] = {0};
|
||||
u32 PsatboxZ[1] = {0};
|
||||
u32 PsatdishZ[1] = {0};
|
||||
u32 Psec_panelZ[1] = {0};
|
||||
u32 Psev_door3Z[1] = {0};
|
||||
u32 Psev_door3_windZ[1] = {0};
|
||||
u32 Psev_door4_windZ[1] = {0};
|
||||
u32 Psev_doorZ[1] = {0};
|
||||
u32 Psev_door_v1Z[1] = {0};
|
||||
u32 Psev_trislideZ[1] = {0};
|
||||
u32 PsevdishZ[1] = {0};
|
||||
u32 PsevdoormetslideZ[1] = {0};
|
||||
u32 PsevdoornowindZ[1] = {0};
|
||||
u32 PsevdoorwindZ[1] = {0};
|
||||
u32 PsevdoorwoodZ[1] = {0};
|
||||
u32 PshuttleZ[1] = {0};
|
||||
u32 Pshuttle_door_lZ[1] = {0};
|
||||
u32 Pshuttle_door_rZ[1] = {0};
|
||||
u32 PsilencerZ[1] = {0};
|
||||
u32 Psilo_lift_doorZ[1] = {0};
|
||||
u32 PsilotopdoorZ[1] = {0};
|
||||
u32 PskorpionmagZ[1] = {0};
|
||||
u32 PspectremagZ[1] = {0};
|
||||
u32 PspeedboatZ[1] = {0};
|
||||
u32 Pst_pete_room_1iZ[1] = {0};
|
||||
u32 Pst_pete_room_2iZ[1] = {0};
|
||||
u32 Pst_pete_room_3tZ[1] = {0};
|
||||
u32 Pst_pete_room_5cZ[1] = {0};
|
||||
u32 Pst_pete_room_6cZ[1] = {0};
|
||||
u32 Psteel_door1Z[1] = {0};
|
||||
u32 Psteel_door2Z[1] = {0};
|
||||
u32 Psteel_door2bZ[1] = {0};
|
||||
u32 Psteel_door3Z[1] = {0};
|
||||
u32 Pstool1Z[1] = {0};
|
||||
u32 Pswipe_card2Z[1] = {0};
|
||||
u32 Pswivel_chair1Z[1] = {0};
|
||||
u32 PtankZ[1] = {0};
|
||||
u32 PtigerZ[1] = {0};
|
||||
u32 Ptorpedo_rackZ[1] = {0};
|
||||
u32 Ptrain_door2Z[1] = {0};
|
||||
u32 Ptrain_door3Z[1] = {0};
|
||||
u32 Ptrain_doorZ[1] = {0};
|
||||
u32 PtrainextdoorZ[1] = {0};
|
||||
u32 Ptt33magZ[1] = {0};
|
||||
u32 Ptuning_console1Z[1] = {0};
|
||||
u32 Ptv1Z[1] = {0};
|
||||
u32 Ptv4screenZ[1] = {0};
|
||||
u32 Ptv_holderZ[1] = {0};
|
||||
u32 PtvscreenZ[1] = {0};
|
||||
u32 PuzimagZ[1] = {0};
|
||||
u32 PvertdoorZ[1] = {0};
|
||||
u32 PwalletbondZ[1] = {0};
|
||||
u32 PwindowZ[1] = {0};
|
||||
u32 Pwindow_cor11Z[1] = {0};
|
||||
u32 Pwindow_lib_lg1Z[1] = {0};
|
||||
u32 Pwindow_lib_sm1Z[1] = {0};
|
||||
u32 Pwood_lg_crate1Z[1] = {0};
|
||||
u32 Pwood_lg_crate2Z[1] = {0};
|
||||
u32 Pwood_md_crate3Z[1] = {0};
|
||||
u32 Pwood_sm_crate4Z[1] = {0};
|
||||
u32 Pwood_sm_crate5Z[1] = {0};
|
||||
u32 Pwood_sm_crate6Z[1] = {0};
|
||||
u32 Pwooden_table1Z[1] = {0};
|
||||
u32 PwppkmagZ[1] = {0};
|
||||
|
||||
/* --- Background texture sets (Tbg_*Z) -------------------------------- */
|
||||
u32 Tbg_ame_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_arch_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_arec_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_ark_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_ash_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_azt_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_cat_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_cave_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_crad_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_cryp_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_dam_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_depo_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_dest_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_dish_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_imp_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_jun_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_len_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_oat_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_pete_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_ref_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_run_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_sev_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_sevb_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_sevx_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_silo_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_stat_all_p_stanZ[1] = {0};
|
||||
u32 Tbg_tra_all_p_stanZ[1] = {0};
|
||||
|
||||
/* --- Stage / MP setup data (U*Z, Ump_*Z, Ubrief*Z) ------------------ */
|
||||
u32 UbriefarchZ[1] = {0};
|
||||
u32 UbriefarkZ[1] = {0};
|
||||
u32 UbriefaztZ[1] = {0};
|
||||
u32 UbriefcaveZ[1] = {0};
|
||||
u32 UbriefcontrolZ[1] = {0};
|
||||
u32 UbriefcradZ[1] = {0};
|
||||
u32 UbriefcrypZ[1] = {0};
|
||||
u32 UbriefdamZ[1] = {0};
|
||||
u32 UbriefdepoZ[1] = {0};
|
||||
u32 UbriefdestZ[1] = {0};
|
||||
u32 UbriefjunZ[1] = {0};
|
||||
u32 UbriefpeteZ[1] = {0};
|
||||
u32 UbriefrunZ[1] = {0};
|
||||
u32 UbriefsevbZ[1] = {0};
|
||||
u32 UbriefsevbunkerZ[1] = {0};
|
||||
u32 UbriefsevxZ[1] = {0};
|
||||
u32 UbriefsevxbZ[1] = {0};
|
||||
u32 UbriefsiloZ[1] = {0};
|
||||
u32 UbriefstatueZ[1] = {0};
|
||||
u32 UbrieftraZ[1] = {0};
|
||||
u32 Ump_setupameZ[1] = {0};
|
||||
u32 Ump_setuparchZ[1] = {0};
|
||||
u32 Ump_setuparkZ[1] = {0};
|
||||
u32 Ump_setupashZ[1] = {0};
|
||||
u32 Ump_setupcaveZ[1] = {0};
|
||||
u32 Ump_setupcradZ[1] = {0};
|
||||
u32 Ump_setupcrypZ[1] = {0};
|
||||
u32 Ump_setupdishZ[1] = {0};
|
||||
u32 Ump_setupimpZ[1] = {0};
|
||||
u32 Ump_setupoatZ[1] = {0};
|
||||
u32 Ump_setuprefZ[1] = {0};
|
||||
u32 Ump_setupsevbZ[1] = {0};
|
||||
u32 Ump_setupstatueZ[1] = {0};
|
||||
u32 UsetuparchZ[1] = {0};
|
||||
u32 UsetuparkZ[1] = {0};
|
||||
u32 UsetupaztZ[1] = {0};
|
||||
u32 UsetupcaveZ[1] = {0};
|
||||
u32 UsetupcontrolZ[1] = {0};
|
||||
u32 UsetupcradZ[1] = {0};
|
||||
u32 UsetupcrypZ[1] = {0};
|
||||
u32 UsetupdamZ[1] = {0};
|
||||
u32 UsetupdepoZ[1] = {0};
|
||||
u32 UsetupdestZ[1] = {0};
|
||||
u32 UsetupjunZ[1] = {0};
|
||||
u32 UsetuplenZ[1] = {0};
|
||||
u32 UsetuppeteZ[1] = {0};
|
||||
u32 UsetuprunZ[1] = {0};
|
||||
u32 UsetupsevbZ[1] = {0};
|
||||
u32 UsetupsevbunkerZ[1] = {0};
|
||||
u32 UsetupsevxZ[1] = {0};
|
||||
u32 UsetupsevxbZ[1] = {0};
|
||||
u32 UsetupsiloZ[1] = {0};
|
||||
u32 UsetupstatueZ[1] = {0};
|
||||
u32 UsetuptraZ[1] = {0};
|
||||
|
||||
/* --- Text tables (L*E / L*J) -------------------------------------------- */
|
||||
u32 LameE[1] = {0};
|
||||
u32 LameJ[1] = {0};
|
||||
u32 LameP[1] = {0};
|
||||
u32 LarchE[1] = {0};
|
||||
u32 LarchJ[1] = {0};
|
||||
u32 LarchP[1] = {0};
|
||||
u32 LarecE[1] = {0};
|
||||
u32 LarecJ[1] = {0};
|
||||
u32 LarecP[1] = {0};
|
||||
u32 LarkE[1] = {0};
|
||||
u32 LarkJ[1] = {0};
|
||||
u32 LarkP[1] = {0};
|
||||
u32 LashE[1] = {0};
|
||||
u32 LashJ[1] = {0};
|
||||
u32 LashP[1] = {0};
|
||||
u32 LaztE[1] = {0};
|
||||
u32 LaztJ[1] = {0};
|
||||
u32 LaztP[1] = {0};
|
||||
u32 LcatE[1] = {0};
|
||||
u32 LcatJ[1] = {0};
|
||||
u32 LcatP[1] = {0};
|
||||
u32 LcaveE[1] = {0};
|
||||
u32 LcaveJ[1] = {0};
|
||||
u32 LcaveP[1] = {0};
|
||||
u32 LcradE[1] = {0};
|
||||
u32 LcradJ[1] = {0};
|
||||
u32 LcradP[1] = {0};
|
||||
u32 LcrypE[1] = {0};
|
||||
u32 LcrypJ[1] = {0};
|
||||
u32 LcrypP[1] = {0};
|
||||
u32 LdamE[1] = {0};
|
||||
u32 LdamJ[1] = {0};
|
||||
u32 LdamP[1] = {0};
|
||||
u32 LdepoE[1] = {0};
|
||||
u32 LdepoJ[1] = {0};
|
||||
u32 LdepoP[1] = {0};
|
||||
u32 LdestE[1] = {0};
|
||||
u32 LdestJ[1] = {0};
|
||||
u32 LdestP[1] = {0};
|
||||
u32 LdishE[1] = {0};
|
||||
u32 LdishJ[1] = {0};
|
||||
u32 LdishP[1] = {0};
|
||||
u32 LearE[1] = {0};
|
||||
u32 LearJ[1] = {0};
|
||||
u32 LearP[1] = {0};
|
||||
u32 LeldE[1] = {0};
|
||||
u32 LeldJ[1] = {0};
|
||||
u32 LeldP[1] = {0};
|
||||
u32 LgunE[1] = {0};
|
||||
u32 LgunJ[1] = {0};
|
||||
u32 LgunP[1] = {0};
|
||||
u32 LimpE[1] = {0};
|
||||
u32 LimpJ[1] = {0};
|
||||
u32 LimpP[1] = {0};
|
||||
u32 LjunE[1] = {0};
|
||||
u32 LjunJ[1] = {0};
|
||||
u32 LjunP[1] = {0};
|
||||
u32 LleeE[1] = {0};
|
||||
u32 LleeJ[1] = {0};
|
||||
u32 LleeP[1] = {0};
|
||||
u32 LlenE[1] = {0};
|
||||
u32 LlenJ[1] = {0};
|
||||
u32 LlenP[1] = {0};
|
||||
u32 LlipE[1] = {0};
|
||||
u32 LlipJ[1] = {0};
|
||||
u32 LlipP[1] = {0};
|
||||
u32 LlueE[1] = {0};
|
||||
u32 LlueJ[1] = {0};
|
||||
u32 LlueP[1] = {0};
|
||||
u32 LmiscE[1] = {0};
|
||||
u32 LmiscJ[1] = {0};
|
||||
u32 LmiscP[1] = {0};
|
||||
u32 LmpmenuE[1] = {0};
|
||||
u32 LmpmenuJ[1] = {0};
|
||||
u32 LmpmenuP[1] = {0};
|
||||
u32 LmpweaponsE[1] = {0};
|
||||
u32 LmpweaponsJ[1] = {0};
|
||||
u32 LmpweaponsP[1] = {0};
|
||||
u32 LoatE[1] = {0};
|
||||
u32 LoatJ[1] = {0};
|
||||
u32 LoatP[1] = {0};
|
||||
u32 LoptionsE[1] = {0};
|
||||
u32 LoptionsJ[1] = {0};
|
||||
u32 LoptionsP[1] = {0};
|
||||
u32 LpamE[1] = {0};
|
||||
u32 LpamJ[1] = {0};
|
||||
u32 LpamP[1] = {0};
|
||||
u32 LpeteE[1] = {0};
|
||||
u32 LpeteJ[1] = {0};
|
||||
u32 LpeteP[1] = {0};
|
||||
u32 LpropobjE[1] = {0};
|
||||
u32 LpropobjJ[1] = {0};
|
||||
u32 LpropobjP[1] = {0};
|
||||
u32 LrefE[1] = {0};
|
||||
u32 LrefJ[1] = {0};
|
||||
u32 LrefP[1] = {0};
|
||||
u32 LritE[1] = {0};
|
||||
u32 LritJ[1] = {0};
|
||||
u32 LritP[1] = {0};
|
||||
u32 LrunE[1] = {0};
|
||||
u32 LrunJ[1] = {0};
|
||||
u32 LrunP[1] = {0};
|
||||
u32 LsevE[1] = {0};
|
||||
u32 LsevJ[1] = {0};
|
||||
u32 LsevP[1] = {0};
|
||||
u32 LsevbE[1] = {0};
|
||||
u32 LsevbJ[1] = {0};
|
||||
u32 LsevbP[1] = {0};
|
||||
u32 LsevxE[1] = {0};
|
||||
u32 LsevxJ[1] = {0};
|
||||
u32 LsevxP[1] = {0};
|
||||
u32 LsevxbE[1] = {0};
|
||||
u32 LsevxbJ[1] = {0};
|
||||
u32 LsevxbP[1] = {0};
|
||||
u32 LshoE[1] = {0};
|
||||
u32 LshoJ[1] = {0};
|
||||
u32 LshoP[1] = {0};
|
||||
u32 LsiloE[1] = {0};
|
||||
u32 LsiloJ[1] = {0};
|
||||
u32 LsiloP[1] = {0};
|
||||
u32 LstatE[1] = {0};
|
||||
u32 LstatJ[1] = {0};
|
||||
u32 LstatP[1] = {0};
|
||||
u32 LtitleE[1] = {0};
|
||||
u32 LtitleJ[1] = {0};
|
||||
u32 LtitleP[1] = {0};
|
||||
u32 LtraE[1] = {0};
|
||||
u32 LtraJ[1] = {0};
|
||||
u32 LtraP[1] = {0};
|
||||
u32 LwaxE[1] = {0};
|
||||
u32 LwaxJ[1] = {0};
|
||||
u32 LwaxP[1] = {0};
|
||||
|
||||
/* --- Background level data segment bases (bg_*_all_p_seg) ------------ */
|
||||
u32 bg_arch_all_p_seg[1] = {0};
|
||||
u32 bg_arec_all_p_seg[1] = {0};
|
||||
u32 bg_ark_all_p_seg[1] = {0};
|
||||
u32 bg_azt_all_p_seg[1] = {0};
|
||||
u32 bg_cat_all_p_seg[1] = {0};
|
||||
u32 bg_cave_all_p_seg[1] = {0};
|
||||
u32 bg_crad_all_p_seg[1] = {0};
|
||||
u32 bg_cryp_all_p_seg[1] = {0};
|
||||
u32 bg_dam_all_p_seg[1] = {0};
|
||||
u32 bg_depo_all_p_seg[1] = {0};
|
||||
u32 bg_dest_all_p_seg[1] = {0};
|
||||
u32 bg_dish_all_p_seg[1] = {0};
|
||||
u32 bg_ear_all_p_seg[1] = {0};
|
||||
u32 bg_imp_all_p_seg[1] = {0};
|
||||
u32 bg_jun_all_p_seg[1] = {0};
|
||||
u32 bg_lee_all_p_seg[1] = {0};
|
||||
u32 bg_len_all_p_seg[1] = {0};
|
||||
u32 bg_lip_all_p_seg[1] = {0};
|
||||
u32 bg_oat_all_p_seg[1] = {0};
|
||||
u32 bg_pam_all_p_seg[1] = {0};
|
||||
u32 bg_pete_all_p_seg[1] = {0};
|
||||
u32 bg_ref_all_p_seg[1] = {0};
|
||||
u32 bg_rit_all_p_seg[1] = {0};
|
||||
u32 bg_run_all_p_seg[1] = {0};
|
||||
u32 bg_sev_all_p_seg[1] = {0};
|
||||
u32 bg_sevb_all_p_seg[1] = {0};
|
||||
u32 bg_sevx_all_p_seg[1] = {0};
|
||||
u32 bg_sho_all_p_seg[1] = {0};
|
||||
u32 bg_silo_all_p_seg[1] = {0};
|
||||
u32 bg_stat_all_p_seg[1] = {0};
|
||||
u32 bg_tra_all_p_seg[1] = {0};
|
||||
u32 bg_wax_all_p_seg[1] = {0};
|
||||
|
||||
/* --- Other --------------------------------------------------------------- */
|
||||
u32 ob__ob_end_seg[1] = {0};
|
||||
|
||||
/* --- RAM-rom demo replays (assets/ramrom/ramrom.s, .incbin) -------------- */
|
||||
/* ramromreplay.c declares these `extern u32 *` and stores &symbol in its */
|
||||
/* table; NULL pointers link and keep the entries inert. */
|
||||
u32 *ramrom_BunkerI_1 = NULL;
|
||||
u32 *ramrom_BunkerI_2 = NULL;
|
||||
u32 *ramrom_Dam_1 = NULL;
|
||||
u32 *ramrom_Dam_2 = NULL;
|
||||
u32 *ramrom_Facility_1 = NULL;
|
||||
u32 *ramrom_Facility_2 = NULL;
|
||||
u32 *ramrom_Facility_3 = NULL;
|
||||
u32 *ramrom_Frigate_1 = NULL;
|
||||
u32 *ramrom_Frigate_2 = NULL;
|
||||
u32 *ramrom_Runway_1 = NULL;
|
||||
u32 *ramrom_Runway_2 = NULL;
|
||||
u32 *ramrom_Silo_1 = NULL;
|
||||
u32 *ramrom_Silo_2 = NULL;
|
||||
u32 *ramrom_Train = NULL;
|
||||
|
||||
/* --- assets/romfiles2.s (.incbin blob) ----------------------------------- */
|
||||
/* title.c romCopies (unknown2_end - unknown2) bytes; as a pointer pair that */
|
||||
/* is zero, so the copy is a no-op. */
|
||||
void *unknown2 = NULL;
|
||||
void *unknown2_end = NULL;
|
||||
+34
-7
@@ -202,20 +202,47 @@ void osAiSetConvert(u32 convert) { (void)convert; }
|
||||
/* PI (peripheral) — ROM/cart reads come from the loaded ROM image. */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* PI DMA is synchronous on the PC: romdata.c maps the .z64 at the N64 cart
|
||||
* base (0x10000000), so an OS_READ from a cart address is a plain memcpy and
|
||||
* the "completion" is already delivered by the time we return. The game's
|
||||
* romReceiveMesg()/osRecvMesg() after every romCopy() is therefore satisfied
|
||||
* trivially (see the message-queue shims below).
|
||||
*/
|
||||
static void piServiceDma(s32 direction, u32 srcPA, void *dstVA, u32 size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
if (direction == OS_READ) {
|
||||
if (!romdataCartAddrValid(srcPA, size)) {
|
||||
sysLogPrintf(LOG_WARNING,
|
||||
"osPiStartDma: ROM read out of range "
|
||||
"(src=0x%08X size=0x%X); skipped",
|
||||
srcPA, size);
|
||||
return;
|
||||
}
|
||||
memcpy(dstVA, (const void *)(uintptr_t)srcPA, size);
|
||||
} else {
|
||||
/* OS_WRITE: the game never writes the cart (saves go to EEPROM via
|
||||
* osEeprom*, shimmed separately). Log once-style and drop. */
|
||||
sysLogPrintf(LOG_WARNING,
|
||||
"osPiStartDma: cart write dropped (src=0x%08X size=0x%X)",
|
||||
srcPA, size);
|
||||
}
|
||||
}
|
||||
|
||||
void osPiCreateManager(OSMesgQueue *mq, int prio) { (void)mq; (void)prio; }
|
||||
s32 osPiStartDma(OSIoMesg *mesg, s32 prio, s32 direction, u32 addr,
|
||||
void *buf, u32 size, OSMesgQueue *mq)
|
||||
{
|
||||
/* TODO(Phase 1): service a read from romdata (cart -> host memory). */
|
||||
(void)mesg; (void)prio; (void)direction; (void)addr; (void)buf;
|
||||
(void)size; (void)mq;
|
||||
return 1;
|
||||
piServiceDma(direction, addr, buf, size);
|
||||
(void)mesg; (void)prio; (void)mq;
|
||||
return 1; /* done */
|
||||
}
|
||||
s32 osPiRawStartDma(s32 direction, u32 srcPA, void *dstVA, u32 size)
|
||||
{
|
||||
/* TODO(Phase 1): copy between ROM image and host memory. */
|
||||
(void)direction; (void)srcPA; (void)dstVA; (void)size;
|
||||
return 1;
|
||||
piServiceDma(direction, srcPA, dstVA, size);
|
||||
return 1; /* done */
|
||||
}
|
||||
u32 osPiGetStatus(void) { return 0; } /* not busy */
|
||||
|
||||
|
||||
+23
-11
@@ -4,13 +4,16 @@
|
||||
* Replaces the N64 boot path (boot.s -> init() -> mainproc() -> bossEntry()).
|
||||
* On the PC we:
|
||||
* 1. set up system / config / fs / rom
|
||||
* 2. load the ROM and map its segments
|
||||
* 2. load the ROM and map it at the cart base (0x10000000)
|
||||
* 3. init video (SDL2 + GL), audio, input
|
||||
* 4. hand control to the game's mainproc() / bossEntry()
|
||||
*
|
||||
* Modelled on the PD port's port/src/main.c.
|
||||
*
|
||||
* STATUS: scaffolding stub — fill in during Phase 1.
|
||||
* STATUS: Phase 1 — ROM loads and maps, a window opens and clears each frame.
|
||||
* Step 4 (mainproc) is deferred until the software RSP (fast3d) and the
|
||||
* scheduler can service the game's rendering/audio threads; see
|
||||
* docs/PCPortResearch.md.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -58,18 +61,27 @@ int main(int argc, char **argv)
|
||||
mixerInit();
|
||||
inputInit();
|
||||
|
||||
/* 4. Hand control to the game.
|
||||
/* 4. Phase 1 demo loop: clear colour until the RSP/scheduler can drive
|
||||
* real frames (Phase 2). Press ESC or close the window to quit.
|
||||
*
|
||||
* mainproc() is defined in src/init.c, which IS compiled for the PC (see
|
||||
* A3 in docs/PCPortResearch.md). init.c's N64-only init() is compiled but
|
||||
* NOT called — its hardware deps (segment starts, decompress, TLB, FPU
|
||||
* CSR) are stubbed in port/src/n64stubs.c, and the libultra OS calls are
|
||||
* shimmed in port/src/libultra.c. mainproc() itself just sets up the
|
||||
* threads and calls schedulerInitThread() + bossEntry().
|
||||
* When fast3d lands, replace this with mainproc(NULL) — defined in
|
||||
* src/init.c (compiled for the PC; see A3 in docs/PCPortResearch.md).
|
||||
* init.c's N64-only init() is never called; its hardware deps are
|
||||
* stubbed in port/src/n64stubs.c and the libultra OS calls are shimmed
|
||||
* in port/src/libultra.c.
|
||||
*/
|
||||
mainproc(NULL);
|
||||
sysLogPrintf(LOG_INFO, "Phase 1: ROM mapped at 0x%08X (%u bytes); "
|
||||
"demo loop running — ESC to quit",
|
||||
(unsigned)0x10000000, romdataGetRomSize());
|
||||
for (;;) {
|
||||
if (videoHandleEvents())
|
||||
break;
|
||||
videoStartFrame();
|
||||
videoSubmitCommands(NULL); /* no-op until fast3d lands */
|
||||
videoEndFrame();
|
||||
}
|
||||
|
||||
/* mainproc() normally does not return; clean up if it does. */
|
||||
/* Clean up. */
|
||||
inputDestroy();
|
||||
mixerDestroy();
|
||||
audioDestroy();
|
||||
|
||||
+11
-55
@@ -33,69 +33,25 @@ u32 get_inflateSegmentRomStart(void) { return 0; }
|
||||
u32 get_inflateSegmentRomEnd(void) { return 0; }
|
||||
|
||||
/*
|
||||
* Linker segment boundary pointers (normally linker-script symbols, declared
|
||||
* `extern u32 *` in bondgame.h and assorted game headers). Game code takes
|
||||
* their ADDRESSES for size calcs + PI DMA / romCopy; on the PC the values are
|
||||
* meaningless until Phase 2 ROM loading remaps them (romdata.c). All defined
|
||||
* NULL: address-of gives a valid host pointer, and pairs of NULLs compute to
|
||||
* zero-length copies. Complete set = every `*Segment*` symbol referenced by
|
||||
* the compiled game sources.
|
||||
* Linker segment boundary symbols.
|
||||
*
|
||||
* The ROM-backed ones (every _XSegment{Start,RomStart,RomEnd,End} from
|
||||
* ge007.ld, plus all obseg/ramrom/music asset labels) are now ABSOLUTE cart
|
||||
* addresses in the generated port/src/romassets_u.s — see
|
||||
* scripts/gen_romassets.py. romdata.c maps the .z64 at 0x10000000 so `&sym`
|
||||
* yields a live address and romCopy() works.
|
||||
*
|
||||
* What remains here are pure-RAM segment symbols that have no ROM presence:
|
||||
* the host's own .bss/.csegment ends and the N64 vaddr markers (only used by
|
||||
* the never-run N64 boot path on the PC).
|
||||
*/
|
||||
u32 *_bssSegmentEnd = NULL;
|
||||
|
||||
u32 *_codeSegmentStart = NULL;
|
||||
u32 *_codeSegmentEnd = NULL;
|
||||
u32 *_codeSegmentRomStart = NULL;
|
||||
u32 *_codeSegmentRomEnd = NULL;
|
||||
|
||||
u32 *_csegmentSegmentStart = NULL;
|
||||
u32 *_csegmentSegmentEnd = NULL;
|
||||
u32 *_cdataSegmentRomStart = NULL;
|
||||
u32 *_cdataSegmentRomEnd = NULL;
|
||||
|
||||
u32 *_inflateSegmentVaddrStart = NULL;
|
||||
u32 *_inflateSegmentVaddrEnd = NULL;
|
||||
u32 *_inflateSegmentRomStart = NULL;
|
||||
u32 *_inflateSegmentRomEnd = NULL;
|
||||
|
||||
u32 *_gameSegmentVaddrStart = NULL;
|
||||
u32 *_gameSegmentVaddrEnd = NULL;
|
||||
u32 *_gameSegmentRomStart = NULL;
|
||||
u32 *_gameSegmentRomEnd = NULL;
|
||||
|
||||
u32 *_animation_dataSegmentRomStart = NULL;
|
||||
u32 *_animation_dataSegmentStart = NULL;
|
||||
u32 *_animation_dataSegmentEnd = NULL;
|
||||
u32 *_animation_entriesSegmentRomStart = NULL;
|
||||
|
||||
u32 *_alt_startSegmentRomStart = NULL;
|
||||
u32 *_alt_startSegmentStart = NULL;
|
||||
|
||||
/* Font / image / music segment bases (referenced by textrelated.c,
|
||||
* image_bank.c, music.c — data for the font/image ones is compiled from
|
||||
* assets/; the ROM-base markers themselves come from ge007.ld on N64). */
|
||||
u32 *_efontchardataSegmentRomStart = NULL;
|
||||
u32 *_jfontchardataSegmentRomStart = NULL;
|
||||
u32 *_fontbankgothicSegmentStart = NULL;
|
||||
u32 *_fontbankgothicSegmentEnd = NULL;
|
||||
u32 *_fontbankgothicSegmentRomStart = NULL;
|
||||
u32 *_fontzurichboldSegmentStart = NULL;
|
||||
u32 *_fontzurichboldSegmentEnd = NULL;
|
||||
u32 *_fontzurichboldSegmentRomStart = NULL;
|
||||
u32 *_fontdlSegmentRomStart = NULL;
|
||||
u32 *_fontdlSegmentRomEnd = NULL;
|
||||
u32 *_imagesSegmentRomStart = NULL;
|
||||
u32 *_GlobalimagetableSegmentStart = NULL;
|
||||
u32 *_GlobalimagetableSegmentEnd = NULL;
|
||||
u32 *_GlobalimagetableSegmentRomStart = NULL;
|
||||
u32 *_instrumentsctlSegmentRomStart = NULL;
|
||||
u32 *_instrumentstblSegmentRomStart = NULL;
|
||||
u32 *_musicsampletblSegmentRomStart = NULL;
|
||||
u32 *_sfxctlSegmentRomStart = NULL;
|
||||
u32 *_sfxtblSegmentRomStart = NULL;
|
||||
u32 *_rarewarelogoSegmentStart = NULL;
|
||||
u32 *_rarewarelogoSegmentEnd = NULL;
|
||||
u32 *_rarewarelogoSegmentRomStart = NULL;
|
||||
|
||||
/* --- Decompressor (src/inflate, not built for PC) ----------------------- */
|
||||
/* init() calls this to unpack the data segment. Unused on the PC. */
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+152
-44
@@ -1,89 +1,197 @@
|
||||
/*
|
||||
* ROM loading: reads the .z64 ROM from the data dir and exposes its segments.
|
||||
* ROM loading: reads the .z64 and maps it at the N64 cart address 0x10000000.
|
||||
*
|
||||
* The N64 build links assets in from the ROM at fixed addresses. The PC port
|
||||
* loads the ROM file into a heap image and maps the game's data segments into
|
||||
* it, then patches the segment table so asset references resolve.
|
||||
* Why the fixed address: every ROM asset symbol in the PC build is an ABSOLUTE
|
||||
* cart address (port/src/romassets_<region>.s, generated by
|
||||
* scripts/gen_romassets.py from ge007.ld + scripts/filelist.<r>.csv). Game
|
||||
* code takes `&symbol` and DMAs via romCopy()/osPiStartDma() exactly as on
|
||||
* hardware. If the ROM image is mapped at 0x10000000, those addresses are
|
||||
* live host pointers: PI reads become plain memcpys (see libultra.c) and
|
||||
* direct-read assets (fonts, image display lists) work unmodified.
|
||||
*
|
||||
* Modelled on the PD port's port/src/romdata.c (~605 lines).
|
||||
* The N64 ROM header is GE-specific (src/rom_header.s): magic 0x80371240 at
|
||||
* 0x00, ROM name "GOLDENEYE" at 0x20, cartridge ID "GE" at 0x3C, country byte
|
||||
* at 0x3E ('E' US / 'P' EU / 'J' JP).
|
||||
*
|
||||
* STATUS: scaffolding stub — implement during Phase 1.
|
||||
*
|
||||
* NOTE: GE's ROM layout (segments, asset offsets) differs from PD's. The
|
||||
* segment table and asset map must be derived from this decomp's
|
||||
* ge007.ld / imagelist.u.csv / assets/ layout. See docs/PCPortResearch.md.
|
||||
* STATUS: Phase 1 — load + validate + map. No segment patching needed: the
|
||||
* generated symbols already carry the layout.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "system.h"
|
||||
#include "fs.h"
|
||||
#include "romdata.h"
|
||||
|
||||
static u8 *rom = NULL;
|
||||
static u32 romSize = 0;
|
||||
#define CART_BASE 0x10000000u
|
||||
|
||||
/* Expected ROM file names per region (place one in the data/ dir). */
|
||||
static const char *romFileName(void)
|
||||
static u8 *rom = NULL; /* heap copy (fallback path) */
|
||||
static u32 romSize = 0;
|
||||
static int mappedAtCartBase = 0;
|
||||
|
||||
/* Candidate ROM file names, in priority order. The decomp repo ships the
|
||||
* base ROM at the repo root (baserom.<r>.z64); a user-supplied final ROM in
|
||||
* data/ takes precedence. */
|
||||
static const char *romFileCandidates(void)
|
||||
{
|
||||
#if GE007_IS_PAL
|
||||
return "ge007.pal-final.z64";
|
||||
return "ge007.pal-final.z64|baserom.e.z64";
|
||||
#else
|
||||
return "ge007.ntsc-final.z64";
|
||||
return "ge007.ntsc-final.z64|baserom.u.z64";
|
||||
#endif
|
||||
}
|
||||
|
||||
static int romHeaderValid(const u8 *h, char *err, size_t errsz)
|
||||
{
|
||||
static const u8 magic[4] = { 0x80, 0x37, 0x12, 0x40 };
|
||||
#if GE007_IS_PAL
|
||||
const char country = 'P';
|
||||
#else
|
||||
const char country = 'E';
|
||||
#endif
|
||||
|
||||
if (memcmp(h, magic, 4) != 0) {
|
||||
snprintf(err, errsz, "bad N64 magic at 0x00 (got %02x%02x%02x%02x)",
|
||||
h[0], h[1], h[2], h[3]);
|
||||
return 0;
|
||||
}
|
||||
if (memcmp(h + 0x20, "GOLDENEYE", 9) != 0) {
|
||||
snprintf(err, errsz, "ROM name at 0x20 is not GOLDENEYE");
|
||||
return 0;
|
||||
}
|
||||
if (h[0x3C] != 'G' || h[0x3D] != 'E') {
|
||||
snprintf(err, errsz, "cartridge ID at 0x3C is not GE");
|
||||
return 0;
|
||||
}
|
||||
if (h[0x3E] != country) {
|
||||
snprintf(err, errsz, "country byte at 0x3E is %c, expected %c",
|
||||
h[0x3E], country);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int romdataInit(void)
|
||||
{
|
||||
static char pathBuf[1024 + 64];
|
||||
snprintf(pathBuf, sizeof(pathBuf), "$S/%s", romFileName());
|
||||
const char *path = sysResolvePath(pathBuf);
|
||||
char err[128] = "";
|
||||
const char *list = romFileCandidates();
|
||||
char listBuf[512];
|
||||
strcpy(listBuf, list);
|
||||
|
||||
/* TODO(Phase 1):
|
||||
* - open + read the whole ROM into `rom`
|
||||
* - validate the N64 magic ("Nintendo" @ 0x100) + region
|
||||
* - build the segment map (code/data/assets) from the decomp's layout
|
||||
* - patch the game's segment table so IMAGESEG / asset refs resolve
|
||||
*/
|
||||
sysLogPrintf(LOG_INFO, "romdataInit: loading %s (TODO Phase 1)", path);
|
||||
/* Try each candidate in the data dir, next to the exe, and in the CWD
|
||||
* (the decomp repo ships baserom.<r>.z64 at the repo root). */
|
||||
for (char *tok = strtok(listBuf, "|"); tok;
|
||||
tok = strtok(NULL, "|")) {
|
||||
static const char *locs[3] = { "$S/", "$E/", "./" };
|
||||
int li;
|
||||
FSFile *f = NULL;
|
||||
|
||||
FSFile *f = fsOpen(path, "rb");
|
||||
if (!f) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataInit: cannot open %s", path);
|
||||
return -1;
|
||||
}
|
||||
romSize = (u32)fsSize(f);
|
||||
rom = (u8 *)malloc(romSize);
|
||||
if (!rom || fsRead(f, rom, romSize) != (int32_t)romSize) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataInit: failed to read ROM");
|
||||
for (li = 0; li < 3 && !f; li++) {
|
||||
char pathbuf[1024];
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s%s", locs[li], tok);
|
||||
f = fsOpen(sysResolvePath(pathbuf), "rb");
|
||||
}
|
||||
if (!f)
|
||||
continue;
|
||||
|
||||
romSize = (u32)fsSize(f);
|
||||
u8 *img = (u8 *)malloc(romSize ? romSize : 1);
|
||||
int ok = img && fsRead(f, img, romSize) == (int32_t)romSize;
|
||||
fsClose(f);
|
||||
return -1;
|
||||
}
|
||||
fsClose(f);
|
||||
if (!ok) {
|
||||
free(img);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (romSize < 0x104 || memcmp(rom + 0x100, "Nintendo", 8) != 0) {
|
||||
sysLogPrintf(LOG_ERROR, "romdataInit: bad N64 ROM magic");
|
||||
return -1;
|
||||
if (romSize < 0x40 || !romHeaderValid(img, err, sizeof(err))) {
|
||||
sysLogPrintf(LOG_WARNING, "romdataInit: %s rejected: %s",
|
||||
tok, err);
|
||||
free(img);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Map at the cart address so absolute asset symbols are live. */
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
void *at = VirtualAlloc((LPVOID)CART_BASE, romSize,
|
||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (at == (void *)(uintptr_t)CART_BASE) {
|
||||
memcpy(at, img, romSize);
|
||||
free(img);
|
||||
mappedAtCartBase = 1;
|
||||
sysLogPrintf(LOG_INFO, "romdataInit: %s (%u bytes) mapped at "
|
||||
"0x%08X (cart base)", tok, romSize, CART_BASE);
|
||||
return 0;
|
||||
}
|
||||
if (at)
|
||||
VirtualFree(at, 0, MEM_RELEASE);
|
||||
sysLogPrintf(LOG_WARNING, "romdataInit: could not reserve 0x%08X; "
|
||||
"using heap copy — direct ROM reads will fail",
|
||||
CART_BASE);
|
||||
#else
|
||||
/* Best effort: fixed-hint mmap. If the hint is refused we fall back
|
||||
* to a heap copy (same limitation as above). */
|
||||
sysLogPrintf(LOG_WARNING, "romdataInit: fixed-address mapping not "
|
||||
"implemented on this platform; using heap copy");
|
||||
#endif
|
||||
|
||||
rom = img;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
sysLogPrintf(LOG_ERROR, "romdataInit: no ROM found (tried %s in data/ "
|
||||
"and exe dir)", list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void romdataDestroy(void)
|
||||
{
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
if (mappedAtCartBase) {
|
||||
VirtualFree((LPVOID)CART_BASE, 0, MEM_RELEASE);
|
||||
mappedAtCartBase = 0;
|
||||
}
|
||||
#endif
|
||||
free(rom);
|
||||
rom = NULL;
|
||||
romSize = 0;
|
||||
}
|
||||
|
||||
const u8 *romdataGetRom(void) { return rom; }
|
||||
const u8 *romdataGetRom(void)
|
||||
{
|
||||
if (mappedAtCartBase)
|
||||
return (const u8 *)(uintptr_t)CART_BASE;
|
||||
return rom;
|
||||
}
|
||||
u32 romdataGetRomSize(void) { return romSize; }
|
||||
|
||||
/*
|
||||
* Is this cart address inside the mapped ROM? Used by the PI shims to bounds-
|
||||
* check DMA before touching host memory.
|
||||
*/
|
||||
int romdataCartAddrValid(u32 addr, u32 size)
|
||||
{
|
||||
if (!mappedAtCartBase || !romSize)
|
||||
return 0;
|
||||
if (addr < CART_BASE)
|
||||
return 0;
|
||||
u32 off = addr - CART_BASE;
|
||||
return off + size <= romSize;
|
||||
}
|
||||
|
||||
const void *romdataMapVa(u32 va)
|
||||
{
|
||||
/* TODO(Phase 1): map an N64 RDRAM VA (0xA0000000+) into the image. */
|
||||
/* N64 RDRAM VAs (0x80000000+/0xA0000000+) have no PC meaning; only cart
|
||||
* addresses are mapped. Kept for API parity with the PD port. */
|
||||
(void)va;
|
||||
return NULL;
|
||||
}
|
||||
+57
-1
@@ -12,6 +12,17 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "system.h"
|
||||
|
||||
/* --- Time --------------------------------------------------------------- */
|
||||
@@ -64,10 +75,55 @@ const char *sysGetExeDir(void)
|
||||
return exeDir;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand the "$S/" (data dir) and "$E/" (exe dir) prefixes used throughout
|
||||
* the port. Anything else is returned unchanged (relative to the CWD).
|
||||
*
|
||||
* $E: directory containing the executable.
|
||||
* $S: "data/" next to the CWD if it exists there, else "data/" next to the
|
||||
* executable (so running build-pc/ge007.*.exe from anywhere finds
|
||||
* ./data/ when run from the repo root).
|
||||
*/
|
||||
const char *sysResolvePath(const char *path)
|
||||
{
|
||||
static char out[1024];
|
||||
/* TODO(Phase 1): expand "$S/" (data dir) and "$E/" (exe dir) prefixes. */
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
static char exedir[1024] = "";
|
||||
if (!exedir[0]) {
|
||||
DWORD n = GetModuleFileNameA(NULL, exedir, sizeof(exedir) - 1);
|
||||
if (n) {
|
||||
char *slash = strrchr(exedir, '\\');
|
||||
if (slash)
|
||||
*slash = 0;
|
||||
} else {
|
||||
exedir[0] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!strncmp(path, "$E/", 3)) {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
snprintf(out, sizeof(out), "%s\\%s", exedir, path + 3);
|
||||
#else
|
||||
snprintf(out, sizeof(out), "./%s", path + 3);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
if (!strncmp(path, "$S/", 3)) {
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
if (_access("data", 0) == 0)
|
||||
snprintf(out, sizeof(out), "data\\%s", path + 3);
|
||||
else
|
||||
snprintf(out, sizeof(out), "%s\\data\\%s", exedir, path + 3);
|
||||
#else
|
||||
if (access("data", F_OK) == 0)
|
||||
snprintf(out, sizeof(out), "data/%s", path + 3);
|
||||
else
|
||||
snprintf(out, sizeof(out), "./data/%s", path + 3);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
strncpy(out, path, sizeof(out) - 1);
|
||||
out[sizeof(out) - 1] = 0;
|
||||
return out;
|
||||
|
||||
+136
-25
@@ -4,13 +4,14 @@
|
||||
* Owns the GL context that the software RSP (fast3d) renders into. Drives the
|
||||
* frame boundary hooks called by the port scheduler (gesched.c).
|
||||
*
|
||||
* Modelled on the PD port's port/src/video.c (~590 lines).
|
||||
*
|
||||
* STATUS: scaffolding stub — implement during Phase 1 (window + clear) and
|
||||
* Phase 2 (frame pacing, options).
|
||||
* Phase 1: real window + GL context + clear colour, driven by the demo loop
|
||||
* in main.c. gfx_* entry points are still the no-ops in gfxstub.c until the
|
||||
* PD-derived fast3d lands (Phase 2); videoStartFrame/EndFrame do the GL work
|
||||
* directly so a visible frame exists before that.
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
#include <GL/gl.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
@@ -30,53 +31,163 @@ static int vidFpsLimit = 0;
|
||||
static int vidFullscreen = 0;
|
||||
static int vidMaximize = 0;
|
||||
|
||||
static SDL_Window *vidWindow = NULL;
|
||||
static SDL_GLContext vidGL = NULL;
|
||||
|
||||
/* FPS accounting. */
|
||||
static uint64_t fpsLastTick = 0;
|
||||
static int fpsFrames = 0;
|
||||
static float fpsValue = 0.0f;
|
||||
|
||||
int videoInit(void)
|
||||
{
|
||||
/* TODO(Phase 1):
|
||||
* - SDL_Init(VIDEO)
|
||||
* - create window (title "GoldenEye 007", default 640x480 or config)
|
||||
* - create GL 3.0+ context
|
||||
* - build the GfxInitSettings (window-manager API = SDL2, rendering API
|
||||
* = OpenGL) and call gfx_init()
|
||||
* - apply config: vsync, fps limit, fullscreen, maximize
|
||||
*/
|
||||
sysLogPrintf(LOG_INFO, "videoInit: TODO (Phase 1)");
|
||||
if (initDone)
|
||||
return 0;
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
sysLogPrintf(LOG_ERROR, "videoInit: SDL_Init failed: %s",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
/* fast3d will want a modern context once it lands; 3.0 core is the floor
|
||||
* the PD port targets. */
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
|
||||
int w = 640, h = 480; /* GE's internal resolution (NTSC) */
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
|
||||
if (vidFullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
|
||||
vidWindow = SDL_CreateWindow("GoldenEye 007 (PC port)",
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, w, h, flags);
|
||||
if (!vidWindow) {
|
||||
sysLogPrintf(LOG_ERROR, "videoInit: SDL_CreateWindow failed: %s",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
if (vidMaximize)
|
||||
SDL_MaximizeWindow(vidWindow);
|
||||
|
||||
vidGL = SDL_GL_CreateContext(vidWindow);
|
||||
if (!vidGL) {
|
||||
sysLogPrintf(LOG_ERROR, "videoInit: SDL_GL_CreateContext failed: %s",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
SDL_GL_MakeCurrent(vidWindow, vidGL);
|
||||
SDL_GL_SetSwapInterval(vidVsync ? 1 : 0);
|
||||
|
||||
/* Phase 1 clear colour: dark blue-grey so the window is visibly alive. */
|
||||
glClearColor(0.10f, 0.12f, 0.16f, 1.0f);
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
/* fast3d hooks (no-ops until Phase 2). */
|
||||
gfx_init(NULL);
|
||||
|
||||
fpsLastTick = sysGetMicroseconds();
|
||||
initDone = 1;
|
||||
sysLogPrintf(LOG_INFO, "videoInit: %dx%d window + GL context up", w, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void videoDestroy(void)
|
||||
{
|
||||
if (initDone) {
|
||||
gfx_destroy();
|
||||
initDone = 0;
|
||||
if (!initDone)
|
||||
return;
|
||||
gfx_destroy();
|
||||
if (vidGL) {
|
||||
SDL_GL_DeleteContext(vidGL);
|
||||
vidGL = NULL;
|
||||
}
|
||||
if (vidWindow) {
|
||||
SDL_DestroyWindow(vidWindow);
|
||||
vidWindow = NULL;
|
||||
}
|
||||
SDL_Quit();
|
||||
initDone = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pump window events. Returns 1 when the app should quit (window close or
|
||||
* ESC). Called by the demo loop until the port scheduler owns the frame
|
||||
* boundary (Phase 2).
|
||||
*/
|
||||
int videoHandleEvents(void)
|
||||
{
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT)
|
||||
return 1;
|
||||
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void videoStartFrame(void)
|
||||
{
|
||||
if (initDone) gfx_start_frame();
|
||||
if (!initDone)
|
||||
return;
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
gfx_start_frame();
|
||||
}
|
||||
|
||||
void videoSubmitCommands(Gfx *cmds)
|
||||
{
|
||||
if (initDone) gfx_run(cmds);
|
||||
if (initDone)
|
||||
gfx_run(cmds);
|
||||
}
|
||||
|
||||
void videoEndFrame(void)
|
||||
{
|
||||
if (!initDone) return;
|
||||
if (!initDone)
|
||||
return;
|
||||
gfx_end_frame();
|
||||
/* TODO(Phase 2): FPS accounting + framerate limiting. */
|
||||
if (vidWindow)
|
||||
SDL_GL_SwapWindow(vidWindow);
|
||||
|
||||
/* FPS accounting. */
|
||||
fpsFrames++;
|
||||
uint64_t now = sysGetMicroseconds();
|
||||
if (now - fpsLastTick >= 1000000) {
|
||||
fpsValue = (float)fpsFrames * 1000000.0f / (float)(now - fpsLastTick);
|
||||
fpsFrames = 0;
|
||||
fpsLastTick = now;
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetVsync(int enabled) { vidVsync = enabled; /* TODO: SDL_GL_SetSwapInterval */ }
|
||||
void videoSetFramerateLimit(int fps) { vidFpsLimit = fps; /* TODO */ }
|
||||
void videoSetVsync(int enabled)
|
||||
{
|
||||
vidVsync = enabled;
|
||||
if (initDone && vidWindow)
|
||||
SDL_GL_SetSwapInterval(vidVsync ? 1 : 0);
|
||||
}
|
||||
void videoSetFramerateLimit(int fps) { vidFpsLimit = fps; /* TODO Phase 2 */ }
|
||||
int videoGetFullscreen(void) { return vidFullscreen; }
|
||||
void videoSetFullscreen(int enabled) { vidFullscreen = enabled; /* TODO */ }
|
||||
void videoSetFullscreen(int enabled)
|
||||
{
|
||||
vidFullscreen = enabled;
|
||||
if (initDone && vidWindow)
|
||||
SDL_SetWindowFullscreen(vidWindow,
|
||||
enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
}
|
||||
int videoGetMaximize(void) { return vidMaximize; }
|
||||
void videoSetMaximize(int enabled) { vidMaximize = enabled; /* TODO */ }
|
||||
float videoGetFPS(void) { return 0.0f; /* TODO */ }
|
||||
void videoSetMaximize(int enabled)
|
||||
{
|
||||
vidMaximize = enabled;
|
||||
if (initDone && vidWindow) {
|
||||
if (enabled)
|
||||
SDL_MaximizeWindow(vidWindow);
|
||||
else
|
||||
SDL_RestoreWindow(vidWindow);
|
||||
}
|
||||
}
|
||||
float videoGetFPS(void) { return fpsValue; }
|
||||
|
||||
PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,559 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
gen_romassets.py — generate absolute ROM asset symbols for the PC port.
|
||||
|
||||
For a given region this emits a GAS file (port/src/romassets_<r>.s) defining
|
||||
every ROM asset symbol as an ABSOLUTE cart address (0x10000000 + rom_offset),
|
||||
matching the N64 linker layout (ge007.ld). The PC port maps the loaded .z64
|
||||
into host memory at exactly 0x10000000 (see port/src/romdata.c), so:
|
||||
|
||||
* `&symbol` in game code yields the cart address, and romCopy()/
|
||||
osPiStartDma(OS_READ, ...) shims memcpy from that address (Phase 1).
|
||||
* file_resource_table.inc.c entries stay in ROM order, so ob.c's
|
||||
`table[i+1].hw_address - table[i].hw_address` size math is correct.
|
||||
|
||||
Sources of truth (all in-repo, N64 build untouched):
|
||||
scripts/filelist.<r>.csv rom offset + size per asset file
|
||||
assets/obseg/file_resource_table.inc.c ordered obseg symbol list
|
||||
assets/ramrom/ramrom.s region-aware (.ifdef/.else/.endif)
|
||||
assets/music/{music,sfx.ctl,sfx.tbl,instruments.ctl,instruments.tbl}.s
|
||||
ge007.ld segment markers (BEGIN_SEG/END_SEG)
|
||||
|
||||
Code segments (header/boot/start/code/cdata/inflate/game) have no CSV entry;
|
||||
their offsets are best-effort values derived from the disassembly comments in
|
||||
ge007.ld and are marked as such. They are only consumed by the N64 boot path,
|
||||
which the PC port does not run.
|
||||
|
||||
Usage:
|
||||
python3 scripts/gen_romassets.py u # -> port/src/romassets_u.s
|
||||
python3 scripts/gen_romassets.py e # (EU manifest quirks: see docs)
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
CART_BASE = 0x10000000
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def die(msg):
|
||||
print(f"gen_romassets: ERROR: {msg}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# CSV manifest
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
class Csv:
|
||||
def __init__(self, region):
|
||||
path = os.path.join(ROOT, "scripts", f"filelist.{region}.csv")
|
||||
self.by_name = {} # basename -> (offset, size, fullpath)
|
||||
self.by_path = {} # fullpath -> (offset, size)
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
parts = line.strip().split(",")
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
try:
|
||||
off, size = int(parts[0]), int(parts[1])
|
||||
except ValueError:
|
||||
continue
|
||||
full = parts[2]
|
||||
name = full.rsplit("/", 1)[-1]
|
||||
self.by_name[name] = (off, size, full)
|
||||
self.by_path[full] = (off, size)
|
||||
# Scanner-prefixed names: ge007.<r>.<hexoffset>.<realname>
|
||||
m = re.match(r"^ge007\.[uje]\.[0-9a-fA-F]+\.(.+)$", name)
|
||||
if m:
|
||||
self.by_name.setdefault(m.group(1), (off, size, full))
|
||||
|
||||
def lookup(self, candidates):
|
||||
"""Try candidate basenames in order; return (offset, size, matched)."""
|
||||
for cand in candidates:
|
||||
if cand in self.by_name:
|
||||
off, size, full = self.by_name[cand]
|
||||
return off, size, cand
|
||||
# also allow an exact full-path hit (dir-qualified lookups)
|
||||
if cand.startswith("assets/") and cand in self.by_path:
|
||||
off, size = self.by_path[cand]
|
||||
return off, size, cand
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def name_candidates(basename):
|
||||
"""Manifest names are inconsistent across regions: with/without .bin,
|
||||
trailing Z added/dropped. Try the plain name first, then variants."""
|
||||
cands = [basename]
|
||||
stem = re.sub(r"\.(bin|seg)$", "", basename)
|
||||
if stem != basename:
|
||||
cands.append(stem)
|
||||
else:
|
||||
cands.append(basename + ".bin") # CSV names carry .bin
|
||||
for s in (stem, stem[:-1] if stem.endswith("Z") else stem + "Z"):
|
||||
cands.append(s) # trailing Z added/dropped
|
||||
cands.append(s + ".bin")
|
||||
cands.append(s + ".seg") # ob__ob_end.seg style
|
||||
return cands
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Region-aware .s parsing (ob_seg.s / ramrom.s)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
class RegionParser:
|
||||
"""Parses asset .s files, honouring .ifdef VERSION_XX / .else / .endif.
|
||||
|
||||
Yields, in file order, either:
|
||||
("file", symbol, incbin_path) — a .global label + .incbin pair
|
||||
("label", symbol) — a bare label (segment marker etc.)
|
||||
for the requested target region.
|
||||
"""
|
||||
|
||||
COND_RE = re.compile(r"^\s*\.ifdef\s+(VERSION_\w+)")
|
||||
GLOBAL_RE = re.compile(r"^\s*\.global\s+(\w+)")
|
||||
LABEL_RE = re.compile(r"^(\w+):")
|
||||
INCBIN_RE = re.compile(r'^\s*\.incbin\s+"([^"]+)"')
|
||||
|
||||
def __init__(self, target_region): # "u" | "e" | "j"
|
||||
self.target = {"u": "VERSION_US", "e": "VERSION_EU",
|
||||
"j": "VERSION_JP"}[target_region]
|
||||
self.stack = [] # entries: (cond, in_else)
|
||||
|
||||
def _active(self):
|
||||
for cond, in_else in self.stack:
|
||||
if (cond == self.target) != (not in_else):
|
||||
return False
|
||||
return True
|
||||
|
||||
def parse(self, path):
|
||||
"""State: a `.global X` arms `pending`; the following `X:` label moves
|
||||
it to `expecting` (its own label line); an `.incbin` then makes it a
|
||||
file entry, otherwise the next `.global` flushes it as a bare marker."""
|
||||
out = []
|
||||
pending = None # symbol from .global, label line not yet seen
|
||||
expecting = None # label seen; waiting for .incbin (file) or next
|
||||
# .global (bare marker)
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
for raw in f:
|
||||
line = raw.rstrip("\n")
|
||||
m = re.match(r"^\s*\.ifdef\s+(\w+)", line)
|
||||
if m:
|
||||
self.stack.append((m.group(1), False))
|
||||
continue
|
||||
if re.match(r"^\s*\.else\b", line):
|
||||
if self.stack:
|
||||
cond, _ = self.stack[-1]
|
||||
self.stack[-1] = (cond, True)
|
||||
continue
|
||||
if re.match(r"^\s*\.endif\b", line):
|
||||
if self.stack:
|
||||
self.stack.pop()
|
||||
continue
|
||||
if not self._active():
|
||||
continue
|
||||
m = self.GLOBAL_RE.match(line)
|
||||
if m:
|
||||
if expecting is not None:
|
||||
out.append(("label", expecting))
|
||||
expecting = None
|
||||
pending = m.group(1)
|
||||
continue
|
||||
m = self.INCBIN_RE.match(line)
|
||||
if m and expecting is not None:
|
||||
out.append(("file", expecting, m.group(1)))
|
||||
expecting = None
|
||||
continue
|
||||
m = self.LABEL_RE.match(line)
|
||||
if m:
|
||||
sym = m.group(1)
|
||||
if sym == pending:
|
||||
expecting = sym
|
||||
pending = None
|
||||
else:
|
||||
out.append(("label", sym))
|
||||
if expecting is not None:
|
||||
out.append(("label", expecting))
|
||||
return out
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Per-source extraction
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def parse_resource_table(region):
|
||||
"""assets/obseg/file_resource_table.inc.c -> ordered [(symbol, pathstr)].
|
||||
|
||||
The table is C: entries for other regions are wrapped in
|
||||
`#ifdef VERSION_XX` (e.g. all L*P text rows under VERSION_EU). Honour
|
||||
those conditionals for the target region."""
|
||||
path = os.path.join(ROOT, "assets", "obseg", "file_resource_table.inc.c")
|
||||
cond_region = {"VERSION_US": "u", "VERSION_EU": "e", "VERSION_JP": "j"}
|
||||
entries = []
|
||||
seen = set()
|
||||
stack = [] # condition names (only VERSION_* occur in this file)
|
||||
rx = re.compile(r'\{\s*(\w+)\s*,\s*"([^"]*)"\s*,\s*&(\w+)\s*\}')
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^\s*#ifdef\s+(\w+)", line)
|
||||
if m:
|
||||
stack.append(m.group(1))
|
||||
continue
|
||||
if re.match(r"^\s*#endif\b", line):
|
||||
if stack:
|
||||
stack.pop()
|
||||
continue
|
||||
active = all(cond_region.get(c, "?") == region for c in stack)
|
||||
if not active:
|
||||
continue
|
||||
m = rx.search(line)
|
||||
if not m:
|
||||
continue
|
||||
sym = m.group(3)
|
||||
if sym in seen:
|
||||
continue
|
||||
seen.add(sym)
|
||||
entries.append((sym, m.group(2)))
|
||||
return entries
|
||||
|
||||
|
||||
def resolve_obseg(entries, csv):
|
||||
"""Map each resource-table symbol to (offset, size)."""
|
||||
resolved = [] # (symbol, offset, size, matched_name)
|
||||
missing = []
|
||||
for sym, pathstr in entries:
|
||||
base = pathstr.rsplit("/", 1)[-1]
|
||||
hit = csv.lookup(Csv.name_candidates(base))
|
||||
if hit is None:
|
||||
# fall back to the symbol name itself (pathstr may be stale,
|
||||
# e.g. BG_ELD -> bg_imp_all_p_seg; strip a trailing _seg)
|
||||
cands = Csv.name_candidates(sym)
|
||||
if sym.endswith("_seg"):
|
||||
cands += Csv.name_candidates(sym[:-4])
|
||||
hit = csv.lookup(cands)
|
||||
if hit is None:
|
||||
missing.append((sym, pathstr))
|
||||
continue
|
||||
off, size, matched = hit
|
||||
resolved.append((sym, off, size, matched))
|
||||
return resolved, missing
|
||||
|
||||
|
||||
def parse_ramrom_region(region, csv):
|
||||
rp = RegionParser(region)
|
||||
path = os.path.join(ROOT, "assets", "ramrom", "ramrom.s")
|
||||
items = rp.parse(path)
|
||||
files = [] # (symbol, offset, size)
|
||||
markers = {} # bare label -> index of next file symbol (resolved later)
|
||||
pending_marker = None
|
||||
for kind, a, *rest in items:
|
||||
if kind == "file":
|
||||
sym, incbin = a, rest[0]
|
||||
base = incbin.rsplit("/", 1)[-1]
|
||||
hit = csv.lookup(Csv.name_candidates(base))
|
||||
if hit is None:
|
||||
die(f"ramrom: no CSV entry for {sym} ({incbin})")
|
||||
off, size, _ = hit
|
||||
files.append((sym, off, size))
|
||||
pending_marker = None
|
||||
else: # bare label
|
||||
if a.endswith("_end"):
|
||||
continue # handled via start+size below
|
||||
markers[a] = len(files) # value = offset of next file
|
||||
return files, markers
|
||||
|
||||
|
||||
def parse_music_tracks(region, csv):
|
||||
"""music.s: music_file <name> lines (region blocks inside the macro)."""
|
||||
path = os.path.join(ROOT, "assets", "music", "music.s")
|
||||
tracks = []
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^\s*music_file\s+(\w+)", line)
|
||||
if m:
|
||||
tracks.append(m.group(1))
|
||||
resolved = []
|
||||
for name in tracks:
|
||||
hit = csv.lookup(Csv.name_candidates(f"{name}.bin"))
|
||||
if hit is None:
|
||||
die(f"music: no CSV entry for track {name}")
|
||||
off, size, _ = hit
|
||||
resolved.append((name, off, size))
|
||||
return resolved
|
||||
|
||||
|
||||
def parse_music_sections(csv):
|
||||
"""sfx.ctl.s / sfx.tbl.s / instruments.ctl.s / instruments.tbl.s:
|
||||
each defines _<x>SegmentRomStart + incbin + _<x>SegmentRomEnd."""
|
||||
out = []
|
||||
for stem in ("sfx.ctl", "sfx.tbl", "instruments.ctl", "instruments.tbl"):
|
||||
path = os.path.join(ROOT, "assets", "music", f"{stem}.s")
|
||||
start_sym = end_sym = incbin = None
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
m = re.match(r"^\s*\.global\s+(\w+)", line)
|
||||
if m:
|
||||
sym = m.group(1)
|
||||
if sym.endswith("SegmentRomStart"):
|
||||
start_sym = sym
|
||||
elif sym.endswith("SegmentRomEnd"):
|
||||
end_sym = sym
|
||||
continue
|
||||
m = re.match(r'^\s*\.incbin\s+"([^"]+)"', line)
|
||||
if m:
|
||||
incbin = m.group(1)
|
||||
if not (start_sym and end_sym and incbin):
|
||||
die(f"music section {stem}: expected start/end/incbin, got "
|
||||
f"{start_sym}/{end_sym}/{incbin}")
|
||||
base = incbin.rsplit("/", 1)[-1]
|
||||
hit = csv.lookup(Csv.name_candidates(base))
|
||||
if hit is None:
|
||||
die(f"music section {stem}: no CSV entry for {incbin}")
|
||||
off, size, _ = hit
|
||||
out.append((start_sym, end_sym, off, size))
|
||||
return out
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Linker-script segments (ge007.ld)
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def build_segments(region, csv, obseg_resolved, ramrom_files, music_tracks,
|
||||
music_sections, rom_size):
|
||||
"""Assign (offset, size) to every BEGIN_SEG segment in ge007.ld.
|
||||
|
||||
Asset-backed segments come from the CSV (exact). Code segments use
|
||||
best-effort values from the disassembly comments in ge007.ld.
|
||||
"""
|
||||
seg = {} # name -> (offset, size, exact?)
|
||||
|
||||
def csv1(basename):
|
||||
hit = csv.lookup(Csv.name_candidates(basename))
|
||||
if hit is None:
|
||||
die(f"segment lookup failed for {basename}")
|
||||
return hit[0], hit[1]
|
||||
|
||||
# --- code segments (approximate; N64 boot path only) -----------------
|
||||
seg["header"] = (0x0, 0x40, False)
|
||||
seg["boot"] = (0x40, 0x1000 - 0x40, False) # start @0x1000 assumed
|
||||
seg["start"] = (0x1000, 0x50, False) # RAM size 0x50 (ld cmt)
|
||||
seg["code"] = (0x1050, 0x21990 - 0x1050, False) # ld comment 001050-021990
|
||||
# inflate size 0x29BD (ld comment [29BD]); game ends at fontdl start.
|
||||
fontdl_off, _ = csv1("jfont_dl.bin")
|
||||
game_end = fontdl_off # ld: fontdl @ game end
|
||||
game_size = 0xE2D51 # ld comment [E2D51]
|
||||
seg["game"] = (game_end - game_size, game_size, False)
|
||||
inflate_size = 0x29BD
|
||||
seg["inflate"] = (seg["game"][0] - inflate_size, inflate_size, False)
|
||||
seg["cdata"] = (seg["code"][0] + seg["code"][1],
|
||||
seg["inflate"][0] - (seg["code"][0] + seg["code"][1]),
|
||||
False)
|
||||
|
||||
# --- asset segments (exact, from CSV) --------------------------------
|
||||
off, size = csv1("jfont_dl.bin")
|
||||
seg["fontdl"] = (off, size, True)
|
||||
off, size = csv1("jfont_chardata.bin")
|
||||
seg["jfontchardata"] = (off, size, True)
|
||||
off, size = csv1("efont_chardata.bin")
|
||||
seg["efontchardata"] = (off, size, True)
|
||||
off, size = csv1("animationtable_entries.bin")
|
||||
seg["animation_entries"] = (off, size, True)
|
||||
off, size = csv1("animationtable_data.bin")
|
||||
seg["animation_data"] = (off, size, True)
|
||||
off, size = csv1("Globalimagetable.bin")
|
||||
seg["Globalimagetable"] = (off, size, True)
|
||||
off, size = csv1("rarewarelogo.bin")
|
||||
seg["rarewarelogo"] = (off, size, True)
|
||||
off, size = csv1("usedby7F008DE4.bin") # assets/romfiles2.s blob
|
||||
seg["romfiles2"] = (off, size, True)
|
||||
|
||||
if ramrom_files:
|
||||
first = min(o for _, o, _ in ramrom_files)
|
||||
last_end = max(o + s for _, o, s in ramrom_files)
|
||||
seg["ramromfiles"] = (first, last_end - first, True)
|
||||
|
||||
# font objects = kerning + chartable, contiguous in ROM (verified).
|
||||
k_off, k_size = csv1("fontBankGothic_kerning.bin")
|
||||
c_off, c_size = csv1("fontBankGothic_fontchartable.bin")
|
||||
if k_off + k_size != c_off:
|
||||
die("fontBankGothic kerning/chartable not contiguous in CSV")
|
||||
seg["fontbankgothic"] = (k_off, k_size + c_size, True)
|
||||
k_off, k_size = csv1("fontZurichBold_kerning.bin")
|
||||
c_off, c_size = csv1("fontZurichBold_fontchartable.bin")
|
||||
if k_off + k_size != c_off:
|
||||
die("fontZurichBold kerning/chartable not contiguous in CSV")
|
||||
seg["fontzurichbold"] = (k_off, k_size + c_size, True)
|
||||
|
||||
# musicfiles = sfx.ctl .. last M* track end.
|
||||
m_off = min(o for _, _, o, _ in music_sections)
|
||||
m_end = max(o + s for _, o, s in music_tracks)
|
||||
seg["musicfiles"] = (m_off, m_end - m_off, True)
|
||||
|
||||
# obseg = first resource-table symbol .. end of ob__ob_end_seg.
|
||||
if obseg_resolved:
|
||||
first = min(o for _, o, _, _ in obseg_resolved)
|
||||
last_end = max(o + s for _, o, s, _ in obseg_resolved)
|
||||
seg["obseg"] = (first, last_end - first, True)
|
||||
|
||||
# images = everything after obseg to the end of the ROM.
|
||||
if "obseg" in seg:
|
||||
img_off = seg["obseg"][0] + seg["obseg"][1]
|
||||
seg["images"] = (img_off, rom_size - img_off, True)
|
||||
|
||||
return seg
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Emission
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def emit(region, csv, rom_size):
|
||||
lines = []
|
||||
ap = lines.append
|
||||
ap("/*")
|
||||
ap(f" * AUTO-GENERATED by scripts/gen_romassets.py ({region.upper()}) -- do not edit.")
|
||||
ap(" *")
|
||||
ap(" * Absolute ROM asset symbols for the PC port. Each symbol is the N64")
|
||||
ap(" * cart address (0x10000000 + rom_offset) of the corresponding asset in")
|
||||
ap(f" * scripts/filelist.{region}.csv, so `&symbol` / romCopy() work exactly as")
|
||||
ap(" * on hardware once romdata.c has mapped the .z64 at 0x10000000.")
|
||||
ap(" * Regenerate: python3 scripts/gen_romassets.py " + region)
|
||||
ap(" */")
|
||||
ap(".section .data")
|
||||
ap("")
|
||||
|
||||
def sym(name, value):
|
||||
ap(f".global {name}")
|
||||
ap(f".set {name}, 0x{value:X}")
|
||||
|
||||
# --- obseg (file_resource_table order = ROM order) -------------------
|
||||
entries = parse_resource_table(region)
|
||||
resolved, missing = resolve_obseg(entries, csv)
|
||||
if missing:
|
||||
for s, p in missing:
|
||||
print(f"gen_romassets: MISSING {s} ({p})", file=sys.stderr)
|
||||
die(f"{len(missing)} obseg symbols unresolved")
|
||||
|
||||
# Invariants the game relies on (ob.c size math = table[i+1]-table[i]).
|
||||
# Offsets must be non-decreasing; zero-size placeholder files may share
|
||||
# an offset (bg_ash/bg_imp/bg_sho all sit at one address in the US ROM).
|
||||
for i in range(1, len(resolved)):
|
||||
prev_sym, prev_off, prev_size, _ = resolved[i - 1]
|
||||
sym2, off2, _, _ = resolved[i]
|
||||
if off2 < prev_off:
|
||||
die(f"resource table not in ROM order: {prev_sym} ({prev_off:#x}) "
|
||||
f"> {sym2} ({off2:#x})")
|
||||
if prev_off + prev_size > off2:
|
||||
print(f"gen_romassets: WARNING overlap {prev_sym}+{prev_size} "
|
||||
f"> {sym2}", file=sys.stderr)
|
||||
|
||||
ap("/* --- assets/obseg (file_resource_table.inc.c order) ------------- */")
|
||||
for symname, off, size, matched in resolved:
|
||||
sym(symname, CART_BASE + off)
|
||||
sym(f"end_{symname}", CART_BASE + off + size)
|
||||
ap("")
|
||||
|
||||
# --- ramrom -----------------------------------------------------------
|
||||
ramrom_files, ramrom_markers = parse_ramrom_region(region, csv)
|
||||
if ramrom_files:
|
||||
ap("/* --- assets/ramrom (demo replays) ---------------------------- */")
|
||||
for i, (symname, off, size) in enumerate(ramrom_files):
|
||||
sym(symname, CART_BASE + off)
|
||||
sym(f"{symname}_end", CART_BASE + off + size)
|
||||
for marker, idx in ramrom_markers.items():
|
||||
if idx < len(ramrom_files):
|
||||
sym(marker, CART_BASE + ramrom_files[idx][1])
|
||||
ap("")
|
||||
|
||||
# --- music ------------------------------------------------------------
|
||||
tracks = parse_music_tracks(region, csv)
|
||||
sections = parse_music_sections(csv)
|
||||
hit = csv.lookup(Csv.name_candidates("number_music_samples"))
|
||||
if hit is None:
|
||||
die("music: no CSV entry for number_music_samples")
|
||||
sample_tbl_off = hit[0]
|
||||
first_track_off = min(o for _, o, _ in tracks)
|
||||
|
||||
ap("/* --- assets/music ------------------------------------------------ */")
|
||||
sym("_musicsampletblSegmentRomStart", CART_BASE + sample_tbl_off)
|
||||
sym("number_music_samples", CART_BASE + sample_tbl_off)
|
||||
sym("number_music_samples_end", CART_BASE + sample_tbl_off + 4)
|
||||
sym("table_music_data", CART_BASE + sample_tbl_off + 4)
|
||||
sym("_musicsampletblSegmentRomEnd", CART_BASE + first_track_off)
|
||||
sym("table_music_data_end", CART_BASE + first_track_off)
|
||||
for start_sym, end_sym, off, size in sections:
|
||||
sym(start_sym, CART_BASE + off)
|
||||
sym(end_sym, CART_BASE + off + size)
|
||||
for name, off, size in tracks:
|
||||
sym(name, CART_BASE + off)
|
||||
sym(f"end_{name}", CART_BASE + off + size)
|
||||
ap("")
|
||||
|
||||
# --- romfiles2 ---------------------------------------------------------
|
||||
hit = csv.lookup(Csv.name_candidates("usedby7F008DE4.bin"))
|
||||
if hit is not None:
|
||||
unknown2_off, unknown2_size = hit[0], hit[1]
|
||||
ap("/* --- assets/romfiles2.s -------------------------------------- */")
|
||||
sym("unknown2", CART_BASE + unknown2_off)
|
||||
sym("unknown2_end", CART_BASE + unknown2_off + unknown2_size)
|
||||
ap("")
|
||||
|
||||
# --- linker-script segment markers -------------------------------------
|
||||
segs = build_segments(region, csv, resolved, ramrom_files, tracks,
|
||||
sections, rom_size)
|
||||
ap("/* --- ge007.ld segment markers ------------------------------------ */")
|
||||
ap("/* RomStart/RomEnd are exact for asset segments; Start/End mirror them")
|
||||
ap("/* (game code only ever uses End-Start size differences for these).")
|
||||
ap("/* Code segments are approximate (see generator header comment). */")
|
||||
for name in ("header", "boot", "start", "code", "cdata", "inflate",
|
||||
"game", "fontdl", "jfontchardata", "efontchardata",
|
||||
"animation_entries", "animation_data", "Globalimagetable",
|
||||
"rarewarelogo", "romfiles2", "ramromfiles", "fontbankgothic",
|
||||
"fontzurichbold", "musicfiles", "obseg", "images"):
|
||||
if name not in segs:
|
||||
continue
|
||||
off, size, exact = segs[name]
|
||||
note = "" if exact else " /* approximate */"
|
||||
sym(f"_{name}SegmentRomStart", CART_BASE + off)
|
||||
sym(f"_{name}SegmentRomEnd", CART_BASE + off + size)
|
||||
sym(f"_{name}SegmentStart", CART_BASE + off)
|
||||
sym(f"_{name}SegmentEnd", CART_BASE + off + size)
|
||||
if note:
|
||||
lines[-1] += note
|
||||
# alt-start (ld: _startSegmentRomStart + 0x100000)
|
||||
s_off = segs["start"][0]
|
||||
sym("_alt_startSegmentRomStart", CART_BASE + s_off + 0x100000)
|
||||
sym("_alt_startSegmentStart", CART_BASE + s_off + 0x100000)
|
||||
|
||||
out = os.path.join(ROOT, "port", "src", f"romassets_{region}.s")
|
||||
with open(out, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
|
||||
n_syms = sum(1 for l in lines if l.startswith(".set "))
|
||||
print(f"gen_romassets: wrote {os.path.relpath(out, ROOT)} "
|
||||
f"({n_syms} symbols, obseg={len(resolved)}, ramrom={len(ramrom_files)}, "
|
||||
f"tracks={len(tracks)}, segments={len(segs)})")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2 or sys.argv[1] not in ("u", "e", "j"):
|
||||
die("usage: gen_romassets.py <u|e|j>")
|
||||
region = sys.argv[1]
|
||||
csv = Csv(region)
|
||||
|
||||
rom_size = None
|
||||
rom_name = {"u": "baserom.u.z64", "e": "baserom.e.z64",
|
||||
"j": "baserom.j.z64"}[region]
|
||||
rom_path = os.path.join(ROOT, rom_name)
|
||||
if os.path.exists(rom_path):
|
||||
rom_size = os.path.getsize(rom_path)
|
||||
else:
|
||||
print(f"gen_romassets: note: {rom_name} not found; "
|
||||
f"images segment sized from CSV max", file=sys.stderr)
|
||||
rom_size = max(o + s for o, s in csv.by_path.values())
|
||||
emit(region, csv, rom_size)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user