Track exact StarletOS bootstrap integration and checkpoint provenance

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

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+439 -438
View File
@@ -1,442 +1,443 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <set>
#include <string>
#include <utility>
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Core/USBUtils.h"
#include "DiscIO/Enums.h"
// DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output")
#define BACKEND_ALSA "ALSA"
#define BACKEND_CUBEB "Cubeb"
#define BACKEND_OPENAL "OpenAL"
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_OPENSLES "OpenSLES"
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
namespace PowerPC
{
enum class CPUCore;
}
namespace AudioCommon
{
enum class DPL2Quality;
}
namespace ExpansionInterface
{
enum class EXIDeviceType : int;
enum class Slot : int;
} // namespace ExpansionInterface
namespace SerialInterface
{
enum SIDevices : int;
}
namespace HSP
{
enum class HSPDeviceType : int;
}
namespace Config
{
// Main.Core
extern const Info<bool> MAIN_SKIP_IPL;
extern const Info<PowerPC::CPUCore> MAIN_CPU_CORE;
extern const Info<bool> MAIN_JIT_FOLLOW_BRANCH;
extern const Info<bool> MAIN_FASTMEM;
extern const Info<bool> MAIN_PAGE_TABLE_FASTMEM;
extern const Info<bool> MAIN_FASTMEM_ARENA;
extern const Info<bool> MAIN_LARGE_ENTRY_POINTS_MAP;
extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
// Should really be in the DSP section, but we're kind of stuck with bad decisions made in the past.
extern const Info<bool> MAIN_DSP_HLE;
extern const Info<int> MAIN_MAX_FALLBACK;
extern const Info<int> MAIN_TIMING_VARIANCE;
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
extern const Info<bool> MAIN_RUSH_FRAME_PRESENTATION;
extern const Info<bool> MAIN_SMOOTH_EARLY_PRESENTATION;
extern const Info<bool> MAIN_CPU_THREAD;
extern const Info<bool> MAIN_LOAD_GAME_INTO_MEMORY;
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
extern const Info<std::string> MAIN_DEFAULT_ISO;
extern const Info<bool> MAIN_ENABLE_CHEATS;
extern const Info<int> MAIN_GC_LANGUAGE;
extern const Info<bool> MAIN_OVERRIDE_REGION_SETTINGS;
extern const Info<bool> MAIN_DPL2_DECODER;
extern const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY;
extern const Info<int> MAIN_AUDIO_LATENCY;
extern const Info<int> MAIN_AUDIO_BUFFER_SIZE;
extern const Info<bool> MAIN_AUDIO_FILL_GAPS;
extern const Info<bool> MAIN_AUDIO_PRESERVE_PITCH;
extern const Info<std::string> MAIN_MEMCARD_A_PATH;
extern const Info<std::string> MAIN_MEMCARD_B_PATH;
const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_AGP_CART_A_PATH;
extern const Info<std::string> MAIN_AGP_CART_B_PATH;
const Info<std::string>& GetInfoForAGPCartPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH;
const Info<std::string>& GetInfoForGCIPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
const Info<std::string>& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot);
extern const Info<int> MAIN_MEMORY_CARD_SIZE;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_A;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_B;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_1;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_2;
const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_BBA_MAC;
extern const Info<std::string> MAIN_BBA_XLINK_IP;
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
extern const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION;
extern const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION;
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
const Info<bool>& GetInfoForAdapterRumble(int channel);
const Info<bool>& GetInfoForSimulateKonga(int channel);
extern const Info<bool> MAIN_WII_SD_CARD;
extern const Info<bool> MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC;
extern const Info<u64> MAIN_WII_SD_CARD_FILESIZE;
extern const Info<bool> MAIN_WII_KEYBOARD;
extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
extern const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES;
extern const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER;
extern const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE;
extern const Info<bool> MAIN_MMU;
extern const Info<bool> MAIN_PAUSE_ON_PANIC;
extern const Info<int> MAIN_BB_DUMP_PORT;
extern const Info<bool> MAIN_SYNC_GPU;
extern const Info<int> MAIN_SYNC_GPU_MAX_DISTANCE;
extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_ACCURATE_FMADDS;
extern const Info<bool> MAIN_DISABLE_ICACHE;
extern const Info<float> MAIN_EMULATION_SPEED;
extern const Info<bool> MAIN_PRECISION_FRAME_TIMING;
extern const Info<float> MAIN_OVERCLOCK;
extern const Info<bool> MAIN_OVERCLOCK_ENABLE;
extern const Info<float> MAIN_VI_OVERCLOCK;
extern const Info<bool> MAIN_VI_OVERCLOCK_ENABLE;
extern const Info<bool> MAIN_RAM_OVERRIDE_ENABLE;
extern const Info<u32> MAIN_MEM1_SIZE;
extern const Info<u32> MAIN_MEM2_SIZE;
// Should really be part of System::GFX, but again, we're stuck with past mistakes.
extern const Info<std::string> MAIN_GFX_BACKEND;
extern const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE;
extern const Info<u32> MAIN_ARAM_EXPANSION_SIZE;
enum class GPUDeterminismMode
{
Auto,
Disabled,
// This is currently the only mode. There will probably be at least
// one more at some point.
FakeCompletion,
};
extern const Info<std::string> MAIN_GPU_DETERMINISM_MODE;
GPUDeterminismMode GetGPUDeterminismMode();
extern const Info<std::string> MAIN_PERF_MAP_DIR;
extern const Info<bool> MAIN_CUSTOM_RTC_ENABLE;
extern const Info<u32> MAIN_CUSTOM_RTC_VALUE;
extern const Info<bool> MAIN_AUTO_DISC_CHANGE;
extern const Info<bool> MAIN_ALLOW_SD_WRITES;
extern const Info<bool> MAIN_ENABLE_SAVESTATES;
extern const Info<DiscIO::Region> MAIN_FALLBACK_REGION;
extern const Info<bool> MAIN_REAL_WII_REMOTE_REPEAT_REPORTS;
extern const Info<s32> MAIN_OVERRIDE_BOOT_IOS;
extern const Info<bool> MAIN_WII_IOS_LLE;
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <set>
#include <string>
#include <utility>
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Config/Config.h"
#include "Core/USBUtils.h"
#include "DiscIO/Enums.h"
// DSP Backend Types
#define BACKEND_NULLSOUND _trans("No Audio Output")
#define BACKEND_ALSA "ALSA"
#define BACKEND_CUBEB "Cubeb"
#define BACKEND_OPENAL "OpenAL"
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_OPENSLES "OpenSLES"
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
namespace PowerPC
{
enum class CPUCore;
}
namespace AudioCommon
{
enum class DPL2Quality;
}
namespace ExpansionInterface
{
enum class EXIDeviceType : int;
enum class Slot : int;
} // namespace ExpansionInterface
namespace SerialInterface
{
enum SIDevices : int;
}
namespace HSP
{
enum class HSPDeviceType : int;
}
namespace Config
{
// Main.Core
extern const Info<bool> MAIN_SKIP_IPL;
extern const Info<PowerPC::CPUCore> MAIN_CPU_CORE;
extern const Info<bool> MAIN_JIT_FOLLOW_BRANCH;
extern const Info<bool> MAIN_FASTMEM;
extern const Info<bool> MAIN_PAGE_TABLE_FASTMEM;
extern const Info<bool> MAIN_FASTMEM_ARENA;
extern const Info<bool> MAIN_LARGE_ENTRY_POINTS_MAP;
extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
// Should really be in the DSP section, but we're kind of stuck with bad decisions made in the past.
extern const Info<bool> MAIN_DSP_HLE;
extern const Info<int> MAIN_MAX_FALLBACK;
extern const Info<int> MAIN_TIMING_VARIANCE;
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
extern const Info<bool> MAIN_RUSH_FRAME_PRESENTATION;
extern const Info<bool> MAIN_SMOOTH_EARLY_PRESENTATION;
extern const Info<bool> MAIN_CPU_THREAD;
extern const Info<bool> MAIN_LOAD_GAME_INTO_MEMORY;
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
extern const Info<std::string> MAIN_DEFAULT_ISO;
extern const Info<bool> MAIN_ENABLE_CHEATS;
extern const Info<int> MAIN_GC_LANGUAGE;
extern const Info<bool> MAIN_OVERRIDE_REGION_SETTINGS;
extern const Info<bool> MAIN_DPL2_DECODER;
extern const Info<AudioCommon::DPL2Quality> MAIN_DPL2_QUALITY;
extern const Info<int> MAIN_AUDIO_LATENCY;
extern const Info<int> MAIN_AUDIO_BUFFER_SIZE;
extern const Info<bool> MAIN_AUDIO_FILL_GAPS;
extern const Info<bool> MAIN_AUDIO_PRESERVE_PITCH;
extern const Info<std::string> MAIN_MEMCARD_A_PATH;
extern const Info<std::string> MAIN_MEMCARD_B_PATH;
const Info<std::string>& GetInfoForMemcardPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_AGP_CART_A_PATH;
extern const Info<std::string> MAIN_AGP_CART_B_PATH;
const Info<std::string>& GetInfoForAGPCartPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH;
const Info<std::string>& GetInfoForGCIPath(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
extern const Info<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
const Info<std::string>& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot);
extern const Info<int> MAIN_MEMORY_CARD_SIZE;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_A;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SLOT_B;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_1;
extern const Info<ExpansionInterface::EXIDeviceType> MAIN_SERIAL_PORT_2;
const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInterface::Slot slot);
extern const Info<std::string> MAIN_BBA_MAC;
extern const Info<std::string> MAIN_BBA_XLINK_IP;
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
extern const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION;
extern const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION;
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
const Info<bool>& GetInfoForAdapterRumble(int channel);
const Info<bool>& GetInfoForSimulateKonga(int channel);
extern const Info<bool> MAIN_WII_SD_CARD;
extern const Info<bool> MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC;
extern const Info<u64> MAIN_WII_SD_CARD_FILESIZE;
extern const Info<bool> MAIN_WII_KEYBOARD;
extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
extern const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES;
extern const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER;
extern const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE;
extern const Info<bool> MAIN_MMU;
extern const Info<bool> MAIN_PAUSE_ON_PANIC;
extern const Info<int> MAIN_BB_DUMP_PORT;
extern const Info<bool> MAIN_SYNC_GPU;
extern const Info<int> MAIN_SYNC_GPU_MAX_DISTANCE;
extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_ACCURATE_FMADDS;
extern const Info<bool> MAIN_DISABLE_ICACHE;
extern const Info<float> MAIN_EMULATION_SPEED;
extern const Info<bool> MAIN_PRECISION_FRAME_TIMING;
extern const Info<float> MAIN_OVERCLOCK;
extern const Info<bool> MAIN_OVERCLOCK_ENABLE;
extern const Info<float> MAIN_VI_OVERCLOCK;
extern const Info<bool> MAIN_VI_OVERCLOCK_ENABLE;
extern const Info<bool> MAIN_RAM_OVERRIDE_ENABLE;
extern const Info<u32> MAIN_MEM1_SIZE;
extern const Info<u32> MAIN_MEM2_SIZE;
// Should really be part of System::GFX, but again, we're stuck with past mistakes.
extern const Info<std::string> MAIN_GFX_BACKEND;
extern const Info<HSP::HSPDeviceType> MAIN_HSP_DEVICE;
extern const Info<u32> MAIN_ARAM_EXPANSION_SIZE;
enum class GPUDeterminismMode
{
Auto,
Disabled,
// This is currently the only mode. There will probably be at least
// one more at some point.
FakeCompletion,
};
extern const Info<std::string> MAIN_GPU_DETERMINISM_MODE;
GPUDeterminismMode GetGPUDeterminismMode();
extern const Info<std::string> MAIN_PERF_MAP_DIR;
extern const Info<bool> MAIN_CUSTOM_RTC_ENABLE;
extern const Info<u32> MAIN_CUSTOM_RTC_VALUE;
extern const Info<bool> MAIN_AUTO_DISC_CHANGE;
extern const Info<bool> MAIN_ALLOW_SD_WRITES;
extern const Info<bool> MAIN_ENABLE_SAVESTATES;
extern const Info<DiscIO::Region> MAIN_FALLBACK_REGION;
extern const Info<bool> MAIN_REAL_WII_REMOTE_REPEAT_REPORTS;
extern const Info<s32> MAIN_OVERRIDE_BOOT_IOS;
extern const Info<bool> MAIN_WII_IOS_LLE;
extern const Info<bool> MAIN_WII_STARLET_JIT;
extern const Info<bool> MAIN_WII_STARLET_JIT_MEMORY_PROFILING;
extern const Info<bool> MAIN_WII_LLE_ETHERNET;
extern const Info<std::string> MAIN_WII_NUS_SHOP_URL;
extern const Info<bool> MAIN_WII_WIILINK_ENABLE;
// Main.DSP
extern const Info<bool> MAIN_DSP_THREAD;
extern const Info<bool> MAIN_DSP_CAPTURE_LOG;
extern const Info<bool> MAIN_DSP_JIT;
extern const Info<bool> MAIN_DUMP_AUDIO;
extern const Info<bool> MAIN_DUMP_AUDIO_SILENT;
extern const Info<bool> MAIN_DUMP_UCODE;
extern const Info<std::string> MAIN_AUDIO_BACKEND;
extern const Info<int> MAIN_AUDIO_VOLUME;
extern const Info<bool> MAIN_AUDIO_MUTED;
extern const Info<bool> MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT;
#ifdef _WIN32
extern const Info<std::string> MAIN_WASAPI_DEVICE;
#endif
bool ShouldUseDPL2Decoder();
// Main.Display
extern const Info<std::string> MAIN_FULLSCREEN_DISPLAY_RES;
extern const Info<bool> MAIN_FULLSCREEN;
extern const Info<bool> MAIN_RENDER_TO_MAIN;
extern const Info<int> MAIN_RENDER_WINDOW_XPOS;
extern const Info<int> MAIN_RENDER_WINDOW_YPOS;
extern const Info<int> MAIN_RENDER_WINDOW_WIDTH;
extern const Info<int> MAIN_RENDER_WINDOW_HEIGHT;
extern const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE;
extern const Info<bool> MAIN_KEEP_WINDOW_ON_TOP;
extern const Info<bool> MAIN_DISABLE_SCREENSAVER;
extern const Info<bool> MAIN_TIME_TRACKING;
// Main.General
extern const Info<std::string> MAIN_DUMP_PATH;
extern const Info<std::string> MAIN_LOAD_PATH;
extern const Info<std::string> MAIN_RESOURCEPACK_PATH;
extern const Info<std::string> MAIN_FS_PATH;
extern const Info<std::string> MAIN_WII_NUS_SHOP_URL;
extern const Info<bool> MAIN_WII_WIILINK_ENABLE;
// Main.DSP
extern const Info<bool> MAIN_DSP_THREAD;
extern const Info<bool> MAIN_DSP_CAPTURE_LOG;
extern const Info<bool> MAIN_DSP_JIT;
extern const Info<bool> MAIN_DUMP_AUDIO;
extern const Info<bool> MAIN_DUMP_AUDIO_SILENT;
extern const Info<bool> MAIN_DUMP_UCODE;
extern const Info<std::string> MAIN_AUDIO_BACKEND;
extern const Info<int> MAIN_AUDIO_VOLUME;
extern const Info<bool> MAIN_AUDIO_MUTED;
extern const Info<bool> MAIN_AUDIO_MUTE_ON_DISABLED_SPEED_LIMIT;
#ifdef _WIN32
extern const Info<std::string> MAIN_WASAPI_DEVICE;
#endif
bool ShouldUseDPL2Decoder();
// Main.Display
extern const Info<std::string> MAIN_FULLSCREEN_DISPLAY_RES;
extern const Info<bool> MAIN_FULLSCREEN;
extern const Info<bool> MAIN_RENDER_TO_MAIN;
extern const Info<int> MAIN_RENDER_WINDOW_XPOS;
extern const Info<int> MAIN_RENDER_WINDOW_YPOS;
extern const Info<int> MAIN_RENDER_WINDOW_WIDTH;
extern const Info<int> MAIN_RENDER_WINDOW_HEIGHT;
extern const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE;
extern const Info<bool> MAIN_KEEP_WINDOW_ON_TOP;
extern const Info<bool> MAIN_DISABLE_SCREENSAVER;
extern const Info<bool> MAIN_TIME_TRACKING;
// Main.General
extern const Info<std::string> MAIN_DUMP_PATH;
extern const Info<std::string> MAIN_LOAD_PATH;
extern const Info<std::string> MAIN_RESOURCEPACK_PATH;
extern const Info<std::string> MAIN_FS_PATH;
extern const Info<std::string> MAIN_WII_LLE_DUMP_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_IMAGE_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_SYNC_FOLDER_PATH;
extern const Info<std::string> MAIN_WFS_PATH;
extern const Info<bool> MAIN_SHOW_LAG;
extern const Info<bool> MAIN_SHOW_FRAME_COUNT;
extern const Info<std::string> MAIN_WIRELESS_MAC;
extern const Info<std::string> MAIN_GDB_SOCKET;
extern const Info<int> MAIN_GDB_PORT;
extern const Info<int> MAIN_ISO_PATH_COUNT;
extern const Info<std::string> MAIN_SKYLANDERS_PATH;
std::vector<std::string> GetIsoPaths();
void SetIsoPaths(std::span<const std::string> paths);
// Main.GBA
#ifdef HAS_LIBMGBA
extern const Info<std::string> MAIN_GBA_BIOS_PATH;
extern const std::array<Info<std::string>, 5> MAIN_GBA_ROM_PATHS;
extern const Info<std::string> MAIN_GBA_SAVES_PATH;
extern const Info<bool> MAIN_GBA_SAVES_IN_ROM_PATH;
static constexpr std::size_t GBPLAYER_GBA_INDEX = 4;
#endif
// Main.Network
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_READ;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
extern const Info<bool> MAIN_NETWORK_DUMP_BBA;
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;
extern const Info<int> MAIN_NETWORK_TIMEOUT;
// Main.Interface
extern const Info<bool> MAIN_USE_HIGH_CONTRAST_TOOLTIPS;
extern const Info<bool> MAIN_USE_PANIC_HANDLERS;
extern const Info<bool> MAIN_ABORT_ON_PANIC_ALERT;
extern const Info<bool> MAIN_OSD_MESSAGES;
extern const Info<int> MAIN_OSD_FONT_SIZE;
extern const Info<bool> MAIN_SKIP_NKIT_WARNING;
extern const Info<bool> MAIN_CONFIRM_ON_STOP;
enum class ShowCursor
{
Never,
Constantly,
OnMovement,
};
extern const Info<ShowCursor> MAIN_SHOW_CURSOR;
extern const Info<bool> MAIN_LOCK_CURSOR;
extern const Info<std::string> MAIN_INTERFACE_LANGUAGE;
extern const Info<bool> MAIN_SHOW_ACTIVE_TITLE;
extern const Info<bool> MAIN_USE_BUILT_IN_TITLE_DATABASE;
extern const Info<std::string> MAIN_THEME_NAME;
extern const Info<bool> MAIN_PAUSE_ON_FOCUS_LOST;
extern const Info<bool> MAIN_ENABLE_DEBUGGING;
// Main.Analytics
extern const Info<std::string> MAIN_ANALYTICS_ID;
extern const Info<bool> MAIN_ANALYTICS_ENABLED;
extern const Info<bool> MAIN_ANALYTICS_PERMISSION_ASKED;
// Main.GameList
extern const Info<bool> MAIN_GAMELIST_LIST_DRIVES;
extern const Info<bool> MAIN_GAMELIST_LIST_WAD;
extern const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL;
extern const Info<bool> MAIN_GAMELIST_LIST_WII;
extern const Info<bool> MAIN_GAMELIST_LIST_GC;
extern const Info<bool> MAIN_GAMELIST_LIST_TRI;
extern const Info<bool> MAIN_GAMELIST_LIST_JPN;
extern const Info<bool> MAIN_GAMELIST_LIST_PAL;
extern const Info<bool> MAIN_GAMELIST_LIST_USA;
extern const Info<bool> MAIN_GAMELIST_LIST_AUSTRALIA;
extern const Info<bool> MAIN_GAMELIST_LIST_FRANCE;
extern const Info<bool> MAIN_GAMELIST_LIST_GERMANY;
extern const Info<bool> MAIN_GAMELIST_LIST_ITALY;
extern const Info<bool> MAIN_GAMELIST_LIST_KOREA;
extern const Info<bool> MAIN_GAMELIST_LIST_NETHERLANDS;
extern const Info<bool> MAIN_GAMELIST_LIST_RUSSIA;
extern const Info<bool> MAIN_GAMELIST_LIST_SPAIN;
extern const Info<bool> MAIN_GAMELIST_LIST_TAIWAN;
extern const Info<bool> MAIN_GAMELIST_LIST_WORLD;
extern const Info<bool> MAIN_GAMELIST_LIST_UNKNOWN;
extern const Info<int> MAIN_GAMELIST_LIST_SORT;
extern const Info<int> MAIN_GAMELIST_LIST_SORT_SECONDARY;
extern const Info<bool> MAIN_GAMELIST_COLUMN_PLATFORM;
extern const Info<bool> MAIN_GAMELIST_COLUMN_DESCRIPTION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BANNER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TITLE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_MAKER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_NAME;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_PATH;
extern const Info<bool> MAIN_GAMELIST_COLUMN_GAME_ID;
extern const Info<bool> MAIN_GAMELIST_COLUMN_REGION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;
// Main.FifoPlayer
extern const Info<bool> MAIN_FIFOPLAYER_LOOP_REPLAY;
extern const Info<bool> MAIN_FIFOPLAYER_EARLY_MEMORY_UPDATES;
// Main.AutoUpdate
extern const Info<std::string> MAIN_AUTOUPDATE_UPDATE_TRACK;
extern const Info<std::string> MAIN_AUTOUPDATE_HASH_OVERRIDE;
// Main.Movie
extern const Info<bool> MAIN_MOVIE_PAUSE_MOVIE;
extern const Info<std::string> MAIN_MOVIE_MOVIE_AUTHOR;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES_SILENT;
extern const Info<bool> MAIN_MOVIE_SHOW_INPUT_DISPLAY;
extern const Info<bool> MAIN_MOVIE_SHOW_RTC;
extern const Info<bool> MAIN_MOVIE_SHOW_RERECORD;
extern const Info<bool> MAIN_MOVIE_SHOW_OSD;
// Main.Input
extern const Info<bool> MAIN_INPUT_BACKGROUND_INPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS;
// Main.Debug
extern const Info<bool> MAIN_DEBUG_JIT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_FLOATING_POINT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_INTEGER_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING;
// Main.BluetoothPassthrough
extern const Info<bool> MAIN_BLUETOOTH_PASSTHROUGH_ENABLED;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_VID;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_PID;
extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS;
// Main.USBPassthrough
extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII;
extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES;
std::set<USBUtils::DeviceInfo> GetUSBDeviceWhitelist();
void SetUSBDeviceWhitelist(const std::set<USBUtils::DeviceInfo>& devices);
// Main.EmulatedUSBDevices
extern const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL;
extern const Info<bool> MAIN_EMULATE_INFINITY_BASE;
extern const Info<bool> MAIN_EMULATE_WII_SPEAK;
extern const Info<std::string> MAIN_WII_SPEAK_MICROPHONE;
extern const Info<bool> MAIN_WII_SPEAK_MUTED;
extern const Info<s16> MAIN_WII_SPEAK_VOLUME_MODIFIER;
static constexpr std::size_t EMULATED_LOGITECH_MIC_COUNT = 4;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_EMULATE_LOGITECH_MIC;
extern const std::array<Info<std::string>, EMULATED_LOGITECH_MIC_COUNT>
MAIN_LOGITECH_MIC_MICROPHONE;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_MUTED;
extern const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUME_MODIFIER;
extern const Info<std::string> MAIN_TRIFORCE_IP_REDIRECTIONS;
// Main.WiimoteAudioRouting
static constexpr std::size_t WIIMOTE_SPEAKER_COUNT = 4;
extern const Info<bool> MAIN_WIIMOTE_AUDIO_ROUTING_ENABLED;
extern const std::array<Info<bool>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_ENABLED;
extern const std::array<Info<std::string>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_DEVICE;
// GameCube path utility functions
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
DiscIO::Region ToGameCubeRegion(DiscIO::Region region);
// The region argument must be valid for GameCube (i.e. must not be NTSC-K)
enum class RegionDirectoryStyle
{
Legacy,
Modern,
};
const char* GetDirectoryForRegion(DiscIO::Region region,
RegionDirectoryStyle style = RegionDirectoryStyle::Legacy);
std::string GetBootROMPath(const std::string& region_directory);
// Builds the memory card according to the configuration with the given region and size. If the
// given region is std::nullopt, the region in the configured path is used if there is one, or the
// fallback region otherwise.
std::string GetMemcardPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region,
u16 size_mb = 0x80);
std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region, u16 size_mb = 0x80);
bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot);
std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region);
std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region);
bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot);
bool AreCheatsEnabled();
bool IsDebuggingEnabled();
} // namespace Config
extern const Info<std::string> MAIN_WII_STARLET_BOOTSTRAP_ELF;
extern const Info<std::string> MAIN_WII_SD_CARD_IMAGE_PATH;
extern const Info<std::string> MAIN_WII_SD_CARD_SYNC_FOLDER_PATH;
extern const Info<std::string> MAIN_WFS_PATH;
extern const Info<bool> MAIN_SHOW_LAG;
extern const Info<bool> MAIN_SHOW_FRAME_COUNT;
extern const Info<std::string> MAIN_WIRELESS_MAC;
extern const Info<std::string> MAIN_GDB_SOCKET;
extern const Info<int> MAIN_GDB_PORT;
extern const Info<int> MAIN_ISO_PATH_COUNT;
extern const Info<std::string> MAIN_SKYLANDERS_PATH;
std::vector<std::string> GetIsoPaths();
void SetIsoPaths(std::span<const std::string> paths);
// Main.GBA
#ifdef HAS_LIBMGBA
extern const Info<std::string> MAIN_GBA_BIOS_PATH;
extern const std::array<Info<std::string>, 5> MAIN_GBA_ROM_PATHS;
extern const Info<std::string> MAIN_GBA_SAVES_PATH;
extern const Info<bool> MAIN_GBA_SAVES_IN_ROM_PATH;
static constexpr std::size_t GBPLAYER_GBA_INDEX = 4;
#endif
// Main.Network
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_READ;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
extern const Info<bool> MAIN_NETWORK_DUMP_BBA;
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;
extern const Info<int> MAIN_NETWORK_TIMEOUT;
// Main.Interface
extern const Info<bool> MAIN_USE_HIGH_CONTRAST_TOOLTIPS;
extern const Info<bool> MAIN_USE_PANIC_HANDLERS;
extern const Info<bool> MAIN_ABORT_ON_PANIC_ALERT;
extern const Info<bool> MAIN_OSD_MESSAGES;
extern const Info<int> MAIN_OSD_FONT_SIZE;
extern const Info<bool> MAIN_SKIP_NKIT_WARNING;
extern const Info<bool> MAIN_CONFIRM_ON_STOP;
enum class ShowCursor
{
Never,
Constantly,
OnMovement,
};
extern const Info<ShowCursor> MAIN_SHOW_CURSOR;
extern const Info<bool> MAIN_LOCK_CURSOR;
extern const Info<std::string> MAIN_INTERFACE_LANGUAGE;
extern const Info<bool> MAIN_SHOW_ACTIVE_TITLE;
extern const Info<bool> MAIN_USE_BUILT_IN_TITLE_DATABASE;
extern const Info<std::string> MAIN_THEME_NAME;
extern const Info<bool> MAIN_PAUSE_ON_FOCUS_LOST;
extern const Info<bool> MAIN_ENABLE_DEBUGGING;
// Main.Analytics
extern const Info<std::string> MAIN_ANALYTICS_ID;
extern const Info<bool> MAIN_ANALYTICS_ENABLED;
extern const Info<bool> MAIN_ANALYTICS_PERMISSION_ASKED;
// Main.GameList
extern const Info<bool> MAIN_GAMELIST_LIST_DRIVES;
extern const Info<bool> MAIN_GAMELIST_LIST_WAD;
extern const Info<bool> MAIN_GAMELIST_LIST_ELF_DOL;
extern const Info<bool> MAIN_GAMELIST_LIST_WII;
extern const Info<bool> MAIN_GAMELIST_LIST_GC;
extern const Info<bool> MAIN_GAMELIST_LIST_TRI;
extern const Info<bool> MAIN_GAMELIST_LIST_JPN;
extern const Info<bool> MAIN_GAMELIST_LIST_PAL;
extern const Info<bool> MAIN_GAMELIST_LIST_USA;
extern const Info<bool> MAIN_GAMELIST_LIST_AUSTRALIA;
extern const Info<bool> MAIN_GAMELIST_LIST_FRANCE;
extern const Info<bool> MAIN_GAMELIST_LIST_GERMANY;
extern const Info<bool> MAIN_GAMELIST_LIST_ITALY;
extern const Info<bool> MAIN_GAMELIST_LIST_KOREA;
extern const Info<bool> MAIN_GAMELIST_LIST_NETHERLANDS;
extern const Info<bool> MAIN_GAMELIST_LIST_RUSSIA;
extern const Info<bool> MAIN_GAMELIST_LIST_SPAIN;
extern const Info<bool> MAIN_GAMELIST_LIST_TAIWAN;
extern const Info<bool> MAIN_GAMELIST_LIST_WORLD;
extern const Info<bool> MAIN_GAMELIST_LIST_UNKNOWN;
extern const Info<int> MAIN_GAMELIST_LIST_SORT;
extern const Info<int> MAIN_GAMELIST_LIST_SORT_SECONDARY;
extern const Info<bool> MAIN_GAMELIST_COLUMN_PLATFORM;
extern const Info<bool> MAIN_GAMELIST_COLUMN_DESCRIPTION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BANNER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TITLE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_MAKER;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_NAME;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_PATH;
extern const Info<bool> MAIN_GAMELIST_COLUMN_GAME_ID;
extern const Info<bool> MAIN_GAMELIST_COLUMN_REGION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;
// Main.FifoPlayer
extern const Info<bool> MAIN_FIFOPLAYER_LOOP_REPLAY;
extern const Info<bool> MAIN_FIFOPLAYER_EARLY_MEMORY_UPDATES;
// Main.AutoUpdate
extern const Info<std::string> MAIN_AUTOUPDATE_UPDATE_TRACK;
extern const Info<std::string> MAIN_AUTOUPDATE_HASH_OVERRIDE;
// Main.Movie
extern const Info<bool> MAIN_MOVIE_PAUSE_MOVIE;
extern const Info<std::string> MAIN_MOVIE_MOVIE_AUTHOR;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES;
extern const Info<bool> MAIN_MOVIE_DUMP_FRAMES_SILENT;
extern const Info<bool> MAIN_MOVIE_SHOW_INPUT_DISPLAY;
extern const Info<bool> MAIN_MOVIE_SHOW_RTC;
extern const Info<bool> MAIN_MOVIE_SHOW_RERECORD;
extern const Info<bool> MAIN_MOVIE_SHOW_OSD;
// Main.Input
extern const Info<bool> MAIN_INPUT_BACKGROUND_INPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS;
extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS;
// Main.Debug
extern const Info<bool> MAIN_DEBUG_JIT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LXZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LWZ_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_LBZX_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_FLOATING_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_LOAD_STORE_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_FLOATING_POINT_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_INTEGER_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_PAIRED_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF;
extern const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING;
// Main.BluetoothPassthrough
extern const Info<bool> MAIN_BLUETOOTH_PASSTHROUGH_ENABLED;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_VID;
extern const Info<int> MAIN_BLUETOOTH_PASSTHROUGH_PID;
extern const Info<std::string> MAIN_BLUETOOTH_PASSTHROUGH_LINK_KEYS;
// Main.USBPassthrough
extern const Info<bool> MAIN_USB_PASSTHROUGH_DISGUISE_PLAYSTATION_AS_WII;
extern const Info<std::string> MAIN_USB_PASSTHROUGH_DEVICES;
std::set<USBUtils::DeviceInfo> GetUSBDeviceWhitelist();
void SetUSBDeviceWhitelist(const std::set<USBUtils::DeviceInfo>& devices);
// Main.EmulatedUSBDevices
extern const Info<bool> MAIN_EMULATE_SKYLANDER_PORTAL;
extern const Info<bool> MAIN_EMULATE_INFINITY_BASE;
extern const Info<bool> MAIN_EMULATE_WII_SPEAK;
extern const Info<std::string> MAIN_WII_SPEAK_MICROPHONE;
extern const Info<bool> MAIN_WII_SPEAK_MUTED;
extern const Info<s16> MAIN_WII_SPEAK_VOLUME_MODIFIER;
static constexpr std::size_t EMULATED_LOGITECH_MIC_COUNT = 4;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_EMULATE_LOGITECH_MIC;
extern const std::array<Info<std::string>, EMULATED_LOGITECH_MIC_COUNT>
MAIN_LOGITECH_MIC_MICROPHONE;
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_MUTED;
extern const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUME_MODIFIER;
extern const Info<std::string> MAIN_TRIFORCE_IP_REDIRECTIONS;
// Main.WiimoteAudioRouting
static constexpr std::size_t WIIMOTE_SPEAKER_COUNT = 4;
extern const Info<bool> MAIN_WIIMOTE_AUDIO_ROUTING_ENABLED;
extern const std::array<Info<bool>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_ENABLED;
extern const std::array<Info<std::string>, WIIMOTE_SPEAKER_COUNT> MAIN_WIIMOTE_AUDIO_OUTPUT_DEVICE;
// GameCube path utility functions
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
DiscIO::Region ToGameCubeRegion(DiscIO::Region region);
// The region argument must be valid for GameCube (i.e. must not be NTSC-K)
enum class RegionDirectoryStyle
{
Legacy,
Modern,
};
const char* GetDirectoryForRegion(DiscIO::Region region,
RegionDirectoryStyle style = RegionDirectoryStyle::Legacy);
std::string GetBootROMPath(const std::string& region_directory);
// Builds the memory card according to the configuration with the given region and size. If the
// given region is std::nullopt, the region in the configured path is used if there is one, or the
// fallback region otherwise.
std::string GetMemcardPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region,
u16 size_mb = 0x80);
std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region, u16 size_mb = 0x80);
bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot);
std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional<DiscIO::Region> region);
std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot,
std::optional<DiscIO::Region> region);
bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot);
bool AreCheatsEnabled();
bool IsDebuggingEnabled();
} // namespace Config
File diff suppressed because it is too large. Load diff
+159 -159
View File
@@ -1,159 +1,159 @@
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/HW.h"
#include <memory>
#include <string>
#include "Common/ChunkFile.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GPFifo.h"
#include "Core/HW/HSP/HSP.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/MemoryInterface.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/Starlet/Starlet.h"
#include "Core/State.h"
#include "Core/System.h"
namespace HW
{
void Init(Core::System& system, const Sram* override_sram)
{
system.GetCoreTiming().Init();
system.GetSystemTimers().PreInit();
State::Init(system);
// Init the whole Hardware
system.GetAudioInterface().Init();
system.GetVideoInterface().Init();
system.GetSerialInterface().Init();
system.GetProcessorInterface().Init();
system.GetExpansionInterface().Init(override_sram); // Needs to be initialized before Memory
system.GetHSP().Init();
system.GetMemory().Init(); // Needs to be initialized before AddressSpace
AddressSpace::Init();
system.GetMemoryInterface().Init();
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
system.GetDVDInterface().Init();
system.GetGPFifo().Init();
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
system.GetSystemTimers().Init();
if (system.IsWii())
{
system.GetWiiIPC().Init();
if (Config::Get(Config::MAIN_WII_IOS_LLE))
{
auto starlet = std::make_unique<IOS::LLE::Starlet>(system);
std::string error;
if (starlet->Init(Config::Get(Config::MAIN_WII_LLE_DUMP_PATH), &error))
{
system.SetStarlet(std::move(starlet));
system.GetWiiIPC().HoldBroadwayForStarletBoot();
}
else
{
PanicAlertFmtT("Could not start Wii IOS LLE: {0}", error);
Core::SetState(system, Core::State::Stopping);
return;
}
}
else
{
IOS::HLE::Init(system); // Depends on Memory
}
}
system.GetMemory().InitMMIO(system);
}
void Shutdown(Core::System& system)
{
// IOS should always be shut down regardless of IsWii because it can be running in GC mode (MIOS).
if (system.GetStarlet())
{
system.GetStarlet()->Shutdown();
system.SetStarlet(nullptr);
}
if (system.GetIOS())
IOS::HLE::Shutdown(system); // Depends on Memory
system.GetWiiIPC().Shutdown();
system.GetSystemTimers().Shutdown();
system.GetCPU().Shutdown();
system.GetDVDInterface().Shutdown();
system.GetDSP().Shutdown();
system.GetMemoryInterface().Shutdown();
AddressSpace::Shutdown();
system.GetMemory().Shutdown();
system.GetHSP().Shutdown();
system.GetExpansionInterface().Shutdown();
system.GetSerialInterface().Shutdown();
system.GetAudioInterface().Shutdown();
State::Shutdown();
system.GetCoreTiming().Shutdown();
}
void DoState(Core::System& system, PointerWrap& p)
{
system.GetMemory().DoState(p);
p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface");
system.GetVideoInterface().DoState(p);
p.DoMarker("VideoInterface");
system.GetSerialInterface().DoState(p);
p.DoMarker("SerialInterface");
system.GetProcessorInterface().DoState(p);
p.DoMarker("ProcessorInterface");
system.GetDSP().DoState(p);
p.DoMarker("DSP");
system.GetDVDInterface().DoState(p);
p.DoMarker("DVDInterface");
system.GetGPFifo().DoState(p);
p.DoMarker("GPFifo");
system.GetExpansionInterface().DoState(p);
p.DoMarker("ExpansionInterface");
system.GetAudioInterface().DoState(p);
p.DoMarker("AudioInterface");
system.GetHSP().DoState(p);
p.DoMarker("HSP");
if (system.IsWii())
{
system.GetWiiIPC().DoState(p);
p.DoMarker("IOS");
if (system.GetStarlet())
{
system.GetStarlet()->DoState(p);
p.DoMarker("IOS::LLE");
}
else if (system.GetIOS())
{
system.GetIOS()->DoState(p);
p.DoMarker("IOS::HLE");
}
}
p.DoMarker("WIIHW");
}
} // namespace HW
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/HW.h"
#include <memory>
#include <string>
#include "Common/ChunkFile.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GPFifo.h"
#include "Core/HW/HSP/HSP.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/MemoryInterface.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/Starlet/Starlet.h"
#include "Core/State.h"
#include "Core/System.h"
namespace HW
{
void Init(Core::System& system, const Sram* override_sram)
{
system.GetCoreTiming().Init();
system.GetSystemTimers().PreInit();
State::Init(system);
// Init the whole Hardware
system.GetAudioInterface().Init();
system.GetVideoInterface().Init();
system.GetSerialInterface().Init();
system.GetProcessorInterface().Init();
system.GetExpansionInterface().Init(override_sram); // Needs to be initialized before Memory
system.GetHSP().Init();
system.GetMemory().Init(); // Needs to be initialized before AddressSpace
AddressSpace::Init();
system.GetMemoryInterface().Init();
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
system.GetDVDInterface().Init();
system.GetGPFifo().Init();
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
system.GetSystemTimers().Init();
if (system.IsWii())
{
system.GetWiiIPC().Init();
if (Config::Get(Config::MAIN_WII_IOS_LLE))
{
auto starlet = IOS::LLE::Starlet::Create(system);
std::string error;
if (starlet->Init(Config::Get(Config::MAIN_WII_LLE_DUMP_PATH), &error))
{
system.SetStarlet(std::move(starlet));
system.GetWiiIPC().HoldBroadwayForStarletBoot();
}
else
{
PanicAlertFmtT("Could not start Wii IOS LLE: {0}", error);
Core::SetState(system, Core::State::Stopping);
return;
}
}
else
{
IOS::HLE::Init(system); // Depends on Memory
}
}
system.GetMemory().InitMMIO(system);
}
void Shutdown(Core::System& system)
{
// IOS should always be shut down regardless of IsWii because it can be running in GC mode (MIOS).
if (system.GetStarlet())
{
system.GetStarlet()->Shutdown();
system.SetStarlet(nullptr);
}
if (system.GetIOS())
IOS::HLE::Shutdown(system); // Depends on Memory
system.GetWiiIPC().Shutdown();
system.GetSystemTimers().Shutdown();
system.GetCPU().Shutdown();
system.GetDVDInterface().Shutdown();
system.GetDSP().Shutdown();
system.GetMemoryInterface().Shutdown();
AddressSpace::Shutdown();
system.GetMemory().Shutdown();
system.GetHSP().Shutdown();
system.GetExpansionInterface().Shutdown();
system.GetSerialInterface().Shutdown();
system.GetAudioInterface().Shutdown();
State::Shutdown();
system.GetCoreTiming().Shutdown();
}
void DoState(Core::System& system, PointerWrap& p)
{
system.GetMemory().DoState(p);
p.DoMarker("Memory");
system.GetMemoryInterface().DoState(p);
p.DoMarker("MemoryInterface");
system.GetVideoInterface().DoState(p);
p.DoMarker("VideoInterface");
system.GetSerialInterface().DoState(p);
p.DoMarker("SerialInterface");
system.GetProcessorInterface().DoState(p);
p.DoMarker("ProcessorInterface");
system.GetDSP().DoState(p);
p.DoMarker("DSP");
system.GetDVDInterface().DoState(p);
p.DoMarker("DVDInterface");
system.GetGPFifo().DoState(p);
p.DoMarker("GPFifo");
system.GetExpansionInterface().DoState(p);
p.DoMarker("ExpansionInterface");
system.GetAudioInterface().DoState(p);
p.DoMarker("AudioInterface");
system.GetHSP().DoState(p);
p.DoMarker("HSP");
if (system.IsWii())
{
system.GetWiiIPC().DoState(p);
p.DoMarker("IOS");
if (system.GetStarlet())
{
system.GetStarlet()->DoState(p);
p.DoMarker("IOS::LLE");
}
else if (system.GetIOS())
{
system.GetIOS()->DoState(p);
p.DoMarker("IOS::HLE");
}
}
p.DoMarker("WIIHW");
}
} // namespace HW
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,222 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/IOS/Starlet/BootstrapELF.h"
#include <algorithm>
#include <array>
#include <optional>
#include <string_view>
#include "Core/IOS/Starlet/ARMCore.h"
namespace IOS::LLE
{
namespace
{
constexpr u32 TEXT_BASE = 0xffff0000;
constexpr u64 TEXT_END = 0x1'0000'0000ULL;
constexpr u32 DATA_BASE = 0xfffe0000;
constexpr u32 DATA_END = 0xfffe4000;
// Revolution IOS Phase 06H: MEM2, for the optional disposable-image text segment.
constexpr u32 MEM2_BASE = 0x10000000;
constexpr u64 MEM2_END = 0x14000000ULL;
constexpr u32 TRACE_SIZE = 1032;
constexpr u32 ELF_HEADER_SIZE = 52;
constexpr u32 PROGRAM_HEADER_SIZE = 32;
constexpr u32 SECTION_HEADER_SIZE = 40;
constexpr u32 SYMBOL_SIZE = 16;
struct Segment
{
u32 offset = 0;
u32 address = 0;
u32 file_size = 0;
u32 memory_size = 0;
};
bool Fits(std::span<const u8> image, u64 offset, u64 size)
{
return offset <= image.size() && size <= image.size() - offset;
}
u16 Read16(std::span<const u8> image, size_t offset)
{
return static_cast<u16>((static_cast<u16>(image[offset]) << 8) | image[offset + 1]);
}
u32 Read32(std::span<const u8> image, size_t offset)
{
return (static_cast<u32>(image[offset]) << 24) | (static_cast<u32>(image[offset + 1]) << 16) |
(static_cast<u32>(image[offset + 2]) << 8) | image[offset + 3];
}
bool Fail(std::string* error, std::string_view reason)
{
if (error)
*error = reason;
return false;
}
std::optional<std::string_view> SymbolName(std::span<const u8> image, u32 strings_offset,
u32 strings_size, u32 name_offset)
{
if (name_offset >= strings_size)
return std::nullopt;
const size_t start = static_cast<size_t>(strings_offset) + name_offset;
const auto first = image.begin() + start;
const auto last = image.begin() + strings_offset + strings_size;
const auto end = std::find(first, last, 0);
if (end == last)
return std::nullopt;
return std::string_view(reinterpret_cast<const char*>(&*first), static_cast<size_t>(end - first));
}
} // namespace
bool LoadBootstrapELF(std::span<const u8> image, ARMBus& bus, BootstrapELFInfo* info,
std::string* error, bool allow_mem2_text)
{
if (!info)
return Fail(error, "missing bootstrap ELF result");
if (!Fits(image, 0, ELF_HEADER_SIZE) || image[0] != 0x7f || image[1] != 'E' || image[2] != 'L' ||
image[3] != 'F' || image[4] != 1 || image[5] != 2 || image[6] != 1 ||
Read16(image, 16) != 2 || Read16(image, 18) != 40 || Read32(image, 20) != 1 ||
Read16(image, 40) != ELF_HEADER_SIZE)
{
return Fail(error, "expected ELF32 big-endian ARM executable");
}
const u32 entry = Read32(image, 24);
const u32 program_offset = Read32(image, 28);
const u32 section_offset = Read32(image, 32);
const u16 program_size = Read16(image, 42);
const u16 program_count = Read16(image, 44);
const u16 section_size = Read16(image, 46);
const u16 section_count = Read16(image, 48);
const bool program_count_valid = program_count == 2 || (allow_mem2_text && program_count == 3);
if (entry != TEXT_BASE || program_size != PROGRAM_HEADER_SIZE || !program_count_valid ||
section_size != SECTION_HEADER_SIZE || section_count == 0 ||
!Fits(image, program_offset, static_cast<u64>(program_size) * program_count) ||
!Fits(image, section_offset, static_cast<u64>(section_size) * section_count))
{
return Fail(error, "bootstrap ELF has an unsupported entry or header table");
}
std::array<Segment, 3> storage;
const std::span<Segment> segments(storage.data(), program_count);
u32 text_count = 0;
u32 data_count = 0;
u32 mem2_count = 0;
for (u32 index = 0; index < segments.size(); ++index)
{
const size_t offset = program_offset + index * PROGRAM_HEADER_SIZE;
const u32 file_offset = Read32(image, offset + 4);
const u32 address = Read32(image, offset + 8);
const u32 physical_address = Read32(image, offset + 12);
const u32 file_size = Read32(image, offset + 16);
const u32 memory_size = Read32(image, offset + 20);
const u32 flags = Read32(image, offset + 24);
const bool text =
address == TEXT_BASE && flags == 5 && static_cast<u64>(address) + memory_size <= TEXT_END;
const bool data =
address == DATA_BASE && flags == 6 && static_cast<u64>(address) + memory_size <= DATA_END;
const bool mem2 = allow_mem2_text && flags == 5 && address >= MEM2_BASE &&
static_cast<u64>(address) + memory_size <= MEM2_END;
if (Read32(image, offset) != 1 || address != physical_address || file_size == 0 ||
memory_size < file_size || !Fits(image, file_offset, file_size) ||
(!text && !data && !mem2))
{
return Fail(error, "bootstrap ELF has an invalid SRAM load segment");
}
text_count += text;
data_count += data;
mem2_count += mem2;
segments[index] = {file_offset, address, file_size, memory_size};
}
if (text_count != 1 || data_count != 1 || mem2_count != program_count - 2u)
return Fail(error, "bootstrap ELF must have one text and one data segment");
BootstrapELFInfo parsed{entry};
bool found_trace = false;
bool found_fault_code = false;
bool found_fault_lr = false;
bool found_fault_spsr = false;
for (u32 index = 0; index < section_count; ++index)
{
const size_t offset = section_offset + index * SECTION_HEADER_SIZE;
if (Read32(image, offset + 4) != 2)
continue;
const u32 symbols_offset = Read32(image, offset + 16);
const u32 symbols_size = Read32(image, offset + 20);
const u32 strings_index = Read32(image, offset + 24);
const u32 entry_size = Read32(image, offset + 36);
if (entry_size != SYMBOL_SIZE || symbols_size % SYMBOL_SIZE != 0 ||
!Fits(image, symbols_offset, symbols_size) || strings_index >= section_count)
{
return Fail(error, "bootstrap ELF has an invalid symbol table");
}
const size_t strings_header = section_offset + strings_index * SECTION_HEADER_SIZE;
const u32 strings_offset = Read32(image, strings_header + 16);
const u32 strings_size = Read32(image, strings_header + 20);
if (Read32(image, strings_header + 4) != 3 || !Fits(image, strings_offset, strings_size))
{
return Fail(error, "bootstrap ELF has an invalid symbol string table");
}
for (u32 symbol = 0; symbol < symbols_size / SYMBOL_SIZE; ++symbol)
{
const size_t symbol_offset = symbols_offset + symbol * SYMBOL_SIZE;
const auto name =
SymbolName(image, strings_offset, strings_size, Read32(image, symbol_offset));
if (!name)
return Fail(error, "bootstrap ELF has an invalid symbol name");
const u32 address = Read32(image, symbol_offset + 4);
if (*name == "REV_TRACE")
{
if (Read32(image, symbol_offset + 8) != TRACE_SIZE)
return Fail(error, "bootstrap ELF trace layout does not match the monitor");
parsed.trace = address;
found_trace = true;
}
else if (*name == "__fault_code")
{
parsed.fault_code = address;
found_fault_code = true;
}
else if (*name == "__fault_lr")
{
parsed.fault_lr = address;
found_fault_lr = true;
}
else if (*name == "__fault_spsr")
{
parsed.fault_spsr = address;
found_fault_spsr = true;
}
}
}
const auto data_segment =
std::find_if(segments.begin(), segments.end(),
[](const Segment& segment) { return segment.address == DATA_BASE; });
const auto in_data = [data_segment](u32 address, u32 size) {
return address >= data_segment->address &&
static_cast<u64>(address) + size <=
static_cast<u64>(data_segment->address) + data_segment->memory_size;
};
if (!found_trace || !found_fault_code || !found_fault_lr || !found_fault_spsr ||
!in_data(parsed.trace, TRACE_SIZE) || !in_data(parsed.fault_code, 4) ||
!in_data(parsed.fault_lr, 4) || !in_data(parsed.fault_spsr, 4))
{
return Fail(error, "bootstrap ELF is missing the trace or fault symbols");
}
for (const Segment& segment : segments)
{
for (u32 index = 0; index < segment.file_size; ++index)
bus.Write8(segment.address + index, image[segment.offset + index]);
for (u32 index = segment.file_size; index < segment.memory_size; ++index)
bus.Write8(segment.address + index, 0);
}
*info = parsed;
return true;
}
} // namespace IOS::LLE
@@ -0,0 +1,31 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <string>
#include "Common/CommonTypes.h"
namespace IOS::LLE
{
class ARMBus;
struct BootstrapELFInfo
{
u32 entry = 0;
u32 trace = 0;
u32 fault_code = 0;
u32 fault_lr = 0;
u32 fault_spsr = 0;
};
// Load the Phase 01 ELF into a bus already configured with the high SRAM mapping.
// All file and address validation completes before the first bus write.
// Revolution IOS Phase 06H: `allow_mem2_text` (set only with the emulator-only disposable
// writable NAND) additionally accepts one read/execute segment wholly inside MEM2. The SRAM
// text and data segments stay mandatory; without the flag the rules are unchanged.
bool LoadBootstrapELF(std::span<const u8> image, ARMBus& bus, BootstrapELFInfo* info,
std::string* error, bool allow_mem2_text = false);
} // namespace IOS::LLE
+188 -2
View File
@@ -4,18 +4,24 @@
#include "Core/IOS/Starlet/Starlet.h"
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <string_view>
#include <vector>
#include "Common/ChunkFile.h"
#include "Common/GekkoDisassembler.h"
#include "Common/IOFile.h"
#include "Common/Logging/Log.h"
#include "Common/PerformanceTrace.h"
#include "Common/Timer.h"
#include "Core/Config/MainSettings.h"
#include "Core/CoreTiming.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/WII_IPC.h"
#include "Core/IOS/Starlet/ARMCore.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
#include "Core/IOS/Starlet/StarletMemory.h"
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h"
@@ -23,6 +29,11 @@
namespace IOS::LLE
{
std::unique_ptr<Starlet> Starlet::Create(Core::System& system)
{
return std::make_unique<Starlet>(system);
}
Starlet::Starlet(Core::System& system) : m_system(system)
{
}
@@ -37,11 +48,87 @@ bool Starlet::Init(const std::string& dump_directory, std::string* error)
// Allocate the concrete bus here; its device-state members are part of the
// allocation size and must stay in lockstep with StarletMemory's definition.
m_memory = std::make_unique<StarletMemory>(m_system);
if (!m_memory->Init(dump_directory, error))
const std::string bootstrap_path = Config::Get(Config::MAIN_WII_STARLET_BOOTSTRAP_ELF);
if (bootstrap_path.empty() && !m_memory->Init(dump_directory, error))
{
m_memory.reset();
return false;
}
if (!bootstrap_path.empty())
{
File::IOFile file(bootstrap_path, "rb");
const u64 size = file ? file.GetSize() : 0;
if (size == 0 || size > 16 * 1024 * 1024)
{
if (error)
*error = "WiiStarletBootstrapELF must name a readable ELF of at most 16 MiB";
m_memory.reset();
return false;
}
std::vector<u8> image(static_cast<size_t>(size));
if (!file.ReadBytes(image.data(), image.size()))
{
if (error)
*error = "could not read WiiStarletBootstrapELF";
m_memory.reset();
return false;
}
m_memory->PrepareBootstrap();
// Revolution IOS Phase 06H, emulator only: a disposable writable NAND, and with it the
// MEM2 text segment of the disposable-image kernel. Nothing else accepts either.
const char* disposable_source = std::getenv("REV_BOOTSTRAP_NAND_DISPOSABLE");
const bool disposable = disposable_source && *disposable_source;
BootstrapELFInfo info;
if (!LoadBootstrapELF(image, *m_memory, &info, error, disposable))
{
m_memory.reset();
return false;
}
m_bootstrap = info;
INFO_LOG_FMT(IOS, "Starlet bootstrap ELF loaded: {} entry={:#010x} trace={:#010x}",
bootstrap_path, info.entry, info.trace);
if (disposable)
{
const char* read_only_path = std::getenv("REV_BOOTSTRAP_NAND");
const char* copy_path = std::getenv("REV_BOOTSTRAP_NAND_COPY");
if ((read_only_path && *read_only_path) || !copy_path || !*copy_path)
{
if (error)
*error = "REV_BOOTSTRAP_NAND_DISPOSABLE needs REV_BOOTSTRAP_NAND_COPY and excludes "
"REV_BOOTSTRAP_NAND";
m_memory.reset();
return false;
}
if (!m_memory->AttachDisposableNAND(disposable_source, copy_path, error))
{
m_memory.reset();
return false;
}
WARN_LOG_FMT(IOS, "Starlet bootstrap NAND DISPOSABLE: source {} (read-only), copy {}",
disposable_source, copy_path);
}
// Revolution IOS Phase 04B: optionally expose a raw NAND image, strictly read-only, so a
// bootstrap ELF can validate its NAND driver. Program and erase always fail in this mode.
if (const char* nand_path = std::getenv("REV_BOOTSTRAP_NAND"); nand_path && *nand_path)
{
if (!m_memory->AttachReadOnlyNAND(nand_path, error))
{
m_memory.reset();
return false;
}
INFO_LOG_FMT(IOS, "Starlet bootstrap NAND attached read-only: {}", nand_path);
}
// Revolution IOS Phase 04C: optional keys.bin so OTP reads match the attached dump.
if (const char* keys_path = std::getenv("REV_BOOTSTRAP_KEYS"); keys_path && *keys_path)
{
if (!m_memory->AttachKeys(keys_path, error))
{
m_memory.reset();
return false;
}
INFO_LOG_FMT(IOS, "Starlet bootstrap keys attached");
}
}
// ARMCore owns generation-tagged software TLB and instruction caches, so
// construct it only after the complete physical bus exists.
m_core = std::make_unique<ARMCore>(*m_memory,
@@ -51,7 +138,9 @@ bool Starlet::Init(const std::string& dump_directory, std::string* error)
#else
m_core->SetJitEnabled(false);
#endif
m_core->Reset(StarletMemory::BOOT_ROM_BASE);
m_core->Reset(m_bootstrap ? m_bootstrap->entry : StarletMemory::BOOT_ROM_BASE);
if (m_bootstrap)
m_core->SetBigEndian(true);
m_next_jit_diagnostic_instruction = 25'000'000;
m_next_realtime_diagnostic_ms = Common::Timer::NowMs() + 1000;
m_realtime_diagnostic_slice_count = 0;
@@ -101,6 +190,7 @@ void Starlet::Shutdown()
m_run_event = nullptr;
m_core.reset();
m_memory.reset();
m_bootstrap.reset();
}
void Starlet::DoState(PointerWrap& p)
@@ -110,6 +200,14 @@ void Starlet::DoState(PointerWrap& p)
return;
m_memory->DoState(p);
m_core->DoState(p);
if (p.IsReadMode())
{
m_bootstrap_trace_length = 0;
m_last_probe_status = 0;
m_bootstrap_line_length = 0;
m_bootstrap_outcome_logged = false;
m_bootstrap_fault_logged = false;
}
}
u32 Starlet::GetPC() const
@@ -201,6 +299,41 @@ void Starlet::RunSlice(s64 cycles_late)
ARM_IDLE_SLICE_CYCLES :
(ipc_handshake_active ? ARM_IPC_SLICE_CYCLES : ARM_ACTIVE_SLICE_CYCLES);
m_core->RunCycles(arm_cycles);
if (m_bootstrap && !m_bootstrap_outcome_logged)
TraceBootstrap();
// Observe the PPC test program's result word. This is diagnostic only: all
// request and response traffic still passes through shared RAM and IPC MMIO.
if (m_bootstrap)
{
constexpr u32 PPC_PROBE_STATUS_ADDRESS = 0x00003c00;
const u32 status = m_system.GetMemory().Read_U32(PPC_PROBE_STATUS_ADDRESS);
if ((status & 0xffff0000) == 0x52500000 && status != m_last_probe_status)
{
m_last_probe_status = status;
INFO_LOG_FMT(IOS, "Broadway ios-probe status={:#010x}", status);
if (status == 0x52500002 || status == 0x52500003 || status == 0x52500005 ||
(status & 0xfffff000) == 0x5250e000)
{
constexpr u32 PPC_PROBE_COUNTERS_ADDRESS = 0x00003c20;
INFO_LOG_FMT(IOS,
"Broadway ios-probe counters sent={} received={} invalid={} duplicate={} "
"timeout={}",
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 4),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 8),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 12),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 16));
// 0x52500005 / 0x5250e2xx: Revolution IOS Phase 04D /dev/fs cases.
if (status == 0x52500003 || status == 0x52500005 ||
(status & 0xffffff00) == 0x5250e100 || (status & 0xffffff00) == 0x5250e200)
{
INFO_LOG_FMT(IOS, "Broadway ios-probe checks={} failures={}",
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 20),
m_system.GetMemory().Read_U32(PPC_PROBE_COUNTERS_ADDRESS + 24));
}
}
}
}
// Keep host-clock reads and diagnostic formatting off the normal scheduler path.
const bool check_realtime_diagnostic =
@@ -415,4 +548,57 @@ void Starlet::RunSlice(s64 cycles_late)
const s64 next = std::max<s64>(1, static_cast<s64>(broadway_cycles) - cycles_late);
m_system.GetCoreTiming().ScheduleEvent(next, m_run_event);
}
void Starlet::TraceBootstrap()
{
const BootstrapELFInfo& bootstrap = *m_bootstrap;
const u32 fault_code = m_memory->Read32(bootstrap.fault_code);
if (fault_code != 0 && !m_bootstrap_fault_logged)
{
ERROR_LOG_FMT(IOS,
"Starlet bootstrap exception {}: LR={:#010x} SPSR={:#010x} PC={:#010x}",
fault_code, m_memory->Read32(bootstrap.fault_lr),
m_memory->Read32(bootstrap.fault_spsr), m_core->GetRegister(15));
m_bootstrap_fault_logged = true;
m_bootstrap_outcome_logged = true;
return;
}
if (m_memory->Read32(bootstrap.trace) != 0x52455631)
return;
const u32 length = m_memory->Read32(bootstrap.trace + 4);
if (length > 1024)
{
ERROR_LOG_FMT(IOS, "Starlet bootstrap trace length is invalid: {}", length);
m_bootstrap_outcome_logged = true;
return;
}
if (length < m_bootstrap_trace_length)
{
m_bootstrap_trace_length = 0;
m_bootstrap_line_length = 0;
}
for (u32 index = m_bootstrap_trace_length; index < length; ++index)
{
const char character = static_cast<char>(m_memory->Read8(bootstrap.trace + 8 + index));
if (character == '\n')
{
const std::string_view line(m_bootstrap_line.data(), m_bootstrap_line_length);
INFO_LOG_FMT(IOS, "Starlet bootstrap: {}", line);
if (line == "REV:BOOT:IDLE" || line.starts_with("REV:PANIC") ||
line.starts_with("REV:FATAL"))
{
INFO_LOG_FMT(IOS, "Starlet bootstrap stopped at PC={:#010x} after {} instructions",
m_core->GetRegister(15), m_core->GetExecutedInstructions());
m_bootstrap_outcome_logged = true;
}
m_bootstrap_line_length = 0;
}
else if (m_bootstrap_line_length < m_bootstrap_line.size())
{
m_bootstrap_line[m_bootstrap_line_length++] = character;
}
}
m_bootstrap_trace_length = length;
}
} // namespace IOS::LLE
+12
View File
@@ -3,11 +3,13 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
class PointerWrap;
@@ -32,6 +34,8 @@ class StarletMemory;
class Starlet final
{
public:
// Allocate in Starlet.cpp so callers never depend on this class's size.
static std::unique_ptr<Starlet> Create(Core::System& system);
explicit Starlet(Core::System& system);
~Starlet();
@@ -77,10 +81,18 @@ private:
static void RunCallback(Core::System& system, u64 userdata, s64 cycles_late);
void RunSlice(s64 cycles_late);
void TraceBootstrap();
Core::System& m_system;
std::unique_ptr<StarletMemory> m_memory;
std::unique_ptr<ARMCore> m_core;
std::optional<BootstrapELFInfo> m_bootstrap;
std::array<char, 256> m_bootstrap_line{};
u32 m_bootstrap_line_length = 0;
u32 m_bootstrap_trace_length = 0;
u32 m_last_probe_status = 0;
bool m_bootstrap_outcome_logged = false;
bool m_bootstrap_fault_logged = false;
CoreTiming::EventType* m_run_event = nullptr;
u64 m_next_jit_diagnostic_instruction = 25'000'000;
u64 m_next_realtime_diagnostic_ms = 0;
+113 -3
View File
@@ -726,6 +726,85 @@ void StarletMemory::Reset()
InitializeWiimotes();
}
void StarletMemory::PrepareBootstrap()
{
m_initialized = false;
Reset();
m_boot0_mapped = false;
m_sram_split_mode = true;
WriteRegister(HW_BOOT0, BOOT0_DISABLE);
WriteRegister(HW_SRNPROT, SRNPROT_SRAM_SPLIT_MODE);
}
bool StarletMemory::AttachReadOnlyNAND(const std::string& path, std::string* error)
{
m_nand_journal.reset();
m_nand_overlay.clear();
m_nand = File::IOFile(path, "rb", File::SharedAccess::Read);
const u64 nand_size = m_nand ? m_nand.GetSize() : 0;
if (nand_size != NAND_IMAGE_SIZE && nand_size != BOOTMII_NAND_IMAGE_SIZE)
{
m_nand.Close();
if (error)
*error = "the bootstrap NAND must be a 0x21000000-byte raw NAND image or a "
"0x21000400-byte BootMii dump";
return false;
}
m_nand_read_only = true;
return true;
}
bool StarletMemory::AttachDisposableNAND(const std::string& source, const std::string& copy,
std::string* error)
{
m_nand_disposable_copy.Close();
if (copy.empty() || File::Exists(copy))
{
if (error)
*error = "the disposable NAND copy must name a file that does not exist yet";
return false;
}
if (!AttachReadOnlyNAND(source, error))
return false;
// "x": creation fails if the file appeared since the check above.
File::IOFile output(copy, "wbx");
std::array<u8, 0x10000> buffer{};
const u64 size = m_nand.GetSize();
bool copied = output.IsOpen() && m_nand.Seek(0, File::SeekOrigin::Begin);
for (u64 offset = 0; copied && offset < size; offset += buffer.size())
{
const size_t count = static_cast<size_t>(std::min<u64>(buffer.size(), size - offset));
copied = m_nand.ReadArray(buffer.data(), count) && output.WriteArray(buffer.data(), count);
}
copied = copied && output.Flush();
output.Close();
if (copied)
m_nand_disposable_copy = File::IOFile(copy, "r+b");
if (!copied || !m_nand_disposable_copy || m_nand_disposable_copy.GetSize() != size)
{
m_nand_disposable_copy.Close();
m_nand.Close();
if (error)
*error = "could not create the disposable NAND copy";
return false;
}
m_nand_read_only = false;
return true;
}
bool StarletMemory::AttachKeys(const std::string& path, std::string* error)
{
File::IOFile keys_file(path, "rb");
if (!keys_file || keys_file.GetSize() != m_keys.size() || !keys_file.ReadArray(&m_keys))
{
m_keys.fill(0);
if (error)
*error = "the bootstrap keys must be a 1024-byte BootMii keys.bin";
return false;
}
return true;
}
void StarletMemory::DoState(PointerWrap& p)
{
const std::lock_guard wifi_sdio_lock(m_wifi_sdio_register_mutex);
@@ -2020,7 +2099,11 @@ void StarletMemory::ExecuteOHCI1HCICommand(const std::vector<u8>& command)
case HCI_CMD_READ_STORED_LINK_KEY:
{
const bool read_all = parameter_size >= sizeof(hci_read_stored_link_key_cp) && command[9] != 0;
const u16 key_count = read_all ? static_cast<u16>(m_wiimotes.size()) : 0;
// Bootstrap mode resets these slots without constructing Wiimote devices.
// Only initialized devices have identities and keys to return.
const u16 key_count = read_all ? static_cast<u16>(std::ranges::count_if(
m_wiimotes, [](const auto& wiimote) { return !!wiimote; })) :
0;
append_le16(&reply, 255);
append_le16(&reply, key_count);
if (key_count != 0)
@@ -2028,6 +2111,8 @@ void StarletMemory::ExecuteOHCI1HCICommand(const std::vector<u8>& command)
std::vector<u8> parameters = {static_cast<u8>(key_count)};
for (const auto& wiimote : m_wiimotes)
{
if (!wiimote)
continue;
parameters.insert(parameters.end(), wiimote->GetBD().begin(), wiimote->GetBD().end());
parameters.insert(parameters.end(), wiimote->GetLinkKey().begin(),
wiimote->GetLinkKey().end());
@@ -4154,6 +4239,21 @@ bool StarletMemory::CommitNANDPages(const NANDJournal::Pages& pages)
const Common::PerformanceTrace::Scope trace(Common::PerformanceTrace::Region::NAND);
if (m_nand_journal && !m_nand_journal->Append(pages))
return false;
// Phase 06H: mirror into the disposable copy first; a failure leaves the array unchanged.
if (m_nand_disposable_copy)
{
for (const auto& [page, data] : pages)
{
if (!m_nand_disposable_copy.Seek(static_cast<s64>(page) * NAND_RAW_PAGE_SIZE,
File::SeekOrigin::Begin) ||
!m_nand_disposable_copy.WriteArray(data))
{
return false;
}
}
if (!m_nand_disposable_copy.Flush())
return false;
}
for (const auto& [page, data] : pages)
m_nand_overlay.insert_or_assign(page, data);
return true;
@@ -4545,6 +4645,11 @@ bool StarletMemory::ReadNANDStatus(u32 command)
bool StarletMemory::StageNANDProgram(u32 command, bool random_data_input)
{
if (m_nand_read_only)
{
ERROR_LOG_FMT(IOS, "Read-only bootstrap NAND refused program command {:#010x}", command);
return false;
}
if (!(command & NAND_CTRL_WRITE))
return false;
@@ -4590,7 +4695,7 @@ bool StarletMemory::StageNANDProgram(u32 command, bool random_data_input)
bool StarletMemory::CommitNANDProgram()
{
Common::PerformanceTrace::Add(Common::PerformanceTrace::Counter::NANDPrograms);
if (!m_nand_program_pending)
if (m_nand_read_only || !m_nand_program_pending)
return false;
NANDPage raw{};
@@ -4628,6 +4733,11 @@ bool StarletMemory::CommitNANDProgram()
bool StarletMemory::StageNANDErase()
{
if (m_nand_read_only)
{
ERROR_LOG_FMT(IOS, "Read-only bootstrap NAND refused erase setup");
return false;
}
m_nand_erase_page = ReadRegister(NAND_ADDR2) & 0xffffff;
m_nand_erase_pending = m_nand_erase_page < NAND_PAGE_COUNT;
if (m_nand_erase_pending)
@@ -4638,7 +4748,7 @@ bool StarletMemory::StageNANDErase()
bool StarletMemory::CommitNANDErase()
{
Common::PerformanceTrace::Add(Common::PerformanceTrace::Counter::NANDErases);
if (!m_nand_erase_pending)
if (m_nand_read_only || !m_nand_erase_pending)
return false;
const u32 block_start = m_nand_erase_page & ~(NAND_PAGES_PER_BLOCK - 1);
@@ -56,6 +56,22 @@ public:
}
bool Init(const std::string& dump_directory, std::string* error);
// Set up empty SRAM with BOOT0 hidden and the IOS-style high SRAM split for an external ELF.
void PrepareBootstrap();
// Bootstrap-ELF mode only: expose a raw NAND image to the NAND controller strictly read-only.
// No journal is opened; every program or erase command then fails with the controller error
// bit, so a Revolution IOS read-only driver can be validated without any write path.
bool AttachReadOnlyNAND(const std::string& path, std::string* error);
// Revolution IOS Phase 06H, emulator only: a disposable writable NAND. `source` is opened
// read-only exactly as above and is never written. `copy` must not exist; it is created as a
// byte-for-byte copy of the source, and every committed program or erase is mirrored into it
// before the in-memory overlay changes, so it holds the mutated image for independent
// checking. No journal is opened.
bool AttachDisposableNAND(const std::string& source, const std::string& copy,
std::string* error);
// Revolution IOS Phase 04C: bootstrap mode loads no keys, so the OTP reads zero. This
// loads a 1024-byte BootMii keys.bin so OTP reads return the console's own words.
bool AttachKeys(const std::string& path, std::string* error);
void Reset();
void DoState(PointerWrap& p);
@@ -321,10 +337,13 @@ private:
u64 m_arm_cycles = 0;
u32 m_timer_offset = 0;
bool m_initialized = false;
bool m_nand_read_only = false;
bool m_boot0_mapped = true;
bool m_sram_split_mode = false;
// Host persistence is not serialized; savestates contain the complete overlay above.
std::unique_ptr<NANDJournal> m_nand_journal;
// Phase 06H: the disposable copy mirroring committed NAND pages (bootstrap mode only).
File::IOFile m_nand_disposable_copy;
std::unique_ptr<AX88772> m_ethernet;
std::unique_ptr<SlirpNetwork> m_ethernet_network;
};
+36 -1
View File
@@ -4,7 +4,9 @@
#include <cstdio>
#include <thread>
#include "Common/Logging/Log.h"
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/System.h"
#include "DolphinNoGUI/Platform.h"
@@ -26,10 +28,43 @@ void PlatformHeadless::SetTitle(const std::string& title)
void PlatformHeadless::MainLoop()
{
bool diff_observed = false;
while (m_running.IsSet())
{
UpdateRunningFlag();
Core::HostDispatchJobs(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
Core::HostDispatchJobs(system);
if (!diff_observed && Core::IsRunning(system))
{
auto& memory = system.GetMemory();
const u32 status = memory.Read_U32(0x00003c00);
if (status == 0x52500004 || (status & 0xffffff00) == 0x5250e100)
{
diff_observed = true;
INFO_LOG_FMT(IOS, "IOS_PROBE_DIFF_DONE status={:#010x}", status);
constexpr u32 trace_base = 0x00003d00;
const u32 count = memory.Read_U32(trace_base);
if (count > 8)
{
ERROR_LOG_FMT(IOS, "IOS_PROBE_DIFF_BAD_COUNT count={}", count);
}
else
{
for (u32 index = 0; index < count; ++index)
{
const u32 record = trace_base + 0x20 + index * 24;
INFO_LOG_FMT(IOS,
"IOS_PROBE_JSON {{\"schema\":1,\"index\":{},\"operation\":{},"
"\"argument\":{},\"return\":{},\"output\":{},\"ticks\":{},"
"\"status\":{}}}",
index, memory.Read_U32(record), memory.Read_U32(record + 4),
static_cast<s32>(memory.Read_U32(record + 8)),
memory.Read_U32(record + 12), memory.Read_U32(record + 16),
memory.Read_U32(record + 20));
}
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
+27 -22
View File
@@ -1,27 +1,32 @@
enable_testing()
add_custom_target(unittests)
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
set_target_properties(tests PROPERTIES FOLDER Tests)
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
add_test(NAME tests COMMAND tests)
add_custom_command(TARGET tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
)
enable_testing()
add_custom_target(unittests)
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
set_target_properties(tests PROPERTIES FOLDER Tests)
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
add_test(NAME tests COMMAND tests)
add_custom_command(TARGET tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
)
add_dependencies(unittests tests)
macro(add_dolphin_test target)
add_library(${target} OBJECT ${ARGN})
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
target_link_libraries(tests PRIVATE ${target})
endmacro()
add_subdirectory(Common)
add_subdirectory(Core)
# Runs a real Starlet ELF through the fork's CPU and SRAM bus without NAND or a frontend.
add_executable(StarletBootstrapProbe EXCLUDE_FROM_ALL
${CMAKE_SOURCE_DIR}/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp StubHost.cpp)
target_link_libraries(StarletBootstrapProbe PRIVATE fmt::fmt core uicommon)
macro(add_dolphin_test target)
add_library(${target} OBJECT ${ARGN})
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
target_link_libraries(tests PRIVATE ${target})
endmacro()
add_subdirectory(Common)
add_subdirectory(Core)
add_subdirectory(VideoCommon)
# Opt-in local trace qualification; never starts a console or accesses a NAND dump.
+46 -45
View File
@@ -1,20 +1,21 @@
add_dolphin_test(MMIOTest MMIOTest.cpp)
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
add_dolphin_test(DSPAssemblyTest
DSP/DSPAssemblyTest.cpp
DSP/DSPTestBinary.cpp
DSP/DSPTestText.cpp
DSP/HermesBinary.cpp
DSP/HermesText.cpp
)
add_dolphin_test(MMIOTest MMIOTest.cpp)
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
add_dolphin_test(DSPAssemblyTest
DSP/DSPAssemblyTest.cpp
DSP/DSPTestBinary.cpp
DSP/DSPTestText.cpp
DSP/HermesBinary.cpp
DSP/HermesText.cpp
)
add_dolphin_test(ESFormatsTest IOS/ES/FormatsTest.cpp)
add_dolphin_test(StarletARMCoreTest IOS/Starlet/ARMCoreTest.cpp)
add_dolphin_test(StarletBootstrapELFTest IOS/Starlet/BootstrapELFTest.cpp)
add_dolphin_test(StarletDMATest IOS/Starlet/DMATest.cpp)
add_dolphin_test(StarletSRAMReadTest IOS/Starlet/SRAMReadTest.cpp)
add_dolphin_test(StarletSRAMWriteTest IOS/Starlet/SRAMWriteTest.cpp)
@@ -24,36 +25,36 @@ add_dolphin_test(StarletEthernetTest IOS/Starlet/AX88772Test.cpp)
target_include_directories(StarletEthernetTest PRIVATE ${PROJECT_SOURCE_DIR}/Externals/libslirp/include)
add_dolphin_test(FileSystemTest IOS/FS/FileSystemTest.cpp)
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
if(_M_X86_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
if(_M_X86_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/Jit64Common/ConvertDoubleToSingle.cpp
PowerPC/Jit64Common/DCache.cpp
PowerPC/Jit64Common/Fres.cpp
PowerPC/Jit64Common/Frsqrte.cpp
)
elseif(_M_ARM_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/JitArm64/ConvertSingleDouble.cpp
PowerPC/JitArm64/FPRF.cpp
PowerPC/JitArm64/Fres.cpp
PowerPC/JitArm64/Frsqrte.cpp
PowerPC/JitArm64/MovI2R.cpp
)
else()
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
)
endif()
target_sources(PowerPCTest PRIVATE
PowerPC/TestValues.h
StubJit.h
)
PowerPC/Jit64Common/Fres.cpp
PowerPC/Jit64Common/Frsqrte.cpp
)
elseif(_M_ARM_64)
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
PowerPC/JitArm64/ConvertSingleDouble.cpp
PowerPC/JitArm64/FPRF.cpp
PowerPC/JitArm64/Fres.cpp
PowerPC/JitArm64/Frsqrte.cpp
PowerPC/JitArm64/MovI2R.cpp
)
else()
add_dolphin_test(PowerPCTest
PowerPC/DivUtilsTest.cpp
PowerPC/PageTableHostMappingTest.cpp
)
endif()
target_sources(PowerPCTest PRIVATE
PowerPC/TestValues.h
StubJit.h
)
@@ -0,0 +1,236 @@
// Copyright 2026 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <string>
#include <unordered_map>
#include <gtest/gtest.h>
#include "Core/IOS/Starlet/ARMCore.h"
#include "Core/IOS/Starlet/BootstrapELF.h"
namespace IOS::LLE
{
namespace
{
constexpr u32 TEXT_BASE = 0xffff0000;
constexpr u32 DATA_BASE = 0xfffe0000;
class RecordingBus final : public ARMBus
{
public:
u8 Read8(u32 address) override
{
const auto it = bytes.find(address);
return it == bytes.end() ? 0 : it->second;
}
void Write8(u32 address, u8 value) override { bytes[address] = value; }
std::unordered_map<u32, u8> bytes;
};
void Put16(std::array<u8, 0x300>& image, size_t offset, u16 value)
{
image[offset] = static_cast<u8>(value >> 8);
image[offset + 1] = static_cast<u8>(value);
}
void Put32(std::array<u8, 0x300>& image, size_t offset, u32 value)
{
image[offset] = static_cast<u8>(value >> 24);
image[offset + 1] = static_cast<u8>(value >> 16);
image[offset + 2] = static_cast<u8>(value >> 8);
image[offset + 3] = static_cast<u8>(value);
}
std::array<u8, 0x300> MakeImage()
{
std::array<u8, 0x300> image{};
image[0] = 0x7f;
image[1] = 'E';
image[2] = 'L';
image[3] = 'F';
image[4] = 1; // ELF32
image[5] = 2; // big endian
image[6] = 1;
Put16(image, 16, 2); // ET_EXEC
Put16(image, 18, 40); // ARM
Put32(image, 20, 1); // ELF version
Put32(image, 24, TEXT_BASE);
Put32(image, 28, 52); // program headers
Put32(image, 32, 0x200); // section headers
Put16(image, 40, 52);
Put16(image, 42, 32);
Put16(image, 44, 2);
Put16(image, 46, 40);
Put16(image, 48, 3);
Put32(image, 52, 1);
Put32(image, 56, 0x100);
Put32(image, 60, TEXT_BASE);
Put32(image, 64, TEXT_BASE);
Put32(image, 68, 4);
Put32(image, 72, 8);
Put32(image, 76, 5);
image[0x100] = 0xea; // ARM branch instruction
Put32(image, 84, 1);
Put32(image, 88, 0x110);
Put32(image, 92, DATA_BASE);
Put32(image, 96, DATA_BASE);
Put32(image, 100, 4);
Put32(image, 104, 0x420);
Put32(image, 108, 6);
image[0x110] = 0x12;
image[0x111] = 0x34;
image[0x112] = 0x56;
image[0x113] = 0x78;
constexpr char names[] = "\0REV_TRACE\0__fault_code\0__fault_lr\0__fault_spsr";
for (size_t index = 0; index < sizeof(names); ++index)
image[0x180 + index] = static_cast<u8>(names[index]);
Put32(image, 0x228 + 4, 3); // SHT_STRTAB
Put32(image, 0x228 + 16, 0x180);
Put32(image, 0x228 + 20, sizeof(names));
Put32(image, 0x250 + 4, 2); // SHT_SYMTAB
Put32(image, 0x250 + 16, 0x120);
Put32(image, 0x250 + 20, 5 * 16);
Put32(image, 0x250 + 24, 1);
Put32(image, 0x250 + 36, 16);
const auto symbol = [&image](size_t index, u32 name, u32 address, u32 size) {
const size_t offset = 0x120 + index * 16;
Put32(image, offset, name);
Put32(image, offset + 4, address);
Put32(image, offset + 8, size);
};
symbol(1, 1, DATA_BASE + 8, 1032);
symbol(2, 11, DATA_BASE + 0x410, 4);
symbol(3, 24, DATA_BASE + 0x414, 4);
symbol(4, 35, DATA_BASE + 0x418, 4);
return image;
}
TEST(BootstrapELFTest, LoadsSegmentsAndZerosUninitializedData)
{
const auto image = MakeImage();
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error)) << error;
EXPECT_EQ(info.entry, TEXT_BASE);
EXPECT_EQ(info.trace, DATA_BASE + 8);
EXPECT_EQ(info.fault_spsr, DATA_BASE + 0x418);
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
EXPECT_EQ(bus.Read8(TEXT_BASE + 4), 0);
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
EXPECT_EQ(bus.Read32(DATA_BASE + 0x41c), 0u);
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420});
}
TEST(BootstrapELFTest, RejectsMalformedImageBeforeWritingMemory)
{
auto image = MakeImage();
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
image[5] = 1;
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
image = MakeImage();
Put32(image, 0x120 + 2 * 16 + 4, DATA_BASE + 0x420);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
image = MakeImage();
Put32(image, 88, 0x300);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
}
// Revolution IOS Phase 06H: the optional MEM2 text segment of the emulator-only
// disposable-image kernel, accepted only when the caller allows it.
constexpr u32 MEM2_TEXT = 0x12600000;
std::array<u8, 0x300> MakeMem2Image(u32 address, u32 flags)
{
auto image = MakeImage();
Put16(image, 44, 3);
Put32(image, 116, 1);
Put32(image, 120, 0x114);
Put32(image, 124, address);
Put32(image, 128, address);
Put32(image, 132, 4);
Put32(image, 136, 0x10);
Put32(image, 140, flags);
Put32(image, 0x114, 0xe12fff1e); // BX LR
return image;
}
TEST(BootstrapELFTest, Mem2TextSegmentOnlyWhenAllowed)
{
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
auto image = MakeMem2Image(MEM2_TEXT, 5);
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
EXPECT_TRUE(bus.bytes.empty());
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error, true)) << error;
EXPECT_EQ(bus.Read32(MEM2_TEXT), 0xe12fff1eu);
EXPECT_EQ(bus.Read32(MEM2_TEXT + 0xc), 0u);
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420 + 0x10});
RecordingBus plain;
const auto two_segments = MakeImage();
EXPECT_TRUE(LoadBootstrapELF(two_segments, plain, &info, &error, true)) << error;
}
TEST(BootstrapELFTest, RejectsInvalidMem2TextSegmentBeforeWritingMemory)
{
RecordingBus bus;
BootstrapELFInfo info;
std::string error;
// Writable, outside MEM2, crossing its end, or a second SRAM text segment.
for (const auto& image : {MakeMem2Image(MEM2_TEXT, 6), MakeMem2Image(0x0fff0000, 5),
MakeMem2Image(0x13fffff8, 5), MakeMem2Image(TEXT_BASE, 5)})
{
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error, true));
EXPECT_TRUE(bus.bytes.empty());
}
}
TEST(ARMCoreTest, Armv5DoublewordTransfersAndAlignmentAbort)
{
RecordingBus bus;
bus.Write32(TEXT_BASE, 0xe1c020d0); // LDRD r2, r3, [r0]
bus.Write32(TEXT_BASE + 4, 0xe1c120f0); // STRD r2, r3, [r1]
bus.Write32(DATA_BASE, 0x11223344);
bus.Write32(DATA_BASE + 4, 0x55667788);
ARMCore core(bus);
core.Reset(TEXT_BASE);
core.SetBigEndian(true);
core.SetRegister(0, DATA_BASE);
core.SetRegister(1, DATA_BASE + 8);
core.Step();
EXPECT_EQ(core.GetRegister(2), 0x11223344u);
EXPECT_EQ(core.GetRegister(3), 0x55667788u);
core.Step();
EXPECT_EQ(bus.Read32(DATA_BASE + 8), 0x11223344u);
EXPECT_EQ(bus.Read32(DATA_BASE + 12), 0x55667788u);
core.Reset(TEXT_BASE);
core.SetBigEndian(true);
core.SetRegister(0, DATA_BASE + 4);
core.Step();
EXPECT_EQ(core.GetRegister(15), TEXT_BASE + 0x10);
EXPECT_EQ(core.GetRegister(14), TEXT_BASE + 8);
EXPECT_EQ(core.GetCP15State().fault_address, DATA_BASE + 4);
}
} // namespace
} // namespace IOS::LLE
@@ -135,6 +135,21 @@ protected:
EXPECT_EQ(m_memory->GetDMAMemoryPointer(address, 4), nullptr);
}
void CheckBootstrapStoredKeys()
{
m_memory->PrepareBootstrap();
for (const u8 read_all : {0, 1})
{
SCOPED_TRACE(read_all);
m_memory->m_ohci1_hci_events.clear();
m_memory->ExecuteOHCI1HCICommand({0x0d, 0x0c, 7, 0, 0, 0, 0, 0, 0, read_all});
ASSERT_EQ(m_memory->m_ohci1_hci_events.size(), 1u);
// Command Complete: capacity 255, zero stored keys, no Return Link Keys event.
EXPECT_EQ(m_memory->m_ohci1_hci_events.front(),
(std::vector<u8>{0x0e, 8, 1, 0x0d, 0x0c, 0, 0xff, 0, 0, 0}));
}
}
void CheckOHCI()
{
for (const u32 base : {0x10000U, 0x10010000U, 0x0d404000U})
@@ -381,6 +396,11 @@ TEST_F(StarletDMATest, OHCIUsesTwoPhysicalPagesAndLittleEndianDescriptors)
CheckOHCI();
}
TEST_F(StarletDMATest, BootstrapReadStoredKeysHasNoWiimoteDevices)
{
CheckBootstrapStoredKeys();
}
TEST_F(StarletDMATest, NANDSeparatesDataSpareAndECCIncludingPartialTransfers)
{
CheckNAND();