This is a major rewrite of the graphics code. A slightly adapted version of the old code is still available and currently the default. The new code is selectable in sci.cpp, but is not yet finished. svn-id: r44565
373 lines
11 KiB
C++
373 lines
11 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "sci/sci.h"
|
|
#include "sci/gui/gui_helpers.h"
|
|
#include "sci/gui/gui_memmgr.h"
|
|
|
|
namespace Sci {
|
|
|
|
static tagHandle *pHandles = 0;
|
|
static int16 nHandles = 0;
|
|
static byte *_heap = 0;
|
|
SCIHANDLE _firstfree = 0;
|
|
Common::HashMap<HEAPHANDLE, tagHeapInfo> heapInfo;
|
|
#define HEAP_BOTTOM 1000
|
|
|
|
//----------------------
|
|
void FreeMem(void) {
|
|
// deleting handles
|
|
if (pHandles) {
|
|
for (uint16 i = 0; i < nHandles; i++)
|
|
hunkDisposeHandle(i);
|
|
delete[] pHandles;
|
|
pHandles = 0;
|
|
}
|
|
// deleting heap
|
|
if (_heap) {
|
|
delete[] _heap;
|
|
_heap = 0;
|
|
}
|
|
}
|
|
//----------------------
|
|
bool InitMem(int16 max_handles) {
|
|
// heap
|
|
_heap = new byte[HEAP_SIZE];
|
|
memset(_heap, 0x55, HEAP_SIZE);
|
|
_firstfree = HEAP_START;
|
|
heapSetBlockSize(_firstfree, HEAP_SIZE - 1 - HEAP_START);
|
|
heapSetBlockNext(_firstfree, (SCIHANDLE)(HEAP_SIZE - 1));
|
|
//hunk
|
|
pHandles = new tagHandle[max_handles];
|
|
if (pHandles) {
|
|
nHandles = max_handles;
|
|
memset(pHandles, 0, sizeof(tagHandle) * max_handles); // zerofy all
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
//----------------------
|
|
SCIHANDLE hunkNeedHandle(uint16 size, uint32 resId) {
|
|
SCIHANDLE newh = 0;
|
|
// see if there is an empty handle available
|
|
for (int16 i = 1; i < nHandles; i++)
|
|
if (pHandles[i].ptr == 0) {
|
|
newh = i;
|
|
break;
|
|
}
|
|
// if unused handle was found - allocate memory and return it
|
|
if (newh == nHandles)
|
|
return 0;
|
|
pHandles[newh].ptr = (byte*)malloc(size);
|
|
assert(pHandles[newh].ptr);
|
|
if (pHandles[newh].ptr) {
|
|
pHandles[newh].size = size;
|
|
pHandles[newh].resId = resId;
|
|
debug(
|
|
5,
|
|
" MemMgr: Requested %6db for res %08X, allocated handle %04X",
|
|
size, resId, newh);
|
|
return newh;
|
|
}
|
|
return 0;
|
|
}
|
|
//----------------------
|
|
void hunkDisposeHandle(SCIHANDLE handle) {
|
|
// if (handle > 0 && handle < nHandles && pHandles[handle].ptr) {
|
|
// debug(
|
|
// 5,
|
|
// " MemMgr: disposing handle 0x%04X, size=%8db, associated res %08X",
|
|
// handle, pHandles[handle].size, pHandles[handle].resId);
|
|
// free(pHandles[handle].ptr);
|
|
// pHandles[handle].ptr = 0;
|
|
// // deleting associated resource handler
|
|
// // Check that this don't fail on delete as ResGetLoaded could return 0;
|
|
// if (pHandles[handle].resId != 0xFFFFFFFF)
|
|
// delete g_sci->ResMgr.ResGetLoaded(pHandles[handle].resId);
|
|
// }
|
|
}
|
|
//----------------------
|
|
byte *hunk2Ptr(SCIHANDLE handle) {
|
|
if (handle > 0 && handle < nHandles && pHandles[handle].ptr)
|
|
return (byte *)pHandles[handle].ptr;
|
|
return 0;
|
|
}
|
|
//----------------------
|
|
SCIHANDLE ptr2hunk(byte *ptr) {
|
|
for (int i = 0; i < nHandles; i++) {
|
|
if (ptr >= pHandles[i].ptr && ptr <= pHandles[i].ptr + pHandles[i].size)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
//----------------------
|
|
uint32 hunkHandleSize(SCIHANDLE handle) {
|
|
if (handle > 0 && handle < nHandles && pHandles[handle].ptr)
|
|
return pHandles[handle].size;
|
|
return 0;
|
|
}
|
|
//----------------------
|
|
// int16 hunkResourceNum(SCIHANDLE handle) {
|
|
// if (handle > 0 && handle < nHandles && pHandles[handle].ptr)
|
|
// return RES_ID2NUM(pHandles[handle].resId);
|
|
// return -1;
|
|
// }
|
|
|
|
//----------------------
|
|
void hunkDump() {
|
|
debug("\nMemMgr: Hunk dump:");
|
|
uint32 size = 0;
|
|
for (int i = 0; i < nHandles; i++)
|
|
if (pHandles[i].ptr) {
|
|
debug(" %04X, size=%8db, associated res %08X", i,
|
|
pHandles[i].size, pHandles[i].resId);
|
|
size += pHandles[i].size;
|
|
}
|
|
debug("Total memory allocated: %db", size);
|
|
debug("End dump");
|
|
}
|
|
|
|
uint32 hunkUsed() {
|
|
uint32 size = 0;
|
|
for (int i = 0; i < nHandles; i++)
|
|
if (pHandles[i].ptr)
|
|
size += pHandles[i].size;
|
|
return size;
|
|
}
|
|
//----------------------
|
|
// HEAP
|
|
//----------------------
|
|
//---------------------------------------------
|
|
// TODO : make sure that STRING, STACK and SCRIPT DATA is never allocated below HEAP_BOTTOM=1000
|
|
// otherwise it will cause problems with kernel string fuctions that assumes that anything below 1000 is res number
|
|
HEAPHANDLE heapNewPtr(uint16 size, kDataType type, const char *info) {
|
|
if (size == 0)
|
|
warning("Zero Heap Allocation Request!");
|
|
|
|
HEAPHANDLE ptr, prev, next;
|
|
ptr = prev = _firstfree;
|
|
// 2 bytes block header header + block size must be odd
|
|
size += 2 + (size & 1);
|
|
// looking for an empty block to use
|
|
uint16 blocksize;
|
|
bool bBottomSafe = !(type == kDataString || type == kDataUnknown);
|
|
|
|
while (ptr < HEAP_SIZE - 1) {
|
|
blocksize = heapGetBlockSize(ptr);
|
|
next = heapGetBlockNext(ptr);
|
|
if (blocksize >= size) {
|
|
if (bBottomSafe || (!bBottomSafe && ptr > HEAP_BOTTOM)) {
|
|
if (blocksize <= size + 4) { // use all block
|
|
size = blocksize;
|
|
heapSetBlockNext(prev, next);
|
|
} else { // split block in 2 blocks
|
|
HEAPHANDLE newblock = ptr + size;
|
|
heapSetBlockNext(prev, newblock);
|
|
heapSetBlockSize(newblock, blocksize - size);
|
|
heapSetBlockNext(newblock, next);
|
|
next = newblock;
|
|
}
|
|
// setting allocated block
|
|
heapSetBlockSize(ptr, size);
|
|
// updating firstfree pointer
|
|
if (ptr == _firstfree)
|
|
_firstfree = next;
|
|
setHeapInfo(ptr, type, info);
|
|
return ptr;
|
|
} //if (bBottomSafe || (!bBottomSafe && ptr>HEAP_BOTTOM))
|
|
else { // !bottomsafe && ptr < HEAP_BOTTOM
|
|
if (blocksize + ptr - HEAP_BOTTOM >= size) {
|
|
// splitting the block into 3 parts
|
|
// [2][ptr...999] [2][1002...1000+size-1] [2][1002+size...ptr+blocksize]
|
|
// free returned free
|
|
heapSetBlockSize(ptr, HEAP_BOTTOM-ptr + 2);
|
|
heapSetBlockSize(HEAP_BOTTOM+2, size);
|
|
heapSetBlockSize(HEAP_BOTTOM+2 + size, blocksize - size
|
|
- (HEAP_BOTTOM-ptr + 2));
|
|
|
|
heapSetBlockNext(HEAP_BOTTOM+2 + size, next);
|
|
heapSetBlockNext(ptr, HEAP_BOTTOM+2 + size);
|
|
setHeapInfo(HEAP_BOTTOM+2, type, info);
|
|
return HEAP_BOTTOM + 2;
|
|
}
|
|
}
|
|
} // if (blocksize >= size)
|
|
// block too small - moving to next one
|
|
prev = ptr;
|
|
ptr = next;
|
|
}
|
|
// allocation error - out of heap
|
|
warning("Out of heap space");
|
|
return 0;
|
|
}
|
|
//--------------------------------------------
|
|
void heapDisposePtr(HEAPHANDLE handle) {
|
|
HEAPHANDLE prev, next;
|
|
prev = next = _firstfree;
|
|
// searching for prev and next free blocks (before & after deleted block)
|
|
while (next < handle) {
|
|
prev = next;
|
|
next = heapGetBlockNext(prev);
|
|
}
|
|
// if deleted block is before 1st free space then it'll be 1st free space
|
|
if (handle < _firstfree) {
|
|
next = _firstfree;
|
|
_firstfree = handle;
|
|
} else
|
|
heapSetBlockNext(prev, handle);
|
|
|
|
heapSetBlockNext(handle, next);
|
|
//Try to merge with previous
|
|
if (prev + heapGetBlockSize(prev) == handle) {
|
|
heapSetBlockSize(prev, heapGetBlockSize(prev)
|
|
+ heapGetBlockSize(handle));
|
|
heapSetBlockNext(prev, next);
|
|
handle = prev;
|
|
}
|
|
//Try to merge with next
|
|
if (handle + heapGetBlockSize(handle) == next && next != (HEAP_SIZE - 1)) {
|
|
heapSetBlockSize(handle, heapGetBlockSize(handle) + heapGetBlockSize(
|
|
next));
|
|
heapSetBlockNext(handle, heapGetBlockNext(next));
|
|
}
|
|
}
|
|
//-------------------------------------
|
|
uint16 heapFreeSize() {
|
|
HEAPHANDLE free = _firstfree;
|
|
uint16 szFree = 0;
|
|
while (free < HEAP_SIZE - 1) {
|
|
int size = heapGetBlockSize(free);
|
|
free = heapGetBlockNext(free);
|
|
szFree += size;
|
|
}
|
|
return szFree;
|
|
}
|
|
//-------------------------------------
|
|
uint16 heapLargestFreeSize() {
|
|
HEAPHANDLE free = _firstfree;
|
|
uint16 maxFree = 0;
|
|
while (free < HEAP_SIZE - 1) {
|
|
int size = heapGetBlockSize(free);
|
|
free = heapGetBlockNext(free);
|
|
if (size > maxFree)
|
|
maxFree = size;
|
|
}
|
|
return maxFree ? maxFree - 2 : 0;
|
|
}
|
|
//-------------------------------------
|
|
void heapDump() {
|
|
debug("\nMemMgr:Heap dump:");
|
|
HEAPHANDLE ptr = HEAP_START;
|
|
HEAPHANDLE free = _firstfree;
|
|
|
|
uint16 szFree = 0, szUsed = 0;
|
|
while (ptr < HEAP_SIZE - 1) {
|
|
int size = heapGetBlockSize(ptr);
|
|
if (ptr == free) {
|
|
free = heapGetBlockNext(free);
|
|
debug(" %04X %6u size:%6d FREE next=%04X", ptr, ptr,
|
|
heapGetDataSize(ptr), free);
|
|
szFree += size;
|
|
} else {
|
|
debug(" %04X %6u size:%6d USED", ptr, ptr, heapGetDataSize(ptr));
|
|
szUsed += size;
|
|
}
|
|
ptr += size;
|
|
}
|
|
debug("Total %db used, %db free\nEnd Dump", szUsed, szFree);
|
|
}
|
|
//----------------------------
|
|
inline byte *heap2Ptr(HEAPHANDLE ptr) {
|
|
return (ptr >= HEAP_START) ? (byte *)(_heap + ptr) : NULL;
|
|
}
|
|
//----------------------------
|
|
HEAPHANDLE ptr2heap(byte *ptr) {
|
|
return ptr > _heap && ptr < (_heap + HEAP_SIZE) ? (HEAPHANDLE)(ptr - _heap)
|
|
: 0;
|
|
}
|
|
//----------------------------
|
|
uint16 heapGetBlockSize(HEAPHANDLE ptr) {
|
|
return READ_UINT16(_heap + ptr - 2);
|
|
}
|
|
//----------------------------
|
|
uint16 heapGetDataSize(HEAPHANDLE ptr) {
|
|
return heapGetBlockSize(ptr) - 2;
|
|
}
|
|
//----------------------------
|
|
void heapFillPtr(HEAPHANDLE ptr, byte filler) {
|
|
memset(heap2Ptr(ptr), filler, heapGetDataSize(ptr));
|
|
}
|
|
//----------------------------
|
|
void heapClearPtr(HEAPHANDLE ptr) {
|
|
heapFillPtr(ptr, 0);
|
|
}
|
|
//----------------------------
|
|
void heapSetBlockSize(HEAPHANDLE ptr, uint16 nbytes) {
|
|
WRITE_UINT16(_heap + ptr - 2, nbytes);
|
|
}
|
|
//----------------------------
|
|
HEAPHANDLE heapGetBlockNext(HEAPHANDLE ptr) {
|
|
return READ_UINT16(_heap + ptr);
|
|
}
|
|
//----------------------------
|
|
void heapSetBlockNext(HEAPHANDLE ptr, SCIHANDLE next) {
|
|
WRITE_UINT16(_heap + ptr, next);
|
|
}
|
|
//----------------------------
|
|
void heapDisposePtr(byte *ptr) {
|
|
heapDisposePtr(ptr - _heap);
|
|
}
|
|
//---------------------------------------------
|
|
//
|
|
//
|
|
void setHeapInfo(HEAPHANDLE hnd, kDataType type, const char *szinfo) {
|
|
tagHeapInfo info = { kDataUnknown, 0 };
|
|
info.type = type;
|
|
if (szinfo)
|
|
strncpy(info.info, szinfo, 20);
|
|
heapInfo.setVal(hnd, info);
|
|
}
|
|
//--------------------------------
|
|
bool saveMemState(Common::OutSaveFile *pFile) {
|
|
pFile->writeString("MEMORY\n");
|
|
// saving heap
|
|
pFile->write(_heap, HEAP_SIZE);
|
|
pFile->writeUint16LE(_firstfree);
|
|
// TODO : process and write hunk handles
|
|
//pFile->writeString("HUNK\n");
|
|
return true;
|
|
}
|
|
//--------------------------------
|
|
bool restoreMemState(Common::InSaveFile *pFile) {
|
|
if (pFile->readLine() != "MEMORY")
|
|
return false;
|
|
pFile->read(_heap, HEAP_SIZE);
|
|
_firstfree = pFile->readUint16LE();
|
|
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------
|
|
}// end of namespace SCI
|