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
21 changes: 21 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Desktop host
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install desktop dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build libsdl2-dev libgl1-mesa-dev
- name: Configure ROM-free setup host
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSNESRECOMP_SDL_BACKEND=SDL2 -DSNESRECOMP_SETUP_HOST=ON
- name: Compile shared desktop host and game adapters
run: cmake --build build --target MegaManX3SNESRecomp --parallel 4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ CMakeCache.txt
/host_report*.json
/*.dmp
/tools/__pycache__/

# Shared desktop host local preferences and reports
/config.local.ini
/keybinds.ini
/tier2_*.json
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "recomp-ui"]
path = recomp-ui
url = https://github.com/mstan/recomp-ui.git
url = https://github.com/RetroPortingToolKit/recomp-ui.git
[submodule "snesrecomp"]
path = snesrecomp
url = https://github.com/mstan/snesrecomp.git
url = https://github.com/RetroPortingToolKit/snesrecomp.git
138 changes: 27 additions & 111 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,128 +1,44 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.20)
project(MegaManX3SNESRecomp C CXX)

# Mega Man X3 (USA) — static recompilation via snesrecomp.
#
# Prerequisites:
# * snesrecomp + recomp-ui at the repo root (submodules, or junctions in a
# local multi-game working tree);
# * generated C in src/gen/ (`bash tools/regen.sh` with a verified
# mmx3.sfc staged at the repo root);
# * SDL3 (or SDL2 via -DSNESRECOMP_SDL_BACKEND=SDL2) + OpenGL.

set(CMAKE_C_STANDARD 11)

set(SNESRECOMP_BUILD_VERSION "dev" CACHE STRING
"Version reported in crash diagnostics and network metadata")

set(SNESRECOMP_ROOT "${CMAKE_SOURCE_DIR}/snesrecomp" CACHE PATH
"Path to the snesrecomp checkout used by this game")
set(SNESRECOMP_ENABLE_MODS ON CACHE BOOL
"Enable MMX3's built-in mod catalog and SNES mod packages" FORCE)
set(SNESRECOMP_SDL_BACKEND "SDL3" CACHE STRING
"Desktop SDL backend (SDL3 or SDL2)")
include(${SNESRECOMP_ROOT}/runner/runner.cmake)

find_package(OpenGL REQUIRED)

file(GLOB X3_GEN_SOURCES CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/gen/*.c)
if(NOT X3_GEN_SOURCES)
message(FATAL_ERROR
"src/gen/ is empty — run `bash tools/regen.sh` with a verified "
"mmx3.sfc at the repo root before building.")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" _version)
string(STRIP "${_version}" GAME_RELEASE_VERSION)
set(SNESRECOMP_BUILD_VERSION "" CACHE STRING "Build stamp (empty: Release uses VERSION, otherwise dev)")
if(SNESRECOMP_BUILD_VERSION STREQUAL "")
set(SNESRECOMP_BUILD_VERSION "$<IF:$<CONFIG:Release>,${GAME_RELEASE_VERSION},dev>")
endif()
set(SNESRECOMP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/snesrecomp" CACHE PATH "snesrecomp checkout")
set(RECOMP_UI_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/recomp-ui" CACHE PATH "recomp-ui checkout")
set(SNESRECOMP_ENABLE_MODS ON CACHE BOOL "Shared mod package runtime" FORCE)
include(${SNESRECOMP_ROOT}/runner/runner.cmake)

add_executable(MegaManX3SNESRecomp
${SNESRECOMP_RUNNER_SOURCES}
# game-specific host runtime
src/main.c
src/opengl.c
src/gen_stubs.c
src/x3_display.c
src/x3_cpu_infra.c
src/x3_rtl.c
src/x3_spc_player.c
src/mods/x3_widescreen_plugin.c

# OpenGL function loader
third_party/gl_core/gl_core_3_1.c
# recompiled banks (generated, gitignored)
${X3_GEN_SOURCES}
)
set_property(SOURCE src/main.c APPEND PROPERTY COMPILE_DEFINITIONS
SNESRECOMP_BUILD_VERSION="${SNESRECOMP_BUILD_VERSION}")

src/main.c src/gen_stubs.c src/x3_display.c src/x3_cpu_infra.c
src/x3_rtl.c src/x3_spc_player.c src/mods/x3_widescreen_plugin.c)
target_include_directories(MegaManX3SNESRecomp PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/recomp
${SNESRECOMP_RUNNER_INCLUDE_DIRS}
)
snesrecomp_target_fiber_compat(MegaManX3SNESRecomp)
snesrecomp_target_post_mortem(MegaManX3SNESRecomp TIER2)
snesrecomp_target_mmx_config(MegaManX3SNESRecomp)
snesrecomp_target_glsl_shader(MegaManX3SNESRecomp)

# prod-vs-debug via the single SNESRECOMP_ENABLE_TRACE switch (runner.cmake,
# default OFF). Prod: no debug_server.c / TCP server / rings.
if(SNESRECOMP_ENABLE_TRACE)
set(_X3_TRACE 1)
else()
set(_X3_TRACE 0)
endif()

${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/recomp ${SNESRECOMP_RUNNER_INCLUDE_DIRS})
target_compile_definitions(MegaManX3SNESRecomp PRIVATE
SMW_DISABLE_LM=1
SYSTEM_VOLUME_MIXER_AVAILABLE=0 # Win32 WASAPI volume control excluded
SNESRECOMP_TRACE=${_X3_TRACE}
SNESRECOMP_REVERSE_DEBUG=0 # raw WRAM stores; matches non-debug gen C
)

# The recompiled C is machine-generated; quiet its warnings and tolerate the
# K&R-style implicit declarations clang rejects by default. Keep the
# implicit-decl flag C-only so the launcher C++ TUs stay clean.
SMW_DISABLE_LM=1 SYSTEM_VOLUME_MIXER_AVAILABLE=0
SNESRECOMP_TRACE=$<BOOL:${SNESRECOMP_ENABLE_TRACE}> SNESRECOMP_REVERSE_DEBUG=0)
set_property(SOURCE src/main.c APPEND PROPERTY COMPILE_DEFINITIONS
SNESRECOMP_BUILD_VERSION="${SNESRECOMP_BUILD_VERSION}")
if(NOT MSVC)
target_compile_options(MegaManX3SNESRecomp PRIVATE -w)
target_compile_options(MegaManX3SNESRecomp PRIVATE
$<$<COMPILE_LANGUAGE:C>:-Wno-implicit-function-declaration>)
endif()

target_link_libraries(MegaManX3SNESRecomp PRIVATE
OpenGL::GL
${SNESRECOMP_RUNNER_LIBRARIES}
)

# SDL3 by default, SDL2 via -DSNESRECOMP_SDL_BACKEND=SDL2. Replaces the game's
# own find_package(SDL2)/libSDL2.dll.a wiring: recomp-ui now compiles its SDL3
# backend unconditionally, so hardcoding SDL2 here put two SDL majors in one
# binary. No SDLmain either way (the host provides a plain main()).
target_link_libraries(MegaManX3SNESRecomp PRIVATE ${SNESRECOMP_RUNNER_LIBRARIES})
snesrecomp_target_sdl(MegaManX3SNESRecomp)
snesrecomp_target_fiber_compat(MegaManX3SNESRecomp)
snesrecomp_target_desktop_host(MegaManX3SNESRecomp TIER2)
snesrecomp_target_generated_code(MegaManX3SNESRecomp ${CMAKE_CURRENT_SOURCE_DIR}/src/gen)
snesrecomp_target_mod_catalog(MegaManX3SNESRecomp ${CMAKE_CURRENT_SOURCE_DIR}/mods/preloaded)

if(WIN32)
target_link_libraries(MegaManX3SNESRecomp PRIVATE ws2_32)
endif()

# Shared Dear ImGui pre-boot launcher (recomp-ui submodule/junction).
set(RECOMP_UI_ROOT "${CMAKE_SOURCE_DIR}/recomp-ui" CACHE PATH
"Root directory of the recomp-ui launcher repo" FORCE)
if(NOT EXISTS "${RECOMP_UI_ROOT}/recomp_ui.cmake")
message(FATAL_ERROR
"recomp-ui missing at ${RECOMP_UI_ROOT}.\n"
"Run: git submodule update --init --recursive recomp-ui")
endif()
include(${RECOMP_UI_ROOT}/recomp_ui.cmake)
recomp_target_launcher_ui(MegaManX3SNESRecomp
CONSOLE snes
BOXART ${CMAKE_SOURCE_DIR}/recomp/launcher/boxart.tga)
recomp_target_launcher_ui(MegaManX3SNESRecomp CONSOLE snes
BOXART ${CMAKE_CURRENT_SOURCE_DIR}/recomp/launcher/boxart.tga)

# Preload the game-owned catalog. Every feature remains disabled until the
# player enables it in Mods; package plugin IDs resolve only to implementations
# statically linked above.
set(X3_PRELOADED_MODS "${CMAKE_CURRENT_SOURCE_DIR}/mods/preloaded")
if(EXISTS "${X3_PRELOADED_MODS}/packages")
add_custom_command(TARGET MegaManX3SNESRecomp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${X3_PRELOADED_MODS}"
"$<TARGET_FILE_DIR:MegaManX3SNESRecomp>/mods"
COMMENT "Preloading the Mega Man X3 mod catalog")
endif()
include(${SNESRECOMP_ROOT}/runner/cmake/mmx_state_tests.cmake)
snesrecomp_target_mmx_state_tests(MegaManX3SNESRecomp ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c)
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@ vendored dependencies under `third_party/` retain their own licenses.

The game ROM and any data extracted from it are **not** in this repo and are
not licensed for redistribution.

Save-state and rewind controls, compatibility and focused verification: [SAVE_STATES.md](docs/SAVE_STATES.md).
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.3
33 changes: 33 additions & 0 deletions docs/SAVE_STATES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Save states and rewind

F7 opens the save-state browser; F8 opens rewind. In recomp-ui, use the
Hotkeys page to rebind or clear either action, including modifier keys.
Existing F7/F8 quick-load defaults migrate to F11/F12; Shift+F1 through
Shift+F10 still save directly. User config.ini and keybinds.ini stay local.

The save browser has 12 slots: arrows select, X loads, S saves, Escape
closes. On a controller, Select+R opens it; Up/Down select, A loads, X saves,
and B closes. Rewind opens with Select+R3 by default; [Controller]
RewindGesture can change or disable that gesture. Left/Right select a frame,
A or Enter restores it, and B or Escape cancels. The guest stays frozen
while a menu is open. Rewind keeps about six seconds by default; the shared
SNESRECOMP_REWIND_DEPTH and SNESRECOMP_REWIND_INTERVAL settings control it.

New states include the title's execution cursor and the framework's complete
CPU, interpreter, APU timing and PPU rollback residue. States are intended
for matching runtime builds; old states retain the earlier compatibility
path. A file load or reset starts a new rewind history. There is no netplay
menu for these single-player games.

## Focused verification

Configure with -DMMX_STATE_TESTS=ON, build mmx_state_tests, then run the
framework's runner/tests/run_mmx_state_tests.py with --exe pointing to that
test executable and --rom pointing to your own verified ROM. It uses a
temporary save directory and checks binding migration, recomp-ui rebind and
clear, complete memory restore, deterministic replay, slot save, rewind,
and loading the file in a new process.

CI builds a ROM-free setup host using SNESRECOMP_SETUP_HOST=ON. Playable
builds use tools/regen.sh and the same shared generated-code, desktop-host,
SDL, OpenGL and mod-catalog CMake helpers as the current project template.
2 changes: 1 addition & 1 deletion recomp-ui
Submodule recomp-ui updated 75 files
+158 −2 CMakeLists.txt
+21 −0 LICENSE
+30 −0 README.md
+11 −0 assets/common/img/NOTICE.md
+ assets/common/img/flags.png
+64 −0 cmake/recomp_gl.cmake
+2 −1 cmake/recomp_ui_assets.cmake
+18 −0 docs/ARCHITECTURE.md
+227 −0 docs/HOST_NETPLAY.md
+19 −0 docs/SBI_INPUT.md
+66 −3 recomp_ui.cmake
+7,143 −1,066 src/common/backends/imgui/launcher_imgui.cpp
+246 −0 src/common/emoji/recomp_emoji.c
+75 −0 src/common/emoji/recomp_emoji.h
+149 −0 src/common/emoji/recomp_emoji_flags.c
+264 −0 src/common/emoji/recomp_emoji_freetype.c
+152 −0 src/common/emoji/recomp_emoji_win32.cpp
+183 −7 src/common/ips_patch.c
+24 −1 src/common/ips_patch.h
+30 −11 src/common/keybinds.c
+821 −21 src/common/launcher_binds.c
+63 −6 src/common/launcher_binds.h
+35 −1 src/common/launcher_debug.c
+2 −1 src/common/launcher_debug.h
+123 −8 src/common/launcher_files.c
+22 −2 src/common/launcher_files.h
+15 −0 src/common/launcher_gl.c
+13 −0 src/common/launcher_gl.h
+228 −0 src/common/launcher_i18n.cpp
+4 −0 src/common/launcher_i18n.h
+50 −0 src/common/launcher_input.c
+9 −0 src/common/launcher_input.h
+1,946 −91 src/common/launcher_model.c
+391 −17 src/common/launcher_model.h
+6 −1 src/common/launcher_ng_capi.c
+6 −6 src/common/launcher_panels.h
+12 −0 src/common/launcher_platform.h
+72 −0 src/common/launcher_platform_sdl2.c
+72 −1 src/common/launcher_platform_sdl3.c
+16 −0 src/common/launcher_sdlcompat.h
+13 −1 src/common/launcher_system_types.h
+571 −0 src/common/pad_binds.c
+114 −0 src/common/pad_binds.h
+83 −0 src/common/recomp_frame_blend.c
+9 −0 src/common/recomp_runtime_settings.c
+2 −2 src/consoles/gba/gba_profile.h
+1 −2 src/consoles/genesis/genesis_profile.h
+34 −3 src/consoles/nds/nds_profile.h
+98 −3 src/consoles/psx/psx_binds.c
+10 −0 src/consoles/psx/psx_binds.h
+118 −0 src/consoles/psx/psx_pad_binds.c
+76 −0 src/consoles/psx/psx_pad_binds.h
+51 −14 src/consoles/psx/psx_profile.h
+169 −0 src/consoles/snes/snes_pad_binds.c
+80 −0 src/consoles/snes/snes_pad_binds.h
+38 −1 src/consoles/snes/snes_profile.h
+440 −0 src/proto_main.c
+95 −0 src/recomp_frame_blend.h
+986 −24 src/recomp_launcher.h
+2 −0 src/recomp_runtime_ui.h
+8 −2 src/third_party/tinyfiledialogs.c
+2 −0 tests/asset_manifest_test.cmake
+1 −0 tests/asset_staging_test.cmake
+153 −0 tests/ips_patch_test.c
+194 −0 tests/launcher_discs_test.c
+147 −0 tests/launcher_memcard_test.c
+239 −0 tests/launcher_pad_mode_test.c
+191 −0 tests/launcher_setup_bios_test.c
+180 −0 tests/launcher_setup_confirm_test.c
+86 −0 tests/launcher_video_pick_test.c
+25 −0 tests/nds_profile_test.c
+89 −0 tests/psx_binds_test.c
+26 −0 tests/psx_profile_test.c
+56 −0 tools/gen_flag_sheet.py
+123 −0 tools/rom_patch_tool.c
2 changes: 1 addition & 1 deletion snesrecomp
Submodule snesrecomp updated 307 files
2 changes: 2 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#define MMX_ROM_SHA256_HEX \
"65b03268afac296330e8ff8d60dd0825879e13ed658b37713c034a3bd074f1d7"
#define MMX_MOD_GAME_ID "megaman-x3-us"
#define MMX_ROM_FILE "mmx3.sfc"
#define MMX_ROM_CRC32_HEX "FA0FE671"
#define MMX_DEBUG_PORT 4384
#define MMX_HAS_BG3_SUB_OVERLAY 0
#define MMX_WIDESCREEN_STATUS_LINES \
Expand Down
Loading