diff --git a/CHANGES.txt b/CHANGES.txt index 875eba7..69723bb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +* release 0.4.8 + - added detection for DOS version with .ABA files + - added Macintosh credits + - fixed ESPIONS cutscene timing with Amiga music + * release 0.4.7 - added detection for Macintosh CD version - restored some content from MEMO cutscene diff --git a/README.txt b/README.txt index e1d9b8a..32aeb4c 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.4.7 +Release version: 0.4.8 ------------------------------------------------------------------------------- diff --git a/cutscene.cpp b/cutscene.cpp index 7849249..24c50a5 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -147,8 +147,14 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p, int len) { void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) { debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify); int len = 0; - if (_res->_type == kResourceTypeMac) { - len = *p++; + if (_res->isMac()) { + if (p == _textBuf) { + while (p[len] != 0xA) { + ++len; + } + } else { + len = *p++; + } } else { len = strlen((const char *)p); } @@ -197,16 +203,39 @@ void Cutscene::clearBackPage() { void Cutscene::drawCreditsText() { if (_creditsSequence) { - if (_creditsKeepText != 0) { - if (_creditsSlowText == 0) { - _creditsKeepText = 0; - } else { + if (_creditsKeepText) { + if (_creditsSlowText) { return; } + _creditsKeepText = false; } if (_creditsTextCounter <= 0) { - const uint8_t code = *_textCurPtr; - if (code == 0xFF) { + uint8_t code; + const bool isMac = _res->isMac(); + if (isMac && _creditsTextLen <= 0) { + const uint8_t *p = _res->getCreditsString(_creditsTextIndex++); + if (!p) { + return; + } + _creditsTextCounter = 60; + _creditsTextPosX = p[0]; + _creditsTextPosY = p[1]; + _creditsTextLen = p[2]; + _textCurPtr = p + 2; + code = 0; + } else { + code = *_textCurPtr; + } + if (code == 0x7D && isMac) { + ++_textCurPtr; + code = *_textCurPtr++; + _creditsTextLen -= 2; + assert(code > 0x30); + for (int i = 0; i < (code - 0x30); ++i) { + *_textCurBuf++ = ' '; + } + *_textCurBuf = 0xA; + } else if (code == 0xFF) { _textBuf[0] = 0xA; } else if (code == 0xFE) { ++_textCurPtr; @@ -219,13 +248,19 @@ void Cutscene::drawCreditsText() { _textCurBuf = _textBuf; _textBuf[0] = 0xA; ++_textCurPtr; - if (_creditsSlowText != 0) { - _creditsKeepText = 0xFF; + if (_creditsSlowText) { + _creditsKeepText = true; } } else { *_textCurBuf++ = code; *_textCurBuf = 0xA; ++_textCurPtr; + if (isMac) { + --_creditsTextLen; + if (_creditsTextLen == 0) { + _creditsTextCounter = 600; + } + } } } else { _creditsTextCounter -= 10; @@ -276,7 +311,7 @@ void Cutscene::op_markCurPos() { _frameDelay = 5; updateScreen(); clearBackPage(); - _creditsSlowText = 0; + _creditsSlowText = false; } void Cutscene::op_refreshScreen() { @@ -284,7 +319,7 @@ void Cutscene::op_refreshScreen() { _clearScreen = fetchNextCmdByte(); if (_clearScreen != 0) { clearBackPage(); - _creditsSlowText = 0; + _creditsSlowText = false; } } @@ -293,17 +328,17 @@ void Cutscene::op_waitForSync() { if (_creditsSequence) { uint16_t n = fetchNextCmdByte() * 2; do { - _creditsSlowText = 0xFF; + _creditsSlowText = true; _frameDelay = 3; if (_textBuf == _textCurBuf) { - _creditsTextCounter = _res->isAmiga() ? 60 : 20; + _creditsTextCounter = _res->isDOS() ? 20 : 60; } memcpy(_backPage, _frontPage, _vid->_layerSize); drawCreditsText(); updateScreen(); } while (--n); clearBackPage(); - _creditsSlowText = 0; + _creditsSlowText = false; } else { _frameDelay = fetchNextCmdByte() * 4; sync(); // XXX handle input @@ -418,15 +453,6 @@ void Cutscene::op_drawCaptionText() { uint16_t strId = fetchNextCmdWord(); if (!_creditsSequence) { - // 'espions' - ignore last call, allows caption to be displayed longer on the screen - if (_id == 0x39 && strId == 0xFFFF) { - if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - getCommandData()) == 0x9F3)) { - _frameDelay = 100; - updateScreen(); - return; - } - } - const int h = 45 * _vid->_layerScale; const int y = Video::GAMESCREEN_H * _vid->_layerScale - h; @@ -439,6 +465,10 @@ void Cutscene::op_drawCaptionText() { drawText(0, 129, str, 0xEF, _backPage, kTextJustifyAlign); drawText(0, 129, str, 0xEF, _auxPage, kTextJustifyAlign); } + } else if (_id == kCineEspions) { + // cutscene relies on drawCaptionText opcodes for timing + _frameDelay = 100; + sync(); } } } @@ -457,7 +487,7 @@ void Cutscene::op_refreshAll() { _frameDelay = 5; updateScreen(); clearBackPage(); - _creditsSlowText = 0xFF; + _creditsSlowText = true; op_handleKeys(); } @@ -634,7 +664,7 @@ void Cutscene::op_drawShapeScale() { _hasAlphaColor = (verticesOffset & 0x4000) != 0; uint8_t color = *shapeData++; if (_clearScreen == 0) { - color += 0x10; // 2nd paletter buffer + color += 0x10; // 2nd palette buffer } _primitiveColor = 0xC0 + color; drawShapeScale(p, zoom, dx, dy, x, y, 0, 0); @@ -908,7 +938,7 @@ static int findSetPaletteColor(const uint16_t color, const uint16_t *paletteBuff void Cutscene::op_copyScreen() { debug(DBG_CUT, "Cutscene::op_copyScreen()"); - _creditsSlowText = 0xFF; + _creditsSlowText = true; if (_textCurBuf == _textBuf) { ++_creditsTextCounter; } @@ -954,11 +984,11 @@ void Cutscene::op_drawTextAtPos() { if (!_creditsSequence) { const uint8_t *str = _res->getCineString(strId & 0xFFF); if (str) { - uint8_t color = 0xD0 + (strId >> 0xC); + const uint8_t color = 0xD0 + (strId >> 0xC); drawText(x, y, str, color, _backPage, kTextJustifyCenter); } // 'voyage' - cutscene script redraws the string to refresh the screen - if (_id == 0x34 && (strId & 0xFFF) == 0x45) { + if (_id == kCineVoyage && (strId & 0xFFF) == 0x45) { if ((_cmdPtr - _cmdPtrBak) == 0xA) { _stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w); _stub->updateScreen(0); @@ -1099,9 +1129,9 @@ bool Cutscene::load(uint16_t cutName) { name = "SERRURE"; } _res->load(name, Resource::OT_CMP); - if (_id == 0x39 && _res->_lang != LANG_FR) { + if (_id == kCineEspions) { // - // 'espions' - '... the power which we need' caption is missing in Amiga English. + // '... the power which we need' caption is missing. // fixed in DOS version, opcodes order is wrong // // opcode 0 pos 0x323 @@ -1167,15 +1197,17 @@ void Cutscene::prepare() { void Cutscene::playCredits() { if (_res->isMac()) { - warning("Cutscene::playCredits() unimplemented"); - return; + _res->MAC_loadCreditsText(); + _creditsTextIndex = 0; + _creditsTextLen = 0; + } else { + _textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS; } - _textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS; _textBuf[0] = 0xA; _textCurBuf = _textBuf; _creditsSequence = true; - _creditsSlowText = 0; - _creditsKeepText = 0; + _creditsSlowText = false; + _creditsKeepText = false; _creditsTextCounter = 0; _interrupted = false; const uint16_t *cut_seq = _creditsCutSeq; diff --git a/cutscene.h b/cutscene.h index 1540292..8c6caf7 100644 --- a/cutscene.h +++ b/cutscene.h @@ -30,7 +30,9 @@ struct Cutscene { }; enum { - kCineMemo = 48 + kCineMemo = 48, + kCineVoyage = 52, + kCineEspions = 57 }; struct SetShape { @@ -104,11 +106,13 @@ struct Cutscene { uint8_t _textBuf[500]; const uint8_t *_textCurPtr; uint8_t *_textCurBuf; - uint8_t _creditsSlowText; - uint8_t _creditsKeepText; + bool _creditsSlowText; + bool _creditsKeepText; uint8_t _creditsTextPosX; uint8_t _creditsTextPosY; int16_t _creditsTextCounter; + int _creditsTextIndex; /* MAC has the credits data in a resource */ + int _creditsTextLen; uint8_t *_frontPage, *_backPage, *_auxPage; Cutscene(Resource *res, SystemStub *stub, Video *vid); diff --git a/decode_mac.cpp b/decode_mac.cpp index 205e25e..8c3106d 100644 --- a/decode_mac.cpp +++ b/decode_mac.cpp @@ -3,11 +3,15 @@ #include #include #include "decode_mac.h" -#include "file.h" +#include "util.h" uint8_t *decodeLzss(File &f, uint32_t &decodedSize) { decodedSize = f.readUint32BE(); uint8_t *dst = (uint8_t *)malloc(decodedSize); + if (!dst) { + warning("Failed to allocate %d bytes for LZSS", decodedSize); + return 0; + } uint32_t count = 0; while (count < decodedSize) { const int code = f.readByte(); diff --git a/game.cpp b/game.cpp index 33dd647..7b39025 100644 --- a/game.cpp +++ b/game.cpp @@ -11,7 +11,6 @@ #include "game.h" #include "seq_player.h" #include "systemstub.h" -#include "unpack.h" #include "util.h" Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave) @@ -557,7 +556,8 @@ void Game::playCutscene(int id) { _stub->setPaletteEntry(0xC0 + i, &palette[i]); } } - if (id == 0x3D) { + if (_cut._id == 0x3D) { + _mix.playMusic(Mixer::MUSIC_TRACK + 9); _cut.playCredits(); } _mix.stopMusic(); @@ -1678,7 +1678,7 @@ void Game::loadLevelData() { _res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_PAL); _res.load(lvl->name, Resource::OT_RP); - if (_res._isDemo || g_options.use_tile_data) { // use .BNQ/.LEV/(.SGD) instead of .MAP (PC demo) + if (_res._isDemo || g_options.use_tile_data || _res._aba) { // use .BNQ/.LEV/(.SGD) instead of .MAP (PC demo) if (_currentLevel == 0) { _res.load(lvl->name, Resource::OT_SGD); } diff --git a/main.cpp b/main.cpp index a9c325a..cec0fbf 100644 --- a/main.cpp +++ b/main.cpp @@ -35,6 +35,7 @@ static int detectVersion(FileSystem *fs) { const char *name; } table[] = { { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" }, + { "GLOB_FR.ABA", kResourceTypeDOS, "DOS" }, { "INTRO.SEQ", kResourceTypeDOS, "DOS CD" }, { "MENU1SSI.MAP", kResourceTypeDOS, "DOS SSI" }, { "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, @@ -60,7 +61,8 @@ static Language detectLanguage(FileSystem *fs) { const char *filename; Language language; } table[] = { - // PC + // DOS + { "GLOB_FR.ABA", LANG_FR }, { "ENGCINE.TXT", LANG_EN }, { "FR_CINE.TXT", LANG_FR }, { "GERCINE.TXT", LANG_DE }, diff --git a/menu.cpp b/menu.cpp index 50e4142..98438d9 100644 --- a/menu.cpp +++ b/menu.cpp @@ -259,9 +259,9 @@ bool Menu::handleLevelScreen() { } _vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H, _vid->_layerScale); - drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3); - drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3); - drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3); + drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3); + drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3); + drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3); _vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H, _vid->_layerScale); _vid->updateScreen(); diff --git a/piege.cpp b/piege.cpp index d27663c..ed9af09 100644 --- a/piege.cpp +++ b/piege.cpp @@ -4,7 +4,6 @@ * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ -#include "cutscene.h" #include "game.h" #include "resource.h" #include "systemstub.h" diff --git a/resource.cpp b/resource.cpp index b93e970..df7c6b0 100644 --- a/resource.cpp +++ b/resource.cpp @@ -56,16 +56,27 @@ Resource::~Resource() { delete _mac; } +static const char *_demoAba = "DEMO_UK.ABA"; + +static const char *_joystickAba[] = { + "GLOB1_FB.ABA", "GLOB2_FB.ABA", "GLOB_FR.ABA", 0 +}; + void Resource::init() { switch (_type) { case kResourceTypeAmiga: _isDemo = _fs->exists("demo.lev"); break; case kResourceTypeDOS: - if (_fs->exists(ResourceAba::FILENAME)) { // fbdemous + if (_fs->exists(_demoAba)) { // fbdemous _aba = new ResourceAba(_fs); - _aba->readEntries(); + _aba->readEntries(_demoAba); _isDemo = true; + } else if (_fs->exists(_joystickAba[0])) { // Joystick "Hors Serie" April 1996 + _aba = new ResourceAba(_fs); + for (int i = 0; _joystickAba[i]; ++i) { + _aba->readEntries(_joystickAba[i]); + } } else if (!fileExists("LEVEL2.MAP")) { // fbdemofr (no cutscenes) _isDemo = true; } @@ -403,7 +414,7 @@ void Resource::load_CINE() { case kResourceTypeDOS: if (_cine_off == 0) { snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix); - if (!_fs->exists(_entryName)) { + if (!fileExists(_entryName)) { strcpy(_entryName, "ENGCINE.BIN"); } File f; @@ -428,7 +439,7 @@ void Resource::load_CINE() { } if (_cine_txt == 0) { snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); - if (!_fs->exists(_entryName)) { + if (!fileExists(_entryName)) { strcpy(_entryName, "ENGCINE.TXT"); } File f; @@ -699,7 +710,7 @@ void Resource::load(const char *objName, int objType, const char *ext) { _numSpc = READ_BE_UINT16(_spc) / 2; break; case OT_RP: - if (size != 0x4A) { + if (size != sizeof(_rp)) { error("Unexpected size %d for '%s'", size, _entryName); } memcpy(_rp, dat, size); @@ -728,6 +739,10 @@ void Resource::load(const char *objName, int objType, const char *ext) { case OT_POL: _pol = dat; break; + case OT_SGD: + _sgd = dat; + _sgd[0] = 0; // clear number of entries, fix first offset + break; case OT_BNQ: _bnq = dat; break; @@ -825,7 +840,7 @@ void Resource::load_SPRM(File *f) { void Resource::load_RP(File *f) { debug(DBG_RES, "Resource::load_RP()"); - f->read(_rp, 0x4A); + f->read(_rp, sizeof(_rp)); } void Resource::load_SPC(File *f) { @@ -1430,7 +1445,7 @@ uint8_t *Resource::decodeResourceMacText(const char *name, const char *suffix) { snprintf(buf, sizeof(buf), "%s %s", name, suffix); const ResourceMacEntry *entry = _mac->findEntry(buf); if (entry) { - return decodeResourceMacData(buf, false); + return decodeResourceMacData(entry, false); } else { // CD version if (strcmp(name, "Flashback") == 0) { name = "Game"; @@ -1442,22 +1457,34 @@ uint8_t *Resource::decodeResourceMacText(const char *name, const char *suffix) { } uint8_t *Resource::decodeResourceMacData(const char *name, bool decompressLzss) { - _resourceMacDataSize = 0; uint8_t *data = 0; const ResourceMacEntry *entry = _mac->findEntry(name); if (entry) { - _mac->_f.seek(_mac->_dataOffset + entry->dataOffset); - _resourceMacDataSize = _mac->_f.readUint32BE(); - if (decompressLzss) { - data = decodeLzss(_mac->_f, _resourceMacDataSize); - } else { - data = (uint8_t *)malloc(_resourceMacDataSize); - if (data) { - _mac->_f.read(data, _resourceMacDataSize); - } + data = decodeResourceMacData(entry, decompressLzss); + } else { + _resourceMacDataSize = 0; + error("Resource '%s' not found", name); + } + return data; +} + +uint8_t *Resource::decodeResourceMacData(const ResourceMacEntry *entry, bool decompressLzss) { + assert(entry); + _mac->_f.seek(_mac->_dataOffset + entry->dataOffset); + _resourceMacDataSize = _mac->_f.readUint32BE(); + uint8_t *data = 0; + if (decompressLzss) { + data = decodeLzss(_mac->_f, _resourceMacDataSize); + if (!data) { + error("Failed to decompress '%s'", entry->name); } } else { - error("Resource '%s' not found", name); + data = (uint8_t *)malloc(_resourceMacDataSize); + if (!data) { + error("Failed to allocate %d bytes for '%s'", _resourceMacDataSize, entry->name); + } else { + _mac->_f.read(data, _resourceMacDataSize); + } } return data; } @@ -1501,10 +1528,8 @@ void Resource::MAC_decodeDataCLUT(const uint8_t *ptr) { void Resource::MAC_loadClutData() { uint8_t *ptr = decodeResourceMacData("Flashback colors", false); - if (ptr) { - MAC_decodeDataCLUT(ptr); - free(ptr); - } + MAC_decodeDataCLUT(ptr); + free(ptr); } void Resource::MAC_loadFontData() { @@ -1575,46 +1600,36 @@ static const char *_macLevelNumbers[] = { "1", "2", "3", "4-1", "4-2", "5-1", "5 void Resource::MAC_loadLevelData(int level) { char name[64]; + // .PGE snprintf(name, sizeof(name), "Level %s objects", _macLevelNumbers[level]); uint8_t *ptr = decodeResourceMacData(name, true); - if (ptr) { - decodePGE(ptr, _resourceMacDataSize); - free(ptr); - } else { - error("Failed to load '%s'", name); - } + decodePGE(ptr, _resourceMacDataSize); + free(ptr); + // .ANI snprintf(name, sizeof(name), "Level %s sequences", _macLevelNumbers[level]); _ani = decodeResourceMacData(name, true); - if (_ani) { - assert(READ_BE_UINT16(_ani) == 0x48D); - } else { - error("Failed to load '%s'", name); - } + assert(READ_BE_UINT16(_ani) == 0x48D); + // .OBJ snprintf(name, sizeof(name), "Level %s conditions", _macLevelNumbers[level]); ptr = decodeResourceMacData(name, true); - if (ptr) { - assert(READ_BE_UINT16(ptr) == 0xE6); - decodeOBJ(ptr, _resourceMacDataSize); - free(ptr); - } else { - error("Failed to load '%s'", name); - } + assert(READ_BE_UINT16(ptr) == 0xE6); + decodeOBJ(ptr, _resourceMacDataSize); + free(ptr); + // .CT snprintf(name, sizeof(name), "Level %c map", _macLevelNumbers[level][0]); ptr = decodeResourceMacData(name, true); - if (ptr) { - assert(_resourceMacDataSize == 0x1D00); - memcpy(_ctData, ptr, _resourceMacDataSize); - free(ptr); - } else { - error("Failed to load '%s'", name); - } + assert(_resourceMacDataSize == 0x1D00); + memcpy(_ctData, ptr, _resourceMacDataSize); + free(ptr); + // .SPC snprintf(name, sizeof(name), "Objects %c", _macLevelNumbers[level][0]); _spc = decodeResourceMacData(name, true); + // .TBN snprintf(name, sizeof(name), "Level %s", _macLevelNumbers[level]); _tbn = decodeResourceMacText(name, "names"); @@ -1626,10 +1641,8 @@ void Resource::MAC_loadLevelRoom(int level, int i, DecodeBuffer *dst) { char name[64]; snprintf(name, sizeof(name), "Level %c Room %d", _macLevelNumbers[level][0], i); uint8_t *ptr = decodeResourceMacData(name, true); - if (ptr) { - MAC_decodeImageData(ptr, 0, dst); - free(ptr); - } + MAC_decodeImageData(ptr, 0, dst); + free(ptr); } void Resource::MAC_clearClut16(Color *clut, uint8_t dest) { @@ -1704,15 +1717,24 @@ void Resource::MAC_unloadCutscene() { } void Resource::MAC_loadCutscene(const char *cutscene) { + MAC_unloadCutscene(); char name[32]; - free(_cmd); + snprintf(name, sizeof(name), "%s movie", cutscene); stringLowerCase(name); - _cmd = decodeResourceMacData(name, true); - free(_pol); + const ResourceMacEntry *cmdEntry = _mac->findEntry(name); + if (!cmdEntry) { + return; + } + _cmd = decodeResourceMacData(cmdEntry, true); + snprintf(name, sizeof(name), "%s polygons", cutscene); stringLowerCase(name); - _pol = decodeResourceMacData(name, true); + const ResourceMacEntry *polEntry = _mac->findEntry(name); + if (!polEntry) { + return; + } + _pol = decodeResourceMacData(polEntry, true); } void Resource::MAC_loadCutsceneText() { diff --git a/resource.h b/resource.h index b705d78..35d63da 100644 --- a/resource.h +++ b/resource.h @@ -137,13 +137,13 @@ struct Resource { uint8_t *_icn; int _icnLen; uint8_t *_tab; - uint8_t *_spc; // BE + uint8_t *_spc; uint16_t _numSpc; - uint8_t _rp[0x4A]; - uint8_t *_pal; // BE + uint8_t _rp[74]; + uint8_t *_pal; uint8_t *_ani; uint8_t *_tbn; - int8_t _ctData[0x1D00]; + int8_t _ctData[256 + 112 * 64]; uint8_t *_spr1; uint8_t *_sprData[NUM_SPRITES]; // 0-0x22F + 0x28E-0x2E9 ... conrad, 0x22F-0x28D : junkie uint8_t _sprm[0x10000]; @@ -313,6 +313,15 @@ struct Resource { const char *getMenuString(int num) const { return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : ""; } + const uint8_t *getCreditsString(int num) { + assert(_type == kResourceTypeMac); + const int count = READ_BE_UINT16(_credits); + if (num < count) { + const int offset = READ_BE_UINT16(_credits + 2 + num * 2); + return _credits + offset; + } + return 0; + } void clearBankData(); int getBankDataSize(uint16_t num); uint8_t *findBankData(uint16_t num); @@ -320,6 +329,7 @@ struct Resource { uint8_t *decodeResourceMacText(const char *name, const char *suffix); uint8_t *decodeResourceMacData(const char *name, bool decompressLzss); + uint8_t *decodeResourceMacData(const ResourceMacEntry *entry, bool decompressLzss); void MAC_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst); void MAC_decodeDataCLUT(const uint8_t *ptr); void MAC_loadClutData(); diff --git a/resource_aba.cpp b/resource_aba.cpp index 68f2705..1ef30c7 100644 --- a/resource_aba.cpp +++ b/resource_aba.cpp @@ -3,10 +3,9 @@ #include "unpack.h" #include "util.h" -const char *ResourceAba::FILENAME = "DEMO_UK.ABA"; - ResourceAba::ResourceAba(FileSystem *fs) : _fs(fs) { + _filesCount = 0; _entries = 0; _entriesCount = 0; } @@ -19,31 +18,38 @@ static int compareAbaEntry(const void *a, const void *b) { return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name); } -void ResourceAba::readEntries() { - if (_f.open(FILENAME, "rb", _fs)) { - _entriesCount = _f.readUint16BE(); - _entries = (ResourceAbaEntry *)calloc(_entriesCount, sizeof(ResourceAbaEntry)); +void ResourceAba::readEntries(const char *aba) { + assert(_filesCount < 3); + if (_f[_filesCount].open(aba, "rb", _fs)) { + File &f = _f[_filesCount]; + const int currentCount = _entriesCount; + const int entriesCount = f.readUint16BE(); + _entries = (ResourceAbaEntry *)realloc(_entries, (currentCount + entriesCount) * sizeof(ResourceAbaEntry)); if (!_entries) { - error("Failed to allocate %d _entries", _entriesCount); + error("Failed to allocate %d _entries", currentCount + entriesCount); return; } - const int entrySize = _f.readUint16BE(); + _entriesCount = currentCount + entriesCount; + const int entrySize = f.readUint16BE(); assert(entrySize == 30); uint32_t nextOffset = 0; - for (int i = 0; i < _entriesCount; ++i) { - _f.read(_entries[i].name, sizeof(_entries[i].name)); - _entries[i].offset = _f.readUint32BE(); - _entries[i].compressedSize = _f.readUint32BE(); - _entries[i].size = _f.readUint32BE(); - const uint32_t tag = _f.readUint32BE(); + for (int i = 0; i < entriesCount; ++i) { + const int j = currentCount + i; + f.read(_entries[j].name, sizeof(_entries[j].name)); + _entries[j].offset = f.readUint32BE(); + _entries[j].compressedSize = f.readUint32BE(); + _entries[j].size = f.readUint32BE(); + _entries[j].fileIndex = _filesCount; + const uint32_t tag = f.readUint32BE(); assert(tag == TAG); - debug(DBG_RES, "'%s' offset 0x%X size %d/%d", _entries[i].name, _entries[i].offset, _entries[i].compressedSize, _entries[i].size); + debug(DBG_RES, "'%s' offset 0x%X size %d/%d", _entries[j].name, _entries[j].offset, _entries[j].compressedSize, _entries[j].size); if (i != 0) { - assert(nextOffset == _entries[i].offset); + assert(nextOffset == _entries[j].offset); } - nextOffset = _entries[i].offset + _entries[i].compressedSize; + nextOffset = _entries[j].offset + _entries[j].compressedSize; } qsort(_entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry); + ++_filesCount; } } @@ -65,8 +71,8 @@ uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) { error("Failed to allocate %d bytes", e->compressedSize); return 0; } - _f.seek(e->offset); - _f.read(tmp, e->compressedSize); + _f[e->fileIndex].seek(e->offset); + _f[e->fileIndex].read(tmp, e->compressedSize); if (e->compressedSize == e->size) { dst = tmp; } else { diff --git a/resource_aba.h b/resource_aba.h index 0736b0f..3667310 100644 --- a/resource_aba.h +++ b/resource_aba.h @@ -11,22 +11,23 @@ struct ResourceAbaEntry { uint32_t offset; uint32_t compressedSize; uint32_t size; + int fileIndex; }; struct ResourceAba { - static const char *FILENAME; static const int TAG = 0x442E4D2E; FileSystem *_fs; - File _f; + File _f[3]; + int _filesCount; ResourceAbaEntry *_entries; int _entriesCount; ResourceAba(FileSystem *fs); ~ResourceAba(); - void readEntries(); + void readEntries(const char *aba); const ResourceAbaEntry *findEntry(const char *name) const; uint8_t *loadEntry(const char *name, uint32_t *size = 0); }; diff --git a/unpack.cpp b/unpack.cpp index ada3a20..1b10db8 100644 --- a/unpack.cpp +++ b/unpack.cpp @@ -30,8 +30,10 @@ static bool nextBit(UnpackCtx *uc) { template static uint32_t getBits(UnpackCtx *uc) { // rdd1bits uint32_t bits = 0; - for (int i = 0; i < count; ++i) { - bits |= (nextBit(uc) ? 1 : 0) << (count - 1 - i); + for (uint32_t mask = 1 << (count - 1); mask != 0; mask >>= 1) { + if (nextBit(uc)) { + bits |= mask; + } } return bits; }