Merge pull request #21148 from hrydgard/more-misc

Add workaround for infamous GoW crash
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-01-23 15:39:17 +01:00
commit da31a99526
8 files changed
+97 -54

No files matched your search

+1
View File
@@ -154,6 +154,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "SaveStatesNotRecommended", &flags_.SaveStatesNotRecommended);
CheckSetting(iniFile, gameID, "IgnoreEnqueue", &flags_.IgnoreEnqueue);
CheckSetting(iniFile, gameID, "MsgDialogAutoStatus", &flags_.MsgDialogAutoStatus);
CheckSetting(iniFile, gameID, "NullPageValid", &flags_.NullPageValid);
}
void Compatibility::CheckVRSettings(IniFile &iniFile, const std::string &gameID) {
+1
View File
@@ -117,6 +117,7 @@ struct CompatFlags {
bool SaveStatesNotRecommended;
bool IgnoreEnqueue;
bool MsgDialogAutoStatus;
bool NullPageValid;
};
struct VRCompat {
+42 -30
View File
@@ -50,6 +50,7 @@ u8* base = nullptr;
MemArena g_arena;
// ==============
u8 *m_pNullPage;
u8 *m_pPhysicalScratchPad;
u8 *m_pUncachedScratchPad;
// 64-bit: Pointers to high-mem mirrors
@@ -67,8 +68,8 @@ u8 *m_pUncachedKernelRAM[3];
// since when we write to the depth buffer (like with the depth rasterizer or the software renderer)
// we write unswizzled data anyway. There are some exceptions, Silent Hill abuses the swizzling in ways that break
// our software renderer.
u8 *m_pPhysicalVRAM[4];
u8 *m_pUncachedVRAM[4];
static u8 *m_pPhysicalVRAM[4];
static u8 *m_pUncachedVRAM[4];
// Holds the ending address of the PSP's user space.
// Required for HD Remasters to work properly.
@@ -77,11 +78,13 @@ u32 g_MemorySize;
// Used to store the PSP model on game startup.
u32 g_PSPModel;
static MemMapSetupFlags g_setupFlags;
std::recursive_mutex g_shutdownLock;
// We don't declare the IO region in here since its handled by other means.
static MemoryView views[] =
{
static MemoryView views[] = {
{&m_pNullPage, 0x00000000, 0x00010000, MV_NULL_PAGE}, // Null page, usually not enabled. Only used for working around some race condition bugs.
{&m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0},
{&m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS},
{&m_pPhysicalVRAM[0], 0x04000000, 0x00200000, 0},
@@ -111,8 +114,6 @@ static MemoryView views[] =
// implement those.
};
static const int num_views = sizeof(views) / sizeof(MemoryView);
inline static bool CanIgnoreView(const MemoryView &view) {
#ifdef MASKED_PSP_MEMORY
// Basically, 32-bit platforms can ignore views that are masked out anyway.
@@ -122,16 +123,19 @@ inline static bool CanIgnoreView(const MemoryView &view) {
#endif
}
static bool SkipView(MemMapSetupFlags flags, u32 viewFlags) {
#if PPSSPP_PLATFORM(IOS) && PPSSPP_ARCH(64BIT)
#define SKIP(a_flags, b_flags) \
if ((b_flags) & MV_KERNEL) \
continue;
#else
#define SKIP(a_flags, b_flags) \
;
// We use a limited memory map on iOS with masking, we don't need to allocate the kernel space views.
if (viewFlags & MV_KERNEL) {
return true;
}
#endif
if ((viewFlags & MV_NULL_PAGE) && !(flags & MemMapSetupFlags::AllocNullPage))
return true;
return false;
}
static bool Memory_TryBase(u32 flags) {
static bool Memory_TryBase(MemMapSetupFlags flags) {
// OK, we know where to find free space. Now grab it!
// We just mimic the popular BAT setup.
@@ -139,17 +143,19 @@ static bool Memory_TryBase(u32 flags) {
size_t last_position = 0;
// Zero all the pointers to be sure.
for (int i = 0; i < num_views; i++) {
if (views[i].out_ptr)
for (int i = 0; i < ARRAY_SIZE(views); i++) {
if (views[i].out_ptr) {
*views[i].out_ptr = 0;
}
}
int i;
for (i = 0; i < num_views; i++) {
int i; // the final values is used in the next loop
for (i = 0; i < ARRAY_SIZE(views); i++) {
const MemoryView &view = views[i];
if (view.size == 0)
continue;
SKIP(flags, view.flags);
if (SkipView(flags, view.flags))
continue;
if (view.flags & MV_MIRROR_PREVIOUS) {
position = last_position;
@@ -184,7 +190,8 @@ bail:
for (int j = 0; j <= i; j++) {
if (views[i].size == 0)
continue;
SKIP(flags, views[i].flags);
if (SkipView(flags, views[i].flags))
continue;
if (views[j].out_ptr && *views[j].out_ptr) {
if (!CanIgnoreView(views[j])) {
g_arena.ReleaseView(0, *views[j].out_ptr, views[j].size);
@@ -195,7 +202,8 @@ bail:
return false;
}
bool MemoryMap_Setup(u32 flags) {
bool MemoryMap_Setup(MemMapSetupFlags flags) {
g_setupFlags = flags;
#if PPSSPP_PLATFORM(UWP)
// We reserve the memory, then simply commit in TryBase.
base = (u8*)VirtualAllocFromApp(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE);
@@ -203,10 +211,11 @@ bool MemoryMap_Setup(u32 flags) {
// Figure out how much memory we need to allocate in total.
size_t total_mem = 0;
for (int i = 0; i < num_views; i++) {
for (int i = 0; i < ARRAY_SIZE(views); i++) {
if (views[i].size == 0)
continue;
SKIP(flags, views[i].flags);
if (SkipView(flags, views[i].flags))
continue;
if (!CanIgnoreView(views[i]))
total_mem += g_arena.roundup(views[i].size);
}
@@ -264,15 +273,18 @@ bool MemoryMap_Setup(u32 flags) {
return Memory_TryBase(flags);
}
void MemoryMap_Shutdown(u32 flags) {
void MemoryMap_Shutdown() {
size_t position = 0;
size_t last_position = 0;
const MemMapSetupFlags flags = g_setupFlags;
g_setupFlags = MemMapSetupFlags::Default;
for (int i = 0; i < num_views; i++) {
for (int i = 0; i < ARRAY_SIZE(views); i++) {
if (views[i].size == 0)
continue;
SKIP(flags, views[i].flags);
if (SkipView(flags, views[i].flags))
continue;
if (views[i].flags & MV_MIRROR_PREVIOUS) {
position = last_position;
}
@@ -291,7 +303,7 @@ void MemoryMap_Shutdown(u32 flags) {
#endif
}
bool Init() {
bool Init(MemMapSetupFlags flags) {
// On some 32 bit platforms (like Android, iOS, etc.), you can only map < 32 megs at a time.
const static int MAX_MMAP_SIZE = 31 * 1024 * 1024;
_dbg_assert_msg_(g_MemorySize <= MAX_MMAP_SIZE * 3, "ACK - too much memory for three mmap views.");
@@ -304,7 +316,6 @@ bool Init() {
views[i].size = std::min(std::max((int)g_MemorySize - MAX_MMAP_SIZE * 2, 0), MAX_MMAP_SIZE);
}
int flags = 0;
if (!MemoryMap_Setup(flags)) {
return false;
}
@@ -319,8 +330,9 @@ bool Init() {
void Reinit() {
_assert_msg_(PSP_GetBootState() == BootState::Complete, "Cannot reinit during startup/shutdown");
Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITING);
MemMapSetupFlags flags = g_setupFlags;
Shutdown();
Init();
Init(flags);
Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITED);
}
@@ -398,7 +410,7 @@ void DoState(PointerWrap &p) {
void Shutdown() {
std::lock_guard<std::recursive_mutex> guard(g_shutdownLock);
u32 flags = 0;
MemoryMap_Shutdown(flags);
MemoryMap_Shutdown();
base = nullptr;
DEBUG_LOG(Log::MemMap, "Memory system shut down.");
}
+12 -10
View File
@@ -25,6 +25,7 @@
#include <stddef.h>
#endif
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Swap.h"
#include "Core/Opcode.h"
@@ -71,8 +72,7 @@ extern u32 g_PSPModel;
#define MASKED_PSP_MEMORY
#endif
enum
{
enum {
// This may be adjusted by remaster games.
RAM_NORMAL_SIZE = 0x02000000,
// Used if the PSP model is PSP-2000 (Slim).
@@ -93,26 +93,28 @@ enum {
MV_IS_PRIMARY_RAM = 0x100,
MV_IS_EXTRA1_RAM = 0x200,
MV_IS_EXTRA2_RAM = 0x400,
MV_KERNEL = 0x800 // Can be skipped on platforms where memory is tight.
MV_KERNEL = 0x800, // Can be skipped on platforms where memory is tight.
MV_NULL_PAGE = 0x1000,
};
struct MemoryView
{
struct MemoryView {
u8 **out_ptr;
u32 virtual_address;
u32 size;
u32 flags;
};
// Uses a memory arena to set up an emulator-friendly memory map
bool MemoryMap_Setup(u32 flags);
void MemoryMap_Shutdown(u32 flags);
enum class MemMapSetupFlags {
Default = 0,
AllocNullPage = 1,
};
ENUM_CLASS_BITOPS(MemMapSetupFlags);
// Init and Shutdown
bool Init();
bool Init(MemMapSetupFlags flags);
void Shutdown();
void DoState(PointerWrap &p);
void Clear();
// False when shutdown has already been called.
bool IsActive();
+6 -1
View File
@@ -365,8 +365,13 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
HLEPlugins::Init();
}
Memory::MemMapSetupFlags memMapFlags = Memory::MemMapSetupFlags::Default;
if (g_CoreParameter.compat.flags().NullPageValid) {
memMapFlags = Memory::MemMapSetupFlags::AllocNullPage;
}
// Initialize the memory map as early as possible (now that we've read the PARAM.SFO).
if (!Memory::Init()) {
if (!Memory::Init(memMapFlags)) {
// We're screwed.
*errorString = "Memory init failed";
return false;
+16 -12
View File
@@ -179,7 +179,7 @@ void ScreenshotViewScreen::OnDeleteState(UI::EventParams &e) {
// TODO: Also show the screenshot on the confirmation screen?
screenManager()->push(new UI::MessagePopupScreen(title, message, di->T("Delete"), di->T("Cancel"), [=](bool result) {
screenManager()->push(new UI::MessagePopupScreen(title, message, di->T("Delete"), di->T("Cancel"), [this](bool result) {
if (result) {
SaveState::DeleteSlot(saveStatePrefix_, slot_);
TriggerFinish(DR_CANCEL);
@@ -588,7 +588,7 @@ void GamePauseScreen::CreateViews() {
rightColumnItems->SetSpacing(0.0f);
if (getUMDReplacePermit()) {
rightColumnItems->Add(new Choice(pa->T("Switch UMD"), ImageID("I_UMD")))->OnClick.Add([=](UI::EventParams &) {
rightColumnItems->Add(new Choice(pa->T("Switch UMD"), ImageID("I_UMD")))->OnClick.Add([this](UI::EventParams &) {
screenManager()->push(new UmdReplaceScreen());
});
}
@@ -616,22 +616,22 @@ void GamePauseScreen::CreateViews() {
}
if (g_Config.bAchievementsEnable && Achievements::HasAchievementsOrLeaderboards()) {
rightColumnItems->Add(new Choice(ac->T("Achievements"), ImageID("I_ACHIEVEMENT")))->OnClick.Add([&](UI::EventParams &e) {
rightColumnItems->Add(new Choice(ac->T("Achievements"), ImageID("I_ACHIEVEMENT")))->OnClick.Add([this](UI::EventParams &e) {
screenManager()->push(new RetroAchievementsListScreen(gamePath_));
});
}
rightColumnItems->Add(new Choice(gr->T("Display layout & effects"), ImageID("I_DISPLAY")))->OnClick.Add([&](UI::EventParams &) -> void {
rightColumnItems->Add(new Choice(gr->T("Display layout & effects"), ImageID("I_DISPLAY")))->OnClick.Add([this](UI::EventParams &) -> void {
screenManager()->push(new DisplayLayoutScreen(gamePath_));
});
if (g_Config.bShowTouchControls) {
rightColumnItems->Add(new Choice(co->T("Edit touch control layout..."), ImageID("I_CONTROLLER")))->OnClick.Add([&](UI::EventParams &) -> void {
rightColumnItems->Add(new Choice(co->T("Edit touch control layout..."), ImageID("I_CONTROLLER")))->OnClick.Add([this](UI::EventParams &) -> void {
screenManager()->push(new TouchControlLayoutScreen(gamePath_));
});
}
if (g_Config.bEnableCheats && PSP_CoreParameter().fileType != IdentifiedFileType::PPSSPP_GE_DUMP) {
rightColumnItems->Add(new Choice(pa->T("Cheats"), ImageID("I_CHEAT")))->OnClick.Add([&](UI::EventParams &e) {
rightColumnItems->Add(new Choice(pa->T("Cheats"), ImageID("I_CHEAT")))->OnClick.Add([this](UI::EventParams &e) {
screenManager()->push(new CwCheatScreen(gamePath_));
});
}
@@ -668,9 +668,9 @@ void GamePauseScreen::CreateViews() {
exit->SetEnabled(!bootPending_);
if (middleColumn) {
middleColumn->SetSpacing(portrait ? 8.0f : 20.0f);
middleColumn->SetSpacing(portrait ? 8.0f : 0.0f);
playButton_ = middleColumn->Add(new Choice(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY"), new LinearLayoutParams(64, 64)));
playButton_->OnClick.Add([=](UI::EventParams &e) {
playButton_->OnClick.Add([this](UI::EventParams &e) {
g_Config.bRunBehindPauseMenu = !g_Config.bRunBehindPauseMenu;
playButton_->SetIconLeft(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY"));
});
@@ -678,12 +678,16 @@ void GamePauseScreen::CreateViews() {
bool mustRunBehind = MustRunBehind();
playButton_->SetVisibility(mustRunBehind ? UI::V_GONE : UI::V_VISIBLE);
if (!portrait) {
middleColumn->Add(new Spacer(20.0f));
}
Choice *infoButton = middleColumn->Add(new Choice(ImageID("I_INFO"), new LinearLayoutParams(64, 64)));
infoButton->OnClick.Add([=](UI::EventParams &e) {
infoButton->OnClick.Add([this](UI::EventParams &e) {
screenManager()->push(new GameScreen(gamePath_, true));
});
if (true || System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
AddRotationPicker(screenManager(), middleColumn, false);
}
@@ -706,7 +710,7 @@ void GamePauseScreen::ShowContextMenu(UI::View *menuButton, bool portrait) {
std::string confirmMessage = GetConfirmExitMessage();
if (!confirmMessage.empty()) {
auto di = GetI18NCategory(I18NCat::DIALOG);
screenManager()->push(new UI::MessagePopupScreen(di->T("Reset"), confirmMessage, di->T("Reset"), di->T("Cancel"), [=](bool result) {
screenManager()->push(new UI::MessagePopupScreen(di->T("Reset"), confirmMessage, di->T("Reset"), di->T("Cancel"), [this](bool result) {
if (result) {
System_PostUIMessage(UIMessage::REQUEST_GAME_RESET);
}
@@ -802,7 +806,7 @@ void GamePauseScreen::OnExit(UI::EventParams &e) {
if (!confirmExitMessage.empty()) {
auto di = GetI18NCategory(I18NCat::DIALOG);
std::string_view title = di->T("Are you sure you want to exit?");
screenManager()->push(new UI::MessagePopupScreen(title, confirmExitMessage, di->T("Exit"), di->T("Cancel"), [=](bool result) {
screenManager()->push(new UI::MessagePopupScreen(title, confirmExitMessage, di->T("Exit"), di->T("Cancel"), [this](bool result) {
if (result) {
if (g_Config.bPauseMenuExitsEmulator) {
System_ExitApp();
+18
View File
@@ -2088,3 +2088,21 @@ ULUS10077 = true
# Ghost Recon - Predator. See #20856
ULUS10445 = true
ULES01350 = True
[NullPageValid]
# Works around crashes in some games (such as GoW: GoS, see issue #14958) that access invalid memory pages, by mapping some extra memory at address 0.
# GOW : Ghost of Sparta
UCUS98737 = true
UCAS40323 = true
NPHG00092 = true
NPEG00044 = true
NPEG00045 = true
NPJG00120 = true
NPUG80508 = true
UCJS10114 = true
UCES01401 = true
UCES01473 = true
# GOW : Ghost of Sparta Demo
NPJG90095 = true
NPEG90035 = true
NPUG70125 = true
+1 -1
View File
@@ -107,7 +107,7 @@ static void SetupJitHarness() {
PSP_CoreParameter().cpuCore = CPUCore::INTERPRETER;
PSP_CoreParameter().fastForward = true;
Memory::Init();
Memory::Init(Memory::MemMapSetupFlags::Default);
mipsr4k.Reset();
CoreTiming::Init();
InitVFPU();