mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
feat(release): self-contained asset-prep tool for the alpha bundle
The Windows bundle from bundle-win.sh / CI was unrunnable on its own (D179):
the port loads two ROM-derived directories (data/pcmodels-<region>/ and
data/pccg-<region>/) that are gitignored and cannot be shipped.
Add tools_pc/dist/prepare-assets/prepare-assets.py — a stdlib-only wrapper
(no compiler, no N64 toolchain, no network) that:
- auto-detects the ROM near the bundle, verifies SHA-1 -> region
(NTSC-U / PAL / NTSC-J), rejects unrecognised / byte-swapped dumps
- runs d43_emit.py + d69_emit.py against a vendored input tree
- writes data/pcmodels-<region>/ and data/pccg-<region>/ next to the exe
bundle-win.sh assembles prepare-assets/ fresh at package time (emit scripts
from tools_pc/, filelist.u.csv + file_resource_table.inc.c + the 512
modelFileHeader.inc.c files as vendored inputs) so it can never drift from
the scripts. Vendored inputs are decomp layout/symbol metadata, not game
assets. bundled README gets a one-time "prepare-assets" setup step.
Verified end-to-end: from a bundle containing only exe + DLLs + a US ROM,
prepare-assets.py produces all four output files byte-identical to
d43_emit.py / d69_emit.py run from the repo root. Bundle 14.1 MB (< 60 MB
guard). Error paths (no ROM / unknown SHA-1) tested.
Resolves BACKLOG "Alpha release — asset-prep tool" steps 1-3; step 4
(clean-machine smoke test with the game actually running) still owed.
This commit is contained in:
1 parent
ce9e090527
commit
dfab1876e4
3 files changed
+196
-8
No files matched your search
@@ -78,6 +78,26 @@ for l in SDL2 zlib gcc-libs libwinpthread mingw-w64; do
|
||||
[ -d "$MINGW/share/licenses/$l" ] && cp -r "$MINGW/share/licenses/$l" "$OUT/licenses/$l"
|
||||
done
|
||||
|
||||
# --- asset-prep tool -------------------------------------------------
|
||||
# The port needs two ROM-derived directories (data/pcmodels-<region>/ and
|
||||
# data/pccg-<region>/) that we cannot ship. prepare-assets.py regenerates
|
||||
# them from the user's own ROM using only the Python standard library. It is
|
||||
# assembled fresh from the tree here so it can never drift from the emit
|
||||
# scripts. The vendored inputs are decomp layout/symbol metadata (.inc.c /
|
||||
# .csv), NOT game assets (no textures, audio, models, levels, or text).
|
||||
PREP="$OUT/prepare-assets"
|
||||
mkdir -p "$PREP/vendor/scripts" "$PREP/vendor/assets/obseg"
|
||||
cp tools_pc/dist/prepare-assets/prepare-assets.py "$PREP/"
|
||||
cp tools_pc/d43_emit.py tools_pc/d69_emit.py "$PREP/"
|
||||
cp scripts/filelist.u.csv "$PREP/vendor/scripts/"
|
||||
cp assets/obseg/file_resource_table.inc.c "$PREP/vendor/assets/obseg/"
|
||||
# d43_emit.py os.walk()s assets/ for every *modelFileHeader.inc.c — preserve paths.
|
||||
( cd . && find assets -iname 'modelfileheader.inc.c' -print0 \
|
||||
| xargs -0 -I{} cp --parents {} "$PREP/vendor/" )
|
||||
nmh="$(find "$PREP/vendor/assets" -iname 'modelfileheader.inc.c' | wc -l)"
|
||||
echo " + prepare-assets/ (emit scripts + $nmh model headers)"
|
||||
[ "$nmh" -gt 400 ] || { echo "error: prepare-assets vendored only $nmh model headers — expected ~512" >&2; exit 1; }
|
||||
|
||||
# --- guard: no ROM / game data snuck in --------------------------------
|
||||
if find "$OUT" -type f \( -iname '*.z64' -o -iname '*.n64' -o -iname '*.v64' \) | grep -q .; then
|
||||
echo "error: bundle contains a ROM image — aborting" >&2
|
||||
|
||||
Vendored
+19
-8
@@ -28,20 +28,31 @@ NTSC-U (US):
|
||||
|
||||
Only the US ROM is supported in this alpha.
|
||||
|
||||
## 2. Run
|
||||
## 2. Set up (one time)
|
||||
|
||||
1. Unzip this folder anywhere.
|
||||
2. Make a `data` folder next to `ge007.x86_64.exe`.
|
||||
3. Copy your ROM into it, named exactly: `data\ge007.ntsc-final.z64`
|
||||
4. Run `ge007.x86_64.exe` **from this folder**.
|
||||
4. Prepare the ROM-derived data (needs Python 3.8+ on your PATH):
|
||||
|
||||
Configuration is written to `ge007.ini` on first run.
|
||||
```
|
||||
python prepare-assets\prepare-assets.py
|
||||
```
|
||||
|
||||
> If the game exits immediately reporting missing data, your ROM's assets need
|
||||
> the one-time extraction step described under "Building" in the project README
|
||||
> — the alpha binary expects the same asset layout a source build produces.
|
||||
This reads your ROM and writes `data\pcmodels-ntsc-final\` and
|
||||
`data\pccg-ntsc-final\` next to the exe. It uses only the Python standard
|
||||
library — no compiler or N64 toolchain — and takes a few seconds. Run it
|
||||
again only if you replace the ROM.
|
||||
|
||||
## 3. Controls (defaults)
|
||||
## 3. Run
|
||||
|
||||
Run `ge007.x86_64.exe` **from this folder**. Configuration is written to
|
||||
`ge007.ini` on first run.
|
||||
|
||||
> If the game exits immediately reporting missing data, re-run step 2.4 above
|
||||
> and check it printed `ALL CHECKS PASSED` for both passes.
|
||||
|
||||
## 4. Controls (defaults)
|
||||
|
||||
| Action | Keyboard / mouse |
|
||||
|---|---|
|
||||
@@ -55,7 +66,7 @@ Configuration is written to `ge007.ini` on first run.
|
||||
|
||||
Mouse sensitivity and Y-inversion are in the `[Input]` section of `ge007.ini`.
|
||||
|
||||
## 4. Legal
|
||||
## 5. Legal
|
||||
|
||||
Non-commercial fan preservation / research project. **Not** affiliated with,
|
||||
endorsed by, or sponsored by Nintendo, Rare, Microsoft, MGM, Danjaq, or EON
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python3
|
||||
"""One-time asset preparation for the GoldenEye 007 PC port (alpha).
|
||||
|
||||
The port loads PC-layout model / display-list / collision / nav data from two
|
||||
directories that are derived from a *GoldenEye* ROM and therefore are NOT
|
||||
shipped in this package:
|
||||
|
||||
data/pcmodels-<region>/ (pcmodels.bin + manifest.csv)
|
||||
data/pccg-<region>/ (pccg.bin + manifest.csv)
|
||||
|
||||
This script regenerates them from a ROM you legally own. It calls only the
|
||||
Python standard library -- no compiler, no N64 toolchain, no network. The
|
||||
conversion is deterministic: the same ROM always produces byte-identical
|
||||
output.
|
||||
|
||||
Usage (from the folder this script lives in, or anywhere):
|
||||
|
||||
python prepare-assets.py [--rom PATH] [--out DIR] [--keep-temp]
|
||||
|
||||
By default it looks for the ROM at <bundle>/data/*.z64 (and a few other
|
||||
obvious spots), verifies its SHA-1 to pick the region, and writes the two
|
||||
directories under <bundle>/data/ , where <bundle> is the folder that
|
||||
contains this "prepare-assets" folder (i.e. next to ge007.x86_64.exe).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
VENDOR = HERE / "vendor"
|
||||
|
||||
# SHA-1 of each supported retail ROM (big-endian .z64) -> region id the
|
||||
# emit scripts expect. Mirrors ge007.{u,e,j}.sha1 in the source tree.
|
||||
ROM_SHA1 = {
|
||||
"abe01e4aeb033b6c0836819f549c791b26cfde83": "ntsc-final", # NTSC-U (US)
|
||||
"167c3c433dec1f1eb921736f7d53fac8cb45ee31": "pal-final", # PAL (EU)
|
||||
"2a5dade32f7fad6c73c659d2026994632c1b3174": "jpn-final", # NTSC-J (JP)
|
||||
}
|
||||
|
||||
EMIT_STEPS = (
|
||||
("d43_emit.py", "pcmodels"), # models / display lists -> data/pcmodels-<region>/
|
||||
("d69_emit.py", "pccg"), # collision + stan nav -> data/pccg-<region>/
|
||||
)
|
||||
|
||||
|
||||
def die(msg):
|
||||
print(f"\nprepare-assets: error: {msg}\n", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def sha1_of(path):
|
||||
h = hashlib.sha1()
|
||||
with open(path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(1 << 20), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def find_rom(explicit, bundle_root):
|
||||
if explicit:
|
||||
p = Path(explicit).expanduser()
|
||||
if not p.is_file():
|
||||
die(f"--rom {p} : not a file")
|
||||
return p
|
||||
search = []
|
||||
for base in (bundle_root, bundle_root / "data", HERE, HERE / "vendor" / "data"):
|
||||
search += sorted(base.glob("*.z64")) + sorted(base.glob("*.n64")) + sorted(base.glob("*.v64"))
|
||||
# de-dup, keep order
|
||||
seen, cand = set(), []
|
||||
for p in search:
|
||||
if p.resolve() not in seen:
|
||||
seen.add(p.resolve())
|
||||
cand.append(p)
|
||||
if not cand:
|
||||
die("no .z64 ROM found. Put your GoldenEye ROM at "
|
||||
f"{bundle_root / 'data'} (create the 'data' folder) or pass --rom PATH.")
|
||||
if len(cand) > 1:
|
||||
print("prepare-assets: multiple ROM candidates found:")
|
||||
for p in cand:
|
||||
print(f" {p}")
|
||||
print("prepare-assets: using the first; pass --rom PATH to choose.")
|
||||
return cand[0]
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Generate the GoldenEye PC port's ROM-derived asset dirs.")
|
||||
ap.add_argument("--rom", help="path to your .z64 ROM (default: auto-detect near the bundle)")
|
||||
ap.add_argument("--out", help="bundle root to write data/ into (default: the folder holding prepare-assets)")
|
||||
ap.add_argument("--keep-temp", action="store_true", help="keep the temporary ROM copy under vendor/data/")
|
||||
args = ap.parse_args()
|
||||
|
||||
if not VENDOR.is_dir():
|
||||
die(f"missing {VENDOR} -- this script must stay inside the 'prepare-assets' folder from the zip.")
|
||||
for script, _ in EMIT_STEPS:
|
||||
if not (HERE / script).is_file():
|
||||
die(f"missing {HERE / script}")
|
||||
|
||||
bundle_root = Path(args.out).expanduser().resolve() if args.out else HERE.parent
|
||||
|
||||
rom = find_rom(args.rom, bundle_root)
|
||||
print(f"prepare-assets: ROM {rom}")
|
||||
digest = sha1_of(rom)
|
||||
region = ROM_SHA1.get(digest)
|
||||
if region is None:
|
||||
die(f"ROM SHA-1 {digest} is not a recognised retail GoldenEye 007 image.\n"
|
||||
" Supported: NTSC-U / PAL / NTSC-J, big-endian .z64.\n"
|
||||
" A byte-swapped (.n64/.v64) or overdumped ROM will not work -- convert to .z64 first.")
|
||||
print(f"prepare-assets: SHA-1 {digest} -> region '{region}'")
|
||||
|
||||
# The emit scripts assume CWD is a tree with scripts/ assets/ data/ .
|
||||
work = VENDOR
|
||||
(work / "data").mkdir(exist_ok=True)
|
||||
staged_rom = work / "data" / f"ge007.{region}.z64"
|
||||
if not (staged_rom.exists() and staged_rom.stat().st_size == rom.stat().st_size):
|
||||
shutil.copy2(rom, staged_rom)
|
||||
|
||||
out_dirs = []
|
||||
for script, tag in EMIT_STEPS:
|
||||
src = work / "data" / f"{tag}-{region}"
|
||||
if src.exists():
|
||||
shutil.rmtree(src)
|
||||
print(f"prepare-assets: running {script} {region} ...")
|
||||
r = subprocess.run([sys.executable, str(HERE / script), region], cwd=work)
|
||||
if r.returncode != 0:
|
||||
die(f"{script} exited {r.returncode}")
|
||||
if not (src / f"{tag}.bin").is_file():
|
||||
die(f"{script} did not produce {src / (tag + '.bin')}")
|
||||
out_dirs.append((src, tag))
|
||||
|
||||
dest_data = bundle_root / "data"
|
||||
dest_data.mkdir(parents=True, exist_ok=True)
|
||||
for src, tag in out_dirs:
|
||||
dest = dest_data / f"{tag}-{region}"
|
||||
if dest.resolve() == src.resolve():
|
||||
continue
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
shutil.move(str(src), str(dest))
|
||||
n = sum(1 for _ in dest.rglob("*") if _.is_file())
|
||||
print(f"prepare-assets: wrote {dest} ({n} files)")
|
||||
|
||||
if not args.keep_temp and staged_rom.exists() and staged_rom.resolve() != rom.resolve():
|
||||
staged_rom.unlink()
|
||||
|
||||
print("\nprepare-assets: done. You can now run ge007.x86_64.exe from the bundle folder.")
|
||||
print(f" (Your ROM must also be at {dest_data / f'ge007.{region}.z64'} for the game itself.)")
|
||||
if region != "ntsc-final":
|
||||
print(" NOTE: only the NTSC-U ROM is validated in this alpha; "
|
||||
f"'{region}' is experimental.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user