mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-29 15:38:27 -04:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22c13cd785 | |||
| 6b853ca371 | |||
| 77a8623416 | |||
| 10ab54adee | |||
| a05c89739d | |||
| 82991ec337 | |||
| e164af9ff4 | |||
| e409d9f99b | |||
| 85f250155f | |||
| a9c9c0de26 | |||
| c92e45c2e7 | |||
| 583e702547 |
@@ -107,7 +107,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
# Wheel Wizard runs the downloaded setup with --version and refuses it when the reported
|
||||
# version differs from the release tag, so a tag that was pushed without bumping every pinned
|
||||
# version differs from the release tag, so a tag that was pushed without bumping the pinned
|
||||
# version would ship an update nobody can install. Catch that before anything is published.
|
||||
- name: Verify the tag matches the pinned setup version
|
||||
env:
|
||||
@@ -122,9 +122,7 @@ jobs:
|
||||
status=1
|
||||
fi
|
||||
}
|
||||
check Launcher/WiiCompiled.Setup.Windows/Program.cs "public const string Version = \"$version\";"
|
||||
check Launcher/WiiCompiled.Setup.Windows/WiiCompiled.Setup.Windows.csproj "<Version>$version</Version>"
|
||||
check Launcher/Build-Installer.ps1 "ProductVersion = '$version'"
|
||||
check Launcher/Directory.Build.props "<Version>$version</Version>"
|
||||
exit $status
|
||||
|
||||
# The build jobs upload with `archive: false`, which stores each installer as a raw file
|
||||
|
||||
@@ -279,9 +279,12 @@ foreach ($required in @('ToolkitFingerprint','TranslationFingerprint','NativeToo
|
||||
if ([string]::IsNullOrWhiteSpace($identities.$required)) { throw "Payload identity output is missing $required." }
|
||||
}
|
||||
|
||||
$productVersion = ((& $setupHost --version) -join '').Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($productVersion)) { throw 'The setup host did not report its version.' }
|
||||
|
||||
$manifest = [ordered]@{
|
||||
SchemaVersion = 2
|
||||
ProductVersion = '0.2.32'
|
||||
ProductVersion = $productVersion
|
||||
ExpectedGameId = $pins.GameId
|
||||
ExpectedDolSha256 = $pins.DolSha256
|
||||
ExpectedRelSha256 = $pins.RelSha256
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- The one product version. Release tags must match it. -->
|
||||
<Version>0.2.33</Version>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+10
-2
@@ -103,7 +103,7 @@ function Write-MkwBuildStep([string]$StepId, [string]$Message) {
|
||||
function Reset-LocalDirectory([string]$Path) {
|
||||
$full = [IO.Path]::GetFullPath($Path)
|
||||
$root = [IO.Path]::GetFullPath($Workspace).TrimEnd('\') + '\'
|
||||
$installRoot = [IO.Path]::GetFullPath((Split-Path -Parent $Workspace)).TrimEnd('\') + '\'
|
||||
$installRoot = [IO.Path]::GetFullPath((Split-Path -Parent $realWorkspace)).TrimEnd('\') + '\'
|
||||
# The caller-supplied output destinations are legitimate reset targets by
|
||||
# definition, wherever the caller placed them: a fresh install's operation
|
||||
# scratch lives beside the installation directory rather than inside it.
|
||||
@@ -150,8 +150,16 @@ if ($Profile -eq 'both' -and [string]::IsNullOrWhiteSpace($BaseOutputDirectory))
|
||||
if ($Profile -ne 'both' -and -not [string]::IsNullOrWhiteSpace($BaseOutputDirectory)) {
|
||||
throw '-BaseOutputDirectory is valid only with -Profile both.'
|
||||
}
|
||||
$realWorkspace = $Workspace.TrimEnd('\')
|
||||
$Workspace = Get-MkwBuildSafePath $realWorkspace 'workspace' 'runtime\CMakeLists.txt'
|
||||
# A selected package inside the install may also name the same Code.pul through the real path.
|
||||
# Give it the workspace spelling before comparing it with the staged copy.
|
||||
if (-not [string]::IsNullOrWhiteSpace($RetroRewindPackageDirectory) -and
|
||||
$RetroRewindPackageDirectory.StartsWith($realWorkspace + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
$RetroRewindPackageDirectory = $Workspace + $RetroRewindPackageDirectory.Substring($realWorkspace.Length)
|
||||
}
|
||||
$translator = Join-Path $Toolkit 'Translator\Translator.Cli.exe'
|
||||
$toolchain = Get-MkwShellSafeToolchainRoot $Toolkit
|
||||
$toolchain = Get-MkwBuildSafePath $Toolkit 'toolchain' 'CMake\bin\cmake.exe'
|
||||
$cmake = Join-Path $toolchain 'CMake\bin\cmake.exe'
|
||||
$ninja = Join-Path $toolchain 'Ninja\ninja.exe'
|
||||
$toolchainBin = Join-Path $toolchain 'llvm-mingw\bin'
|
||||
|
||||
@@ -40,41 +40,55 @@ function Get-MkwToolchainPath([string]$ToolchainRoot) {
|
||||
) -join ';')
|
||||
}
|
||||
|
||||
function Get-MkwShellSafeToolchainRoot([string]$ToolchainRoot) {
|
||||
if ([string]::IsNullOrWhiteSpace($ToolchainRoot)) { throw 'A toolchain root is required.' }
|
||||
$full = [IO.Path]::GetFullPath($ToolchainRoot)
|
||||
function Get-MkwBuildSafePath([string]$Path, [string]$Kind, [string]$MarkerFile) {
|
||||
<#
|
||||
The Windows native build passes workspace paths through CMake, Ninja response files and
|
||||
clang, which do not all interpret quotes the same way. Keep those paths plain even when the
|
||||
user's install directory contains an apostrophe, ampersand or other punctuation.
|
||||
#>
|
||||
if ([string]::IsNullOrWhiteSpace($Path)) { throw "A $Kind path is required." }
|
||||
$full = [IO.Path]::GetFullPath($Path)
|
||||
# A drive root keeps its separator: "C:" is relative to the current directory on that drive.
|
||||
if ($full -ne [IO.Path]::GetPathRoot($full)) { $full = $full.TrimEnd('\') }
|
||||
if ($full -notmatch '[()&^%!]') { return $full }
|
||||
if ($full -cmatch '^[A-Za-z0-9 ._\\:-]+$') { return $full }
|
||||
|
||||
Assert-File (Join-Path $full $MarkerFile) "$Kind marker"
|
||||
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($full.ToLowerInvariant()))
|
||||
} finally { $sha.Dispose() }
|
||||
$linkName = 'toolchain-' + ((($bytes[0..7]) | ForEach-Object { $_.ToString('x2') }) -join '')
|
||||
$linkName = "$Kind-" + ((($bytes[0..7]) | ForEach-Object { $_.ToString('x2') }) -join '')
|
||||
|
||||
$failures = @()
|
||||
foreach ($base in @($env:ProgramData, $env:PUBLIC)) {
|
||||
if ([string]::IsNullOrWhiteSpace($base) -or $base -match '[()&^%! ]') { continue }
|
||||
if ([string]::IsNullOrWhiteSpace($base) -or $base -cnotmatch '^[A-Za-z0-9._\\:-]+$') { continue }
|
||||
$link = Join-Path (Join-Path $base 'WiiCompiled') $linkName
|
||||
try {
|
||||
[IO.Directory]::CreateDirectory((Split-Path -Parent $link)) | Out-Null
|
||||
# The name already identifies the target, so an existing junction that still resolves is
|
||||
# this one; only a broken leftover is replaced. Directory.Delete removes the reparse
|
||||
# point itself, where Remove-Item -Recurse would delete the toolchain it points at.
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $link 'CMake\bin\cmake.exe') -PathType Leaf)) {
|
||||
if (Test-Path -LiteralPath $link) { [IO.Directory]::Delete($link) }
|
||||
$existing = Get-Item -LiteralPath $link -Force -ErrorAction SilentlyContinue
|
||||
if ($null -ne $existing) {
|
||||
# Never trust a directory just because it has the marker: it could point at a
|
||||
# different installation. Nor may we remove a directory we did not create.
|
||||
if ($existing.LinkType -ne 'Junction' -or
|
||||
@($existing.Target).Count -ne 1 -or
|
||||
-not [string]::Equals([IO.Path]::GetFullPath(@($existing.Target)[0]),
|
||||
$full, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "An existing path is not the expected junction: $link"
|
||||
}
|
||||
} else {
|
||||
New-Item -ItemType Junction -Path $link -Target $full -ErrorAction Stop | Out-Null
|
||||
}
|
||||
Write-Host "MKWCBUILD: Building through $link, because $full contains characters cmd.exe cannot parse"
|
||||
Assert-File (Join-Path $link $MarkerFile) "$Kind junction marker"
|
||||
Write-Host "MKWCBUILD: Building the $Kind through $link, because $full contains characters the native build cannot quote reliably"
|
||||
return $link
|
||||
} catch {
|
||||
$failures += "$link ($($_.Exception.Message))"
|
||||
}
|
||||
}
|
||||
throw ("The toolchain path $full contains a character (one of ( ) & ^ % !) that the compiler " +
|
||||
'cannot be invoked through, and no junction to it could be created: ' + ($failures -join '; ') +
|
||||
'. Install to a path without those characters.')
|
||||
throw ("The $Kind path $full cannot be passed safely to the native build, and no junction " +
|
||||
'to it could be created: ' + ($failures -join '; ') + '. Install to a path of plain ' +
|
||||
'letters, digits and spaces, or make a safe junction location available.')
|
||||
}
|
||||
|
||||
function Get-MkwProjectPins([string]$ProjectFile) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the redistributable precompiled aurora + third-party package for native Linux: aurora
|
||||
# (~43% of local build CPU time per Prepare-NativePrebuilt.ps1) and vendored Crypto++ are identical
|
||||
# (~43% of local build CPU time per Prepare-NativePrebuilt.ps1), Crypto++ and mbed TLS are identical
|
||||
# for every user under the pinned toolchain prepare-portable-tools.sh bundles, so this configures
|
||||
# runtime/ against that toolchain, builds just that closure, and harvests the archives plus a
|
||||
# generated CMake description into an output package - the Linux counterpart to
|
||||
@@ -39,8 +39,7 @@ Usage: Prepare-NativePrebuilt.sh --arch {x86_64|aarch64} [options]
|
||||
--reuse-stage Reuse an existing staging build directory (maintainer iteration aid: a
|
||||
re-harvest does not recompile aurora from scratch)
|
||||
--parallel N Ninja build parallelism (default: nproc)
|
||||
--print-fingerprint-only Print the four provenance inputs (compiler_sha256, flag_fingerprint,
|
||||
aurora_fingerprint, third_party_fingerprint) as "key=value" lines and
|
||||
--print-fingerprint-only Print the provenance inputs as "key=value" lines and
|
||||
exit, without configuring/building/harvesting anything - lets a caller
|
||||
(build-appimage.sh) decide whether an existing package is still current
|
||||
without paying for a full aurora rebuild just to find out.
|
||||
@@ -131,6 +130,8 @@ fixed_configure_flags=(
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_absl=ON
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_PNG=ON
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_Freetype=ON
|
||||
-DUSE_STATIC_MBEDTLS_LIBRARY=ON
|
||||
-DUSE_SHARED_MBEDTLS_LIBRARY=OFF
|
||||
# Freetype's own vendored CMakeLists.txt separately probes for system BZip2 (optional
|
||||
# bzip2-compressed-font support aurora-main never asked for) regardless of the Freetype
|
||||
# find_package disable above, since that only stops aurora's own outer find_package(Freetype)
|
||||
@@ -148,11 +149,10 @@ flag_fingerprint=$(printf '%s\n' "${fixed_configure_flags[@]}" | sha256sum | awk
|
||||
aurora_fingerprint=$(fingerprint_tree "$aurora_source" extern build)
|
||||
[[ -n "$aurora_fingerprint" ]] || fail "The aurora source tree could not be fingerprinted: $aurora_source"
|
||||
|
||||
# The harvested Crypto++ archive is consumed against this tree's headers, so it is fingerprinted
|
||||
# for the same reason as aurora above. No exclusions: unlike aurora's extern/, nothing under
|
||||
# runtime/third_party is shipped separately.
|
||||
# The harvested Crypto++ archive is consumed against this tree's headers, so it is fingerprinted.
|
||||
third_party_fingerprint=$(fingerprint_tree "$runtime_source/third_party")
|
||||
[[ -n "$third_party_fingerprint" ]] || fail "The vendored third-party tree could not be fingerprinted: $runtime_source/third_party"
|
||||
mbedtls_fingerprint=$(sha256_of "$runtime_source/cmake/MbedTLSPin.cmake")
|
||||
|
||||
compiler_sha256=$(sha256_of "$clang_binary")
|
||||
|
||||
@@ -161,6 +161,7 @@ if [[ "$print_fingerprint_only" -eq 1 ]]; then
|
||||
printf 'flag_fingerprint=%s\n' "$flag_fingerprint"
|
||||
printf 'aurora_fingerprint=%s\n' "$aurora_fingerprint"
|
||||
printf 'third_party_fingerprint=%s\n' "$third_party_fingerprint"
|
||||
printf 'mbedtls_fingerprint=%s\n' "$mbedtls_fingerprint"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -361,6 +362,21 @@ while IFS='|' read -r name type file linkerfile; do
|
||||
linker_file_to_reference["$linkerfile"]="@PKG@/$relative_linker"
|
||||
fi
|
||||
done < "$targets_txt"
|
||||
mbedtls_refs=()
|
||||
mbedtls_txt="$export_dir/mbedtls.txt"
|
||||
assert_file "$mbedtls_txt" "Mbed TLS export targets list"
|
||||
while IFS='|' read -r name linkerfile; do
|
||||
[[ -n "$name" ]] || continue
|
||||
linkerfile=$(normalize "$linkerfile")
|
||||
ref=${linker_file_to_reference["$linkerfile"]:-}
|
||||
[[ -n "$ref" ]] || fail "Mbed TLS archive was not harvested: $name ($linkerfile)"
|
||||
mbedtls_refs+=("$ref")
|
||||
done < "$mbedtls_txt"
|
||||
[[ ${#mbedtls_refs[@]} -eq 3 ]] || fail "Expected three Mbed TLS archives, got ${#mbedtls_refs[@]}"
|
||||
mbedtls_source_dir=$(get_meta mbedtls_source_dir)
|
||||
assert_dir "$mbedtls_source_dir/include/mbedtls" "Mbed TLS headers"
|
||||
mkdir -p "$output_dir/include/mbedtls"
|
||||
cp -a "$mbedtls_source_dir/include/." "$output_dir/include/mbedtls/"
|
||||
# Unlike Windows (SDL/zlib/libpng ship as DLLs by default), everything here was forced static above
|
||||
# and Dawn's own Linux package (verified directly) ships libwebgpu_dawn.a, also static - so zero
|
||||
# shared imports is the expected, normal outcome, not a failure.
|
||||
@@ -426,6 +442,9 @@ for item in "${link_items[@]}"; do
|
||||
if [[ "$item" = /* ]]; then item_abs=$(normalize "$item"); else item_abs=$(normalize "$stage_dir/$item"); fi
|
||||
ref=${linker_file_to_reference["$item_abs"]:-}
|
||||
if [[ -n "$ref" ]]; then
|
||||
for mbedtls_ref in "${mbedtls_refs[@]}"; do
|
||||
[[ "$ref" == "$mbedtls_ref" ]] && continue 2
|
||||
done
|
||||
package_link_items+=("$ref")
|
||||
continue
|
||||
fi
|
||||
@@ -505,6 +524,9 @@ generated_cmake="$output_dir/native_prebuilt.cmake"
|
||||
format_cmake_block MKW_NP_COMPILE_DEFINITIONS "${package_definitions[@]}"
|
||||
format_cmake_block MKW_NP_COMPILE_OPTIONS "${package_compile_options[@]}"
|
||||
format_cmake_block MKW_NP_LINK_LIBRARIES "${package_link_items[@]}"
|
||||
format_cmake_block MKW_NP_MBEDTLS_LIBRARIES "${mbedtls_refs[@]}"
|
||||
echo 'set(MKW_NP_MBEDTLS_INCLUDE_DIR "@PKG@/include/mbedtls")'
|
||||
printf 'set(MKW_NP_MBEDTLS_FINGERPRINT "%s")\n' "$mbedtls_fingerprint"
|
||||
format_cmake_block MKW_NP_AURORA_TARGETS "aurora::gx" "aurora::pad" "aurora::si" "aurora::vi" "aurora::mtx"
|
||||
echo ""
|
||||
printf 'set(MKW_NP_DAWN_CONFIG_DIR "%s")\n' "$dawn_config_token"
|
||||
@@ -540,12 +562,12 @@ harvested_count=${#linker_file_to_reference[@]}
|
||||
|
||||
python3 - "$output_dir" "$compiler_sha256" "$compiler_version" "$flag_fingerprint" \
|
||||
"$dawn_version" "$dawn_runtime_sha256" "$aurora_fingerprint" "$third_party_fingerprint" \
|
||||
"$sdl3_target" "$harvested_count" <<'PY'
|
||||
"$sdl3_target" "$harvested_count" "$mbedtls_fingerprint" <<'PY'
|
||||
import hashlib, json, os, sys, datetime
|
||||
|
||||
(output_dir, compiler_sha256, compiler_version, flag_fingerprint, dawn_version,
|
||||
dawn_runtime_sha256, aurora_fingerprint, third_party_fingerprint, sdl3_target,
|
||||
harvested_count) = sys.argv[1:]
|
||||
harvested_count, mbedtls_fingerprint) = sys.argv[1:]
|
||||
|
||||
contents = []
|
||||
for root, dirs, files in os.walk(output_dir):
|
||||
@@ -570,6 +592,7 @@ provenance = {
|
||||
"DawnRuntimeSha256": dawn_runtime_sha256,
|
||||
"AuroraSourceFingerprint": aurora_fingerprint,
|
||||
"ThirdPartySourceFingerprint": third_party_fingerprint,
|
||||
"MbedTlsFingerprint": mbedtls_fingerprint,
|
||||
"Sdl3Target": sdl3_target,
|
||||
"HarvestedLibraryCount": int(harvested_count),
|
||||
"Contents": contents,
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>WiiCompiled.Setup.Common.Cli</RootNamespace>
|
||||
<AssemblyName>WiiCompiled.Setup.Common.Cli</AssemblyName>
|
||||
<Version>0.2.32</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Packaging-time helper: resolves (downloading if needed) the nodtool binary bundled by build-appimage.sh and Build-Installer.ps1</Description>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>WiiCompiled.Setup.Common</RootNamespace>
|
||||
<AssemblyName>WiiCompiled.Setup.Common</AssemblyName>
|
||||
<Version>0.2.32</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Shared nodtool/Retro-WFC-payload logic used by both the Windows and Linux installers</Description>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace WiiCompiled.Setup.Linux;
|
||||
|
||||
internal static class ProductInfo
|
||||
{
|
||||
public const string Name = "WiiCompiled";
|
||||
public const string Version = "0.2.32";
|
||||
public static readonly string Version =
|
||||
typeof(ProductInfo).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
|
||||
.InformationalVersion;
|
||||
}
|
||||
|
||||
/// <summary>One installed product's record inside install-state.json.</summary>
|
||||
|
||||
@@ -127,19 +127,40 @@ internal static class Program
|
||||
string? retroWfcOfflineDir = null;
|
||||
if (downloadPayload)
|
||||
{
|
||||
// Reused if a previous install already downloaded and it's still valid - matches
|
||||
// Windows's own reuse-if-valid behavior instead of re-downloading on every install.
|
||||
var cacheDir = Path.Combine(workspace, "generated", "retro-wfc-payload");
|
||||
reporter.Progress(InstallStages.Validate, "Preparing the Retro-WFC payload", 1);
|
||||
reporter.Progress(InstallStages.Validate,
|
||||
"Downloading the current Retro-WFC payload", 1);
|
||||
try
|
||||
{
|
||||
RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir);
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
// A valid signature authenticates a payload, but does not prove it is the latest
|
||||
// signed revision. Always ask the fixed endpoint for the current snapshot; the
|
||||
// downloader verifies it before atomically replacing the cache.
|
||||
await RetroWfcPayload.DownloadRetroWfcPayloadAsync(
|
||||
RetroWfcPayload.CurrentRetroWfcPayloadUri, cacheDir, token);
|
||||
}
|
||||
catch (Exception downloadFailure) when (!token.IsCancellationRequested &&
|
||||
downloadFailure is HttpRequestException or TimeoutException
|
||||
or IOException)
|
||||
{
|
||||
// Offline installs may continue with a previously authenticated snapshot. Do not
|
||||
// use this path for a newly downloaded payload that failed signature validation:
|
||||
// that must remain a hard failure instead of hiding possible endpoint tampering.
|
||||
try
|
||||
{
|
||||
RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir);
|
||||
}
|
||||
catch (Exception cacheFailure) when (cacheFailure is IOException or
|
||||
UnauthorizedAccessException or InvalidDataException)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current Retro-WFC payload could not be downloaded and no valid cached " +
|
||||
$"payload is available ({cacheFailure.Message.TrimEnd('.')}).", downloadFailure);
|
||||
}
|
||||
|
||||
reporter.Diagnostic(
|
||||
"The current Retro-WFC payload could not be downloaded; using the previously " +
|
||||
$"verified cached payload instead ({downloadFailure.Message.TrimEnd('.')}).");
|
||||
}
|
||||
retroWfcOfflineDir = cacheDir;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>WiiCompiled.Setup.Linux</AssemblyName>
|
||||
<RootNamespace>WiiCompiled.Setup.Linux</RootNamespace>
|
||||
<Version>0.2.32</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Command-line installer and launcher for WiiCompiled on Linux</Description>
|
||||
|
||||
@@ -110,8 +110,7 @@ internal sealed class ProductRepairService
|
||||
InputValidation.CurrentRetroWfcPayloadUri, payloadScratch, cancellationToken);
|
||||
}
|
||||
catch (Exception ex) when (!cancellationToken.IsCancellationRequested &&
|
||||
ex is HttpRequestException or IOException or InvalidDataException
|
||||
or InvalidOperationException or OperationCanceledException)
|
||||
ex is HttpRequestException or TimeoutException or IOException)
|
||||
{
|
||||
payloadSnapshot = RecoverInstalledRetroWfcPayload(toolkitFingerprint,
|
||||
Path.Combine(scratchRoot, "retro-wfc-payload-recovered"), ex, cancellationToken);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace WiiCompiled.Setup.Windows;
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static class Program
|
||||
@@ -121,7 +122,9 @@ internal static class PlatformChecks
|
||||
internal static class ProductInfo
|
||||
{
|
||||
public const string Name = "WiiCompiled";
|
||||
public const string Version = "0.2.32";
|
||||
public static readonly string Version =
|
||||
typeof(ProductInfo).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
|
||||
.InformationalVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The setup executable is copied into the installation under this name. It is the launcher and
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<AssemblyName>WiiCompiled.Setup</AssemblyName>
|
||||
<RootNamespace>WiiCompiled.Setup.Windows</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Version>0.2.32</Version>
|
||||
<Authors>patchzy</Authors>
|
||||
<Product>WiiCompiled</Product>
|
||||
<Description>Command-line installer and launcher for WiiCompiled</Description>
|
||||
|
||||
@@ -128,9 +128,9 @@ cp -a "$workspace/Launcher/artifacts/portable-tools/toolchain-$appimagetool_arch
|
||||
|
||||
# Precompiled aurora + third-party package (see Prepare-NativePrebuilt.sh) so a user's own
|
||||
# local-build.sh never has to compile aurora itself (~43% of local build CPU time). Re-harvesting
|
||||
# recompiles the whole aurora/Crypto++ closure with the toolchain above, so this is skipped unless
|
||||
# recompiles the aurora/Crypto++/mbed TLS closure with the toolchain above, so this is skipped unless
|
||||
# --print-fingerprint-only (a fast, build-free check) says the existing package no longer matches
|
||||
# the current compiler/flags/aurora/third_party sources.
|
||||
# the current compiler, flags, source trees or mbed TLS pin.
|
||||
native_prebuilt_dir="$workspace/Launcher/artifacts/native-prebuilt-$appimagetool_arch"
|
||||
echo "Checking whether the precompiled aurora + third-party package ($appimagetool_arch) is current..."
|
||||
current_fingerprint=$(bash "$script_dir/Prepare-NativePrebuilt.sh" --arch "$appimagetool_arch" --print-fingerprint-only)
|
||||
@@ -148,6 +148,7 @@ fields = {
|
||||
"flag_fingerprint": "FlagFingerprint",
|
||||
"aurora_fingerprint": "AuroraSourceFingerprint",
|
||||
"third_party_fingerprint": "ThirdPartySourceFingerprint",
|
||||
"mbedtls_fingerprint": "MbedTlsFingerprint",
|
||||
}
|
||||
print(1 if all(provenance.get(v) == current.get(k) for k, v in fields.items()) else 0)
|
||||
PY
|
||||
|
||||
@@ -190,6 +190,30 @@ assert_file "$project" "Translation project"
|
||||
assert_file "$assets/main.dol" "Extracted main.dol (see translator/README.md - owning the game is required)"
|
||||
assert_file "$assets/StaticR.rel" "Extracted StaticR.rel (see translator/README.md - owning the game is required)"
|
||||
|
||||
# The AppImage bundles Clang, but Linux startup objects and the C/C++ link runtimes
|
||||
# still come from the host. Check them before the expensive translation so a missing
|
||||
# development package produces a useful error instead of CMake's generic exit 1.
|
||||
link_probe_dir=$(mktemp -d)
|
||||
link_probe_flags=()
|
||||
[[ -z "$sysroot" ]] || link_probe_flags+=(--sysroot="$sysroot")
|
||||
[[ -z "$fuse_ld_override" ]] || link_probe_flags+=(-fuse-ld="$fuse_ld_override")
|
||||
printf 'int main(void) { return 0; }\n' > "$link_probe_dir/probe.c"
|
||||
cat > "$link_probe_dir/probe.cpp" <<'EOF'
|
||||
#include <vector>
|
||||
int main() { std::vector<int> values{1}; return values.front() - 1; }
|
||||
EOF
|
||||
if ! "$cc_bin" "${link_probe_flags[@]}" "$link_probe_dir/probe.c" -o "$link_probe_dir/probe-c" > "$link_probe_dir/error" 2>&1; then
|
||||
cat "$link_probe_dir/error" >&2
|
||||
rm -rf "$link_probe_dir"
|
||||
fail "The C compiler cannot link a test program. Linux needs C development files (glibc startup objects and a compiler runtime) in addition to bundled Clang. Install your distribution's development packages, or on SteamOS run WiiCompiled through the Wheel Wizard Flatpak."
|
||||
fi
|
||||
if ! "$cxx_bin" "${link_probe_flags[@]}" "$link_probe_dir/probe.cpp" -o "$link_probe_dir/probe-cxx" > "$link_probe_dir/error" 2>&1; then
|
||||
cat "$link_probe_dir/error" >&2
|
||||
rm -rf "$link_probe_dir"
|
||||
fail "The C++ compiler cannot link a test program. Install your distribution's C++ development packages, or on SteamOS run WiiCompiled through the Wheel Wizard Flatpak."
|
||||
fi
|
||||
rm -rf "$link_probe_dir"
|
||||
|
||||
# Literal line matching against the manifest's fixed shape, not a YAML dependency - the same
|
||||
# approach NativeBuildFlags.ps1's Get-MkwProjectPins uses on Windows, kept here only for the one
|
||||
# field this script actually needs from the manifest.
|
||||
|
||||
@@ -77,26 +77,40 @@ if [[ -n "$retro_dir" ]]; then
|
||||
# verify its pinned signature before publishing it into the local cache.
|
||||
retro_wfc_dir="$support_root/RetroWfcPayload"
|
||||
retro_wfc_payload="$retro_wfc_dir/binary/payload.RMCPD00.bin"
|
||||
if [[ -f "$retro_wfc_payload" ]] && ! "$translator" validate-retro-wfc-payload --directory "$retro_wfc_dir"; then
|
||||
printf 'Discarding an invalid cached Retro-WFC payload...\n' >&2
|
||||
rm -f "$retro_wfc_payload"
|
||||
cached_payload_valid=0
|
||||
if [[ -f "$retro_wfc_payload" ]]; then
|
||||
if "$translator" validate-retro-wfc-payload --directory "$retro_wfc_dir"; then
|
||||
cached_payload_valid=1
|
||||
else
|
||||
printf 'Discarding an invalid cached Retro-WFC payload...\n' >&2
|
||||
rm -f "$retro_wfc_payload"
|
||||
fi
|
||||
fi
|
||||
if [[ ! -f "$retro_wfc_payload" ]]; then
|
||||
printf 'Downloading the Retro-WFC payload needed for online play...\n'
|
||||
mkdir -p "$retro_wfc_dir"
|
||||
payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX")
|
||||
temporary_payload="$payload_stage/binary/payload.RMCPD00.bin"
|
||||
mkdir -p "$(dirname "$temporary_payload")"
|
||||
trap 'rm -rf "$payload_stage"' EXIT
|
||||
/usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 30 \
|
||||
--retry 1 --output "$temporary_payload" \
|
||||
'https://rwfc.net/api/wfc/payload?g=RMCPD00' || fail 'could not download the Retro-WFC payload needed for online play'
|
||||
|
||||
# A signed cache may still be an older vulnerable revision, so always attempt to replace it
|
||||
# with the current signed snapshot. A transport failure may fall back to the verified cache;
|
||||
# a downloaded snapshot with an invalid signature remains a hard failure.
|
||||
printf 'Downloading the current Retro-WFC payload needed for online play...\n'
|
||||
mkdir -p "$retro_wfc_dir"
|
||||
payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX")
|
||||
temporary_payload="$payload_stage/binary/payload.RMCPD00.bin"
|
||||
mkdir -p "$(dirname "$temporary_payload")"
|
||||
trap 'rm -rf "$payload_stage"' EXIT
|
||||
if /usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 30 \
|
||||
--retry 1 --output "$temporary_payload" \
|
||||
'https://rwfc.net/api/wfc/payload?g=RMCPD00'; then
|
||||
"$translator" validate-retro-wfc-payload --directory "$payload_stage" || \
|
||||
fail 'downloaded Retro-WFC payload failed signature validation'
|
||||
mkdir -p "$retro_wfc_dir/binary"
|
||||
mv "$temporary_payload" "$retro_wfc_payload"
|
||||
rmdir "$payload_stage/binary" "$payload_stage"
|
||||
trap - EXIT
|
||||
elif (( cached_payload_valid )); then
|
||||
printf 'Could not download the current Retro-WFC payload; using the previously verified cached payload.\n' >&2
|
||||
rm -rf "$payload_stage"
|
||||
trap - EXIT
|
||||
else
|
||||
fail 'could not download the current Retro-WFC payload and no valid cached payload is available'
|
||||
fi
|
||||
build_args+=(--profile both --base-output-dir "$products" --retro-rewind-package-dir "$retro_dir" --retro-wfc-offline-dir "$retro_wfc_dir")
|
||||
fi
|
||||
|
||||
@@ -88,6 +88,7 @@ typedef enum _AuroraViewportPolicy {
|
||||
AURORA_VIEWPORT_FIT = 0, // Preserve logical aspect in the content framebuffer
|
||||
AURORA_VIEWPORT_STRETCH = 1, // Match content framebuffer aspect to the native surface
|
||||
AURORA_VIEWPORT_NATIVE = 2, // Use active framebuffer pixels directly
|
||||
AURORA_VIEWPORT_16_9 = 3, // Fixed 16:9 content with bars on other surfaces
|
||||
} AuroraViewportPolicy;
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ void AuroraSetViewportPolicy(AuroraViewportPolicy policy) {
|
||||
}
|
||||
g_gxState.viewportPolicy = policy;
|
||||
aurora::window::set_frame_buffer_aspect_fit(policy == AURORA_VIEWPORT_FIT);
|
||||
aurora::window::set_force_aspect_16_9(policy == AURORA_VIEWPORT_16_9);
|
||||
aurora::window::set_present_surface_fill(policy == AURORA_VIEWPORT_STRETCH);
|
||||
if (changed) {
|
||||
// Reapply the guest viewport and scissor after a resize.
|
||||
|
||||
@@ -520,11 +520,9 @@ void GXCopyTex(void* dest, GXBool clear) {
|
||||
clearState.clearAlpha = clear && alphaUpdate;
|
||||
}
|
||||
const auto copyFilter = combined_copy_filter_coefficients(g_gxState.copyFilterVFilter);
|
||||
// Every GXCopyTex is observable texture data. Reusing a destination in this
|
||||
// or the previous frame does not guarantee another redraw: menu thumbnail
|
||||
// scratch targets can be reused and then retained. Depth copies have the
|
||||
// same requirement. Only display presentation may skip unfinished draws.
|
||||
const bool persistentCopy = true;
|
||||
// Skip only recurring color copies so one-shot copies are never lost.
|
||||
const bool producedConsecutively = handle.revision != 0 && currentFrame - handle.lastProducedFrame <= 1;
|
||||
const bool persistentCopy = !aurora::gx::is_depth_format(texCopyFmt) && !producedConsecutively;
|
||||
aurora::gfx::resolve_pass(handle.handle, rect, clearState.clearColor, clearState.clearAlpha, clearState.clearDepth,
|
||||
clearState.clearColorValue, aurora::gx::clear_depth_value(), resolveFmt,
|
||||
&sourceRect.sampleRect, g_gxState.texCopyHalfScale, ©Filter, forceOpaqueAlpha,
|
||||
|
||||
@@ -399,19 +399,20 @@ const char* PADGetNameForControllerIndex(const u32 idx) {
|
||||
}
|
||||
|
||||
void PADSetPortForIndex(const u32 idx, const u32 port) {
|
||||
if (port >= PAD_MAX_CONTROLLERS) return;
|
||||
const auto* ctrl = __PADGetControllerForIndex(idx);
|
||||
if (ctrl == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t oldPort = SDL_GetGamepadPlayerIndex(ctrl->m_controller);
|
||||
const int32_t oldPort = aurora::input::player_index(ctrl->m_index);
|
||||
if (const auto* dest = aurora::input::get_controller_for_player(port); dest != nullptr && dest != ctrl) {
|
||||
SDL_SetGamepadPlayerIndex(dest->m_controller, -1);
|
||||
aurora::input::set_player_index(dest->m_index, -1);
|
||||
}
|
||||
if (oldPort >= 0 && oldPort != port) {
|
||||
aurora::input::persist_controller_for_player(oldPort, nullptr);
|
||||
}
|
||||
SDL_SetGamepadPlayerIndex(ctrl->m_controller, static_cast<Sint32>(port));
|
||||
aurora::input::set_player_index(ctrl->m_index, static_cast<Sint32>(port));
|
||||
aurora::input::persist_controller_for_player(port, ctrl);
|
||||
}
|
||||
|
||||
@@ -437,7 +438,7 @@ void PADClearPort(const u32 port) {
|
||||
if (ctrl == nullptr) {
|
||||
return;
|
||||
}
|
||||
SDL_SetGamepadPlayerIndex(ctrl->m_controller, -1);
|
||||
aurora::input::set_player_index(ctrl->m_index, -1);
|
||||
}
|
||||
|
||||
// Secondary bindings live only in memory; the runtime re-applies them from its
|
||||
|
||||
@@ -1259,11 +1259,6 @@ void split_staging_batch() {
|
||||
retained[i].assign(buffers[i]->data(), buffers[i]->data() + g_suspendedEfbBytes[i]);
|
||||
}
|
||||
}
|
||||
// GXCopyTex can capture the ordinary EFB as well as an explicit offscreen
|
||||
// target. Its command may arrive in the next batch, after this prefix has
|
||||
// already been submitted. Preserve every prefix; target kind cannot tell
|
||||
// us whether the guest will later retain these pixels in a texture.
|
||||
for (auto& pass : g_renderPasses) pass.requireReadyPipelines = true;
|
||||
auto encoder = g_device.CreateCommandEncoder();
|
||||
end_batch(encoder);
|
||||
render(encoder);
|
||||
@@ -1614,8 +1609,8 @@ bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass, Pipelin
|
||||
if (!skip_unready_pipelines()) {
|
||||
pipelineReady = wait_pipeline(ref, pipeline);
|
||||
} else if (requireReady) {
|
||||
// Texture copies and capacity prefixes must retain complete draw results.
|
||||
// A future display frame cannot repair a texture that already captured them.
|
||||
// The pass resolves into a persistent texture (a one-shot bake such as MKW's minimap), so a
|
||||
// skipped draw would never be re-issued. These run behind loads, not mid-race.
|
||||
pipelineReady = wait_pipeline_for_persistent_pass(ref, pipeline);
|
||||
} else {
|
||||
pipelineReady = try_pipeline(ref, pipeline);
|
||||
|
||||
@@ -214,7 +214,8 @@ Params make_params(wgpu::Extent3D sourceSize, const FrameMapping& mapping) noexc
|
||||
return params;
|
||||
}
|
||||
|
||||
const bool stretch = mapping.viewportPolicy == AURORA_VIEWPORT_STRETCH;
|
||||
const bool stretch = mapping.viewportPolicy == AURORA_VIEWPORT_STRETCH ||
|
||||
mapping.viewportPolicy == AURORA_VIEWPORT_16_9;
|
||||
const float scaleX = static_cast<float>(sourceSize.width) / static_cast<float>(logicalSize.x);
|
||||
const float scaleY = static_cast<float>(sourceSize.height) / static_cast<float>(logicalSize.y);
|
||||
const float scale = std::min(scaleX, scaleY);
|
||||
|
||||
@@ -414,6 +414,29 @@ SDL_JoystickID add_controller(SDL_JoystickID which) noexcept {
|
||||
g_GameControllers[instance] = controller;
|
||||
ensure_player_index(g_GameControllers[instance]);
|
||||
apply_port_preferences();
|
||||
#if defined(SDL_PLATFORM_MACOS)
|
||||
// First-use convenience only: never override a saved assignment or None.
|
||||
if (g_portPreferences[0].state == PortPreferenceState::Unset) {
|
||||
bool hasOtherPortPreference = false;
|
||||
for (size_t port = 1; port < g_portPreferences.size(); ++port) {
|
||||
if (g_portPreferences[port].state == PortPreferenceState::Controller &&
|
||||
identity_match(g_portPreferences[port].identity, controller_identity(g_GameControllers[instance])) !=
|
||||
IdentityMatch::None) {
|
||||
hasOtherPortPreference = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasOtherPortPreference) {
|
||||
const auto* p0 = get_controller_for_player(0);
|
||||
if (p0 == nullptr) {
|
||||
assign_player_index(g_GameControllers[instance], 0);
|
||||
persist_controller_for_player(0, &g_GameControllers[instance]);
|
||||
} else if (p0 == &g_GameControllers[instance]) {
|
||||
persist_controller_for_player(0, &g_GameControllers[instance]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ SDL_Window* g_window;
|
||||
SDL_Renderer* g_renderer;
|
||||
float g_frameBufferScale = 0.f;
|
||||
bool g_frameBufferAspectFit = true;
|
||||
std::atomic_bool g_forceAspect169{false};
|
||||
bool g_presentSurfaceFill = false;
|
||||
int g_presentAspectWidth = 0;
|
||||
int g_presentAspectHeight = 0;
|
||||
@@ -525,7 +526,20 @@ AuroraWindowSize get_window_size() {
|
||||
int fb_w = native_fb_w;
|
||||
int fb_h = native_fb_h;
|
||||
const auto [baseW, baseH] = vi::configured_fb_size();
|
||||
if (g_frameBufferAspectFit && baseW > 0 && baseH > 0) {
|
||||
if (g_forceAspect169.load(std::memory_order_acquire) && native_fb_w > 0 && native_fb_h > 0) {
|
||||
if (g_frameBufferScale > 0.f && baseW > 0 && baseH > 0) {
|
||||
const auto [scaledW, scaledH] =
|
||||
scale_frame_buffer_to_aspect(static_cast<int>(baseW), static_cast<int>(baseH),
|
||||
g_frameBufferScale, 16.f / 9.f);
|
||||
fb_w = scaledW;
|
||||
fb_h = scaledH;
|
||||
} else {
|
||||
fb_w = std::min(native_fb_w,
|
||||
std::max(1, static_cast<int>(std::lround(native_fb_h * (16.f / 9.f)))));
|
||||
fb_h = std::min(native_fb_h,
|
||||
std::max(1, static_cast<int>(std::lround(native_fb_w * (9.f / 16.f)))));
|
||||
}
|
||||
} else if (g_frameBufferAspectFit && baseW > 0 && baseH > 0) {
|
||||
float renderScale = g_frameBufferScale > 0.f ? g_frameBufferScale : 1.f;
|
||||
if (g_frameBufferScale <= 0.f) {
|
||||
renderScale = std::min(static_cast<float>(native_fb_w) / static_cast<float>(baseW),
|
||||
@@ -759,6 +773,14 @@ void set_frame_buffer_aspect_fit(bool fit) {
|
||||
request_frame_buffer_resize();
|
||||
}
|
||||
|
||||
void set_force_aspect_16_9(bool force) {
|
||||
if (g_forceAspect169.load(std::memory_order_relaxed) == force) {
|
||||
return;
|
||||
}
|
||||
g_forceAspect169.store(force, std::memory_order_release);
|
||||
request_frame_buffer_resize();
|
||||
}
|
||||
|
||||
void set_present_surface_fill(bool fill) {
|
||||
g_presentSurfaceFill = fill;
|
||||
}
|
||||
@@ -781,6 +803,10 @@ void unlock_present_aspect_ratio() {
|
||||
}
|
||||
|
||||
bool get_present_aspect_ratio(float& aspect) noexcept {
|
||||
if (g_forceAspect169.load(std::memory_order_acquire)) {
|
||||
aspect = 16.f / 9.f;
|
||||
return true;
|
||||
}
|
||||
if (g_presentSurfaceFill && g_window != nullptr) {
|
||||
// Queried once per presentation snapshot; use the cached native client size
|
||||
// instead of re-entering SDL for a value the window procedure already knows.
|
||||
|
||||
@@ -56,6 +56,7 @@ void sync_frame_buffer_size() noexcept;
|
||||
void request_frame_buffer_resize();
|
||||
void set_frame_buffer_scale(float scale);
|
||||
void set_frame_buffer_aspect_fit(bool fit);
|
||||
void set_force_aspect_16_9(bool force);
|
||||
void set_present_surface_fill(bool fill);
|
||||
void lock_present_aspect_ratio(int width, int height);
|
||||
void unlock_present_aspect_ratio();
|
||||
|
||||
@@ -4267,7 +4267,7 @@ TEST_F(GXFifoTest, CopyTexColorFormatMarksResolvePersistent) {
|
||||
EXPECT_TRUE(records.front().persistentCopy);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, RecurringColorCopyPreservesEveryResolve) {
|
||||
TEST_F(GXFifoTest, RecurringColorCopyKeepsLaterResolveSkippable) {
|
||||
std::array<u8, 152 * 114 * 4> image{};
|
||||
gxState().pixelFmt = GX_PF_RGBA6_Z24;
|
||||
|
||||
@@ -4281,7 +4281,7 @@ TEST_F(GXFifoTest, RecurringColorCopyPreservesEveryResolve) {
|
||||
const auto& records = aurora::gfx::testing::resolve_pass_records();
|
||||
ASSERT_EQ(records.size(), 2u);
|
||||
EXPECT_TRUE(records[0].persistentCopy);
|
||||
EXPECT_TRUE(records[1].persistentCopy);
|
||||
EXPECT_FALSE(records[1].persistentCopy);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, ColorCopyAfterFrameGapRegainsPersistentProtection) {
|
||||
@@ -4301,7 +4301,7 @@ TEST_F(GXFifoTest, ColorCopyAfterFrameGapRegainsPersistentProtection) {
|
||||
EXPECT_TRUE(records[1].persistentCopy);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, CopyTexDepthFormatPreservesResolve) {
|
||||
TEST_F(GXFifoTest, CopyTexDepthFormatKeepsResolveSkippable) {
|
||||
std::array<u8, 4 * 4 * 4> image{};
|
||||
gxState().pixelFmt = GX_PF_RGBA6_Z24;
|
||||
|
||||
@@ -4311,7 +4311,7 @@ TEST_F(GXFifoTest, CopyTexDepthFormatPreservesResolve) {
|
||||
|
||||
const auto& records = aurora::gfx::testing::resolve_pass_records();
|
||||
ASSERT_EQ(records.size(), 1u);
|
||||
EXPECT_TRUE(records.front().persistentCopy);
|
||||
EXPECT_FALSE(records.front().persistentCopy);
|
||||
}
|
||||
|
||||
TEST_F(GXFifoTest, CopyDispResolveIsNotPersistent) {
|
||||
|
||||
+18
-19
@@ -113,11 +113,10 @@ endif()
|
||||
# compiles cleanly for Android with nothing beyond a plain C toolchain (no perl/asm build-script
|
||||
# dependency the way OpenSSL's build has), matching how this project already prefers toolchain-
|
||||
# simple libraries (see Crypto++ above, similarly stripped of ASM/SIMD for portability).
|
||||
# Fetched at build time from a pinned upstream release tarball with a checked SHA-256, the same way
|
||||
# aurora-main's own dependencies (SDL, zlib, etc.) are pulled in - not committed as a vendored
|
||||
# source tree, so the repository ships the compiled dependency rather than ~280 tracked upstream
|
||||
# files. Bump MKW_MBEDTLS_VERSION/MKW_MBEDTLS_SHA256 together when updating; the hash comes from
|
||||
# upstream's own signed `mbedtls-<version>-sha256sum.txt` release asset.
|
||||
# The from-source build fetches a pinned upstream tarball with a checked SHA-256.
|
||||
# The Linux native prebuilt package instead ships its compiled archives and headers.
|
||||
# Bump both values in cmake/MbedTLSPin.cmake when updating; the hash comes from
|
||||
# upstream's signed `mbedtls-<version>-sha256sum.txt` release asset.
|
||||
#
|
||||
# The alias exists on every platform so the link lines in cmake/PublicProducts.cmake stay
|
||||
# platform-independent, but it is only populated where network_ssl.cpp actually compiles the mbed
|
||||
@@ -128,20 +127,20 @@ endif()
|
||||
add_library(mkw_mbedtls INTERFACE)
|
||||
add_library(mkw::mbedtls ALIAS mkw_mbedtls)
|
||||
if(NOT MKW_PLATFORM_WINDOWS)
|
||||
include(FetchContent)
|
||||
set(MKW_MBEDTLS_VERSION "3.6.7")
|
||||
set(MKW_MBEDTLS_SHA256 "a7e8bcbec0e6f761b4af24f25677626b35f762f68eef79c08677a363212d11f6")
|
||||
FetchContent_Declare(mkw_mbedtls_upstream
|
||||
URL "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MKW_MBEDTLS_VERSION}/mbedtls-${MKW_MBEDTLS_VERSION}.tar.bz2"
|
||||
URL_HASH SHA256=${MKW_MBEDTLS_SHA256})
|
||||
# Subproject mode already defaults ENABLE_TESTING off and skips codegen (GEN_FILES), but
|
||||
# ENABLE_PROGRAMS defaults on and installation/package-config isn't wanted for a linked-in copy.
|
||||
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
|
||||
set(DISABLE_PACKAGE_CONFIG_AND_INSTALL ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(mkw_mbedtls_upstream)
|
||||
target_link_libraries(mkw_mbedtls INTERFACE MbedTLS::mbedtls MbedTLS::mbedx509 MbedTLS::mbedcrypto)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmake/MbedTLSPin.cmake")
|
||||
if(NOT MKW_NATIVE_PREBUILT_DIR)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(mkw_mbedtls_upstream
|
||||
URL "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MKW_MBEDTLS_VERSION}/mbedtls-${MKW_MBEDTLS_VERSION}.tar.bz2"
|
||||
URL_HASH SHA256=${MKW_MBEDTLS_SHA256})
|
||||
# Subproject mode already disables testing and codegen; it still enables programs.
|
||||
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
|
||||
set(DISABLE_PACKAGE_CONFIG_AND_INSTALL ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(mkw_mbedtls_upstream)
|
||||
target_link_libraries(mkw_mbedtls INTERFACE MbedTLS::mbedtls MbedTLS::mbedx509 MbedTLS::mbedcrypto)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(MKW_TRANSLATED_COMPILE_JOBS 0 CACHE STRING
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Keep the release and verified archive hash together. The Linux native prebuilt
|
||||
# fingerprint includes this file, so changing either value forces a new harvest.
|
||||
set(MKW_MBEDTLS_VERSION "3.6.7")
|
||||
set(MKW_MBEDTLS_SHA256 "a7e8bcbec0e6f761b4af24f25677626b35f762f68eef79c08677a363212d11f6")
|
||||
@@ -42,6 +42,28 @@ endfunction()
|
||||
mkw_np_resolve(_mkw_np_includes ${MKW_NP_INCLUDE_DIRECTORIES})
|
||||
mkw_np_resolve(_mkw_np_links ${MKW_NP_LINK_LIBRARIES})
|
||||
mkw_np_resolve(_mkw_np_dawn_config ${MKW_NP_DAWN_CONFIG_DIR})
|
||||
if(NOT MKW_PLATFORM_WINDOWS)
|
||||
if(NOT MKW_NP_MBEDTLS_LIBRARIES OR NOT MKW_NP_MBEDTLS_INCLUDE_DIR)
|
||||
message(FATAL_ERROR
|
||||
"The Linux native prebuilt package has no Mbed TLS archives; regenerate it with Prepare-NativePrebuilt.sh")
|
||||
endif()
|
||||
file(SHA256 "${CMAKE_CURRENT_LIST_DIR}/MbedTLSPin.cmake" _mkw_np_current_mbedtls_fingerprint)
|
||||
if(NOT MKW_NP_MBEDTLS_FINGERPRINT STREQUAL _mkw_np_current_mbedtls_fingerprint)
|
||||
message(FATAL_ERROR "The native prebuilt Mbed TLS version differs from this workspace; regenerate the package")
|
||||
endif()
|
||||
mkw_np_resolve(_mkw_np_mbedtls_links ${MKW_NP_MBEDTLS_LIBRARIES})
|
||||
mkw_np_resolve(_mkw_np_mbedtls_include ${MKW_NP_MBEDTLS_INCLUDE_DIR})
|
||||
foreach(_archive IN LISTS _mkw_np_mbedtls_links)
|
||||
if(NOT EXISTS "${_archive}")
|
||||
message(FATAL_ERROR "The native prebuilt package is missing Mbed TLS archive: ${_archive}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT IS_DIRECTORY "${_mkw_np_mbedtls_include}")
|
||||
message(FATAL_ERROR "The native prebuilt package is missing Mbed TLS headers: ${_mkw_np_mbedtls_include}")
|
||||
endif()
|
||||
target_include_directories(mkw_mbedtls SYSTEM INTERFACE "${_mkw_np_mbedtls_include}")
|
||||
target_link_libraries(mkw_mbedtls INTERFACE ${_mkw_np_mbedtls_links})
|
||||
endif()
|
||||
|
||||
foreach(_dir IN LISTS _mkw_np_includes)
|
||||
if(NOT IS_DIRECTORY "${_dir}")
|
||||
|
||||
@@ -77,6 +77,9 @@ target_compile_features(mkw_np_probe PRIVATE cxx_std_20)
|
||||
target_link_libraries(mkw_np_probe PRIVATE
|
||||
aurora::gx aurora::pad aurora::si aurora::vi aurora::mtx
|
||||
mkw::cryptopp)
|
||||
if(NOT MKW_PLATFORM_WINDOWS)
|
||||
target_link_libraries(mkw_np_probe PRIVATE mkw::mbedtls)
|
||||
endif()
|
||||
|
||||
get_filename_component(_mkw_np_aurora_dir "${MKW_AURORA_DIR}" ABSOLUTE)
|
||||
mkw_collect_buildsystem_targets("${_mkw_np_aurora_dir}" _mkw_np_all_targets)
|
||||
@@ -84,6 +87,16 @@ mkw_collect_buildsystem_targets("${_mkw_np_aurora_dir}" _mkw_np_all_targets)
|
||||
# the scan above cannot see it; it is appended explicitly. The type and closure
|
||||
# checks below still apply to it.
|
||||
list(APPEND _mkw_np_all_targets "mkw_cryptopp")
|
||||
if(NOT MKW_PLATFORM_WINDOWS)
|
||||
list(APPEND _mkw_np_all_targets mbedtls mbedx509 mbedcrypto)
|
||||
# Keep the TLS archives separate from aurora's aggregate link interface.
|
||||
string(CONCAT _mkw_np_mbedtls_lines
|
||||
"mbedtls|$<TARGET_LINKER_FILE:MbedTLS::mbedtls>\n"
|
||||
"mbedx509|$<TARGET_LINKER_FILE:MbedTLS::mbedx509>\n"
|
||||
"mbedcrypto|$<TARGET_LINKER_FILE:MbedTLS::mbedcrypto>\n")
|
||||
file(GENERATE OUTPUT "${MKW_NATIVE_PREBUILT_EXPORT_DIR}/mbedtls.txt"
|
||||
CONTENT "${_mkw_np_mbedtls_lines}")
|
||||
endif()
|
||||
if(NOT _mkw_np_all_targets)
|
||||
message(FATAL_ERROR "No buildsystem targets were found under ${_mkw_np_aurora_dir}")
|
||||
endif()
|
||||
@@ -136,6 +149,7 @@ string(REPLACE ";" "," _mkw_np_lib_targets_csv "${_mkw_np_lib_targets}")
|
||||
file(WRITE "${MKW_NATIVE_PREBUILT_EXPORT_DIR}/meta.txt"
|
||||
"aurora_dir=${_mkw_np_aurora_dir}\n"
|
||||
"runtime_dir=${CMAKE_CURRENT_SOURCE_DIR}\n"
|
||||
"mbedtls_source_dir=${mkw_mbedtls_upstream_SOURCE_DIR}\n"
|
||||
"binary_dir=${CMAKE_BINARY_DIR}\n"
|
||||
"dawn_config_dir=${_mkw_np_dawn_config_dir}\n"
|
||||
"dawn_prebuilt_source_dir=${dawn_prebuilt_SOURCE_DIR}\n"
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#endif
|
||||
|
||||
extern "C" bool g_dynamicAspectRatioEnabled;
|
||||
void ConfigureMkwDynamicAspect(bool widescreen, uint32_t surfaceWidth, uint32_t surfaceHeight);
|
||||
void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect169, uint32_t surfaceWidth, uint32_t surfaceHeight);
|
||||
void SetMkwForceAspect169(bool enabled);
|
||||
bool MkwForceAspect169Requested();
|
||||
void UpdateMkwDynamicAspectSurface(uint32_t surfaceWidth, uint32_t surfaceHeight);
|
||||
// Arms the "keep EGG::Frustum's projection scale" flag on every screen that
|
||||
// renders to a fixed-size offscreen target. Cheap and idempotent; called from
|
||||
|
||||
@@ -294,7 +294,7 @@ inline double PpcLoadPairPsqFloatFastInline(uint32_t addr)
|
||||
return PpcLoadPairPsqFloatFromHostInline(host);
|
||||
}
|
||||
return PpcBitCastToDoubleInline(
|
||||
PpcLoadPairPsqFloatBitsPackedInline(MemoryInline::Read64Slow(addr)));
|
||||
PpcLoadPairPsqFloatBitsPackedInline(Memory::Read64(addr)));
|
||||
}
|
||||
|
||||
inline double PpcLoadSinglePsqFloatFastInline(uint32_t addr)
|
||||
@@ -349,7 +349,7 @@ inline void PpcStorePairPsqFloatFastInline(uint32_t addr, double value)
|
||||
PpcStorePairPsqFloatToHostInline(host, value);
|
||||
return;
|
||||
}
|
||||
MemoryInline::Write64Slow(
|
||||
Memory::Write64(
|
||||
addr, PpcStorePairPsqFloatBitsPackedInline(PpcBitCastToU64Inline(value)));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
struct RuntimeUserConfig {
|
||||
std::optional<bool> widescreen;
|
||||
std::optional<bool> forceAspect169;
|
||||
std::optional<int32_t> windowPosX;
|
||||
std::optional<int32_t> windowPosY;
|
||||
std::optional<uint32_t> windowWidth;
|
||||
@@ -298,6 +299,7 @@ inline void EnsureConfigFile() {
|
||||
"# Set paths.dvd_root to an extracted Mario Kart Wii DATA directory.\n\n"
|
||||
"[video]\n"
|
||||
"widescreen = true\n"
|
||||
"force_16_9 = false\n"
|
||||
"resolution_multiplier = 1.0\n"
|
||||
"frame_interpolation_fps = 0\n"
|
||||
"display_mode = \"windowed\"\n"
|
||||
@@ -426,6 +428,7 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
}
|
||||
|
||||
config.widescreen = FindConfigValue<bool>(document, "video", "widescreen");
|
||||
config.forceAspect169 = FindConfigValue<bool>(document, "video", "force_16_9");
|
||||
config.windowPosX = FindConfigInt(document, "video", "window_x");
|
||||
config.windowPosY = FindConfigInt(document, "video", "window_y");
|
||||
if (auto value = FindConfigUint(document, "video", "window_width"); value && *value != 0) {
|
||||
@@ -782,6 +785,15 @@ inline bool WidescreenEnabled(bool fallback = false) {
|
||||
return Get().widescreen.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool ForceAspect169Enabled(bool fallback = false) {
|
||||
return Get().forceAspect169.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool SetForceAspect169(bool value) {
|
||||
Mutable().forceAspect169 = value;
|
||||
return WriteSetting("video", "force_16_9", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool WindowPosition(int32_t& x, int32_t& y) {
|
||||
if (!Get().windowPosX || !Get().windowPosY) {
|
||||
return false;
|
||||
|
||||
@@ -132,8 +132,9 @@ using CryptoEcdsa = CryptoPP::ECDSA<CryptoPP::EC2N, CryptoPP::SHA1>;
|
||||
|
||||
inline CryptoEcdsa::PrivateKey MakePrivateKey(const uint8_t* key) {
|
||||
CryptoPP::DL_GroupParameters_EC<CryptoPP::EC2N> parameters(CryptoPP::ASN1::sect233r1());
|
||||
const CryptoPP::Integer exponent(key, 30);
|
||||
if (exponent <= CryptoPP::Integer::Zero() || exponent >= parameters.GetSubgroupOrder()) {
|
||||
// Wii keys need not be canonical scalars; reduction preserves their public key.
|
||||
const CryptoPP::Integer exponent = CryptoPP::Integer(key, 30) % parameters.GetSubgroupOrder();
|
||||
if (exponent == CryptoPP::Integer::Zero()) {
|
||||
throw std::invalid_argument("Wii ES private key is outside the sect233r1 subgroup");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <imgui.h>
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_joystick.h>
|
||||
#include <SDL3/SDL_platform.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -146,7 +147,7 @@ std::string BuildMappingString() {
|
||||
mapping += std::string(kSteps[i].mappingKey) + ":" + *g_wizard.bindings[i] + ",";
|
||||
}
|
||||
}
|
||||
mapping += "platform:Windows,";
|
||||
mapping += std::string("platform:") + SDL_GetPlatform() + ",";
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "memory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include "aurora_events.h"
|
||||
#include "hle/gx/gx_dynamic_aspect.h"
|
||||
|
||||
@@ -14,6 +15,10 @@ extern "C" int g_gxFrameCount;
|
||||
namespace {
|
||||
|
||||
bool g_widescreenConfigured = false;
|
||||
bool g_widescreenSetting = false;
|
||||
bool g_forceAspect169 = false;
|
||||
std::atomic_bool g_requestedForceAspect169{false};
|
||||
bool g_policyDirty = false;
|
||||
uint32_t g_lastEggWidth43 = 0;
|
||||
uint32_t g_lastEggWidth169 = 0;
|
||||
|
||||
@@ -167,21 +172,54 @@ void AssertMkwOffscreenScreenBypass() {
|
||||
}
|
||||
|
||||
void UpdateMkwDynamicAspectSurface(uint32_t surfaceWidth, uint32_t surfaceHeight) {
|
||||
if (!g_widescreenConfigured || surfaceWidth == 0 || surfaceHeight == 0) {
|
||||
const bool requestedForceAspect169 = g_requestedForceAspect169.load(std::memory_order_acquire);
|
||||
if (g_forceAspect169 != requestedForceAspect169) {
|
||||
g_forceAspect169 = requestedForceAspect169;
|
||||
g_widescreenConfigured = g_widescreenSetting || g_forceAspect169;
|
||||
g_dynamicAspectRatioEnabled = g_widescreenConfigured;
|
||||
g_policyDirty = true;
|
||||
}
|
||||
if (surfaceWidth == 0 || surfaceHeight == 0) {
|
||||
return;
|
||||
}
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_STRETCH);
|
||||
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
|
||||
if (!g_widescreenConfigured) {
|
||||
if (g_policyDirty) {
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
||||
VILockAspectRatio(4, 3);
|
||||
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
|
||||
g_policyDirty = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (g_policyDirty) {
|
||||
VIUnlockAspectRatio();
|
||||
g_policyDirty = false;
|
||||
}
|
||||
AuroraSetViewportPolicy(g_forceAspect169 ? AURORA_VIEWPORT_16_9 : AURORA_VIEWPORT_STRETCH);
|
||||
ApplyEggScreenRecords(g_forceAspect169 ? 16u : surfaceWidth,
|
||||
g_forceAspect169 ? 9u : surfaceHeight);
|
||||
}
|
||||
|
||||
void ConfigureMkwDynamicAspect(bool widescreen, uint32_t surfaceWidth, uint32_t surfaceHeight) {
|
||||
g_widescreenConfigured = widescreen;
|
||||
g_dynamicAspectRatioEnabled = widescreen;
|
||||
void SetMkwForceAspect169(bool enabled) {
|
||||
g_requestedForceAspect169.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool MkwForceAspect169Requested() {
|
||||
return g_requestedForceAspect169.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void ConfigureMkwDynamicAspect(bool widescreen, bool forceAspect169, uint32_t surfaceWidth, uint32_t surfaceHeight) {
|
||||
g_widescreenSetting = widescreen;
|
||||
g_widescreenConfigured = widescreen || forceAspect169;
|
||||
g_forceAspect169 = forceAspect169;
|
||||
g_requestedForceAspect169.store(forceAspect169, std::memory_order_relaxed);
|
||||
g_dynamicAspectRatioEnabled = g_widescreenConfigured;
|
||||
g_lastEggWidth43 = 0;
|
||||
g_lastEggWidth169 = 0;
|
||||
if (widescreen) {
|
||||
if (g_widescreenConfigured) {
|
||||
VIUnlockAspectRatio();
|
||||
ApplyEggScreenRecords(surfaceWidth, surfaceHeight);
|
||||
ApplyEggScreenRecords(forceAspect169 ? 16u : surfaceWidth,
|
||||
forceAspect169 ? 9u : surfaceHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "hle_stubs.h"
|
||||
#include "input_bindings.h"
|
||||
#include "memory.h"
|
||||
#include "wii_remote_input.h"
|
||||
|
||||
@@ -203,6 +204,24 @@ void WriteUnifiedStatus(uint32_t addr, const WiiRemoteInput::KpadSample* sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Neutral sticks and no buttons while an overlay owns input; the remote stays connected.
|
||||
bool ReadSample(uint32_t chan, WiiRemoteInput::KpadSample& sample) {
|
||||
if (!WiiRemoteInput::ReadKpadSample(chan, sample)) {
|
||||
return false;
|
||||
}
|
||||
if (InputBindings::InputBlocked()) {
|
||||
sample.hold = 0;
|
||||
sample.clHold = 0;
|
||||
sample.stick[0] = sample.stick[1] = 0.0f;
|
||||
sample.clLStick[0] = sample.clLStick[1] = 0.0f;
|
||||
sample.clRStick[0] = sample.clRStick[1] = 0.0f;
|
||||
sample.clLStickRaw[0] = sample.clLStickRaw[1] = 0;
|
||||
sample.clRStickRaw[0] = sample.clRStickRaw[1] = 0;
|
||||
sample.clTriggerL = sample.clTriggerR = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// KPADRead: fills KPADStatus[0] for `chan` from the Bluetooth remote, returns the entry count.
|
||||
@@ -212,7 +231,7 @@ extern "C" int32_t KPAD__Read_HLE(uint32_t chan, uint32_t statusPtr, uint32_t co
|
||||
return 0;
|
||||
}
|
||||
WiiRemoteInput::KpadSample sample;
|
||||
const bool have = WiiRemoteInput::ReadKpadSample(chan, sample);
|
||||
const bool have = ReadSample(chan, sample);
|
||||
try {
|
||||
return WriteStatus(chan, statusPtr, have ? &sample : nullptr);
|
||||
} catch (const Memory::AccessViolation&) {
|
||||
@@ -234,7 +253,7 @@ extern "C" int32_t KPAD__GetUnifiedWpadStatus_HLE(uint32_t chan, uint32_t status
|
||||
return 0;
|
||||
}
|
||||
WiiRemoteInput::KpadSample sample;
|
||||
const bool have = WiiRemoteInput::ReadKpadSample(chan, sample);
|
||||
const bool have = ReadSample(chan, sample);
|
||||
try {
|
||||
const uint32_t entries = std::min(count, kMaxEntries);
|
||||
for (uint32_t i = 0; i < entries; ++i) {
|
||||
|
||||
@@ -467,7 +467,8 @@ int32_t HandleIpTopIoctlv(uint32_t cmd, const std::vector<IoVector>& in, const s
|
||||
const int ret = sendto(s->native, reinterpret_cast<const char*>(sendData), static_cast<int>(sendSize),
|
||||
static_cast<int>(flags), destPtr, destLen);
|
||||
const int hostError = ret < 0 ? NativeLastError() : 0;
|
||||
int32_t result = SocketResult(ret);
|
||||
// Diagnostics may change the native error; use the send result captured above.
|
||||
int32_t result = ret >= 0 ? SocketResult(ret) : SocketErrorResult(hostError);
|
||||
if (patchedWrite && ret == static_cast<int>(sendSize)) {
|
||||
result = static_cast<int32_t>(in[0].size);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstring>
|
||||
#include "memory.h"
|
||||
#include "runtime_config.h"
|
||||
#include "aurora_events.h"
|
||||
#include "runtime_log.h"
|
||||
|
||||
namespace {
|
||||
@@ -52,7 +53,7 @@ PPC_NATIVE_OVERRIDE(801B0220, SCCheckStatus_HLE, uint32_t, (), ());
|
||||
// Returns: 0 = 4:3, 1 = 16:9
|
||||
extern "C" uint32_t SCGetAspectRatio_HLE()
|
||||
{
|
||||
return RuntimeConfigFile::WidescreenEnabled(true) ? 1u : 0u;
|
||||
return (RuntimeConfigFile::WidescreenEnabled(true) || MkwForceAspect169Requested()) ? 1u : 0u;
|
||||
}
|
||||
|
||||
PPC_NATIVE_OVERRIDE(801B1BE4, SCGetAspectRatio_HLE, uint32_t, (), ());
|
||||
|
||||
@@ -1354,7 +1354,8 @@ int RuntimeMain(int argc, char** argv) {
|
||||
auroraConfig.logCallback = &RuntimeAuroraLogCallback;
|
||||
auroraConfig.logLevel = LOG_DEBUG;
|
||||
const bool configWidescreen = RuntimeConfigFile::WidescreenEnabled(true);
|
||||
auroraConfig.windowWidth = configWidescreen ? 854 : 640;
|
||||
const bool forceAspect169 = RuntimeConfigFile::ForceAspect169Enabled();
|
||||
auroraConfig.windowWidth = (configWidescreen || forceAspect169) ? 854 : 640;
|
||||
auroraConfig.windowHeight = 480;
|
||||
auroraConfig.windowWidth = RuntimeConfigFile::WindowWidth(auroraConfig.windowWidth);
|
||||
auroraConfig.windowHeight = RuntimeConfigFile::WindowHeight(auroraConfig.windowHeight);
|
||||
@@ -1372,7 +1373,8 @@ int RuntimeMain(int argc, char** argv) {
|
||||
// No vsync knob: aurora always configures a non-blocking present mode.
|
||||
auroraConfig.desiredBackend = BACKEND_AUTO;
|
||||
const float resolutionMultiplier = RuntimeConfigFile::ResolutionMultiplier(1.0f);
|
||||
ConfigureMkwDynamicAspect(configWidescreen, auroraConfig.windowWidth, auroraConfig.windowHeight);
|
||||
ConfigureMkwDynamicAspect(configWidescreen, forceAspect169,
|
||||
auroraConfig.windowWidth, auroraConfig.windowHeight);
|
||||
VISetFrameBufferScale(resolutionMultiplier);
|
||||
// One table for both directions. RuntimeConfigFile::IsSupportedGraphicsApi
|
||||
// whitelists exactly these config names, so an unrecognised value has
|
||||
@@ -1439,14 +1441,15 @@ int RuntimeMain(int argc, char** argv) {
|
||||
auroraInfo.windowSize.native_fb_height);
|
||||
settings_overlay::InitializeRuntimeSettings();
|
||||
RT_LOG(RT_TAG_CONFIG) << "video.widescreen=" << (configWidescreen ? "true" : "false")
|
||||
<< " SCGetAspectRatio=" << (configWidescreen ? 1 : 0)
|
||||
<< " force_16_9=" << (forceAspect169 ? "true" : "false")
|
||||
<< " SCGetAspectRatio=" << (configWidescreen || forceAspect169 ? 1 : 0)
|
||||
<< " resolutionMultiplier=" << resolutionMultiplier
|
||||
<< " window=" << auroraInfo.windowSize.width << "x" << auroraInfo.windowSize.height
|
||||
<< " native=" << auroraInfo.windowSize.native_fb_width << "x"
|
||||
<< auroraInfo.windowSize.native_fb_height
|
||||
<< " viewportPolicy=" << (g_dynamicAspectRatioEnabled ? "stretch" : "fit")
|
||||
<< " viewportPolicy=" << (forceAspect169 ? "16:9" : (configWidescreen ? "stretch" : "fit"))
|
||||
<< " presentAspect="
|
||||
<< (g_dynamicAspectRatioEnabled ? "surface (dynamic EGG canvas)" : "4:3")
|
||||
<< (forceAspect169 ? "16:9" : (configWidescreen ? "surface (dynamic EGG canvas)" : "4:3"))
|
||||
<< std::endl;
|
||||
g_auroraInitialized.store(true, std::memory_order_release);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
@@ -109,6 +110,7 @@ int g_displayMode = [] {
|
||||
bool g_skipUnreadyPipelines = RuntimeConfigFile::SkipUnreadyPipelines(true);
|
||||
bool g_disableCopyFilter = RuntimeConfigFile::DisableCopyFilter(true);
|
||||
bool g_showFps = RuntimeConfigFile::ShowFps(true);
|
||||
bool g_forceAspect169 = RuntimeConfigFile::ForceAspect169Enabled();
|
||||
uint32_t g_disabledPostProcessingPaths = RuntimeConfigFile::DisabledPostProcessingPaths(0);
|
||||
std::array<int32_t, PAD_MAX_CONTROLLERS> g_configuredControllerIndices = [] {
|
||||
std::array<int32_t, PAD_MAX_CONTROLLERS> indices{};
|
||||
@@ -161,6 +163,11 @@ uint64_t g_presentedFrame = 0;
|
||||
std::atomic_bool g_strapInputAccepted = false;
|
||||
std::atomic_uint64_t g_startupDismissFrame = UINT64_MAX;
|
||||
constexpr uint64_t kStrapTransitionCoverFrames = 60;
|
||||
std::atomic_bool g_bootShadersReady = false;
|
||||
bool g_bootShaderNotice = false;
|
||||
Clock::time_point g_bootShaderWaitStart{};
|
||||
constexpr uint32_t kBootShaderNoticeThreshold = 100;
|
||||
constexpr auto kBootShaderWaitLimit = std::chrono::minutes(3);
|
||||
|
||||
constexpr std::array<ResolutionItem, 8> kResolutions = {{
|
||||
{"Auto (window size)", 0.0f}, {"Native (1x)", 1.0f}, {"1.5x", 1.5f}, {"2x", 2.0f},
|
||||
@@ -1006,6 +1013,12 @@ void DrawAudioSettings() {
|
||||
|
||||
void DrawGraphicsSettings() {
|
||||
g_displayMode = static_cast<int>(aurora_get_display_mode());
|
||||
if (ImGui::Checkbox("Force 16:9", &g_forceAspect169)) {
|
||||
SetMkwForceAspect169(g_forceAspect169);
|
||||
RuntimeConfigFile::SetForceAspect169(g_forceAspect169);
|
||||
}
|
||||
ImGui::TextDisabled("Keep a 16:9 image with black bars when the window has another shape.");
|
||||
ImGui::Separator();
|
||||
struct EffectFlag {
|
||||
const char* label;
|
||||
uint32_t flag;
|
||||
@@ -1186,12 +1199,36 @@ void DrawStartupScreen() {
|
||||
const float startY = std::max(0.0f, (viewport->Size.y - titleSize.y) * 0.5f);
|
||||
ImGui::SetCursorPos(ImVec2(titleX, startY));
|
||||
ImGui::TextUnformatted(kTitle);
|
||||
if (g_bootShaderNotice && !g_bootShadersReady.load(std::memory_order_relaxed)) {
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
char line[96];
|
||||
std::snprintf(line, sizeof(line), "Compiling shaders, please hold on: %u remaining",
|
||||
aurora_get_queued_pipeline_count());
|
||||
const float lineX = std::max(0.0f, (viewport->Size.x - ImGui::CalcTextSize(line).x) * 0.5f);
|
||||
ImGui::SetCursorPos(ImVec2(lineX, startY + titleSize.y * 1.8f));
|
||||
ImGui::TextUnformatted(line);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void UpdateBootShaderState() {
|
||||
if (g_bootShadersReady.load(std::memory_order_relaxed)) {
|
||||
return;
|
||||
}
|
||||
const auto now = Clock::now();
|
||||
if (g_bootShaderWaitStart == Clock::time_point{}) {
|
||||
g_bootShaderWaitStart = now;
|
||||
}
|
||||
const uint32_t queued = aurora_get_queued_pipeline_count();
|
||||
g_bootShaderNotice |= queued > kBootShaderNoticeThreshold;
|
||||
if (queued == 0 || now - g_bootShaderWaitStart > kBootShaderWaitLimit) {
|
||||
g_bootShadersReady.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawExitPrompt() {
|
||||
constexpr const char* kTitle = "Exit";
|
||||
if (g_exitPromptOpen && !ImGui::IsPopupOpen(kTitle)) ImGui::OpenPopup(kTitle);
|
||||
@@ -1341,7 +1378,7 @@ void PersistDisplayModeIfChanged() {
|
||||
|
||||
void ApplyInputBlockState() {
|
||||
const bool blocked = controller_mapping_wizard::IsActive() || g_rebind.active ||
|
||||
g_exitPromptOpen || g_topBarVisible;
|
||||
g_exitPromptOpen || g_topBarVisible || StartupScreenVisible();
|
||||
PADBlockInput(blocked);
|
||||
InputBindings::SetInputBlocked(blocked);
|
||||
}
|
||||
@@ -1369,6 +1406,9 @@ void InitializeRuntimeSettings() noexcept {
|
||||
aurora_set_skip_unready_pipelines(g_skipUnreadyPipelines);
|
||||
g_strapInputAccepted.store(false, std::memory_order_relaxed);
|
||||
g_startupDismissFrame.store(UINT64_MAX, std::memory_order_relaxed);
|
||||
g_bootShadersReady.store(false, std::memory_order_relaxed);
|
||||
g_bootShaderNotice = false;
|
||||
g_bootShaderWaitStart = {};
|
||||
PADBlockInput(false);
|
||||
InputBindings::SetInputBlocked(false);
|
||||
}
|
||||
@@ -1449,6 +1489,7 @@ void Draw() noexcept {
|
||||
ApplyConfiguredMappings();
|
||||
PersistDisplayModeIfChanged();
|
||||
UpdateCursorAutoHide();
|
||||
UpdateBootShaderState();
|
||||
if (!StartupScreenVisible()) {
|
||||
DrawShaderCompilationStatus();
|
||||
}
|
||||
@@ -1462,6 +1503,7 @@ void Draw() noexcept {
|
||||
|
||||
bool StartupScreenVisible() noexcept {
|
||||
return !g_strapInputAccepted.load(std::memory_order_acquire) ||
|
||||
!g_bootShadersReady.load(std::memory_order_acquire) ||
|
||||
g_presentedFrame < g_startupDismissFrame.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
|
||||
@@ -1942,6 +1942,22 @@ public sealed partial class PpcLifter
|
||||
new IrCall(string.Empty, TargetLabel(ins, validAddresses, preferFallthrough: false), blArgs)
|
||||
};
|
||||
|
||||
case "bltl":
|
||||
{
|
||||
var crField = "cr0";
|
||||
if (ops.Count > 0 && ops[0] is PpcConditionRegisterOperand crOp)
|
||||
{
|
||||
crField = NormalizeRegister(crOp.Name);
|
||||
}
|
||||
|
||||
return new IrInstruction[]
|
||||
{
|
||||
new IrAssign("lr", IrValue.Imm((int)ins.EndAddress)),
|
||||
new IrBranch("blt", TargetLabel(ins, validAddresses, preferFallthrough: false),
|
||||
$"0x{ins.EndAddress:X8}", crField)
|
||||
};
|
||||
}
|
||||
|
||||
case "bcl":
|
||||
{
|
||||
var rawInstr = ReadRawInstruction(ins);
|
||||
|
||||
@@ -14,6 +14,23 @@ public class PpcLifterAdditionalCoverageTests
|
||||
private static PpcInstruction Instruction(uint raw, string mnemonic, params PpcOperand[] operands)
|
||||
=> PpcInstruction.Synthetic(0x80000000, raw, mnemonic, operands);
|
||||
|
||||
[Fact]
|
||||
public void LinkedConditionalBranchSetsLrBeforeEitherPath()
|
||||
{
|
||||
var branch = PpcDecoder.Decode(0x80004394, 0x41800029);
|
||||
var ir = Assert.Single(new PpcLifter().Lift(new[] { branch })).Ir;
|
||||
|
||||
Assert.Equal("bltl", branch.Mnemonic);
|
||||
var lr = Assert.IsType<IrAssign>(ir[0]);
|
||||
Assert.Equal("lr", lr.Destination);
|
||||
Assert.Equal(unchecked((int)0x80004398u), lr.Value.Constant);
|
||||
|
||||
var decision = Assert.IsType<IrBranch>(ir[1]);
|
||||
Assert.Equal("blt", decision.Condition);
|
||||
Assert.Equal("0x800043BC", decision.TrueLabel);
|
||||
Assert.Equal("0x80004398", decision.FalseLabel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LiftsAdditionalNonDotArithmeticAndLogicalForms()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user