Files
scummvm-cursorfix/engines/awe/resource.cpp
2026-02-02 04:50:13 +01:00

696 lines
17 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "awe/resource.h"
#include "awe/pak.h"
#include "awe/resource_nth.h"
#include "awe/resource_win31.h"
#include "awe/resource_3do.h"
#include "awe/unpack.h"
#include "awe/video.h"
namespace Awe {
static const char *atariDemo = "aw.tos";
static const char *kGameTitleEU = "Another World";
static const char *kGameTitleUS = "Out Of This World";
static const char *kGameTitle15thEdition = "Another World 15th anniversary edition";
static const char *kGameTitle20thEdition = "Another World 20th anniversary edition";
static const int MEMLIST_BMP[] = {
145, 144, 73, 72, 70, 69, 68, 67, -1
};
Resource::Resource(Video *vid, DataType dataType) :
_vid(vid), _dataType(dataType) {
if (_dataType == DT_AMIGA || _dataType == DT_ATARI) {
_amigaMemList = detectAmigaAtari();
} else
_amigaMemList = nullptr;
}
Resource::~Resource() {
free(_demo3Joy.bufPtr);
delete _nth;
delete _win31;
delete _3do;
}
bool Resource::readBank(const MemEntry *me, uint8 *dstBuf) {
bool ret = false;
char name[10];
snprintf(name, sizeof(name), "%s%02x", _bankPrefix, me->bankNum);
Common::File f;
if (f.open(name) || (_dataType == DT_ATARI_DEMO && f.open(atariDemo))) {
f.seek(me->bankPos);
const size_t count = f.read(dstBuf, me->packedSize);
ret = (count == me->packedSize);
if (ret && me->packedSize != me->unpackedSize) {
ret = bytekiller_unpack(dstBuf, me->unpackedSize, dstBuf, me->packedSize);
}
}
return ret;
}
const AmigaMemEntry *Resource::detectAmigaAtari() {
Common::File f;
static const struct {
uint32 bank01Size;
const AmigaMemEntry *entries;
} FILES[] = {
{ 244674, Resource::MEMLIST_AMIGA_FR },
{ 244868, Resource::MEMLIST_AMIGA_EN },
{ 227142, Resource::MEMLIST_ATARI_EN },
{ 0, nullptr }
};
if (f.open("bank01")) {
const uint32 size = f.size();
for (int i = 0; FILES[i].entries; ++i) {
if (FILES[i].bank01Size == size) {
return FILES[i].entries;
}
}
}
return nullptr;
}
const char *Resource::getGameTitle(Language lang) const {
switch (_dataType) {
case DT_15TH_EDITION:
return kGameTitle15thEdition;
case DT_20TH_EDITION:
return kGameTitle20thEdition;
case DT_3DO:
return kGameTitleUS;
case DT_DOS:
if (lang == Common::EN_ANY) {
return kGameTitleUS;
}
/* fall-through */
default:
break;
}
return kGameTitleEU;
}
void Resource::readEntries() {
switch (_dataType) {
case DT_15TH_EDITION:
_numMemList = ENTRIES_COUNT;
_nth = ResourceNth::create(15);
if (_nth && _nth->init()) {
return;
}
break;
case DT_20TH_EDITION:
_numMemList = ENTRIES_COUNT_20TH;
_nth = ResourceNth::create(20);
if (_nth && _nth->init()) {
return;
}
break;
case DT_AMIGA:
case DT_ATARI:
assert(_amigaMemList);
readEntriesAmiga(_amigaMemList, ENTRIES_COUNT);
return;
case DT_DOS:
{
_hasPasswordScreen = false; // DOS demo versions do not have the resources
Common::File f;
if (Common::File::exists("demo01")) {
_bankPrefix = "demo";
}
if (f.open("memlist.bin")) {
MemEntry *me = _memList;
for (;;) {
assert(_numMemList < ARRAYSIZE(_memList));
me->load(&f);
if (me->status == 0xFF) {
const int num = MEMLIST_PARTS[8][1]; // 16008 bytecode
assert(num < _numMemList);
const Common::String bank = Common::String::format(
"%s%02x", _bankPrefix, _memList[num].bankNum);
_hasPasswordScreen = Common::File::exists(bank.c_str());
return;
}
++_numMemList;
++me;
}
}
}
break;
case DT_WIN31:
_numMemList = ENTRIES_COUNT;
_win31 = new ResourceWin31();
if (_win31->readEntries()) {
return;
}
break;
case DT_3DO:
_numMemList = ENTRIES_COUNT;
_3do = new Resource3do();
if (_3do->readEntries()) {
return;
}
break;
case DT_ATARI_DEMO:
{
Common::File f;
if (f.open(atariDemo)) {
static const struct {
uint8 type;
uint8 num;
uint32 offset;
uint16 size;
} DATA[] = {
{ RT_SHAPE, 0x19, 0x50f0, 65146 },
{ RT_PALETTE, 0x17, 0x14f6a, 2048 },
{ RT_BYTECODE, 0x18, 0x1576a, 8368 }
};
_numMemList = ENTRIES_COUNT;
for (int i = 0; i < 3; ++i) {
MemEntry *entry = &_memList[DATA[i].num];
entry->type = DATA[i].type;
entry->bankNum = 15;
entry->bankPos = DATA[i].offset;
entry->packedSize = entry->unpackedSize = DATA[i].size;
}
return;
}
}
break;
}
error("No data files found");
}
void Resource::readEntriesAmiga(const AmigaMemEntry *entries, int count) {
_numMemList = count;
for (int i = 0; i < count; ++i) {
_memList[i].type = entries[i].type;
_memList[i].bankNum = entries[i].bank;
_memList[i].bankPos = entries[i].offset;
_memList[i].packedSize = entries[i].packedSize;
_memList[i].unpackedSize = entries[i].unpackedSize;
}
_memList[count].status = 0xFF;
}
void Resource::dumpEntries() {
if (ConfMan.getBool("dump_scripts") && (_dataType == DT_DOS || _dataType == DT_AMIGA || _dataType == DT_ATARI)) {
for (int i = 0; i < _numMemList; ++i) {
if (_memList[i].unpackedSize == 0) {
continue;
}
if (_memList[i].bankNum == 5 && (_dataType == DT_AMIGA || _dataType == DT_ATARI)) {
continue;
}
uint8 *p = (uint8 *)malloc(_memList[i].unpackedSize);
if (p) {
if (readBank(&_memList[i], p)) {
Common::String fname = Common::String::format("dumps/data_%02x_%d", i, _memList[i].type);
Common::DumpFile out;
if (!out.open(fname.c_str(), true)) {
warning("Resource::dumpEntries(): Can not open dump file %s", fname.c_str());
} else {
out.write(p, _memList[i].unpackedSize);
out.flush();
out.close();
}
}
free(p);
}
}
}
}
void Resource::load() {
while (true) {
MemEntry *me = nullptr;
// get resource with max rankNum
uint8 maxNum = 0;
for (int i = 0; i < _numMemList; ++i) {
MemEntry *it = &_memList[i];
if (it->status == STATUS_TOLOAD && maxNum <= it->rankNum) {
maxNum = it->rankNum;
me = it;
}
}
if (me == nullptr) {
break; // no entry found
}
const int resourceNum = me - _memList;
uint8 *memPtr;
if (me->type == RT_BITMAP) {
memPtr = _vidCurPtr;
} else {
memPtr = _scriptCurPtr;
const uint32 avail = uint32(_vidCurPtr - _scriptCurPtr);
if (me->unpackedSize > avail) {
warning("Resource::load() not enough memory, available=%d", avail);
me->status = STATUS_NULL;
continue;
}
}
if (me->bankNum == 0) {
warning("Resource::load() ec=0x%X (me->bankNum == 0)", 0xF00);
me->status = STATUS_NULL;
} else {
debugC(kDebugBank, "Resource::load() bufPos=0x%x size=%d type=%d pos=0x%x bankNum=%d",
(uint)(memPtr - _memPtrStart), me->packedSize, me->type,
me->bankPos, me->bankNum);
if (readBank(me, memPtr)) {
if (me->type == RT_BITMAP) {
_vid->copyBitmapPtr(_vidCurPtr, me->unpackedSize);
me->status = STATUS_NULL;
} else {
me->bufPtr = memPtr;
me->status = STATUS_LOADED;
_scriptCurPtr += me->unpackedSize;
}
} else {
if (_dataType == DT_DOS && me->bankNum == 12 && me->type == RT_BANK) {
// DOS demo version does not have the bank for this resource
// this should be safe to ignore as the resource does not appear to be used by the game code
me->status = STATUS_NULL;
continue;
}
error("Unable to read resource %d from bank %d", resourceNum, me->bankNum);
}
}
}
}
void Resource::invalidateRes() {
for (int i = 0; i < _numMemList; ++i) {
MemEntry *me = &_memList[i];
if (me->type <= 2 || me->type > 6) {
me->status = STATUS_NULL;
}
}
_scriptCurPtr = _scriptBakPtr;
_vid->_currentPal = 0xFF;
}
void Resource::invalidateAll() {
for (int i = 0; i < _numMemList; ++i) {
_memList[i].status = STATUS_NULL;
}
_scriptCurPtr = _memPtrStart;
_vid->_currentPal = 0xFF;
}
static const uint8 *getSoundsList3DO(int num) {
static const uint8 INTRO7[] = {
0x33, 0xFF
};
static const uint8 WATER7[] = {
0x08, 0x10, 0x2D, 0x30, 0x31, 0x32, 0x35, 0x39, 0x3A, 0x3C,
0x3D, 0x3E, 0x4A, 0x4B, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
0x54, 0xFF
};
static const uint8 PRI1[] = {
0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
0x5E, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x70, 0x71, 0x72, 0x73, 0xFF
};
static const uint8 CITE1[] = {
0x02, 0x03, 0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x63, 0x66, 0x6A, 0x6B, 0x6C,
0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x74, 0x75, 0x77, 0x78, 0x79,
0x7A, 0x7B, 0x7C, 0x88, 0xFF
};
static const uint8 ARENE2[] = {
0x52, 0x57, 0x58, 0x59, 0x5B, 0x84, 0x8B, 0x8C, 0x8E, 0xFF
};
static const uint8 LUXE2[] = {
0x30, 0x52, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C,
0x5D, 0x5E, 0x5F, 0x60, 0x66, 0x67, 0x6B, 0x6C, 0x70, 0x74,
0x75, 0x79, 0x7A, 0x8D, 0xFF
};
static const uint8 FINAL3[] = {
0x08, 0x0E, 0x0F, 0x57, 0xFF
};
switch (num) {
case 2001: return INTRO7;
case 2002: return WATER7;
case 2003: return PRI1;
case 2004: return CITE1;
case 2005: return ARENE2;
case 2006: return LUXE2;
case 2007: return FINAL3;
default: break;
}
return nullptr;
}
void Resource::update(uint16 num, PreloadSoundProc preloadSound, void *data) {
if (num > 16000) {
_nextPart = num;
return;
}
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
if (num >= 3000) {
loadBmp(num);
break;
} else if (num >= 2000) {
break;
}
/* fall-through */
case DT_WIN31:
if (num == 71 || num == 83) {
loadBmp(num);
break;
}
for (int i = 0; MEMLIST_BMP[i] != -1; ++i) {
if (num == MEMLIST_BMP[i]) {
loadBmp(num);
return;
}
}
break;
case DT_3DO:
if (num >= 2000) { // preload sounds
const uint8 *soundsList = getSoundsList3DO(num);
for (int i = 0; soundsList[i] != 255; ++i) {
const int soundNum = soundsList[i];
loadDat(soundNum);
if (_memList[soundNum].status == STATUS_LOADED) {
preloadSound(data, soundNum, _memList[soundNum].bufPtr);
}
}
} else if (num >= 200) {
loadBmp(num);
}
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS:
if (num >= ENTRIES_COUNT_20TH)
error("Resource::update - Resource number too high %d", num);
MemEntry *me = &_memList[num];
if (me->status == STATUS_NULL) {
me->status = STATUS_TOLOAD;
load();
}
break;
}
}
void Resource::loadBmp(int num) {
uint32 size = 0;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadBmp(num);
break;
case DT_WIN31:
p = _win31->loadFile(num, nullptr, &size);
break;
case DT_3DO:
p = _3do->loadFile(num, nullptr, &size);
break;
default:
break;
}
if (p) {
_vid->copyBitmapPtr(p, size);
free(p);
}
}
uint8 *Resource::loadDat(int num) {
assert(num < _numMemList);
if (_memList[num].status == STATUS_LOADED) {
return _memList[num].bufPtr;
}
uint32 size = 0;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadDat(num, _scriptCurPtr, &size);
break;
case DT_WIN31:
p = _win31->loadFile(num, _scriptCurPtr, &size);
break;
case DT_3DO:
p = _3do->loadFile(num, _scriptCurPtr, &size);
break;
default:
break;
}
if (p) {
_scriptCurPtr += size;
_memList[num].bufPtr = p;
_memList[num].status = STATUS_LOADED;
}
return p;
}
void Resource::loadFont() {
if (_nth) {
uint8 *p = _nth->load("font.bmp");
if (p) {
_vid->setFont(p);
free(p);
}
}
}
void Resource::loadHeads() {
if (_nth) {
uint8 *p = _nth->load("heads.bmp");
if (p) {
_vid->setHeads(p);
free(p);
}
}
}
uint8 *Resource::loadWav(int num, uint32 *size) {
if (_memList[num].status == STATUS_LOADED) {
return _memList[num].bufPtr;
}
uint32 dummy = 0;
if (!size)
size = &dummy;
uint8 *p = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
p = _nth->loadWav(num, _scriptCurPtr, size);
break;
case DT_WIN31:
p = _win31->loadFile(num, _scriptCurPtr, size);
break;
default:
break;
}
if (p) {
_scriptCurPtr += *size;
_memList[num].bufPtr = p;
_memList[num].status = STATUS_LOADED;
}
return p;
}
const char *Resource::getString(int num) {
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
return _nth->getString(_lang, num);
case DT_WIN31:
return _win31->getString(num);
default:
break;
}
return nullptr;
}
const char *Resource::getMusicPath(int num, char *buf, int bufSize, uint32 *offset) {
const char *name = nullptr;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
name = _nth->getMusicName(num);
break;
case DT_WIN31:
name = _win31->getMusicName(num);
break;
case DT_3DO:
assert(offset);
name = _3do->getMusicName(num, offset);
break;
default:
break;
}
if (name) {
snprintf(buf, bufSize, "%s", name);
return buf;
}
return nullptr;
}
const uint8 Resource::MEMLIST_PARTS[][4] = {
{ 0x14, 0x15, 0x16, 0x00 }, // 16000 - protection screens
{ 0x17, 0x18, 0x19, 0x00 }, // 16001 - introduction
{ 0x1A, 0x1B, 0x1C, 0x11 }, // 16002 - water
{ 0x1D, 0x1E, 0x1F, 0x11 }, // 16003 - jail
{ 0x20, 0x21, 0x22, 0x11 }, // 16004 - 'cite'
{ 0x23, 0x24, 0x25, 0x00 }, // 16005 - 'arene'
{ 0x26, 0x27, 0x28, 0x11 }, // 16006 - 'luxe'
{ 0x29, 0x2A, 0x2B, 0x11 }, // 16007 - 'final'
{ 0x7D, 0x7E, 0x7F, 0x00 }, // 16008 - password screen
{ 0x7D, 0x7E, 0x7F, 0x00 } // 16009 - password screen
};
void Resource::setupPart(int ptrId) {
int firstPart = kPartCopyProtection;
switch (_dataType) {
case DT_15TH_EDITION:
case DT_20TH_EDITION:
case DT_3DO:
firstPart = kPartIntro;
/* fall-through */
case DT_WIN31:
if (ptrId >= firstPart && ptrId <= 16009) {
invalidateAll();
uint8 **segments[4] = { &_segVideoPal, &_segCode, &_segVideo1, &_segVideo2 };
for (int i = 0; i < 4; ++i) {
const int num = MEMLIST_PARTS[ptrId - 16000][i];
if (num != 0) {
*segments[i] = loadDat(num);
}
}
_currentPart = ptrId;
} else {
error("Resource::setupPart() ec=0x%X invalid part", 0xF07);
}
_scriptBakPtr = _scriptCurPtr;
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS:
if (ptrId != _currentPart) {
uint8 ipal = 0;
uint8 icod = 0;
uint8 ivd1 = 0;
uint8 ivd2 = 0;
if (ptrId >= 16000 && ptrId <= 16009) {
const uint16 part = ptrId - 16000;
ipal = MEMLIST_PARTS[part][0];
icod = MEMLIST_PARTS[part][1];
ivd1 = MEMLIST_PARTS[part][2];
ivd2 = MEMLIST_PARTS[part][3];
} else {
error("Resource::setupPart() ec=0x%X invalid part", 0xF07);
}
invalidateAll();
_memList[ipal].status = STATUS_TOLOAD;
_memList[icod].status = STATUS_TOLOAD;
_memList[ivd1].status = STATUS_TOLOAD;
if (ivd2 != 0) {
_memList[ivd2].status = STATUS_TOLOAD;
}
load();
_segVideoPal = _memList[ipal].bufPtr;
_segCode = _memList[icod].bufPtr;
_segVideo1 = _memList[ivd1].bufPtr;
if (ivd2 != 0) {
_segVideo2 = _memList[ivd2].bufPtr;
}
_currentPart = ptrId;
}
_scriptBakPtr = _scriptCurPtr;
break;
}
}
void Resource::allocMemBlock() {
_memPtrStart = (uint8 *)malloc(MEM_BLOCK_SIZE);
if (!_memPtrStart)
error("allocMemBlock - Error allocating memory");
_scriptBakPtr = _scriptCurPtr = _memPtrStart;
_vidCurPtr = _memPtrStart + MEM_BLOCK_SIZE - (320 * 200 / 2); // 4bpp bitmap
_useSegVideo2 = false;
}
void Resource::freeMemBlock() {
free(_memPtrStart);
_memPtrStart = nullptr;
}
void Resource::readDemo3Joy() {
static const char *filename = "demo3.joy";
Common::File f;
if (f.open(filename)) {
const uint32 fileSize = f.size();
_demo3Joy.bufPtr = (uint8 *)malloc(fileSize);
if (_demo3Joy.bufPtr) {
_demo3Joy.bufSize = f.read(_demo3Joy.bufPtr, fileSize);
_demo3Joy.bufPos = -1;
}
} else {
warning("Unable to open '%s'", filename);
}
}
void MemEntry::load(Common::SeekableReadStream *src) {
status = src->readByte();
type = src->readByte();
bufPtr = nullptr; (void)src->readUint32BE();
rankNum = src->readByte();
bankNum = src->readByte();
bankPos = src->readUint32BE();
packedSize = src->readUint32BE();
unpackedSize = src->readUint32BE();
}
} // namespace Awe