mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-29 07:30:32 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f715d37678 | |||
| a05c89739d |
@@ -152,7 +152,8 @@ if ($Profile -ne 'both' -and -not [string]::IsNullOrWhiteSpace($BaseOutputDirect
|
|||||||
}
|
}
|
||||||
$realWorkspace = $Workspace.TrimEnd('\')
|
$realWorkspace = $Workspace.TrimEnd('\')
|
||||||
$Workspace = Get-MkwBuildSafePath $realWorkspace 'workspace' 'runtime\CMakeLists.txt'
|
$Workspace = Get-MkwBuildSafePath $realWorkspace 'workspace' 'runtime\CMakeLists.txt'
|
||||||
# One spelling of the workspace, so the staged Code.pul check below can't copy a file onto itself.
|
# 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
|
if (-not [string]::IsNullOrWhiteSpace($RetroRewindPackageDirectory) -and
|
||||||
$RetroRewindPackageDirectory.StartsWith($realWorkspace + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
$RetroRewindPackageDirectory.StartsWith($realWorkspace + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||||
$RetroRewindPackageDirectory = $Workspace + $RetroRewindPackageDirectory.Substring($realWorkspace.Length)
|
$RetroRewindPackageDirectory = $Workspace + $RetroRewindPackageDirectory.Substring($realWorkspace.Length)
|
||||||
|
|||||||
@@ -42,10 +42,9 @@ function Get-MkwToolchainPath([string]$ToolchainRoot) {
|
|||||||
|
|
||||||
function Get-MkwBuildSafePath([string]$Path, [string]$Kind, [string]$MarkerFile) {
|
function Get-MkwBuildSafePath([string]$Path, [string]$Kind, [string]$MarkerFile) {
|
||||||
<#
|
<#
|
||||||
$Path, or a junction to it whose path is plain ASCII. cmd.exe, Ninja response files and the
|
The Windows native build passes workspace paths through CMake, Ninja response files and
|
||||||
compiler each have their own quoting rules, so a path outside this allowlist ('&', '%', an
|
clang, which do not all interpret quotes the same way. Keep those paths plain even when the
|
||||||
apostrophe, non-ASCII...) is never handed to the native build at all. $MarkerFile is a file
|
user's install directory contains an apostrophe, ampersand or other punctuation.
|
||||||
that must exist under a live junction.
|
|
||||||
#>
|
#>
|
||||||
if ([string]::IsNullOrWhiteSpace($Path)) { throw "A $Kind path is required." }
|
if ([string]::IsNullOrWhiteSpace($Path)) { throw "A $Kind path is required." }
|
||||||
$full = [IO.Path]::GetFullPath($Path)
|
$full = [IO.Path]::GetFullPath($Path)
|
||||||
@@ -53,6 +52,8 @@ function Get-MkwBuildSafePath([string]$Path, [string]$Kind, [string]$MarkerFile)
|
|||||||
if ($full -ne [IO.Path]::GetPathRoot($full)) { $full = $full.TrimEnd('\') }
|
if ($full -ne [IO.Path]::GetPathRoot($full)) { $full = $full.TrimEnd('\') }
|
||||||
if ($full -cmatch '^[A-Za-z0-9 ._\\:-]+$') { return $full }
|
if ($full -cmatch '^[A-Za-z0-9 ._\\:-]+$') { return $full }
|
||||||
|
|
||||||
|
Assert-File (Join-Path $full $MarkerFile) "$Kind marker"
|
||||||
|
|
||||||
$sha = [Security.Cryptography.SHA256]::Create()
|
$sha = [Security.Cryptography.SHA256]::Create()
|
||||||
try {
|
try {
|
||||||
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($full.ToLowerInvariant()))
|
$bytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($full.ToLowerInvariant()))
|
||||||
@@ -65,23 +66,29 @@ function Get-MkwBuildSafePath([string]$Path, [string]$Kind, [string]$MarkerFile)
|
|||||||
$link = Join-Path (Join-Path $base 'WiiCompiled') $linkName
|
$link = Join-Path (Join-Path $base 'WiiCompiled') $linkName
|
||||||
try {
|
try {
|
||||||
[IO.Directory]::CreateDirectory((Split-Path -Parent $link)) | Out-Null
|
[IO.Directory]::CreateDirectory((Split-Path -Parent $link)) | Out-Null
|
||||||
# The name already identifies the target, so an existing junction that still resolves is
|
$existing = Get-Item -LiteralPath $link -Force -ErrorAction SilentlyContinue
|
||||||
# this one; only a broken leftover is replaced. Directory.Delete removes the reparse
|
if ($null -ne $existing) {
|
||||||
# point itself, where Remove-Item -Recurse would delete the tree it points at.
|
# Never trust a directory just because it has the marker: it could point at a
|
||||||
if (-not (Test-Path -LiteralPath (Join-Path $link $MarkerFile) -PathType Leaf)) {
|
# different installation. Nor may we remove a directory we did not create.
|
||||||
if (Test-Path -LiteralPath $link) { [IO.Directory]::Delete($link) }
|
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
|
New-Item -ItemType Junction -Path $link -Target $full -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
|
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"
|
Write-Host "MKWCBUILD: Building the $Kind through $link, because $full contains characters the native build cannot quote reliably"
|
||||||
return $link
|
return $link
|
||||||
} catch {
|
} catch {
|
||||||
$failures += "$link ($($_.Exception.Message))"
|
$failures += "$link ($($_.Exception.Message))"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Write-Host ("MKWCBUILD: Warning: no junction to $full could be created (" + ($failures -join '; ') +
|
throw ("The $Kind path $full cannot be passed safely to the native build, and no junction " +
|
||||||
'); building from the original path, which may fail. Installing to a path of plain ' +
|
'to it could be created: ' + ($failures -join '; ') + '. Install to a path of plain ' +
|
||||||
'letters, digits and spaces avoids this.')
|
'letters, digits and spaces, or make a safe junction location available.')
|
||||||
return $full
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-MkwProjectPins([string]$ProjectFile) {
|
function Get-MkwProjectPins([string]$ProjectFile) {
|
||||||
|
|||||||
@@ -127,19 +127,40 @@ internal static class Program
|
|||||||
string? retroWfcOfflineDir = null;
|
string? retroWfcOfflineDir = null;
|
||||||
if (downloadPayload)
|
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");
|
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
|
try
|
||||||
{
|
{
|
||||||
RetroWfcPayload.ValidateStagedRetroWfcPayloadDirectory(cacheDir);
|
// 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
|
||||||
catch (InvalidDataException)
|
// downloader verifies it before atomically replacing the cache.
|
||||||
{
|
|
||||||
await RetroWfcPayload.DownloadRetroWfcPayloadAsync(
|
await RetroWfcPayload.DownloadRetroWfcPayloadAsync(
|
||||||
RetroWfcPayload.CurrentRetroWfcPayloadUri, cacheDir, token);
|
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;
|
retroWfcOfflineDir = cacheDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,8 +110,7 @@ internal sealed class ProductRepairService
|
|||||||
InputValidation.CurrentRetroWfcPayloadUri, payloadScratch, cancellationToken);
|
InputValidation.CurrentRetroWfcPayloadUri, payloadScratch, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!cancellationToken.IsCancellationRequested &&
|
catch (Exception ex) when (!cancellationToken.IsCancellationRequested &&
|
||||||
ex is HttpRequestException or IOException or InvalidDataException
|
ex is HttpRequestException or TimeoutException or IOException)
|
||||||
or InvalidOperationException or OperationCanceledException)
|
|
||||||
{
|
{
|
||||||
payloadSnapshot = RecoverInstalledRetroWfcPayload(toolkitFingerprint,
|
payloadSnapshot = RecoverInstalledRetroWfcPayload(toolkitFingerprint,
|
||||||
Path.Combine(scratchRoot, "retro-wfc-payload-recovered"), ex, cancellationToken);
|
Path.Combine(scratchRoot, "retro-wfc-payload-recovered"), ex, cancellationToken);
|
||||||
|
|||||||
@@ -77,26 +77,40 @@ if [[ -n "$retro_dir" ]]; then
|
|||||||
# verify its pinned signature before publishing it into the local cache.
|
# verify its pinned signature before publishing it into the local cache.
|
||||||
retro_wfc_dir="$support_root/RetroWfcPayload"
|
retro_wfc_dir="$support_root/RetroWfcPayload"
|
||||||
retro_wfc_payload="$retro_wfc_dir/binary/payload.RMCPD00.bin"
|
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
|
cached_payload_valid=0
|
||||||
printf 'Discarding an invalid cached Retro-WFC payload...\n' >&2
|
if [[ -f "$retro_wfc_payload" ]]; then
|
||||||
rm -f "$retro_wfc_payload"
|
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
|
fi
|
||||||
if [[ ! -f "$retro_wfc_payload" ]]; then
|
|
||||||
printf 'Downloading the Retro-WFC payload needed for online play...\n'
|
# A signed cache may still be an older vulnerable revision, so always attempt to replace it
|
||||||
mkdir -p "$retro_wfc_dir"
|
# with the current signed snapshot. A transport failure may fall back to the verified cache;
|
||||||
payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX")
|
# a downloaded snapshot with an invalid signature remains a hard failure.
|
||||||
temporary_payload="$payload_stage/binary/payload.RMCPD00.bin"
|
printf 'Downloading the current Retro-WFC payload needed for online play...\n'
|
||||||
mkdir -p "$(dirname "$temporary_payload")"
|
mkdir -p "$retro_wfc_dir"
|
||||||
trap 'rm -rf "$payload_stage"' EXIT
|
payload_stage=$(mktemp -d "$retro_wfc_dir/.payload-download.XXXXXX")
|
||||||
/usr/bin/curl --fail --silent --show-error --connect-timeout 10 --max-time 30 \
|
temporary_payload="$payload_stage/binary/payload.RMCPD00.bin"
|
||||||
--retry 1 --output "$temporary_payload" \
|
mkdir -p "$(dirname "$temporary_payload")"
|
||||||
'https://rwfc.net/api/wfc/payload?g=RMCPD00' || fail 'could not download the Retro-WFC payload needed for online play'
|
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" || \
|
"$translator" validate-retro-wfc-payload --directory "$payload_stage" || \
|
||||||
fail 'downloaded Retro-WFC payload failed signature validation'
|
fail 'downloaded Retro-WFC payload failed signature validation'
|
||||||
mkdir -p "$retro_wfc_dir/binary"
|
mkdir -p "$retro_wfc_dir/binary"
|
||||||
mv "$temporary_payload" "$retro_wfc_payload"
|
mv "$temporary_payload" "$retro_wfc_payload"
|
||||||
rmdir "$payload_stage/binary" "$payload_stage"
|
rmdir "$payload_stage/binary" "$payload_stage"
|
||||||
trap - EXIT
|
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
|
fi
|
||||||
build_args+=(--profile both --base-output-dir "$products" --retro-rewind-package-dir "$retro_dir" --retro-wfc-offline-dir "$retro_wfc_dir")
|
build_args+=(--profile both --base-output-dir "$products" --retro-rewind-package-dir "$retro_dir" --retro-wfc-offline-dir "$retro_wfc_dir")
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -21,13 +21,6 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
|
|||||||
message(FATAL_ERROR "WiiCompiled only supports Release builds")
|
message(FATAL_ERROR "WiiCompiled only supports Release builds")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Ninja writes Windows-quoted response files; the GNU clang driver otherwise reads them POSIX-style.
|
|
||||||
if(CMAKE_HOST_WIN32)
|
|
||||||
foreach(_mkw_lang C CXX)
|
|
||||||
set(CMAKE_${_mkw_lang}_RESPONSE_FILE_LINK_FLAG "--rsp-quoting=windows @")
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(MKW_BUILD_PRODUCTS "Build translated WiiCompiled product targets" ON)
|
option(MKW_BUILD_PRODUCTS "Build translated WiiCompiled product targets" ON)
|
||||||
|
|
||||||
# Preprocessor definitions that belong to this project's own code (the runtime,
|
# Preprocessor definitions that belong to this project's own code (the runtime,
|
||||||
|
|||||||
Reference in New Issue
Block a user