mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-12 17:45:52 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ab77952f4 | |||
| 5d67b229f6 | |||
| 56db6ba641 | |||
| a67069afd3 | |||
| 009697fb97 | |||
| 3f7fed48c9 | |||
| 6eba523d70 |
@@ -34,34 +34,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test translator/Translator.sln -c Release --no-build --verbosity normal
|
run: dotnet test translator/Translator.sln -c Release --no-build --verbosity normal
|
||||||
|
|
||||||
macos_substrate:
|
|
||||||
name: macOS arm64 (configure + substrate tests)
|
|
||||||
runs-on: macos-14
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Configure native runtime
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
test "$(uname -m)" = arm64
|
|
||||||
cmake -S runtime -B build-macos -G Ninja \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release -DMKW_BUILD_PRODUCTS=OFF
|
|
||||||
grep -qx 'CMAKE_OSX_DEPLOYMENT_TARGET:STRING=14.0' \
|
|
||||||
build-macos/CMakeCache.txt
|
|
||||||
|
|
||||||
- name: Build macOS portability targets
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cmake --build build-macos --target \
|
|
||||||
mkw_platform_paths_tests \
|
|
||||||
mkw_macos_native_compile \
|
|
||||||
mkw_macos_context_abi_tests \
|
|
||||||
mkw_macos_host_context_tests \
|
|
||||||
mkw_macos_guest_flat_memory_tests
|
|
||||||
|
|
||||||
- name: Test execution substrate
|
|
||||||
shell: bash
|
|
||||||
run: ctest --test-dir build-macos --output-on-failure
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
name: Package installers
|
name: Package installers
|
||||||
|
|
||||||
# Builds the per-platform installer/setup tool (WiiCompiled-Setup.exe /
|
# Builds the per-platform installer/setup tool (WiiCompiled-Setup.exe /
|
||||||
# WiiCompiled-Setup-x86_64.AppImage / WiiCompiled-Setup.pkg) via the platform packaging scripts -
|
# WiiCompiled-Setup-x86_64.AppImage) via Launcher/Build-Installer.ps1 and
|
||||||
# the same scripts a maintainer runs by hand today to
|
# Launcher/build-appimage.sh respectively - the same scripts a maintainer runs by hand today to
|
||||||
# produce a GitHub Release asset. This does NOT build the actual translated game executable:
|
# produce a GitHub Release asset. This does NOT build the actual translated game executable:
|
||||||
# that step requires the end user's own Mario Kart Wii dump (Assets/main.dol, Assets/StaticR.rel),
|
# that step requires the end user's own Mario Kart Wii dump (Assets/main.dol, Assets/StaticR.rel),
|
||||||
# which is proprietary and not present in this repository or in CI.
|
# which is proprietary and not present in this repository or in CI.
|
||||||
@@ -11,11 +11,6 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: Package version
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -91,99 +86,14 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
archive: false
|
archive: false
|
||||||
|
|
||||||
macos-setup-package:
|
|
||||||
name: macOS (Setup.pkg, Apple Silicon)
|
|
||||||
runs-on: macos-14
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v6
|
|
||||||
with:
|
|
||||||
dotnet-version: '8.0.x'
|
|
||||||
|
|
||||||
- name: Verify Apple Silicon runner tools
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
test "$(uname -m)" = arm64
|
|
||||||
xcode-select -p
|
|
||||||
command -v ninja
|
|
||||||
file "$(command -v ninja)" | grep -q arm64
|
|
||||||
|
|
||||||
- name: Download pinned nodtool release
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p Launcher/artifacts/macos
|
|
||||||
nodtool_version=v2.0.0-alpha.10
|
|
||||||
nodtool_asset=nodtool-macos-arm64
|
|
||||||
nodtool_sha256=e23ca466999b720c55e6d29c9683fce8cc74451ba64ead2e543d50129f24528a
|
|
||||||
curl -fsSL --retry 3 \
|
|
||||||
"https://github.com/encounter/nod/releases/download/${nodtool_version}/${nodtool_asset}" \
|
|
||||||
-o Launcher/artifacts/macos/nodtool
|
|
||||||
printf '%s %s\n' "$nodtool_sha256" Launcher/artifacts/macos/nodtool | shasum -a 256 -c -
|
|
||||||
chmod +x Launcher/artifacts/macos/nodtool
|
|
||||||
Launcher/artifacts/macos/nodtool --version
|
|
||||||
|
|
||||||
- name: Publish self-contained translator
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
dotnet publish translator/src/Translator.Cli/Translator.Cli.csproj \
|
|
||||||
-c Release -r osx-arm64 --self-contained true \
|
|
||||||
-p:PublishSingleFile=true \
|
|
||||||
-o Launcher/artifacts/macos/translator
|
|
||||||
|
|
||||||
- name: Download pinned portable CMake
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cmake_version=4.4.3
|
|
||||||
archive="cmake-${cmake_version}-macos-universal.tar.gz"
|
|
||||||
base_url="https://github.com/Kitware/CMake/releases/download/v${cmake_version}"
|
|
||||||
expected_sha256=0c5d65251c14cc884bfa16bdbed3c263ce5bffe2e21c0d0d00962cb0610464fa
|
|
||||||
curl -fsSL --retry 3 "$base_url/$archive" -o "$archive"
|
|
||||||
printf '%s %s\n' "$expected_sha256" "$archive" | shasum -a 256 -c -
|
|
||||||
tar -xzf "$archive"
|
|
||||||
mv "cmake-${cmake_version}-macos-universal/CMake.app/Contents" Launcher/artifacts/macos/cmake
|
|
||||||
|
|
||||||
- name: Build Setup.pkg
|
|
||||||
env:
|
|
||||||
PACKAGE_VERSION: ${{ inputs.version }}
|
|
||||||
TAG_VERSION: ${{ github.ref_name }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
package_version="$PACKAGE_VERSION"
|
|
||||||
if [[ -z "$package_version" ]]; then package_version="${TAG_VERSION#v}"; fi
|
|
||||||
mkdir -p Launcher/dist
|
|
||||||
Launcher/macos/build-setup-pkg.command \
|
|
||||||
--nodtool Launcher/artifacts/macos/nodtool \
|
|
||||||
--translator Launcher/artifacts/macos/translator/Translator.Cli \
|
|
||||||
--cmake-root Launcher/artifacts/macos/cmake \
|
|
||||||
--ninja "$(command -v ninja)" \
|
|
||||||
--output Launcher/dist/WiiCompiled-Setup.pkg \
|
|
||||||
--version "$package_version"
|
|
||||||
|
|
||||||
- name: Verify package boundary
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
pkgutil --check-signature Launcher/dist/WiiCompiled-Setup.pkg
|
|
||||||
! pkgutil --payload-files Launcher/dist/WiiCompiled-Setup.pkg | \
|
|
||||||
grep -E '/(Assets|generated|PulsarPacks|WiiCompiled.app|RetroRewind.app)(/|$)'
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: WiiCompiled-Setup-macos-arm64
|
|
||||||
path: Launcher/dist/WiiCompiled-Setup.pkg
|
|
||||||
if-no-files-found: error
|
|
||||||
archive: false
|
|
||||||
|
|
||||||
# Publishes the packaged installers as a GitHub Release whenever a v* tag is pushed. Wheel Wizard
|
# Publishes the packaged installers as a GitHub Release whenever a v* tag is pushed. Wheel Wizard
|
||||||
# discovers updates from these releases, so the contract it relies on is enforced here: a full
|
# discovers updates from these releases, so the contract it relies on is enforced here: a full
|
||||||
# (non-prerelease) release whose tag is v<semver>, carrying the expected platform assets,
|
# (non-prerelease) release whose tag is v<semver>, carrying an asset named exactly
|
||||||
# produced by setup hosts that report that same version.
|
# WiiCompiled-Setup.exe, produced by a setup host that reports that same version.
|
||||||
release:
|
release:
|
||||||
name: Publish GitHub Release
|
name: Publish GitHub Release
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
needs: [linux-appimage, windows-installer, macos-setup-package]
|
needs: [linux-appimage, windows-installer]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -229,7 +139,7 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ls -lR artifacts
|
ls -lR artifacts
|
||||||
assets=()
|
assets=()
|
||||||
for name in WiiCompiled-Setup.exe WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage WiiCompiled-Setup.pkg; do
|
for name in WiiCompiled-Setup.exe WiiCompiled-Setup-x86_64.AppImage WiiCompiled-Setup-aarch64.AppImage; do
|
||||||
found="$(find artifacts -type f -name "$name" | head -n 1)"
|
found="$(find artifacts -type f -name "$name" | head -n 1)"
|
||||||
[ -n "$found" ] && [ -s "$found" ] || { echo "::error::missing release asset $name"; exit 1; }
|
[ -n "$found" ] && [ -s "$found" ] || { echo "::error::missing release asset $name"; exit 1; }
|
||||||
assets+=("$found")
|
assets+=("$found")
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ foreach ($required in @('ToolkitFingerprint','TranslationFingerprint','NativeToo
|
|||||||
|
|
||||||
$manifest = [ordered]@{
|
$manifest = [ordered]@{
|
||||||
SchemaVersion = 2
|
SchemaVersion = 2
|
||||||
ProductVersion = '0.2.27'
|
ProductVersion = '0.2.28'
|
||||||
ExpectedGameId = $pins.GameId
|
ExpectedGameId = $pins.GameId
|
||||||
ExpectedDolSha256 = $pins.DolSha256
|
ExpectedDolSha256 = $pins.DolSha256
|
||||||
ExpectedRelSha256 = $pins.RelSha256
|
ExpectedRelSha256 = $pins.RelSha256
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ assert_file "$ninja_bin" "Portable Ninja"
|
|||||||
assert_file "$cc" "Portable C compiler"
|
assert_file "$cc" "Portable C compiler"
|
||||||
assert_file "$cxx" "Portable C++ compiler"
|
assert_file "$cxx" "Portable C++ compiler"
|
||||||
assert_dir "$aurora_source" "aurora-main source tree"
|
assert_dir "$aurora_source" "aurora-main source tree"
|
||||||
clang_binary=$(normalize "$toolchain_dir/bin/clang-23")
|
clang_binary=$(normalize "$toolchain_dir/bin/clang-22")
|
||||||
assert_file "$clang_binary" "Portable clang driver binary"
|
assert_file "$clang_binary" "Portable clang driver binary"
|
||||||
|
|
||||||
(( parallel > 0 )) || parallel=$(nproc)
|
(( parallel > 0 )) || parallel=$(nproc)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RootNamespace>WiiCompiled.Setup.Common.Cli</RootNamespace>
|
<RootNamespace>WiiCompiled.Setup.Common.Cli</RootNamespace>
|
||||||
<AssemblyName>WiiCompiled.Setup.Common.Cli</AssemblyName>
|
<AssemblyName>WiiCompiled.Setup.Common.Cli</AssemblyName>
|
||||||
<Version>0.2.22</Version>
|
<Version>0.2.28</Version>
|
||||||
<Authors>patchzy</Authors>
|
<Authors>patchzy</Authors>
|
||||||
<Product>WiiCompiled</Product>
|
<Product>WiiCompiled</Product>
|
||||||
<Description>Packaging-time helper: resolves (downloading if needed) the nodtool binary bundled by build-appimage.sh and Build-Installer.ps1</Description>
|
<Description>Packaging-time helper: resolves (downloading if needed) the nodtool binary bundled by build-appimage.sh and Build-Installer.ps1</Description>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RootNamespace>WiiCompiled.Setup.Common</RootNamespace>
|
<RootNamespace>WiiCompiled.Setup.Common</RootNamespace>
|
||||||
<AssemblyName>WiiCompiled.Setup.Common</AssemblyName>
|
<AssemblyName>WiiCompiled.Setup.Common</AssemblyName>
|
||||||
<Version>0.2.22</Version>
|
<Version>0.2.28</Version>
|
||||||
<Authors>patchzy</Authors>
|
<Authors>patchzy</Authors>
|
||||||
<Product>WiiCompiled</Product>
|
<Product>WiiCompiled</Product>
|
||||||
<Description>Shared nodtool/Retro-WFC-payload logic used by both the Windows and Linux installers</Description>
|
<Description>Shared nodtool/Retro-WFC-payload logic used by both the Windows and Linux installers</Description>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace WiiCompiled.Setup.Linux;
|
|||||||
internal static class ProductInfo
|
internal static class ProductInfo
|
||||||
{
|
{
|
||||||
public const string Name = "WiiCompiled";
|
public const string Name = "WiiCompiled";
|
||||||
public const string Version = "0.2.27";
|
public const string Version = "0.2.28";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>One installed product's record inside install-state.json.</summary>
|
/// <summary>One installed product's record inside install-state.json.</summary>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyName>WiiCompiled.Setup.Linux</AssemblyName>
|
<AssemblyName>WiiCompiled.Setup.Linux</AssemblyName>
|
||||||
<RootNamespace>WiiCompiled.Setup.Linux</RootNamespace>
|
<RootNamespace>WiiCompiled.Setup.Linux</RootNamespace>
|
||||||
<Version>0.2.22</Version>
|
<Version>0.2.28</Version>
|
||||||
<Authors>patchzy</Authors>
|
<Authors>patchzy</Authors>
|
||||||
<Product>WiiCompiled</Product>
|
<Product>WiiCompiled</Product>
|
||||||
<Description>Command-line installer and launcher for WiiCompiled on Linux</Description>
|
<Description>Command-line installer and launcher for WiiCompiled on Linux</Description>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ internal static class PlatformChecks
|
|||||||
internal static class ProductInfo
|
internal static class ProductInfo
|
||||||
{
|
{
|
||||||
public const string Name = "WiiCompiled";
|
public const string Name = "WiiCompiled";
|
||||||
public const string Version = "0.2.27";
|
public const string Version = "0.2.28";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The setup executable is copied into the installation under this name. It is the launcher and
|
/// The setup executable is copied into the installation under this name. It is the launcher and
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<AssemblyName>WiiCompiled.Setup</AssemblyName>
|
<AssemblyName>WiiCompiled.Setup</AssemblyName>
|
||||||
<RootNamespace>WiiCompiled.Setup.Windows</RootNamespace>
|
<RootNamespace>WiiCompiled.Setup.Windows</RootNamespace>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<Version>0.2.27</Version>
|
<Version>0.2.28</Version>
|
||||||
<Authors>patchzy</Authors>
|
<Authors>patchzy</Authors>
|
||||||
<Product>WiiCompiled</Product>
|
<Product>WiiCompiled</Product>
|
||||||
<Description>Command-line installer and launcher for WiiCompiled</Description>
|
<Description>Command-line installer and launcher for WiiCompiled</Description>
|
||||||
|
|||||||
@@ -131,9 +131,7 @@ if (( builds_retro )); then args+=(--resolved-profile "$mod_out/resolved_dispatc
|
|||||||
step emit-build-shards 'Preparing native build shards'; translator "${args[@]}"
|
step emit-build-shards 'Preparing native build shards'; translator "${args[@]}"
|
||||||
|
|
||||||
step configure-native 'Configuring the native toolchain'
|
step configure-native 'Configuring the native toolchain'
|
||||||
# Use Aurora's pinned SDL3 source on macOS. A system SDL3 can be older than
|
"$cmake_bin" -S "$workspace/runtime" -B "$native_build" -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_MAKE_PROGRAM="$ninja_bin" -DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs"
|
||||||
# Aurora's required API even when find_package() succeeds.
|
|
||||||
"$cmake_bin" -S "$workspace/runtime" -B "$native_build" -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_MAKE_PROGRAM="$ninja_bin" -DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs" -DAURORA_SDL3_PROVIDER=vendor
|
|
||||||
targets=(); [[ "$profile" != retro-rewind ]] && targets+=(WiiCompiled); [[ "$profile" != base ]] && targets+=(RetroRewind)
|
targets=(); [[ "$profile" != retro-rewind ]] && targets+=(WiiCompiled); [[ "$profile" != base ]] && targets+=(RetroRewind)
|
||||||
step compile "Compiling ${targets[*]} locally"; "$cmake_bin" --build "$native_build" --target "${targets[@]}" --parallel "$global_jobs"
|
step compile "Compiling ${targets[*]} locally"; "$cmake_bin" --build "$native_build" --target "${targets[@]}" --parallel "$global_jobs"
|
||||||
if [[ "$profile" != retro-rewind ]]; then "$script_dir/macos/publish-app.command" --build-dir "$native_build" --product WiiCompiled --output-dir "${base_output_dir:-$output_dir}"; fi
|
if [[ "$profile" != retro-rewind ]]; then "$script_dir/macos/publish-app.command" --build-dir "$native_build" --product WiiCompiled --output-dir "${base_output_dir:-$output_dir}"; fi
|
||||||
|
|||||||
@@ -103,9 +103,6 @@ copy_clean "$workspace/Launcher/local-build-macos.command" "$resources/workspace
|
|||||||
copy_clean "$workspace/Launcher/macos/extract-disc.command" "$resources/workspace/Launcher/macos/extract-disc.command"
|
copy_clean "$workspace/Launcher/macos/extract-disc.command" "$resources/workspace/Launcher/macos/extract-disc.command"
|
||||||
copy_clean "$workspace/Launcher/macos/publish-app.command" "$resources/workspace/Launcher/macos/publish-app.command"
|
copy_clean "$workspace/Launcher/macos/publish-app.command" "$resources/workspace/Launcher/macos/publish-app.command"
|
||||||
chmod +x "$resources/workspace/Launcher/local-build-macos.command" "$resources/workspace/Launcher/macos/"*.command
|
chmod +x "$resources/workspace/Launcher/local-build-macos.command" "$resources/workspace/Launcher/macos/"*.command
|
||||||
# setup.command uses this marker to update source inputs in an existing user
|
|
||||||
# workspace without replacing extracted game assets or Retro Rewind files.
|
|
||||||
printf '%s\n' "$version" > "$resources/workspace/.bundle-version"
|
|
||||||
mkdir -p "$resources/tools/cmake"
|
mkdir -p "$resources/tools/cmake"
|
||||||
copy_clean "$nodtool" "$resources/tools/nodtool"; chmod +x "$resources/tools/nodtool"
|
copy_clean "$nodtool" "$resources/tools/nodtool"; chmod +x "$resources/tools/nodtool"
|
||||||
copy_clean "$translator" "$resources/tools/Translator.Cli"; chmod +x "$resources/tools/Translator.Cli"
|
copy_clean "$translator" "$resources/tools/Translator.Cli"; chmod +x "$resources/tools/Translator.Cli"
|
||||||
|
|||||||
@@ -62,30 +62,10 @@ if ! /usr/bin/xcode-select -p >/dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$support_root" "$products"
|
mkdir -p "$support_root" "$products"
|
||||||
source_bundle_version="$workspace_source/.bundle-version"
|
if [[ ! -d "$workspace/.git" && ! -f "$workspace/projects/mkwii/recomp.yml" ]]; then
|
||||||
workspace_bundle_version="$workspace/.bundle-version"
|
|
||||||
needs_workspace_refresh=0
|
|
||||||
if [[ ! -f "$workspace/projects/mkwii/recomp.yml" ]]; then
|
|
||||||
needs_workspace_refresh=1
|
|
||||||
elif [[ -f "$source_bundle_version" ]] && [[ ! -f "$workspace_bundle_version" || "$(<"$source_bundle_version")" != "$(<"$workspace_bundle_version")" ]]; then
|
|
||||||
needs_workspace_refresh=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (( needs_workspace_refresh )); then
|
|
||||||
printf 'Preparing the local build workspace...\n'
|
printf 'Preparing the local build workspace...\n'
|
||||||
if [[ ! -d "$workspace" ]]; then
|
rm -rf "$workspace"
|
||||||
/usr/bin/ditto "$workspace_source" "$workspace"
|
/usr/bin/ditto "$workspace_source" "$workspace"
|
||||||
else
|
|
||||||
# Refresh only packaged source inputs. Assets and the staged Retro
|
|
||||||
# Rewind package belong to the user and stay in place.
|
|
||||||
for source in aurora-main projects runtime translator Launcher; do
|
|
||||||
/usr/bin/ditto "$workspace_source/$source" "$workspace/$source"
|
|
||||||
done
|
|
||||||
/usr/bin/ditto "$source_bundle_version" "$workspace_bundle_version"
|
|
||||||
# A dependency provider can be cached in this directory, so make the
|
|
||||||
# refreshed sources configure from a clean native build tree.
|
|
||||||
rm -rf "$workspace/native-build-macos"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
profile=base
|
profile=base
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ set -euo pipefail
|
|||||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
workspace=$(cd "$script_dir/.." && pwd)
|
workspace=$(cd "$script_dir/.." && pwd)
|
||||||
|
|
||||||
llvm_version=23.1.0
|
llvm_version=22.1.8
|
||||||
cmake_version=4.3.3
|
cmake_version=4.3.3
|
||||||
ninja_version=1.13.2
|
ninja_version=1.13.2
|
||||||
destination="$script_dir/artifacts/portable-tools"
|
destination="$script_dir/artifacts/portable-tools"
|
||||||
@@ -52,13 +52,13 @@ done
|
|||||||
|
|
||||||
case "$arch" in
|
case "$arch" in
|
||||||
x86_64) llvm_release_arch=X64; target_triple=x86_64-unknown-linux-gnu
|
x86_64) llvm_release_arch=X64; target_triple=x86_64-unknown-linux-gnu
|
||||||
llvm_release_sha256=18da30f77f475688a18f7704d23f9f155ae007ed9922dbed6850a9419d9fec8c
|
llvm_release_sha256=df0e1ecf16caf3489a272a5eea4eec9b0d82878f6477fa309504f918a0006384
|
||||||
cmake_release_arch=x86_64
|
cmake_release_arch=x86_64
|
||||||
cmake_sha256=927b2368a946c37269c3a66225ab00544e756459cdd0b5d0da438694fb9ff802
|
cmake_sha256=927b2368a946c37269c3a66225ab00544e756459cdd0b5d0da438694fb9ff802
|
||||||
ninja_asset=ninja-linux.zip
|
ninja_asset=ninja-linux.zip
|
||||||
ninja_sha256=5749cbc4e668273514150a80e387a957f933c6ed3f5f11e03fb30955e2bbead6 ;;
|
ninja_sha256=5749cbc4e668273514150a80e387a957f933c6ed3f5f11e03fb30955e2bbead6 ;;
|
||||||
aarch64) llvm_release_arch=ARM64; target_triple=aarch64-unknown-linux-gnu
|
aarch64) llvm_release_arch=ARM64; target_triple=aarch64-unknown-linux-gnu
|
||||||
llvm_release_sha256=cfb31bfc713ef453248bf5bd026312f838ad6c52c25623e987cb6a340f3050d4
|
llvm_release_sha256=805efad2bb91cb4967fa569e0881d10c0f69c04461cf671cccbae19f547acc34
|
||||||
cmake_release_arch=aarch64
|
cmake_release_arch=aarch64
|
||||||
cmake_sha256=9ea38356dbd3e32e51029a3e09a0f2f8e117ef4fbcaad7a21ffb36409bbd5cb4
|
cmake_sha256=9ea38356dbd3e32e51029a3e09a0f2f8e117ef4fbcaad7a21ffb36409bbd5cb4
|
||||||
ninja_asset=ninja-linux-aarch64.zip
|
ninja_asset=ninja-linux-aarch64.zip
|
||||||
@@ -120,9 +120,9 @@ echo "prepare-portable-tools.sh: pruning to the minimal compile+link toolchain..
|
|||||||
|
|
||||||
# clang: the real driver executable plus the clang/clang++ symlinks CMake/local-build.sh invoke.
|
# clang: the real driver executable plus the clang/clang++ symlinks CMake/local-build.sh invoke.
|
||||||
# Stripped: debug symbols are dead weight for a bundled compiler nobody will debug.
|
# Stripped: debug symbols are dead weight for a bundled compiler nobody will debug.
|
||||||
cp -a "$src/bin/clang-23" "$work/bin/"
|
cp -a "$src/bin/clang-22" "$work/bin/"
|
||||||
strip "$work/bin/clang-23"
|
strip "$work/bin/clang-22"
|
||||||
ln -s clang-23 "$work/bin/clang"
|
ln -s clang-22 "$work/bin/clang"
|
||||||
ln -s clang "$work/bin/clang++"
|
ln -s clang "$work/bin/clang++"
|
||||||
|
|
||||||
# lld: linked via -fuse-ld=lld, which clang resolves by looking for ld.lld next to itself first -
|
# lld: linked via -fuse-ld=lld, which clang resolves by looking for ld.lld next to itself first -
|
||||||
|
|||||||
@@ -111,21 +111,6 @@ image under Settings, turn on **WiiCompiled (beta)**, and hit install from the H
|
|||||||
Wheel Wizard downloads the setup tool from this repo and walks you through install, updates and
|
Wheel Wizard downloads the setup tool from this repo and walks you through install, updates and
|
||||||
launching. The backend itself is deliberately command-line only, Wheel Wizard is a wrapper around it.
|
launching. The backend itself is deliberately command-line only, Wheel Wizard is a wrapper around it.
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
Download `WiiCompiled-Setup.pkg` from this repository's Releases page and open it. It requires an
|
|
||||||
Apple Silicon Mac because its bundled nodtool and Translator.Cli executables are arm64. It installs
|
|
||||||
**WiiCompiled Setup** in Applications; open that app, choose your clean PAL `RMCP01` disc image,
|
|
||||||
and select either the base game or Retro Rewind. For Retro Rewind, choose the `RetroRewind6` folder
|
|
||||||
or its parent folder.
|
|
||||||
|
|
||||||
Setup verifies and extracts the image locally, then translates and compiles the native app on your
|
|
||||||
Mac. On a first run it may ask macOS to install Xcode Command Line Tools; complete Apple's installer,
|
|
||||||
then open Setup again. When the build completes, Setup asks for administrator approval once to install
|
|
||||||
`WiiCompiled.app` (and, if selected, `RetroRewind.app`) in `/Applications`.
|
|
||||||
|
|
||||||
Setup opens Terminal while it works, so the extraction and build progress—and any error that needs
|
|
||||||
reporting—remain visible.
|
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> Only take builds from this repository's
|
> Only take builds from this repository's
|
||||||
|
|||||||
@@ -41,10 +41,7 @@ if (_aurora_sdl3_provider STREQUAL "auto")
|
|||||||
set(_aurora_sdl3_provider "package")
|
set(_aurora_sdl3_provider "package")
|
||||||
else ()
|
else ()
|
||||||
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
|
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
|
||||||
# Aurora uses APIs from the SDL version pinned by AURORA_SDL3_VERSION.
|
find_package(SDL3 QUIET)
|
||||||
# Do not silently select an older system package and fail later while
|
|
||||||
# compiling its headers.
|
|
||||||
find_package(SDL3 ${AURORA_SDL3_VERSION} QUIET)
|
|
||||||
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL OFF)
|
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL OFF)
|
||||||
if (SDL3_FOUND)
|
if (SDL3_FOUND)
|
||||||
set(_aurora_sdl3_provider "system")
|
set(_aurora_sdl3_provider "system")
|
||||||
@@ -61,7 +58,7 @@ if (_aurora_sdl3_provider STREQUAL "system")
|
|||||||
message(STATUS "aurora: Using system SDL3 (provider=system)")
|
message(STATUS "aurora: Using system SDL3 (provider=system)")
|
||||||
if (NOT SDL3_FOUND)
|
if (NOT SDL3_FOUND)
|
||||||
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
|
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
|
||||||
find_package(SDL3 ${AURORA_SDL3_VERSION} REQUIRED)
|
find_package(SDL3 REQUIRED)
|
||||||
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL OFF)
|
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL OFF)
|
||||||
endif ()
|
endif ()
|
||||||
_aurora_sdl3_select_target()
|
_aurora_sdl3_select_target()
|
||||||
|
|||||||
@@ -1438,10 +1438,7 @@ void __PADWriteDeadZones(SDL_IOStream* file, // NOLINT(*-reserved-identifier)
|
|||||||
void PADSerializeMappings() {
|
void PADSerializeMappings() {
|
||||||
const std::filesystem::path basePath = fs_path_from_string(aurora::g_config.userPath);
|
const std::filesystem::path basePath = fs_path_from_string(aurora::g_config.userPath);
|
||||||
|
|
||||||
// Avoid std::views::values here: older Apple libc++ releases implement the
|
for (auto& controller : aurora::input::g_GameControllers | std::views::values) {
|
||||||
// C++20 ranges algorithms we use but not this adaptor.
|
|
||||||
for (auto& entry : aurora::input::g_GameControllers) {
|
|
||||||
auto& controller = entry.second;
|
|
||||||
EnsureMappingLoaded(&controller);
|
EnsureMappingLoaded(&controller);
|
||||||
const auto filePath =
|
const auto filePath =
|
||||||
basePath / fmt::format("{}_{:04X}_{:04X}.controller", aurora::input::controller_name(controller.m_index),
|
basePath / fmt::format("{}_{:04X}_{:04X}.controller", aurora::input::controller_name(controller.m_index),
|
||||||
|
|||||||
@@ -401,6 +401,8 @@ SDL_JoystickID add_controller(SDL_JoystickID which) noexcept {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
controller.m_isGameCube = controller.m_vid == 0x057E && controller.m_pid == 0x0337;
|
controller.m_isGameCube = controller.m_vid == 0x057E && controller.m_pid == 0x0337;
|
||||||
|
const char* serial = SDL_GetGamepadSerial(ctrl);
|
||||||
|
controller.m_gameCubeUseOrdinaryStop = controller.m_isGameCube && serial && "GCP+"sv == serial;
|
||||||
if (controller.m_isGameCube ||
|
if (controller.m_isGameCube ||
|
||||||
(SDL_GetGamepadType(ctrl) == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO && controller.m_pid == 0x2073)) {
|
(SDL_GetGamepadType(ctrl) == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO && controller.m_pid == 0x2073)) {
|
||||||
controller.m_deadZones.emulateTriggers = false;
|
controller.m_deadZones.emulateTriggers = false;
|
||||||
@@ -481,6 +483,13 @@ bool controller_has_rumble(Uint32 instance) noexcept {
|
|||||||
void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity,
|
void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity,
|
||||||
uint16_t duration_ms) noexcept {
|
uint16_t duration_ms) noexcept {
|
||||||
if (auto it = g_GameControllers.find(instance); it != g_GameControllers.end()) {
|
if (auto it = g_GameControllers.find(instance); it != g_GameControllers.end()) {
|
||||||
|
// GC Pocket+ has been observed continuing to vibrate after a hard stop;
|
||||||
|
// an ordinary stop cleared it. With GAMECUBE_RUMBLE_BRAKE enabled, SDL
|
||||||
|
// encodes (0, 1) as adapter command 0, whereas (0, 0) sends command 2.
|
||||||
|
// Apply the workaround here so shutdown uses the same stop as PAD calls.
|
||||||
|
if (it->second.m_gameCubeUseOrdinaryStop && low_freq_intensity == 0 && high_freq_intensity == 0) {
|
||||||
|
high_freq_intensity = 1;
|
||||||
|
}
|
||||||
SDL_RumbleGamepad(it->second.m_controller, low_freq_intensity, high_freq_intensity, duration_ms);
|
SDL_RumbleGamepad(it->second.m_controller, low_freq_intensity, high_freq_intensity, duration_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ extern Module Log;
|
|||||||
struct GameController {
|
struct GameController {
|
||||||
SDL_Gamepad* m_controller = nullptr;
|
SDL_Gamepad* m_controller = nullptr;
|
||||||
bool m_isGameCube = false;
|
bool m_isGameCube = false;
|
||||||
|
bool m_gameCubeUseOrdinaryStop = false;
|
||||||
Sint32 m_index = -1;
|
Sint32 m_index = -1;
|
||||||
Sint32 m_playerIndex = -1;
|
Sint32 m_playerIndex = -1;
|
||||||
bool m_hasRumble = false;
|
bool m_hasRumble = false;
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ std::string GetOSVersion() {
|
|||||||
constexpr auto name = "iOS";
|
constexpr auto name = "iOS";
|
||||||
#elif TARGET_OS_TV
|
#elif TARGET_OS_TV
|
||||||
constexpr auto name = "tvOS";
|
constexpr auto name = "tvOS";
|
||||||
#else
|
#elif
|
||||||
constexpr auto name = Unknown;
|
constexpr auto name = Unknown;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+5
-10
@@ -17,16 +17,6 @@ else()
|
|||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"WiiCompiled supports 64-bit LLVM-MinGW Clang on Windows, native Linux x86_64/aarch64, or Apple Clang on macOS arm64")
|
"WiiCompiled supports 64-bit LLVM-MinGW Clang on Windows, native Linux x86_64/aarch64, or Apple Clang on macOS arm64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Do not inherit the host SDK's deployment target (macOS 26 on current
|
|
||||||
# toolchains). The supported Apple Silicon release is macOS 14 and the app
|
|
||||||
# bundle advertises that same minimum. This remains arm64-only until the Intel
|
|
||||||
# support work is merged.
|
|
||||||
if(MKW_PLATFORM_MACOS)
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "14.0" CACHE STRING
|
|
||||||
"Minimum macOS version for the Apple Silicon build" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
|
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()
|
||||||
@@ -287,6 +277,11 @@ target_link_libraries(mkw_platform_paths_tests PRIVATE mkw_platform)
|
|||||||
target_compile_features(mkw_platform_paths_tests PRIVATE cxx_std_17)
|
target_compile_features(mkw_platform_paths_tests PRIVATE cxx_std_17)
|
||||||
add_test(NAME mkw_platform_paths_tests COMMAND mkw_platform_paths_tests)
|
add_test(NAME mkw_platform_paths_tests COMMAND mkw_platform_paths_tests)
|
||||||
|
|
||||||
|
add_executable(mkw_nand_settings_tests "${CMAKE_CURRENT_LIST_DIR}/tests/nand_settings_tests.cpp")
|
||||||
|
target_include_directories(mkw_nand_settings_tests PRIVATE "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||||
|
target_compile_features(mkw_nand_settings_tests PRIVATE cxx_std_17)
|
||||||
|
add_test(NAME mkw_nand_settings_tests COMMAND mkw_nand_settings_tests)
|
||||||
|
|
||||||
# The input expression engine is self-contained, so it can be exercised without
|
# The input expression engine is self-contained, so it can be exercised without
|
||||||
# linking the runtime or SDL.
|
# linking the runtime or SDL.
|
||||||
add_executable(mkw_input_expr_tests
|
add_executable(mkw_input_expr_tests
|
||||||
|
|||||||
@@ -1,38 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "runtime_config.h"
|
#include "nand_path.h"
|
||||||
|
#include "nand_settings.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cctype>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <optional>
|
|
||||||
#include <random>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace RuntimeConsoleIdentity {
|
namespace RuntimeConsoleIdentity {
|
||||||
|
|
||||||
struct Identity {
|
struct Identity {
|
||||||
std::string serial;
|
std::string serial;
|
||||||
|
std::string productCode;
|
||||||
|
std::string area;
|
||||||
|
std::string gameRegion;
|
||||||
std::array<uint8_t, 6> mac;
|
std::array<uint8_t, 6> mac;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool IsValidSerial(const std::string& serial) {
|
|
||||||
return serial.size() == 9 &&
|
|
||||||
serial != "000000000" &&
|
|
||||||
std::all_of(serial.begin(), serial.end(),
|
|
||||||
[](unsigned char value) { return std::isdigit(value) != 0; });
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Identity FromSerial(std::string serial) {
|
inline Identity FromSerial(std::string serial) {
|
||||||
// Keep Nintendo's Wii OUI. The suffix is derived from the persisted serial
|
// Keep Nintendo's Wii OUI. The suffix is derived from the NAND serial
|
||||||
// so every API exposes one coherent, stable virtual-console identity.
|
// so every API exposes one coherent, stable virtual-console identity.
|
||||||
uint32_t hash = 2166136261u;
|
uint32_t hash = 2166136261u;
|
||||||
for (const unsigned char value : serial) {
|
for (const unsigned char value : serial) {
|
||||||
@@ -46,6 +36,7 @@ inline Identity FromSerial(std::string serial) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
std::move(serial),
|
std::move(serial),
|
||||||
|
{}, {}, {},
|
||||||
{
|
{
|
||||||
0x00,
|
0x00,
|
||||||
0x09,
|
0x09,
|
||||||
@@ -57,83 +48,23 @@ inline Identity FromSerial(std::string serial) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::optional<std::string> ReadSerial(const std::filesystem::path& path) {
|
inline Identity LoadFromNand() {
|
||||||
std::ifstream input(path);
|
const auto root = RuntimeNandPath::DiscoverNandRootPath();
|
||||||
std::string line;
|
const auto settings = RuntimeNandSettings::Read(root);
|
||||||
if (!input || !std::getline(input, line)) {
|
if (!settings || !RuntimeNandSettings::HasIdentity(*settings)) {
|
||||||
return std::nullopt;
|
RuntimeNandPath::FailNandRoot(
|
||||||
|
"NAND setting.txt is missing or has invalid console identity fields (SERNO, CODE, AREA, GAME)",
|
||||||
|
root / "title/00000001/00000002/data/setting.txt");
|
||||||
}
|
}
|
||||||
constexpr std::string_view prefix = "serial=";
|
Identity identity = FromSerial(settings->at("SERNO"));
|
||||||
if (line.rfind(prefix, 0) != 0) {
|
identity.productCode = settings->at("CODE");
|
||||||
return std::nullopt;
|
identity.area = settings->at("AREA");
|
||||||
}
|
identity.gameRegion = settings->at("GAME");
|
||||||
std::string serial = line.substr(prefix.size());
|
return identity;
|
||||||
if (!IsValidSerial(serial)) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool WriteSerial(const std::filesystem::path& path, const std::string& serial) {
|
|
||||||
std::error_code ec;
|
|
||||||
std::filesystem::create_directories(path.parent_path(), ec);
|
|
||||||
if (ec) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path temporary = path;
|
|
||||||
temporary += ".tmp";
|
|
||||||
{
|
|
||||||
std::ofstream output(temporary, std::ios::trunc);
|
|
||||||
if (!output) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
output << "serial=" << serial << '\n';
|
|
||||||
output.close();
|
|
||||||
if (!output) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::rename(temporary, path, ec);
|
|
||||||
if (!ec) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
std::filesystem::remove(temporary, ec);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string GenerateSerial() {
|
|
||||||
std::random_device entropy;
|
|
||||||
std::seed_seq seed{
|
|
||||||
entropy(),
|
|
||||||
entropy(),
|
|
||||||
entropy(),
|
|
||||||
entropy(),
|
|
||||||
};
|
|
||||||
std::mt19937 generator(seed);
|
|
||||||
std::uniform_int_distribution<uint32_t> distribution(100000000u, 999999999u);
|
|
||||||
return std::to_string(distribution(generator));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Identity LoadOrCreate(const std::filesystem::path& path) {
|
|
||||||
if (const auto serial = ReadSerial(path)) {
|
|
||||||
return FromSerial(*serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string generated = GenerateSerial();
|
|
||||||
if (WriteSerial(path, generated)) {
|
|
||||||
return FromSerial(generated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remain operational in a read-only environment. This fallback matches
|
|
||||||
// Dolphin's deterministic serial while keeping the same valid identity shape.
|
|
||||||
return FromSerial("123456789");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Identity& Current() {
|
inline const Identity& Current() {
|
||||||
static const Identity identity =
|
static const Identity identity = LoadFromNand();
|
||||||
LoadOrCreate(RuntimeConfigFile::ApplicationDataDirectory() / "ConsoleIdentity.txt");
|
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace RuntimeNandSettings {
|
||||||
|
|
||||||
|
using Settings = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
// Wii setting.txt is a 256-byte buffer encrypted with a rotating XOR key.
|
||||||
|
inline std::optional<Settings> Read(const std::filesystem::path& nandRoot) {
|
||||||
|
std::ifstream input(nandRoot / "title/00000001/00000002/data/setting.txt",
|
||||||
|
std::ios::binary);
|
||||||
|
std::array<uint8_t, 256> bytes{};
|
||||||
|
if (!input.read(reinterpret_cast<char*>(bytes.data()), bytes.size())) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
uint32_t key = 0x73B5DBFAu;
|
||||||
|
std::string decoded;
|
||||||
|
for (const uint8_t byte : bytes) {
|
||||||
|
const char value = static_cast<char>(byte ^ static_cast<uint8_t>(key));
|
||||||
|
key = (key << 1) | (key >> 31);
|
||||||
|
if (value == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value != '\r') {
|
||||||
|
decoded += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Settings settings;
|
||||||
|
for (size_t start = 0; start < decoded.size();) {
|
||||||
|
const size_t end = decoded.find('\n', start);
|
||||||
|
const std::string line = decoded.substr(start, end - start);
|
||||||
|
const size_t equals = line.find('=');
|
||||||
|
if (equals != std::string::npos && equals != 0) {
|
||||||
|
settings.emplace(line.substr(0, equals), line.substr(equals + 1));
|
||||||
|
}
|
||||||
|
if (end == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HasIdentity(const Settings& settings) {
|
||||||
|
const auto serial = settings.find("SERNO");
|
||||||
|
if (serial == settings.end() || serial->second.empty() || serial->second.size() > 9 ||
|
||||||
|
serial->second.find_first_not_of("0123456789") != std::string::npos ||
|
||||||
|
serial->second.find_first_not_of('0') == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto& field : {std::pair{"CODE", 5u}, {"AREA", 3u}, {"GAME", 2u}}) {
|
||||||
|
const auto value = settings.find(field.first);
|
||||||
|
if (value == settings.end() || value->second.empty() ||
|
||||||
|
value->second.size() > field.second) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RuntimeNandSettings
|
||||||
@@ -256,8 +256,12 @@ bool GuestFiberManager::CreateGuestFiber(uint32_t guestThreadAddr, uint32_t entr
|
|||||||
gf.cpuContext.srr0 = entryPoint;
|
gf.cpuContext.srr0 = entryPoint;
|
||||||
|
|
||||||
// The host stack models only translated host calls; the guest stack starts
|
// The host stack models only translated host calls; the guest stack starts
|
||||||
// at stackBase in the CPU context above.
|
// at stackBase in the CPU context above. 64 KiB is too small for deep
|
||||||
constexpr size_t kHostStackSize = 64 * 1024;
|
// translated/HLE call chains (notably NW4R's sound worker), and on macOS
|
||||||
|
// it can exhaust the guarded coroutine stack as unrelated host work (such
|
||||||
|
// as a window resize) adds a little more nesting. Keep enough headroom for
|
||||||
|
// those chains while the guest stack remains separately bounded.
|
||||||
|
constexpr size_t kHostStackSize = 1024 * 1024;
|
||||||
gf.fiber = HostContext::Create(kHostStackSize, FiberProc,
|
gf.fiber = HostContext::Create(kHostStackSize, FiberProc,
|
||||||
reinterpret_cast<void*>(static_cast<uintptr_t>(guestThreadAddr)));
|
reinterpret_cast<void*>(static_cast<uintptr_t>(guestThreadAddr)));
|
||||||
|
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ int32_t HandleIpTopIoctlv(uint32_t cmd, const std::vector<IoVector>& in, const s
|
|||||||
// Nonblocking sockets get -SO_EAGAIN immediately (Dolphin's retry predicate
|
// Nonblocking sockets get -SO_EAGAIN immediately (Dolphin's retry predicate
|
||||||
// short-circuits on nonBlock/forceNonBlock, IOS/Network/Socket.cpp:715-718);
|
// short-circuits on nonBlock/forceNonBlock, IOS/Network/Socket.cpp:715-718);
|
||||||
// waiting here anyway stalled the whole emulation thread on every empty read.
|
// waiting here anyway stalled the whole emulation thread on every empty read.
|
||||||
constexpr int kStreamRecvWaitMs = 250;
|
constexpr int kStreamRecvWaitMs = 1000;
|
||||||
const int streamWaitMs = (forceNonBlock || s->nonblocking) ? 0 : kStreamRecvWaitMs;
|
const int streamWaitMs = (forceNonBlock || s->nonblocking) ? 0 : kStreamRecvWaitMs;
|
||||||
const bool waited = ret < 0 && !fromPtr && s->type == SOCK_STREAM &&
|
const bool waited = ret < 0 && !fromPtr && s->type == SOCK_STREAM &&
|
||||||
IsWouldBlockError(nativeErr) && WaitForReadable(s->native, streamWaitMs);
|
IsWouldBlockError(nativeErr) && WaitForReadable(s->native, streamWaitMs);
|
||||||
|
|||||||
+29
-15
@@ -12,7 +12,25 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uint32_t kPalProductRegion = 2;
|
// Use the SDK's own value tables, including its unknown-region result.
|
||||||
|
uint32_t LookupProductRegion(uint32_t table, uint32_t stride, uint32_t count,
|
||||||
|
const std::string& value) {
|
||||||
|
for (uint32_t index = 0; index < count; ++index) {
|
||||||
|
const uint32_t entry = table + index * stride;
|
||||||
|
if (!Memory::Contains(entry, stride)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto* bytes = static_cast<const uint8_t*>(Memory::GetPointer(entry, stride));
|
||||||
|
if (bytes[0] == 0xFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value.size() < stride - 1 &&
|
||||||
|
std::memcmp(bytes + 1, value.c_str(), value.size() + 1) == 0) {
|
||||||
|
return bytes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xFFFFFFFFu;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -50,16 +68,12 @@ extern "C" uint32_t SCGetEuRgb60Mode_HLE()
|
|||||||
|
|
||||||
PPC_NATIVE_OVERRIDE(801B1CAC, SCGetEuRgb60Mode_HLE, uint32_t, (), ());
|
PPC_NATIVE_OVERRIDE(801B1CAC, SCGetEuRgb60Mode_HLE, uint32_t, (), ());
|
||||||
|
|
||||||
// The managed NAND intentionally starts without a console-owned setting.txt.
|
// Expose the selected emulated NAND identity through the SDK SC APIs.
|
||||||
// DWC nevertheless requires the Wii product code and serial number so it can
|
|
||||||
// include csnum in NAS authentication. Expose one stable virtual-console
|
|
||||||
// identity without requiring or mutating a user's real NAND.
|
|
||||||
|
|
||||||
extern "C" uint32_t SCGetProductArea_HLE()
|
extern "C" uint32_t SCGetProductArea_HLE()
|
||||||
{
|
{
|
||||||
// The PAL setting.txt AREA value is "EUR". The SDK's lookup table at
|
return LookupProductRegion(0x8029CEB0u, 5, 13,
|
||||||
// 0x8029CEB0 maps JPN=0, USA=1, EUR=2.
|
RuntimeConsoleIdentity::Current().area);
|
||||||
return kPalProductRegion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PPC_NATIVE_OVERRIDE(801B23A0, SCGetProductArea_HLE, uint32_t, (), ());
|
PPC_NATIVE_OVERRIDE(801B23A0, SCGetProductArea_HLE, uint32_t, (), ());
|
||||||
@@ -68,12 +82,13 @@ extern "C" uint32_t SCGetProductCode_HLE()
|
|||||||
{
|
{
|
||||||
// Original PAL SC storage for the six-byte CODE value.
|
// Original PAL SC storage for the six-byte CODE value.
|
||||||
constexpr uint32_t kProductCodeAddress = 0x803869E0u;
|
constexpr uint32_t kProductCodeAddress = 0x803869E0u;
|
||||||
static constexpr char kProductCode[] = "LEH";
|
const std::string& productCode = RuntimeConsoleIdentity::Current().productCode;
|
||||||
if (!Memory::Contains(kProductCodeAddress, sizeof(kProductCode))) {
|
const size_t size = productCode.size() + 1;
|
||||||
|
if (!Memory::Contains(kProductCodeAddress, size)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
std::memcpy(Memory::GetPointer(kProductCodeAddress, sizeof(kProductCode)),
|
std::memcpy(Memory::GetPointer(kProductCodeAddress, size),
|
||||||
kProductCode, sizeof(kProductCode));
|
productCode.c_str(), size);
|
||||||
return kProductCodeAddress;
|
return kProductCodeAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +109,8 @@ PPC_NATIVE_OVERRIDE(801B2460, SCGetProductSN_HLE, uint32_t, (uint32_t serialAddr
|
|||||||
|
|
||||||
extern "C" uint32_t SCGetProductGameRegion_HLE()
|
extern "C" uint32_t SCGetProductGameRegion_HLE()
|
||||||
{
|
{
|
||||||
// The PAL setting.txt GAME value is "EU". The SDK's own lookup table at
|
return LookupProductRegion(0x8029CEF8u, 4, 4,
|
||||||
// 0x8029CEF8 maps JP=0, US=1, EU=2.
|
RuntimeConsoleIdentity::Current().gameRegion);
|
||||||
return kPalProductRegion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PPC_NATIVE_OVERRIDE(801B24C8, SCGetProductGameRegion_HLE, uint32_t, (), ());
|
PPC_NATIVE_OVERRIDE(801B24C8, SCGetProductGameRegion_HLE, uint32_t, (), ());
|
||||||
|
|||||||
@@ -483,11 +483,6 @@ void DrawRumbleSettings() {
|
|||||||
PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD,
|
PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD, PAD_MOTOR_STOP_HARD,
|
||||||
};
|
};
|
||||||
PADControlAllMotors(stopAll.data());
|
PADControlAllMotors(stopAll.data());
|
||||||
#if defined(_WIN32)
|
|
||||||
for (uint32_t port = 0; port < PAD_MAX_CONTROLLERS; ++port) {
|
|
||||||
Wup028Adapter::SetRumble(port, false);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#include "nand_settings.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
static void Require(bool condition) {
|
||||||
|
if (!condition) {
|
||||||
|
throw std::runtime_error("NAND settings check failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const auto root = std::filesystem::temp_directory_path() /
|
||||||
|
("wiicomp-nand-settings-" + std::to_string(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||||
|
const auto path = root / "title/00000001/00000002/data/setting.txt";
|
||||||
|
try {
|
||||||
|
Require(!RuntimeNandSettings::Read(root));
|
||||||
|
Require(!std::filesystem::exists(root));
|
||||||
|
std::filesystem::create_directories(path.parent_path());
|
||||||
|
const std::string plain = "AREA=USA\r\n\nCODE=LU\r\nSERNO=987654321\r\nGAME=US\r\n";
|
||||||
|
std::array<uint8_t, 256> fixture{};
|
||||||
|
for (size_t i = 0; i < fixture.size(); ++i) {
|
||||||
|
const unsigned shift = i % 32;
|
||||||
|
const uint32_t key = shift == 0 ? 0x73B5DBFAu :
|
||||||
|
(0x73B5DBFAu << shift) | (0x73B5DBFAu >> (32 - shift));
|
||||||
|
fixture[i] = static_cast<uint8_t>(key) ^ (i < plain.size() ? plain[i] : 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::ofstream output(path, std::ios::binary);
|
||||||
|
output.write(reinterpret_cast<const char*>(fixture.data()), fixture.size());
|
||||||
|
}
|
||||||
|
auto settings = RuntimeNandSettings::Read(root);
|
||||||
|
Require(settings && RuntimeNandSettings::HasIdentity(*settings));
|
||||||
|
Require(settings->at("SERNO") == "987654321" && settings->at("CODE") == "LU");
|
||||||
|
Require(settings->at("AREA") == "USA" && settings->at("GAME") == "US");
|
||||||
|
std::array<uint8_t, 256> after{};
|
||||||
|
{
|
||||||
|
std::ifstream input(path, std::ios::binary);
|
||||||
|
input.read(reinterpret_cast<char*>(after.data()), after.size());
|
||||||
|
}
|
||||||
|
Require(after == fixture);
|
||||||
|
for (const auto serial : {"", "000000000", "1234567890", "123ABC789"}) {
|
||||||
|
(*settings)["SERNO"] = serial;
|
||||||
|
Require(!RuntimeNandSettings::HasIdentity(*settings));
|
||||||
|
}
|
||||||
|
(*settings)["SERNO"] = "012345678";
|
||||||
|
Require(RuntimeNandSettings::HasIdentity(*settings));
|
||||||
|
(*settings)["CODE"] = "TOOLONG";
|
||||||
|
Require(!RuntimeNandSettings::HasIdentity(*settings));
|
||||||
|
(*settings)["CODE"] = "LEH";
|
||||||
|
settings->erase("GAME");
|
||||||
|
Require(!RuntimeNandSettings::HasIdentity(*settings));
|
||||||
|
std::filesystem::resize_file(path, 128);
|
||||||
|
Require(!RuntimeNandSettings::Read(root));
|
||||||
|
std::filesystem::remove_all(root);
|
||||||
|
std::cout << "NAND settings checks passed\n";
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
std::filesystem::remove_all(root);
|
||||||
|
std::cerr << error.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user