scummvm/engines/cryo/resource.cpp
2019-11-09 01:08:14 +02:00

556 lines
16 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 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryo/defs.h"
#include "cryo/cryo.h"
#include "cryo/platdefs.h"
#include "cryo/cryolib.h"
#include "cryo/eden.h"
#include "cryo/sound.h"
namespace Cryo {
#define CRYO_DAT_VER 1 // 32-bit integer
// Original name: prechargephrases
void EdenGame::preloadDialogs(int16 vid) {
perso_t *perso = &_persons[PER_MORKUS];
if (vid == 170)
perso = &_persons[PER_UNKN_156];
_globals->_characterPtr = perso;
_globals->_dialogType = DialogType::dtInspect;
int num = (perso->_id << 3) | _globals->_dialogType;
Dialog *dial = (Dialog *)getElem(_gameDialogs, num);
dialoscansvmas(dial);
}
////// datfile.c
void EdenGame::verifh(byte *ptr) {
byte sum = 0;
byte *head = ptr;
for (int8 i = 0; i < 6; i++)
sum += *head++;
if (sum != 0xAB)
return;
debug("* Begin unpacking resource");
head -= 6;
uint16 h0 = READ_LE_UINT16(head);
// 3 = 2 bytes for the uint16 and 1 byte for an unused char
head += 3;
uint16 h3 = READ_LE_UINT16(head);
head += 2;
byte *data = h0 + head + 26;
h3 -= 6;
head += h3;
for (; h3; h3--)
*data-- = *head--;
head = data + 1;
data = ptr;
expandHSQ(head, data);
}
void EdenGame::openbigfile() {
_bigfile.open("EDEN.DAT");
char buf[16];
int count = _bigfile.readUint16LE();
_bigfileHeader = new PakHeaderNode(count);
for (int j = 0; j < count; j++) {
for (int k = 0; k < 16; k++)
buf[k] = _bigfile.readByte();
_bigfileHeader->_files[j]._name = Common::String(buf);
_bigfileHeader->_files[j]._size = _bigfile.readUint32LE();
_bigfileHeader->_files[j]._offs = _bigfile.readUint32LE();
_bigfileHeader->_files[j]._flag = _bigfile.readByte();
}
_vm->_video->resetInternals();
_vm->_video->setFile(&_bigfile);
}
void EdenGame::closebigfile() {
_bigfile.close();
}
int EdenGame::loadmusicfile(int16 num) {
PakHeaderItem *file = &_bigfileHeader->_files[num + 435];
int32 size = file->_size;
int32 offs = file->_offs;
_bigfile.seek(offs, SEEK_SET);
uint32 numread = size;
if (numread > kMaxMusicSize)
error("Music file %s is too big", file->_name.c_str());
_bigfile.read(_musicBuf, numread);
return size;
}
void EdenGame::loadRawFile(uint16 num, byte *buffer) {
if (_vm->getPlatform() == Common::kPlatformDOS) {
if ((_vm->isDemo() && num > 2204) || num > 2472)
error("Trying to read invalid game resource");
}
assert(num < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[num];
int32 size = file->_size;
int32 offs = file->_offs;
_bigfile.seek(offs, SEEK_SET);
_bigfile.read(buffer, size);
}
void EdenGame::loadIconFile(uint16 num, Icon *buffer) {
if (_vm->getPlatform() == Common::kPlatformDOS) {
if ((_vm->isDemo() && num > 2204) || num > 2472)
error("Trying to read invalid game resource");
}
assert(num < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[num];
int32 size = file->_size;
int32 offs = file->_offs;
debug("* Loading icon - Resource %d (%s) at 0x%X, %d bytes", num, file->_name.c_str(), offs, size);
_bigfile.seek(offs, SEEK_SET);
int count = size / 18; // sizeof(Icon)
for (int i = 0; i < count; i++) {
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
buffer[i].sx = _bigfile.readSint16BE();
buffer[i].sy = _bigfile.readSint16BE();
buffer[i].ex = _bigfile.readSint16BE();
buffer[i].ey = _bigfile.readSint16BE();
buffer[i]._cursorId = _bigfile.readUint16BE();
buffer[i]._actionId = _bigfile.readUint32BE();
buffer[i]._objectId = _bigfile.readUint32BE();
}
else {
buffer[i].sx = _bigfile.readSint16LE();
buffer[i].sy = _bigfile.readSint16LE();
buffer[i].ex = _bigfile.readSint16LE();
buffer[i].ey = _bigfile.readSint16LE();
buffer[i]._cursorId = _bigfile.readUint16LE();
buffer[i]._actionId = _bigfile.readUint32LE();
buffer[i]._objectId = _bigfile.readUint32LE();
}
}
}
void EdenGame::loadRoomFile(uint16 num, Room *buffer) {
if (_vm->getPlatform() == Common::kPlatformDOS) {
if ((_vm->isDemo() && num > 2204) || num > 2472)
error("Trying to read invalid game resource");
}
assert(num < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[num];
int32 size = file->_size;
int32 offs = file->_offs;
debug("* Loading room - Resource %d (%s) at 0x%X, %d bytes", num, file->_name.c_str(), offs, size);
_bigfile.seek(offs, SEEK_SET);
int count = size / 14; // sizeof(Room)
for (int i = 0; i < count; i++) {
buffer[i]._id = _bigfile.readByte();
for (int j = 0; j < 4; j++)
buffer[i]._exits[j] = _bigfile.readByte();
buffer[i]._flags = _bigfile.readByte();
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
buffer[i]._bank = _bigfile.readUint16BE();
buffer[i]._party = _bigfile.readUint16BE();
}
else {
buffer[i]._bank = _bigfile.readUint16LE();
buffer[i]._party = _bigfile.readUint16LE();
}
buffer[i]._level = _bigfile.readByte();
buffer[i]._video = _bigfile.readByte();
buffer[i]._location = _bigfile.readByte();
buffer[i]._backgroundBankNum = _bigfile.readByte();
}
}
// Original name: shnmfl
void EdenGame::loadHnm(uint16 num) {
unsigned int resNum = num - 1 + 485;
assert(resNum < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[resNum];
int size = file->_size;
int offs = file->_offs;
debug("* Loading movie %d (%s) at 0x%X, %d bytes", num, file->_name.c_str(), (uint)offs, size);
_vm->_video->_file->seek(offs, SEEK_SET);
}
// Original name: ssndfl
int EdenGame::loadSound(uint16 num) {
unsigned int resNum = num - 1 + ((_vm->getPlatform() == Common::kPlatformDOS && _vm->isDemo()) ? 656 : 661);
assert(resNum < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[resNum];
int32 size = file->_size;
int32 offs = file->_offs;
debug("* Loading sound %d (%s) at 0x%X, %d bytes", num, file->_name.c_str(), (uint)offs, size);
if (_soundAllocated) {
free(_voiceSamplesBuffer);
_voiceSamplesBuffer = nullptr;
_soundAllocated = false; //TODO: bug??? no alloc
}
else {
_voiceSamplesBuffer = (byte *)malloc(size);
_soundAllocated = true;
}
_bigfile.seek(offs, SEEK_SET);
//For PC loaded data is a VOC file, on Mac version this is a raw samples
if (_vm->getPlatform() == Common::kPlatformMacintosh)
_bigfile.read(_voiceSamplesBuffer, size);
else {
// VOC files also include extra information for lipsync
// 1. Standard VOC header
_bigfile.read(_voiceSamplesBuffer, 0x1A);
// 2. Lipsync?
unsigned char chunkType = _bigfile.readByte();
uint32 val = 0;
_bigfile.read(&val, 3);
unsigned int chunkLen = FROM_LE_32(val);
if (chunkType == 5) {
_bigfile.read(_gameLipsync + 7260, chunkLen);
chunkType = _bigfile.readByte();
_bigfile.read(&val, 3);
chunkLen = FROM_LE_32(val);
}
// 3. Normal sound data
if (chunkType == 1) {
_bigfile.readUint16LE();
size = chunkLen - 2;
_bigfile.read(_voiceSamplesBuffer, size);
}
}
return size;
}
void EdenGame::convertMacToPC() {
// Convert all mac (big-endian) resources to native format
// Array of longs
int *p = (int *)_gameLipsync;
for (int i = 0; i < 7240 / 4; i++)
p[i] = FROM_BE_32(p[i]);
}
void EdenGame::loadpermfiles() {
Common::File f;
const int kNumIcons = 136;
const int kNumRooms = 424;
const int kNumFollowers = 15;
const int kNumLabyrinthPath = 70;
const int kNumDinoSpeedForCitaLevel = 16;
const int kNumTabletView = 12;
const int kNumPersoRoomBankTable = 84;
const int kNumGotos = 130;
const int kNumObjects = 42;
const int kNumObjectLocations = 45;
const int kNumPersons = 58;
const int kNumCitadel = 7;
const int kNumCharacterRects = 19;
const int kNumCharacters = 20;
const int kNumAreas = 12;
// tab_2CEF0
// tab_2CF70
const int kNumActionCursors = 299;
const int expectedDataSize =
kNumIcons * 18 + // sizeof(Icon)
kNumRooms * 14 + // sizeof(Room)
kNumFollowers * 16 + // sizeof(Follower)
kNumLabyrinthPath +
kNumDinoSpeedForCitaLevel +
kNumTabletView +
kNumPersoRoomBankTable +
kNumGotos * 5 + // sizeof(Goto)
kNumObjects * 12 + // sizeof(object_t)
kNumObjectLocations * 2 +
kNumPersons * 18 + // sizeof(perso_t)
kNumCitadel * 34 + // sizeof(Citadel)
kNumCharacterRects * 8 +
kNumCharacters * 5 +
kNumAreas * 10 + // (sizeof(Area) - 4)
64 * 2 +
64 * 2 +
kNumActionCursors +
12 +
3 * 6 * 2 * 3 * 2;
if (f.open("cryo.dat")) {
const int dataSize = f.size() - 8 - 4; // CRYODATA + version
char headerId[9];
f.read(headerId, 8);
headerId[8] = '\0';
if (strcmp(headerId, "CRYODATA"))
error("Invalid cryo.dat aux data file");
if (f.readUint32LE() != CRYO_DAT_VER)
error("Incorrect data version for cryo.dat");
if (dataSize != expectedDataSize)
error("Mismatching data in cryo.dat aux data file (got %d, expected %d)", dataSize, expectedDataSize);
}
else
error("Can not load cryo.dat");
switch (_vm->getPlatform()) {
case Common::kPlatformDOS:
// Since PC version stores hotspots and rooms info in the executable, load them from premade resource file
for (int i = 0; i < kNumIcons; i++) {
_gameIcons[i].sx = f.readSint16LE();
_gameIcons[i].sy = f.readSint16LE();
_gameIcons[i].ex = f.readSint16LE();
_gameIcons[i].ey = f.readSint16LE();
_gameIcons[i]._cursorId = f.readUint16LE();
_gameIcons[i]._actionId = f.readUint32LE();
_gameIcons[i]._objectId = f.readUint32LE();
}
for (int i = 0; i < kNumRooms; i++) {
_gameRooms[i]._id = f.readByte();
for (int j = 0; j < 4; j++)
_gameRooms[i]._exits[j] = f.readByte();
_gameRooms[i]._flags = f.readByte();
_gameRooms[i]._bank = f.readUint16LE();
_gameRooms[i]._party = f.readUint16LE();
_gameRooms[i]._level = f.readByte();
_gameRooms[i]._video = f.readByte();
_gameRooms[i]._location = f.readByte();
_gameRooms[i]._backgroundBankNum = f.readByte();
}
break;
case Common::kPlatformMacintosh:
loadIconFile(2498, _gameIcons);
loadRoomFile(2497, _gameRooms);
loadRawFile(2486, _gameLipsync);
convertMacToPC();
// Skip the icons and rooms of the DOS version
f.skip(kNumIcons * 14 + kNumRooms * 11);
break;
default:
error("Unsupported platform");
}
// Read the common static data
for (int i = 0; i < kNumFollowers; i++) {
_followerList[i]._id = f.readSByte();
_followerList[i]._spriteNum = f.readSByte();
_followerList[i].sx = f.readSint16LE();
_followerList[i].sy = f.readSint16LE();
_followerList[i].ex = f.readSint16LE();
_followerList[i].ey = f.readSint16LE();
_followerList[i]._spriteBank = f.readSint16LE();
_followerList[i].ff_C = f.readSint16LE();
_followerList[i].ff_E = f.readSint16LE();
}
f.read(_labyrinthPath, kNumLabyrinthPath);
f.read(_dinoSpeedForCitadelLevel, kNumDinoSpeedForCitaLevel);
f.read(_tabletView, kNumTabletView);
f.read(_personRoomBankTable, kNumPersoRoomBankTable);
f.read(_gotos, kNumGotos * 5); // sizeof(Goto)
for (int i = 0; i < kNumObjects; i++) {
_objects[i]._id = f.readByte();
_objects[i]._flags = f.readByte();
_objects[i]._locations = f.readUint32LE();
_objects[i]._itemMask = f.readUint16LE();
_objects[i]._powerMask = f.readUint16LE();
_objects[i]._count = f.readSint16LE();
}
for (int i = 0; i < kNumObjectLocations; i++) {
_objectLocations[i] = f.readUint16LE();
}
for (int i = 0; i < kNumPersons; i++) {
_persons[i]._roomNum = f.readUint16LE();
_persons[i]._actionId = f.readUint16LE();
_persons[i]._partyMask = f.readUint16LE();
_persons[i]._id = f.readByte();
_persons[i]._flags = f.readByte();
_persons[i]._roomBankId = f.readByte();
_persons[i]._spriteBank = f.readByte();
_persons[i]._items = f.readUint16LE();
_persons[i]._powers = f.readUint16LE();
_persons[i]._targetLoc = f.readByte();
_persons[i]._lastLoc = f.readByte();
_persons[i]._speed = f.readByte();
_persons[i]._steps = f.readByte();
}
for (int i = 0; i < kNumCitadel; i++) {
_citadelList[i]._id = f.readSint16LE();
for (int j = 0; j < 8; j++)
_citadelList[i]._bank[j] = f.readSint16LE();
for (int j = 0; j < 8; j++)
_citadelList[i]._video[j] = f.readSint16LE();
}
for (int i = 0; i < kNumCharacterRects; i++) {
_characterRects[i].left = f.readSint16LE();
_characterRects[i].top = f.readSint16LE();
_characterRects[i].right = f.readSint16LE();
_characterRects[i].bottom = f.readSint16LE();
}
f.read(_characterArray, kNumCharacters * 5);
for (int i = 0; i < kNumAreas; i++) {
_areasTable[i]._num = f.readByte();
_areasTable[i]._type = f.readByte();
_areasTable[i]._flags = f.readUint16LE();
_areasTable[i]._firstRoomIdx = f.readUint16LE();
_areasTable[i]._citadelLevel = f.readByte();
_areasTable[i]._placeNum = f.readByte();
_areasTable[i]._citadelRoomPtr = nullptr;
_areasTable[i]._visitCount = f.readSint16LE();
}
for (int i = 0; i < 64; i++) {
tab_2CEF0[i] = f.readSint16LE();
}
for (int i = 0; i < 64; i++) {
tab_2CF70[i] = f.readSint16LE();
}
f.read(_actionCursors, kNumActionCursors);
f.read(_mapMode, 12);
f.read(_cubeTextureCoords, 3 * 6 * 2 * 3 * 2);
f.close();
loadRawFile(0, _mainBankBuf);
loadRawFile(402, _gameFont);
loadRawFile(404, _gameDialogs);
loadRawFile(403, _gameConditions);
}
bool EdenGame::ReadDataSyncVOC(unsigned int num) {
unsigned int resNum = num - 1 + ((_vm->getPlatform() == Common::kPlatformDOS && _vm->isDemo()) ? 656 : 661);
unsigned char vocHeader[0x1A];
int filePos = 0;
loadpartoffile(resNum, vocHeader, filePos, sizeof(vocHeader));
filePos += sizeof(vocHeader);
unsigned char chunkType = 0;
loadpartoffile(resNum, &chunkType, sizeof(vocHeader), 1);
filePos++;
if (chunkType == 5) {
uint32 chunkLen = 0;
loadpartoffile(resNum, &chunkLen, filePos, 3);
filePos += 3;
chunkLen = FROM_LE_32(chunkLen);
loadpartoffile(resNum, _gameLipsync + 7260, filePos, chunkLen);
return true;
}
return false;
}
bool EdenGame::ReadDataSync(uint16 num) {
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
long pos = READ_LE_UINT32(_gameLipsync + num * 4);
if (pos != -1) {
long len = 1024;
loadpartoffile(1936, _gameLipsync + 7260, pos, len);
return true;
}
}
else
return ReadDataSyncVOC(num + 1); //TODO: remove -1 in caller
return false;
}
void EdenGame::loadpartoffile(uint16 num, void *buffer, int32 pos, int32 len) {
assert(num < _bigfileHeader->_count);
PakHeaderItem *file = &_bigfileHeader->_files[num];
int32 offs = READ_LE_UINT32(&file->_offs);
debug("* Loading partial resource %d (%s) at 0x%X(+0x%X), %d bytes", num, file->_name.c_str(), offs, pos, len);
_bigfile.seek(offs + pos, SEEK_SET);
_bigfile.read(buffer, len);
}
void EdenGame::expandHSQ(byte *input, byte *output) {
byte *src = input;
byte *dst = output;
byte *ptr;
uint16 bit; // bit
uint16 queue = 0; // queue
uint16 len = 0;
int16 ofs;
#define GetBit \
bit = queue & 1; \
queue >>= 1; \
if (!queue) { \
queue = (src[1] << 8) | src[0]; src += 2; \
bit = queue & 1; \
queue = (queue >> 1) | 0x8000; \
}
for (;;) {
GetBit;
if (bit)
*dst++ = *src++;
else {
len = 0;
GetBit;
if (!bit) {
GetBit;
len = (len << 1) | bit;
GetBit;
len = (len << 1) | bit;
ofs = 0xFF00 | *src++; //TODO: -256
}
else {
ofs = (src[1] << 8) | src[0];
src += 2;
len = ofs & 7;
ofs = (ofs >> 3) | 0xE000;
if (!len) {
len = *src++;
if (!len)
break;
}
}
ptr = dst + ofs;
len += 2;
while (len--)
*dst++ = *ptr++;
}
}
}
} // namespace Cryo