Phase 4: ROM Injection & Testing
Goals
Inject the converted Nyjah Houston model into the THPS ROM and validate it works correctly.
Step 1: Backup Original ROM
cp "Tony Hawk's Pro Skater (USA) (Rev 1).z64" "THPS_ORIGINAL_BACKUP.z64"
Always keep an unmodified backup.
Step 2: Inject Model Data
Using the ROM offsets documented in Phase 1, write the new binary data.
Python Injection Script
import struct
def inject_model(rom_path, output_path, model_binary, model_offset,
texture_binary, texture_offset):
"""Inject model and texture data into ROM at documented offsets"""
with open(rom_path, 'rb') as f:
rom = bytearray(f.read())
# Inject model (display lists + vertices)
rom[model_offset:model_offset + len(model_binary)] = model_binary
# Inject textures
rom[texture_offset:texture_offset + len(texture_binary)] = texture_binary
# Write modified ROM
with open(output_path, 'wb') as f:
f.write(rom)
print(f"Injected {len(model_binary)} bytes at offset 0x{model_offset:08X}")
print(f"Injected {len(texture_binary)} bytes at offset 0x{texture_offset:08X}")
return output_path
Step 3: Fix Internal Pointers (if needed)
If the container format has size/offset fields that reference the model data:
def fix_pointers(rom, pointer_offsets, old_size, new_size):
"""Update any size fields in the file table/container headers"""
for offset in pointer_offsets:
# Read current value
current = struct.unpack_from('>I', rom, offset)[0]
# Update if it references the old size
if current == old_size:
struct.pack_into('>I', rom, offset, new_size)
print(f"Fixed pointer at 0x{offset:08X}: {old_size} -> {new_size}")
Step 4: Fix ROM Checksum
After modifying the ROM, CRC values in the header must be recalculated.
Using rn64crc (command line)
# Download from romhacking.net utilities section
./rn64crc "THPS_Nyjah.z64"
Python CRC Fix (if tool not available)
def fix_n64_crc(rom_path):
"""
Recalculate CRC1 and CRC2 for N64 ROM header.
CRCs are at offsets 0x10 (CRC1) and 0x14 (CRC2).
Based on the N64 boot code CRC algorithm.
"""
# Use the n64crc algorithm (complex - use a library or tool)
# For now, emulators like Project64 will boot without correct CRC
# Real hardware (EverDrive) requires correct CRC
pass
Alternative: Use a GUI Tool
- Tool N64 (Windows) — fixes CRC with one click
- N64 ROM Checksum Fixer — available on romhacking.net
Step 5: Test in Emulator
Initial Testing (Project64 / Mupen64Plus)
- Load the modified ROM
- Boot test: Does the game reach the title screen?
- Menu test: Can you navigate to character select?
- Model test: Does Nyjah's model render correctly?
- Animation test: Do all animations play without deformation?
Test Checklist
| Test | Pass/Fail | Notes |
|---|---|---|
| Game boots | ||
| Title screen renders | ||
| Character select works | ||
| Model visible on select screen | ||
| Model renders in-game | ||
| Idle animation plays | ||
| Push animation works | ||
| Ollie animation works | ||
| Kickflip animation works | ||
| Grind animation works | ||
| Bail animation works | ||
| No texture corruption | ||
| No vertex explosion | ||
| No crashes during gameplay | ||
| All levels load |
Common Issues & Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Invisible model | Wrong vertex winding order | Flip normals in Blender |
| Garbled textures | Wrong texture format/offset | Verify CI4/CI8 format matches original |
| Vertex explosion | Size mismatch in vertex buffer load | Check G_VTX command vertex count |
| T-pose (no animation) | Skeleton mismatch | Verify bone count and hierarchy |
| Crash on load | Data overflows allocation | Reduce model size |
| Wrong colors | Palette offset incorrect | Fix palette data alignment |
| Z-fighting/flicker | Duplicate overlapping faces | Remove internal faces |
| Partial model missing | Display list truncated | Check gsSPEndDisplayList placement |
Step 6: Test on Real Hardware (Optional)
If you have an EverDrive 64 or similar flashcart:
- Ensure CRC is fixed (real hardware validates checksums)
- Copy ROM to SD card
- Test on actual N64 hardware
- Check for timing issues (emulators are more forgiving)
Step 7: Iterate
Based on testing results:
- Fix any rendering issues in Blender
- Re-export display lists
- Re-convert to binary
- Re-inject into ROM
- Re-test
This cycle typically takes 3-5 iterations to get right.
Final Output
n64-mods/
├── Tony Hawk's Pro Skater (USA) (Rev 1).z64 # Original (untouched)
├── THPS_Nyjah_Houston.z64 # Modified ROM
├── blender/
│ └── nyjah_houston.blend # Source model
├── textures/
│ ├── face.png
│ ├── torso_front.png
│ ├── torso_back.png
│ └── ...
├── scripts/
│ ├── convert_display_list.py
│ ├── convert_textures.py
│ ├── inject_model.py
│ └── fix_crc.py
└── docs/
├── rom_offsets.md # Documented offsets
└── skeleton_hierarchy.md # Bone structure
Deliverables
After completing this phase:
- Working modified ROM with Nyjah Houston as playable character
- All source files (Blender, textures, scripts) for future modifications
- Documentation of ROM offsets for reproducibility
- Test results confirming all animations work