Track exact StarletOS bootstrap integration and checkpoint provenance

Recover 16 modified and 6 previously untracked integration files. All 22 source SHA-256s match the pre-publication StarletOS snapshot. Add handoff instructions, byte-preserving attributes and private backup exclusion. This commit publishes existing behavior; it does not claim a new full build or hardware qualification. The two preceding local commits and staged files were screened before publication.
This commit is contained in:
Yaya48 committed 2026-10-03 16:27:07 +02:00
1 parent a8dbbab62d
commit 54f5a74bbf
28 files changed
+9161 -7407

No files matched your search

+25
View File
@@ -1 +1,26 @@
*.sln text eol=crlf
# Preserve the qualified StarletOS integration bytes across operating systems.
CMakeLists.txt -text
Run-Wii-Starlet-Bootstrap.ps1 -text
Source/Core/Core/Boot/Boot.cpp -text
Source/Core/Core/CMakeLists.txt -text
Source/Core/Core/Config/MainSettings.cpp -text
Source/Core/Core/Config/MainSettings.h -text
Source/Core/Core/ConfigManager.cpp -text
Source/Core/Core/HW/HW.cpp -text
Source/Core/Core/IOS/Starlet/ARMCore.cpp -text
Source/Core/Core/IOS/Starlet/BootstrapELF.cpp -text
Source/Core/Core/IOS/Starlet/BootstrapELF.h -text
Source/Core/Core/IOS/Starlet/Starlet.cpp -text
Source/Core/Core/IOS/Starlet/Starlet.h -text
Source/Core/Core/IOS/Starlet/StarletMemory.cpp -text
Source/Core/Core/IOS/Starlet/StarletMemory.h -text
Source/Core/DolphinNoGUI/PlatformHeadless.cpp -text
Source/UnitTests/CMakeLists.txt -text
Source/UnitTests/Core/CMakeLists.txt -text
Source/UnitTests/Core/IOS/Starlet/BootstrapELFTest.cpp -text
Source/UnitTests/Core/IOS/Starlet/DMATest.cpp -text
Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp -text
docs/Wii_Starlet_Bootstrap.md -text
docs/StarletOS_Checkpoint.json -text
+3
View File
@@ -75,3 +75,6 @@ CMakeUserPresets.json
# Generated bytecode from the performance analysis helpers
/Tools/PerformanceTrace/__pycache__/
# Historical local script backup; active script is tracked.
/Run-Wii-Starlet-Bootstrap.ps1.before-*
+30
View File
@@ -0,0 +1,30 @@
# Instructions for the StarletOS Dolphin fork
Read `docs/StarletOS_Handoff.md`, `docs/StarletOS_Checkpoint.json` and
`docs/Wii_Starlet_Bootstrap.md` before working on the Starlet integration.
Also read the companion StarletOS HANDOFF and current phase SPEC/STATUS when
changing support for its tests. Current user instructions take precedence.
- One writer per checkout. Inspect Git status and the latest handoff before
editing; preserve unrelated changes and never overwrite another agent's work.
- Fetch the user's fork before work and inspect divergence. Use fast-forward
pulls only on a clean branch; never reset or force-push to hide divergence.
- The publication destination is `https://git.yaya48.xyz/Yaya48/dolphin`.
On the original checkout it is named `yaya`; on a fresh clone it is normally
`origin`. The github.com/dolphin-emu upstream is NOT the publication target.
- Preserve qualified source bytes/line endings. Do not broadly normalize the
repository. Exact integration files are protected by `.gitattributes`.
- Do not commit dumps, console keys/OTP/SEEPROM, Nintendo binaries, real device
identifiers, credentials, private emulator profiles or diagnostic captures.
Stage explicit paths and inspect staged contents AND pending commits before
publishing. Never use force-add to bypass these exclusions.
- Respect companion phase boundaries and public-source/clean-room rules. A
fake emulated device is supporting test evidence, not the physical contract.
- Update the handoff with changes, reasons, commands/results, limitations and
the next exact action at every stop. Preserve old evidence and distinguish
source inspection, build results, emulation and physical observations.
- Commit and push completed work to the user's fork, verify the remote SHA,
then update StarletOS's pinned Dolphin commit/hashes. Never leave a required
source change only in a private bundle or a copy in another repository.
- Keep generated caches and private evidence locally; no blanket cleanup of
ignored files. Do not modify a removable SD or console for a Git-only task.
+3
View File
@@ -0,0 +1,3 @@
# Shared instructions
Read AGENTS.md and docs/StarletOS_Handoff.md before working in this fork.
+887 -879
View File
File diff suppressed because it is too large. Load diff
+322
View File
@@ -0,0 +1,322 @@
param(
[string]$ELF = '../starlet-ios-rust/target/armebv5te-none-eabi/release/revolution-ios-bootstrap',
[string]$BuildDirectory = '.starlet_msvc2',
[string]$UserDirectory = '.starlet_user3',
[ValidateSet('Idle', 'Panic', 'Fatal', 'Fault', 'Phase2', 'Phase3Raw', 'Phase3Ios', 'Phase4Fs', 'Phase5Dir', 'Phase5Es', 'Phase5Client', 'Phase6Write', 'Phase6Verify', 'Phase8Client', 'Phase9Resolve', 'Phase9Launch', 'Phase9Menu', 'Phase9Rescue', 'Phase10AOh1')][string]$ExpectedOutcome = 'Idle',
[string]$PpcDol = '',
[int]$TimeoutSeconds = 30,
# Phase9Launch: the launched program's minimum check count. 5 for the
# P09-H02 payload and a requested P09-H02M launch; 3 for a P09-H02M launch
# made by the kernel's watchdog (no launcher mailbox to check).
[ValidateRange(1, 5)][int]$Phase9MinimumChecks = 5,
# Phase10AOh1: the program's minimum check count. 40 for the P10A-H07
# reduced smoke; 16 for the P10A-H07B launched payload.
[ValidateRange(1, 1000)][int]$Phase10AMinimumChecks = 40,
[switch]$JIT,
[switch]$FullDolphin
)
$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$elfCandidate = if ([System.IO.Path]::IsPathRooted($ELF)) { $ELF } else { Join-Path $repoRoot $ELF }
$elfPath = (Resolve-Path -LiteralPath $elfCandidate).Path
$dolPath = if ($PpcDol) { (Resolve-Path -LiteralPath $PpcDol).Path } else { '' }
$probePath = Join-Path $repoRoot "$BuildDirectory/Binaries/Tests/StarletBootstrapProbe.exe"
if (-not $FullDolphin) {
if ($PpcDol) { throw 'PpcDol requires FullDolphin' }
if (-not (Test-Path -LiteralPath $probePath -PathType Leaf)) {
throw "Build StarletBootstrapProbe first: $probePath"
}
$probeArguments = @($elfPath)
if ($JIT) { $probeArguments += '--jit' }
if ($ExpectedOutcome -eq 'Panic') { $probeArguments += '--expect-panic' }
if ($ExpectedOutcome -eq 'Fatal') { $probeArguments += '--expect-fatal-data' }
if ($ExpectedOutcome -eq 'Fault') { $probeArguments += '--expect-undefined-fault' }
if ($ExpectedOutcome -eq 'Phase2') { $probeArguments += '--expect-phase2' }
& $probePath @probeArguments
exit $LASTEXITCODE
}
$dolphinPath = Join-Path $repoRoot "$BuildDirectory/Binaries/DolphinNoGUI.exe"
$userPath = (Resolve-Path -LiteralPath (Join-Path $repoRoot $UserDirectory)).Path
$logPath = Join-Path $userPath 'Logs/dolphin.log'
if (-not (Test-Path -LiteralPath $dolphinPath -PathType Leaf)) {
throw "Build DolphinNoGUI first: $dolphinPath"
}
if ($TimeoutSeconds -lt 1) { throw 'TimeoutSeconds must be positive' }
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $logPath) | Out-Null
$bootArguments = if ($dolPath) { @('-e', "`"$dolPath`"") } else { @('-n', '0000000100000002') }
$arguments = @('-u', "`"$userPath`"") + $bootArguments + @(
'-C', 'Dolphin.Core.WiiIOSLLE=True',
'-C', 'Dolphin.Core.WiiSDCard=False',
'-C', "Dolphin.Core.WiiStarletJIT=$($JIT.IsPresent)",
'-C', "`"Dolphin.General.WiiStarletBootstrapELF=$elfPath`"",
'-C', 'Logger.Options.WriteToFile=True',
'-C', 'Logger.Options.Verbosity=4',
'-C', 'Logger.Logs.IOS=True',
'-v', 'Null', '-p', 'headless'
)
$position = if (Test-Path -LiteralPath $logPath) { (Get-Item -LiteralPath $logPath).Length } else { 0 }
$pending = ''
$trace = [System.Collections.Generic.List[string]]::new()
$rescueStarted = $false
$outcome = $null
$faultLine = $null
$probeCounters = $null
$probeChecks = $null
$dolphin = Start-Process -FilePath $dolphinPath -ArgumentList $arguments `
-WorkingDirectory $repoRoot -PassThru -WindowStyle Hidden
try {
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
while ((Get-Date) -lt $deadline) {
if (Test-Path -LiteralPath $logPath) {
$stream = [System.IO.File]::Open($logPath, [System.IO.FileMode]::Open,
[System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
try {
if ($position -gt $stream.Length) { $position = 0; $pending = '' }
[void]$stream.Seek($position, [System.IO.SeekOrigin]::Begin)
$reader = [System.IO.StreamReader]::new($stream, [System.Text.Encoding]::UTF8,
$false, 4096, $true)
try { $chunk = $reader.ReadToEnd() } finally { $reader.Dispose() }
$position = $stream.Position
} finally {
$stream.Dispose()
}
if ($chunk.Length -gt 0) {
$lines = ($pending + $chunk) -split "`n"
$pending = $lines[-1]
for ($index = 0; $index -lt $lines.Length - 1; ++$index) {
$line = $lines[$index]
if ($line -match 'Starlet bootstrap: (.+)$') {
$message = $Matches[1].TrimEnd("`r")
$trace.Add($message)
Write-Host $message
if ($message -eq 'REV:BOOT:IDLE' -and $ExpectedOutcome -notin @('Phase3Raw', 'Phase3Ios', 'Phase4Fs', 'Phase5Dir', 'Phase5Es', 'Phase5Client', 'Phase6Write', 'Phase6Verify', 'Phase8Client', 'Phase9Launch', 'Phase9Menu', 'Phase9Rescue', 'Phase10AOh1')) {
$outcome = if ($ExpectedOutcome -eq 'Phase2') { 'Phase2' } else { 'Idle' }
}
elseif ($message -like 'REV:PANIC*') { $outcome = 'Panic' }
# Revolution IOS Phase 09 P09-H01: the read-only System Menu resolution ends.
elseif ($ExpectedOutcome -eq 'Phase9Resolve' -and $message -like 'REV:P9:H01 DONE*') { $outcome = 'Phase9Resolve' }
elseif ($ExpectedOutcome -eq 'Phase9Resolve' -and $message -like 'REV:P9:H01 *FAIL*') { $outcome = 'Phase9ResolveFailed' }
# Revolution IOS Phase 09 P09-H04: the System Menu's observation ends with the kernel's
# summary (no request for a while, or none at all); a load failure ends it too.
elseif ($ExpectedOutcome -eq 'Phase9Menu' -and ($message -like 'REV:P9:H04 QUIET *' -or $message -like 'REV:P9:H04 NOIPC *')) { $outcome = 'Phase9Menu' }
elseif ($ExpectedOutcome -eq 'Phase9Menu' -and $message -like 'REV:P9:H0[34] FAIL*') { $outcome = 'Phase9MenuFailed' }
# P09-H04 rescue: the kernel stopped the Menu and launches the rescue screen, whose
# status (0x52500005 once drawn) ends the run below.
elseif ($ExpectedOutcome -eq 'Phase9Rescue' -and $message -like 'REV:P9:H04 RESCUE GO *') { $rescueStarted = $true }
elseif ($ExpectedOutcome -eq 'Phase9Rescue' -and ($message -like 'REV:P9:H0[34] FAIL*' -or $message -like 'REV:P9:H04 RESCUE PLAN FAIL*')) { $outcome = 'Phase9RescueFailed' }
elseif ($message -like 'REV:FATAL*') { $outcome = 'Fatal' }
} elseif ($line -match 'Starlet bootstrap exception|Starlet bootstrap trace length is invalid') {
$outcome = 'Fault'
$faultLine = $line.TrimEnd("`r")
Write-Host $faultLine
} elseif ($line -match 'Broadway ios-probe status=(0x[0-9a-f]+)') {
$probeStatus = $Matches[1]
Write-Host "PPC: $probeStatus"
if ($probeStatus -eq '0x52500002') { $outcome = 'Phase3Raw' }
elseif ($probeStatus -eq '0x52500005' -and $ExpectedOutcome -eq 'Phase10AOh1') { $outcome = 'Phase10AOh1' }
elseif ($probeStatus -eq '0x52500003') { $outcome = 'Phase3Ios' }
# Phase 09 P09-H02: the payload's success is final only with the kernel's
# release and first-request lines (checked after each chunk below).
elseif ($probeStatus -eq '0x52500005' -and $ExpectedOutcome -eq 'Phase9Launch') { $payloadPassed = $true }
elseif ($ExpectedOutcome -eq 'Phase9Rescue') { if ($probeStatus -eq '0x52500005' -and $rescueStarted) { $outcome = 'Phase9Rescue' } }
elseif ($probeStatus -eq '0x52500005') { $outcome = $(if ($ExpectedOutcome -in @('Phase5Dir', 'Phase5Es', 'Phase5Client', 'Phase6Write', 'Phase6Verify', 'Phase8Client')) { $ExpectedOutcome } else { 'Phase4Fs' }) }
# Phase 09 P09-H02M: a later program's status (the launched monitor
# starting) voids an earlier program's pass and its checks.
elseif ($ExpectedOutcome -eq 'Phase9Launch' -and $probeStatus -notmatch '^0x5250e') { $payloadPassed = $false; $probeChecks = $null }
elseif ($probeStatus -match '^0x5250e') { $outcome = 'ProbeFailed' }
} elseif ($line -match 'Broadway ios-probe counters sent=(\d+) received=(\d+) invalid=(\d+) duplicate=(\d+) timeout=(\d+)') {
$probeCounters = @([int]$Matches[1], [int]$Matches[2], [int]$Matches[3],
[int]$Matches[4], [int]$Matches[5])
Write-Host "PPC counters: $($probeCounters -join '/')"
} elseif ($line -match 'Broadway ios-probe checks=(\d+) failures=(\d+)') {
$probeChecks = @([int]$Matches[1], [int]$Matches[2])
Write-Host "PPC checks: $($probeChecks -join '/')"
}
}
}
}
if (-not $outcome -and $ExpectedOutcome -eq 'Phase9Launch' -and $payloadPassed -and $probeChecks -and
@($trace | Where-Object { $_ -like 'REV:P9:H02 GO *' }).Count -eq 1 -and
@($trace | Where-Object { $_ -like 'REV:P9:H02 FIRST CMD *' }).Count -eq 1) {
$outcome = 'Phase9Launch'
}
if ($outcome) { break }
if ($dolphin.HasExited) { break }
Start-Sleep -Milliseconds 200
}
if ($outcome -ne $ExpectedOutcome) {
$exitDetail = if ($dolphin.HasExited) { "Dolphin exited with code $($dolphin.ExitCode). " } else { '' }
throw "$exitDetail Bootstrap outcome '$outcome' (expected '$ExpectedOutcome'); see $logPath"
}
if ($ExpectedOutcome -eq 'Fault') {
if ($faultLine -notmatch 'Starlet bootstrap exception 1: LR=0x[0-9a-f]+ SPSR=0x[0-9a-f]+ PC=0x[0-9a-f]+') {
throw "Expected an undefined-instruction exception with registers; see $logPath"
}
}
if ($ExpectedOutcome -eq 'Fatal') {
if (-not $trace.Contains('REV:BOOT:ENTRY') -or
-not $trace.Contains('REV:BOOT:BSS_OK') -or
-not $trace.Contains('REV:FATAL:DATA') -or
$trace.Contains('REV:BOOT:DATA_OK')) {
throw "Fatal data check did not produce the expected markers; see $logPath"
}
}
if ($ExpectedOutcome -eq 'Phase2') {
$requiredPhase2 = @('REV:P2:START', 'ARCH PASS', 'CRITICAL PASS', 'TIMER PASS',
'MONOTONIC PASS', 'MEM PASS', 'IRQ PASS', 'BARRIER PASS', 'LOGGING PASS',
'REV:P2:PASS', 'REV:P2:KERNEL_READY', 'REV:BOOT:IDLE')
$missingPhase2 = @($requiredPhase2 | Where-Object { -not $trace.Contains($_) })
if ($missingPhase2.Count -gt 0 -or @($trace | Where-Object { $_ -like '* FAIL' }).Count -gt 0) {
throw "Phase 02 self-test markers are incomplete; see $logPath"
}
}
if ($ExpectedOutcome -eq 'Phase3Raw' -and
(-not $trace.Contains('REV:P3A:START') -or
-not $trace.Contains('REV:P3A:ARM_128') -or
-not $trace.Contains('REV:P3A:ARM_PASS') -or
-not $probeCounters -or
$probeCounters[0] -ne 128 -or $probeCounters[1] -ne 128 -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0)) {
throw "Phase 03A Starlet markers are incomplete; see $logPath"
}
if ($ExpectedOutcome -eq 'Phase3Ios' -and
(-not $trace.Contains('REV:P3B:START') -or
-not $trace.Contains('REV:P3B:OPEN_OK') -or
-not $trace.Contains('REV:P3B:PING_OK') -or
-not $trace.Contains('REV:P3B:CLOSE_OK') -or
-not $probeCounters -or $probeCounters[0] -lt 200 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 260 -or
$probeChecks[1] -ne 0)) {
throw "Phase 03B IOS probe markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 04D: the shared /dev/fs cases (ppc/common/fs_cases.h).
if ($ExpectedOutcome -eq 'Phase4Fs' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeCounters -or $probeCounters[0] -lt 100 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 30 -or
$probeChecks[1] -ne 0)) {
throw "Phase 04D /dev/fs markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 05B: the READDIR/GETUSAGE cases (ppc/common/fs_dir_cases.h).
if ($ExpectedOutcome -eq 'Phase5Dir' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeCounters -or $probeCounters[0] -lt 600 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 13 -or
$probeChecks[1] -ne 0)) {
throw "Phase 05B READDIR/GETUSAGE markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 05C/05D: the /dev/es cases (ppc/common/es_cases.h).
if ($ExpectedOutcome -eq 'Phase5Es' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeCounters -or $probeCounters[0] -lt 600 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 18 -or
$probeChecks[1] -ne 0)) {
throw "Phase 05C /dev/es markers or counters are incomplete; see $logPath"
}
# P10A-H07 reduced smoke: real service on the emulator USB double; no
# local loopback and no physical acceptance. No synthetic IPC counters.
if ($ExpectedOutcome -eq 'Phase10AOh1' -and
(-not $trace.Contains('REV:P10A:OH1 READY REAL USB') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeChecks -or $probeChecks[0] -lt $Phase10AMinimumChecks -or $probeChecks[1] -ne 0)) {
throw 'Phase 10A OH1 reduced smoke markers or checks are incomplete'
}
# Revolution IOS Phase 05F: the unchanged libogc stock client (ppc/stock-client).
if ($ExpectedOutcome -eq 'Phase5Client' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeCounters -or $probeCounters[0] -lt 16 -or
$probeCounters[0] -ne $probeCounters[1] -or
-not $probeChecks -or $probeChecks[0] -lt 16 -or
$probeChecks[1] -ne 0)) {
throw "Phase 05F stock client markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 06H: the writable scenario (ppc/fs-write-probe, mode Write) on the
# emulator-only writable kernel over the disposable NAND (REV_BOOTSTRAP_NAND_DISPOSABLE).
if ($ExpectedOutcome -eq 'Phase6Write' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P6:FS MOUNT GEN * WRITABLE' }).Count -ne 1 -or
@($trace | Where-Object { $_ -like 'REV:P6:FS GEN * REMOUNT *' }).Count -ne 0 -or
-not $probeCounters -or $probeCounters[0] -lt 100 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 60 -or
$probeChecks[1] -ne 0)) {
throw "Phase 06H writable scenario markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 06H: the resulting image after a fresh boot of the read-only kernel
# (ppc/fs-write-probe, mode Verify).
if ($ExpectedOutcome -eq 'Phase6Verify' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P4:FS MOUNT GEN * AUTH YES *' }).Count -ne 1 -or
-not $probeCounters -or $probeCounters[0] -lt 30 -or
$probeCounters[0] -ne $probeCounters[1] -or
$probeCounters[2] -ne 0 -or $probeCounters[3] -ne 0 -or
$probeCounters[4] -ne 0 -or
-not $probeChecks -or $probeChecks[0] -lt 25 -or
$probeChecks[1] -ne 0)) {
throw "Phase 06H verification markers or counters are incomplete; see $logPath"
}
# Revolution IOS Phase 08 H07R (section 69): the libogc read-after-reboot client
# (ppc/p8-client/reboot.c), the same boot.dol as on the console, on the emulator-only
# writable kernel over the disposable NAND. It writes only its status word (0x52500005 when
# every check of its part passed); the part is proven by the NAND copy, outside this script.
if ($ExpectedOutcome -eq 'Phase8Client' -and
(-not $trace.Contains('REV:P3B:START') -or
@($trace | Where-Object { $_ -like 'REV:P6:FS MOUNT GEN * WRITABLE' }).Count -ne 1)) {
throw "Phase 08 client markers are incomplete; see $logPath"
}
# Revolution IOS Phase 09 P09-H02: Broadway stopped and restarted into the payload while the
# kernel stays resident; the payload's own checks passed.
if ($ExpectedOutcome -eq 'Phase9Launch' -and
(@($trace | Where-Object { $_ -like 'REV:P9:H02 GO *' }).Count -ne 1 -or
@($trace | Where-Object { $_ -like 'REV:P9:H02 FIRST CMD *' }).Count -ne 1 -or
-not $probeChecks -or $probeChecks[0] -lt $Phase9MinimumChecks -or $probeChecks[1] -ne 0)) {
throw "Phase 09 launch markers or checks are incomplete; see $logPath"
}
# Revolution IOS Phase 09 P09-H04: the Menu was resolved, profiled, loaded and released
# (M0-M4) before the observation ended.
if ($ExpectedOutcome -in @('Phase9Menu', 'Phase9Rescue')) {
$missingMenu = @('REV:P9:H04 M0 ', 'REV:P9:H04 M1 ', 'REV:P9:H04 M2 STOPPED LOWMEM VERIFY OK', 'REV:P9:H04 M3 ', 'REV:P9:H04 M4 ') |
Where-Object { $prefix = $_; @($trace | Where-Object { $_.StartsWith($prefix) }).Count -ne 1 }
if (@($missingMenu).Count -gt 0) {
throw "Phase 09 System Menu milestones are incomplete ($($missingMenu -join ', ')); see $logPath"
}
}
$required = @('REV:BOOT:ENTRY', 'REV:BOOT:BSS_OK', 'REV:BOOT:DATA_OK',
'REV:BOOT:STACK_OK', 'REV:BOOT:RUST', 'Revolution IOS',
'Starlet Rust bootstrap reached')
$missing = @($required | Where-Object { -not $trace.Contains($_) })
if ($ExpectedOutcome -in @('Idle', 'Phase2') -and $missing.Count -gt 0) {
throw "Bootstrap reached idle without all expected markers; see $logPath"
}
Write-Host "Starlet bootstrap $outcome passed ($($trace.Count) trace lines)."
} finally {
if (-not $dolphin.HasExited) {
Stop-Process -Id $dolphin.Id
$dolphin.WaitForExit()
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+439 -438
View File
@@ -1,442 +1,443 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <set>
#include <string>
#include <utility>
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Core/USBUtils.h"
#include "DiscIO/Enums.h"
// DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output")
#define BACKEND_ALSA "ALSA"
#define BACKEND_CUBEB "Cubeb"
#define BACKEND_OPENAL "OpenAL"
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_OPENSLES "OpenSLES"
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
namespace PowerPC
{
enum class CPUCore;
}
namespace AudioCommon
{
enum class DPL2Quality;
}
namespace ExpansionInterface
{
enum class EXIDeviceType : int;
enum class Slot : int;
} // namespace ExpansionInterface
namespace SerialInterface
{
enum SIDevices : int;
}
namespace HSP
{
enum class HSPDeviceType : int;
}
namespace Config
{
// Main.Core
extern const Info<bool> MAIN_SKIP_IPL;
extern const Info<PowerPC::CPUCore> MAIN_CPU_CORE;
extern const Info<bool> MAIN_JIT_FOLLOW_BRANCH;
extern const Info<bool> MAIN_FASTMEM;
extern const Info<bool> MAIN_PAGE_TABLE_FASTMEM;
extern const Info<bool> MAIN_FASTMEM_ARENA;
extern const Info<bool> MAIN_LARGE_ENTRY_POINTS_MAP;
extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
// Should really be in the DSP section, but we're kind of stuck with bad decisions made in the past.
extern const Info<bool> MAIN_DSP_HLE;
extern const Info<int> MAIN_MAX_FALLBACK;
extern const Info<int> MAIN_TIMING_VARIANCE;
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
extern const Info<bool> MAIN_RUSH_FRAME_PRESENTATION;
extern const Info<bool> MAIN_SMOOTH_EARLY_PRESENTATION;
extern const Info<bool> MAIN_CPU_THREAD;
extern const Info<bool> MAIN_LOAD_GAME_INTO_MEMORY;
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
extern const Info<std::string> MAIN_DEFAULT_ISO;
extern const Info<bool> MAIN_ENABLE_CHEATS;
extern const Info<int> MAIN_GC_LANGUAGE;
extern const Info<bool> MAIN_OVERRIDE_REGION_SETTINGS;
extern const Info<bool> MAIN_DPL2_DECODER;
extern const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY;
extern const Info<int> MAIN_AUDIO_LATENCY;
extern const Info<int> MAIN_AUDIO_BUFFER_SIZE;
extern const Info<bool> MAIN_AUDIO_FILL_GAPS;
extern const Info<bool> MAIN_AUDIO_PRESERVE_PITCH;
extern const Info<std::string> MAIN_MEMCARD_A_PATH;
extern const Info<std::string> MAIN_MEMCARD_B_PATH;
const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_AGP_CART_A_PATH;
extern const Info<std::string> MAIN_AGP_CART_B_PATH;
const Info<std::string>& GetInfoForAGPCartPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH;
const Info<std::string>& GetInfoForGCIPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
const Info<std::string>& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot);
extern const Info<int> MAIN_MEMORY_CARD_SIZE;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_A;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_B;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_1;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_2;
const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_BBA_MAC;
extern const Info<std::string> MAIN_BBA_XLINK_IP;
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
extern const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION;
extern const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION;
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
const Info<bool>& GetInfoForAdapterRumble(int channel);
const Info<bool>& GetInfoForSimulateKonga(int channel);
extern const Info<bool> MAIN_WII_SD_CARD;
extern const Info<bool> MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC;
extern const Info<u64> MAIN_WII_SD_CARD_FILESIZE;
extern const Info<bool> MAIN_WII_KEYBOARD;
extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
extern const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES;
extern const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER;
extern const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE;
extern const Info<bool> MAIN_MMU;
extern const Info<bool> MAIN_PAUSE_ON_PANIC;
extern const Info<int> MAIN_BB_DUMP_PORT;
extern const Info<bool> MAIN_SYNC_GPU;
extern const Info<int> MAIN_SYNC_GPU_MAX_DISTANCE;
extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_ACCURATE_FMADDS;
extern const Info<bool> MAIN_DISABLE_ICACHE;
extern const Info<float> MAIN_EMULATION_SPEED;
extern const Info<bool> MAIN_PRECISION_FRAME_TIMING;
extern const Info<float> MAIN_OVERCLOCK;
extern const Info<bool> MAIN_OVERCLOCK_ENABLE;
extern const Info<float> MAIN_VI_OVERCLOCK;
extern const Info<bool> MAIN_VI_OVERCLOCK_ENABLE;
extern const Info<bool> MAIN_RAM_OVERRIDE_ENABLE;
extern const Info<u32> MAIN_MEM1_SIZE;
extern const Info<u32> MAIN_MEM2_SIZE;
// Should really be part of System::GFX, but again, we're stuck with past mistakes.
extern const Info<std::string> MAIN_GFX_BACKEND;
extern const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE;
extern const Info<u32> MAIN_ARAM_EXPANSION_SIZE;
enum class GPUDeterminismMode
{
Auto,
Disabled,
// This is currently the only mode. There will probably be at least
// one more at some point.
FakeCompletion,
};
extern const Info<std::string> MAIN_GPU_DETERMINISM_MODE;
GPUDeterminismMode GetGPUDeterminismMode();
extern const Info<std::string> MAIN_PERF_MAP_DIR;
extern const Info<bool> MAIN_CUSTOM_RTC_ENABLE;
extern const Info<u32> MAIN_CUSTOM_RTC_VALUE;
extern const Info<bool> MAIN_AUTO_DISC_CHANGE;
extern const Info<bool> MAIN_ALLOW_SD_WRITES;
extern const Info<bool> MAIN_ENABLE_SAVESTATES;
extern const Info<DiscIO::Region> MAIN_FALLBACK_REGION;
extern const Info<bool> MAIN_REAL_WII_REMOTE_REPEAT_REPORTS;
extern const Info<s32> MAIN_OVERRIDE_BOOT_IOS;
extern const Info<bool> MAIN_WII_IOS_LLE;
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <set>
#include <string>
#include <utility>
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Core/USBUtils.h"
#include "DiscIO/Enums.h"
// DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output")
#define BACKEND_ALSA "ALSA"
#define BACKEND_CUBEB "Cubeb"
#define BACKEND_OPENAL "OpenAL"
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_OPENSLES "OpenSLES"
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
namespace PowerPC
{
enum class CPUCore;
}
namespace AudioCommon
{
enum class DPL2Quality;
}
namespace ExpansionInterface
{
enum class EXIDeviceType : int;
enum class Slot : int;
} // namespace ExpansionInterface
namespace SerialInterface
{
enum SIDevices : int;
}
namespace HSP
{
enum class HSPDeviceType : int;
}
namespace Config
{
// Main.Core
extern const Info<bool> MAIN_SKIP_IPL;
extern const Info<PowerPC::CPUCore> MAIN_CPU_CORE;
extern const Info<bool> MAIN_JIT_FOLLOW_BRANCH;
extern const Info<bool> MAIN_FASTMEM;
extern const Info<bool> MAIN_PAGE_TABLE_FASTMEM;
extern const Info<bool> MAIN_FASTMEM_ARENA;
extern const Info<bool> MAIN_LARGE_ENTRY_POINTS_MAP;
extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
// Should really be in the DSP section, but we're kind of stuck with bad decisions made in the past.
extern const Info<bool> MAIN_DSP_HLE;
extern const Info<int> MAIN_MAX_FALLBACK;
extern const Info<int> MAIN_TIMING_VARIANCE;
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
extern const Info<bool> MAIN_RUSH_FRAME_PRESENTATION;
extern const Info<bool> MAIN_SMOOTH_EARLY_PRESENTATION;
extern const Info<bool> MAIN_CPU_THREAD;
extern const Info<bool> MAIN_LOAD_GAME_INTO_MEMORY;
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
extern const Info<std::string> MAIN_DEFAULT_ISO;
extern const Info<bool> MAIN_ENABLE_CHEATS;
extern const Info<int> MAIN_GC_LANGUAGE;
extern const Info<bool> MAIN_OVERRIDE_REGION_SETTINGS;
extern const Info<bool> MAIN_DPL2_DECODER;
extern const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY;
extern const Info<int> MAIN_AUDIO_LATENCY;
extern const Info<int> MAIN_AUDIO_BUFFER_SIZE;
extern const Info<bool> MAIN_AUDIO_FILL_GAPS;
extern const Info<bool> MAIN_AUDIO_PRESERVE_PITCH;
extern const Info<std::string> MAIN_MEMCARD_A_PATH;
extern const Info<std::string> MAIN_MEMCARD_B_PATH;
const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_AGP_CART_A_PATH;
extern const Info<std::string> MAIN_AGP_CART_B_PATH;
const Info<std::string>& GetInfoForAGPCartPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH;
const Info<std::string>& GetInfoForGCIPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
const Info<std::string>& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot);
extern const Info<int> MAIN_MEMORY_CARD_SIZE;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_A;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_B;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_1;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_2;
const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_BBA_MAC;
extern const Info<std::string> MAIN_BBA_XLINK_IP;
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
extern const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION;
extern const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION;
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
const Info<bool>& GetInfoForAdapterRumble(int channel);
const Info<bool>& GetInfoForSimulateKonga(int channel);
extern const Info<bool> MAIN_WII_SD_CARD;
extern const Info<bool> MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC;
extern const Info<u64> MAIN_WII_SD_CARD_FILESIZE;
extern const Info<bool> MAIN_WII_KEYBOARD;
extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
extern const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES;
extern const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER;
extern const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE;
extern const Info<bool> MAIN_MMU;
extern const Info<bool> MAIN_PAUSE_ON_PANIC;
extern const Info<int> MAIN_BB_DUMP_PORT;
extern const Info<bool> MAIN_SYNC_GPU;
extern const Info<int> MAIN_SYNC_GPU_MAX_DISTANCE;
extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_ACCURATE_FMADDS;
extern const Info<bool> MAIN_DISABLE_ICACHE;
extern const Info<float> MAIN_EMULATION_SPEED;
extern const Info<bool> MAIN_PRECISION_FRAME_TIMING;
extern const Info<float> MAIN_OVERCLOCK;
extern const Info<bool> MAIN_OVERCLOCK_ENABLE;
extern const Info<float> MAIN_VI_OVERCLOCK;
extern const Info<bool> MAIN_VI_OVERCLOCK_ENABLE;
extern const Info<bool> MAIN_RAM_OVERRIDE_ENABLE;
extern const Info<u32> MAIN_MEM1_SIZE;
extern const Info<u32> MAIN_MEM2_SIZE;
// Should really be part of System::GFX, but again, we're stuck with past mistakes.
extern const Info<std::string> MAIN_GFX_BACKEND;
extern const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE;
extern const Info<u32> MAIN_ARAM_EXPANSION_SIZE;
enum class GPUDeterminismMode
{
Auto,
Disabled,
// This is currently the only mode. There will probably be at least
// one more at some point.
FakeCompletion,
};
extern const Info<std::string> MAIN_GPU_DETERMINISM_MODE;
GPUDeterminismMode GetGPUDeterminismMode();
extern const Info<std::string> MAIN_PERF_MAP_DIR;
extern const Info<bool> MAIN_CUSTOM_RTC_ENABLE;
extern const Info<u32> MAIN_CUSTOM_RTC_VALUE;
extern const Info<bool> MAIN_AUTO_DISC_CHANGE;
extern const Info<bool> MAIN_ALLOW_SD_WRITES;
extern const Info<bool> MAIN_ENABLE_SAVESTATES;
extern const Info<DiscIO::Region> MAIN_FALLBACK_REGION;
extern const Info<bool> MAIN_REAL_WII_REMOTE_REPEAT_REPORTS;
extern const Info<s32> MAIN_OVERRIDE_BOOT_IOS;
extern const Info<bool> MAIN_WII_IOS_LLE;
extern const Info<bool> MAIN_WII_STARLET_JIT;
extern const Info<bool> MAIN_WII_STARLET_JIT_MEMORY_PROFILING;
extern const Info<bool> MAIN_WII_LLE_ETHERNET;
extern const Info<std::string> MAIN_WII_NUS_SHOP_URL;
extern const Info<bool> MAIN_WII_WIILINK_ENABLE;
// Main.DSP
extern const Info<bool> MAIN_DSP_THREAD;
extern const Info<bool> MAIN_DSP_CAPTURE_LOG;
extern const Info<bool> MAIN_DSP_JIT;
extern const Info<bool> MAIN_DUMP_AUDIO;
extern const Info<bool> MAIN_DUMP_AUDIO_SILENT;
extern const Info<bool> MAIN_DUMP_UCODE;
extern const Info<std::string> MAIN_AUDIO_BACKEND;
extern const Info<int> MAIN_AUDIO_VOLUME;
extern const Info<bool> MAIN_AUDIO_MUTED;
extern const Info<bool> MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT;
#ifdef _WIN32
extern const Info<std::string> MAIN_WASAPI_DEVICE;
#endif
bool ShouldUseDPL2Decoder();
// Main.Display
extern const Info<std::string> MAIN_FULLSCREEN_DISPLAY_RES;
extern const Info<bool> MAIN_FULLSCREEN;
extern const Info<bool> MAIN_RENDER_TO_MAIN;
extern const Info<int> MAIN_RENDER_WINDOW_XPOS;
extern const Info<int> MAIN_RENDER_WINDOW_YPOS;
extern const Info<int> MAIN_RENDER_WINDOW_WIDTH;
extern const Info<int> MAIN_RENDER_WINDOW_HEIGHT;
extern const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE;
extern const Info<bool> MAIN_KEEP_WINDOW_ON_TOP;
extern const Info<bool> MAIN_DISABLE_SCREENSAVER;
extern const Info<bool> MAIN_TIME_TRACKING;
// Main.General
extern const Info<std::string> MAIN_DUMP_PATH;
extern const Info<std::string> MAIN_LOAD_PATH;
extern const Info<std::string> MAIN_RESOURCEPACK_PATH;
extern const Info<std::string> MAIN_FS_PATH;
extern const Info<std::string> MAIN_WII_NUS_SHOP_URL;
extern const Info<bool> MAIN_WII_WIILINK_ENABLE;
// Main.DSP
extern const Info<bool> MAIN_DSP_THREAD;
extern const Info<bool> MAIN_DSP_CAPTURE_LOG;
extern const Info<bool> MAIN_DSP_JIT;
extern const Info<bool> MAIN_DUMP_AUDIO;
extern const Info<bool> MAIN_DUMP_AUDIO_SILENT;
extern const Info<bool> MAIN_DUMP_UCODE;
extern const Info<std::string> MAIN_AUDIO_BACKEND;
extern const Info<int> MAIN_AUDIO_VOLUME;
extern const Info<bool> MAIN_AUDIO_MUTED;
extern const Info<bool> MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT;
#ifdef _WIN32
extern const Info<std::string> MAIN_WASAPI_DEVICE;
#endif
bool ShouldUseDPL2Decoder();
// Main.Display
extern const Info<std::string> MAIN_FULLSCREEN_DISPLAY_RES;
extern const Info<bool> MAIN_FULLSCREEN;
extern const Info<bool> MAIN_RENDER_TO_MAIN;
extern const Info<int> MAIN_RENDER_WINDOW_XPOS;
extern const Info<int> MAIN_RENDER_WINDOW_YPOS;
extern const Info<int> MAIN_RENDER_WINDOW_WIDTH;
extern const Info<int> MAIN_RENDER_WINDOW_HEIGHT;
extern const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE;
extern const Info<bool> MAIN_KEEP_WINDOW_ON_TOP;
extern const Info<bool> MAIN_DISABLE_SCREENSAVER;
extern const Info<bool> MAIN_TIME_TRACKING;
// Main.General
extern const Info<std::string> MAIN_DUMP_PATH;
extern const Info<std::string> MAIN_LOAD_PATH;
extern const Info<std::string> MAIN_RESOURCEPACK_PATH;
extern const Info<std::string> MAIN_FS_PATH;
extern const Info<std::string> MAIN_WII_LLE_DUMP_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_IMAGE_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_SYNC_FOLDER_PATH;
extern const Info<std::string> MAIN_WFS_PATH;
extern const Info<bool> MAIN_SHOW_LAG;
extern const Info<bool> MAIN_SHOW_FRAME_COUNT;
extern const Info<std::string> MAIN_WIRELESS_MAC;
extern const Info<std::string> MAIN_GDB_SOCKET;
extern const Info<int> MAIN_GDB_PORT;
extern const Info<int> MAIN_ISO_PATH_COUNT;
extern const Info<std::string> MAIN_SKYLANDERS_PATH;
std::vector<std::string> GetIsoPaths();
void SetIsoPaths(std::span<const std::string> paths);
// Main.GBA
#ifdef HAS_LIBMGBA
extern const Info<std::string> MAIN_GBA_BIOS_PATH;
extern const std::array<Info<std::string>, 5> MAIN_GBA_ROM_PATHS;
extern const Info<std::string> MAIN_GBA_SAVES_PATH;
extern const Info<bool> MAIN_GBA_SAVES_IN_ROM_PATH;
static constexpr std::size_t GBPLAYER_GBA_INDEX = 4;
#endif
// Main.Network
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_READ;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
extern const Info<bool> MAIN_NETWORK_DUMP_BBA;
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;
extern const Info<int> MAIN_NETWORK_TIMEOUT;
// Main.Interface
extern const Info<bool> MAIN_USE_HIGH_CONTRAST_TOOLTIPS;
extern const Info<bool> MAIN_USE_PANIC_HANDLERS;
extern const Info<bool> MAIN_ABORT_ON_PANIC_ALERT;
extern const Info<bool> MAIN_OSD_MESSAGES;
extern const Info<int> MAIN_OSD_FONT_SIZE;
extern const Info<bool> MAIN_SKIP_NKIT_WARNING;
extern const Info<bool> MAIN_CONFIRM_ON_STOP;
enum class ShowCursor
{
Never,
Constantly,
OnMovement,
};
extern const Info<ShowCursor> MAIN_SHOW_CURSOR;
extern const Info<bool> MAIN_LOCK_CURSOR;
extern const Info<std::string> MAIN_INTERFACE_LANGUAGE;
extern const Info<bool> MAIN_SHOW_ACTIVE_TITLE;
extern const Info<bool> MAIN_USE_BUILT_IN_TITLE_DATABASE;
extern const Info<std::string> MAIN_THEME_NAME;
extern const Info<bool> MAIN_PAUSE_ON_FOCUS_LOST;
extern const Info<bool> MAIN_ENABLE_DEBUGGING;
// Main.Analytics
extern const Info<std::string> MAIN_ANALYTICS_ID;
extern const Info<bool> MAIN_ANALYTICS_ENABLED;
extern const Info<bool> MAIN_ANALYTICS_PERMISSION_ASKED;
// Main.GameList
extern const Info<bool> MAIN_GAMELIST_LIST_DRIVES;
extern const Info<bool> MAIN_GAMELIST_LIST_WAD;
extern const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL;
extern const Info<bool> MAIN_GAMELIST_LIST_WII;
extern const Info<bool> MAIN_GAMELIST_LIST_GC;
extern const Info<bool> MAIN_GAMELIST_LIST_TRI;
extern const Info<bool> MAIN_GAMELIST_LIST_JPN;
extern const Info<bool> MAIN_GAMELIST_LIST_PAL;
extern const Info<bool> MAIN_GAMELIST_LIST_USA;
extern const Info<bool> MAIN_GAMELIST_LIST_AUSTRALIA;
extern const Info<bool> MAIN_GAMELIST_LIST_FRANCE;
extern const Info<bool> MAIN_GAMELIST_LIST_GERMANY;
extern const Info<bool> MAIN_GAMELIST_LIST_ITALY;
extern const Info<bool> MAIN_GAMELIST_LIST_KOREA;
extern const Info<bool> MAIN_GAMELIST_LIST_NETHERLANDS;
extern const Info<bool> MAIN_GAMELIST_LIST_RUSSIA;
extern const Info<bool> MAIN_GAMELIST_LIST_SPAIN;
extern const Info<bool> MAIN_GAMELIST_LIST_TAIWAN;
extern const Info<bool> MAIN_GAMELIST_LIST_WORLD;
extern const Info<bool> MAIN_GAMELIST_LIST_UNKNOWN;
extern const Info<int> MAIN_GAMELIST_LIST_SORT;
extern const Info<int> MAIN_GAMELIST_LIST_SORT_SECONDARY;
extern const Info<bool> MAIN_GAMELIST_COLUMN_PLATFORM;
extern const Info<bool> MAIN_GAMELIST_COLUMN_DESCRIPTION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BANNER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TITLE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_MAKER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_NAME;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_PATH;
extern const Info<bool> MAIN_GAMELIST_COLUMN_GAME_ID;
extern const Info<bool> MAIN_GAMELIST_COLUMN_REGION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;
// Main.FifoPlayer
extern const Info<bool> MAIN_FIFOPLAYER_LOOP_REPLAY;
extern const Info<bool> MAIN_FIFOPLAYER_EARLY_MEMORY_UPDATES;
// Main.AutoUpdate
extern const Info<std::string> MAIN_AUTOUPDATE_UPDATE_TRACK;
extern const Info<std::string> MAIN_AUTOUPDATE_HASH_OVERRIDE;
// Main.Movie
extern const Info<bool> MAIN_MOVIE_PAUSE_MOVIE;
extern const Info<std::string> MAIN_MOVIE_MOVIE_AUTHOR;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES_SILENT;
extern const Info<bool> MAIN_MOVIE_SHOW_INPUT_DISPLAY;
extern const Info<bool> MAIN_MOVIE_SHOW_RTC;
extern const Info<bool> MAIN_MOVIE_SHOW_RERECORD;
extern const Info<bool> MAIN_MOVIE_SHOW_OSD;
// Main.Input
extern const Info<bool> MAIN_INPUT_BACKGROUND_INPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS;
// Main.Debug
extern const Info<bool> MAIN_DEBUG_JIT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_FLOATING_POINT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_INTEGER_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING;
// Main.BluetoothPassthrough
extern const Info<bool> MAIN_BLUETOOTH_PASSTHROUGH_ENABLED;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_VID;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_PID;
extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS;
// Main.USBPassthrough
extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII;
extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES;
std::set<USBUtils::DeviceInfo> GetUSBDeviceWhitelist();
void SetUSBDeviceWhitelist(const std::set<USBUtils::DeviceInfo>& devices);
// Main.EmulatedUSBDevices
extern const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL;
extern const Info<bool> MAIN_EMULATE_INFINITY_BASE;
extern const Info<bool> MAIN_EMULATE_WII_SPEAK;
extern const Info<std::string> MAIN_WII_SPEAK_MICROPHONE;
extern const Info<bool> MAIN_WII_SPEAK_MUTED;
extern const Info<s16> MAIN_WII_SPEAK_VOLUME_MODIFIER;
static constexpr std::size_t EMULATED_LOGITECH_MIC_COUNT = 4;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_EMULATE_LOGITECH_MIC;
extern const std::array<Info<std::string>, EMULATED_LOGITECH_MIC_COUNT>
MAIN_LOGITECH_MIC_MICROPHONE;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_MUTED;
extern const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUME_MODIFIER;
extern const Info<std::string> MAIN_TRIFORCE_IP_REDIRECTIONS;
// Main.WiimoteAudioRouting
static constexpr std::size_t WIIMOTE_SPEAKER_COUNT = 4;
extern const Info<bool> MAIN_WIIMOTE_AUDIO_ROUTING_ENABLED;
extern const std::array<Info<bool>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_ENABLED;
extern const std::array<Info<std::string>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_DEVICE;
// GameCube path utility functions
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
DiscIO::Region ToGameCubeRegion(DiscIO::Region region);
// The region argument must be valid for GameCube (i.e. must not be NTSC-K)
enum class RegionDirectoryStyle
{
Legacy,
Modern,
};
const char* GetDirectoryForRegion(DiscIO::Region region,
RegionDirectoryStyle style = RegionDirectoryStyle::Legacy);
std::string GetBootROMPath(const std::string& region_directory);
// Builds the memory card according to the configuration with the given region and size. If the
// given region is std::nullopt, the region in the configured path is used if there is one, or the
// fallback region otherwise.
std::string GetMemcardPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region,
u16 size_mb = 0x80);
std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region, u16 size_mb = 0x80);
bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot);
std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region);
std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region);
bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot);
bool AreCheatsEnabled();
bool IsDebuggingEnabled();
} // namespace Config
extern const Info<std::string> MAIN_WII_STARLET_BOOTSTRAP_ELF;
extern const Info<std::string> MAIN_WII_SD_CARD_IMAGE_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_SYNC_FOLDER_PATH;
extern const Info<std::string> MAIN_WFS_PATH;
extern const Info<bool> MAIN_SHOW_LAG;
extern const Info<bool> MAIN_SHOW_FRAME_COUNT;
extern const Info<std::string> MAIN_WIRELESS_MAC;
extern const Info<std::string> MAIN_GDB_SOCKET;
extern const Info<int> MAIN_GDB_PORT;
extern const Info<int> MAIN_ISO_PATH_COUNT;
extern const Info<std::string> MAIN_SKYLANDERS_PATH;
std::vector<std::string> GetIsoPaths();
void SetIsoPaths(std::span<const std::string> paths);
// Main.GBA
#ifdef HAS_LIBMGBA
extern const Info<std::string> MAIN_GBA_BIOS_PATH;
extern const std::array<Info<std::string>, 5> MAIN_GBA_ROM_PATHS;
extern const Info<std::string> MAIN_GBA_SAVES_PATH;
extern const Info<bool> MAIN_GBA_SAVES_IN_ROM_PATH;
static constexpr std::size_t GBPLAYER_GBA_INDEX = 4;
#endif
// Main.Network
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_READ;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
extern const Info<bool> MAIN_NETWORK_DUMP_BBA;
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;
extern const Info<int> MAIN_NETWORK_TIMEOUT;
// Main.Interface
extern const Info<bool> MAIN_USE_HIGH_CONTRAST_TOOLTIPS;
extern const Info<bool> MAIN_USE_PANIC_HANDLERS;
extern const Info<bool> MAIN_ABORT_ON_PANIC_ALERT;
extern const Info<bool> MAIN_OSD_MESSAGES;
extern const Info<int> MAIN_OSD_FONT_SIZE;
extern const Info<bool> MAIN_SKIP_NKIT_WARNING;
extern const Info<bool> MAIN_CONFIRM_ON_STOP;
enum class ShowCursor
{
Never,
Constantly,
OnMovement,
};
extern const Info<ShowCursor> MAIN_SHOW_CURSOR;
extern const Info<bool> MAIN_LOCK_CURSOR;
extern const Info<std::string> MAIN_INTERFACE_LANGUAGE;
extern const Info<bool> MAIN_SHOW_ACTIVE_TITLE;
extern const Info<bool> MAIN_USE_BUILT_IN_TITLE_DATABASE;
extern const Info<std::string> MAIN_THEME_NAME;
extern const Info<bool> MAIN_PAUSE_ON_FOCUS_LOST;
extern const Info<bool> MAIN_ENABLE_DEBUGGING;
// Main.Analytics
extern const Info<std::string> MAIN_ANALYTICS_ID;
extern const Info<bool> MAIN_ANALYTICS_ENABLED;
extern const Info<bool> MAIN_ANALYTICS_PERMISSION_ASKED;
// Main.GameList
extern const Info<bool> MAIN_GAMELIST_LIST_DRIVES;
extern const Info<bool> MAIN_GAMELIST_LIST_WAD;
extern const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL;
extern const Info<bool> MAIN_GAMELIST_LIST_WII;
extern const Info<bool> MAIN_GAMELIST_LIST_GC;
extern const Info<bool> MAIN_GAMELIST_LIST_TRI;
extern const Info<bool> MAIN_GAMELIST_LIST_JPN;
extern const Info<bool> MAIN_GAMELIST_LIST_PAL;
extern const Info<bool> MAIN_GAMELIST_LIST_USA;
extern const Info<bool> MAIN_GAMELIST_LIST_AUSTRALIA;
extern const Info<bool> MAIN_GAMELIST_LIST_FRANCE;
extern const Info<bool> MAIN_GAMELIST_LIST_GERMANY;
extern const Info<bool> MAIN_GAMELIST_LIST_ITALY;
extern const Info<bool> MAIN_GAMELIST_LIST_KOREA;
extern const Info<bool> MAIN_GAMELIST_LIST_NETHERLANDS;
extern const Info<bool> MAIN_GAMELIST_LIST_RUSSIA;
extern const Info<bool> MAIN_GAMELIST_LIST_SPAIN;
extern const Info<bool> MAIN_GAMELIST_LIST_TAIWAN;
extern const Info<bool> MAIN_GAMELIST_LIST_WORLD;
extern const Info<bool> MAIN_GAMELIST_LIST_UNKNOWN;
extern const Info<int> MAIN_GAMELIST_LIST_SORT;
extern const Info<int> MAIN_GAMELIST_LIST_SORT_SECONDARY;
extern const Info<bool> MAIN_GAMELIST_COLUMN_PLATFORM;
extern const Info<bool> MAIN_GAMELIST_COLUMN_DESCRIPTION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BANNER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TITLE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_MAKER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_NAME;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_PATH;
extern const Info<bool> MAIN_GAMELIST_COLUMN_GAME_ID;
extern const Info<bool> MAIN_GAMELIST_COLUMN_REGION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;
// Main.FifoPlayer
extern const Info<bool> MAIN_FIFOPLAYER_LOOP_REPLAY;
extern const Info<bool> MAIN_FIFOPLAYER_EARLY_MEMORY_UPDATES;
// Main.AutoUpdate
extern const Info<std::string> MAIN_AUTOUPDATE_UPDATE_TRACK;
extern const Info<std::string> MAIN_AUTOUPDATE_HASH_OVERRIDE;
// Main.Movie
extern const Info<bool> MAIN_MOVIE_PAUSE_MOVIE;
extern const Info<std::string> MAIN_MOVIE_MOVIE_AUTHOR;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES_SILENT;
extern const Info<bool> MAIN_MOVIE_SHOW_INPUT_DISPLAY;
extern const Info<bool> MAIN_MOVIE_SHOW_RTC;
extern const Info<bool> MAIN_MOVIE_SHOW_RERECORD;
extern const Info<bool> MAIN_MOVIE_SHOW_OSD;
// Main.Input
extern const Info<bool> MAIN_INPUT_BACKGROUND_INPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS;
// Main.Debug
extern const Info<bool> MAIN_DEBUG_JIT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_FLOATING_POINT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_INTEGER_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING;
// Main.BluetoothPassthrough
extern const Info<bool> MAIN_BLUETOOTH_PASSTHROUGH_ENABLED;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_VID;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_PID;
extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS;
// Main.USBPassthrough
extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII;
extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES;
std::set<USBUtils::DeviceInfo> GetUSBDeviceWhitelist();
void SetUSBDeviceWhitelist(const std::set<USBUtils::DeviceInfo>& devices);
// Main.EmulatedUSBDevices
extern const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL;
extern const Info<bool> MAIN_EMULATE_INFINITY_BASE;
extern const Info<bool> MAIN_EMULATE_WII_SPEAK;
extern const Info<std::string> MAIN_WII_SPEAK_MICROPHONE;
extern const Info<bool> MAIN_WII_SPEAK_MUTED;
extern const Info<s16> MAIN_WII_SPEAK_VOLUME_MODIFIER;
static constexpr std::size_t EMULATED_LOGITECH_MIC_COUNT = 4;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_EMULATE_LOGITECH_MIC;
extern const std::array<Info<std::string>, EMULATED_LOGITECH_MIC_COUNT>
MAIN_LOGITECH_MIC_MICROPHONE;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_MUTED;
extern const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUME_MODIFIER;
extern const Info<std::string> MAIN_TRIFORCE_IP_REDIRECTIONS;
// Main.WiimoteAudioRouting
static constexpr std::size_t WIIMOTE_SPEAKER_COUNT = 4;
extern const Info<bool> MAIN_WIIMOTE_AUDIO_ROUTING_ENABLED;
extern const std::array<Info<bool>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_ENABLED;
extern const std::array<Info<std::string>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_DEVICE;
// GameCube path utility functions
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
DiscIO::Region ToGameCubeRegion(DiscIO::Region region);
// The region argument must be valid for GameCube (i.e. must not be NTSC-K)
enum class RegionDirectoryStyle
{
Legacy,
Modern,
};
const char* GetDirectoryForRegion(DiscIO::Region region,
RegionDirectoryStyle style = RegionDirectoryStyle::Legacy);
std::string GetBootROMPath(const std::string& region_directory);
// Builds the memory card according to the configuration with the given region and size. If the
// given region is std::nullopt, the region in the configured path is used if there is one, or the
// fallback region otherwise.
std::string GetMemcardPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region,
u16 size_mb = 0x80);
std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region, u16 size_mb = 0x80);
bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot);
std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region);
std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region);
bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot);
bool AreCheatsEnabled();
bool IsDebuggingEnabled();
} // namespace Config
File diff suppressed because it is too large. Load diff
+159 -159
View File
@@ -1,159 +1,159 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/HW.h"
#include <memory>
#include <string>
#include "Common/ChunkFile.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GPFifo.h"
#include "Core/HW/HSP/HSP.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/MemoryInterface.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/Starlet/Starlet.h"
#include "Core/State.h"
#include "Core/System.h"
namespace HW
{
void Init(Core::System& system, const Sram* override_sram)
{
system.GetCoreTiming().Init();
system.GetSystemTimers().PreInit();
State::Init(system);
// Init the whole Hardware
system.GetAudioInterface().Init();
system.GetVideoInterface().Init();
system.GetSerialInterface().Init();
system.GetProcessorInterface().Init();
system.GetExpansionInterface().Init(override_sram); // Needs to be initialized before Memory
system.GetHSP().Init();
system.GetMemory().Init(); // Needs to be initialized before AddressSpace
AddressSpace::Init();
system.GetMemoryInterface().Init();
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
system.GetDVDInterface().Init();
system.GetGPFifo().Init();
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
system.GetSystemTimers().Init();
if (system.IsWii())
{
system.GetWiiIPC().Init();
if (Config::Get(Config::MAIN_WII_IOS_LLE))
{
auto starlet = std::make_unique<IOS::LLE::Starlet>(system);
std::string error;
if (starlet->Init(Config::Get(Config::MAIN_WII_LLE_DUMP_PATH), &error))
{
system.SetStarlet(std::move(starlet));
system.GetWiiIPC().HoldBroadwayForStarletBoot();
}
else
{
PanicAlertFmtT("Could not start Wii IOS LLE: {0}", error);
Core::SetState(system, Core::State::Stopping);
return;
}
}
else
{
IOS::HLE::Init(system); // Depends on Memory
}
}
system.GetMemory().InitMMIO(system);
}
void Shutdown(Core::System& system)
{
// IOS should always be shut down regardless of IsWii because it can be running in GC mode (MIOS).
if (system.GetStarlet())
{
system.GetStarlet()->Shutdown();
system.SetStarlet(nullptr);
}
if (system.GetIOS())
IOS::HLE::Shutdown(system); // Depends on Memory
system.GetWiiIPC().Shutdown();
system.GetSystemTimers().Shutdown();
system.GetCPU().Shutdown();
system.GetDVDInterface().Shutdown();
system.GetDSP().Shutdown();
system.GetMemoryInterface().Shutdown();
AddressSpace::Shutdown();
system.GetMemory().Shutdown();
system.GetHSP().Shutdown();
system.GetExpansionInterface().Shutdown();
system.GetSerialInterface().Shutdown();
system.GetAudioInterface().Shutdown();
State::Shutdown();
system.GetCoreTiming().Shutdown();
}
void DoState(Core::System& system, PointerWrap& p)
{
system.GetMemory().DoState(p);
p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface");
system.GetVideoInterface().DoState(p);
p.DoMarker("VideoInterface");
system.GetSerialInterface().DoState(p);
p.DoMarker("SerialInterface");
system.GetProcessorInterface().DoState(p);
p.DoMarker("ProcessorInterface");
system.GetDSP().DoState(p);
p.DoMarker("DSP");
system.GetDVDInterface().DoState(p);
p.DoMarker("DVDInterface");
system.GetGPFifo().DoState(p);
p.DoMarker("GPFifo");
system.GetExpansionInterface().DoState(p);
p.DoMarker("ExpansionInterface");
system.GetAudioInterface().DoState(p);
p.DoMarker("AudioInterface");
system.GetHSP().DoState(p);
p.DoMarker("HSP");
if (system.IsWii())
{
system.GetWiiIPC().DoState(p);
p.DoMarker("IOS");
if (system.GetStarlet())
{
system.GetStarlet()->DoState(p);
p.DoMarker("IOS::LLE");
}
else if (system.GetIOS())
{
system.GetIOS()->DoState(p);
p.DoMarker("IOS::HLE");
}
}
p.DoMarker("WIIHW");
}
} // namespace HW
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/HW.h"
#include <memory>
#include <string>
#include "Common/ChunkFile.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GPFifo.h"
#include "Core/HW/HSP/HSP.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/MemoryInterface.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/Starlet/Starlet.h"
#include "Core/State.h"
#include "Core/System.h"
namespace HW
{
void Init(Core::System& system, const Sram* override_sram)
{
system.GetCoreTiming().Init();
system.GetSystemTimers().PreInit();
State::Init(system);
// Init the whole Hardware
system.GetAudioInterface().Init();
system.GetVideoInterface().Init();
system.GetSerialInterface().Init();
system.GetProcessorInterface().Init();
system.GetExpansionInterface().Init(override_sram); // Needs to be initialized before Memory
system.GetHSP().Init();
system.GetMemory().Init(); // Needs to be initialized before AddressSpace
AddressSpace::Init();
system.GetMemoryInterface().Init();
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
system.GetDVDInterface().Init();
system.GetGPFifo().Init();
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
system.GetSystemTimers().Init();
if (system.IsWii())
{
system.GetWiiIPC().Init();
if (Config::Get(Config::MAIN_WII_IOS_LLE))
{
auto starlet = IOS::LLE::Starlet::Create(system);
std::string error;
if (starlet->Init(Config::Get(Config::MAIN_WII_LLE_DUMP_PATH), &error))
{
system.SetStarlet(std::move(starlet));
system.GetWiiIPC().HoldBroadwayForStarletBoot();
}
else
{
PanicAlertFmtT("Could not start Wii IOS LLE: {0}", error);
Core::SetState(system, Core::State::Stopping);
return;
}
}
else
{
IOS::HLE::Init(system); // Depends on Memory
}
}
system.GetMemory().InitMMIO(system);
}
void Shutdown(Core::System& system)
{
// IOS should always be shut down regardless of IsWii because it can be running in GC mode (MIOS).
if (system.GetStarlet())
{
system.GetStarlet()->Shutdown();
system.SetStarlet(nullptr);
}
if (system.GetIOS())
IOS::HLE::Shutdown(system); // Depends on Memory
system.GetWiiIPC().Shutdown();
system.GetSystemTimers().Shutdown();
system.GetCPU().Shutdown();
system.GetDVDInterface().Shutdown();
system.GetDSP().Shutdown();
system.GetMemoryInterface().Shutdown();
AddressSpace::Shutdown();
system.GetMemory().Shutdown();
system.GetHSP().Shutdown();
system.GetExpansionInterface().Shutdown();
system.GetSerialInterface().Shutdown();
system.GetAudioInterface().Shutdown();
State::Shutdown();
system.GetCoreTiming().Shutdown();
}
void DoState(Core::System& system, PointerWrap& p)
{
system.GetMemory().DoState(p);
p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface");
system.GetVideoInterface().DoState(p);
p.DoMarker("VideoInterface");
system.GetSerialInterface().DoState(p);
p.DoMarker("SerialInterface");
system.GetProcessorInterface().DoState(p);
p.DoMarker("ProcessorInterface");
system.GetDSP().DoState(p);
p.DoMarker("DSP");
system.GetDVDInterface().DoState(p);
p.DoMarker("DVDInterface");
system.GetGPFifo().DoState(p);
p.DoMarker("GPFifo");
system.GetExpansionInterface().DoState(p);
p.DoMarker("ExpansionInterface");
system.GetAudioInterface().DoState(p);
p.DoMarker("AudioInterface");
system.GetHSP().DoState(p);
p.DoMarker("HSP");
if (system.IsWii())
{
system.GetWiiIPC().DoState(p);
p.DoMarker("IOS");
if (system.GetStarlet())
{
system.GetStarlet()->DoState(p);
p.DoMarker("IOS::LLE");
}
else if (system.GetIOS())
{
system.GetIOS()->DoState(p);
p.DoMarker("IOS::HLE");
}
}
p.DoMarker("WIIHW");
}
} // namespace HW
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,222 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/IOS/Starlet/BootstrapELF.h"
#include <algorithm>
#include <array>
#include <optional>
#include <string_view>
#include "Core/IOS/Starlet/ARMCore.h"
namespace IOS::LLE
{
namespace
{
constexpr u32 TEXT_BASE = 0xffff0000;
constexpr u64 TEXT_END = 0x1'0000'0000ULL;
constexpr u32 DATA_BASE = 0xfffe0000;
constexpr u32 DATA_END = 0xfffe4000;
// Revolution IOS Phase 06H: MEM2, for the optional disposable-image text segment.
constexpr u32 MEM2_BASE = 0x10000000;
constexpr u64 MEM2_END = 0x14000000ULL;
constexpr u32 TRACE_SIZE = 1032;
constexpr u32 ELF_HEADER_SIZE = 52;
constexpr u32 PROGRAM_HEADER_SIZE = 32;
constexpr u32 SECTION_HEADER_SIZE = 40;
constexpr u32 SYMBOL_SIZE = 16;
struct Segment
{
u32 offset = 0;
u32 address = 0;
u32 file_size = 0;
u32 memory_size = 0;
};
bool Fits(std::span<const u8> image, u64 offset, u64 size)
{
return offset <= image.size() && size <= image.size() - offset;
}
u16 Read16(std::span<const u8> image, size_t offset)
{
return static_cast<u16>((static_cast<u16>(image[offset]) << 8) | image[offset + 1]);
}
u32 Read32(std::span<const u8> image, size_t offset)
{
return (static_cast<u32>(image[offset]) << 24) | (static_cast<u32>(image[offset + 1]) << 16) |
(static_cast<u32>(image[offset + 2]) << 8) | image[offset + 3];
}
bool Fail(std::string* error, std::string_view reason)
{
if (error)
*error = reason;
return false;
}
std::optional<std::string_view> SymbolName(std::span<const u8> image, u32 strings_offset,
u32 strings_size, u32 name_offset)
{
if (name_offset >= strings_size)
return std::nullopt;
const size_t start = static_cast<size_t>(strings_offset) + name_offset;
const auto first = image.begin() + start;
const auto last = image.begin() + strings_offset + strings_size;
const auto end = std::find(first, last, 0);
if (end == last)
return std::nullopt;
return std::string_view(reinterpret_cast<const char*>(&*first), static_cast<size_t>(end - first));
}
} // namespace
bool LoadBootstrapELF(std::span<const u8> image, ARMBus& bus, BootstrapELFInfo* info,
std::string* error, bool allow_mem2_text)
{
if (!info)
return Fail(error, "missing bootstrap ELF result");
if (!Fits(image, 0, ELF_HEADER_SIZE) || image[0] != 0x7f || image[1] != 'E' || image[2] != 'L' ||
image[3] != 'F' || image[4] != 1 || image[5] != 2 || image[6] != 1 ||
Read16(image, 16) != 2 || Read16(image, 18) != 40 || Read32(image, 20) != 1 ||
Read16(image, 40) != ELF_HEADER_SIZE)
{
return Fail(error, "expected ELF32 big-endian ARM executable");
}
const u32 entry = Read32(image, 24);
const u32 program_offset = Read32(image, 28);
const u32 section_offset = Read32(image, 32);
const u16 program_size = Read16(image, 42);
const u16 program_count = Read16(image, 44);
const u16 section_size = Read16(image, 46);
const u16 section_count = Read16(image, 48);
const bool program_count_valid = program_count == 2 || (allow_mem2_text && program_count == 3);
if (entry != TEXT_BASE || program_size != PROGRAM_HEADER_SIZE || !program_count_valid ||
section_size != SECTION_HEADER_SIZE || section_count == 0 ||
!Fits(image, program_offset, static_cast<u64>(program_size) * program_count) ||
!Fits(image, section_offset, static_cast<u64>(section_size) * section_count))
{
return Fail(error, "bootstrap ELF has an unsupported entry or header table");
}
std::array<Segment, 3> storage;
const std::span<Segment> segments(storage.data(), program_count);
u32 text_count = 0;
u32 data_count = 0;
u32 mem2_count = 0;
for (u32 index = 0; index < segments.size(); ++index)
{
const size_t offset = program_offset + index * PROGRAM_HEADER_SIZE;
const u32 file_offset = Read32(image, offset + 4);
const u32 address = Read32(image, offset + 8);
const u32 physical_address = Read32(image, offset + 12);
const u32 file_size = Read32(image, offset + 16);
const u32 memory_size = Read32(image, offset + 20);
const u32 flags = Read32(image, offset + 24);
const bool text =
address == TEXT_BASE && flags == 5 && static_cast<u64>(address) + memory_size <= TEXT_END;
const bool data =
address == DATA_BASE && flags == 6 && static_cast<u64>(address) + memory_size <= DATA_END;
const bool mem2 = allow_mem2_text && flags == 5 && address >= MEM2_BASE &&
static_cast<u64>(address) + memory_size <= MEM2_END;
if (Read32(image, offset) != 1 || address != physical_address || file_size == 0 ||
memory_size < file_size || !Fits(image, file_offset, file_size) ||
(!text && !data && !mem2))
{
return Fail(error, "bootstrap ELF has an invalid SRAM load segment");
}
text_count += text;
data_count += data;
mem2_count += mem2;
segments[index] = {file_offset, address, file_size, memory_size};
}
if (text_count != 1 || data_count != 1 || mem2_count != program_count - 2u)
return Fail(error, "bootstrap ELF must have one text and one data segment");
BootstrapELFInfo parsed{entry};
bool found_trace = false;
bool found_fault_code = false;
bool found_fault_lr = false;
bool found_fault_spsr = false;
for (u32 index = 0; index < section_count; ++index)
{
const size_t offset = section_offset + index * SECTION_HEADER_SIZE;
if (Read32(image, offset + 4) != 2)
continue;
const u32 symbols_offset = Read32(image, offset + 16);
const u32 symbols_size = Read32(image, offset + 20);
const u32 strings_index = Read32(image, offset + 24);
const u32 entry_size = Read32(image, offset + 36);
if (entry_size != SYMBOL_SIZE || symbols_size % SYMBOL_SIZE != 0 ||
!Fits(image, symbols_offset, symbols_size) || strings_index >= section_count)
{
return Fail(error, "bootstrap ELF has an invalid symbol table");
}
const size_t strings_header = section_offset + strings_index * SECTION_HEADER_SIZE;
const u32 strings_offset = Read32(image, strings_header + 16);
const u32 strings_size = Read32(image, strings_header + 20);
if (Read32(image, strings_header + 4) != 3 || !Fits(image, strings_offset, strings_size))
{
return Fail(error, "bootstrap ELF has an invalid symbol string table");
}
for (u32 symbol = 0; symbol < symbols_size / SYMBOL_SIZE; ++symbol)
{
const size_t symbol_offset = symbols_offset + symbol * SYMBOL_SIZE;
const auto name =
SymbolName(image, strings_offset, strings_size, Read32(image, symbol_offset));
if (!name)
return Fail(error, "bootstrap ELF has an invalid symbol name");
const u32 address = Read32(image, symbol_offset + 4);
if (*name == "REV_TRACE")
{
if (Read32(image, symbol_offset + 8) != TRACE_SIZE)
return Fail(error, "bootstrap ELF trace layout does not match the monitor");
parsed.trace = address;
found_trace = true;
}
else if (*name == "__fault_code")
{
parsed.fault_code = address;
found_fault_code = true;
}
else if (*name == "__fault_lr")
{
parsed.fault_lr = address;
found_fault_lr = true;
}
else if (*name == "__fault_spsr")
{
parsed.fault_spsr = address;
found_fault_spsr = true;
}
}
}
const auto data_segment =
std::find_if(segments.begin(), segments.end(),
[](const Segment& segment) { return segment.address == DATA_BASE; });
const auto in_data = [data_segment](u32 address, u32 size) {
return address >= data_segment->address &&
static_cast<u64>(address) + size <=
static_cast<u64>(data_segment->address) + data_segment->memory_size;
};
if (!found_trace || !found_fault_code || !found_fault_lr || !found_fault_spsr ||
!in_data(parsed.trace, TRACE_SIZE) || !in_data(parsed.fault_code, 4) ||
!in_data(parsed.fault_lr, 4) || !in_data(parsed.fault_spsr, 4))
{
return Fail(error, "bootstrap ELF is missing the trace or fault symbols");
}
for (const Segment& segment : segments)
{
for (u32 index = 0; index < segment.file_size; ++index)
bus.Write8(segment.address + index, image[segment.offset + index]);
for (u32 index = segment.file_size; index < segment.memory_size; ++index)
bus.Write8(segment.address + index, 0);
}
*info = parsed;
return true;
}
} // namespace IOS::LLE
@@ -0,0 +1,31 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <string>
#include "Common/CommonTypes.h"
namespace IOS::LLE
{
class ARMBus;
struct BootstrapELFInfo
{
u32 entry = 0;
u32 trace = 0;
u32 fault_code = 0;
u32 fault_lr = 0;
u32 fault_spsr = 0;
};
// Load the Phase 01 ELF into a bus already configured with the high SRAM mapping.
// All file and address validation completes before the first bus write.
// Revolution IOS Phase 06H: `allow_mem2_text` (set only with the emulator-only disposable
// writable NAND) additionally accepts one read/execute segment wholly inside MEM2. The SRAM
// text and data segments stay mandatory; without the flag the rules are unchanged.
bool LoadBootstrapELF(std::span<const u8> image, ARMBus& bus, BootstrapELFInfo* info,
std::string* error, bool allow_mem2_text = false);
} // namespace IOS::LLE
+188 -2
View File
@@ -4,18 +4,24 @@
#include "Core/IOS/Starlet/Starlet.h"
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <string_view>
#include <vector>
#include "Common/ChunkFile.h"
#include "Common/GekkoDisassembler.h"
#include "Common/IOFile.h"
#include "Common/Logging/Log.h"
#include "Common/PerformanceTrace.h"
#include "Common/Timer.h"
#include "Core/Config/MainSettings.h"
#include "Core/CoreTiming.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/Starlet/ARMCore.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
#include "Core/IOS/Starlet/StarletMemory.h"
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h"
@@ -23,6 +29,11 @@
namespace IOS::LLE
{
std::unique_ptr<Starlet> Starlet::Create(Core::System& system)
{
return std::make_unique<Starlet>(system);
}
Starlet::Starlet(Core::System& system) : m_system(system)
{
}
@@ -37,11 +48,87 @@ bool Starlet::Init(const std::string& dump_directory, std::string* error)
// Allocate the concrete bus here; its device-state members are part of the
// allocation size and must stay in lockstep with StarletMemory's definition.
m_memory = std::make_unique<StarletMemory>(m_system);
if (!m_memory->Init(dump_directory, error))
const std::string bootstrap_path = Config::Get(Config::MAIN_WII_STARLET_BOOTSTRAP_ELF);
if (bootstrap_path.empty() && !m_memory->Init(dump_directory, error))
{
m_memory.reset();
return false;
}
if (!bootstrap_path.empty())
{
File::IOFile file(bootstrap_path, "rb");
const u64 size = file ? file.GetSize() : 0;
if (size == 0 || size > 16 * 1024 * 1024)
{
if (error)
*error = "WiiStarletBootstrapELF must name a readable ELF of at most 16 MiB";
m_memory.reset();
return false;
}
std::vector<u8> image(static_cast<size_t>(size));
if (!file.ReadBytes(image.data(), image.size()))
{
if (error)
*error = "could not read WiiStarletBootstrapELF";
m_memory.reset();
return false;
}
m_memory->PrepareBootstrap();
// Revolution IOS Phase 06H, emulator only: a disposable writable NAND, and with it the
// MEM2 text segment of the disposable-image kernel. Nothing else accepts either.
const char* disposable_source = std::getenv("REV_BOOTSTRAP_NAND_DISPOSABLE");
const bool disposable = disposable_source && *disposable_source;
BootstrapELFInfo info;
if (!LoadBootstrapELF(image, *m_memory, &info, error, disposable))
{
m_memory.reset();
return false;
}
m_bootstrap = info;
INFO_LOG_FMT(IOS, "Starlet bootstrap ELF loaded: {} entry={:#010x} trace={:#010x}",
bootstrap_path, info.entry, info.trace);
if (disposable)
{
const char* read_only_path = std::getenv("REV_BOOTSTRAP_NAND");
const char* copy_path = std::getenv("REV_BOOTSTRAP_NAND_COPY");
if ((read_only_path && *read_only_path) || !copy_path || !*copy_path)
{
if (error)
*error = "REV_BOOTSTRAP_NAND_DISPOSABLE needs REV_BOOTSTRAP_NAND_COPY and excludes "
"REV_BOOTSTRAP_NAND";
m_memory.reset();
return false;
}
if (!m_memory->AttachDisposableNAND(disposable_source, copy_path, error))
{
m_memory.reset();
return false;
}
WARN_LOG_FMT(IOS, "Starlet bootstrap NAND DISPOSABLE: source {} (read-only), copy {}",
disposable_source, copy_path);
}
// Revolution IOS Phase 04B: optionally expose a raw NAND image, strictly read-only, so a
// bootstrap ELF can validate its NAND driver. Program and erase always fail in this mode.
if (const char* nand_path = std::getenv("REV_BOOTSTRAP_NAND"); nand_path && *nand_path)
{
if (!m_memory->AttachReadOnlyNAND(nand_path, error))
{
m_memory.reset();
return false;
}
INFO_LOG_FMT(IOS, "Starlet bootstrap NAND attached read-only: {}", nand_path);
}
// Revolution IOS Phase 04C: optional keys.bin so OTP reads match the attached dump.
if (const char* keys_path = std::getenv("REV_BOOTSTRAP_KEYS"); keys_path && *keys_path)
{
if (!m_memory->AttachKeys(keys_path, error))
{
m_memory.reset();
return false;
}
INFO_LOG_FMT(IOS, "Starlet bootstrap keys attached");
}
}
// ARMCore owns generation-tagged software TLB and instruction caches, so
// construct it only after the complete physical bus exists.
m_core = std::make_unique<ARMCore>(*m_memory,
@@ -51,7 +138,9 @@ bool Starlet::Init(const std::string& dump_directory, std::string* error)
#else
m_core->SetJitEnabled(false);
#endif
m_core->Reset(StarletMemory::BOOT_ROM_BASE);
m_core->Reset(m_bootstrap ? m_bootstrap->entry : StarletMemory::BOOT_ROM_BASE);
if (m_bootstrap)
m_core->SetBigEndian(true);
m_next_jit_diagnostic_instruction = 25'000'000;
m_next_realtime_diagnostic_ms = Common::Timer::NowMs() + 1000;
m_realtime_diagnostic_slice_count = 0;
@@ -101,6 +190,7 @@ void Starlet::Shutdown()
m_run_event = nullptr;
m_core.reset();
m_memory.reset();
m_bootstrap.reset();
}
void Starlet::DoState(PointerWrap& p)
@@ -110,6 +200,14 @@ void Starlet::DoState(PointerWrap& p)
return;
m_memory->DoState(p);
m_core->DoState(p);
if (p.IsReadMode())
{
m_bootstrap_trace_length = 0;
m_last_probe_status = 0;
m_bootstrap_line_length = 0;
m_bootstrap_outcome_logged = false;
m_bootstrap_fault_logged = false;
}
}
u32 Starlet::GetPC() const
@@ -201,6 +299,41 @@ void Starlet::RunSlice(s64 cycles_late)
ARM_IDLE_SLICE_CYCLES :
(ipc_handshake_active ? ARM_IPC_SLICE_CYCLES : ARM_ACTIVE_SLICE_CYCLES);
m_core->RunCycles(arm_cycles);
if (m_bootstrap && !m_bootstrap_outcome_logged)
TraceBootstrap();
// Observe the PPC test program's result word. This is diagnostic only: all
// request and response traffic still passes through shared RAM and IPC MMIO.
if (m_bootstrap)
{
constexpr u32 PPC_PROBE_STATUS_ADDRESS = 0x00003c00;
const u32 status = m_system.GetMemory().Read_U32(PPC_PROBE_STATUS_ADDRESS);
if ((status & 0xffff0000) == 0x52500000 && status != m_last_probe_status)
{
m_last_probe_status = status;
INFO_LOG_FMT(IOS, "Broadway ios-probe status={:#010x}", status);
if (status == 0x52500002 || status == 0x52500003 || status == 0x52500005 ||
(status & 0xfffff000) == 0x5250e000)
{
constexpr u32 PPC_PROBE_COUNTERS_ADDRESS = 0x00003c20;
INFO_LOG_FMT(IOS,
"Broadway ios-probe counters sent={} received={} invalid={} duplicate={} "
"timeout={}",
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 4),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 8),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 12),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 16));
// 0x52500005 / 0x5250e2xx: Revolution IOS Phase 04D /dev/fs cases.
if (status == 0x52500003 || status == 0x52500005 ||
(status & 0xffffff00) == 0x5250e100 || (status & 0xffffff00) == 0x5250e200)
{
INFO_LOG_FMT(IOS, "Broadway ios-probe checks={} failures={}",
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 20),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 24));
}
}
}
}
// Keep host-clock reads and diagnostic formatting off the normal scheduler path.
const bool check_realtime_diagnostic =
@@ -415,4 +548,57 @@ void Starlet::RunSlice(s64 cycles_late)
const s64 next = std::max<s64>(1, static_cast<s64>(broadway_cycles) - cycles_late);
m_system.GetCoreTiming().ScheduleEvent(next, m_run_event);
}
void Starlet::TraceBootstrap()
{
const BootstrapELFInfo& bootstrap = *m_bootstrap;
const u32 fault_code = m_memory->Read32(bootstrap.fault_code);
if (fault_code != 0 && !m_bootstrap_fault_logged)
{
ERROR_LOG_FMT(IOS,
"Starlet bootstrap exception {}: LR={:#010x} SPSR={:#010x} PC={:#010x}",
fault_code, m_memory->Read32(bootstrap.fault_lr),
m_memory->Read32(bootstrap.fault_spsr), m_core->GetRegister(15));
m_bootstrap_fault_logged = true;
m_bootstrap_outcome_logged = true;
return;
}
if (m_memory->Read32(bootstrap.trace) != 0x52455631)
return;
const u32 length = m_memory->Read32(bootstrap.trace + 4);
if (length > 1024)
{
ERROR_LOG_FMT(IOS, "Starlet bootstrap trace length is invalid: {}", length);
m_bootstrap_outcome_logged = true;
return;
}
if (length < m_bootstrap_trace_length)
{
m_bootstrap_trace_length = 0;
m_bootstrap_line_length = 0;
}
for (u32 index = m_bootstrap_trace_length; index < length; ++index)
{
const char character = static_cast<char>(m_memory->Read8(bootstrap.trace + 8 + index));
if (character == '\n')
{
const std::string_view line(m_bootstrap_line.data(), m_bootstrap_line_length);
INFO_LOG_FMT(IOS, "Starlet bootstrap: {}", line);
if (line == "REV:BOOT:IDLE" || line.starts_with("REV:PANIC") ||
line.starts_with("REV:FATAL"))
{
INFO_LOG_FMT(IOS, "Starlet bootstrap stopped at PC={:#010x} after {} instructions",
m_core->GetRegister(15), m_core->GetExecutedInstructions());
m_bootstrap_outcome_logged = true;
}
m_bootstrap_line_length = 0;
}
else if (m_bootstrap_line_length < m_bootstrap_line.size())
{
m_bootstrap_line[m_bootstrap_line_length++] = character;
}
}
m_bootstrap_trace_length = length;
}
} // namespace IOS::LLE
+12
View File
@@ -3,11 +3,13 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
class PointerWrap;
@@ -32,6 +34,8 @@ class StarletMemory;
class Starlet final
{
public:
// Allocate in Starlet.cpp so callers never depend on this class's size.
static std::unique_ptr<Starlet> Create(Core::System& system);
explicit Starlet(Core::System& system);
~Starlet();
@@ -77,10 +81,18 @@ private:
static void RunCallback(Core::System& system, u64 userdata, s64 cycles_late);
void RunSlice(s64 cycles_late);
void TraceBootstrap();
Core::System& m_system;
std::unique_ptr<StarletMemory> m_memory;
std::unique_ptr<ARMCore> m_core;
std::optional<BootstrapELFInfo> m_bootstrap;
std::array<char, 256> m_bootstrap_line{};
u32 m_bootstrap_line_length = 0;
u32 m_bootstrap_trace_length = 0;
u32 m_last_probe_status = 0;
bool m_bootstrap_outcome_logged = false;
bool m_bootstrap_fault_logged = false;
CoreTiming::EventType* m_run_event = nullptr;
u64 m_next_jit_diagnostic_instruction = 25'000'000;
u64 m_next_realtime_diagnostic_ms = 0;
+113 -3
View File
@@ -726,6 +726,85 @@ void StarletMemory::Reset()
InitializeWiimotes();
}
void StarletMemory::PrepareBootstrap()
{
m_initialized = false;
Reset();
m_boot0_mapped = false;
m_sram_split_mode = true;
WriteRegister(HW_BOOT0, BOOT0_DISABLE);
WriteRegister(HW_SRNPROT, SRNPROT_SRAM_SPLIT_MODE);
}
bool StarletMemory::AttachReadOnlyNAND(const std::string& path, std::string* error)
{
m_nand_journal.reset();
m_nand_overlay.clear();
m_nand = File::IOFile(path, "rb", File::SharedAccess::Read);
const u64 nand_size = m_nand ? m_nand.GetSize() : 0;
if (nand_size != NAND_IMAGE_SIZE && nand_size != BOOTMII_NAND_IMAGE_SIZE)
{
m_nand.Close();
if (error)
*error = "the bootstrap NAND must be a 0x21000000-byte raw NAND image or a "
"0x21000400-byte BootMii dump";
return false;
}
m_nand_read_only = true;
return true;
}
bool StarletMemory::AttachDisposableNAND(const std::string& source, const std::string& copy,
std::string* error)
{
m_nand_disposable_copy.Close();
if (copy.empty() || File::Exists(copy))
{
if (error)
*error = "the disposable NAND copy must name a file that does not exist yet";
return false;
}
if (!AttachReadOnlyNAND(source, error))
return false;
// "x": creation fails if the file appeared since the check above.
File::IOFile output(copy, "wbx");
std::array<u8, 0x10000> buffer{};
const u64 size = m_nand.GetSize();
bool copied = output.IsOpen() && m_nand.Seek(0, File::SeekOrigin::Begin);
for (u64 offset = 0; copied && offset < size; offset += buffer.size())
{
const size_t count = static_cast<size_t>(std::min<u64>(buffer.size(), size - offset));
copied = m_nand.ReadArray(buffer.data(), count) && output.WriteArray(buffer.data(), count);
}
copied = copied && output.Flush();
output.Close();
if (copied)
m_nand_disposable_copy = File::IOFile(copy, "r+b");
if (!copied || !m_nand_disposable_copy || m_nand_disposable_copy.GetSize() != size)
{
m_nand_disposable_copy.Close();
m_nand.Close();
if (error)
*error = "could not create the disposable NAND copy";
return false;
}
m_nand_read_only = false;
return true;
}
bool StarletMemory::AttachKeys(const std::string& path, std::string* error)
{
File::IOFile keys_file(path, "rb");
if (!keys_file || keys_file.GetSize() != m_keys.size() || !keys_file.ReadArray(&m_keys))
{
m_keys.fill(0);
if (error)
*error = "the bootstrap keys must be a 1024-byte BootMii keys.bin";
return false;
}
return true;
}
void StarletMemory::DoState(PointerWrap& p)
{
const std::lock_guard wifi_sdio_lock(m_wifi_sdio_register_mutex);
@@ -2020,7 +2099,11 @@ void StarletMemory::ExecuteOHCI1HCICommand(const std::vector<u8>& command)
case HCI_CMD_READ_STORED_LINK_KEY:
{
const bool read_all = parameter_size >= sizeof(hci_read_stored_link_key_cp) && command[9] != 0;
const u16 key_count = read_all ? static_cast<u16>(m_wiimotes.size()) : 0;
// Bootstrap mode resets these slots without constructing Wiimote devices.
// Only initialized devices have identities and keys to return.
const u16 key_count = read_all ? static_cast<u16>(std::ranges::count_if(
m_wiimotes, [](const auto& wiimote) { return !!wiimote; })) :
0;
append_le16(&reply, 255);
append_le16(&reply, key_count);
if (key_count != 0)
@@ -2028,6 +2111,8 @@ void StarletMemory::ExecuteOHCI1HCICommand(const std::vector<u8>& command)
std::vector<u8> parameters = {static_cast<u8>(key_count)};
for (const auto& wiimote : m_wiimotes)
{
if (!wiimote)
continue;
parameters.insert(parameters.end(), wiimote->GetBD().begin(), wiimote->GetBD().end());
parameters.insert(parameters.end(), wiimote->GetLinkKey().begin(),
wiimote->GetLinkKey().end());
@@ -4154,6 +4239,21 @@ bool StarletMemory::CommitNANDPages(const NANDJournal::Pages& pages)
const Common::PerformanceTrace::Scope trace(Common::PerformanceTrace::Region::NAND);
if (m_nand_journal && !m_nand_journal->Append(pages))
return false;
// Phase 06H: mirror into the disposable copy first; a failure leaves the array unchanged.
if (m_nand_disposable_copy)
{
for (const auto& [page, data] : pages)
{
if (!m_nand_disposable_copy.Seek(static_cast<s64>(page) * NAND_RAW_PAGE_SIZE,
File::SeekOrigin::Begin) ||
!m_nand_disposable_copy.WriteArray(data))
{
return false;
}
}
if (!m_nand_disposable_copy.Flush())
return false;
}
for (const auto& [page, data] : pages)
m_nand_overlay.insert_or_assign(page, data);
return true;
@@ -4545,6 +4645,11 @@ bool StarletMemory::ReadNANDStatus(u32 command)
bool StarletMemory::StageNANDProgram(u32 command, bool random_data_input)
{
if (m_nand_read_only)
{
ERROR_LOG_FMT(IOS, "Read-only bootstrap NAND refused program command {:#010x}", command);
return false;
}
if (!(command & NAND_CTRL_WRITE))
return false;
@@ -4590,7 +4695,7 @@ bool StarletMemory::StageNANDProgram(u32 command, bool random_data_input)
bool StarletMemory::CommitNANDProgram()
{
Common::PerformanceTrace::Add(Common::PerformanceTrace::Counter::NANDPrograms);
if (!m_nand_program_pending)
if (m_nand_read_only || !m_nand_program_pending)
return false;
NANDPage raw{};
@@ -4628,6 +4733,11 @@ bool StarletMemory::CommitNANDProgram()
bool StarletMemory::StageNANDErase()
{
if (m_nand_read_only)
{
ERROR_LOG_FMT(IOS, "Read-only bootstrap NAND refused erase setup");
return false;
}
m_nand_erase_page = ReadRegister(NAND_ADDR2) & 0xffffff;
m_nand_erase_pending = m_nand_erase_page < NAND_PAGE_COUNT;
if (m_nand_erase_pending)
@@ -4638,7 +4748,7 @@ bool StarletMemory::StageNANDErase()
bool StarletMemory::CommitNANDErase()
{
Common::PerformanceTrace::Add(Common::PerformanceTrace::Counter::NANDErases);
if (!m_nand_erase_pending)
if (m_nand_read_only || !m_nand_erase_pending)
return false;
const u32 block_start = m_nand_erase_page & ~(NAND_PAGES_PER_BLOCK - 1);
@@ -56,6 +56,22 @@ public:
}
bool Init(const std::string& dump_directory, std::string* error);
// Set up empty SRAM with BOOT0 hidden and the IOS-style high SRAM split for an external ELF.
void PrepareBootstrap();
// Bootstrap-ELF mode only: expose a raw NAND image to the NAND controller strictly read-only.
// No journal is opened; every program or erase command then fails with the controller error
// bit, so a Revolution IOS read-only driver can be validated without any write path.
bool AttachReadOnlyNAND(const std::string& path, std::string* error);
// Revolution IOS Phase 06H, emulator only: a disposable writable NAND. `source` is opened
// read-only exactly as above and is never written. `copy` must not exist; it is created as a
// byte-for-byte copy of the source, and every committed program or erase is mirrored into it
// before the in-memory overlay changes, so it holds the mutated image for independent
// checking. No journal is opened.
bool AttachDisposableNAND(const std::string& source, const std::string& copy,
std::string* error);
// Revolution IOS Phase 04C: bootstrap mode loads no keys, so the OTP reads zero. This
// loads a 1024-byte BootMii keys.bin so OTP reads return the console's own words.
bool AttachKeys(const std::string& path, std::string* error);
void Reset();
void DoState(PointerWrap& p);
@@ -321,10 +337,13 @@ private:
u64 m_arm_cycles = 0;
u32 m_timer_offset = 0;
bool m_initialized = false;
bool m_nand_read_only = false;
bool m_boot0_mapped = true;
bool m_sram_split_mode = false;
// Host persistence is not serialized; savestates contain the complete overlay above.
std::unique_ptr<NANDJournal> m_nand_journal;
// Phase 06H: the disposable copy mirroring committed NAND pages (bootstrap mode only).
File::IOFile m_nand_disposable_copy;
std::unique_ptr<AX88772> m_ethernet;
std::unique_ptr<SlirpNetwork> m_ethernet_network;
};
+36 -1
View File
@@ -4,7 +4,9 @@
#include <cstdio>
#include <thread>
#include "Common/Logging/Log.h"
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/System.h"
#include "DolphinNoGUI/Platform.h"
@@ -26,10 +28,43 @@ void PlatformHeadless::SetTitle(const std::string& title)
void PlatformHeadless::MainLoop()
{
bool diff_observed = false;
while (m_running.IsSet())
{
UpdateRunningFlag();
Core::HostDispatchJobs(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
Core::HostDispatchJobs(system);
if (!diff_observed && Core::IsRunning(system))
{
auto& memory = system.GetMemory();
const u32 status = memory.Read_U32(0x00003c00);
if (status == 0x52500004 || (status & 0xffffff00) == 0x5250e100)
{
diff_observed = true;
INFO_LOG_FMT(IOS, "IOS_PROBE_DIFF_DONE status={:#010x}", status);
constexpr u32 trace_base = 0x00003d00;
const u32 count = memory.Read_U32(trace_base);
if (count > 8)
{
ERROR_LOG_FMT(IOS, "IOS_PROBE_DIFF_BAD_COUNT count={}", count);
}
else
{
for (u32 index = 0; index < count; ++index)
{
const u32 record = trace_base + 0x20 + index * 24;
INFO_LOG_FMT(IOS,
"IOS_PROBE_JSON {{\"schema\":1,\"index\":{},\"operation\":{},"
"\"argument\":{},\"return\":{},\"output\":{},\"ticks\":{},"
"\"status\":{}}}",
index, memory.Read_U32(record), memory.Read_U32(record + 4),
static_cast<s32>(memory.Read_U32(record + 8)),
memory.Read_U32(record + 12), memory.Read_U32(record + 16),
memory.Read_U32(record + 20));
}
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
+27 -22
View File
@@ -1,27 +1,32 @@
enable_testing()
add_custom_target(unittests)
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
set_target_properties(tests PROPERTIES FOLDER Tests)
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
add_test(NAME tests COMMAND tests)
add_custom_command(TARGET tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
)
enable_testing()
add_custom_target(unittests)
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
set_target_properties(tests PROPERTIES FOLDER Tests)
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
add_test(NAME tests COMMAND tests)
add_custom_command(TARGET tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
)
add_dependencies(unittests tests)
macro(add_dolphin_test target)
add_library(${target} OBJECT ${ARGN})
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
target_link_libraries(tests PRIVATE ${target})
endmacro()
add_subdirectory(Common)
add_subdirectory(Core)
# Runs a real Starlet ELF through the fork's CPU and SRAM bus without NAND or a frontend.
add_executable(StarletBootstrapProbe EXCLUDE_FROM_ALL
${CMAKE_SOURCE_DIR}/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp StubHost.cpp)
target_link_libraries(StarletBootstrapProbe PRIVATE fmt::fmt core uicommon)
macro(add_dolphin_test target)
add_library(${target} OBJECT ${ARGN})
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
target_link_libraries(tests PRIVATE ${target})
endmacro()
add_subdirectory(Common)
add_subdirectory(Core)
add_subdirectory(VideoCommon)
# Opt-in local trace qualification; never starts a console or accesses a NAND dump.
+46 -45
View File
@@ -1,20 +1,21 @@
add_dolphin_test(MMIOTest MMIOTest.cpp)
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
add_dolphin_test(DSPAssemblyTest
DSP/DSPAssemblyTest.cpp
DSP/DSPTestBinary.cpp
DSP/DSPTestText.cpp
DSP/HermesBinary.cpp
DSP/HermesText.cpp
)
add_dolphin_test(MMIOTest MMIOTest.cpp)
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
add_dolphin_test(DSPAssemblyTest
DSP/DSPAssemblyTest.cpp
DSP/DSPTestBinary.cpp
DSP/DSPTestText.cpp
DSP/HermesBinary.cpp
DSP/HermesText.cpp
)
add_dolphin_test(ESFormatsTest IOS/ES/FormatsTest.cpp)
add_dolphin_test(StarletARMCoreTest IOS/Starlet/ARMCoreTest.cpp)
add_dolphin_test(StarletBootstrapELFTest IOS/Starlet/BootstrapELFTest.cpp)
add_dolphin_test(StarletDMATest IOS/Starlet/DMATest.cpp)
add_dolphin_test(StarletSRAMReadTest IOS/Starlet/SRAMReadTest.cpp)
add_dolphin_test(StarletSRAMWriteTest IOS/Starlet/SRAMWriteTest.cpp)
@@ -24,36 +25,36 @@ add_dolphin_test(StarletEthernetTest IOS/Starlet/AX88772Test.cpp)
target_include_directories(StarletEthernetTest PRIVATE ${PROJECT_SOURCE_DIR}/Externals/libslirp/include)
add_dolphin_test(FileSystemTest IOS/FS/FileSystemTest.cpp)
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
if(_M_X86_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
if(_M_X86_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/Jit64Common/ConvertDoubleToSingle.cpp
PowerPC/Jit64Common/DCache.cpp
PowerPC/Jit64Common/Fres.cpp
PowerPC/Jit64Common/Frsqrte.cpp
)
elseif(_M_ARM_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/JitArm64/ConvertSingleDouble.cpp
PowerPC/JitArm64/FPRF.cpp
PowerPC/JitArm64/Fres.cpp
PowerPC/JitArm64/Frsqrte.cpp
PowerPC/JitArm64/MovI2R.cpp
)
else()
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
)
endif()
target_sources(PowerPCTest PRIVATE
PowerPC/TestValues.h
StubJit.h
)
PowerPC/Jit64Common/Fres.cpp
PowerPC/Jit64Common/Frsqrte.cpp
)
elseif(_M_ARM_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/JitArm64/ConvertSingleDouble.cpp
PowerPC/JitArm64/FPRF.cpp
PowerPC/JitArm64/Fres.cpp
PowerPC/JitArm64/Frsqrte.cpp
PowerPC/JitArm64/MovI2R.cpp
)
else()
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
)
endif()
target_sources(PowerPCTest PRIVATE
PowerPC/TestValues.h
StubJit.h
)
@@ -0,0 +1,236 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <string>
#include <unordered_map>
#include <gtest/gtest.h>
#include "Core/IOS/Starlet/ARMCore.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
namespace IOS::LLE
{
namespace
{
constexpr u32 TEXT_BASE = 0xffff0000;
constexpr u32 DATA_BASE = 0xfffe0000;
class RecordingBus final : public ARMBus
{
public:
u8 Read8(u32 address) override
{
const auto it = bytes.find(address);
return it == bytes.end() ? 0 : it->second;
}
void Write8(u32 address, u8 value) override { bytes[address] = value; }
std::unordered_map<u32, u8> bytes;
};
void Put16(std::array<u8, 0x300>& image, size_t offset, u16 value)
{
image[offset] = static_cast<u8>(value >> 8);
image[offset + 1] = static_cast<u8>(value);
}
void Put32(std::array<u8, 0x300>& image, size_t offset, u32 value)
{
image[offset] = static_cast<u8>(value >> 24);
image[offset + 1] = static_cast<u8>(value >> 16);
image[offset + 2] = static_cast<u8>(value >> 8);
image[offset + 3] = static_cast<u8>(value);
}
std::array<u8, 0x300> MakeImage()
{
std::array<u8, 0x300> image{};
image[0] = 0x7f;
image[1] = 'E';
image[2] = 'L';
image[3] = 'F';
image[4] = 1; // ELF32
image[5] = 2; // big endian
image[6] = 1;
Put16(image, 16, 2); // ET_EXEC
Put16(image, 18, 40); // ARM
Put32(image, 20, 1); // ELF version
Put32(image, 24, TEXT_BASE);
Put32(image, 28, 52); // program headers
Put32(image, 32, 0x200); // section headers
Put16(image, 40, 52);
Put16(image, 42, 32);
Put16(image, 44, 2);
Put16(image, 46, 40);
Put16(image, 48, 3);
Put32(image, 52, 1);
Put32(image, 56, 0x100);
Put32(image, 60, TEXT_BASE);
Put32(image, 64, TEXT_BASE);
Put32(image, 68, 4);
Put32(image, 72, 8);
Put32(image, 76, 5);
image[0x100] = 0xea; // ARM branch instruction
Put32(image, 84, 1);
Put32(image, 88, 0x110);
Put32(image, 92, DATA_BASE);
Put32(image, 96, DATA_BASE);
Put32(image, 100, 4);
Put32(image, 104, 0x420);
Put32(image, 108, 6);
image[0x110] = 0x12;
image[0x111] = 0x34;
image[0x112] = 0x56;
image[0x113] = 0x78;
constexpr char names[] = "\0REV_TRACE\0__fault_code\0__fault_lr\0__fault_spsr";
for (size_t index = 0; index < sizeof(names); ++index)
image[0x180 + index] = static_cast<u8>(names[index]);
Put32(image, 0x228 + 4, 3); // SHT_STRTAB
Put32(image, 0x228 + 16, 0x180);
Put32(image, 0x228 + 20, sizeof(names));
Put32(image, 0x250 + 4, 2); // SHT_SYMTAB
Put32(image, 0x250 + 16, 0x120);
Put32(image, 0x250 + 20, 5 * 16);
Put32(image, 0x250 + 24, 1);
Put32(image, 0x250 + 36, 16);
const auto symbol = [&image](size_t index, u32 name, u32 address, u32 size) {
const size_t offset = 0x120 + index * 16;
Put32(image, offset, name);
Put32(image, offset + 4, address);
Put32(image, offset + 8, size);
};
symbol(1, 1, DATA_BASE + 8, 1032);
symbol(2, 11, DATA_BASE + 0x410, 4);
symbol(3, 24, DATA_BASE + 0x414, 4);
symbol(4, 35, DATA_BASE + 0x418, 4);
return image;
}
TEST(BootstrapELFTest, LoadsSegmentsAndZerosUninitializedData)
{
const auto image = MakeImage();
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error)) << error;
EXPECT_EQ(info.entry, TEXT_BASE);
EXPECT_EQ(info.trace, DATA_BASE + 8);
EXPECT_EQ(info.fault_spsr, DATA_BASE + 0x418);
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
EXPECT_EQ(bus.Read8(TEXT_BASE + 4), 0);
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
EXPECT_EQ(bus.Read32(DATA_BASE + 0x41c), 0u);
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420});
}
TEST(BootstrapELFTest, RejectsMalformedImageBeforeWritingMemory)
{
auto image = MakeImage();
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
image[5] = 1;
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
image = MakeImage();
Put32(image, 0x120 + 2 * 16 + 4, DATA_BASE + 0x420);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
image = MakeImage();
Put32(image, 88, 0x300);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
}
// Revolution IOS Phase 06H: the optional MEM2 text segment of the emulator-only
// disposable-image kernel, accepted only when the caller allows it.
constexpr u32 MEM2_TEXT = 0x12600000;
std::array<u8, 0x300> MakeMem2Image(u32 address, u32 flags)
{
auto image = MakeImage();
Put16(image, 44, 3);
Put32(image, 116, 1);
Put32(image, 120, 0x114);
Put32(image, 124, address);
Put32(image, 128, address);
Put32(image, 132, 4);
Put32(image, 136, 0x10);
Put32(image, 140, flags);
Put32(image, 0x114, 0xe12fff1e); // BX LR
return image;
}
TEST(BootstrapELFTest, Mem2TextSegmentOnlyWhenAllowed)
{
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
auto image = MakeMem2Image(MEM2_TEXT, 5);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error, true)) << error;
EXPECT_EQ(bus.Read32(MEM2_TEXT), 0xe12fff1eu);
EXPECT_EQ(bus.Read32(MEM2_TEXT + 0xc), 0u);
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420 + 0x10});
RecordingBus plain;
const auto two_segments = MakeImage();
EXPECT_TRUE(LoadBootstrapELF(two_segments, plain, &info, &error, true)) << error;
}
TEST(BootstrapELFTest, RejectsInvalidMem2TextSegmentBeforeWritingMemory)
{
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
// Writable, outside MEM2, crossing its end, or a second SRAM text segment.
for (const auto& image : {MakeMem2Image(MEM2_TEXT, 6), MakeMem2Image(0x0fff0000, 5),
MakeMem2Image(0x13fffff8, 5), MakeMem2Image(TEXT_BASE, 5)})
{
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error, true));
EXPECT_TRUE(bus.bytes.empty());
}
}
TEST(ARMCoreTest, Armv5DoublewordTransfersAndAlignmentAbort)
{
RecordingBus bus;
bus.Write32(TEXT_BASE, 0xe1c020d0); // LDRD r2, r3, [r0]
bus.Write32(TEXT_BASE + 4, 0xe1c120f0); // STRD r2, r3, [r1]
bus.Write32(DATA_BASE, 0x11223344);
bus.Write32(DATA_BASE + 4, 0x55667788);
ARMCore core(bus);
core.Reset(TEXT_BASE);
core.SetBigEndian(true);
core.SetRegister(0, DATA_BASE);
core.SetRegister(1, DATA_BASE + 8);
core.Step();
EXPECT_EQ(core.GetRegister(2), 0x11223344u);
EXPECT_EQ(core.GetRegister(3), 0x55667788u);
core.Step();
EXPECT_EQ(bus.Read32(DATA_BASE + 8), 0x11223344u);
EXPECT_EQ(bus.Read32(DATA_BASE + 12), 0x55667788u);
core.Reset(TEXT_BASE);
core.SetBigEndian(true);
core.SetRegister(0, DATA_BASE + 4);
core.Step();
EXPECT_EQ(core.GetRegister(15), TEXT_BASE + 0x10);
EXPECT_EQ(core.GetRegister(14), TEXT_BASE + 8);
EXPECT_EQ(core.GetCP15State().fault_address, DATA_BASE + 4);
}
} // namespace
} // namespace IOS::LLE
@@ -135,6 +135,21 @@ protected:
EXPECT_EQ(m_memory->GetDMAMemoryPointer(address, 4), nullptr);
}
void CheckBootstrapStoredKeys()
{
m_memory->PrepareBootstrap();
for (const u8 read_all : {0, 1})
{
SCOPED_TRACE(read_all);
m_memory->m_ohci1_hci_events.clear();
m_memory->ExecuteOHCI1HCICommand({0x0d, 0x0c, 7, 0, 0, 0, 0, 0, 0, read_all});
ASSERT_EQ(m_memory->m_ohci1_hci_events.size(), 1u);
// Command Complete: capacity 255, zero stored keys, no Return Link Keys event.
EXPECT_EQ(m_memory->m_ohci1_hci_events.front(),
(std::vector<u8>{0x0e, 8, 1, 0x0d, 0x0c, 0, 0xff, 0, 0, 0}));
}
}
void CheckOHCI()
{
for (const u32 base : {0x10000U, 0x10010000U, 0x0d404000U})
@@ -381,6 +396,11 @@ TEST_F(StarletDMATest, OHCIUsesTwoPhysicalPagesAndLittleEndianDescriptors)
CheckOHCI();
}
TEST_F(StarletDMATest, BootstrapReadStoredKeysHasNoWiimoteDevices)
{
CheckBootstrapStoredKeys();
}
TEST_F(StarletDMATest, NANDSeparatesDataSpareAndECCIncludingPartialTransfers)
{
CheckNAND();
@@ -0,0 +1,170 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include "Core/Core.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/Starlet/ARMCore.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
#include "Core/IOS/Starlet/StarletMemory.h"
#include "Core/System.h"
namespace
{
enum class ExpectedOutcome
{
Idle,
Panic,
FatalData,
UndefinedFault,
Phase2
};
bool Run(const std::string& path, bool jit, ExpectedOutcome expected)
{
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file || file.tellg() <= 0 || file.tellg() > 16 * 1024 * 1024)
{
std::cerr << "Expected a readable Starlet ELF of at most 16 MiB: " << path << '\n';
return false;
}
const size_t size = static_cast<size_t>(file.tellg());
std::vector<u8> image(size);
file.seekg(0);
if (!file.read(reinterpret_cast<char*>(image.data()), size))
{
std::cerr << "Could not read Starlet ELF: " << path << '\n';
return false;
}
Core::DeclareAsCPUThread();
auto& system = Core::System::GetInstance();
system.GetWiiIPC().Reset();
IOS::LLE::StarletMemory memory(system);
memory.PrepareBootstrap();
IOS::LLE::BootstrapELFInfo info;
std::string error;
if (!IOS::LLE::LoadBootstrapELF(image, memory, &info, &error))
{
std::cerr << error << '\n';
return false;
}
IOS::LLE::ARMCore core(memory);
core.SetJitEnabled(jit);
core.Reset(info.entry);
core.SetBigEndian(true);
constexpr u32 TRACE_MAGIC = 0x52455631;
constexpr u32 TRACE_CAPACITY = 1024;
for (int slice = 0; slice < 512; ++slice)
{
core.SetIRQLine(system.GetWiiIPC().IsStarletIRQAsserted());
core.SetFIQLine(system.GetWiiIPC().IsStarletFIQAsserted());
core.RunCycles(4096);
const u32 fault_code = memory.Read32(info.fault_code);
if (fault_code != 0)
{
std::cerr << "Starlet exception " << fault_code << " LR=" << std::hex
<< memory.Read32(info.fault_lr) << " SPSR=" << memory.Read32(info.fault_spsr)
<< " PC=" << core.GetRegister(15) << std::dec << '\n';
return expected == ExpectedOutcome::UndefinedFault && fault_code == 1 &&
memory.Read32(info.fault_lr) != 0 && memory.Read32(info.fault_spsr) != 0;
}
if (memory.Read32(info.trace) != TRACE_MAGIC)
continue;
const u32 length = memory.Read32(info.trace + 4);
if (length > TRACE_CAPACITY)
{
std::cerr << "Invalid REV_TRACE length: " << length << '\n';
return false;
}
std::string trace(length, '\0');
for (u32 index = 0; index < length; ++index)
trace[index] = static_cast<char>(memory.Read8(info.trace + 8 + index));
if (trace.find("REV:BOOT:IDLE\n") == std::string::npos &&
trace.find("REV:PANIC") == std::string::npos &&
trace.find("REV:FATAL") == std::string::npos)
{
continue;
}
std::cout << trace;
std::cout << "PC=0x" << std::hex << core.GetRegister(15) << std::dec
<< " instructions=" << core.GetExecutedInstructions() << '\n';
const bool early_startup_ok = trace.find("REV:BOOT:ENTRY\n") != std::string::npos &&
trace.find("REV:BOOT:BSS_OK\n") != std::string::npos;
if (expected == ExpectedOutcome::FatalData)
{
return early_startup_ok && trace.find("REV:FATAL:DATA\n") != std::string::npos &&
trace.find("REV:BOOT:DATA_OK\n") == std::string::npos;
}
const bool startup_ok = early_startup_ok &&
trace.find("REV:BOOT:DATA_OK\n") != std::string::npos &&
trace.find("REV:BOOT:STACK_OK\n") != std::string::npos &&
trace.find("REV:BOOT:RUST\n") != std::string::npos;
if (expected == ExpectedOutcome::Phase2)
{
const std::string_view required[] = {
"REV:P2:START\n", "ARCH PASS\n", "CRITICAL PASS\n",
"TIMER PASS\n", "MONOTONIC PASS\n", "MEM PASS\n",
"IRQ PASS\n", "BARRIER PASS\n", "LOGGING PASS\n",
"REV:P2:PASS\n", "REV:P2:KERNEL_READY\n", "REV:BOOT:IDLE\n"};
bool phase2_ok = true;
for (const std::string_view marker : required)
phase2_ok &= trace.find(marker) != std::string::npos;
return startup_ok && phase2_ok && trace.find(" FAIL\n") == std::string::npos;
}
const bool outcome_ok =
expected == ExpectedOutcome::Panic ?
trace.find("REV:PANIC") != std::string::npos :
expected == ExpectedOutcome::Idle && trace.find("REV:BOOT:IDLE\n") != std::string::npos;
return startup_ok && outcome_ok;
}
std::cerr << "No terminal bootstrap marker after 2097152 ARM cycles, PC=0x" << std::hex
<< core.GetRegister(15) << std::dec << '\n';
return false;
}
} // namespace
int main(int argc, char** argv)
{
if (argc < 2 || argc > 4)
{
std::cerr << "Usage: StarletBootstrapProbe <ELF> [--jit] "
"[--expect-panic|--expect-fatal-data|--expect-undefined-fault|--expect-phase2]\n";
return 2;
}
bool jit = false;
ExpectedOutcome expected = ExpectedOutcome::Idle;
bool outcome_selected = false;
for (int index = 2; index < argc; ++index)
{
const std::string_view option = argv[index];
if (option == "--jit")
jit = true;
else if (option == "--expect-panic" || option == "--expect-fatal-data" ||
option == "--expect-undefined-fault" || option == "--expect-phase2")
{
if (outcome_selected)
{
std::cerr << "Select only one expected outcome\n";
return 2;
}
outcome_selected = true;
expected = option == "--expect-panic" ? ExpectedOutcome::Panic :
option == "--expect-fatal-data" ? ExpectedOutcome::FatalData :
option == "--expect-undefined-fault" ? ExpectedOutcome::UndefinedFault :
ExpectedOutcome::Phase2;
}
else
{
std::cerr << "Unknown option: " << option << '\n';
return 2;
}
}
return Run(argv[1], jit, expected) ? 0 : 1;
}
+126
View File
@@ -0,0 +1,126 @@
{
"recordedAt": "2026-10-03",
"purpose": "Publish the exact existing StarletOS integration, without emulator behavior changes",
"sourceBaseCommit": "a8dbbab62de0ec429d9d887c39b9669c75f51c21",
"previousRemoteMaster": "b931671fde1e0461cbca37eeb8348a25d44ab76e",
"companionRepository": "https://git.yaya48.xyz/Yaya48/StarletOS",
"companionCheckpoint": "efa8b310da2c3e3bd36d762080d42d410a5413c9",
"files": [
{
"path": "CMakeLists.txt",
"sha256": "1ed308f4ccc9d6a4405d9ec232a4ac64fb88aa9c69a4773ee93fe7c62b357d4d",
"bytes": 34657
},
{
"path": "Run-Wii-Starlet-Bootstrap.ps1",
"sha256": "486f99f899fde18371b7bfc2b67fcf12a64ceb748285cc00ae4a71716349d07a",
"bytes": 18583
},
{
"path": "Source/Core/Core/Boot/Boot.cpp",
"sha256": "43a0b1c2e60c9a362fe3637ba0aa7b23e8b75ce5e5517cd3515b50ec596c43a5",
"bytes": 27009
},
{
"path": "Source/Core/Core/CMakeLists.txt",
"sha256": "32dc85b995c9beb3739cc1ea554c308042e16b6c270071550728d18c85ada79e",
"bytes": 24388
},
{
"path": "Source/Core/Core/Config/MainSettings.cpp",
"sha256": "e331e8f7145ebfce5cd3d2be1b3dabe1dce597e8040132402f04a0152526ce56",
"bytes": 47583
},
{
"path": "Source/Core/Core/Config/MainSettings.h",
"sha256": "9052fbc4a1da18ea3e8a11a93d62ef06423da74671b1f9b1091bb62f1fcff62b",
"bytes": 18437
},
{
"path": "Source/Core/Core/ConfigManager.cpp",
"sha256": "7d369d0264f5a1ad0df5ac44e9b6ea0f514a196ec7f80952218f9439ab17ea10",
"bytes": 19807
},
{
"path": "Source/Core/Core/HW/HW.cpp",
"sha256": "7e9bcfff87f526510aad149e781ae57140e32f7b32beafec00271fbcdb152848",
"bytes": 4526
},
{
"path": "Source/Core/Core/IOS/Starlet/ARMCore.cpp",
"sha256": "40d45aed7c5edb4540105dcdee4dd8dcde88c21ba7f3279eff0a9564f2d02a29",
"bytes": 75597
},
{
"path": "Source/Core/Core/IOS/Starlet/BootstrapELF.cpp",
"sha256": "344e6e38922b64fa58d9755c2df5fd752b00cf3dc73114809f6e9cada724c42a",
"bytes": 8554
},
{
"path": "Source/Core/Core/IOS/Starlet/BootstrapELF.h",
"sha256": "b25184cb4d3934e02220417b8e4b8005fc57010f4d3ab7af697c98f11afb305f",
"bytes": 943
},
{
"path": "Source/Core/Core/IOS/Starlet/Starlet.cpp",
"sha256": "78e3b006501af06fbef220d95213690f8a31ed5b3c36699c6e07ba58be3ba852",
"bytes": 26039
},
{
"path": "Source/Core/Core/IOS/Starlet/Starlet.h",
"sha256": "5dbdd1a854fd17377a339886949d80ebb1969d6453a4bdfd5a6f9a503bbb4f2a",
"bytes": 3553
},
{
"path": "Source/Core/Core/IOS/Starlet/StarletMemory.cpp",
"sha256": "34900b6c340e412f99ddb3ce6ba17bdb4d85ba7b27b2adc522315b158b675fca",
"bytes": 189757
},
{
"path": "Source/Core/Core/IOS/Starlet/StarletMemory.h",
"sha256": "cfa3aee13f260590ae9f7cdd48f8f08d1c67e4e6d647ef3eca6e01d472dbdd29",
"bytes": 14873
},
{
"path": "Source/Core/DolphinNoGUI/PlatformHeadless.cpp",
"sha256": "1390cff0a139f0b0a90559e0fda855446d279190827a6c5079718421924f4041",
"bytes": 2533
},
{
"path": "Source/UnitTests/CMakeLists.txt",
"sha256": "bb0aeb694c2f328b891e5e961fe0af16c5b30ff1a5f02d4a8d84bdfa5930b581",
"bytes": 1659
},
{
"path": "Source/UnitTests/Core/CMakeLists.txt",
"sha256": "563899ae142ba4d0e146bb125bd35c8387a77d25db5ac48cf8ba48a32cb30314",
"bytes": 2043
},
{
"path": "Source/UnitTests/Core/IOS/Starlet/BootstrapELFTest.cpp",
"sha256": "62d483a737b68cca00e51c4a93511b4863022ed2f4b90e721ff36325630cfc3a",
"bytes": 7057
},
{
"path": "Source/UnitTests/Core/IOS/Starlet/DMATest.cpp",
"sha256": "15b0816292fff6dcf48d485466f7bf495f2128ba625f3e7c1f225614be631e84",
"bytes": 16450
},
{
"path": "Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp",
"sha256": "83873691164d9b92e3be62f699146feaf8ab713bcb45d25821975e7454f56ede",
"bytes": 6179
},
{
"path": "docs/Wii_Starlet_Bootstrap.md",
"sha256": "bd7ec585472dc44815da8fcb2be6d726b91f04dd0964d82097842fb4ba764021",
"bytes": 4937
}
],
"checks": {
"sourceSnapshotMatchesPriorStarletOSArchive": true,
"unpublishedHistoricalFileVersionsScanned": 67,
"privateFindings": 0,
"newFullBuildOrHardwareTest": "NOT RUN: publication of existing qualified bytes only; see companion phase reports"
}
}
+45
View File
@@ -0,0 +1,45 @@
# StarletOS integration handoff — 2026-10-03
This checkpoint publishes the existing emulator changes used by the companion
[StarletOS repository](https://git.yaya48.xyz/Yaya48/StarletOS). It is a recovered
source checkpoint, not a new claim that stock IOS or physical hardware is fully
supported. The Rust project's current phase is 10C, stock NCD probe H02B awaiting
SD staging and its physical result; consult its HANDOFF and phase reports.
## Published work
- The existing local commits c230971f07 (NAND conformance/diagnostic tools) and
a8dbbab62d (SD CMD6/conformance report) precede this integration checkpoint.
- 16 modified files and 6 previously untracked files are now tracked here:
ARM bootstrap ELF loader and tests, direct PPC/Starlet launch, read-only NAND
and explicit disposable-copy support, diagnostics and regression runner,
ARMv5TE LDRD/STRD handling, and the existing MSVC dependency-detection fix.
- `docs/StarletOS_Checkpoint.json` records SHA-256s of the 22 exact source files.
The companion repository also pins the published Dolphin commit.
- Every runtime byte matches the source snapshot captured before publication.
Only Git attributes, ignore rules and handoff metadata were added this turn.
- Inspection of 67 changed file versions in the two pending commits found no
available private NAND key/content markers, device identifiers or credentials.
The staged integration files were inspected separately before publication.
- No full rebuild, new emulator test or Wii test is claimed by this Git import.
Historical qualification remains in the companion project's phase reports.
## Other computers
Clone this fork beside `starlet-ios-rust`, checkout the commit pinned in
`StarletOS/support/dolphin/manifest.json`, then follow the companion
`docs/DEVELOPMENT.md`. The previous private base bundle and source overlay are
no longer needed to retrieve these sources: their commits are on this server.
Use `docs/Wii_Starlet_Bootstrap.md` for the existing build and test runner.
NAND, keys, user profiles, traces and generated builds remain local/ignored.
The `.starlet_user3` profile is needed only for the full Menu scenarios; the
standalone Starlet bootstrap probe does not need a NAND or keys.
The old `.before-p10a-h07b` script is retained locally as an ignored backup.
## Future sessions
Read AGENTS.md. Record the starting commit, changed behavior, actual tests and
remaining work. Update this handoff and any changed file hashes, commit to this
fork, then update the pinned dependency in StarletOS. Never label a build, a
source comparison or a fake emulator device as a physical Wii observation.
+98
View File
@@ -0,0 +1,98 @@
# Starlet Rust bootstrap in this Dolphin fork
This fork can load the Phase 01 Revolution IOS ELF directly into Starlet SRAM. The loader uses
the ELF's two `PT_LOAD` segments, zeroes their uninitialized tails, starts the ARM core at
`0xffff0000` in big-endian supervisor mode, and publishes `REV_TRACE` lines in the IOS log.
No boot0, keys, or NAND dump is needed for the standalone probe.
## Fast development loop
Build the Rust ELF from `starlet-ios-rust`:
```powershell
cargo build --release
```
Configure this Dolphin fork with `ENABLE_TESTS=ON`, then build `StarletBootstrapProbe`. Run
the build commands from an x64 Native Tools Command Prompt (or after `vcvars64.bat`). For the
local MSVC configuration:
```powershell
cmake --build .starlet_msvc2 --target StarletBootstrapProbe
powershell -NoProfile -ExecutionPolicy Bypass -File .\Run-Wii-Starlet-Bootstrap.ps1
```
The script defaults to the sibling Rust repository's release ELF. It prints every trace line,
the stopping PC, and the ARM instruction count; it exits nonzero for a missing marker, fault,
or invalid ELF. `-JIT` exercises the fork's ARM JIT.
To check the panic path without replacing the ordinary release artifact:
```powershell
cd ..\starlet-ios-rust
cargo build --release --features panic-smoke --target-dir target/panic-smoke
cd ..\dolphin
powershell -NoProfile -ExecutionPolicy Bypass -File .\Run-Wii-Starlet-Bootstrap.ps1 `
-ELF '../starlet-ios-rust/target/panic-smoke/armebv5te-none-eabi/release/revolution-ios-bootstrap' `
-ExpectedOutcome Panic
```
To exercise a Rust fatal check and a synchronous ARM exception, derive two diagnostic
ELF copies from the release artifact:
```powershell
cd ..\starlet-ios-rust
py -3 tools/make_smoke_elf.py target/armebv5te-none-eabi/release/revolution-ios-bootstrap target/phase01-smoke
cd ..\dolphin
powershell -NoProfile -ExecutionPolicy Bypass -File .\Run-Wii-Starlet-Bootstrap.ps1 `
-ELF '../starlet-ios-rust/target/phase01-smoke/fatal-data.elf' -ExpectedOutcome Fatal
powershell -NoProfile -ExecutionPolicy Bypass -File .\Run-Wii-Starlet-Bootstrap.ps1 `
-ELF '../starlet-ios-rust/target/phase01-smoke/undefined-entry.elf' -ExpectedOutcome Fault
```
Each command also accepts `-JIT` and `-FullDolphin`. The fatal image changes only the loaded
`.data` probe, producing `REV:FATAL:DATA`. The exception image replaces the reset branch with
an undefined instruction; the fault record reports code 1 and raw LR/SPSR. Both passed in the
standalone probe and full Dolphin, with interpreter and JIT, on 2026-09-23. The source release
ELF remains unchanged.
The probe uses the actual `ARMCore` and `StarletMemory` implementations. It initializes the
high SRAM mapping, runs 4096-cycle slices, checks `__fault_code`, and decodes `REV_TRACE`. It
does not start Broadway, graphics, USB, or a Wii title. The two ELF parser unit tests run with:
```powershell
cmake --build .starlet_msvc2 --target tests
.starlet_msvc2\Binaries\Tests\tests.exe --gtest_filter=BootstrapELFTest.*
```
## Full Dolphin run
Set `Dolphin.General.WiiStarletBootstrapELF` to the absolute ELF path and
`Dolphin.Core.WiiIOSLLE=True`. This bypasses the boot0/keys/NAND dump initialization in
`StarletMemory` and starts the loaded program at its ELF entry. To run through `DolphinNoGUI`
using the existing `.starlet_user3` Wii profile:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\Run-Wii-Starlet-Bootstrap.ps1 -FullDolphin
```
This mode launches the Wii System Menu title only to initialize the full emulator; the Starlet
runtime is the supplied Rust ELF. It uses the Null video backend and headless platform, reads
new IOS log lines, and stops its own process after the expected marker. A populated Wii user
profile is required for this full frontend path. The standalone probe is the reproducible path
for the Phase 01 CPU and SRAM contract.
The accepted ELF is ELF32, big-endian ARM `ET_EXEC`, entry `0xffff0000`, with exactly one
executable segment at `0xffff0000` (up to 64 KiB) and one writable segment at `0xfffe0000`
(up to 16 KiB). It must expose `REV_TRACE`, `__fault_code`, `__fault_lr`, and
`__fault_spsr` in the data segment's loaded memory span. Rejected images never write to SRAM.
The Rust code reaches `REV:BOOT:IDLE` in both interpreter and JIT; the panic-smoke image
reaches `REV:PANIC`, and the diagnostic images reach fatal and fault states. All four outcomes
pass through the full `DolphinNoGUI` path. An initial
incremental MSVC build crashed before the first slice because `HW.cpp` still allocated the old
`Starlet` class size; AddressSanitizer identified the overflow. A forced rebuild fixed it,
and `Starlet::Create` now keeps allocation in the same translation unit as the class definition.
The CMake configuration also corrects a French MSVC `/showIncludes` prefix that made Ninja
record zero header dependencies. After rebuilding `HW.cpp`, Ninja records `Starlet.h` as a
dependency.