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 * release 0.4.7
- added detection for Macintosh CD version - added detection for Macintosh CD version
- restored some content from MEMO cutscene - restored some content from MEMO cutscene

View File

@ -1,6 +1,6 @@
REminiscence README 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) { 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); debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify);
int len = 0; int len = 0;
if (_res->_type == kResourceTypeMac) { if (_res->isMac()) {
len = *p++; if (p == _textBuf) {
while (p[len] != 0xA) {
++len;
}
} else {
len = *p++;
}
} else { } else {
len = strlen((const char *)p); len = strlen((const char *)p);
} }
@ -197,16 +203,39 @@ void Cutscene::clearBackPage() {
void Cutscene::drawCreditsText() { void Cutscene::drawCreditsText() {
if (_creditsSequence) { if (_creditsSequence) {
if (_creditsKeepText != 0) { if (_creditsKeepText) {
if (_creditsSlowText == 0) { if (_creditsSlowText) {
_creditsKeepText = 0;
} else {
return; return;
} }
_creditsKeepText = false;
} }
if (_creditsTextCounter <= 0) { if (_creditsTextCounter <= 0) {
const uint8_t code = *_textCurPtr; uint8_t code;
if (code == 0xFF) { 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; _textBuf[0] = 0xA;
} else if (code == 0xFE) { } else if (code == 0xFE) {
++_textCurPtr; ++_textCurPtr;
@ -219,13 +248,19 @@ void Cutscene::drawCreditsText() {
_textCurBuf = _textBuf; _textCurBuf = _textBuf;
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
++_textCurPtr; ++_textCurPtr;
if (_creditsSlowText != 0) { if (_creditsSlowText) {
_creditsKeepText = 0xFF; _creditsKeepText = true;
} }
} else { } else {
*_textCurBuf++ = code; *_textCurBuf++ = code;
*_textCurBuf = 0xA; *_textCurBuf = 0xA;
++_textCurPtr; ++_textCurPtr;
if (isMac) {
--_creditsTextLen;
if (_creditsTextLen == 0) {
_creditsTextCounter = 600;
}
}
} }
} else { } else {
_creditsTextCounter -= 10; _creditsTextCounter -= 10;
@ -276,7 +311,7 @@ void Cutscene::op_markCurPos() {
_frameDelay = 5; _frameDelay = 5;
updateScreen(); updateScreen();
clearBackPage(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = false;
} }
void Cutscene::op_refreshScreen() { void Cutscene::op_refreshScreen() {
@ -284,7 +319,7 @@ void Cutscene::op_refreshScreen() {
_clearScreen = fetchNextCmdByte(); _clearScreen = fetchNextCmdByte();
if (_clearScreen != 0) { if (_clearScreen != 0) {
clearBackPage(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = false;
} }
} }
@ -293,17 +328,17 @@ void Cutscene::op_waitForSync() {
if (_creditsSequence) { if (_creditsSequence) {
uint16_t n = fetchNextCmdByte() * 2; uint16_t n = fetchNextCmdByte() * 2;
do { do {
_creditsSlowText = 0xFF; _creditsSlowText = true;
_frameDelay = 3; _frameDelay = 3;
if (_textBuf == _textCurBuf) { if (_textBuf == _textCurBuf) {
_creditsTextCounter = _res->isAmiga() ? 60 : 20; _creditsTextCounter = _res->isDOS() ? 20 : 60;
} }
memcpy(_backPage, _frontPage, _vid->_layerSize); memcpy(_backPage, _frontPage, _vid->_layerSize);
drawCreditsText(); drawCreditsText();
updateScreen(); updateScreen();
} while (--n); } while (--n);
clearBackPage(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = false;
} else { } else {
_frameDelay = fetchNextCmdByte() * 4; _frameDelay = fetchNextCmdByte() * 4;
sync(); // XXX handle input sync(); // XXX handle input
@ -418,15 +453,6 @@ void Cutscene::op_drawCaptionText() {
uint16_t strId = fetchNextCmdWord(); uint16_t strId = fetchNextCmdWord();
if (!_creditsSequence) { 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 h = 45 * _vid->_layerScale;
const int y = Video::GAMESCREEN_H * _vid->_layerScale - h; 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, _backPage, kTextJustifyAlign);
drawText(0, 129, str, 0xEF, _auxPage, 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; _frameDelay = 5;
updateScreen(); updateScreen();
clearBackPage(); clearBackPage();
_creditsSlowText = 0xFF; _creditsSlowText = true;
op_handleKeys(); op_handleKeys();
} }
@ -634,7 +664,7 @@ void Cutscene::op_drawShapeScale() {
_hasAlphaColor = (verticesOffset & 0x4000) != 0; _hasAlphaColor = (verticesOffset & 0x4000) != 0;
uint8_t color = *shapeData++; uint8_t color = *shapeData++;
if (_clearScreen == 0) { if (_clearScreen == 0) {
color += 0x10; // 2nd paletter buffer color += 0x10; // 2nd palette buffer
} }
_primitiveColor = 0xC0 + color; _primitiveColor = 0xC0 + color;
drawShapeScale(p, zoom, dx, dy, x, y, 0, 0); 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() { void Cutscene::op_copyScreen() {
debug(DBG_CUT, "Cutscene::op_copyScreen()"); debug(DBG_CUT, "Cutscene::op_copyScreen()");
_creditsSlowText = 0xFF; _creditsSlowText = true;
if (_textCurBuf == _textBuf) { if (_textCurBuf == _textBuf) {
++_creditsTextCounter; ++_creditsTextCounter;
} }
@ -954,11 +984,11 @@ void Cutscene::op_drawTextAtPos() {
if (!_creditsSequence) { if (!_creditsSequence) {
const uint8_t *str = _res->getCineString(strId & 0xFFF); const uint8_t *str = _res->getCineString(strId & 0xFFF);
if (str) { if (str) {
uint8_t color = 0xD0 + (strId >> 0xC); const uint8_t color = 0xD0 + (strId >> 0xC);
drawText(x, y, str, color, _backPage, kTextJustifyCenter); drawText(x, y, str, color, _backPage, kTextJustifyCenter);
} }
// 'voyage' - cutscene script redraws the string to refresh the screen // '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) { if ((_cmdPtr - _cmdPtrBak) == 0xA) {
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w);
_stub->updateScreen(0); _stub->updateScreen(0);
@ -1099,9 +1129,9 @@ bool Cutscene::load(uint16_t cutName) {
name = "SERRURE"; name = "SERRURE";
} }
_res->load(name, Resource::OT_CMP); _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 // fixed in DOS version, opcodes order is wrong
// //
// opcode 0 pos 0x323 // opcode 0 pos 0x323
@ -1167,15 +1197,17 @@ void Cutscene::prepare() {
void Cutscene::playCredits() { void Cutscene::playCredits() {
if (_res->isMac()) { if (_res->isMac()) {
warning("Cutscene::playCredits() unimplemented"); _res->MAC_loadCreditsText();
return; _creditsTextIndex = 0;
_creditsTextLen = 0;
} else {
_textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS;
} }
_textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS;
_textBuf[0] = 0xA; _textBuf[0] = 0xA;
_textCurBuf = _textBuf; _textCurBuf = _textBuf;
_creditsSequence = true; _creditsSequence = true;
_creditsSlowText = 0; _creditsSlowText = false;
_creditsKeepText = 0; _creditsKeepText = false;
_creditsTextCounter = 0; _creditsTextCounter = 0;
_interrupted = false; _interrupted = false;
const uint16_t *cut_seq = _creditsCutSeq; const uint16_t *cut_seq = _creditsCutSeq;

View File

@ -30,7 +30,9 @@ struct Cutscene {
}; };
enum { enum {
kCineMemo = 48 kCineMemo = 48,
kCineVoyage = 52,
kCineEspions = 57
}; };
struct SetShape { struct SetShape {
@ -104,11 +106,13 @@ struct Cutscene {
uint8_t _textBuf[500]; uint8_t _textBuf[500];
const uint8_t *_textCurPtr; const uint8_t *_textCurPtr;
uint8_t *_textCurBuf; uint8_t *_textCurBuf;
uint8_t _creditsSlowText; bool _creditsSlowText;
uint8_t _creditsKeepText; bool _creditsKeepText;
uint8_t _creditsTextPosX; uint8_t _creditsTextPosX;
uint8_t _creditsTextPosY; uint8_t _creditsTextPosY;
int16_t _creditsTextCounter; int16_t _creditsTextCounter;
int _creditsTextIndex; /* MAC has the credits data in a resource */
int _creditsTextLen;
uint8_t *_frontPage, *_backPage, *_auxPage; uint8_t *_frontPage, *_backPage, *_auxPage;
Cutscene(Resource *res, SystemStub *stub, Video *vid); Cutscene(Resource *res, SystemStub *stub, Video *vid);

View File

@ -3,11 +3,15 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "decode_mac.h" #include "decode_mac.h"
#include "file.h" #include "util.h"
uint8_t *decodeLzss(File &f, uint32_t &decodedSize) { uint8_t *decodeLzss(File &f, uint32_t &decodedSize) {
decodedSize = f.readUint32BE(); decodedSize = f.readUint32BE();
uint8_t *dst = (uint8_t *)malloc(decodedSize); uint8_t *dst = (uint8_t *)malloc(decodedSize);
if (!dst) {
warning("Failed to allocate %d bytes for LZSS", decodedSize);
return 0;
}
uint32_t count = 0; uint32_t count = 0;
while (count < decodedSize) { while (count < decodedSize) {
const int code = f.readByte(); const int code = f.readByte();

View File

@ -11,7 +11,6 @@
#include "game.h" #include "game.h"
#include "seq_player.h" #include "seq_player.h"
#include "systemstub.h" #include "systemstub.h"
#include "unpack.h"
#include "util.h" #include "util.h"
Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave) 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]); _stub->setPaletteEntry(0xC0 + i, &palette[i]);
} }
} }
if (id == 0x3D) { if (_cut._id == 0x3D) {
_mix.playMusic(Mixer::MUSIC_TRACK + 9);
_cut.playCredits(); _cut.playCredits();
} }
_mix.stopMusic(); _mix.stopMusic();
@ -1678,7 +1678,7 @@ void Game::loadLevelData() {
_res.load(lvl->name, Resource::OT_CT); _res.load(lvl->name, Resource::OT_CT);
_res.load(lvl->name, Resource::OT_PAL); _res.load(lvl->name, Resource::OT_PAL);
_res.load(lvl->name, Resource::OT_RP); _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) { if (_currentLevel == 0) {
_res.load(lvl->name, Resource::OT_SGD); _res.load(lvl->name, Resource::OT_SGD);
} }

View File

@ -35,6 +35,7 @@ static int detectVersion(FileSystem *fs) {
const char *name; const char *name;
} table[] = { } table[] = {
{ "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" }, { "DEMO_UK.ABA", kResourceTypeDOS, "DOS (Demo)" },
{ "GLOB_FR.ABA", kResourceTypeDOS, "DOS" },
{ "INTRO.SEQ", kResourceTypeDOS, "DOS CD" }, { "INTRO.SEQ", kResourceTypeDOS, "DOS CD" },
{ "MENU1SSI.MAP", kResourceTypeDOS, "DOS SSI" }, { "MENU1SSI.MAP", kResourceTypeDOS, "DOS SSI" },
{ "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, { "LEVEL1.MAP", kResourceTypeDOS, "DOS" },
@ -60,7 +61,8 @@ static Language detectLanguage(FileSystem *fs) {
const char *filename; const char *filename;
Language language; Language language;
} table[] = { } table[] = {
// PC // DOS
{ "GLOB_FR.ABA", LANG_FR },
{ "ENGCINE.TXT", LANG_EN }, { "ENGCINE.TXT", LANG_EN },
{ "FR_CINE.TXT", LANG_FR }, { "FR_CINE.TXT", LANG_FR },
{ "GERCINE.TXT", LANG_DE }, { "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); _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_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_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_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->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H, _vid->_layerScale);
_vid->updateScreen(); _vid->updateScreen();

View File

@ -4,7 +4,6 @@
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "cutscene.h"
#include "game.h" #include "game.h"
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"

View File

@ -56,16 +56,27 @@ Resource::~Resource() {
delete _mac; 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() { void Resource::init() {
switch (_type) { switch (_type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
_isDemo = _fs->exists("demo.lev"); _isDemo = _fs->exists("demo.lev");
break; break;
case kResourceTypeDOS: case kResourceTypeDOS:
if (_fs->exists(ResourceAba::FILENAME)) { // fbdemous if (_fs->exists(_demoAba)) { // fbdemous
_aba = new ResourceAba(_fs); _aba = new ResourceAba(_fs);
_aba->readEntries(); _aba->readEntries(_demoAba);
_isDemo = true; _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) } else if (!fileExists("LEVEL2.MAP")) { // fbdemofr (no cutscenes)
_isDemo = true; _isDemo = true;
} }
@ -403,7 +414,7 @@ void Resource::load_CINE() {
case kResourceTypeDOS: case kResourceTypeDOS:
if (_cine_off == 0) { if (_cine_off == 0) {
snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix); snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix);
if (!_fs->exists(_entryName)) { if (!fileExists(_entryName)) {
strcpy(_entryName, "ENGCINE.BIN"); strcpy(_entryName, "ENGCINE.BIN");
} }
File f; File f;
@ -428,7 +439,7 @@ void Resource::load_CINE() {
} }
if (_cine_txt == 0) { if (_cine_txt == 0) {
snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix);
if (!_fs->exists(_entryName)) { if (!fileExists(_entryName)) {
strcpy(_entryName, "ENGCINE.TXT"); strcpy(_entryName, "ENGCINE.TXT");
} }
File f; File f;
@ -699,7 +710,7 @@ void Resource::load(const char *objName, int objType, const char *ext) {
_numSpc = READ_BE_UINT16(_spc) / 2; _numSpc = READ_BE_UINT16(_spc) / 2;
break; break;
case OT_RP: case OT_RP:
if (size != 0x4A) { if (size != sizeof(_rp)) {
error("Unexpected size %d for '%s'", size, _entryName); error("Unexpected size %d for '%s'", size, _entryName);
} }
memcpy(_rp, dat, size); memcpy(_rp, dat, size);
@ -728,6 +739,10 @@ void Resource::load(const char *objName, int objType, const char *ext) {
case OT_POL: case OT_POL:
_pol = dat; _pol = dat;
break; break;
case OT_SGD:
_sgd = dat;
_sgd[0] = 0; // clear number of entries, fix first offset
break;
case OT_BNQ: case OT_BNQ:
_bnq = dat; _bnq = dat;
break; break;
@ -825,7 +840,7 @@ void Resource::load_SPRM(File *f) {
void Resource::load_RP(File *f) { void Resource::load_RP(File *f) {
debug(DBG_RES, "Resource::load_RP()"); debug(DBG_RES, "Resource::load_RP()");
f->read(_rp, 0x4A); f->read(_rp, sizeof(_rp));
} }
void Resource::load_SPC(File *f) { 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); snprintf(buf, sizeof(buf), "%s %s", name, suffix);
const ResourceMacEntry *entry = _mac->findEntry(buf); const ResourceMacEntry *entry = _mac->findEntry(buf);
if (entry) { if (entry) {
return decodeResourceMacData(buf, false); return decodeResourceMacData(entry, false);
} else { // CD version } else { // CD version
if (strcmp(name, "Flashback") == 0) { if (strcmp(name, "Flashback") == 0) {
name = "Game"; 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) { uint8_t *Resource::decodeResourceMacData(const char *name, bool decompressLzss) {
_resourceMacDataSize = 0;
uint8_t *data = 0; uint8_t *data = 0;
const ResourceMacEntry *entry = _mac->findEntry(name); const ResourceMacEntry *entry = _mac->findEntry(name);
if (entry) { if (entry) {
_mac->_f.seek(_mac->_dataOffset + entry->dataOffset); data = decodeResourceMacData(entry, decompressLzss);
_resourceMacDataSize = _mac->_f.readUint32BE(); } else {
if (decompressLzss) { _resourceMacDataSize = 0;
data = decodeLzss(_mac->_f, _resourceMacDataSize); error("Resource '%s' not found", name);
} else { }
data = (uint8_t *)malloc(_resourceMacDataSize); return data;
if (data) { }
_mac->_f.read(data, _resourceMacDataSize);
} 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 { } 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; return data;
} }
@ -1501,10 +1528,8 @@ void Resource::MAC_decodeDataCLUT(const uint8_t *ptr) {
void Resource::MAC_loadClutData() { void Resource::MAC_loadClutData() {
uint8_t *ptr = decodeResourceMacData("Flashback colors", false); uint8_t *ptr = decodeResourceMacData("Flashback colors", false);
if (ptr) { MAC_decodeDataCLUT(ptr);
MAC_decodeDataCLUT(ptr); free(ptr);
free(ptr);
}
} }
void Resource::MAC_loadFontData() { 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) { void Resource::MAC_loadLevelData(int level) {
char name[64]; char name[64];
// .PGE // .PGE
snprintf(name, sizeof(name), "Level %s objects", _macLevelNumbers[level]); snprintf(name, sizeof(name), "Level %s objects", _macLevelNumbers[level]);
uint8_t *ptr = decodeResourceMacData(name, true); uint8_t *ptr = decodeResourceMacData(name, true);
if (ptr) { decodePGE(ptr, _resourceMacDataSize);
decodePGE(ptr, _resourceMacDataSize); free(ptr);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .ANI // .ANI
snprintf(name, sizeof(name), "Level %s sequences", _macLevelNumbers[level]); snprintf(name, sizeof(name), "Level %s sequences", _macLevelNumbers[level]);
_ani = decodeResourceMacData(name, true); _ani = decodeResourceMacData(name, true);
if (_ani) { assert(READ_BE_UINT16(_ani) == 0x48D);
assert(READ_BE_UINT16(_ani) == 0x48D);
} else {
error("Failed to load '%s'", name);
}
// .OBJ // .OBJ
snprintf(name, sizeof(name), "Level %s conditions", _macLevelNumbers[level]); snprintf(name, sizeof(name), "Level %s conditions", _macLevelNumbers[level]);
ptr = decodeResourceMacData(name, true); ptr = decodeResourceMacData(name, true);
if (ptr) { assert(READ_BE_UINT16(ptr) == 0xE6);
assert(READ_BE_UINT16(ptr) == 0xE6); decodeOBJ(ptr, _resourceMacDataSize);
decodeOBJ(ptr, _resourceMacDataSize); free(ptr);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .CT // .CT
snprintf(name, sizeof(name), "Level %c map", _macLevelNumbers[level][0]); snprintf(name, sizeof(name), "Level %c map", _macLevelNumbers[level][0]);
ptr = decodeResourceMacData(name, true); ptr = decodeResourceMacData(name, true);
if (ptr) { assert(_resourceMacDataSize == 0x1D00);
assert(_resourceMacDataSize == 0x1D00); memcpy(_ctData, ptr, _resourceMacDataSize);
memcpy(_ctData, ptr, _resourceMacDataSize); free(ptr);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .SPC // .SPC
snprintf(name, sizeof(name), "Objects %c", _macLevelNumbers[level][0]); snprintf(name, sizeof(name), "Objects %c", _macLevelNumbers[level][0]);
_spc = decodeResourceMacData(name, true); _spc = decodeResourceMacData(name, true);
// .TBN // .TBN
snprintf(name, sizeof(name), "Level %s", _macLevelNumbers[level]); snprintf(name, sizeof(name), "Level %s", _macLevelNumbers[level]);
_tbn = decodeResourceMacText(name, "names"); _tbn = decodeResourceMacText(name, "names");
@ -1626,10 +1641,8 @@ void Resource::MAC_loadLevelRoom(int level, int i, DecodeBuffer *dst) {
char name[64]; char name[64];
snprintf(name, sizeof(name), "Level %c Room %d", _macLevelNumbers[level][0], i); snprintf(name, sizeof(name), "Level %c Room %d", _macLevelNumbers[level][0], i);
uint8_t *ptr = decodeResourceMacData(name, true); uint8_t *ptr = decodeResourceMacData(name, true);
if (ptr) { MAC_decodeImageData(ptr, 0, dst);
MAC_decodeImageData(ptr, 0, dst); free(ptr);
free(ptr);
}
} }
void Resource::MAC_clearClut16(Color *clut, uint8_t dest) { void Resource::MAC_clearClut16(Color *clut, uint8_t dest) {
@ -1704,15 +1717,24 @@ void Resource::MAC_unloadCutscene() {
} }
void Resource::MAC_loadCutscene(const char *cutscene) { void Resource::MAC_loadCutscene(const char *cutscene) {
MAC_unloadCutscene();
char name[32]; char name[32];
free(_cmd);
snprintf(name, sizeof(name), "%s movie", cutscene); snprintf(name, sizeof(name), "%s movie", cutscene);
stringLowerCase(name); stringLowerCase(name);
_cmd = decodeResourceMacData(name, true); const ResourceMacEntry *cmdEntry = _mac->findEntry(name);
free(_pol); if (!cmdEntry) {
return;
}
_cmd = decodeResourceMacData(cmdEntry, true);
snprintf(name, sizeof(name), "%s polygons", cutscene); snprintf(name, sizeof(name), "%s polygons", cutscene);
stringLowerCase(name); stringLowerCase(name);
_pol = decodeResourceMacData(name, true); const ResourceMacEntry *polEntry = _mac->findEntry(name);
if (!polEntry) {
return;
}
_pol = decodeResourceMacData(polEntry, true);
} }
void Resource::MAC_loadCutsceneText() { void Resource::MAC_loadCutsceneText() {

View File

@ -137,13 +137,13 @@ struct Resource {
uint8_t *_icn; uint8_t *_icn;
int _icnLen; int _icnLen;
uint8_t *_tab; uint8_t *_tab;
uint8_t *_spc; // BE uint8_t *_spc;
uint16_t _numSpc; uint16_t _numSpc;
uint8_t _rp[0x4A]; uint8_t _rp[74];
uint8_t *_pal; // BE uint8_t *_pal;
uint8_t *_ani; uint8_t *_ani;
uint8_t *_tbn; uint8_t *_tbn;
int8_t _ctData[0x1D00]; int8_t _ctData[256 + 112 * 64];
uint8_t *_spr1; uint8_t *_spr1;
uint8_t *_sprData[NUM_SPRITES]; // 0-0x22F + 0x28E-0x2E9 ... conrad, 0x22F-0x28D : junkie uint8_t *_sprData[NUM_SPRITES]; // 0-0x22F + 0x28E-0x2E9 ... conrad, 0x22F-0x28D : junkie
uint8_t _sprm[0x10000]; uint8_t _sprm[0x10000];
@ -313,6 +313,15 @@ struct Resource {
const char *getMenuString(int num) const { const char *getMenuString(int num) const {
return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : ""; 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(); void clearBankData();
int getBankDataSize(uint16_t num); int getBankDataSize(uint16_t num);
uint8_t *findBankData(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 *decodeResourceMacText(const char *name, const char *suffix);
uint8_t *decodeResourceMacData(const char *name, bool decompressLzss); 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_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst);
void MAC_decodeDataCLUT(const uint8_t *ptr); void MAC_decodeDataCLUT(const uint8_t *ptr);
void MAC_loadClutData(); void MAC_loadClutData();

View File

@ -3,10 +3,9 @@
#include "unpack.h" #include "unpack.h"
#include "util.h" #include "util.h"
const char *ResourceAba::FILENAME = "DEMO_UK.ABA";
ResourceAba::ResourceAba(FileSystem *fs) ResourceAba::ResourceAba(FileSystem *fs)
: _fs(fs) { : _fs(fs) {
_filesCount = 0;
_entries = 0; _entries = 0;
_entriesCount = 0; _entriesCount = 0;
} }
@ -19,31 +18,38 @@ static int compareAbaEntry(const void *a, const void *b) {
return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name); return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name);
} }
void ResourceAba::readEntries() { void ResourceAba::readEntries(const char *aba) {
if (_f.open(FILENAME, "rb", _fs)) { assert(_filesCount < 3);
_entriesCount = _f.readUint16BE(); if (_f[_filesCount].open(aba, "rb", _fs)) {
_entries = (ResourceAbaEntry *)calloc(_entriesCount, sizeof(ResourceAbaEntry)); File &f = _f[_filesCount];
const int currentCount = _entriesCount;
const int entriesCount = f.readUint16BE();
_entries = (ResourceAbaEntry *)realloc(_entries, (currentCount + entriesCount) * sizeof(ResourceAbaEntry));
if (!_entries) { if (!_entries) {
error("Failed to allocate %d _entries", _entriesCount); error("Failed to allocate %d _entries", currentCount + entriesCount);
return; return;
} }
const int entrySize = _f.readUint16BE(); _entriesCount = currentCount + entriesCount;
const int entrySize = f.readUint16BE();
assert(entrySize == 30); assert(entrySize == 30);
uint32_t nextOffset = 0; uint32_t nextOffset = 0;
for (int i = 0; i < _entriesCount; ++i) { for (int i = 0; i < entriesCount; ++i) {
_f.read(_entries[i].name, sizeof(_entries[i].name)); const int j = currentCount + i;
_entries[i].offset = _f.readUint32BE(); f.read(_entries[j].name, sizeof(_entries[j].name));
_entries[i].compressedSize = _f.readUint32BE(); _entries[j].offset = f.readUint32BE();
_entries[i].size = _f.readUint32BE(); _entries[j].compressedSize = f.readUint32BE();
const uint32_t tag = _f.readUint32BE(); _entries[j].size = f.readUint32BE();
_entries[j].fileIndex = _filesCount;
const uint32_t tag = f.readUint32BE();
assert(tag == TAG); 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) { 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); 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); error("Failed to allocate %d bytes", e->compressedSize);
return 0; return 0;
} }
_f.seek(e->offset); _f[e->fileIndex].seek(e->offset);
_f.read(tmp, e->compressedSize); _f[e->fileIndex].read(tmp, e->compressedSize);
if (e->compressedSize == e->size) { if (e->compressedSize == e->size) {
dst = tmp; dst = tmp;
} else { } else {

View File

@ -11,22 +11,23 @@ struct ResourceAbaEntry {
uint32_t offset; uint32_t offset;
uint32_t compressedSize; uint32_t compressedSize;
uint32_t size; uint32_t size;
int fileIndex;
}; };
struct ResourceAba { struct ResourceAba {
static const char *FILENAME;
static const int TAG = 0x442E4D2E; static const int TAG = 0x442E4D2E;
FileSystem *_fs; FileSystem *_fs;
File _f; File _f[3];
int _filesCount;
ResourceAbaEntry *_entries; ResourceAbaEntry *_entries;
int _entriesCount; int _entriesCount;
ResourceAba(FileSystem *fs); ResourceAba(FileSystem *fs);
~ResourceAba(); ~ResourceAba();
void readEntries(); void readEntries(const char *aba);
const ResourceAbaEntry *findEntry(const char *name) const; const ResourceAbaEntry *findEntry(const char *name) const;
uint8_t *loadEntry(const char *name, uint32_t *size = 0); uint8_t *loadEntry(const char *name, uint32_t *size = 0);
}; };

View File

@ -30,8 +30,10 @@ static bool nextBit(UnpackCtx *uc) {
template<int count> template<int count>
static uint32_t getBits(UnpackCtx *uc) { // rdd1bits static uint32_t getBits(UnpackCtx *uc) { // rdd1bits
uint32_t bits = 0; uint32_t bits = 0;
for (int i = 0; i < count; ++i) { for (uint32_t mask = 1 << (count - 1); mask != 0; mask >>= 1) {
bits |= (nextBit(uc) ? 1 : 0) << (count - 1 - i); if (nextBit(uc)) {
bits |= mask;
}
} }
return bits; return bits;
} }