Added Support for VR in Launcher Setup

This commit is contained in:
iChris4 committed 2026-09-09 20:40:23 +02:00
1 parent d86dcb0aeb
commit 5caec70da9
28 files changed
+397 -157

No files matched your search

+3 -1
View File
@@ -15,6 +15,7 @@ Set-StrictMode -Version 3.0
. (Join-Path $PSScriptRoot 'NativeBuildFlags.ps1')
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$releaseVersion = ([xml](Get-Content -LiteralPath (Join-Path $PSScriptRoot 'Directory.Build.props') -Raw)).Project.PropertyGroup.Version
$outputRoot = [IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDirectory))
$portableTools = [IO.Path]::GetFullPath((Join-Path $repoRoot $PortableToolsDirectory))
$dependencySources = [IO.Path]::GetFullPath((Join-Path $repoRoot $DependencySourceDirectory))
@@ -282,7 +283,8 @@ foreach ($required in @('ToolkitFingerprint','TranslationFingerprint','NativeToo
$manifest = [ordered]@{
SchemaVersion = 2
ProductVersion = '0.2.31'
ProductVersion = $releaseVersion
ProductId = 'wiicompiled-openxr-vr'
ExpectedGameId = $pins.GameId
ExpectedDolSha256 = $pins.DolSha256
ExpectedRelSha256 = $pins.RelSha256
+5
View File
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<Version>0.2.32</Version>
</PropertyGroup>
</Project>
+1 -1
View File
@@ -390,7 +390,7 @@ try {
-SourceDirectory (Join-Path $Workspace 'runtime') -BuildDirectory $build `
-Ninja $ninja -CCompiler $cc -CxxCompiler $cxx -ResourceCompiler $windres `
-DependenciesDirectory $dependencies -NativePrebuiltDirectory $nativePrebuilt `
-AdditionalArguments @("-DMKW_TRANSLATED_COMPILE_JOBS=$translatedJobs")
-AdditionalArguments @("-DMKW_TRANSLATED_COMPILE_JOBS=$translatedJobs", '-DMKW_ENABLE_OPENXR=ON')
Invoke-Checked $cmake $configure 'Configuring the bundled native toolchain' -StepId 'configure-native'
if (-not [string]::IsNullOrWhiteSpace($NativeToolchainFingerprint)) {
[ordered]@{ SchemaVersion = 1; NativeToolchainFingerprint = $NativeToolchainFingerprint } |
+4 -2
View File
@@ -9,9 +9,11 @@ $forbiddenNames = @(
'WiiCompiled.exe', 'RetroRewind.exe', 'mkw_recompiled.exe',
'main.dol', 'StaticR.rel', 'Code.pul', 'base_translation_sources.bin',
'data_sections_init.cpp', 'data_sections_init_blobs.S', 'mkwii_base_manifest.json',
'generated_func_aliases.cpp', 'translated_function_decls.h'
'generated_func_aliases.cpp', 'translated_function_decls.h',
'rksys.dat', 'RFL_DB.dat', 'Config.toml', 'config-vr.json', 'install-state.json'
)
$forbiddenExtensions = @('.iso', '.gcm', '.gcz', '.ciso', '.wbfs', '.wia', '.rvz', '.dol', '.rel', '.pul')
$forbiddenExtensions = @('.iso', '.gcm', '.gcz', '.ciso', '.wbfs', '.wia', '.rvz', '.dol', '.rel', '.pul',
'.szs', '.brres', '.thp', '.brsar', '.rkg')
$forbiddenSegments = @(
'\BuildWorkspace\generated\', '\BuildWorkspace\runtime\build\', '\GameAssets\', '\BuildWorkspace\Assets\',
'\BuildWorkspace\PulsarPacks\', '\BuildWorkspace\build\base\', '\BuildWorkspace\build\mods\'
+4 -3
View File
@@ -45,13 +45,14 @@ foreach ($relative in $sourceFiles | Sort-Object -Unique) {
}
# One synthetic text section: li r3,40; addi r3,r3,2; nop; blr.
# Native HLE wrappers also call these eight guest symbols directly. Give each
# Native HLE and VR wrappers also call these guest symbols directly. Give each
# its own generated blr function so the real product can link without game code.
# Keep this list explicit: a new unresolved guest dependency must fail the test.
[uint32]$entry = 0x80001000L
[uint32[]]$guestCallbacks = @(
0x8012B830L, 0x801A0620L, 0x801A1ED8L, 0x801A961CL,
0x801AADE0L, 0x801D8D30L, 0x801D9E94L, 0x8055531CL
0x801AADE0L, 0x801D8D30L, 0x801D9E94L, 0x8055531CL,
0x8056A470L, 0x8056A580L, 0x805A6C58L
)
$textSize = [int]($guestCallbacks[-1] - $entry + 4)
$dataOffset = 0x100 + $textSize
@@ -135,7 +136,7 @@ try {
-Ninja $ninja -CCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang.exe') `
-CxxCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang++.exe') `
-ResourceCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-windres.exe') `
-DependenciesDirectory $dependencies -AdditionalArguments @('-DMKW_BUILD_PRODUCTS=ON')
-DependenciesDirectory $dependencies -AdditionalArguments @('-DMKW_BUILD_PRODUCTS=ON', '-DMKW_ENABLE_OPENXR=ON')
Invoke-Checked $cmake $configure 'Configuring the production Windows runtime' `
-WaitForProcessTree $false
Invoke-Checked $cmake @('--build', $nativeBuild, '--target', 'WiiCompiled', '--parallel', "$Parallel") `
+49
View File
@@ -0,0 +1,49 @@
[CmdletBinding()]
param([string]$Tag, [string]$SetupPath)
$ErrorActionPreference = 'Stop'
$version = ([xml](Get-Content -LiteralPath (Join-Path $PSScriptRoot 'Directory.Build.props') -Raw)).Project.PropertyGroup.Version
if ($Tag -and $Tag -cne "v$version") { throw "Release tag must be v$version, got $Tag" }
if ($SetupPath) {
$reported = (& $SetupPath --version | Out-String).Trim()
if ($LASTEXITCODE -ne 0 -or $reported -cne $version) { throw 'Setup executable version mismatch.' }
$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.' }
# 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))
$payload = [IO.MemoryStream]::new()
try {
if ($stream.Length -lt 24) { throw 'Missing payload footer.' }
$stream.Position = $stream.Length - 24
$reader = [IO.BinaryReader]::new($stream, [Text.Encoding]::ASCII, $true)
try {
$magic = [Text.Encoding]::ASCII.GetString($reader.ReadBytes(8))
$offset = $reader.ReadInt64()
$length = $reader.ReadInt64()
} finally { $reader.Dispose() }
if ($magic -cne 'MKWCPAY1' -or $offset -lt 0 -or $length -le 0 -or $offset + $length -ne $stream.Length - 24) {
throw 'Invalid payload footer.'
}
$stream.Position = $offset
$buffer = [byte[]]::new(1048576)
while ($length -gt 0) {
$count = $stream.Read($buffer, 0, [int][Math]::Min($length, $buffer.Length))
if ($count -eq 0) { throw 'Truncated payload.' }
$payload.Write($buffer, 0, $count)
$length -= $count
}
$payload.Position = 0
$zip = [IO.Compression.ZipArchive]::new($payload, [IO.Compression.ZipArchiveMode]::Read, $true)
try {
$entry = $zip.GetEntry('payload-manifest.json')
if (-not $entry) { throw 'Missing payload manifest.' }
$textReader = [IO.StreamReader]::new($entry.Open())
try { $manifest = $textReader.ReadToEnd() | ConvertFrom-Json } finally { $textReader.Dispose() }
if ($manifest.ProductId -cne $info.productId -or $manifest.ProductVersion -cne $version) {
throw 'Payload identity/version does not match the executable.'
}
} finally { $zip.Dispose() }
} finally { $payload.Dispose(); $stream.Dispose() }
}
Write-Output "Verified WiiCompiled OpenXR VR $version"
@@ -26,7 +26,7 @@ public static class RuntimeConfiguration
/// <summary>The per-user configuration an ordinary (non-portable) installation shares.</summary>
public static string ApplicationDataConfigPath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"WiiCompiled", ConfigFileName);
OperatingSystem.IsWindows() ? "WiiCompiledOpenXRVR" : "WiiCompiled", ConfigFileName);
/// <summary>
/// The configuration file that governs <paramref name="installDirectory"/>. This mirrors
@@ -14,6 +14,7 @@ internal enum AppMode
CheckProducts,
RepairProducts,
Version,
InfoJson,
EmitPayloadIdentities
}
@@ -58,6 +59,7 @@ internal sealed class CommandLine
switch (args[i].ToLowerInvariant())
{
case "--version": result.Mode = AppMode.Version; break;
case "--info-json": result.Mode = AppMode.InfoJson; break;
case "--silent": result.Mode = AppMode.SilentInstall; break;
case "--verify-inputs": result.Mode = AppMode.VerifyInputs; break;
case "--uninstall": result.Mode = AppMode.Uninstall; break;
@@ -19,6 +19,7 @@ internal static class ConsoleCommands
Console.Out.WriteLine(" --launch-retro | --launch-base");
Console.Out.WriteLine(" --uninstall --install-dir <dir>");
Console.Out.WriteLine(" --version");
Console.Out.WriteLine(" --info-json");
return 0;
}
@@ -28,6 +29,15 @@ internal static class ConsoleCommands
return 0;
}
public static int InfoJson()
{
Console.Out.WriteLine(JsonSerializer.Serialize(new
{
productId = ProductInfo.Id, version = ProductInfo.Version, openxrD3D12 = true
}));
return 0;
}
public static int EmitPayloadIdentities(CommandLine command)
{
@@ -7,6 +7,7 @@ internal static class GameLaunchService
public static Task<int> LaunchAsync(BuildProfile profile)
{
var installation = new Installation(AppContext.BaseDirectory);
ProductOwnership.Ensure(installation.Root);
using var operation = InstallOperationLock.Acquire(installation.Root);
// A portable installation the user moved is reconciled before anything is read from its
// recorded location. Launch never needs the discarded native build tree.
@@ -12,6 +12,7 @@ internal sealed class InstallerEngine
{
var installDirectory = Path.GetFullPath(options.InstallDirectory);
ValidateInstallDirectory(installDirectory);
ProductOwnership.Ensure(installDirectory, allowEmpty: true);
InputValidation.ValidateExtension(options.GamePath);
var parent = Directory.GetParent(installDirectory)?.FullName
@@ -37,6 +38,8 @@ internal sealed class InstallerEngine
using var payload = PayloadArchive.OpenCurrent();
var manifest = payload.ReadManifest();
if (manifest.ProductId != ProductInfo.Id || manifest.ProductVersion != ProductInfo.Version)
throw new InvalidDataException("This setup payload does not match WiiCompiled OpenXR VR and its version.");
var toolkit = InstalledLayout.Toolkit(staging);
var workspace = InstalledLayout.Workspace(staging);
@@ -366,6 +369,7 @@ internal sealed class InstallerEngine
ProductVersion = manifest.ProductVersion,
InstallDir = installDirectory,
InstalledUtc = previousState?.InstalledUtc ?? DateTime.UtcNow,
ProductId = ProductInfo.Id,
RetroRewindInstalled = previousState?.RetroRewindInstalled ?? false,
ToolkitReleaseTag = manifest.ToolkitReleaseTag,
DolSha256 = manifest.ExpectedDolSha256,
@@ -35,6 +35,7 @@ internal sealed class InstallOptions
internal sealed class PayloadManifest
{
public string ProductId { get; set; } = "";
public int SchemaVersion { get; set; }
public string ProductVersion { get; set; } = "";
public string ExpectedGameId { get; set; } = "RMCP01";
@@ -72,6 +73,7 @@ internal sealed class DiscHeader
internal sealed class InstallState
{
public string ProductId { get; set; } = "";
[JsonRequired]
public int SchemaVersion { get; set; } = 1;
@@ -0,0 +1,29 @@
using System.Text.Json;
namespace WiiCompiled.Setup.Windows;
internal static class ProductOwnership
{
public static void Ensure(string directory, bool allowEmpty = false)
{
for (var current = new DirectoryInfo(Path.GetFullPath(directory)); current is not null; current = current.Parent)
if (current.Exists && (current.Attributes & FileAttributes.ReparsePoint) != 0)
throw new InvalidDataException("Use a VR installation directory without junctions or symbolic links.");
if (!Directory.Exists(directory))
{
if (allowEmpty) return;
throw new InvalidDataException("WiiCompiled OpenXR VR is not installed in this directory.");
}
// A junction must not let an apparently separate VR destination overwrite another install.
if (allowEmpty && !Directory.EnumerateFileSystemEntries(directory).Any()) return;
var statePath = Path.Combine(directory, InstalledLayout.InstallStateFileName);
if (File.Exists(statePath))
{
using var state = JsonDocument.Parse(File.ReadAllText(statePath));
if (state.RootElement.TryGetProperty("ProductId", out var id) && id.GetString() == ProductInfo.Id)
return;
}
throw new InvalidDataException("This folder belongs to a normal or unidentified WiiCompiled installation. " +
"Choose a separate empty folder for OpenXR VR; existing files have not been changed.");
}
}
@@ -77,6 +77,7 @@ internal sealed class ProductRepairService
RetroWfcPayloadMode requestedPayloadMode, ReconcileOptions options,
CancellationToken cancellationToken)
{
ProductOwnership.Ensure(_installation.Root);
if (!_installation.IsPresent)
throw new InvalidOperationException("WiiCompiled is not installed here.");
if (!_installation.HasToolkit)
@@ -16,7 +16,7 @@ internal static class Program
{
Console.Out.WriteLine(
"Mario Kart WiiCompiled is installed through Wheel Wizard - download it from " +
"https://github.com/TeamWheelWizard/WheelWizard");
"https://github.com/iChris4/WheelWizard_VR");
Console.ReadKey(intercept: true);
return 0;
}
@@ -37,6 +37,8 @@ internal static class Program
if (command.Mode == AppMode.Version)
return ConsoleCommands.Version();
if (command.Mode == AppMode.InfoJson)
return ConsoleCommands.InfoJson();
if (command.Mode == AppMode.Help)
return ConsoleCommands.Help();
@@ -120,8 +122,9 @@ internal static class PlatformChecks
internal static class ProductInfo
{
public const string Name = "WiiCompiled";
public const string Version = "0.2.31";
public const string Name = "WiiCompiled OpenXR VR";
public const string Id = "wiicompiled-openxr-vr";
public static string Version => typeof(ProductInfo).Assembly.GetName().Version!.ToString(3);
/// <summary>
/// The setup executable is copied into the installation under this name. It is the launcher and
@@ -129,8 +132,8 @@ internal static class ProductInfo
/// </summary>
public const string SetupCopyName = "WiiCompiled-Setup.exe";
public const string UninstallKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\WiiCompiled";
public const string UninstallKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\WiiCompiledOpenXRVR";
public static string DefaultInstallDirectory =>
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Programs", "WiiCompiled");
"Programs", "WiiCompiledOpenXRVR");
}
@@ -6,9 +6,36 @@ namespace WiiCompiled.Setup.Windows;
internal static class SelfTests
{
private static void TestVrOwnership()
{
var root = Path.Combine(Path.GetTempPath(), "WiiCompiled-VR-ownership-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
try
{
ProductOwnership.Ensure(root, allowEmpty: true);
var statePath = Path.Combine(root, InstalledLayout.InstallStateFileName);
File.WriteAllText(statePath, "{\"SetupVersion\":\"0.2.32\"}");
var original = File.ReadAllText(statePath);
try { ProductOwnership.Ensure(root, allowEmpty: true); throw new Exception("Accepted an unidentified install."); }
catch (InvalidDataException) { }
if (File.ReadAllText(statePath) != original) throw new Exception("Normal state was changed by rejection.");
File.WriteAllText(statePath, "{\"ProductId\":\"wiicompiled\"}");
try { ProductOwnership.Ensure(root); throw new Exception("Accepted a normal install."); }
catch (InvalidDataException) { }
JsonState.Write(statePath, new InstallState { ProductId = ProductInfo.Id, InstallDir = root });
ProductOwnership.Ensure(root);
if (RuntimeConfiguration.ApplicationDataConfigPath.Contains("WiiCompiled\\Config.toml", StringComparison.Ordinal))
throw new Exception("The VR setup still uses normal application data.");
if (!ProductInfo.UninstallKey.EndsWith("WiiCompiledOpenXRVR", StringComparison.Ordinal))
throw new Exception("The VR uninstall identity is not isolated.");
}
finally { Directory.Delete(root, recursive: true); }
}
public static int Run()
{
var failures = new List<string>();
Test("VR installation ownership", TestVrOwnership, failures);
Test("ISO extension", () => TestAcceptedExtension(".iso"), failures);
Test("GCM extension", () => TestAcceptedExtension(".gcm"), failures);
Test("GCZ extension", () => TestAcceptedExtension(".gcz"), failures);
@@ -1291,7 +1318,7 @@ internal static class SelfTests
RetroWfcPayloadMode = "skipped" });
JsonState.Write(installation.InstallStatePath, new InstallState
{
InstallDir = root, RetroRewindInstalled = true,
ProductId = ProductInfo.Id, InstallDir = root, RetroRewindInstalled = true,
DolSha256 = dolSha, RelSha256 = relSha,
RetroRewindCodePulSha256 = inputsA.CodePulSha256,
RetroRewindCompileInputsSha256 = inputsA.CompileInputsSha256,
@@ -4,7 +4,7 @@ namespace WiiCompiled.Setup.Windows;
internal static class ShellIntegration
{
private const string ShortcutFileName = "wiicompiled (base) (beta).lnk";
private const string ShortcutFileName = "WiiCompiled OpenXR VR (base) (beta).lnk";
public static void RegisterUninstaller(string installDirectory, bool retroInstalled)
{
@@ -28,6 +28,9 @@ internal static class UninstallService
public static int RunWorker(string installDirectory, bool quiet)
{
installDirectory = Path.GetFullPath(installDirectory);
ProductOwnership.Ensure(installDirectory);
using var operation = InstallOperationLock.Acquire(installDirectory);
RunningProductGuard.EnsureProductsNotRunning(installDirectory);
Thread.Sleep(750);
TryCleanup("remove shortcuts", quiet, ShellIntegration.RemoveShortcuts);
@@ -7,9 +7,8 @@
<AssemblyName>WiiCompiled.Setup</AssemblyName>
<RootNamespace>WiiCompiled.Setup.Windows</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>0.2.31</Version>
<Authors>patchzy</Authors>
<Product>WiiCompiled</Product>
<Product>WiiCompiled OpenXR VR</Product>
<Description>Command-line installer and launcher for WiiCompiled</Description>
<DebugType>embedded</DebugType>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
+2
View File
@@ -0,0 +1,2 @@
64fdd492f6ce71fd223b49f493e0809d9a7f9ed45ebaa3605f626eb77282dbe4 WiiCompiled-Setup.exe
b319b86d46bea6ebfa52965c65706976d6004abc01914ff82a2b73c71de84def WheelWizardVRWindows.exe