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
15 changes: 14 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,22 @@ if(RETCOMM_HAVE_SDL3 AND RETCOMM_IMGUI_READY)
endif()
# A core running in the hub's window (Retro-Runtime docs/CORE_LINK.md), on
# every OS the link has a transport for: Linux, macOS, Windows.
target_sources(retro-hub PRIVATE src/hub/hub_play.cpp)
target_sources(retro-hub PRIVATE src/hub/hub_play.cpp src/hub/hub_core_settings.cpp)
target_link_libraries(retro-hub PRIVATE retro_corelink)
target_compile_definitions(retro-hub PRIVATE RETCOMM_HUB_HAVE_PLAY=1)
# The pieces of it that need no window: --describe parsing, bindings and
# option files, and the pad table they produce.
include(CTest)
if(BUILD_TESTING)
add_executable(retro-hub-settings-test tests/hub_core_settings_test.cpp
src/hub/hub_core_settings.cpp)
target_include_directories(retro-hub-settings-test PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(retro-hub-settings-test PRIVATE retcomm_core retro_corelink
${RETCOMM_HUB_SDL_TARGET})
add_test(NAME hub_core_settings COMMAND retro-hub-settings-test
${CMAKE_CURRENT_BINARY_DIR}/hub_core_settings_test.d)
endif()
install(TARGETS retro-hub RUNTIME DESTINATION bin)
# The hub's old name, retcomm-hub (until 2026-09-25), keeps working:
# shortcuts, Steam entries and older self-updaters relaunch it by name.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ cp config.example.json ~/.config/retcomm/config.json
To run one core directly in the hub's window, without the library (what a
port's dev build and a tool use), use Direct mode:
`retro-hub --run-core <core> [--package <shim>] --rom <image> [--title-dir <dir>]`.
Flags, files and runner lookup: [`docs/RELEASES.md`](docs/RELEASES.md#direct-mode).
It opens on the title's home page (Play, n64lle Settings, Mods); add
`--boot` to play at once. Flags, files and runner lookup:
[`docs/RELEASES.md`](docs/RELEASES.md#direct-mode).

`launch` stages disc/ROM/BIOS sidecars next to the install and prefers a companion
`.cue` for disc titles. Installs land under
Expand Down
Binary file added assets/controllers/pad_n64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions assets/src/pad_n64.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 35 additions & 8 deletions docs/RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ Retro-Runtime's archives do.

```sh
retro-hub --run-core <core> [--package <shim>] --rom <image> [--title-dir <dir>] \
[--tpak1-rom <gb rom>] [--tpak1-save <sav>] [--no-gl] [--opt key=value]...
[--tpak1-rom <gb rom>] [--tpak1-save <sav>] [--no-gl] [--opt key=value]... [--boot]
```

Direct mode boots straight into the core in the hub's window and exits when the
player closes it; no library pages, no setup wizard. It is built on every OS.
Direct mode runs one title in the hub's window; no library pages, no setup
wizard. It is built on every OS. Since revision 3 it opens on the title's
**home page**: Play, the core's settings page (n64lle Settings), Mods and Quit. Closing the game
returns to the page, and Quit leaves the app. `--boot` skips the page, plays at
once and exits when the player closes the game (what every revision before 3
did).
The session itself (menu, input, fault screen) is described in Retro-Runtime's
[`docs/CORE_LINK.md`](https://github.com/RetroPortingToolKit/Retro-Runtime/blob/main/docs/CORE_LINK.md), "Direct mode".

Expand All @@ -47,14 +51,34 @@ The session itself (menu, input, fault screen) is described in Retro-Runtime's
| `--title-dir <dir>` | 1 | The directory holding the title's `game.toml`. Default: the shim's directory with `--package`, else the core's. |
| `--tpak1-rom`, `--tpak1-save` | 1 | Transfer Pak cartridge and its save, port 1. |
| `--no-gl` | 1 | Do not lend the core GL. |
| `--opt key=value` | 1 | A core option; repeatable. |
| `--opt key=value` | 1 | A core option; repeatable. Wins over a value stored by Core Settings. |
| `--boot` | 3 | Play at once and exit with the game, skipping the home page. |

**Files.** Session logs go to `<data dir>/sessions/<stem>/` and saves to
`<data dir>/saves/<stem>/`, where `<stem>` is the title's: the shim's file stem
with `--package` (`pokemonstadium_game`), the core's otherwise
(`pokemonstadium_core`). A generic core is shared by every packaged title, so
it never names one.

The settings page -- the same one the library opens from an N64 platform
header's **n64lle Config** -- writes these, and every play path reads them
(`src/hub/hub_core_settings.hpp`):

| File | Scope |
|---|---|
| `<data dir>/platform/<platform>/input.ini` | the four controller seats (device and maps) and the stick deadzone, every title of the platform (the core's `.rcore.toml` `platforms`) |
| `<data dir>/platform/<platform>/options.ini` | option values for every title |
| `<data dir>/platform/<platform>/options/<stem>.ini` | one title's overrides; `--opt` wins over both |
| `<data dir>/platform/<platform>/core_description.txt` | the last `--describe`, so the page can label things with no core at hand |

The page is built from what the core declares, asked of the runner with
`retro-core-runner --describe` (Retro-Runtime `docs/CORE_RUNNER.md`), never
from a list compiled into the hub. A runner without `describe 1` in its
`--version` still plays; the page then uses the cached description, or says
the core's settings are unavailable and labels the pad chips generically. Mods reads the title dir's `mods/` tree, in either layout
`include/retcomm/mods.hpp` names (n64lle writes its selection to
`<title dir>/mods.toml`).

**The runner.** Direct mode finds `retro-core-runner` the way the launcher does
(`resolve_runner`, `src/update/runtime_update.cpp`), in this order:

Expand Down Expand Up @@ -136,8 +160,8 @@ Its shape follows Retro-Runtime's `runtime-manifest.json`:
"link_protocol": { "major": 1, "minor": 0 },
"rcore_abi": { "major": 0, "draft_revision": 5 },
"direct_mode": {
"cli_revision": 2,
"flags": ["--run-core", "--package", "--rom", "--title-dir", "--tpak1-rom", "--tpak1-save", "--no-gl", "--opt"],
"cli_revision": 3,
"flags": ["--run-core", "--package", "--rom", "--title-dir", "--tpak1-rom", "--tpak1-save", "--no-gl", "--opt", "--boot"],
"runner_lookup": "RETRO_CORE_RUNNER exe_dir/retro-core-runner data_dir/runtime/<version>/retro-core-runner"
},
"platforms": {
Expand Down Expand Up @@ -176,6 +200,9 @@ the bundled SDL3.
flags it passes, e.g. 2 for `--package`. Each revision still accepts every
earlier revision's command line; a change that breaks that will be called out
here.
- **Revision 3 changed what an earlier command line does:** it is still
accepted, but it opens the home page instead of playing at once. A tool that
needs the old behaviour passes `--boot`, and so needs revision 3.

## `retro-hub --version`

Expand All @@ -188,8 +215,8 @@ commit 8f538f7218855e94cdcba437a23245a4e1be22f8
link_protocol 1.0
rcore_abi_major 0
rcore_draft_revision 5
direct_mode 2
direct_mode_flags --run-core --package --rom --title-dir --tpak1-rom --tpak1-save --no-gl --opt
direct_mode 3
direct_mode_flags --run-core --package --rom --title-dir --tpak1-rom --tpak1-save --no-gl --opt --boot
runner_lookup RETRO_CORE_RUNNER exe_dir/retro-core-runner data_dir/runtime/<version>/retro-core-runner
```

Expand Down
23 changes: 23 additions & 0 deletions include/retcomm/mods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,19 @@ struct ModPackageInfo {
bool has_features() const { return !features.empty(); }
};

// Which engine's layout a mods tree is in. They differ in where manifests sit,
// what a manifest says, and which file holds the selection, so a write in the
// wrong one is a write nothing reads.
//
// Engine psxrecomp / snesrecomp: mods/<origin>/<id>/<version>/manifest.toml,
// [[feature]] arrays, selection in mods/state.toml
// N64lle n64lle guarded-write packages (n64lle docs/MODDING.md §5):
// mods/<origin>/<id>/manifest.toml with [target] rom_sha256 and
// [feature.<id>] tables, selection in <game>/mods.toml
enum class ModLayout { Engine, N64lle };

struct ModScanResult {
ModLayout layout = ModLayout::Engine;
std::vector<ModPackageInfo> packages;
// Manifests that exist but could not be read. Surfaced rather than
// dropped: a mod that silently fails to appear is a support question.
Expand Down Expand Up @@ -133,5 +145,16 @@ ModScanResult scan_game_mods(const fs::path& game_dir, const fs::path& install_r

const char* mod_origin_name(ModOrigin origin);

// The writes a mods page makes, in whichever layout `scan` found: one feature
// (feature_id empty = an all-or-nothing package), or every feature of `pkg`.
// In the n64lle layout a package's mods.toml section lists exactly the features
// that are on, so both compute that list from what the scan saw and write it
// whole -- the first write turns "manifest defaults" into an explicit list.
bool set_scanned_mod_enabled(const ModScanResult& scan, const fs::path& game_dir,
const ModPackageInfo& pkg, const std::string& feature_id,
bool enabled, std::string* error = nullptr);
bool set_scanned_mod_all(const ModScanResult& scan, const fs::path& game_dir,
const ModPackageInfo& pkg, bool enabled, std::string* error = nullptr);


} // namespace retcomm
Loading