Path: blob/master/RSDKv5/RSDK/Storage/Storage.cpp
1163 views
#include "RSDK/Core/RetroEngine.hpp"12using namespace RSDK;34#if RETRO_REV0U5#include "Legacy/UserStorageLegacy.cpp"6#endif78// Macro to access the header variables of a block of memory.9// Note that this is pointless if the pointer is already pointing directly at the header rather than the memory after it.10#define HEADER(memory, header_value) memory[-HEADER_SIZE + header_value]1112// Every block of allocated memory is prefixed with a header that consists of the following four longwords.13enum {14// Whether the block of memory is actually allocated or not.15HEADER_ACTIVE,16// Which 'data set' this block of memory belongs to.17HEADER_SET_ID,18// The offset in the buffer which the block of memory begins at.19HEADER_DATA_OFFSET,20// How long the block of memory is (measured in 'uint32's).21HEADER_DATA_LENGTH,22// This is not part of the header: it's just a bit of enum magic to calculate the size of the header.23HEADER_SIZE24};2526DataStorage RSDK::dataStorage[DATASET_MAX];2728bool32 RSDK::InitStorage()29{30// Storage limits.31dataStorage[DATASET_STG].storageLimit = 24 * 1024 * 1024; // 24MB32dataStorage[DATASET_MUS].storageLimit = 8 * 1024 * 1024; // 8MB33dataStorage[DATASET_SFX].storageLimit = 32 * 1024 * 1024; // 32MB34dataStorage[DATASET_STR].storageLimit = 2 * 1024 * 1024; // 2MB35dataStorage[DATASET_TMP].storageLimit = 8 * 1024 * 1024; // 8MB3637for (int32 s = 0; s < DATASET_MAX; ++s) {38dataStorage[s].usedStorage = 0;39dataStorage[s].entryCount = 0;40dataStorage[s].clearCount = 0;41dataStorage[s].memoryTable = (uint32 *)malloc(dataStorage[s].storageLimit);4243if (dataStorage[s].memoryTable == NULL)44return false;45}4647return true;48}4950void RSDK::ReleaseStorage()51{52for (int32 s = 0; s < DATASET_MAX; ++s) {53if (dataStorage[s].memoryTable != NULL)54free(dataStorage[s].memoryTable);5556dataStorage[s].usedStorage = 0;57dataStorage[s].entryCount = 0;58dataStorage[s].clearCount = 0;59}6061// this code isn't in steam executable, since it omits the "load datapack into memory" feature.62// I don't think it's in the console versions either, but this never seems to be freed in those versions.63// so, I figured doing it here would be the neatest.64#if !RETRO_USE_ORIGINAL_CODE65for (int32 p = 0; p < dataPackCount; ++p) {66if (dataPacks[p].fileBuffer)67free(dataPacks[p].fileBuffer);6869dataPacks[p].fileBuffer = NULL;70}71#endif72}7374void RSDK::AllocateStorage(void **dataPtr, uint32 size, StorageDataSets dataSet, bool32 clear)75{76uint32 **data = (uint32 **)dataPtr;77*data = NULL;7879if ((uint32)dataSet < DATASET_MAX) {80// Align allocation to prevent unaligned memory accesses later on.81const uint32 size_aligned = size & -(int32)sizeof(void *);8283if (size_aligned < size)84size = size_aligned + sizeof(void *);8586if (dataStorage[dataSet].entryCount < STORAGE_ENTRY_COUNT) {87DataStorage *storage = &dataStorage[dataSet];8889#if !RETRO_USE_ORIGINAL_CODE90// Bug: The original release never takes into account the size of the header when checking if there's enough storage left.91// Omitting this will overflow the memory pool when (storageLimit - usedStorage + size) < header size (16 bytes here).92if (storage->usedStorage * sizeof(uint32) + size + (HEADER_SIZE * sizeof(uint32)) < storage->storageLimit) {93#else94if (storage->usedStorage * sizeof(uint32) + size < storage->storageLimit) {95#endif96// HEADER_ACTIVE97storage->memoryTable[storage->usedStorage] = true;98++storage->usedStorage;99100// HEADER_SET_ID101storage->memoryTable[storage->usedStorage] = dataSet;102++storage->usedStorage;103104// HEADER_DATA_OFFSET105storage->memoryTable[storage->usedStorage] = storage->usedStorage + HEADER_SIZE - HEADER_DATA_OFFSET;106++storage->usedStorage;107108// HEADER_DATA_LENGTH109storage->memoryTable[storage->usedStorage] = size;110++storage->usedStorage;111112*data = &storage->memoryTable[storage->usedStorage];113storage->usedStorage += size / sizeof(uint32);114115dataStorage[dataSet].dataEntries[storage->entryCount] = data;116dataStorage[dataSet].storageEntries[storage->entryCount] = *data;117118++storage->entryCount;119}120else {121// We've run out of room, so perform defragmentation and garbage-collection.122DefragmentAndGarbageCollectStorage(dataSet);123124// If there is now room, then perform allocation.125// Yes, this really is a massive chunk of duplicate code.126#if !RETRO_USE_ORIGINAL_CODE127if (storage->usedStorage * sizeof(uint32) + size + (HEADER_SIZE * sizeof(uint32)) < storage->storageLimit) {128#else129if (storage->usedStorage * sizeof(uint32) + size < storage->storageLimit) {130#endif131// HEADER_ACTIVE132storage->memoryTable[storage->usedStorage] = true;133++storage->usedStorage;134135// HEADER_SET_ID136storage->memoryTable[storage->usedStorage] = dataSet;137++storage->usedStorage;138139// HEADER_DATA_OFFSET140storage->memoryTable[storage->usedStorage] = storage->usedStorage + HEADER_SIZE - HEADER_DATA_OFFSET;141++storage->usedStorage;142143// HEADER_DATA_LENGTH144storage->memoryTable[storage->usedStorage] = size;145++storage->usedStorage;146147*data = &storage->memoryTable[storage->usedStorage];148storage->usedStorage += size / sizeof(uint32);149150dataStorage[dataSet].dataEntries[storage->entryCount] = data;151dataStorage[dataSet].storageEntries[storage->entryCount] = *data;152153++storage->entryCount;154}155}156157// If there are too many storage entries, then perform garbage collection.158if (storage->entryCount >= STORAGE_ENTRY_COUNT)159GarbageCollectStorage(dataSet);160161// Clear the allocated memory if requested.162if (*data != NULL && clear == (bool32)true)163memset(*data, 0, size);164}165}166}167168void RSDK::RemoveStorageEntry(void **dataPtr)169{170if (dataPtr != NULL && *dataPtr != NULL) {171uint32 *data = *(uint32 **)dataPtr;172173uint32 set = HEADER(data, HEADER_SET_ID);174for (int32 e = 0; e < dataStorage[set].entryCount; ++e) {175#if !RETRO_USE_ORIGINAL_CODE176// make sure dataEntries[e] isn't null. If it is null by some ungodly chance then it was prolly already freed or something idk177if (dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] && *dataPtr == *dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e]) {178*dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;179dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;180}181#else182if (*dataPtr == *dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e]) {183*dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;184dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;185}186#endif187188set = HEADER(data, HEADER_SET_ID);189}190191uint32 newEntryCount = 0;192set = HEADER(data, HEADER_SET_ID);193for (uint32 entryID = 0; entryID < dataStorage[set].entryCount; ++entryID) {194if (dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID]) {195if (entryID != newEntryCount) {196dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[newEntryCount] =197dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID];198dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID] = NULL;199dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[newEntryCount] =200dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[entryID];201dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[entryID] = NULL;202}203204++newEntryCount;205}206207set = HEADER(data, HEADER_SET_ID);208}209210dataStorage[HEADER(data, HEADER_SET_ID)].entryCount = newEntryCount;211212for (uint32 e = newEntryCount; e < STORAGE_ENTRY_COUNT; ++e) {213dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;214dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[e] = NULL;215}216217HEADER(data, HEADER_ACTIVE) = false;218}219}220221// This defragments the storage, leaving all empty space at the end.222void RSDK::DefragmentAndGarbageCollectStorage(StorageDataSets set)223{224uint32 processedStorage = 0;225uint32 unusedStorage = 0;226227uint32 *defragmentDestination = dataStorage[set].memoryTable;228uint32 *currentHeader = dataStorage[set].memoryTable;229230++dataStorage[set].clearCount;231232// Perform garbage-collection. This deallocates all memory allocations that are no longer being used.233GarbageCollectStorage(set);234235// This performs defragmentation. It works by removing 'gaps' between the various blocks of allocated memory,236// grouping them all together at the start of the buffer while all the empty space goes at the end.237// Avoiding fragmentation is important, as fragmentation can cause allocations to fail despite there being238// enough free memory because that free memory isn't contiguous.239while (processedStorage < dataStorage[set].usedStorage) {240uint32 *dataPtr = &dataStorage[set].memoryTable[currentHeader[HEADER_DATA_OFFSET]];241uint32 size = (currentHeader[HEADER_DATA_LENGTH] / sizeof(uint32)) + HEADER_SIZE;242243// Check if this block of memory is currently allocated.244currentHeader[HEADER_ACTIVE] = false;245246for (int32 e = 0; e < dataStorage[set].entryCount; ++e)247if (dataPtr == dataStorage[set].storageEntries[e])248currentHeader[HEADER_ACTIVE] = true;249250if (currentHeader[HEADER_ACTIVE]) {251// This memory is being used.252processedStorage += size;253254if (currentHeader > defragmentDestination) {255// This memory has a gap before it, so move it backwards into that free space.256for (uint32 i = 0; i < size; ++i) *defragmentDestination++ = *currentHeader++;257}258else {259// This memory doesn't have a gap before it, so we don't need to move it - just skip it instead.260defragmentDestination += size;261currentHeader += size;262}263}264else {265// This memory is not being used, so skip it.266currentHeader += size;267processedStorage += size;268unusedStorage += size;269}270}271272// If defragmentation occurred, then we need to update every single273// pointer to allocated memory to point to their new locations in the buffer.274if (unusedStorage != 0) {275dataStorage[set].usedStorage -= unusedStorage;276277uint32 *currentHeader = dataStorage[set].memoryTable;278279uint32 dataOffset = 0;280while (dataOffset < dataStorage[set].usedStorage) {281uint32 *dataPtr = &dataStorage[set].memoryTable[currentHeader[HEADER_DATA_OFFSET]];282uint32 size = (currentHeader[HEADER_DATA_LENGTH] / sizeof(uint32)) + HEADER_SIZE; // size (in int32s)283284// Find every single pointer to this memory allocation and update them with its new address.285for (int32 c = 0; c < dataStorage[set].entryCount; ++c)286287#if !RETRO_USE_ORIGINAL_CODE288// make sure dataEntries[e] isn't null. If it is null by some ungodly chance then it was prolly already freed or something idk289if (dataPtr == dataStorage[set].storageEntries[c] && dataStorage[set].dataEntries[c])290dataStorage[set].storageEntries[c] = *dataStorage[set].dataEntries[c] = currentHeader + HEADER_SIZE;291#else292if (dataPtr == dataStorage[set].storageEntries[c])293dataStorage[set].storageEntries[c] = *dataStorage[set].dataEntries[c] = currentHeader + HEADER_SIZE;294#endif295296297// Update the offset in the allocation's header too.298currentHeader[HEADER_DATA_OFFSET] = dataOffset + HEADER_SIZE;299300// Advance to the next memory allocation.301currentHeader += size;302dataOffset += size;303}304}305}306307void RSDK::CopyStorage(uint32 **src, uint32 **dst)308{309if (dst != NULL) {310uint32 *dstPtr = *dst;311*src = *dst;312313if (dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount < STORAGE_ENTRY_COUNT) {314dataStorage[HEADER(dstPtr, HEADER_SET_ID)].dataEntries[dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount] = src;315dataStorage[HEADER(dstPtr, HEADER_SET_ID)].storageEntries[dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount] = *src;316317++dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount;318319if (dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount >= STORAGE_ENTRY_COUNT)320GarbageCollectStorage((StorageDataSets)HEADER(dstPtr, HEADER_SET_ID));321}322}323}324325void RSDK::GarbageCollectStorage(StorageDataSets set)326{327if ((uint32)set < DATASET_MAX) {328for (uint32 e = 0; e < dataStorage[set].entryCount; ++e) {329// So what's happening here is the engine is checking to see if the storage entry330// (which is the pointer to the "memoryTable" offset that is allocated for this entry)331// matches what the actual variable that allocated the storage is currently pointing to.332// if they don't match, the storage entry is considered invalid and marked for removal.333334if (dataStorage[set].dataEntries[e] != NULL && *dataStorage[set].dataEntries[e] != dataStorage[set].storageEntries[e])335dataStorage[set].dataEntries[e] = NULL;336}337338uint32 newEntryCount = 0;339for (uint32 entryID = 0; entryID < dataStorage[set].entryCount; ++entryID) {340if (dataStorage[set].dataEntries[entryID]) {341if (entryID != newEntryCount) {342dataStorage[set].dataEntries[newEntryCount] = dataStorage[set].dataEntries[entryID];343dataStorage[set].dataEntries[entryID] = NULL;344dataStorage[set].storageEntries[newEntryCount] = dataStorage[set].storageEntries[entryID];345dataStorage[set].storageEntries[entryID] = NULL;346}347348++newEntryCount;349}350}351dataStorage[set].entryCount = newEntryCount;352353for (int32 e = dataStorage[set].entryCount; e < STORAGE_ENTRY_COUNT; ++e) {354dataStorage[set].dataEntries[e] = NULL;355dataStorage[set].storageEntries[e] = NULL;356}357}358}359360361