Import 0.4.8

This commit is contained in:
Gregory Montoir 2021-05-28 00:00:00 +08:00
parent bc1337da63
commit 315bb9bcff
14 changed files with 219 additions and 132 deletions

View File

@ -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

View File

@ -1,6 +1,6 @@
REminiscence README
Release version: 0.4.7
Release version: 0.4.8
-------------------------------------------------------------------------------

View File

@ -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;

View File

@ -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);

View File

@ -3,11 +3,15 @@
#include <stdlib.h>
#include <string.h>
#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();

View File

@ -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);
}

View File

@ -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 },

View File

@ -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();

View File

@ -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"

View File

@ -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() {

View File

@ -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();

View File

@ -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 {

View File

@ -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);
};

View File

@ -30,8 +30,10 @@ static bool nextBit(UnpackCtx *uc) {
template<int count>
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;
}