Restarting a map on SP with numerous custom scripts can cause an out-of-memory error #1

Closed
opened 2025-07-16 14:04:31 +00:00 by Future · 3 comments
Owner

Restarting a map on SP with numerous custom scripts can cause an out-of-memory error.

"Persona Non Grata"

userraw.zip

💡 Idea from H1-Mod: https://github.com/auroramod/h1-mod/blob/develop/src/client/component/gsc/script_loading.cpp

Implement custom heap alloc code and pop it by implementing PMem_PopFromSource_NoDebug

Restarting a map on SP with numerous custom scripts can cause an out-of-memory error. "Persona Non Grata" [userraw.zip](/attachments/83a67eca-f74f-44d6-97d2-dbb4c31a4943) 💡 Idea from H1-Mod: https://github.com/auroramod/h1-mod/blob/develop/src/client/component/gsc/script_loading.cpp Implement custom heap alloc code and pop it by implementing `PMem_PopFromSource_NoDebug`
Author
Owner

I asked Grok 3 to backport S1/H1's version of the pop function

#include <stdint.h>
#include <stdbool.h>

// Assuming these are defined elsewhere
typedef struct {
    unsigned char *buf;
    PhysicalMemoryPrim prim[2];
} PhysicalMemory;

typedef struct {
    const char *allocName;
    unsigned int allocListCount;
    unsigned int pos;
    PhysicalMemoryAllocation allocList[32];
} PhysicalMemoryPrim;

typedef struct {
    const char *name;
    unsigned int pos;
} PhysicalMemoryAllocation;

enum PMem_Source {
    PMEM_SOURCE_EXTERNAL,
    PMEM_SOURCE_DATABASE,
    PMEM_SOURCE_MOVIE,
    PMEM_SOURCE_SCRIPT,
};

// External variables
extern PhysicalMemory g_mem;
extern PhysicalMemory g_scriptmem;

// External functions (assumed to exist in iw5)
extern bool DB_IsAlloc(void);
extern uint32_t DB_GetAllocType(void);
extern void Sys_OutOfMemErrorInternal(void);

int __cdecl PMem_PopFromSource_NoDebug(void *ptr, unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source) {
    PhysicalMemory *mem;
    uint32_t allocDir;
    PhysicalMemoryPrim *prim;
    uint32_t alignedPos;
    unsigned char *basePtr;

    // Select the appropriate memory pool based on source
    if (source == PMEM_SOURCE_SCRIPT) {
        mem = &g_scriptmem;
    } else {
        mem = &g_mem;
        if (source == PMEM_SOURCE_MOVIE) {
            allocDir = 0; // Low pool for movie source
            goto select_pool;
        }
    }

    // Determine allocation direction (0 for low, 1 for high)
    if (DB_IsAlloc()) {
        allocDir = DB_GetAllocType();
    } else {
        allocDir = (type != 4); // 0 if type == 4, 1 otherwise
    }

select_pool:
    prim = &mem->prim[allocDir];
    basePtr = mem->buf;

    // Calculate aligned position for comparison
    alignment = alignment - 1;

    if (allocDir == 0) {
        // Low pool: memory is allocated upward
        uint32_t currentPos = prim->pos - size;
        if (&basePtr[currentPos] == ptr) {
            // Ensure alignment
            if ((currentPos + alignment) & ~alignment == currentPos) {
                prim->pos = currentPos;
                return 1; // Success
            }
        }
    } else {
        // High pool: memory is allocated downward
        uint32_t currentPos = prim->pos;
        if (&basePtr[currentPos] == ptr) {
            // Ensure alignment
            if ((currentPos + alignment) & ~alignment == currentPos) {
                prim->pos = currentPos + size;
                return 1; // Success
            }
        }
    }

    // Failure: pointer does not match the expected position
    return 0;
}
I asked Grok 3 to backport S1/H1's version of the pop function ``` #include <stdint.h> #include <stdbool.h> // Assuming these are defined elsewhere typedef struct { unsigned char *buf; PhysicalMemoryPrim prim[2]; } PhysicalMemory; typedef struct { const char *allocName; unsigned int allocListCount; unsigned int pos; PhysicalMemoryAllocation allocList[32]; } PhysicalMemoryPrim; typedef struct { const char *name; unsigned int pos; } PhysicalMemoryAllocation; enum PMem_Source { PMEM_SOURCE_EXTERNAL, PMEM_SOURCE_DATABASE, PMEM_SOURCE_MOVIE, PMEM_SOURCE_SCRIPT, }; // External variables extern PhysicalMemory g_mem; extern PhysicalMemory g_scriptmem; // External functions (assumed to exist in iw5) extern bool DB_IsAlloc(void); extern uint32_t DB_GetAllocType(void); extern void Sys_OutOfMemErrorInternal(void); int __cdecl PMem_PopFromSource_NoDebug(void *ptr, unsigned int size, unsigned int alignment, unsigned int type, PMem_Source source) { PhysicalMemory *mem; uint32_t allocDir; PhysicalMemoryPrim *prim; uint32_t alignedPos; unsigned char *basePtr; // Select the appropriate memory pool based on source if (source == PMEM_SOURCE_SCRIPT) { mem = &g_scriptmem; } else { mem = &g_mem; if (source == PMEM_SOURCE_MOVIE) { allocDir = 0; // Low pool for movie source goto select_pool; } } // Determine allocation direction (0 for low, 1 for high) if (DB_IsAlloc()) { allocDir = DB_GetAllocType(); } else { allocDir = (type != 4); // 0 if type == 4, 1 otherwise } select_pool: prim = &mem->prim[allocDir]; basePtr = mem->buf; // Calculate aligned position for comparison alignment = alignment - 1; if (allocDir == 0) { // Low pool: memory is allocated upward uint32_t currentPos = prim->pos - size; if (&basePtr[currentPos] == ptr) { // Ensure alignment if ((currentPos + alignment) & ~alignment == currentPos) { prim->pos = currentPos; return 1; // Success } } } else { // High pool: memory is allocated downward uint32_t currentPos = prim->pos; if (&basePtr[currentPos] == ptr) { // Ensure alignment if ((currentPos + alignment) & ~alignment == currentPos) { prim->pos = currentPos + size; return 1; // Success } } } // Failure: pointer does not match the expected position return 0; } ```
Author
Owner

should be fixed as of today? do you know what day is today?

should be fixed as of today? do you know what day is today?
Author
Owner

Keep in mind, I also asked Claude sonnet to take a look at Grok's code and turns out it was matching more or less what H1 gave me via IDA's pseudo-code, so I checked manually that it did make sense, there are many projects that now use AI to decomp CoD so I think it's fair to let AI take a look at something and then check manually. (Would that take as much time as doing it yourself? I don't know but it's probably a moral booster if AI gives you clear code coming from IDA and then you just have to check if it does "make sense" all things considered. Anyhow, this issue was in the works for a year basically so nothing was rushed with AI.

Also, testing this was difficult, it appears no memory leak was found so far. It appears it was SP specific (Pluto IW5 uses the same code as iw5-mod for MP and no memory leak was ever found). This code will help make sure the GSC byte code is always unloaded manually on both SP and MP

Keep in mind, I also asked Claude sonnet to take a look at Grok's code and turns out it was matching more or less what H1 gave me via IDA's pseudo-code, so I checked manually that it did make sense, there are many projects that now use AI to decomp CoD so I think it's fair to let AI take a look at something and then check manually. (Would that take as much time as doing it yourself? I don't know but it's probably a moral booster if AI gives you clear code coming from IDA and then you just have to check if it does "make sense" all things considered. Anyhow, this issue was in the works for a year basically so nothing was rushed with AI. Also, testing this was difficult, it appears no memory leak was found so far. It appears it was SP specific (Pluto IW5 uses the same code as iw5-mod for MP and no memory leak was ever found). This code will help make sure the GSC byte code is always unloaded manually on both SP and MP
This repo is archived. You cannot comment on issues.
No labels
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alterware/iw5-mod#1