mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 07:39:08 +00:00
8f8185f035
resource manager. All new code! All new bugs! svn-id: r13603
228 lines
6.0 KiB
C++
228 lines
6.0 KiB
C++
/* Copyright (C) 1994-2004 Revolution Software Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
// The new memory manager, now only used by the resource manager. Unlike the
|
|
// original, this one does not allocate a 12 MB memory pool at startup.
|
|
//
|
|
// There is one thing that prevents us from replacing the whole memory manager
|
|
// with the standard memory allocation functions: Broken Sword II absolutely,
|
|
// positively needs to be able to encode pointers as 32-bit integers. The
|
|
// original engine did this simply by casting between pointers and integers,
|
|
// but as far as I know that's not a very portable thing to do.
|
|
//
|
|
// For a while I was hopeing that the engine only needed pointers as function
|
|
// parameters, in which case I could have extended the stack to use a struct or
|
|
// a union instead, but no such luck. There is code in walker.cpp that
|
|
// obviously violates that assumption, and there are probably other, more
|
|
// well-hidden places, as well.
|
|
//
|
|
// This attacks the problem from the direction of another limitation in the
|
|
// original memory manager: it could only handle up to 999 blocks of memory.
|
|
// This memory manager has the same limitation, although it's probably way too
|
|
// large now, which means that a pointer can be encoded as 10 bits to store the
|
|
// block id, and another 22 bits to store an index into that block.
|
|
|
|
#include "common/stdafx.h"
|
|
#include "sword2/sword2.h"
|
|
#include "sword2/console.h"
|
|
|
|
namespace Sword2 {
|
|
|
|
#define MAX_BLOCKS 999
|
|
|
|
#define Debug_Printf _vm->_debugger->DebugPrintf
|
|
|
|
MemoryManager::MemoryManager(Sword2Engine *vm) : _vm(vm) {
|
|
_memBlocks = (MemBlock *) malloc(MAX_BLOCKS * sizeof(MemBlock));
|
|
_memBlockIndex = (MemBlock **) malloc(MAX_BLOCKS * sizeof(MemBlock *));
|
|
_idStack = (int16 *) malloc(MAX_BLOCKS * sizeof(int16));
|
|
|
|
_totAlloc = 0;
|
|
_numBlocks = 0;
|
|
|
|
for (int i = 0; i < MAX_BLOCKS; i++) {
|
|
_idStack[i] = MAX_BLOCKS - i - 1;
|
|
_memBlocks[i].ptr = NULL;
|
|
_memBlockIndex[i] = NULL;
|
|
}
|
|
|
|
_idStackPtr = MAX_BLOCKS;
|
|
}
|
|
|
|
MemoryManager::~MemoryManager() {
|
|
for (int i = 0; i < MAX_BLOCKS; i++)
|
|
free(_memBlocks[i].ptr);
|
|
free(_memBlocks);
|
|
free(_memBlockIndex);
|
|
free(_idStack);
|
|
}
|
|
|
|
int32 MemoryManager::encodePtr(byte *ptr) {
|
|
int idx = findPointerInIndex(ptr);
|
|
|
|
if (idx == -1)
|
|
error("Encoding non-allocated pointer %p", ptr);
|
|
|
|
int id = _memBlockIndex[idx]->id;
|
|
|
|
return (id << 22) | (ptr - _memBlocks[id].ptr);
|
|
}
|
|
|
|
byte *MemoryManager::decodePtr(int32 n) {
|
|
int16 id = (n >> 22) & 0x03ff;
|
|
int32 offset = n & 0x003fffff;
|
|
|
|
return _memBlocks[id].ptr + offset;
|
|
}
|
|
|
|
int16 MemoryManager::findExactPointerInIndex(byte *ptr) {
|
|
int left = 0;
|
|
int right = _numBlocks - 1;
|
|
|
|
while (right >= left) {
|
|
int n = (left + right) / 2;
|
|
|
|
if (_memBlockIndex[n]->ptr == ptr)
|
|
return n;
|
|
|
|
if (_memBlockIndex[n]->ptr > ptr)
|
|
right = n - 1;
|
|
else
|
|
left = n + 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int16 MemoryManager::findPointerInIndex(byte *ptr) {
|
|
int left = 0;
|
|
int right = _numBlocks - 1;
|
|
|
|
while (right >= left) {
|
|
int n = (left + right) / 2;
|
|
|
|
if (_memBlockIndex[n]->ptr <= ptr && _memBlockIndex[n]->ptr + _memBlockIndex[n]->size > ptr)
|
|
return n;
|
|
|
|
if (_memBlockIndex[n]->ptr > ptr)
|
|
right = n - 1;
|
|
else
|
|
left = n + 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int16 MemoryManager::findInsertionPointInIndex(byte *ptr) {
|
|
if (_numBlocks == 0)
|
|
return 0;
|
|
|
|
int left = 0;
|
|
int right = _numBlocks - 1;
|
|
int n = 0;
|
|
|
|
while (right >= left) {
|
|
n = (left + right) / 2;
|
|
|
|
if (_memBlockIndex[n]->ptr == ptr)
|
|
return -1;
|
|
|
|
if (_memBlockIndex[n]->ptr > ptr)
|
|
right = n - 1;
|
|
else
|
|
left = n + 1;
|
|
}
|
|
|
|
if (_memBlockIndex[n]->ptr < ptr)
|
|
n++;
|
|
|
|
return n;
|
|
}
|
|
|
|
byte *MemoryManager::memAlloc(uint32 size, int16 uid) {
|
|
byte *ptr = (byte *) malloc(size);
|
|
|
|
assert(ptr);
|
|
assert(_idStackPtr > 0);
|
|
|
|
// Get the new block's id from the stack.
|
|
int16 id = _idStack[--_idStackPtr];
|
|
|
|
_memBlocks[id].id = id;
|
|
_memBlocks[id].uid = uid;
|
|
_memBlocks[id].ptr = ptr;
|
|
_memBlocks[id].size = size;
|
|
|
|
// The memory index provides a method for figuring out which memory
|
|
// block an arbitrary pointer points to. A balanced tree might be more
|
|
// efficient, but such beasts are tricky to implement.
|
|
|
|
int16 idx = findInsertionPointInIndex(ptr);
|
|
|
|
assert(idx != -1);
|
|
|
|
for (int i = _numBlocks; i > idx; i--)
|
|
_memBlockIndex[i] = _memBlockIndex[i - 1];
|
|
|
|
_memBlockIndex[idx] = &_memBlocks[id];
|
|
_numBlocks++;
|
|
_totAlloc += size;
|
|
|
|
return _memBlocks[id].ptr;
|
|
}
|
|
|
|
void MemoryManager::memFree(byte *ptr) {
|
|
int16 idx = findExactPointerInIndex(ptr);
|
|
|
|
if (idx == -1) {
|
|
warning("Freeing non-allocated pointer %p", ptr);
|
|
return;
|
|
}
|
|
|
|
// Put back the id on the stack
|
|
_idStack[_idStackPtr++] = _memBlockIndex[idx]->id;
|
|
|
|
free(_memBlockIndex[idx]->ptr);
|
|
_memBlockIndex[idx]->ptr = NULL;
|
|
|
|
_totAlloc -= _memBlockIndex[idx]->size;
|
|
|
|
// Remove the pointer from the index
|
|
_numBlocks--;
|
|
|
|
for (int i = idx; i < _numBlocks; i++)
|
|
_memBlockIndex[i] = _memBlockIndex[i + 1];
|
|
}
|
|
|
|
void MemoryManager::memDisplay() {
|
|
for (int i = 0; i < _numBlocks; i++)
|
|
Debug_Printf("%d: %ld bytes allocated by resource %d\n", i, _memBlocks[i].size, _memBlocks[i].uid);
|
|
}
|
|
|
|
void MemoryManager::memStatusStr(char *buf) {
|
|
if (_totAlloc < 1024)
|
|
sprintf(buf, "%u bytes in %d memory blocks", _totAlloc, _numBlocks);
|
|
else if (_totAlloc < 1048576)
|
|
sprintf(buf, "%uK in %d memory blocks", _totAlloc / 1024, _numBlocks);
|
|
else
|
|
sprintf(buf, "%.02fM in %d memory blocks", _totAlloc / 1048576., _numBlocks);
|
|
}
|
|
|
|
} // End of namespace Sword2
|