Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Rubberduckycooly
GitHub Repository: Rubberduckycooly/RSDKv5-Decompilation
Path: blob/master/RSDKv5/RSDK/Storage/Storage.cpp
1163 views
1
#include "RSDK/Core/RetroEngine.hpp"
2
3
using namespace RSDK;
4
5
#if RETRO_REV0U
6
#include "Legacy/UserStorageLegacy.cpp"
7
#endif
8
9
// Macro to access the header variables of a block of memory.
10
// Note that this is pointless if the pointer is already pointing directly at the header rather than the memory after it.
11
#define HEADER(memory, header_value) memory[-HEADER_SIZE + header_value]
12
13
// Every block of allocated memory is prefixed with a header that consists of the following four longwords.
14
enum {
15
// Whether the block of memory is actually allocated or not.
16
HEADER_ACTIVE,
17
// Which 'data set' this block of memory belongs to.
18
HEADER_SET_ID,
19
// The offset in the buffer which the block of memory begins at.
20
HEADER_DATA_OFFSET,
21
// How long the block of memory is (measured in 'uint32's).
22
HEADER_DATA_LENGTH,
23
// This is not part of the header: it's just a bit of enum magic to calculate the size of the header.
24
HEADER_SIZE
25
};
26
27
DataStorage RSDK::dataStorage[DATASET_MAX];
28
29
bool32 RSDK::InitStorage()
30
{
31
// Storage limits.
32
dataStorage[DATASET_STG].storageLimit = 24 * 1024 * 1024; // 24MB
33
dataStorage[DATASET_MUS].storageLimit = 8 * 1024 * 1024; // 8MB
34
dataStorage[DATASET_SFX].storageLimit = 32 * 1024 * 1024; // 32MB
35
dataStorage[DATASET_STR].storageLimit = 2 * 1024 * 1024; // 2MB
36
dataStorage[DATASET_TMP].storageLimit = 8 * 1024 * 1024; // 8MB
37
38
for (int32 s = 0; s < DATASET_MAX; ++s) {
39
dataStorage[s].usedStorage = 0;
40
dataStorage[s].entryCount = 0;
41
dataStorage[s].clearCount = 0;
42
dataStorage[s].memoryTable = (uint32 *)malloc(dataStorage[s].storageLimit);
43
44
if (dataStorage[s].memoryTable == NULL)
45
return false;
46
}
47
48
return true;
49
}
50
51
void RSDK::ReleaseStorage()
52
{
53
for (int32 s = 0; s < DATASET_MAX; ++s) {
54
if (dataStorage[s].memoryTable != NULL)
55
free(dataStorage[s].memoryTable);
56
57
dataStorage[s].usedStorage = 0;
58
dataStorage[s].entryCount = 0;
59
dataStorage[s].clearCount = 0;
60
}
61
62
// this code isn't in steam executable, since it omits the "load datapack into memory" feature.
63
// I don't think it's in the console versions either, but this never seems to be freed in those versions.
64
// so, I figured doing it here would be the neatest.
65
#if !RETRO_USE_ORIGINAL_CODE
66
for (int32 p = 0; p < dataPackCount; ++p) {
67
if (dataPacks[p].fileBuffer)
68
free(dataPacks[p].fileBuffer);
69
70
dataPacks[p].fileBuffer = NULL;
71
}
72
#endif
73
}
74
75
void RSDK::AllocateStorage(void **dataPtr, uint32 size, StorageDataSets dataSet, bool32 clear)
76
{
77
uint32 **data = (uint32 **)dataPtr;
78
*data = NULL;
79
80
if ((uint32)dataSet < DATASET_MAX) {
81
// Align allocation to prevent unaligned memory accesses later on.
82
const uint32 size_aligned = size & -(int32)sizeof(void *);
83
84
if (size_aligned < size)
85
size = size_aligned + sizeof(void *);
86
87
if (dataStorage[dataSet].entryCount < STORAGE_ENTRY_COUNT) {
88
DataStorage *storage = &dataStorage[dataSet];
89
90
#if !RETRO_USE_ORIGINAL_CODE
91
// Bug: The original release never takes into account the size of the header when checking if there's enough storage left.
92
// Omitting this will overflow the memory pool when (storageLimit - usedStorage + size) < header size (16 bytes here).
93
if (storage->usedStorage * sizeof(uint32) + size + (HEADER_SIZE * sizeof(uint32)) < storage->storageLimit) {
94
#else
95
if (storage->usedStorage * sizeof(uint32) + size < storage->storageLimit) {
96
#endif
97
// HEADER_ACTIVE
98
storage->memoryTable[storage->usedStorage] = true;
99
++storage->usedStorage;
100
101
// HEADER_SET_ID
102
storage->memoryTable[storage->usedStorage] = dataSet;
103
++storage->usedStorage;
104
105
// HEADER_DATA_OFFSET
106
storage->memoryTable[storage->usedStorage] = storage->usedStorage + HEADER_SIZE - HEADER_DATA_OFFSET;
107
++storage->usedStorage;
108
109
// HEADER_DATA_LENGTH
110
storage->memoryTable[storage->usedStorage] = size;
111
++storage->usedStorage;
112
113
*data = &storage->memoryTable[storage->usedStorage];
114
storage->usedStorage += size / sizeof(uint32);
115
116
dataStorage[dataSet].dataEntries[storage->entryCount] = data;
117
dataStorage[dataSet].storageEntries[storage->entryCount] = *data;
118
119
++storage->entryCount;
120
}
121
else {
122
// We've run out of room, so perform defragmentation and garbage-collection.
123
DefragmentAndGarbageCollectStorage(dataSet);
124
125
// If there is now room, then perform allocation.
126
// Yes, this really is a massive chunk of duplicate code.
127
#if !RETRO_USE_ORIGINAL_CODE
128
if (storage->usedStorage * sizeof(uint32) + size + (HEADER_SIZE * sizeof(uint32)) < storage->storageLimit) {
129
#else
130
if (storage->usedStorage * sizeof(uint32) + size < storage->storageLimit) {
131
#endif
132
// HEADER_ACTIVE
133
storage->memoryTable[storage->usedStorage] = true;
134
++storage->usedStorage;
135
136
// HEADER_SET_ID
137
storage->memoryTable[storage->usedStorage] = dataSet;
138
++storage->usedStorage;
139
140
// HEADER_DATA_OFFSET
141
storage->memoryTable[storage->usedStorage] = storage->usedStorage + HEADER_SIZE - HEADER_DATA_OFFSET;
142
++storage->usedStorage;
143
144
// HEADER_DATA_LENGTH
145
storage->memoryTable[storage->usedStorage] = size;
146
++storage->usedStorage;
147
148
*data = &storage->memoryTable[storage->usedStorage];
149
storage->usedStorage += size / sizeof(uint32);
150
151
dataStorage[dataSet].dataEntries[storage->entryCount] = data;
152
dataStorage[dataSet].storageEntries[storage->entryCount] = *data;
153
154
++storage->entryCount;
155
}
156
}
157
158
// If there are too many storage entries, then perform garbage collection.
159
if (storage->entryCount >= STORAGE_ENTRY_COUNT)
160
GarbageCollectStorage(dataSet);
161
162
// Clear the allocated memory if requested.
163
if (*data != NULL && clear == (bool32)true)
164
memset(*data, 0, size);
165
}
166
}
167
}
168
169
void RSDK::RemoveStorageEntry(void **dataPtr)
170
{
171
if (dataPtr != NULL && *dataPtr != NULL) {
172
uint32 *data = *(uint32 **)dataPtr;
173
174
uint32 set = HEADER(data, HEADER_SET_ID);
175
for (int32 e = 0; e < dataStorage[set].entryCount; ++e) {
176
#if !RETRO_USE_ORIGINAL_CODE
177
// make sure dataEntries[e] isn't null. If it is null by some ungodly chance then it was prolly already freed or something idk
178
if (dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] && *dataPtr == *dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e]) {
179
*dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;
180
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;
181
}
182
#else
183
if (*dataPtr == *dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e]) {
184
*dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;
185
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;
186
}
187
#endif
188
189
set = HEADER(data, HEADER_SET_ID);
190
}
191
192
uint32 newEntryCount = 0;
193
set = HEADER(data, HEADER_SET_ID);
194
for (uint32 entryID = 0; entryID < dataStorage[set].entryCount; ++entryID) {
195
if (dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID]) {
196
if (entryID != newEntryCount) {
197
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[newEntryCount] =
198
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID];
199
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[entryID] = NULL;
200
dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[newEntryCount] =
201
dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[entryID];
202
dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[entryID] = NULL;
203
}
204
205
++newEntryCount;
206
}
207
208
set = HEADER(data, HEADER_SET_ID);
209
}
210
211
dataStorage[HEADER(data, HEADER_SET_ID)].entryCount = newEntryCount;
212
213
for (uint32 e = newEntryCount; e < STORAGE_ENTRY_COUNT; ++e) {
214
dataStorage[HEADER(data, HEADER_SET_ID)].dataEntries[e] = NULL;
215
dataStorage[HEADER(data, HEADER_SET_ID)].storageEntries[e] = NULL;
216
}
217
218
HEADER(data, HEADER_ACTIVE) = false;
219
}
220
}
221
222
// This defragments the storage, leaving all empty space at the end.
223
void RSDK::DefragmentAndGarbageCollectStorage(StorageDataSets set)
224
{
225
uint32 processedStorage = 0;
226
uint32 unusedStorage = 0;
227
228
uint32 *defragmentDestination = dataStorage[set].memoryTable;
229
uint32 *currentHeader = dataStorage[set].memoryTable;
230
231
++dataStorage[set].clearCount;
232
233
// Perform garbage-collection. This deallocates all memory allocations that are no longer being used.
234
GarbageCollectStorage(set);
235
236
// This performs defragmentation. It works by removing 'gaps' between the various blocks of allocated memory,
237
// grouping them all together at the start of the buffer while all the empty space goes at the end.
238
// Avoiding fragmentation is important, as fragmentation can cause allocations to fail despite there being
239
// enough free memory because that free memory isn't contiguous.
240
while (processedStorage < dataStorage[set].usedStorage) {
241
uint32 *dataPtr = &dataStorage[set].memoryTable[currentHeader[HEADER_DATA_OFFSET]];
242
uint32 size = (currentHeader[HEADER_DATA_LENGTH] / sizeof(uint32)) + HEADER_SIZE;
243
244
// Check if this block of memory is currently allocated.
245
currentHeader[HEADER_ACTIVE] = false;
246
247
for (int32 e = 0; e < dataStorage[set].entryCount; ++e)
248
if (dataPtr == dataStorage[set].storageEntries[e])
249
currentHeader[HEADER_ACTIVE] = true;
250
251
if (currentHeader[HEADER_ACTIVE]) {
252
// This memory is being used.
253
processedStorage += size;
254
255
if (currentHeader > defragmentDestination) {
256
// This memory has a gap before it, so move it backwards into that free space.
257
for (uint32 i = 0; i < size; ++i) *defragmentDestination++ = *currentHeader++;
258
}
259
else {
260
// This memory doesn't have a gap before it, so we don't need to move it - just skip it instead.
261
defragmentDestination += size;
262
currentHeader += size;
263
}
264
}
265
else {
266
// This memory is not being used, so skip it.
267
currentHeader += size;
268
processedStorage += size;
269
unusedStorage += size;
270
}
271
}
272
273
// If defragmentation occurred, then we need to update every single
274
// pointer to allocated memory to point to their new locations in the buffer.
275
if (unusedStorage != 0) {
276
dataStorage[set].usedStorage -= unusedStorage;
277
278
uint32 *currentHeader = dataStorage[set].memoryTable;
279
280
uint32 dataOffset = 0;
281
while (dataOffset < dataStorage[set].usedStorage) {
282
uint32 *dataPtr = &dataStorage[set].memoryTable[currentHeader[HEADER_DATA_OFFSET]];
283
uint32 size = (currentHeader[HEADER_DATA_LENGTH] / sizeof(uint32)) + HEADER_SIZE; // size (in int32s)
284
285
// Find every single pointer to this memory allocation and update them with its new address.
286
for (int32 c = 0; c < dataStorage[set].entryCount; ++c)
287
288
#if !RETRO_USE_ORIGINAL_CODE
289
// make sure dataEntries[e] isn't null. If it is null by some ungodly chance then it was prolly already freed or something idk
290
if (dataPtr == dataStorage[set].storageEntries[c] && dataStorage[set].dataEntries[c])
291
dataStorage[set].storageEntries[c] = *dataStorage[set].dataEntries[c] = currentHeader + HEADER_SIZE;
292
#else
293
if (dataPtr == dataStorage[set].storageEntries[c])
294
dataStorage[set].storageEntries[c] = *dataStorage[set].dataEntries[c] = currentHeader + HEADER_SIZE;
295
#endif
296
297
298
// Update the offset in the allocation's header too.
299
currentHeader[HEADER_DATA_OFFSET] = dataOffset + HEADER_SIZE;
300
301
// Advance to the next memory allocation.
302
currentHeader += size;
303
dataOffset += size;
304
}
305
}
306
}
307
308
void RSDK::CopyStorage(uint32 **src, uint32 **dst)
309
{
310
if (dst != NULL) {
311
uint32 *dstPtr = *dst;
312
*src = *dst;
313
314
if (dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount < STORAGE_ENTRY_COUNT) {
315
dataStorage[HEADER(dstPtr, HEADER_SET_ID)].dataEntries[dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount] = src;
316
dataStorage[HEADER(dstPtr, HEADER_SET_ID)].storageEntries[dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount] = *src;
317
318
++dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount;
319
320
if (dataStorage[HEADER(dstPtr, HEADER_SET_ID)].entryCount >= STORAGE_ENTRY_COUNT)
321
GarbageCollectStorage((StorageDataSets)HEADER(dstPtr, HEADER_SET_ID));
322
}
323
}
324
}
325
326
void RSDK::GarbageCollectStorage(StorageDataSets set)
327
{
328
if ((uint32)set < DATASET_MAX) {
329
for (uint32 e = 0; e < dataStorage[set].entryCount; ++e) {
330
// So what's happening here is the engine is checking to see if the storage entry
331
// (which is the pointer to the "memoryTable" offset that is allocated for this entry)
332
// matches what the actual variable that allocated the storage is currently pointing to.
333
// if they don't match, the storage entry is considered invalid and marked for removal.
334
335
if (dataStorage[set].dataEntries[e] != NULL && *dataStorage[set].dataEntries[e] != dataStorage[set].storageEntries[e])
336
dataStorage[set].dataEntries[e] = NULL;
337
}
338
339
uint32 newEntryCount = 0;
340
for (uint32 entryID = 0; entryID < dataStorage[set].entryCount; ++entryID) {
341
if (dataStorage[set].dataEntries[entryID]) {
342
if (entryID != newEntryCount) {
343
dataStorage[set].dataEntries[newEntryCount] = dataStorage[set].dataEntries[entryID];
344
dataStorage[set].dataEntries[entryID] = NULL;
345
dataStorage[set].storageEntries[newEntryCount] = dataStorage[set].storageEntries[entryID];
346
dataStorage[set].storageEntries[entryID] = NULL;
347
}
348
349
++newEntryCount;
350
}
351
}
352
dataStorage[set].entryCount = newEntryCount;
353
354
for (int32 e = dataStorage[set].entryCount; e < STORAGE_ENTRY_COUNT; ++e) {
355
dataStorage[set].dataEntries[e] = NULL;
356
dataStorage[set].storageEntries[e] = NULL;
357
}
358
}
359
}
360
361