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:
1 parent
a8dbbab62d
commit
54f5a74bbf
28 files changed
+9161
-7407
No files matched your search
@@ -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
|
||||
@@ -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-*
|
||||
@@ -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.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Shared instructions
|
||||
|
||||
Read AGENTS.md and docs/StarletOS_Handoff.md before working in this fork.
|
||||
+887
-879
File diff suppressed because it is too large.
Load diff
@@ -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()
|
||||
}
|
||||
}
|
||||
+800
-795
File diff suppressed because it is too large.
Load diff
+865
-863
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
+656
-652
File diff suppressed because it is too large.
Load diff
+159
-159
@@ -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
|
||||
+2660
-2627
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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user