Skip to content
Open
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
51 changes: 47 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ endif()
if(NOT DEFINED SNESRECOMP_ROOT)
set(SNESRECOMP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/snesrecomp)
endif()
if(APPLE AND NOT DEFINED SNESRECOMP_SDL_BACKEND)
# The DKC1 macOS frontend currently uses SDL2's queued-audio API. Keep
# this project on the engine's explicit compatibility backend until its
# SDL3 audio-stream equivalent is implemented and validated.
if(UNIX AND NOT DEFINED SNESRECOMP_SDL_BACKEND)
# The DKC1 SDL frontend currently uses SDL2's queued-audio API. Keep this
# project on the engine's explicit compatibility backend until its SDL3
# audio-stream equivalent is implemented and validated.
set(SNESRECOMP_SDL_BACKEND "SDL2" CACHE STRING
"Desktop SDL backend (SDL3 or SDL2)")
endif()
Expand Down Expand Up @@ -85,6 +85,9 @@ target_compile_definitions(dkc1_snesrecomp_headless PRIVATE
SNESRECOMP_REVERSE_DEBUG=0
SNESRECOMP_EXTERNAL_RAM_ROUTINE_GUARDS=1
SYSTEM_VOLUME_MIXER_AVAILABLE=0)
if(UNIX)
target_compile_definitions(dkc1_snesrecomp_headless PRIVATE _GNU_SOURCE)
endif()
if(MSVC)
# Do not use /MP here. This generated-source workload has reproduced an
# MSVC race where cl.exe returns before every bank object is available to
Expand Down Expand Up @@ -147,3 +150,43 @@ if(APPLE)
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
endif()

if(UNIX AND NOT APPLE)
if(NOT SNESRECOMP_SDL_BACKEND STREQUAL "SDL2")
message(FATAL_ERROR
"The DKC1 Linux frontend currently requires "
"-DSNESRECOMP_SDL_BACKEND=SDL2")
endif()

add_executable(dkc1_snesrecomp_sdl
${DKC1_ENGINE_SOURCES}
${DKC1_SNESRECOMP_GEN_SOURCES}
${DKC1_RUNNER_SOURCES}
runner/headless_host.c
runner/dkc1_haptics.c
runner/dkc1_msu1.c
runner/sdl_host.c
runner/nonapple_sdl_platform.c)
target_include_directories(dkc1_snesrecomp_sdl PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/runner
${CMAKE_CURRENT_SOURCE_DIR}/recomp
${SNESRECOMP_RUNNER_INCLUDE_DIRS}
${SNESRECOMP_ROOT}/runner/src/desktop)
target_compile_definitions(dkc1_snesrecomp_sdl PRIVATE
_GNU_SOURCE
SNESRECOMP_TRACE=0
SNESRECOMP_REVERSE_DEBUG=0
SNESRECOMP_EXTERNAL_RAM_ROUTINE_GUARDS=1
SYSTEM_VOLUME_MIXER_AVAILABLE=0
DKC1_BUILD_COMMIT="${DKC1_BUILD_COMMIT}"
DKC1_BUILD_CONFIG="linux")
target_compile_options(dkc1_snesrecomp_sdl PRIVATE
-Wno-unused-function)
target_link_libraries(dkc1_snesrecomp_sdl PRIVATE
${SNESRECOMP_RUNNER_LIBRARIES})
find_package(Threads REQUIRED)
target_link_libraries(dkc1_snesrecomp_sdl PRIVATE Threads::Threads)
snesrecomp_target_sdl(dkc1_snesrecomp_sdl)
set_target_properties(dkc1_snesrecomp_sdl PROPERTIES
OUTPUT_NAME "DKC1Recomp")
endif()
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ framework — following the working [`DKC2Recomp`](https://github.com/Nicktendon
project (same Rare engine family) as the template.

**Status: playable bring-up.** The project has 100% statically generated game
code, a headless validation host, playable Windows and native macOS hosts,
code, a headless validation host, playable Windows, Linux SDL2, and native macOS hosts,
continuous audio, and an opt-in 342x224 widescreen presentation path. See `docs/BRINGUP.md` for the
chronological bring-up record and `docs/WIDESCREEN.md` for the widescreen
architecture, ported SuperZSNES findings, validation, and limitations.
Expand Down Expand Up @@ -100,6 +100,33 @@ wall, black past the wall, or the earlier inward clamp. The headless validator a
count and supports deterministic input playback and private frame/state
captures; its environment variables are documented in `docs/BRINGUP.md`.

### Linux

Install a C compiler, CMake, Ninja, Python 3, Cargo, and the SDL2 development
package. On Debian/Ubuntu:

```sh
sudo apt install build-essential cmake ninja-build python3 cargo libsdl2-dev
```

Then build both the playable SDL2 host and the headless validator. The first
build uses the verified ROM to generate private ignored C sources; the ROM is
never copied into the repository or executable.

```sh
git submodule update --init --recursive
./build_linux.sh "/path/to/Donkey Kong Country (USA).sfc"
./build/linux/DKC1Recomp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The documented Linux build-and-run sequence exits immediately because the executable has no ROM path. Pass the ROM path to the executable as well, or set DKC1_ROM for the run command.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 119:

<comment>The documented Linux build-and-run sequence exits immediately because the executable has no ROM path. Pass the ROM path to the executable as well, or set `DKC1_ROM` for the run command.</comment>

<file context>
@@ -100,6 +100,33 @@ wall, black past the wall, or the earlier inward clamp. The headless validator a
+```sh
+git submodule update --init --recursive
+./build_linux.sh "/path/to/Donkey Kong Country (USA).sfc"
+./build/linux/DKC1Recomp
+```
+
</file context>
Suggested change
./build/linux/DKC1Recomp
./build/linux/DKC1Recomp "/path/to/Donkey Kong Country (USA).sfc"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed

```

The Linux frontend accepts the ROM path as its first argument or through
`DKC1_ROM`. It uses SDL2 for the window, keyboard/controller input, queued
audio, fullscreen, and rendering. Native macOS menus and file pickers are not
present, so Linux-only options use the existing environment variables. Keys
match the other visible hosts: arrows, Z/X/S/A, Q/W, Return, Right Shift,
F7/F8 pause and step, F9 export, F11/F12 quick save/load, Alt-Return
fullscreen, and Escape to leave fullscreen or quit.

### macOS

Install CMake, Ninja, and SDL2, then provide the same verified ROM the first
Expand Down
44 changes: 44 additions & 0 deletions build_linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ "$(uname -s)" != "Linux" ]]; then
echo "error: build_linux.sh must run on Linux" >&2
exit 2
fi

repo_dir="$(cd "$(dirname "$0")" && pwd)"
build_dir="$repo_dir/build/linux"
rom_path="${1:-${DKC1_ROM:-}}"

for tool in cc cmake ninja sdl2-config python3; do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When private sources are absent and no prebuilt analyzer exists, first-time generation requires Cargo, but this dependency check omits it. Check Cargo or the prebuilt analyzer before generation and include the missing prerequisite in the error guidance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At build_linux.sh, line 13:

<comment>When private sources are absent and no prebuilt analyzer exists, first-time generation requires Cargo, but this dependency check omits it. Check Cargo or the prebuilt analyzer before generation and include the missing prerequisite in the error guidance.</comment>

<file context>
@@ -0,0 +1,44 @@
+build_dir="$repo_dir/build/linux"
+rom_path="${1:-${DKC1_ROM:-}}"
+
+for tool in cc cmake ninja sdl2-config python3; do
+  if ! command -v "$tool" >/dev/null 2>&1; then
+    echo "error: missing required tool: $tool" >&2
</file context>

if ! command -v "$tool" >/dev/null 2>&1; then
echo "error: missing required tool: $tool" >&2
echo "Install a C compiler, CMake, Ninja, Python 3, and SDL2 development files." >&2
exit 2
fi
done

if [[ ! -f "$repo_dir/snesrecomp/runner/runner.cmake" ]]; then
echo "error: snesrecomp is not initialized" >&2
echo "run: git submodule update --init --recursive" >&2
exit 2
fi

if ! compgen -G "$repo_dir/generated/snesrecomp/*.c" >/dev/null; then
if [[ -z "$rom_path" ]]; then
echo "error: private generated sources are missing" >&2
echo "usage: ./build_linux.sh '/path/to/Donkey Kong Country (USA).sfc'" >&2
exit 2
fi
python3 "$repo_dir/scripts/generate_snesrecomp.py" --rom "$rom_path"
fi

cmake -S "$repo_dir" -B "$build_dir" -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DSNESRECOMP_SDL_BACKEND=SDL2 \
-DCMAKE_PREFIX_PATH="$(sdl2-config --prefix)"
cmake --build "$build_dir" \
--target dkc1_snesrecomp_sdl dkc1_snesrecomp_headless --parallel

echo "LINUX_BUILD_OK"
echo "$build_dir/DKC1Recomp"
108 changes: 108 additions & 0 deletions runner/nonapple_sdl_platform.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "macos_file_picker.h"
#include "macos_metal_presenter.h"

#include <stddef.h>

/* The SDL frontend is shared with macOS, where these entry points provide
* native menus, file pickers, preferences, and display-linked Metal output.
* Linux deliberately keeps the command-line/environment configuration and
* ordinary SDL renderer path, so its platform adapters are inert. */

char *Dkc1MacChooseRom(void) { return NULL; }
char *Dkc1MacChooseBabyKongRom(void) { return NULL; }
char *Dkc1MacSavedBabyKongRom(void) { return NULL; }
void Dkc1MacSetBabyKongRom(const char *path) { (void)path; }
int Dkc1MacSavedBabyKongEnabled(void) { return 0; }
void Dkc1MacSetBabyKongEnabled(int enabled) { (void)enabled; }

char *Dkc1MacChooseMsu1(void) { return NULL; }
char *Dkc1MacSavedMsu1(void) { return NULL; }
void Dkc1MacClearMsu1(void) {}

Dkc1MacFullscreenScaling Dkc1MacSavedFullscreenScaling(void) {
return kDkc1MacFullscreenSharpBilinear;
}

void Dkc1MacSetFullscreenScaling(Dkc1MacFullscreenScaling scaling) {
(void)scaling;
}

Dkc1EdgePolicy Dkc1MacSavedWidescreenEdge(void) {
return kDkc1EdgeGlide;
}

void Dkc1MacSetWidescreenEdge(Dkc1EdgePolicy policy) { (void)policy; }

void Dkc1MacInstallMenu(void) {}

void Dkc1MacUpdateMenuState(int paused, int fullscreen,
Dkc1MacFullscreenScaling fullscreen_scaling,
Dkc1VideoAspect aspect, Dkc1EdgePolicy edge,
unsigned char layer_mask, int provenance,
int replacement_music, int baby_kong_enabled,
int baby_kong_ready) {
(void)paused;
(void)fullscreen;
(void)fullscreen_scaling;
(void)aspect;
(void)edge;
(void)layer_mask;
(void)provenance;
(void)replacement_music;
(void)baby_kong_enabled;
(void)baby_kong_ready;
}

int Dkc1MacDisplayLinkStart(void *native_window, double preferred_fps) {
(void)native_window;
(void)preferred_fps;
return 0;
}

int Dkc1MacDisplayLinkWait(unsigned long long after_callback_number,
double timeout_seconds, double *timestamp,
double *target_timestamp, double *duration,
unsigned long long *callback_number) {
(void)after_callback_number;
(void)timeout_seconds;
(void)timestamp;
(void)target_timestamp;
(void)duration;
(void)callback_number;
return 0;
}

void Dkc1MacDisplayLinkStop(void) {}

int Dkc1MacMetalPresenterStart(void *native_window, double preferred_hz,
Dkc1MacFullscreenScaling scaling,
int fullscreen) {
(void)native_window;
(void)preferred_hz;
(void)scaling;
(void)fullscreen;
return 0;
}

void Dkc1MacMetalPresenterQueueFrame(
const uint32_t *pixels, int width, int height, int presentation_width,
const Dkc1MacPresentationFrameInfo *info) {
(void)pixels;
(void)width;
(void)height;
(void)presentation_width;
(void)info;
}

void Dkc1MacMetalPresenterSetGeometry(int presentation_width,
int fullscreen) {
(void)presentation_width;
(void)fullscreen;
}

void Dkc1MacMetalPresenterSetScaling(Dkc1MacFullscreenScaling scaling) {
(void)scaling;
}

void Dkc1MacMetalPresenterSetActive(int active) { (void)active; }
void Dkc1MacMetalPresenterStop(void) {}
Loading