/* REminiscence - Flashback interpreter * Copyright (C) 2005-2007 Gregory Montoir * * 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. */ #include "file.h" #include "unpack.h" #include "resource.h" Resource::Resource(const char *dataPath, Version ver) { memset(this, 0, sizeof(Resource)); _dataPath = dataPath; _ver = ver; _memBuf = (uint8 *)malloc(0xE000); } Resource::~Resource() { clearLevelRes(); free(_fnt); free(_icn); free(_tab); free(_spr1); free(_memBuf); free(_cmd); free(_pol); free(_cine_off); free(_cine_txt); for (int i = 0; i < _numSfx; ++i) { free(_sfxList[i].data); } free(_sfxList); free(_voiceBuf); } void Resource::clearLevelRes() { free(_tbn); _tbn = 0; free(_mbk); _mbk = 0; free(_mbkData); _mbkData = 0; free(_pal); _pal = 0; free(_map); _map = 0; free(_spc); _spc = 0; free(_ani); _ani = 0; free_OBJ(); } void Resource::load_FIB(const char *fileName) { debug(DBG_RES, "Resource::load_FIB('%s')", fileName); static const uint8 fibonacciTable[] = { 0xDE, 0xEB, 0xF3, 0xF8, 0xFB, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0D, 0x15 }; sprintf(_entryName, "%s.FIB", fileName); File f; if (f.open(_entryName, _dataPath, "rb")) { _numSfx = f.readUint16LE(); _sfxList = (SoundFx *)malloc(_numSfx * sizeof(SoundFx)); if (!_sfxList) { error("Unable to allocate SoundFx table"); } int i; for (i = 0; i < _numSfx; ++i) { SoundFx *sfx = &_sfxList[i]; sfx->offset = f.readUint32LE(); sfx->len = f.readUint16LE(); sfx->data = 0; } for (i = 0; i < _numSfx; ++i) { SoundFx *sfx = &_sfxList[i]; if (sfx->len == 0) { continue; } f.seek(sfx->offset); uint8 *data = (uint8 *)malloc(sfx->len * 2); if (!data) { error("Unable to allocate SoundFx data buffer"); } sfx->data = data; uint8 c = f.readByte(); *data++ = c; *data++ = c; uint16 sz = sfx->len - 1; while (sz--) { uint8 d = f.readByte(); c += fibonacciTable[d >> 4]; *data++ = c; c += fibonacciTable[d & 15]; *data++ = c; } sfx->len *= 2; } if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } void Resource::load_MAP_menu(const char *fileName, uint8 *dstPtr) { debug(DBG_RES, "Resource::load_MAP_menu('%s')", fileName); sprintf(_entryName, "%s.MAP", fileName); File f; if (f.open(_entryName, _dataPath, "rb")) { if (f.size() != 0x3800 * 4) { error("Wrong file size for '%s', %d", _entryName, f.size()); } f.read(dstPtr, 0x3800 * 4); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } void Resource::load_PAL_menu(const char *fileName, uint8 *dstPtr) { debug(DBG_RES, "Resource::load_PAL_menu('%s')", fileName); sprintf(_entryName, "%s.PAL", fileName); File f; if (f.open(_entryName, _dataPath, "rb")) { if (f.size() != 768) { error("Wrong file size for '%s', %d", _entryName, f.size()); } f.read(dstPtr, 768); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } void Resource::load_SPR_OFF(const char *fileName, uint8 *sprData) { debug(DBG_RES, "Resource::load_SPR_OFF('%s')", fileName); sprintf(_entryName, "%s.OFF", fileName); File f; if (f.open(_entryName, _dataPath, "rb")) { int len = f.size(); uint8 *offData = (uint8 *)malloc(len); if (!offData) { error("Unable to allocate sprite offsets"); } f.read(offData, len); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } const uint8 *p = offData; uint16 pos; while ((pos = READ_LE_UINT16(p)) != 0xFFFF) { uint32 off = READ_LE_UINT32(p + 2); if (off == 0xFFFFFFFF) { _spr_off[pos] = 0; } else { _spr_off[pos] = sprData + off; } p += 6; } free(offData); } else { error("Can't open '%s'", _entryName); } } void Resource::load_CINE() { const char *baseName = 0; switch (_ver) { case VER_FR: baseName = "FR_CINE"; break; case VER_EN: baseName = "ENGCINE"; break; case VER_DE: baseName = "GERCINE"; break; case VER_SP: baseName = "SPACINE"; break; } debug(DBG_RES, "Resource::load_CINE('%s')", baseName); if (_cine_off == 0) { sprintf(_entryName, "%s.BIN", baseName); File f; if (f.open(_entryName, _dataPath, "rb")) { int len = f.size(); _cine_off = (uint8 *)malloc(len); if (!_cine_off) { error("Unable to allocate cinematics offsets"); } f.read(_cine_off, len); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } if (_cine_txt == 0) { sprintf(_entryName, "%s.TXT", baseName); File f; if (f.open(_entryName, _dataPath, "rb")) { int len = f.size(); _cine_txt = (uint8 *)malloc(len); if (!_cine_txt) { error("Unable to allocate cinematics text data"); } f.read(_cine_txt, len); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } } void Resource::load_TEXT() { File f; // Load game strings _stringsTable = 0; if (f.open("STRINGS.TXT", _dataPath, "rb")) { const int sz = f.size(); _extStringsTable = (uint8 *)malloc(sz); if (_extStringsTable) { f.read(_extStringsTable, sz); _stringsTable = _extStringsTable; } f.close(); } if (!_stringsTable) { switch (_ver) { case VER_FR: _stringsTable = LocaleData::_stringsTableFR; break; case VER_EN: _stringsTable = LocaleData::_stringsTableEN; break; case VER_DE: _stringsTable = LocaleData::_stringsTableDE; break; case VER_SP: _stringsTable = LocaleData::_stringsTableSP; break; } } // Load menu strings _textsTable = 0; if (f.open("MENUS.TXT", _dataPath, "rb")) { const int offs = LocaleData::LI_NUM * sizeof(char *); const int sz = f.size() + 1; _extTextsTable = (char **)malloc(offs + sz); if (_extTextsTable) { char *textData = (char *)_extTextsTable + offs; f.read(textData, sz); textData[sz] = 0; int textsCount = 0; for (char *eol; (eol = strpbrk(textData, "\r\n")) != 0; ) { *eol++ = 0; if (*eol == '\r' || *eol == '\n') { *eol++ = 0; } if (textsCount < LocaleData::LI_NUM && textData[0] != 0) { _extTextsTable[textsCount] = textData; ++textsCount; } textData = eol; } if (textsCount < LocaleData::LI_NUM && textData[0] != 0) { _extTextsTable[textsCount] = textData; ++textsCount; } if (textsCount < LocaleData::LI_NUM) { free(_extTextsTable); _extTextsTable = 0; } else { _textsTable = (const char **)_extTextsTable; } } } if (!_textsTable) { switch (_ver) { case VER_FR: _textsTable = LocaleData::_textsTableFR; break; case VER_EN: _textsTable = LocaleData::_textsTableEN; break; case VER_DE: _textsTable = LocaleData::_textsTableDE; break; case VER_SP: _textsTable = LocaleData::_textsTableSP; break; } } } void Resource::free_TEXT() { if (_extTextsTable) { free(_extTextsTable); _extTextsTable = 0; } _stringsTable = 0; if (_extStringsTable) { free(_extStringsTable); _extStringsTable = 0; } _textsTable = 0; } void Resource::load(const char *objName, int objType) { debug(DBG_RES, "Resource::load('%s', %d)", objName, objType); LoadStub loadStub = 0; switch (objType) { case OT_MBK: debug(DBG_RES, "chargement bank (mbk)"); sprintf(_entryName, "%s.MBK", objName); loadStub = &Resource::load_MBK; break; case OT_PGE: debug(DBG_RES, "chargement piege (pge)"); sprintf(_entryName, "%s.PGE", objName); loadStub = &Resource::load_PGE; break; case OT_PAL: debug(DBG_RES, "chargement palettes (pal)"); sprintf(_entryName, "%s.PAL", objName); loadStub = &Resource::load_PAL; break; case OT_CT: debug(DBG_RES, "chargement collision (ct)"); sprintf(_entryName, "%s.CT", objName); loadStub = &Resource::load_CT; break; case OT_MAP: debug(DBG_RES, "ouverture map (map)"); sprintf(_entryName, "%s.MAP", objName); loadStub = &Resource::load_MAP; break; case OT_SPC: debug(DBG_RES, "chargement sprites caracteres (spc)"); strcpy(_entryName, "GLOBAL.SPC"); loadStub = &Resource::load_SPC; break; case OT_RP: debug(DBG_RES, "chargement positions des banks pour sprite_car (rp)"); sprintf(_entryName, "%s.RP", objName); loadStub = &Resource::load_RP; break; case OT_SPR: debug(DBG_RES, "chargement des sprites (spr)"); sprintf(_entryName, "%s.SPR", objName); loadStub = &Resource::load_SPR; break; case OT_SPRM: debug(DBG_RES, "chargement des sprites monstre (spr)"); sprintf(_entryName, "%s.SPR", objName); loadStub = &Resource::load_SPRM; break; case OT_ICN: debug(DBG_RES, "chargement des icones (icn)"); sprintf(_entryName, "%s.ICN", objName); loadStub = &Resource::load_ICN; break; case OT_FNT: debug(DBG_RES, "chargement de la font (fnt)"); sprintf(_entryName, "%s.FNT", objName); loadStub = &Resource::load_FNT; break; case OT_OBJ: debug(DBG_RES, "chargement objets (obj)"); sprintf(_entryName, "%s.OBJ", objName); loadStub = &Resource::load_OBJ; break; case OT_ANI: debug(DBG_RES, "chargement animations (ani)"); sprintf(_entryName, "%s.ANI", objName); loadStub = &Resource::load_ANI; break; case OT_TBN: debug(DBG_RES, "chargement des textes (tbn)"); sprintf(_entryName, "%s.TBN", objName); loadStub = &Resource::load_TBN; break; case OT_CMD: debug(DBG_RES, "chargement des commandes (cmd)"); sprintf(_entryName, "%s.CMD", objName); loadStub = &Resource::load_CMD; break; case OT_POL: debug(DBG_RES, "chargement des polygones (pol)"); sprintf(_entryName, "%s.POL", objName); loadStub = &Resource::load_POL; break; default: error("Unimplemented Resource::load() type %d", objType); break; } if (loadStub) { File f; if (f.open(_entryName, _dataPath, "rb")) { (this->*loadStub)(&f); if (f.ioErr()) { error("I/O error when reading '%s'", _entryName); } } else { error("Can't open '%s'", _entryName); } } } void Resource::load_CT(File *pf) { debug(DBG_RES, "Resource::load_CT()"); int len = pf->size(); uint8 *tmp = (uint8 *)malloc(len); if (!tmp) { error("Unable to allocate CT buffer"); } else { pf->read(tmp, len); if (!delphine_unpack((uint8 *)_ctData, tmp, len)) { error("Bad CRC for collision data"); } free(tmp); } } void Resource::load_FNT(File *f) { debug(DBG_RES, "Resource::load_FNT()"); int len = f->size(); _fnt = (uint8 *)malloc(len); if (!_fnt) { error("Unable to allocate FNT buffer"); } else { f->read(_fnt, len); } } void Resource::load_MBK(File *f) { debug(DBG_RES, "Resource::load_MBK()"); uint8 num = f->readByte(); int dataSize = f->size() - num * 6; _mbk = (MbkEntry *)malloc(sizeof(MbkEntry) * num); if (!_mbk) { error("Unable to allocate MBK buffer"); } f->seek(0); for (int i = 0; i < num; ++i) { f->readUint16BE(); /* unused */ _mbk[i].offset = f->readUint16BE() - num * 6; _mbk[i].len = f->readUint16BE(); debug(DBG_RES, "dataSize=0x%X entry %d off=0x%X len=0x%X", dataSize, i, _mbk[i].offset + num * 6, _mbk[i].len); assert(_mbk[i].offset <= dataSize); } _mbkData = (uint8 *)malloc(dataSize); if (!_mbkData) { error("Unable to allocate MBK data buffer"); } f->read(_mbkData, dataSize); } void Resource::load_ICN(File *f) { debug(DBG_RES, "Resource::load_ICN()"); int len = f->size(); _icn = (uint8 *)malloc(len); if (!_icn) { error("Unable to allocate ICN buffer"); } else { f->read(_icn, len); } } void Resource::load_SPR(File *f) { debug(DBG_RES, "Resource::load_SPR()"); int len = f->size() - 12; _spr1 = (uint8 *)malloc(len); if (!_spr1) { error("Unable to allocate SPR buffer"); } else { f->seek(12); f->read(_spr1, len); } } void Resource::load_SPRM(File *f) { debug(DBG_RES, "Resource::load_SPRM()"); int len = f->size() - 12; f->seek(12); f->read(_sprm, len); } void Resource::load_RP(File *f) { debug(DBG_RES, "Resource::load_RP()"); f->read(_rp, 0x4A); } void Resource::load_SPC(File *f) { debug(DBG_RES, "Resource::load_SPC()"); int len = f->size(); _spc = (uint8 *)malloc(len); if (!_spc) { error("Unable to allocate SPC buffer"); } else { f->read(_spc, len); _numSpc = READ_BE_UINT16(_spc) / 2; } } void Resource::load_PAL(File *f) { debug(DBG_RES, "Resource::load_PAL()"); int len = f->size(); _pal = (uint8 *)malloc(len); if (!_pal) { error("Unable to allocate PAL buffer"); } else { f->read(_pal, len); } } void Resource::load_MAP(File *f) { debug(DBG_RES, "Resource::load_MAP()"); int len = f->size(); _map = (uint8 *)malloc(len); if (!_map) { error("Unable to allocate MAP buffer"); } else { f->read(_map, len); } } void Resource::load_OBJ(File *f) { debug(DBG_RES, "Resource::load_OBJ()"); uint16 i; _numObjectNodes = f->readUint16LE(); assert(_numObjectNodes < 255); uint32 offsets[256]; for (i = 0; i < _numObjectNodes; ++i) { offsets[i] = f->readUint32LE(); } offsets[i] = f->size() - 2; int numObjectsCount = 0; uint16 objectsCount[256]; for (i = 0; i < _numObjectNodes; ++i) { int diff = offsets[i + 1] - offsets[i]; if (diff != 0) { objectsCount[numObjectsCount] = (diff - 2) / 0x12; debug(DBG_RES, "i=%d objectsCount[numObjectsCount]=%d", i, objectsCount[numObjectsCount]); ++numObjectsCount; } } uint32 prevOffset = 0; ObjectNode *prevNode = 0; int iObj = 0; for (i = 0; i < _numObjectNodes; ++i) { if (prevOffset != offsets[i]) { ObjectNode *on = (ObjectNode *)malloc(sizeof(ObjectNode)); if (!on) { error("Unable to allocate ObjectNode num=%d", i); } f->seek(offsets[i] + 2); on->last_obj_number = f->readUint16LE(); on->num_objects = objectsCount[iObj]; debug(DBG_RES, "last=%d num=%d", on->last_obj_number, on->num_objects); on->objects = (Object *)malloc(sizeof(Object) * on->num_objects); for (uint16 j = 0; j < on->num_objects; ++j) { Object *obj = &on->objects[j]; obj->type = f->readUint16LE(); obj->dx = f->readByte(); obj->dy = f->readByte(); obj->init_obj_type = f->readUint16LE(); obj->opcode2 = f->readByte(); obj->opcode1 = f->readByte(); obj->flags = f->readByte(); obj->opcode3 = f->readByte(); obj->init_obj_number = f->readUint16LE(); obj->opcode_arg1 = f->readUint16LE(); obj->opcode_arg2 = f->readUint16LE(); obj->opcode_arg3 = f->readUint16LE(); debug(DBG_RES, "obj_node=%d obj=%d op1=0x%X op2=0x%X op3=0x%X", i, j, obj->opcode2, obj->opcode1, obj->opcode3); } ++iObj; prevOffset = offsets[i]; prevNode = on; } _objectNodesMap[i] = prevNode; } } void Resource::free_OBJ() { debug(DBG_RES, "Resource::free_OBJ()"); ObjectNode *prevNode = 0; for (int i = 0; i < _numObjectNodes; ++i) { if (_objectNodesMap[i] != prevNode) { ObjectNode *curNode = _objectNodesMap[i]; free(curNode->objects); _objectNodesMap[i] = 0; prevNode = curNode; } } } void Resource::load_PGE(File *f) { debug(DBG_RES, "Resource::load_PGE()"); int len = f->size() - 2; _pgeNum = f->readUint16LE(); memset(_pgeInit, 0, sizeof(_pgeInit)); debug(DBG_RES, "len=%d _pgeNum=%d", len, _pgeNum); for (uint16 i = 0; i < _pgeNum; ++i) { InitPGE *pge = &_pgeInit[i]; pge->type = f->readUint16LE(); pge->pos_x = f->readUint16LE(); pge->pos_y = f->readUint16LE(); pge->obj_node_number = f->readUint16LE(); pge->life = f->readUint16LE(); for (int lc = 0; lc < 4; ++lc) { pge->counter_values[lc] = f->readUint16LE(); } pge->object_type = f->readByte(); pge->init_room = f->readByte(); pge->room_location = f->readByte(); pge->init_flags = f->readByte(); pge->colliding_icon_num = f->readByte(); pge->icon_num = f->readByte(); pge->object_id = f->readByte(); pge->skill = f->readByte(); pge->mirror_x = f->readByte(); pge->flags = f->readByte(); pge->unk1C = f->readByte(); f->readByte(); pge->text_num = f->readByte(); f->readByte(); } } void Resource::load_ANI(File *f) { debug(DBG_RES, "Resource::load_ANI()"); int size = f->size() - 2; _ani = (uint8 *)malloc(size); if (!_ani) { error("Unable to allocate ANI buffer"); } else { f->seek(2); f->read(_ani, size); } } void Resource::load_TBN(File *f) { debug(DBG_RES, "Resource::load_TBN()"); int len = f->size(); _tbn = (uint8 *)malloc(len); if (!_tbn) { error("Unable to allocate TBN buffer"); } else { f->read(_tbn, len); } } void Resource::load_CMD(File *pf) { debug(DBG_RES, "Resource::load_CMD()"); free(_cmd); int len = pf->size(); _cmd = (uint8 *)malloc(len); if (!_cmd) { error("Unable to allocate CMD buffer"); } else { pf->read(_cmd, len); } } void Resource::load_POL(File *pf) { debug(DBG_RES, "Resource::load_POL()"); free(_pol); int len = pf->size(); _pol = (uint8 *)malloc(len); if (!_pol) { error("Unable to allocate POL buffer"); } else { pf->read(_pol, len); } } void Resource::load_VCE(int num, int segment, uint8 **buf, uint32 *bufSize) { *buf = 0; int offset = _voicesOffsetsTable[num]; if (offset != 0xFFFF) { const uint16 *p = _voicesOffsetsTable + offset / 2; offset = (*p++) * 2048; int count = *p++; if (segment < count) { File f; if (f.open("VOICE.VCE", _dataPath, "rb")) { int voiceSize = p[segment] * 2048 / 5; free(_voiceBuf); _voiceBuf = (uint8 *)malloc(voiceSize); if (_voiceBuf) { uint8 *dst = _voiceBuf; offset += 0x2000; for (int s = 0; s < count; ++s) { int len = p[s] * 2048; for (int i = 0; i < len / (0x2000 + 2048); ++i) { if (s == segment) { f.seek(offset); int n = 2048; while (n--) { int v = f.readByte(); if (v & 0x80) { v = -(v & 0x7F); } *dst++ = (uint8)(v & 0xFF); } } offset += 0x2000 + 2048; } if (s == segment) { break; } } *buf = _voiceBuf; *bufSize = voiceSize; } } } } }