mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 04:04:18 +02:00
Added Support for both Base and Retro Rewind in the launher
This commit is contained in:
1 parent
58c0890983
commit
321db74a0f
33 files changed
+733
-377
No files matched your search
@@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>0.2.38</Version>
|
||||
<Version>0.2.39</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -9,6 +9,9 @@ if ($SetupPath) {
|
||||
$info = (& $SetupPath --info-json | Out-String) | ConvertFrom-Json
|
||||
if ($LASTEXITCODE -ne 0 -or $info.productId -cne 'wiicompiled-openxr-vr' -or
|
||||
$info.version -cne $version -or $info.openxrD3D12 -ne $true) { throw 'Setup VR identity mismatch.' }
|
||||
# WheelWizard offers Build for Quest only to a setup that declares it, so a release that says
|
||||
# so must also carry what the build runs.
|
||||
if ($info.questBuild -ne $true) { throw 'Setup does not declare Quest build support.' }
|
||||
# Inspect the appended payload as well: a correctly versioned host can still wrap stale tools.
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
$stream = [IO.File]::OpenRead((Resolve-Path -LiteralPath $SetupPath))
|
||||
@@ -43,6 +46,12 @@ if ($SetupPath) {
|
||||
if ($manifest.ProductId -cne $info.productId -or $manifest.ProductVersion -cne $version) {
|
||||
throw 'Payload identity/version does not match the executable.'
|
||||
}
|
||||
# The scripts --build-quest runs live in the installed workspace, not the toolkit.
|
||||
foreach ($script in 'QuestGameKit.psm1', 'Build-QuestGame.ps1') {
|
||||
if (-not $zip.GetEntry("BuildWorkspace/android/$script")) {
|
||||
throw "The payload has no BuildWorkspace/android/$script, so --build-quest cannot run."
|
||||
}
|
||||
}
|
||||
} finally { $zip.Dispose() }
|
||||
} finally { $payload.Dispose(); $stream.Dispose() }
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ internal sealed class CommandLine
|
||||
public string? QuestApkPath { get; private set; }
|
||||
public string? OutputPath { get; private set; }
|
||||
public bool IncludeGameFiles { get; private set; }
|
||||
/// <summary>Which game the Quest app is to build: "base" (the default) or "retro_rewind".</summary>
|
||||
public string QuestProduct { get; private set; } = "base";
|
||||
public bool IncludeModContent { get; private set; }
|
||||
|
||||
public static bool WantsProgressJson(string[] args) =>
|
||||
args.Any(argument => argument.Equals("--progress-json", StringComparison.OrdinalIgnoreCase));
|
||||
@@ -97,6 +100,8 @@ internal sealed class CommandLine
|
||||
case "--quest-apk": result.QuestApkPath = RequireValue(args, ref i); break;
|
||||
case "--output": result.OutputPath = RequireValue(args, ref i); break;
|
||||
case "--include-game-files": result.IncludeGameFiles = true; break;
|
||||
case "--quest-product": result.QuestProduct = RequireValue(args, ref i); break;
|
||||
case "--include-mod-content": result.IncludeModContent = true; break;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown CLI option: {args[i]}");
|
||||
}
|
||||
@@ -149,7 +154,7 @@ internal sealed class CommandLine
|
||||
[AppMode.BuildQuest] = new ModeRules
|
||||
{
|
||||
Flag = "--build-quest", AcceptsProgressJson = true, RequiresInstallDirectory = true,
|
||||
AcceptsQuestBuild = true
|
||||
AcceptsQuestBuild = true, AcceptsRetroDirectory = true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -170,6 +175,13 @@ internal sealed class CommandLine
|
||||
Reject(QuestApkPath is not null && !rules.AcceptsQuestBuild, "--quest-apk");
|
||||
Reject(OutputPath is not null && !rules.AcceptsQuestBuild, "--output");
|
||||
Reject(IncludeGameFiles && !rules.AcceptsQuestBuild, "--include-game-files");
|
||||
Reject(IncludeModContent && !rules.AcceptsQuestBuild, "--include-mod-content");
|
||||
if (rules.AcceptsQuestBuild && QuestProduct is not ("base" or "retro_rewind"))
|
||||
throw new ArgumentException("--quest-product must be base or retro_rewind.");
|
||||
if (IncludeModContent && QuestProduct != "retro_rewind")
|
||||
throw new ArgumentException("--include-mod-content needs --quest-product retro_rewind.");
|
||||
if (IncludeModContent && string.IsNullOrWhiteSpace(RetroDirectoryPath))
|
||||
throw new ArgumentException("--include-mod-content needs --retro-dir.");
|
||||
if (rules.AcceptsQuestBuild && (string.IsNullOrWhiteSpace(QuestApkPath) || string.IsNullOrWhiteSpace(OutputPath)))
|
||||
throw new ArgumentException($"--quest-apk and --output are required with {rules.Flag}.");
|
||||
Reject(RetroWfcPayloadMode != RetroWfcPayloadMode.NotApplicable && !rules.AcceptsPayloadMode,
|
||||
|
||||
@@ -17,7 +17,8 @@ internal static class ConsoleCommands
|
||||
Console.Out.WriteLine(" --repair-products --install-dir <dir> --retro-dir <folder> " +
|
||||
"(--download-retro-wfc-payload | --skip-retro-wfc-payload) [--progress-json]");
|
||||
Console.Out.WriteLine(" --build-quest --install-dir <dir> --quest-apk <apk> --output <file.wcgame> " +
|
||||
"[--include-game-files] [--progress-json]");
|
||||
"[--quest-product base|retro_rewind] [--include-game-files] " +
|
||||
"[--retro-dir <folder> --include-mod-content] [--progress-json]");
|
||||
Console.Out.WriteLine(" --launch-retro | --launch-base");
|
||||
Console.Out.WriteLine(" --uninstall --install-dir <dir>");
|
||||
Console.Out.WriteLine(" --version");
|
||||
@@ -293,7 +294,8 @@ internal static class ConsoleCommands
|
||||
using var operationLock = InstallOperationLock.Acquire(installation.Root, reporter);
|
||||
var result = await new QuestBuildService(reporter).BuildAsync(installation,
|
||||
Path.GetFullPath(command.QuestApkPath!), Path.GetFullPath(command.OutputPath!),
|
||||
command.IncludeGameFiles, cancellationToken);
|
||||
command.IncludeGameFiles, cancellationToken, command.QuestProduct,
|
||||
command.IncludeModContent ? Path.GetFullPath(command.RetroDirectoryPath!) : null);
|
||||
if (ndjson is null)
|
||||
Console.Out.WriteLine($"Quest game package: {result.PackagePath} ({result.SizeBytes / 1_000_000} MB)");
|
||||
ndjson?.QuestPackage(result);
|
||||
|
||||
@@ -49,7 +49,8 @@ internal sealed class QuestBuildService
|
||||
internal sealed record Toolchain(string ClangCxx, string ClangC, string Sysroot);
|
||||
|
||||
public async Task<Result> BuildAsync(Installation installation, string apkPath, string outputPath,
|
||||
bool includeGameFiles, CancellationToken cancellationToken)
|
||||
bool includeGameFiles, CancellationToken cancellationToken, string product = "base",
|
||||
string? modContentDirectory = null)
|
||||
{
|
||||
_reporter.Progress(InstallStages.Validate, "Checking the installation and the Quest app...", 1);
|
||||
var workspace = installation.WorkspaceDirectory;
|
||||
@@ -73,14 +74,24 @@ internal sealed class QuestBuildService
|
||||
var kitDirectory = Path.Combine(questRoot, "kit");
|
||||
_reporter.Progress(InstallStages.QuestKit, "Reading the game kit from the Quest app...", 2);
|
||||
var kit = ExtractKit(apkPath, kitDirectory, cancellationToken);
|
||||
_reporter.Diagnostic($"Quest game kit {kit.Fingerprint} ({kit.Product})");
|
||||
// A Retro Rewind app needs this installation's Retro Rewind translation, which only exists
|
||||
// once the mod has been built here at least once.
|
||||
if (kit.Product == "retro_rewind" && !HasRetroRewindShards(generated))
|
||||
_reporter.Diagnostic($"Quest game kit {kit.Fingerprint} [{string.Join(", ", kit.Products)}]");
|
||||
if (!kit.Products.Contains(product))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"This Quest app is the Retro Rewind one, and this installation has no Retro Rewind " +
|
||||
"translation. Install or repair Retro Rewind, then build for Quest again.");
|
||||
$"The Quest app cannot play {product}: its game kit carries {string.Join(", ", kit.Products)}.");
|
||||
}
|
||||
// Retro Rewind needs this installation's own Retro Rewind translation, which only exists
|
||||
// once the mod has been built here at least once.
|
||||
if (product == "retro_rewind" && !HasRetroRewindShards(generated))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"This installation has no Retro Rewind translation. Install or repair Retro Rewind, " +
|
||||
"then build for Quest again.");
|
||||
}
|
||||
if (modContentDirectory is not null && !File.Exists(Path.Combine(modContentDirectory, "Binaries", "Code.pul")))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The Retro Rewind content to put in the game file is missing its Binaries/Code.pul.");
|
||||
}
|
||||
|
||||
var toolchain = await EnsureToolchainAsync(questRoot, cancellationToken);
|
||||
@@ -94,13 +105,18 @@ internal sealed class QuestBuildService
|
||||
"-Generated", generated, "-Kit", kitDirectory, "-Manifest", manifest,
|
||||
"-BuildDir", Path.Combine(questRoot, "build"), "-Output", temporaryOutput,
|
||||
"-ClangCxx", toolchain.ClangCxx, "-ClangC", toolchain.ClangC, "-Sysroot", toolchain.Sysroot,
|
||||
"-Ninja", ninja, "-BuiltBy", $"WiiCompiled Setup {ProductInfo.Version}"
|
||||
"-Ninja", ninja, "-BuiltBy", $"WiiCompiled Setup {ProductInfo.Version}", "-Product", product
|
||||
};
|
||||
if (includeGameFiles)
|
||||
{
|
||||
arguments.Add("-Data");
|
||||
arguments.Add(installation.GameDataDirectory);
|
||||
}
|
||||
if (modContentDirectory is not null)
|
||||
{
|
||||
arguments.Add("-Mod");
|
||||
arguments.Add(modContentDirectory);
|
||||
}
|
||||
|
||||
_reporter.Progress(InstallStages.QuestBuild, "Preparing the Quest build...", 40);
|
||||
var powershell = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System),
|
||||
@@ -168,8 +184,8 @@ internal sealed class QuestBuildService
|
||||
File.Exists(Path.Combine(directory, "sys", "fst.bin")) &&
|
||||
File.Exists(Path.Combine(directory, "sys", "main.dol"));
|
||||
|
||||
/// <summary>What the extracted kit says it builds: its fingerprint and product ("base" or "retro_rewind").</summary>
|
||||
internal sealed record Kit(string Fingerprint, string Product);
|
||||
/// <summary>What the extracted kit says it builds: its fingerprint and the products it carries.</summary>
|
||||
internal sealed record Kit(string Fingerprint, IReadOnlyCollection<string> Products);
|
||||
|
||||
/// <summary>Extracts <c>assets/game_kit</c> from the Quest app's APK and reads what it is for.</summary>
|
||||
internal static Kit ExtractKit(string apkPath, string destination, CancellationToken cancellationToken)
|
||||
@@ -203,11 +219,11 @@ internal sealed class QuestBuildService
|
||||
{
|
||||
throw new InvalidDataException("The Quest app's game kit has no fingerprint.");
|
||||
}
|
||||
var product = document.RootElement.TryGetProperty("product", out var value) &&
|
||||
value.ValueKind == JsonValueKind.String
|
||||
? value.GetString()!
|
||||
: "base";
|
||||
return new Kit(fingerprint.GetString()!, product);
|
||||
var products = document.RootElement.TryGetProperty("products", out var value) &&
|
||||
value.ValueKind == JsonValueKind.Object
|
||||
? value.EnumerateObject().Select(property => property.Name).ToArray()
|
||||
: throw new InvalidDataException("The Quest app's game kit lists no games it can build.");
|
||||
return new Kit(fingerprint.GetString()!, products);
|
||||
}
|
||||
|
||||
private async Task<Toolchain> EnsureToolchainAsync(string questRoot, CancellationToken cancellationToken)
|
||||
|
||||
@@ -302,9 +302,18 @@ internal static class SelfTests
|
||||
"--output", "D:\\MarioKartWii.wcgame", "--include-game-files", "--progress-json"
|
||||
]);
|
||||
if (build.Mode != AppMode.BuildQuest || build.QuestApkPath != "quest.apk" ||
|
||||
build.OutputPath != "D:\\MarioKartWii.wcgame" || !build.IncludeGameFiles || !build.ProgressJson)
|
||||
build.OutputPath != "D:\\MarioKartWii.wcgame" || !build.IncludeGameFiles || !build.ProgressJson ||
|
||||
build.QuestProduct != "base" || build.IncludeModContent)
|
||||
throw new Exception("The Quest build contract did not parse.");
|
||||
|
||||
var retro = CommandLine.Parse([
|
||||
"--build-quest", "--install-dir", "C:\\Games\\MKW", "--quest-apk", "quest.apk",
|
||||
"--output", "D:\\RetroRewind.wcgame", "--quest-product", "retro_rewind",
|
||||
"--retro-dir", "D:\\RetroRewind6", "--include-mod-content"
|
||||
]);
|
||||
if (retro.QuestProduct != "retro_rewind" || !retro.IncludeModContent || retro.RetroDirectoryPath != "D:\\RetroRewind6")
|
||||
throw new Exception("The Retro Rewind Quest build contract did not parse.");
|
||||
|
||||
void Rejects(string description, params string[] args)
|
||||
{
|
||||
try
|
||||
@@ -319,6 +328,12 @@ internal static class SelfTests
|
||||
Rejects("A Quest build without an installation", "--build-quest", "--quest-apk", "a.apk", "--output", "o");
|
||||
Rejects("--quest-apk outside a Quest build", "--launch-base", "--quest-apk", "a.apk");
|
||||
Rejects("--include-game-files outside a Quest build", "--check-products", "--include-game-files");
|
||||
Rejects("An unknown Quest product", "--build-quest", "--install-dir", "i", "--quest-apk", "a.apk",
|
||||
"--output", "o.wcgame", "--quest-product", "mario-kart-8");
|
||||
Rejects("The mod content of a base game", "--build-quest", "--install-dir", "i", "--quest-apk", "a.apk",
|
||||
"--output", "o.wcgame", "--retro-dir", "r", "--include-mod-content");
|
||||
Rejects("Mod content without its source", "--build-quest", "--install-dir", "i", "--quest-apk", "a.apk",
|
||||
"--output", "o.wcgame", "--quest-product", "retro_rewind", "--include-mod-content");
|
||||
}
|
||||
|
||||
private static void TestQuestToolchainSelection()
|
||||
@@ -369,15 +384,16 @@ internal static class SelfTests
|
||||
using var writer = new StreamWriter(zip.CreateEntry(name).Open());
|
||||
writer.Write(text);
|
||||
}
|
||||
Add("assets/game_kit/kit.json", "{\"schema\":2,\"product\":\"retro_rewind\",\"fingerprint\":\"abc123\"}");
|
||||
Add("assets/game_kit/kit.json", "{\"schema\":3,\"fingerprint\":\"abc123\",\"products\":{\"base\":{},\"retro_rewind\":{}}}");
|
||||
Add("assets/game_kit/include/memory.h", "#pragma once");
|
||||
Add("assets/runtime_resources/dsp_coef.bin", "not the kit");
|
||||
Add("lib/arm64-v8a/libSDL3.so", "not the kit");
|
||||
}
|
||||
var kit = Path.Combine(temp, "kit");
|
||||
var extracted = QuestBuildService.ExtractKit(apk, kit, CancellationToken.None);
|
||||
if (extracted.Fingerprint != "abc123" || extracted.Product != "retro_rewind")
|
||||
throw new Exception("The kit fingerprint or product was not read.");
|
||||
if (extracted.Fingerprint != "abc123" || !extracted.Products.Contains("base") ||
|
||||
!extracted.Products.Contains("retro_rewind"))
|
||||
throw new Exception("The kit fingerprint or its games were not read.");
|
||||
if (!File.Exists(Path.Combine(kit, "include", "memory.h")) ||
|
||||
Directory.GetFiles(kit, "*", SearchOption.AllDirectories).Length != 2)
|
||||
throw new Exception("The kit extraction took the wrong files.");
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
64fdd492f6ce71fd223b49f493e0809d9a7f9ed45ebaa3605f626eb77282dbe4 WiiCompiled-Setup.exe
|
||||
b319b86d46bea6ebfa52965c65706976d6004abc01914ff82a2b73c71de84def WheelWizardVRWindows.exe
|
||||
@@ -1 +0,0 @@
|
||||
946bb530c9deea52f397689d637a4e0c10070d9c1980d7d02c4acc78f8fb7e5d WheelWizardVRWindows.exe
|
||||
@@ -1 +0,0 @@
|
||||
46ab8a1d1c6d8af551cdd45808397a0eb4fbbc96bb97e25d7a5a70d6ccde01bf WheelWizardVRWindows.exe
|
||||
Reference in new issue
Block a user