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
20 changes: 19 additions & 1 deletion .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ jobs:
if-no-files-found: error
archive: false

# The same setup, zipped with the recomplauncher.json RecompLauncher reads, so it unpacks into
# its own folder instead of being run as an installer. WiiCompiled-Setup.exe stays as it is.
- name: Zip for RecompLauncher
shell: pwsh
run: |
$stage = Join-Path $env:RUNNER_TEMP 'recomplauncher-zip'
New-Item -ItemType Directory -Force -Path $stage | Out-Null
Copy-Item Launcher/dist/WiiCompiled-Setup.exe $stage
Copy-Item Launcher/recomplauncher/windows.json (Join-Path $stage 'recomplauncher.json')
Compress-Archive -Path (Join-Path $stage '*') -DestinationPath Launcher/dist/WiiCompiled-Setup-windows-x64.zip

- uses: actions/upload-artifact@v7
with:
name: WiiCompiled-Setup-windows-x64-zip
path: Launcher/dist/WiiCompiled-Setup-windows-x64.zip
if-no-files-found: error
archive: false

macos-setup-package:
name: macOS (universal Setup.pkg)
runs-on: macos-15
Expand Down Expand Up @@ -307,7 +325,7 @@ jobs:
set -euo pipefail
ls -lR artifacts
assets=()
for name in WiiCompiled-Setup.exe WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage WiiCompiled-Setup.pkg WiiCompiled-Setup-macos.run; do
for name in WiiCompiled-Setup.exe WiiCompiled-Setup-windows-x64.zip WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage WiiCompiled-Setup.pkg WiiCompiled-Setup-macos.run; do
found="$(find artifacts -type f -name "$name" | head -n 1)"
[ -n "$found" ] && [ -s "$found" ] || { echo "::error::missing release asset $name"; exit 1; }
assets+=("$found")
Expand Down
34 changes: 34 additions & 0 deletions Launcher/WiiCompiled.Setup.Common/InstallProgress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,40 @@ private static void WriteLine(object value)
}
}

/// <summary>
/// The <c>--progress-lines</c> format RecompLauncher reads from a setup step: one
/// <c>progress &lt;0 to 1&gt; &lt;text&gt;</c> line per update on stdout, diagnostics on stderr. The
/// outcome is the exit code.
/// </summary>
public sealed class LauncherProgressInstallReporter : IInstallReporter
{
private readonly Action<string>? _log;
private readonly object _gate = new();
private int _lastPercent;

public LauncherProgressInstallReporter(Action<string>? log = null) => _log = log;

public void Progress(string stage, string message, int percent)
{
lock (_gate)
{
// Monotonic, like the NDJSON reporter: the bar never walks backwards.
_lastPercent = Math.Clamp(Math.Max(percent, _lastPercent), 0, 99);
var text = message.ReplaceLineEndings(" ").Trim();
Console.Out.WriteLine(string.Create(System.Globalization.CultureInfo.InvariantCulture,
$"progress {_lastPercent / 100.0:0.00} {text}"));
Console.Out.Flush();
}
_log?.Invoke(message);
}

public void Diagnostic(string line)
{
Console.Error.WriteLine(line);
_log?.Invoke(line);
}
}

/// <summary>Plain-text console reporting for a run without <c>--progress-json</c>.</summary>
public sealed class ConsoleInstallReporter : IInstallReporter
{
Expand Down
13 changes: 11 additions & 2 deletions Launcher/WiiCompiled.Setup.Linux/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ private static async Task<int> RunAsync(string[] args, CancellationTokenSource c
var (command, flags) = ParseArgs(args);
if (command is null) { PrintUsage(); return 1; }
var progressJson = flags.ContainsKey("progress-json");
IInstallReporter reporter = progressJson ? new NdjsonInstallReporter(ProductInfo.Version) : new ConsoleInstallReporter();
var progressLines = flags.ContainsKey("progress-lines");
if (progressJson && progressLines)
{
Console.Error.WriteLine("error: Choose only one of --progress-json and --progress-lines.");
return 1;
}
IInstallReporter reporter = progressJson ? new NdjsonInstallReporter(ProductInfo.Version)
: progressLines ? (IInstallReporter)new LauncherProgressInstallReporter()
: new ConsoleInstallReporter();

try
{
Expand Down Expand Up @@ -361,7 +369,8 @@ install [--game ISO_PATH] [--install-dir DIR] [--retro-dir DIR
{--download-retro-wfc-payload | --skip-retro-wfc-payload}]
[--force-clean-build] [--translator-bin PATH] [--disc-tool-bin PATH]
[--cc PATH] [--cxx PATH] [--fuse-ld NAME_OR_PATH] [--cmake PATH] [--ninja PATH]
[--native-prebuilt-dir DIR] [--sysroot PATH] [--progress-json] [--workspace DIR]
[--native-prebuilt-dir DIR] [--sysroot PATH] [--workspace DIR]
[--progress-json | --progress-lines]
uninstall
launch-base
launch-retro
Expand Down
8 changes: 7 additions & 1 deletion Launcher/WiiCompiled.Setup.Windows/CommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal sealed record ModeRules
{
public string Flag { get; init; } = "this command";
public bool AcceptsProgressJson { get; init; }
public bool AcceptsProgressLines { get; init; }
public bool AcceptsGame { get; init; }
public bool RequiresGame { get; init; }
public bool AcceptsRetroDirectory { get; init; }
Expand All @@ -44,6 +45,7 @@ internal sealed class CommandLine
public string? InstallDirectory { get; private set; }
public bool Quiet { get; private set; }
public bool ProgressJson { get; private set; }
public bool ProgressLines { get; private set; }
public string? PayloadRootPath { get; private set; }
public bool Portable { get; private set; }

Expand Down Expand Up @@ -73,6 +75,7 @@ public static CommandLine Parse(string[] args)
case "--payload-root": result.PayloadRootPath = RequireValue(args, ref i); break;
case "--quiet": result.Quiet = true; break;
case "--progress-json": result.ProgressJson = true; break;
case "--progress-lines": result.ProgressLines = true; break;
case "--game": result.GamePath = RequireValue(args, ref i); break;
case "--retro-dir": result.RetroDirectoryPath = RequireValue(args, ref i); break;
case "--download-retro-wfc-payload":
Expand Down Expand Up @@ -100,7 +103,7 @@ public static CommandLine Parse(string[] args)
[AppMode.SilentInstall] = new ModeRules
{
Flag = "--silent",
AcceptsProgressJson = true, AcceptsGame = true, RequiresGame = true,
AcceptsProgressJson = true, AcceptsProgressLines = true, AcceptsGame = true, RequiresGame = true,
AcceptsRetroDirectory = true, AcceptsPayloadMode = true,
AcceptsPortable = true
},
Expand Down Expand Up @@ -149,6 +152,9 @@ void Reject(bool present, string option)
Reject(GamePath is not null && !rules.AcceptsGame, "--game");
Reject(RetroDirectoryPath is not null && !rules.AcceptsRetroDirectory, "--retro-dir");
Reject(ProgressJson && !rules.AcceptsProgressJson, "--progress-json");
Reject(ProgressLines && !rules.AcceptsProgressLines, "--progress-lines");
if (ProgressJson && ProgressLines)
throw new ArgumentException("Choose only one of --progress-json and --progress-lines.");
Reject(PayloadRootPath is not null && !rules.RequiresPayloadRoot, "--payload-root");
Reject(Portable && !rules.AcceptsPortable, "--portable");
Reject(RetroWfcPayloadMode != RetroWfcPayloadMode.NotApplicable && !rules.AcceptsPayloadMode,
Expand Down
5 changes: 4 additions & 1 deletion Launcher/WiiCompiled.Setup.Windows/ConsoleCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public static int Help()
Console.Out.WriteLine();
Console.Out.WriteLine("Commands:");
Console.Out.WriteLine(" --silent --game <image> --install-dir <dir> [--retro-dir <folder>] [--portable]");
Console.Out.WriteLine(" [--progress-json | --progress-lines]");
Console.Out.WriteLine(" --verify-inputs --game <image> [--retro-dir <folder>]");
Console.Out.WriteLine(" --check-products [--install-dir <dir>] [--retro-dir <folder>] [--progress-json]");
Console.Out.WriteLine(" --repair-products --install-dir <dir> --retro-dir <folder> " +
Expand Down Expand Up @@ -106,7 +107,9 @@ void Log(string message)
File.WriteAllText(logPath, $"{ProductInfo.Name} setup {ProductInfo.Version}{Environment.NewLine}");

var ndjson = command.ProgressJson ? new NdjsonInstallReporter(ProductInfo.Version, Log) : null;
IInstallReporter reporter = ndjson ?? (IInstallReporter)new ConsoleInstallReporter(Log);
IInstallReporter reporter = ndjson ?? (command.ProgressLines
? (IInstallReporter)new LauncherProgressInstallReporter(Log)
: new ConsoleInstallReporter(Log));
var installDirectory = Path.GetFullPath(command.InstallDirectory!);
try
{
Expand Down
10 changes: 10 additions & 0 deletions Launcher/WiiCompiled.Setup.Windows/SelfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,16 @@ void Rejects(string description, params string[] args)
if (portable.Mode != AppMode.SilentInstall || !portable.Portable)
throw new Exception("The portable install contract did not parse.");

var lines = CommandLine.Parse([
"--silent", "--game", "game.iso", "--install-dir", "D:\\Games\\Recomp\\Install",
"--portable", "--progress-lines"
]);
if (lines.Mode != AppMode.SilentInstall || !lines.ProgressLines || lines.ProgressJson)
throw new Exception("The progress-lines install contract did not parse.");
Rejects("--progress-lines with --progress-json", "--silent", "--game", "g.iso",
"--progress-lines", "--progress-json");
Rejects("--progress-lines outside an install", "--check-products", "--progress-lines");

Rejects("--portable without an install directory", "--silent", "--game", "g.iso", "--portable");
Rejects("--portable outside an install", "--check-products", "--install-dir", "fixed", "--portable");
// The config verbs are gone: WheelWizard reads and writes Config.toml directly now.
Expand Down
3 changes: 3 additions & 0 deletions Launcher/build-appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ exec "$HERE/usr/bin/wiicompiled-setup" --workspace "$CACHE" \
APPRUN
chmod +x "$appdir/AppRun"

# RecompLauncher reads this straight out of the AppImage to set the game up and start it.
cp "$script_dir/recomplauncher/linux.json" "$appdir/recomplauncher.json"

echo "Writing desktop entry and icon..."
cat > "$appdir/wiicompiled-setup.desktop" <<'DESKTOP'
[Desktop Entry]
Expand Down
119 changes: 119 additions & 0 deletions Launcher/recomplauncher/linux.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{
"$schema": "https://recomplauncher.com/schema/recomplauncher.json",
"format": 1,
"name": "Mario Kart WiiCompiled",
"repo": "patchzyy/Wiicompiled",
"game": {
"kind": "iso",
"label": "Your own disc image of Mario Kart Wii: the PAL (RMCP01), USA (RMCE01) or Japan (RMCJ01) release, a clean dump as .iso, .gcm, .gcz, .ciso, .wbfs, .wia or .rvz.",
"check": { "ids": ["RMCP01", "RMCE01", "RMCJ01"] }
},
"prepare": {
"program": ["WiiCompiled-Setup-x86_64.AppImage", "WiiCompiled-Setup-aarch64.AppImage"],
"args": ["install", "--game", "{game}", "--progress-lines"],
"label": "Translating and compiling the game from your disc. This takes a while, and only happens once",
"timeout": 10800
},
"start": {
"program": ["WiiCompiled-Setup-x86_64.AppImage", "WiiCompiled-Setup-aarch64.AppImage"],
"args": ["launch-base"]
},
"config": [
{
"id": "main",
"format": "toml",
"file": [{ "path": "{local}/WiiCompiled/Config.toml" }]
}
],
"saves": ["{local}/WiiCompiled/NAND"],
"folders": {
"saves": [{ "path": "{local}/WiiCompiled/NAND" }],
"logs": [{ "path": "{local}/WiiCompiled/Logs" }]
},
"settings": [
{
"id": "display_mode", "label": "Window", "group": "Video",
"help": "Windowed, borderless fullscreen, or exclusive fullscreen, which asks your screen for the closest mode to the game's frame rate.",
"choices": [
{ "label": "Windowed", "set": { "video/display_mode": "\"windowed\"" } },
{ "label": "Borderless", "set": { "video/display_mode": "\"borderless\"" } },
{ "label": "Exclusive", "set": { "video/display_mode": "\"exclusive\"" } }
],
"defaults": { "video/display_mode": "\"windowed\"" }
},
{
"id": "resolution", "label": "Resolution", "group": "Video",
"help": "What the game draws at. Native is the Wii's own. Auto follows the size of the window. Higher is sharper and needs a faster graphics card.",
"choices": [
{ "label": "Auto", "set": { "video/resolution_multiplier": "0.0" } },
{ "label": "Native", "set": { "video/resolution_multiplier": "1.0" } },
{ "label": "1.5x", "set": { "video/resolution_multiplier": "1.5" } },
{ "label": "2x", "set": { "video/resolution_multiplier": "2.0" } },
{ "label": "3x", "set": { "video/resolution_multiplier": "3.0" } },
{ "label": "4x", "set": { "video/resolution_multiplier": "4.0" } },
{ "label": "6x", "set": { "video/resolution_multiplier": "6.0" } },
{ "label": "8x", "set": { "video/resolution_multiplier": "8.0" } }
],
"defaults": { "video/resolution_multiplier": "1.0" }
},
{
"id": "aspect", "label": "Aspect ratio", "group": "Video",
"help": "16:9 for widescreen screens, 4:3 for the original shape. Takes effect the next time the game starts.",
"choices": [
{ "label": "16:9", "set": { "video/widescreen": "true" } },
{ "label": "4:3", "set": { "video/widescreen": "false" } }
],
"defaults": { "video/widescreen": "true" }
},
{
"id": "interpolation", "label": "Race frame rate", "group": "Video",
"help": "The game runs at 60 FPS. Interpolation adds frames in between during races for 120 or 144 Hz screens. It is experimental and can show artifacts.",
"choices": [
{ "label": "60 FPS", "set": { "video/frame_interpolation_fps": "0" } },
{ "label": "120 FPS", "set": { "video/frame_interpolation_fps": "120" } },
{ "label": "180 FPS", "set": { "video/frame_interpolation_fps": "180" } }
],
"defaults": { "video/frame_interpolation_fps": "0" }
},
{
"id": "vsync", "label": "V-Sync", "group": "Video",
"help": "Waits for the screen's refresh. Turn it on if frame interpolation looks uneven on your screen.",
"choices": [
{ "label": "Off", "set": { "video/vsync": "false" } },
{ "label": "On", "set": { "video/vsync": "true" } }
],
"defaults": { "video/vsync": "false" }
},
{
"id": "fps", "label": "Show FPS", "group": "Video",
"help": "The frame rate in a corner of the game.",
"choices": [
{ "label": "On", "set": { "video/show_fps": "true" } },
{ "label": "Off", "set": { "video/show_fps": "false" } }
],
"defaults": { "video/show_fps": "true" }
},
{
"id": "mute", "label": "Sound", "group": "Audio",
"help": "Turn all of the game's sound off or on.",
"choices": [
{ "label": "On", "set": { "audio/muted": "false" } },
{ "label": "Muted", "set": { "audio/muted": "true" } }
],
"defaults": { "audio/muted": "false" }
},
{
"id": "ducking", "label": "Music while other audio plays", "group": "Audio",
"help": "Mutes the game's music while another app, like a video or a music player, is playing sound.",
"choices": [
{ "label": "Keep playing", "set": { "audio/attenuate_music_when_media_plays": "false" } },
{ "label": "Mute music", "set": { "audio/attenuate_music_when_media_plays": "true" } }
],
"defaults": { "audio/attenuate_music_when_media_plays": "false" }
}
],
"actions": [
{ "id": "saves", "label": "Saves", "help": "Your Mario Kart Wii save data.", "open": "saves" },
{ "id": "logs", "label": "Logs", "help": "The log of each run, for bug reports.", "open": "logs" }
]
}
Loading