Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ jobs:
- name: Compile shared desktop host and game adapters
run: cmake --build build --target MegaManXSNESRecomp RockmanXSNESRecomp --parallel 4
- name: Check display, widescreen and password saves
run: cmake --build build --target mmx_display_test mmx_wide_policy_test mmx_renderer_test mmx_password_save_test && ctest --test-dir build --output-on-failure
run: cmake --build build --target mmx_display_test mmx_wide_policy_test mmx_renderer_test mmx_password_save_test mmx_zero_test && ctest --test-dir build --output-on-failure
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rom.cfg

# Extracted ROM data (generated from ROM at build time — copyrighted)
smw_assets.dat
zero-x3.bin
assets/

# Save files
Expand Down
25 changes: 20 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ function(add_mmx_variant TARGET GEN_DIR CFG_DIR)
src/mmx_00.c src/gen_stubs.c src/mmx_cpu_infra.c src/mmx_rtl.c
src/mmx_spc_player.c src/mods/mmx_diagnostics_plugin.c
src/mods/mmx_widescreen_plugin.c src/mods/mmx_msu1_plugin.c
src/mods/mmx_password_plugin.c src/mmx_password_save.cpp)
src/mods/mmx_password_plugin.c src/mmx_password_save.cpp
src/mods/mmx_zero_plugin.c src/mmx_zero.c src/mmx_source_assets.cpp)
target_include_directories(${TARGET} PRIVATE
${CFG_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR} ${SNESRECOMP_RUNNER_INCLUDE_DIRS})
Expand Down Expand Up @@ -69,14 +70,17 @@ function(add_mmx_variant TARGET GEN_DIR CFG_DIR)
set_property(SOURCE ${mmx_generated} APPEND PROPERTY OBJECT_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_overrides.py
${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_renderer_capture.py
${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_password_hooks.py)
${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_password_hooks.py
${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_zero_hooks.py)
add_custom_target(${TARGET}_widescreen_overrides
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_overrides.py
--gen-dir ${GEN_DIR}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_renderer_capture.py
--gen-dir ${GEN_DIR}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_password_hooks.py
--gen-dir ${GEN_DIR}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/apply_zero_hooks.py
--gen-dir ${GEN_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Applying Mega Man X generated-code mod hooks" VERBATIM)
add_dependencies(${TARGET} ${TARGET}_widescreen_overrides)
Expand All @@ -97,6 +101,13 @@ add_mmx_variant(RockmanXSNESRecomp "${CMAKE_CURRENT_SOURCE_DIR}/variants/jp/gen"

include(CTest)
if(BUILD_TESTING)
add_executable(mmx_source_assets_test tests/mmx_source_assets_test.cpp src/mmx_source_assets.cpp
${SNESRECOMP_ROOT}/runner/src/sha256.c)
target_include_directories(mmx_source_assets_test PRIVATE src ${SNESRECOMP_ROOT}/runner/src)
target_compile_features(mmx_source_assets_test PRIVATE cxx_std_17)
add_executable(mmx_zero_test tests/mmx_zero_test.c src/mmx_zero.c)
target_include_directories(mmx_zero_test PRIVATE src)
add_test(NAME mmx_zero_character COMMAND mmx_zero_test)
add_executable(mmx_password_save_test tests/mmx_password_save_test.cpp src/mmx_password_save.cpp)
target_include_directories(mmx_password_save_test PRIVATE src)
target_compile_features(mmx_password_save_test PRIVATE cxx_std_17)
Expand All @@ -107,12 +118,12 @@ if(BUILD_TESTING)
add_executable(mmx_wide_policy_test tests/mmx_wide_policy_test.c src/mmx_wide_policy.c)
target_include_directories(mmx_wide_policy_test PRIVATE src)
add_test(NAME mmx_wide_policy COMMAND mmx_wide_policy_test)
add_executable(mmx_renderer_test tests/mmx_renderer_test.c src/mmx_renderer.c src/mmx_render_assets.c src/mmx_display.c src/mmx_wide_policy.c)
add_executable(mmx_renderer_test tests/mmx_renderer_test.c src/mmx_renderer.c src/mmx_render_assets.c src/mmx_display.c src/mmx_wide_policy.c src/mmx_zero.c)
target_include_directories(mmx_renderer_test PRIVATE src ${SNESRECOMP_ROOT}/runner/src)
add_test(NAME mmx_custom_renderer COMMAND mmx_renderer_test)
add_executable(mmx_render_capture tools/mmx_render_capture.c src/mmx_renderer.c src/mmx_render_assets.c src/mmx_display.c src/mmx_wide_policy.c)
add_executable(mmx_render_capture tools/mmx_render_capture.c src/mmx_renderer.c src/mmx_render_assets.c src/mmx_display.c src/mmx_wide_policy.c src/mmx_zero.c)
target_include_directories(mmx_render_capture PRIVATE src ${SNESRECOMP_ROOT}/runner/src)
foreach(mmx_test mmx_display_test mmx_wide_policy_test mmx_renderer_test mmx_password_save_test)
foreach(mmx_test mmx_display_test mmx_wide_policy_test mmx_renderer_test mmx_password_save_test mmx_zero_test)
if(MSVC)
target_compile_options(${mmx_test} PRIVATE /UNDEBUG)
else()
Expand All @@ -138,6 +149,10 @@ include(${SNESRECOMP_ROOT}/runner/cmake/mmx_state_tests.cmake)
snesrecomp_target_mmx_state_tests(MegaManXSNESRecomp "${CMAKE_CURRENT_SOURCE_DIR}/src/main.c")

if(TARGET mmx_state_tests)
add_dependencies(mmx_state_tests MegaManXSNESRecomp_widescreen_overrides)
target_compile_definitions(mmx_state_tests PRIVATE
MMX_ZERO_COMBAT_REFERENCE_DEFAULT="${CMAKE_CURRENT_SOURCE_DIR}/tests/data/zero_x3_combat.csv"
MMX_ZERO_MOTION_REFERENCE_DEFAULT="${CMAKE_CURRENT_SOURCE_DIR}/tests/data/zero_x3_motion.csv")
get_target_property(mmx_state_sources mmx_state_tests SOURCES)
list(REMOVE_ITEM mmx_state_sources "${SNESRECOMP_ROOT}/runner/tests/mmx_state_runtime.c")
set_property(TARGET mmx_state_tests PROPERTY SOURCES
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ image. It is disabled by default and enabled from the launcher's **Mods** page.
See [Adaptive widescreen support](#adaptive-widescreen-support) for
availability and controls.

The disabled-by-default **X3 Zero 0.0.1 mod** adds playable Zero, his
buster/saber combo, and grounded Select exchange with X. Both characters have
separate current health; pickups heal only the active character. Enable it in
**Mods** and select your **Mega Man X3 USA ROM**. Assets are prepared locally
automatically. See [setup and release notes](docs/zero-0.0.1.md) and the
[technical handoff](docs/zero-port.md). X2/X3 boss weapons are a separate follow-up.

<p align="center">
<img src="docs/screenshots/widescreen-ocean.png" width="32%" alt="Mega Man X experimental widescreen rendering in an ocean scene">
<img src="docs/screenshots/widescreen-highway.png" width="32%" alt="Mega Man X experimental widescreen rendering on the opening highway stage">
Expand Down
46 changes: 46 additions & 0 deletions docs/mod-source-roms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Public mods: user-supplied source ROMs

The user supplies the supported X1 ROM and an original USA X3 ROM for Zero.
The later X2/X3 weapon expansion requires those games' respective ROMs. This
contract covers all imported sprites, palettes, animation records, collision
data, badges and future audio. No extracted content belongs in a public package.

## Zero 0.0.1 setup

The launcher asks for a **ROM**, never an extracted cache. Its X3 file resource
uses shared key `megaman-x.source.x3`, reserved also for the weapon follow-up.
The provider validates normalized SHA-256, accepts 512-byte copier headers,
persists the path locally, and rejects unsupported ROMs before activation.

Supported X3 USA normalized SHA-256:
`65b03268afac296330e8ff8d60dd0825879e13ed658b37713c034a3bd074f1d7`.

`src/mmx_source_assets.cpp` repeats validation and extracts Zero assets natively
from the selected ROM, atomically publishing `cache/mmx-source/x3-zero-v6.bin`
beside the executable. Failed validation leaves the previous cache untouched.
Activation always requires the selected ROM; it never falls back to developer
assets. Runtime extraction requires no Python or additional download.

The native output is byte-for-byte equivalent to `tools/extract_zero.py`.
The cache contains the original 117 body poses, 21 saber body poses, 14 saber
blade poses, palettes, four HUD tiles, animation records, muzzle offsets and
saber bounds. [Zero's source notebook](zero-port.md) records the ROM addresses.
Code stores addresses and integration logic; imported bytes stay in the cache.

## Release staging

`tools/make_release.ps1` stages the executable, launcher assets, default config,
README, Zero release notes and checked-in mod catalog. It does not copy the
runtime's writable `mods` directory, key bindings or private caches. Catalog
staging rejects untracked files, `state.toml`, ROMs, binary asset caches, saves
and compositor captures. Runtime DLL dependencies are audited from PE imports.
ROMs, extracted cache files and private fixtures are used only for local tests.

## Follow-up weapons contract

Both weapon packs live on the separate `feat/x2-x3-weapons` branch. X3 weapons
reuse `megaman-x.source.x3`; changing it in either feature updates the other.
X2 uses `megaman-x.source.x2` and normalized USA SHA-256
`f3246755f608a1e1dc9c848b61da3b824c7853b29b3be40df6fc7f2793a887ed`.
Both remain subject to the same private extraction and release audit rules.
Co-op is a later mutually exclusive mod; see the [roadmap](zero-weapons-coop-roadmap.md).
44 changes: 44 additions & 0 deletions docs/zero-0.0.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# X3 Zero mod 0.0.1

This optional mod adds X3 Zero to Mega Man X (USA Rev 1). Enable **X3 Zero**
in the launcher's Mods page, select your **Mega Man X3 USA ROM** (`.sfc` or
`.smc`), then launch. The game validates the ROM and prepares the required
assets locally. No Python, manual extraction, or asset download is needed.
The mod is disabled by default. It requires this release's host executable;
copying its manifest into an older host does not install the native plugin.

## Playing

- Start as Zero, using X1's normal controls and earned special weapons.
- Hold fire for progressively stronger charges: small green shot, full shot,
two-shot sequence, then two shots plus saber at maximum charge. Press fire
again for each stored follow-up. The saber waits for preceding beams to finish.
- Press **Select** while standing idle on solid ground to exchange X and Zero.
Original blue/red teleport effects play while stage simulation pauses.
- Each character has separate current health. Health pickups and subtanks heal
the active character only. Heart tanks increase the shared maximum.
- Active-character death loses a life normally; respawn refills both pools.
- X1 charged special weapons still require the arm upgrade. X keeps native X1
abilities/progression. Zero has his basic dash and buster/saber abilities.

Zero uses original X3 body, saber, teleport and health badge artwork, including
the pause portrait and title cursor. The title selection shot still works.
The 1-up head deliberately remains X's original artwork.

## Scope and known limits

This is the first playable mod release, not a claim of exact X3 emulation or
complete campaign validation. It retains X1's buster projectile engine and
audio; original X3 projectile/effect/audio fidelity remains follow-up work.
Some scripted poses, ride armor, capsules and unusual terrain still need
broader playtesting. Separate mod save profiles are not yet enforced: keep
your modded saves separate from stock and weapon-development saves.

**X2/X3 boss weapons and simultaneous co-op are not included.** Weapons are
developed on `feat/x2-x3-weapons`; co-op follows only after weapons finish.
The source-ROM selection uses a shared X3 key so the future X3 weapons option
can reuse it automatically. X2 weapons will require their own X2 ROM.

The download includes no ROMs, extracted art, local ROM paths, saved games or
asset caches. See [source provenance](mod-source-roms.md) and the complete
[technical handoff](zero-port.md) for integration details and validation.
Loading
Loading