From 18e15e39931976f2018375e95bfa6b43557ecfb5 Mon Sep 17 00:00:00 2001 From: Tim Strulik Date: Mon, 9 Nov 2026 17:16:28 +0100 Subject: [PATCH 1/2] feat: launcher profiles with saves of their own, and save import The launcher (Windows and Linux) gets a Profile choice above Play. Each profile is a user of the game with its own save folder, save/ in the user folder; Play starts the game with HOOKSHOT_PROFILE= and the game mounts that profile's saves (host/nn_applet.c). Without the variable the game plays the default profile, the uid every build had, so existing saves stay where they are and an older game simply ignores it. Manage renames the chosen profile, imports a save into it, opens its save folder or removes it; New profile makes one. Import takes the game's save folder (account.sav, option.sav, slot_00 ...) or an emulator's whole save folder, and asks which user when it holds several. Nothing is deleted: the profile's saves before an import, and a removed profile's saves, are moved to save-backups. The default profile is marked "(default)" and cannot be removed. builder/linux/profiles.py and builder/HookshotBuilder/Profiles.cs keep the same profiles.txt. Tests: tests/test_launcher_profiles.py, tests/test_account_profile.py (pick_user built from the source), tests/launcher_profiles (C#, wired into source-regressions on Windows) and tests/test_hookshot_launch.py (--play passes the chosen profile). Assisted-by: Claude Code Co-Authored-By: Claude Opus 5.5 --- .github/workflows/source-regressions.yml | 6 + builder/HookshotBuilder/App.xaml | 31 +++ builder/HookshotBuilder/MainWindow.xaml | 44 +++ builder/HookshotBuilder/MainWindow.xaml.cs | 204 +++++++++++++- builder/HookshotBuilder/Profiles.cs | 257 ++++++++++++++++++ builder/linux/hookshot-launch | 26 +- builder/linux/hookshot_builder.py | 168 +++++++++++- builder/linux/profiles.py | 223 +++++++++++++++ host/nn_applet.c | 35 ++- readme.md | 16 ++ .../LauncherProfilesTests.csproj | 12 + tests/launcher_profiles/NuGet.Config | 2 + tests/launcher_profiles/Program.cs | 140 ++++++++++ tests/run_all.py | 2 + tests/test_account_profile.py | 75 +++++ tests/test_hookshot_launch.py | 20 +- tests/test_launcher_profiles.py | 122 +++++++++ 17 files changed, 1360 insertions(+), 23 deletions(-) create mode 100644 builder/HookshotBuilder/Profiles.cs create mode 100644 builder/linux/profiles.py create mode 100644 tests/launcher_profiles/LauncherProfilesTests.csproj create mode 100644 tests/launcher_profiles/NuGet.Config create mode 100644 tests/launcher_profiles/Program.cs create mode 100644 tests/test_account_profile.py create mode 100644 tests/test_launcher_profiles.py diff --git a/.github/workflows/source-regressions.yml b/.github/workflows/source-regressions.yml index 40bb187d5..d3bad4e0b 100644 --- a/.github/workflows/source-regressions.yml +++ b/.github/workflows/source-regressions.yml @@ -100,6 +100,12 @@ jobs: run: | dotnet restore tests/builder_workers/BuilderWorkersTests.csproj --configfile tests/builder_workers/NuGet.Config dotnet run --project tests/builder_workers/BuilderWorkersTests.csproj --configuration Release --no-restore -- (Get-Command python).Source + - name: Check the launcher's profiles + if: ${{ !cancelled() && runner.os == 'Windows' }} + shell: pwsh + run: | + dotnet restore tests/launcher_profiles/LauncherProfilesTests.csproj --configfile tests/launcher_profiles/NuGet.Config + dotnet run --project tests/launcher_profiles/LauncherProfilesTests.csproj --configuration Release --no-restore - name: Compile Windows Builder if: ${{ !cancelled() && runner.os == 'Windows' }} run: dotnet build builder/HookshotBuilder/HookshotBuilder.csproj --configuration Debug -p:NuGetAudit=false diff --git a/builder/HookshotBuilder/App.xaml b/builder/HookshotBuilder/App.xaml index 0c9113683..449346146 100644 --- a/builder/HookshotBuilder/App.xaml +++ b/builder/HookshotBuilder/App.xaml @@ -334,6 +334,37 @@ + + +