Clear the codec context maps per boot, and replace functions in headless too

sceMpeg and sceAudiocodec only cleared their context maps on shutdown, while
sceMpegbase and sceVideocodec clear theirs on init. Both maps are keyed on an
address the game chooses, and getMpegCtx reads its key straight out of game
memory, so anything left behind can be handed to the next game we run in the
same session. Clear them on init as well. sceVideocodec's init cleared its map
without deleting the decoders in it; use ClearContexts for that.

Headless never loads a config file, so every g_Config field it doesn't set
keeps the zero-initialized value rather than the ConfigSetting default.
bFuncReplacements is one of those, so headless was the only build running games
without function replacements - which is why a crash in Jak and Daxter's
memcpy_jak wouldn't reproduce there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-17 12:51:52 -06:00
1 parent bb906f33aa
commit 0207c6d04a
4 files changed
+21 -7

No files matched your search

+3
View File
@@ -210,6 +210,9 @@ static void clearDecoders() {
}
void __AudioCodecInit() {
// findDecoder keys on a game-chosen context address, so don't leave decoders from a previous
// game around for the next one to find.
clearDecoders();
oldStateLoaded = false;
}
+11 -5
View File
@@ -348,8 +348,18 @@ private:
s32 remainingPackets_ = 0;
};
static void ClearMpegContexts() {
for (const auto &[_, ctx] : g_mpegCtxs) {
delete ctx;
}
g_mpegCtxs.clear();
}
void __MpegInit() {
__MpegBaseInit();
// getMpegCtx keys on a handle read out of game memory, so don't leave contexts from a previous
// game around for the next one to find.
ClearMpegContexts();
isMpegInit = false;
mpegLibVersion = 0x010A;
streamIdGen = 1;
@@ -403,11 +413,7 @@ void __MpegDoState(PointerWrap &p) {
}
void __MpegShutdown() {
std::map<u32, MpegContext *>::iterator it, end;
for (it = g_mpegCtxs.begin(), end = g_mpegCtxs.end(); it != end; ++it) {
delete it->second;
}
g_mpegCtxs.clear();
ClearMpegContexts();
}
void __MpegLoadModule(int version,u32 crc) {
+3 -2
View File
@@ -162,8 +162,9 @@ static void ClearContexts(bool freeMemory) {
}
void __VideocodecInit() {
// Nothing to free: a boot starts with a fresh allocator.
g_videocodecCtxs.clear();
// The decoders have to be deleted; the ME blocks they hold don't need freeing individually,
// since the allocator is emptied right below.
ClearContexts(false);
g_meRam.clear();
g_meRam.shrink_to_fit();
g_meAlloc.Shutdown();
+4
View File
@@ -785,6 +785,10 @@ int main(int argc, const char* argv[]) {
g_Config.iInternalResolution = cmdLineOptions.resolutionScale.value_or(1);
g_Config.bEnableLogging = (fullLog || outputDebugStringLog);
g_Config.bVertexDecoderJit = true;
// Headless never loads a config file, so anything not set here keeps the zero-initialized
// value rather than the ConfigSetting default. This one defaults to true in the app, and
// leaving it false made headless run games differently from every other build.
g_Config.bFuncReplacements = true;
g_Config.bSoftwareRendering = cmdLineOptions.softwareRendering.value_or(false);
g_Config.bSoftwareRenderingJit = true;
g_Config.iSplineBezierQuality = 2;