scummvm/sword2/resman.cpp
Torbjörn Andersson fa2b8ba8de More cleanup. I've eliminated all the temporary global variables I've added
over the past few weeks, except for g_sword2. (Of course, this doesn't
necessarily make the code any prettier, but we can work on that later.)

svn-id: r11309
2003-11-16 14:18:29 +00:00

979 lines
26 KiB
C++

/* Copyright (C) 1994-2003 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$
*/
#include "common/stdafx.h"
#include "common/file.h"
#include "sword2/sword2.h"
#include "sword2/defs.h"
namespace Sword2 {
// ---------------------------------------------------------------------------
// welcome to the easy resource manager - written in simple code for easy
// maintenance
//
// the resource compiler will create two files
//
// resource.inf which is a list of ascii cluster file names
// resource.tab which is a table which tells us which cluster a resource
// is located in and the number within the cluster
// ---------------------------------------------------------------------------
#define NONE 0
#define FETCHING 1
#define BUFFERSIZE 4096
// ---------------------------------------------------------------------------
//
//
// resman.
//
//
// ---------------------------------------------------------------------------
enum {
BOTH = 0x0, // Cluster is on both CDs
CD1 = 0x1, // Cluster is on CD1 only
CD2 = 0x2, // Cluster is on CD2 only
LOCAL_CACHE = 0x4, // Cluster is cached on HDD
LOCAL_PERM = 0x8 // Cluster is on HDD.
};
#if !defined(__GNUC__)
#pragma START_PACK_STRUCTS
#endif
struct _cd_inf {
uint8 clusterName[20]; // Null terminated cluster name.
uint8 cd; // Cd cluster is on and whether it is on the local drive or not.
} GCC_PACK;
#if !defined(__GNUC__)
#pragma END_PACK_STRUCTS
#endif
ResourceManager::ResourceManager(Sword2Engine *vm) {
_vm = vm;
// We read in the resource info which tells us the names of the
// resource cluster files ultimately, although there might be groups
// within the clusters at this point it makes no difference. We only
// wish to know what resource files there are and what is in each
File file;
uint32 end;
mem *temp;
uint32 pos = 0;
uint32 j = 0;
_totalClusters = 0;
if (!file.open("resource.inf")) {
error("init cannot *OPEN* resource.inf");
}
end = file.size();
//get some space for the incoming resource file - soon to be trashed
temp = _vm->_memory->allocMemory(end, MEM_locked, UID_temp);
if (file.read(temp->ad, end) != end) {
file.close();
error("init cannot *READ* resource.inf");
}
file.close();
// ok, we've loaded in the resource.inf file which contains a list of
// all the files now extract the filenames
do {
// item must have an #0d0a
while(temp->ad[j] != 13) {
_resourceFiles[_totalClusters][pos] = temp->ad[j];
j++;
pos++;
}
// NULL terminate our extracted string
_resourceFiles[_totalClusters][pos]=0;
// Reset position in current slot between entries, skip the
// 0x0a in the source and increase the number of clusters.
pos = 0;
j += 2;
_totalClusters++;
// TODO: put overload check here
} while (j != end); // using this method the Gode generated resource.inf must have #0d0a on the last entry
// now load in the binary id to res conversion table
if (!file.open("resource.tab")) {
error("init cannot *OPEN* resource.tab");
}
// find how many resources
end = file.size();
_totalResFiles = end / 4;
// table seems ok so malloc some space
_resConvTable = (uint16 *) malloc(end);
for (j = 0; j < end / 2; j++)
_resConvTable[j] = file.readUint16LE();
if (file.ioFailed()) {
file.close();
error("init cannot *READ* resource.tab");
}
file.close();
if (!file.open("cd.inf")) {
error("init cannot *OPEN* cd.inf");
}
_cd_inf *cdInf = new _cd_inf[_totalClusters];
for (j = 0; j < _totalClusters; j++) {
file.read(cdInf[j].clusterName, sizeof(cdInf[j].clusterName));
cdInf[j].cd = file.readByte();
if (file.ioFailed()) {
error("init failed to read cd.inf. Insufficient entries?");
}
}
file.close();
for (j = 0; j < _totalClusters; j++) {
uint32 i = 0;
while (scumm_stricmp((char *) cdInf[i].clusterName, _resourceFiles[j]) != 0 && i < _totalClusters)
i++;
if (i == _totalClusters) {
error("init, %s is not in cd.inf", _resourceFiles[j]);
} else
_cdTab[j] = cdInf[i].cd;
}
delete cdInf;
debug(5, "%d resources in %d cluster files", _totalResFiles, _totalClusters);
for (j = 0; j < _totalClusters; j++)
debug(5, "filename of cluster %d: -%s", j, _resourceFiles[j]);
// create space for a list of pointers to mem's
_resList = (mem **) malloc(_totalResFiles * sizeof(mem *));
_age = (uint32 *) malloc(_totalResFiles * sizeof(uint32));
_count = (uint16 *) malloc(_totalResFiles * sizeof(uint16));
for (j = 0; j < _totalResFiles; j++) {
// age must be 0 if the file is not in memory at all
_age[j] = 0;
_count[j] = 0;
}
_resTime = 1; //cannot start at 0
_vm->_memory->freeMemory(temp); //get that memory back
}
ResourceManager::~ResourceManager(void) {
// free up our mallocs
free(_resList);
free(_age);
free(_count);
}
// Quick macro to make swapping in-place easier to write
#define SWAP16(x) x = SWAP_BYTES_16(x)
#define SWAP32(x) x = SWAP_BYTES_32(x)
void convertEndian(uint8 *file, uint32 len) {
int i;
_standardHeader *hdr = (_standardHeader *)file;
file += sizeof(_standardHeader);
SWAP32(hdr->compSize);
SWAP32(hdr->decompSize);
switch (hdr->fileType) {
case ANIMATION_FILE: {
_animHeader *animHead = (_animHeader *)file;
SWAP16(animHead->noAnimFrames);
SWAP16(animHead->feetStartX);
SWAP16(animHead->feetStartY);
SWAP16(animHead->feetEndX);
SWAP16(animHead->feetEndY);
SWAP16(animHead->blend);
_cdtEntry *cdtEntry = (_cdtEntry *) (file + sizeof(_animHeader));
for (i = 0; i < animHead->noAnimFrames; i++, cdtEntry++) {
SWAP16(cdtEntry->x);
SWAP16(cdtEntry->y);
SWAP32(cdtEntry->frameOffset);
_frameHeader *frameHeader = (_frameHeader *) (file + cdtEntry->frameOffset);
// Quick trick to prevent us from incorrectly applying the endian
// fixes multiple times. This assumes that frames are less than 1 MB
// and have height/width less than 4096.
if ((frameHeader->compSize & 0xFFF00000) ||
(frameHeader->width & 0xF000) ||
(frameHeader->height & 0xF000)) {
SWAP32(frameHeader->compSize);
SWAP16(frameHeader->width);
SWAP16(frameHeader->height);
}
}
break;
}
case SCREEN_FILE: {
_multiScreenHeader *mscreenHeader = (_multiScreenHeader *) file;
SWAP32(mscreenHeader->palette);
SWAP32(mscreenHeader->bg_parallax[0]);
SWAP32(mscreenHeader->bg_parallax[1]);
SWAP32(mscreenHeader->screen);
SWAP32(mscreenHeader->fg_parallax[0]);
SWAP32(mscreenHeader->fg_parallax[1]);
SWAP32(mscreenHeader->layers);
SWAP32(mscreenHeader->paletteTable);
SWAP32(mscreenHeader->maskOffset);
// screenHeader
_screenHeader *screenHeader = (_screenHeader*) (file + mscreenHeader->screen);
SWAP16(screenHeader->width);
SWAP16(screenHeader->height);
SWAP16(screenHeader->noLayers);
// layerHeader
_layerHeader *layerHeader = (_layerHeader *) (file + mscreenHeader->layers);
for (i = 0; i < screenHeader->noLayers; i++, layerHeader++) {
SWAP16(layerHeader->x);
SWAP16(layerHeader->y);
SWAP16(layerHeader->width);
SWAP16(layerHeader->height);
SWAP32(layerHeader->maskSize);
SWAP32(layerHeader->offset);
}
// backgroundParallaxLayer
_parallax *parallax;
int offset;
offset = mscreenHeader->bg_parallax[0];
if (offset > 0) {
parallax = (_parallax *) (file + offset);
SWAP16(parallax->w);
SWAP16(parallax->h);
}
offset = mscreenHeader->bg_parallax[1];
if (offset > 0) {
parallax = (_parallax *) (file + offset);
SWAP16(parallax->w);
SWAP16(parallax->h);
}
// backgroundLayer
offset = mscreenHeader->screen + sizeof(_screenHeader);
if (offset > 0) {
parallax = (_parallax *) (file + offset);
SWAP16(parallax->w);
SWAP16(parallax->h);
}
// foregroundParallaxLayer
offset = mscreenHeader->fg_parallax[0];
if (offset > 0) {
parallax = (_parallax *) (file + offset);
SWAP16(parallax->w);
SWAP16(parallax->h);
}
offset = mscreenHeader->fg_parallax[1];
if (offset > 0) {
parallax = (_parallax *) (file + offset);
SWAP16(parallax->w);
SWAP16(parallax->h);
}
break;
}
case GAME_OBJECT: {
_object_hub *objectHub = (_object_hub *)file;
objectHub->type = (int)SWAP_BYTES_32(objectHub->type);
SWAP32(objectHub->logic_level);
for (i = 0; i < TREE_SIZE; i++) {
SWAP32(objectHub->logic[i]);
SWAP32(objectHub->script_id[i]);
SWAP32(objectHub->script_pc[i]);
}
break;
}
case WALK_GRID_FILE: {
_walkGridHeader *walkGridHeader = (_walkGridHeader *)file;
SWAP32(walkGridHeader->numBars);
SWAP32(walkGridHeader->numNodes);
_barData *barData = (_barData *) (file + sizeof(_walkGridHeader));
for (i = 0; i < walkGridHeader->numBars; i++) {
SWAP16(barData->x1);
SWAP16(barData->y1);
SWAP16(barData->x2);
SWAP16(barData->y2);
SWAP16(barData->xmin);
SWAP16(barData->ymin);
SWAP16(barData->xmax);
SWAP16(barData->ymax);
SWAP16(barData->dx);
SWAP16(barData->dy);
SWAP32(barData->co);
barData++;
}
uint16 *node = (uint16 *) (file + sizeof(_walkGridHeader) + walkGridHeader->numBars * sizeof(_barData));
for (i = 0; i < walkGridHeader->numNodes*2; i++) {
SWAP16(*node);
node++;
}
break;
}
case GLOBAL_VAR_FILE:
break;
case PARALLAX_FILE_null:
break;
case RUN_LIST: {
uint32 *list = (uint32 *)file;
while (*list) {
SWAP32(*list);
list++;
}
break;
}
case TEXT_FILE: {
_textHeader *textHeader = (_textHeader *)file;
SWAP32(textHeader->noOfLines);
break;
}
case SCREEN_MANAGER:
break;
case MOUSE_FILE:
break;
case ICON_FILE:
break;
}
}
uint8 *ResourceManager::openResource(uint32 res) {
// returns ad of resource. Loads if not in memory
// retains a count
// resource can be aged out of memory if count = 0
// the resource is locked while count != 0 i.e. until a closeResource is
// called
File file;
uint16 parent_res_file;
uint16 actual_res;
uint32 pos, len;
uint32 table_offset;
//#ifdef _SWORD2_DEBUG
if (res >= _totalResFiles)
error("open illegal resource %d (there are %d resources 0-%d)", res, _totalResFiles, _totalResFiles - 1);
//#endif
// is the resource in memory already?
// if the file is not in memory then age should and MUST be 0
if (!_age[res]) {
// fetch the correct file and read in the correct portion
// if the file cannot fit then we must trash the oldest large
// enough floating file
// points to the number of the ascii filename
parent_res_file = _resConvTable[res * 2];
//#ifdef _SWORD2_DEBUG
if (parent_res_file == 0xffff)
error("open tried to open null & void resource number %d", res);
//#endif
// relative resource within the file
actual_res = _resConvTable[(res * 2) + 1];
// first we have to find the file via the _resConvTable
debug(5, "resOpen %s res %d", _resourceFiles[parent_res_file], res);
// If we're loading a cluster that's only available from one
// of the CDs, remember which one so that we can play the
// correct music.
if (!(_cdTab[parent_res_file] & LOCAL_PERM)) {
_curCd = _cdTab[parent_res_file] & 3;
}
// Actually, as long as the file can be found we don't really
// care which CD it's on. But if we can't find it, keep asking
// for the CD until we do.
while (!file.open(_resourceFiles[parent_res_file])) {
// If the file is supposed to be on hard disk, or we're
// playing a demo, then we're in trouble if the file
// can't be found!
if ((_vm->_features & GF_DEMO) || (_cdTab[parent_res_file] & LOCAL_PERM))
error("Could not find '%s'", _resourceFiles[parent_res_file]);
getCd(_cdTab[parent_res_file] & 3);
}
// 1st DWORD of a cluster is an offset to the look-up table
table_offset = file.readUint32LE();
debug(5, "table offset = %d", table_offset);
// 2 dwords per resource
file.seek(table_offset + actual_res * 8, SEEK_SET);
// get position of our resource within the cluster file
pos = file.readUint32LE();
// read the length
len = file.readUint32LE();
// get to position in file of our particular resource
file.seek(pos, SEEK_SET);
debug(5, "res len %d", len);
// ok, we know the length so try and allocate the memory
// if it can't then old files will be ditched until it works
_resList[res] = _vm->_memory->allocMemory(len, MEM_locked, res);
// now load the file
// hurray, load it in.
file.read(_resList[res]->ad, len);
// close the cluster
file.close();
#ifdef SCUMM_BIG_ENDIAN
convertEndian((uint8 *) _resList[res]->ad, len);
#endif
} else {
debug(5, "RO %d, already open count=%d", res, _count[res]);
}
// number of times opened - the file won't move in memory while count
// is non zero
_count[res]++;
// update the accessed time stamp - touch the file in other words
_age[res] = _resTime;
// pass the address of the mem & lock the memory too
// might be locked already (if count > 1)
_vm->_memory->lockMemory(_resList[res]);
return (uint8 *) _resList[res]->ad;
}
uint8 ResourceManager::checkValid(uint32 res) {
// returns '1' if resource is valid, otherwise returns '0'
// used in startup.cpp to ignore invalid screen-manager resources
uint16 parent_res_file;
// resource number out of range
if (res >= _totalResFiles)
return 0;
// points to the number of the ascii filename
parent_res_file = _resConvTable[res * 2];
// null & void resource
if (parent_res_file == 0xffff)
return 0;
// ok
return 1;
}
void ResourceManager::nextCycle(void) {
// increment the cycle and calculate actual per-cycle memory useage
#ifdef _SWORD2_DEBUG
uint32 j;
#endif
#ifdef _SWORD2_DEBUG
_currentMemoryUsage = 0;
for (j = 1; j < _totalResFiles; j++) {
// was accessed last cycle
if (_age[j] == _resTime)
_currentMemoryUsage += _resList[j]->size;
}
#endif
_resTime++;
// if you left the game running for a hundred years when this went to 0
// there'd be a resource left stuck in memory - after another hundred
// years there'd be another...
//
// Mind you, by then the our get_msecs() function will have wrapped
// around too, probably causing a mess of other problems.
if (!_resTime)
_resTime++;
}
uint32 ResourceManager::fetchUsage(void) {
// returns memory usage previous cycle
return _currentMemoryUsage;
}
void ResourceManager::closeResource(uint32 res) {
// decrements the count
// resource floats when count = 0
//#ifdef _SWORD2_DEBUG
if (res >= _totalResFiles)
error("closing illegal resource %d (there are %d resources 0-%d)", res, _totalResFiles, _totalResFiles - 1);
//closing but isnt open?
if (!(_count[res]))
error("closeResource: closing %d but it isn't open", res);
//#endif
//one less has it open
_count[res]--;
//if noone has the file open then unlock and allow to float
if (!_count[res]) {
// pass the address of the mem
_vm->_memory->floatMemory(_resList[res]);
}
}
uint32 ResourceManager::fetchLen(uint32 res) {
// returns the total file length of a resource - i.e. all headers are
// included too
File fh;
uint16 parent_res_file;
uint16 actual_res;
uint32 len;
uint32 table_offset;
// points to the number of the ascii filename
parent_res_file = _resConvTable[res * 2];
// relative resource within the file
actual_res = _resConvTable[(res * 2) + 1];
// first we have to find the file via the _resConvTable
// open the cluster file
if (!fh.open(_resourceFiles[parent_res_file]))
error("fetchLen cannot *OPEN* %s", _resourceFiles[parent_res_file]);
// 1st DWORD of a cluster is an offset to the look-up table
table_offset = fh.readUint32LE();
// 2 dwords per resource + skip the position dword
fh.seek(table_offset + (actual_res * 8) + 4, SEEK_SET);
// read the length
len = fh.readUint32LE();
return len;
}
char *ResourceManager::fetchCluster(uint32 res) {
// returns a pointer to the ascii name of the cluster file which
// contains resource res
return _resourceFiles[_resConvTable[res * 2]];
}
uint32 ResourceManager::fetchAge(uint32 res) {
// return the age of res
return _age[res];
}
uint32 ResourceManager::fetchCount(uint32 res) {
// return the open count of res
return _count[res];
}
uint32 ResourceManager::helpTheAgedOut(void) {
// remove from memory the oldest closed resource
uint32 oldest_res; // holds id of oldest found so far when we have to chuck stuff out of memory
uint32 oldest_age; // age of above during search
uint32 j;
uint32 largestResource = 0;
oldest_age = _resTime;
oldest_res = 0;
for (j = 2; j < _totalResFiles; j++) {
// not held open and older than this one
if (!_count[j] && _age[j] && _age[j] <= oldest_age) {
if (_age[j] == oldest_age && _resList[j]->size > largestResource) {
// Kick old resource of oldest age and largest
// size (Helps the poor defragger).
oldest_res = j;
largestResource = _resList[j]->size;
} else if (_age[j] < oldest_age) {
oldest_res = j;
oldest_age = _age[j];
largestResource = _resList[j]->size;
}
}
}
// there was not a file we could release
// no bytes released - oh dear, lets hope this never happens
if (!oldest_res)
return 0;
debug(5, "removing %d, age %d, size %d", oldest_res, _age[oldest_res], _resList[oldest_res]->size);
// trash this old resource
_age[oldest_res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[oldest_res]); // release the memory too
return _resList[oldest_res]->size; // return bytes freed
}
void ResourceManager::printConsoleClusters(void) {
if (_totalClusters) {
for (uint i = 0; i < _totalClusters; i++) {
Debug_Printf("%-20s ", _resourceFiles[i]);
if (!(_cdTab[i] & LOCAL_PERM)) {
switch (_cdTab[i] & 3) {
case BOTH:
Debug_Printf("CD 1 & 2\n");
break;
case CD1:
Debug_Printf("CD 1\n");
break;
case CD2:
Debug_Printf("CD 2\n");
break;
default:
Debug_Printf("CD 3? Huh?!\n");
break;
}
} else
Debug_Printf("HD\n");
}
Debug_Printf("%d resources\n", _totalResFiles);
} else
Debug_Printf("Argh! No resources!\n");
}
void ResourceManager::examine(int res) {
_standardHeader *file_header;
if (res < 0 || res >= (int) _totalResFiles)
Debug_Printf("Illegal resource %d (there are %d resources 0-%d)\n", res, _totalResFiles, _totalResFiles - 1);
else if (_resConvTable[res * 2] == 0xffff)
Debug_Printf("%d is a null & void resource number\n", res);
else {
// open up the resource and take a look inside!
file_header = (_standardHeader*) openResource(res);
// Debug_Printf("%d\n", file_header->fileType);
// Debug_Printf("%s\n", file_header->name);
//----------------------------------------------------
// resource types: (taken from header.h)
// 1: ANIMATION_FILE
// all normal animations & sprites including mega-sets &
// font files which are the same format
// 2: SCREEN_FILE
// each contains background, palette, layer sprites,
// parallax layers & shading mask
// 3: GAME_OBJECT
// each contains object hub + structures + script data
// 4: WALK_GRID_FILE
// walk-grid data
// 5: GLOBAL_VAR_FILE
// all the global script variables in one file; "there can
// be only one"
// 6: PARALLAX_FILE_null
// NOT USED
// 7: RUN_LIST
// each contains a list of object resource ids
// 8: TEXT_FILE
// each contains all the lines of text for a location or a
// character's conversation script
// 9: SCREEN_MANAGER
// one for each location; this contains special startup
// scripts
// 10: MOUSE_FILE
// mouse pointers and luggage icons (sprites in General,
// Mouse pointers & Luggage icons)
// 11: WAV_FILE
// NOT USED HERE
// 12: ICON_FILE
// menu icon (sprites in General and Menu icons)
// 13: PALETTE_FILE
// NOT USED HERE
//----------------------------------------------------
switch (file_header->fileType) {
case ANIMATION_FILE:
Debug_Printf("<anim> %s\n", file_header->name);
break;
case SCREEN_FILE:
Debug_Printf("<layer> %s\n", file_header->name);
break;
case GAME_OBJECT:
Debug_Printf("<game object> %s\n", file_header->name);
break;
case WALK_GRID_FILE:
Debug_Printf("<walk grid> %s\n", file_header->name);
break;
case GLOBAL_VAR_FILE:
Debug_Printf("<global variables> %s\n", file_header->name);
break;
case PARALLAX_FILE_null:
Debug_Printf("<parallax file NOT USED!> %s\n", file_header->name);
break;
case RUN_LIST:
Debug_Printf("<run list> %s\n", file_header->name);
break;
case TEXT_FILE:
Debug_Printf("<text file> %s\n", file_header->name);
break;
case SCREEN_MANAGER:
Debug_Printf("<screen manager> %s\n", file_header->name);
break;
case MOUSE_FILE:
Debug_Printf("<mouse pointer> %s\n", file_header->name);
break;
case ICON_FILE:
Debug_Printf("<menu icon> %s\n", file_header->name);
break;
default:
Debug_Printf("unrecognised fileType %d\n", file_header->fileType);
break;
}
closeResource(res);
}
}
void ResourceManager::kill(int res) {
if (res < 0 || res >= (int) _totalResFiles) {
Debug_Printf("Illegal resource %d (there are %d resources 0-%d)\n", res, _totalResFiles, _totalResFiles - 1);
return;
}
// if noone has the file open then unlock and allow to float
if (!_count[res]) {
if (_age[res]) {
_age[res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[res]); // release the memory too
Debug_Printf("Trashed %d\n", res);
} else
Debug_Printf("%d not in memory\n", res);
} else
Debug_Printf("File is open - cannot remove\n");
}
void ResourceManager::remove(uint32 res) {
if (_age[res]) {
_age[res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[res]); // release the memory too
debug(5, " - Trashing %d", res);
} else
debug(5, "remove(%d) not even in memory!", res);
}
void ResourceManager::removeAll(void) {
// remove all res files from memory - ready for a total restart
// including player object & global variables resource
int j;
uint32 res;
j = _vm->_memory->_baseMemBlock;
do {
if (_vm->_memory->_memList[j].uid < 65536) { // a resource
res = _vm->_memory->_memList[j].uid;
_age[res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[res]); // release the memory too
}
j = _vm->_memory->_memList[j].child;
} while (j != -1);
}
void ResourceManager::killAll(bool wantInfo) {
// remove all res files from memory
// its quicker to search the mem blocs for res files than search
// resource lists for those in memory
int j;
uint32 res;
uint32 nuked = 0;
_standardHeader *header;
j = _vm->_memory->_baseMemBlock;
do {
if (_vm->_memory->_memList[j].uid < 65536) { // a resource
res = _vm->_memory->_memList[j].uid;
// not the global vars which are assumed to be open in
// memory & not the player object!
if (res != 1 && res != CUR_PLAYER_ID) {
header = (_standardHeader *) openResource(res);
closeResource(res);
_age[res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[res]); // release the memory too
nuked++;
// if this was called from the console,
if (wantInfo) {
Debug_Printf("Nuked %5d: %s\n", res, header->name);
debug(5, " nuked %d: %s", res, header->name);
}
}
}
j = _vm->_memory->_memList[j].child;
} while (j != -1);
// if this was called from the console
if (wantInfo) {
Debug_Printf("Expelled %d resource(s)\n", nuked);
}
}
//----------------------------------------------------------------------------
// Like killAll but only kills objects (except George & the variable table of
// course) - ie. forcing them to reload & restart their scripts, which
// simulates the effect of a save & restore, thus checking that each object's
// re-entrant logic works correctly, and doesn't cause a statuette to
// disappear forever, or some plaster-filled holes in sand to crash the game &
// get James in trouble again.
void ResourceManager::killAllObjects(bool wantInfo) {
// remove all object res files from memory, excluding George
// its quicker to search the mem blocs for res files than search
// resource lists for those in memory
int j;
uint32 res;
uint32 nuked = 0;
_standardHeader *header;
j = _vm->_memory->_baseMemBlock;
do {
if (_vm->_memory->_memList[j].uid < 65536) { // a resource
res = _vm->_memory->_memList[j].uid;
//not the global vars which are assumed to be open in
// memory & not the player object!
if (res != 1 && res != CUR_PLAYER_ID) {
header = (_standardHeader*) openResource(res);
closeResource(res);
if (header->fileType == GAME_OBJECT) {
_age[res] = 0; // effectively gone from _resList
_vm->_memory->freeMemory(_resList[res]); // release the memory too
nuked++;
// if this was called from the console
if (wantInfo) {
Debug_Printf("Nuked %5d: %s\n", res, header->name);
debug(5, " nuked %d: %s", res, header->name);
}
}
}
}
j = _vm->_memory->_memList[j].child;
} while (j != -1);
// if this was called from the console
if (wantInfo)
Debug_Printf("Expelled %d object resource(s)\n", nuked);
}
void ResourceManager::getCd(int cd) {
uint8 *textRes;
// stop any music from playing - so the system no longer needs the
// current CD - otherwise when we take out the CD, Windows will
// complain!
_vm->_logic->fnStopMusic(NULL);
textRes = openResource(2283);
_vm->displayMsg(_vm->fetchTextLine(textRes, 5 + cd) + 2, 0);
closeResource(2283);
// The original code probably determined automagically when the correct
// CD had been inserted, but our backend doesn't support that, and
// anyway I don't know if all systems allow that sort of thing. So we
// wait for the user to press any key instead, or click the mouse.
//
// But just in case we ever try to identify the CDs by their labels,
// they should be:
//
// CD1: "RBSII1" (or "PCF76" for the PCF76 version, whatever that is)
// CD2: "RBSII2"
while (1) {
_keyboardEvent ke;
_mouseEvent *me;
me = _vm->_input->mouseEvent();
if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)))
break;
if (_vm->_input->readKey(&ke) == RD_OK)
break;
_vm->_graphics->updateDisplay();
_vm->_system->delay_msecs(50);
}
_vm->removeMsg();
}
} // End of namespace Sword2