diff options
| author | Scott Mansell <phiren@gmail.com> | 2026-07-12 11:34:44 +1200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-12 11:34:44 +1200 |
| commit | fc9fd6952a62c4fe48acc00333ce360cb47b57c7 (patch) | |
| tree | 98c900e645f4e0252968f2812a77fb5f1ece41a1 | |
| parent | 6de526c684ee6dd8b6fc3447dae858ae480ccbb5 (diff) | |
| parent | f9ef6c2151d48ef9b5dbe9ee1c2278e5d60b5ed9 (diff) | |
Merge pull request #14665 from cristian64/simulated_memory_size
Core: Adjust emulated memory size automatically.
| -rw-r--r-- | Source/Android/app/src/main/res/values/strings.xml | 2 | ||||
| -rw-r--r-- | Source/Core/Core/ConfigManager.cpp | 25 | ||||
| -rw-r--r-- | Source/Core/Core/ConfigManager.h | 6 | ||||
| -rw-r--r-- | Source/Core/Core/DolphinAnalytics.cpp | 3 | ||||
| -rw-r--r-- | Source/Core/Core/FifoPlayer/FifoDataFile.cpp | 3 | ||||
| -rw-r--r-- | Source/Core/Core/HW/Memmap.cpp | 13 | ||||
| -rw-r--r-- | Source/Core/Core/HW/Memmap.h | 2 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/IOS.cpp | 5 | ||||
| -rw-r--r-- | Source/Core/Core/System.cpp | 1 | ||||
| -rw-r--r-- | Source/Core/Core/System.h | 4 | ||||
| -rw-r--r-- | Source/Core/DiscIO/Volume.h | 1 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeDisc.cpp | 8 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeDisc.h | 1 | ||||
| -rw-r--r-- | Source/Core/DiscIO/VolumeWad.h | 1 | ||||
| -rw-r--r-- | Source/Core/DolphinQt/Settings/AdvancedPane.cpp | 7 | ||||
| -rw-r--r-- | Source/UnitTests/Core/PowerPC/PageTableHostMappingTest.cpp | 5 |
16 files changed, 70 insertions, 17 deletions
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 07c5e7ebc0..9d97169d95 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -400,7 +400,7 @@ <string name="overclock_title_description">Adjusts the emulated CPU\'s clock rate if \"Override Emulated CPU Clock Speed\" is enabled.</string> <string name="memory_override">Memory Override</string> <string name="enable_memory_size_override">Enable Emulated Memory Size Override</string> - <string name="enable_memory_size_override_description">Adjusts the amount of RAM in the emulated console.\n\nWARNING: Enabling this will completely break many games. Only a small number of games can benefit from this.</string> + <string name="enable_memory_size_override_description">Sets the amount of RAM in the emulated console to the values provided.\n\nWARNING: Enabling this will completely break many games. By default, Dolphin determines what value is required based on the game information.</string> <string name="main_mem1_size">MEM1 Size</string> <string name="main_mem2_size">MEM2 Size</string> <string name="gpu_options">GPU Options</string> diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 8404cfff07..460ca2e048 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -154,27 +154,35 @@ u16 SConfig::GetRevision() const return m_revision; } +u32 SConfig::GetSimulatedMemorySize() const +{ + std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); + return m_simulated_memory_size; +} + void SConfig::ResetRunningGameMetadata() { std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); - SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown); + SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown, 0); } void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition) { std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); + if (partition == volume.GetGamePartition()) { SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0), - volume.GetRegion()); + volume.GetRegion(), volume.GetSimulatedMemorySize()); } else { SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition), volume.GetTitleID(partition).value_or(0), - volume.GetRevision(partition).value_or(0), volume.GetRegion()); + volume.GetRevision(partition).value_or(0), volume.GetRegion(), + volume.GetSimulatedMemorySize()); } } @@ -192,26 +200,29 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat { // If not launching a disc game, just read everything from the TMD. SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(), - tmd.GetRegion()); + tmd.GetRegion(), 0); } } void SConfig::SetRunningGameMetadata(const std::string& game_id) { std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); - SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown); + SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown, 0); } void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, - u64 title_id, u16 revision, DiscIO::Region region) + u64 title_id, u16 revision, DiscIO::Region region, + u32 simulated_memory_size) { std::lock_guard<std::recursive_mutex> lock(m_metadata_lock); const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id || - m_title_id != title_id || m_revision != revision; + m_title_id != title_id || m_revision != revision || + m_simulated_memory_size != simulated_memory_size; m_game_id = game_id; m_gametdb_id = gametdb_id; m_title_id = title_id; m_revision = revision; + m_simulated_memory_size = simulated_memory_size; if (game_id.length() == 6) { diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 39dbc6d39a..145b002418 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -65,6 +65,7 @@ struct SConfig const std::string GetTitleDescription() const; u64 GetTitleID() const; u16 GetRevision() const; + u32 GetSimulatedMemorySize() const; void ResetRunningGameMetadata(); void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition); void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform); @@ -119,7 +120,8 @@ private: static void ReloadTextures(Core::System& system); void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, - u64 title_id, u16 revision, DiscIO::Region region); + u64 title_id, u16 revision, DiscIO::Region region, + u32 simulated_memory_size); static SConfig* m_Instance; mutable std::recursive_mutex m_metadata_lock; @@ -130,4 +132,6 @@ private: std::string m_title_description; u64 m_title_id; u16 m_revision; + // Memory size requested by game's header. + u32 m_simulated_memory_size; }; diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp index 1fff6bc5c5..8ae3bf6bcf 100644 --- a/Source/Core/Core/DolphinAnalytics.cpp +++ b/Source/Core/Core/DolphinAnalytics.cpp @@ -354,6 +354,9 @@ void DolphinAnalytics::MakePerGameBuilder() // Unique id bound to the gameid. builder.AddData("id", MakeUniqueId(SConfig::GetInstance().GetGameID())); + // Other game-specific fields. + builder.AddData("simulated-memory-size", SConfig::GetInstance().GetSimulatedMemorySize()); + // Configuration. builder.AddData("cfg-dsp-hle", Config::Get(Config::MAIN_DSP_HLE)); builder.AddData("cfg-dsp-jit", Config::Get(Config::MAIN_DSP_JIT)); diff --git a/Source/Core/Core/FifoPlayer/FifoDataFile.cpp b/Source/Core/Core/FifoPlayer/FifoDataFile.cpp index f795d5f08a..0e1e44aef9 100644 --- a/Source/Core/Core/FifoPlayer/FifoDataFile.cpp +++ b/Source/Core/Core/FifoPlayer/FifoDataFile.cpp @@ -140,7 +140,8 @@ bool FifoDataFile::Save(const std::string& filename) header.fileId = FILE_ID; header.file_version = VERSION_NUMBER; // Maintain backwards compatibility so long as the RAM sizes aren't overridden. - if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) + if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) || + SConfig::GetInstance().GetSimulatedMemorySize() > Memory::MEM1_SIZE_RETAIL) header.min_loader_version = MIN_LOADER_VERSION_FOR_RAM_OVERRIDE; else header.min_loader_version = MIN_LOADER_VERSION; diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index e6d8fd56a5..054596e3d8 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -25,6 +25,7 @@ #include "Common/MsgHandler.h" #include "Common/Swap.h" #include "Core/Config/MainSettings.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/DSP.h" @@ -87,10 +88,18 @@ void MemoryManager::InitMMIO(Core::System& system) void MemoryManager::Init() { - const auto get_mem1_size = [] { + const auto get_mem1_size = [this] { if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) return Config::Get(Config::MAIN_MEM1_SIZE); - return Memory::MEM1_SIZE_RETAIL; + // The simulated memory size in the game's header was originally used to ask the Apploader to + // simulate a smaller memory size than that in development systems. That is, it could make the + // 48 MiB devkit act like a 24 MiB retail console during testing, or any other config. + // + // Dolphin is reusing this as a signal to automatically adjust the emulated memory size. The + // upper bound is clamped to 64 MiB (maximum supported by Flipper's memory controller), + // reflecting Apploader behaviour of clamping the simulated size to physical memory size. It is + // never desired to emulate less than a retail console, so the lower bound is clapped to 24 MiB. + return std::clamp(m_system.GetSimulatedMemorySize(), MEM1_SIZE_RETAIL, MEM1_SIZE_MAX); }; const auto get_mem2_size = [] { if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index baa910cc70..e9253272f6 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -34,8 +34,10 @@ constexpr u32 MEM1_BASE_ADDR = 0x80000000U; constexpr u32 MEM2_BASE_ADDR = 0x90000000U; constexpr u32 MEM1_SIZE_RETAIL = 0x01800000U; constexpr u32 MEM1_SIZE_GDEV = 0x04000000U; +constexpr u32 MEM1_SIZE_MAX = 0x04000000U; constexpr u32 MEM2_SIZE_RETAIL = 0x04000000U; constexpr u32 MEM2_SIZE_NDEV = 0x08000000U; +constexpr u32 MEM2_SIZE_MAX = 0x08000000U; struct PhysicalMemoryRegion { diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index d3986775b6..49c0b2bbd3 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -221,8 +221,11 @@ static void ReleasePPCAncast(Core::System& system) void RAMOverrideForIOSMemoryValues(Memory::MemoryManager& memory, MemorySetupType setup_type) { // Don't touch anything if the feature isn't enabled. - if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE)) + if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) || + SConfig::GetInstance().GetSimulatedMemorySize() <= Memory::MEM1_SIZE_RETAIL) + { return; + } // Some unstated constants that can be inferred. const u32 ipc_buffer_size = diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 686d1a3264..12da7dcaea 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -119,6 +119,7 @@ void System::Initialize() m_separate_cpu_and_gpu_threads = Config::Get(Config::MAIN_CPU_THREAD); m_mmu_enabled = Config::Get(Config::MAIN_MMU); m_pause_on_panic_enabled = Config::Get(Config::MAIN_PAUSE_ON_PANIC); + m_simulated_memory_size = SConfig::GetInstance().GetSimulatedMemorySize(); } SoundStream* System::GetSoundStream() const diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index a170c608ee..8524003400 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -5,6 +5,8 @@ #include <memory> +#include "Common/CommonTypes.h" + class GeometryShaderManager; class Interpreter; class JitInterface; @@ -147,6 +149,7 @@ public: bool IsTriforce() const { return m_is_triforce; } bool IsWii() const { return m_is_wii; } bool IsBranchWatchIgnoreApploader() { return m_branch_watch_ignore_apploader; } + u32 GetSimulatedMemorySize() const { return m_simulated_memory_size; } void SetIsMIOS(bool is_mios) { m_is_mios = is_mios; } void SetIsTriforce(bool is_triforce) { m_is_triforce = is_triforce; } @@ -216,5 +219,6 @@ private: bool m_is_triforce = false; bool m_is_wii = false; bool m_branch_watch_ignore_apploader = false; + u32 m_simulated_memory_size{0}; }; } // namespace Core diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 7dfabb3e97..61df7e032f 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -126,6 +126,7 @@ public: } virtual Region GetRegion() const = 0; virtual Country GetCountry(const Partition& partition = PARTITION_NONE) const = 0; + virtual u32 GetSimulatedMemorySize() const = 0; virtual BlobType GetBlobType() const = 0; // Size of virtual disc (may be inaccurate depending on the blob type) virtual u64 GetDataSize() const = 0; diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index e842540376..4dd16c4cef 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -179,6 +179,14 @@ std::optional<u8> VolumeDisc::GetDiscNumber(const Partition& partition) const return ReadSwapped<u8>(6, partition); } +u32 VolumeDisc::GetSimulatedMemorySize() const +{ + // Simulated memory size is a 32-bit unsigned integer in the `bi2.bin` file at offset `0x04`. + // Apploader reads this and "simulates" a smaller memory size. Originally useful for simulating + // 24 MiB of memory on devkits with 48 MiB. + return ReadSwapped<u32>(BI2_ADDRESS + 0x04, GetGamePartition()).value_or(0); +} + bool VolumeDisc::IsNKit() const { constexpr u32 NKIT_MAGIC = 0x4E4B4954; // "NKIT" diff --git a/Source/Core/DiscIO/VolumeDisc.h b/Source/Core/DiscIO/VolumeDisc.h index f630522e68..1b3774a87d 100644 --- a/Source/Core/DiscIO/VolumeDisc.h +++ b/Source/Core/DiscIO/VolumeDisc.h @@ -47,6 +47,7 @@ public: std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; std::string GetApploaderDate(const Partition& partition) const override; std::optional<u8> GetDiscNumber(const Partition& partition = PARTITION_NONE) const override; + u32 GetSimulatedMemorySize() const override; bool IsNKit() const override; protected: diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index 2c383c0254..88a0847f80 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -61,6 +61,7 @@ public: bool IsNKit() const override; Region GetRegion() const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override; + u32 GetSimulatedMemorySize() const override { return 0; } BlobType GetBlobType() const override; u64 GetDataSize() const override; diff --git a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp index a526e8c7cb..a2fc9c5410 100644 --- a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp +++ b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp @@ -276,10 +276,9 @@ void AdvancedPane::CreateLayout() }); m_ram_override_checkbox->SetDescription( - tr("Adjusts the amount of RAM in the emulated console.<br><br>" - "<b>WARNING</b>: Enabling this will completely break many games.<br>Only a small " - "number " - "of games can benefit from this." + tr("Sets the amount of RAM in the emulated console to the values provided.<br><br>" + "<b>WARNING</b>: Enabling this will completely break many games. By default, Dolphin " + "determines what value is required based on the game information." "<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>")); auto* rtc_options = new QGroupBox(tr("Custom RTC Options")); diff --git a/Source/UnitTests/Core/PowerPC/PageTableHostMappingTest.cpp b/Source/UnitTests/Core/PowerPC/PageTableHostMappingTest.cpp index 2cfb4b8c8f..c2622cee37 100644 --- a/Source/UnitTests/Core/PowerPC/PageTableHostMappingTest.cpp +++ b/Source/UnitTests/Core/PowerPC/PageTableHostMappingTest.cpp @@ -10,6 +10,7 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/Swap.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/MemTools.h" #include "Core/PowerPC/BreakPoints.h" @@ -131,6 +132,8 @@ public: if (!EMM::IsExceptionHandlerSupported()) GTEST_SKIP() << "Skipping PageTableHostMappingTest because exception handler is unsupported."; + SConfig::Init(); + auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); const u32 host_page_size = memory.GetHostPageSize(); @@ -177,6 +180,8 @@ public: EMM::UninstallExceptionHandler(); Core::UndeclareAsCPUThread(); system.GetMemory().Shutdown(); + + SConfig::Shutdown(); } static void SetSR(size_t index, u32 vsid) |
