/* 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. * */ // Resource file routines for Simon1/Simon2 #include "common/archive.h" #include "common/file.h" #include "common/memstream.h" #include "common/textconsole.h" #include "common/util.h" #include "agos/agos.h" #include "agos/intern.h" #include "common/zlib.h" namespace AGOS { #ifdef ENABLE_AGOS2 uint16 AGOSEngine_Feeble::to16Wrapper(uint value) { return TO_LE_16(value); } uint16 AGOSEngine_Feeble::readUint16Wrapper(const void *src) { return READ_LE_UINT16(src); } uint32 AGOSEngine_Feeble::readUint32Wrapper(const void *src) { return READ_LE_UINT32(src); } #endif uint16 AGOSEngine::to16Wrapper(uint value) { return TO_BE_16(value); } uint16 AGOSEngine::readUint16Wrapper(const void *src) { return READ_BE_UINT16(src); } uint32 AGOSEngine::readUint32Wrapper(const void *src) { return READ_BE_UINT32(src); } void AGOSEngine::decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize) { #ifdef USE_ZLIB Common::File in; in.open(srcName); if (in.isOpen() == false) error("decompressData: Can't load %s", srcName); in.seek(offset, SEEK_SET); if (srcSize != dstSize) { byte *srcBuffer = (byte *)malloc(srcSize); if (in.read(srcBuffer, srcSize) != srcSize) error("decompressData: Read failed"); unsigned long decompressedSize = dstSize; if (!Common::uncompress(dst, &decompressedSize, srcBuffer, srcSize)) error("decompressData: Zlib uncompress error"); free(srcBuffer); } else { if (in.read(dst, dstSize) != dstSize) error("decompressData: Read failed"); } in.close(); #else error("Zlib support is required for Amiga and Macintosh versions"); #endif } void AGOSEngine::loadOffsets(const char *filename, int number, uint32 &file, uint32 &offset, uint32 &srcSize, uint32 &dstSize) { Common::File in; int offsSize = (getPlatform() == Common::kPlatformAmiga) ? 16 : 12; /* read offsets from index */ in.open(filename); if (in.isOpen() == false) { error("loadOffsets: Can't load index file '%s'", filename); } in.seek(number * offsSize, SEEK_SET); offset = in.readUint32LE(); dstSize = in.readUint32LE(); srcSize = in.readUint32LE(); file = in.readUint32LE(); in.close(); } int AGOSEngine::allocGamePcVars(Common::SeekableReadStream *in) { uint32 itemArraySize, itemArrayInited, stringTableNum; uint32 version; uint32 i; itemArraySize = in->readUint32BE(); version = in->readUint32BE(); itemArrayInited = in->readUint32BE(); stringTableNum = in->readUint32BE(); // First two items are predefined if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) { itemArraySize += 2; itemArrayInited = itemArraySize; } else { itemArrayInited += 2; itemArraySize += 2; } if (version != 0x80) error("allocGamePcVars: Not a runtime database"); _itemArrayPtr = (Item **)calloc(itemArraySize, sizeof(Item *)); if (_itemArrayPtr == NULL) error("allocGamePcVars: Out of memory for Item array"); _itemArraySize = itemArraySize; _itemArrayInited = itemArrayInited; for (i = 1; i < itemArrayInited; i++) { _itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item)); } // The rest is cleared automatically by calloc allocateStringTable(stringTableNum + 10); _stringTabNum = stringTableNum; return itemArrayInited; } void AGOSEngine_PN::loadGamePcFile() { if (getFileName(GAME_BASEFILE) != NULL) { Common::File in; // Read dataBase if (!in.open(getFileName(GAME_BASEFILE))) { error("loadGamePcFile: Can't load database file '%s'", getFileName(GAME_BASEFILE)); } _dataBaseSize = in.size(); _dataBase = (byte *)malloc(_dataBaseSize); if (_dataBase == NULL) error("loadGamePcFile: Out of memory for dataBase"); in.read(_dataBase, _dataBaseSize); if (_dataBase[31] != 0) error("Later version of system requested"); } if (getFileName(GAME_TEXTFILE) != NULL) { Common::File in; // Read textBase if (!in.open(getFileName(GAME_TEXTFILE))) { error("loadGamePcFile: Can't load textbase file '%s'", getFileName(GAME_TEXTFILE)); } _textBaseSize = in.size(); _textBase = (byte *)malloc(_textBaseSize); if (_textBase == NULL) error("loadGamePcFile: Out of memory for textBase"); in.read(_textBase, _textBaseSize); if (_textBase[getlong(30L)] != 128) error("Unknown compression format"); } } void AGOSEngine::loadGamePcFile() { int fileSize; if (getFileName(GAME_BASEFILE) != NULL) { /* Read main gamexx file */ Common::File in; if (!in.open(getFileName(GAME_BASEFILE))) { error("loadGamePcFile: Can't load gamexx file '%s'", getFileName(GAME_BASEFILE)); } if (getFeatures() & GF_CRUNCHED_GAMEPC) { uint srcSize = in.size(); byte *srcBuf = (byte *)malloc(srcSize); in.read(srcBuf, srcSize); uint dstSize = READ_BE_UINT32(srcBuf + srcSize - 4); byte *dstBuf = (byte *)malloc(dstSize); decrunchFile(srcBuf, dstBuf, srcSize); free(srcBuf); Common::MemoryReadStream stream(dstBuf, dstSize); readGamePcFile(&stream); free(dstBuf); } else { readGamePcFile(&in); } } if (getFileName(GAME_TBLFILE) != NULL) { /* Read list of TABLE resources */ Common::File in; if (!in.open(getFileName(GAME_TBLFILE))) { error("loadGamePcFile: Can't load table resources file '%s'", getFileName(GAME_TBLFILE)); } fileSize = in.size(); _tblList = (byte *)malloc(fileSize); if (_tblList == NULL) error("loadGamePcFile: Out of memory for strip table list"); in.read(_tblList, fileSize); /* Remember the current state */ _subroutineListOrg = _subroutineList; _tablesHeapPtrOrg = _tablesHeapPtr; _tablesHeapCurPosOrg = _tablesHeapCurPos; } if (getFileName(GAME_STRFILE) != NULL) { /* Read list of TEXT resources */ Common::File in; if (!in.open(getFileName(GAME_STRFILE))) error("loadGamePcFile: Can't load text resources file '%s'", getFileName(GAME_STRFILE)); fileSize = in.size(); _strippedTxtMem = (byte *)malloc(fileSize); if (_strippedTxtMem == NULL) error("loadGamePcFile: Out of memory for strip text list"); in.read(_strippedTxtMem, fileSize); } if (getFileName(GAME_STATFILE) != NULL) { /* Read list of ROOM STATE resources */ Common::File in; if (!in.open(getFileName(GAME_STATFILE))) { error("loadGamePcFile: Can't load state resources file '%s'", getFileName(GAME_STATFILE)); } _numRoomStates = in.size() / 8; _roomStates = (RoomState *)calloc(_numRoomStates, sizeof(RoomState)); if (_roomStates == NULL) error("loadGamePcFile: Out of memory for room state list"); for (uint s = 0; s < _numRoomStates; s++) { uint16 num = in.readUint16BE() - (_itemArrayInited - 2); _roomStates[num].state = in.readUint16BE(); _roomStates[num].classFlags = in.readUint16BE(); _roomStates[num].roomExitStates = in.readUint16BE(); } } if (getFileName(GAME_RMSLFILE) != NULL) { /* Read list of ROOM ITEMS resources */ Common::File in; if (!in.open(getFileName(GAME_RMSLFILE))) { error("loadGamePcFile: Can't load room resources file '%s'", getFileName(GAME_RMSLFILE)); } fileSize = in.size(); _roomsList = (byte *)malloc(fileSize); if (_roomsList == NULL) error("loadGamePcFile: Out of memory for room items list"); in.read(_roomsList, fileSize); } if (getFileName(GAME_XTBLFILE) != NULL) { /* Read list of XTABLE resources */ Common::File in; if (!in.open(getFileName(GAME_XTBLFILE))) { error("loadGamePcFile: Can't load xtable resources file '%s'", getFileName(GAME_XTBLFILE)); } fileSize = in.size(); _xtblList = (byte *)malloc(fileSize); if (_xtblList == NULL) error("loadGamePcFile: Out of memory for strip xtable list"); in.read(_xtblList, fileSize); /* Remember the current state */ _xsubroutineListOrg = _subroutineList; _xtablesHeapPtrOrg = _tablesHeapPtr; _xtablesHeapCurPosOrg = _tablesHeapCurPos; } } void AGOSEngine::readGamePcFile(Common::SeekableReadStream *in) { int num_inited_objects; int i; num_inited_objects = allocGamePcVars(in); createPlayer(); readGamePcText(in); for (i = 2; i < num_inited_objects; i++) { readItemFromGamePc(in, _itemArrayPtr[i]); } readSubroutineBlock(in); } void AGOSEngine::readGamePcText(Common::SeekableReadStream *in) { _textSize = in->readUint32BE(); _textMem = (byte *)malloc(_textSize); if (_textMem == NULL) error("readGamePcText: Out of text memory"); in->read(_textMem, _textSize); setupStringTable(_textMem, _stringTabNum); } void AGOSEngine::readItemFromGamePc(Common::SeekableReadStream *in, Item *item) { uint32 type; if (getGameType() == GType_ELVIRA1) { item->itemName = (uint16)in->readUint32BE(); item->adjective = in->readUint16BE(); item->noun = in->readUint16BE(); item->state = in->readUint16BE(); in->readUint16BE(); item->next = (uint16)fileReadItemID(in); item->child = (uint16)fileReadItemID(in); item->parent = (uint16)fileReadItemID(in); in->readUint16BE(); in->readUint16BE(); in->readUint16BE(); item->classFlags = in->readUint16BE(); item->children = NULL; } else if (getGameType() == GType_ELVIRA2) { item->itemName = (uint16)in->readUint32BE(); item->adjective = in->readUint16BE(); item->noun = in->readUint16BE(); item->state = in->readUint16BE(); item->next = (uint16)fileReadItemID(in); item->child = (uint16)fileReadItemID(in); item->parent = (uint16)fileReadItemID(in); in->readUint16BE(); item->classFlags = in->readUint16BE(); item->children = NULL; } else { item->adjective = in->readUint16BE(); item->noun = in->readUint16BE(); item->state = in->readUint16BE(); item->next = (uint16)fileReadItemID(in); item->child = (uint16)fileReadItemID(in); item->parent = (uint16)fileReadItemID(in); in->readUint16BE(); item->classFlags = in->readUint16BE(); item->children = NULL; } type = in->readUint32BE(); while (type) { type = in->readUint16BE(); if (type != 0) readItemChildren(in, item, type); } } void AGOSEngine::readItemChildren(Common::SeekableReadStream *in, Item *item, uint type) { if (type == kRoomType) { SubRoom *subRoom = (SubRoom *)allocateChildBlock(item, kRoomType, sizeof(SubRoom)); subRoom->roomShort = in->readUint32BE(); subRoom->roomLong = in->readUint32BE(); subRoom->flags = in->readUint16BE(); } else if (type == kObjectType) { SubObject *subObject = (SubObject *)allocateChildBlock(item, kObjectType, sizeof(SubObject)); in->readUint32BE(); in->readUint32BE(); in->readUint32BE(); subObject->objectName = in->readUint32BE(); subObject->objectSize = in->readUint16BE(); subObject->objectWeight = in->readUint16BE(); subObject->objectFlags = in->readUint16BE(); } else if (type == kGenExitType) { SubGenExit *genExit = (SubGenExit *)allocateChildBlock(item, kGenExitType, sizeof(SubGenExit)); genExit->dest[0] = (uint16)fileReadItemID(in); genExit->dest[1] = (uint16)fileReadItemID(in); genExit->dest[2] = (uint16)fileReadItemID(in); genExit->dest[3] = (uint16)fileReadItemID(in); genExit->dest[4] = (uint16)fileReadItemID(in); genExit->dest[5] = (uint16)fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); } else if (type == kContainerType) { SubContainer *container = (SubContainer *)allocateChildBlock(item, kContainerType, sizeof(SubContainer)); container->volume = in->readUint16BE(); container->flags = in->readUint16BE(); } else if (type == kChainType) { SubChain *chain = (SubChain *)allocateChildBlock(item, kChainType, sizeof(SubChain)); chain->chChained = (uint16)fileReadItemID(in); } else if (type == kUserFlagType) { setUserFlag(item, 0, in->readUint16BE()); setUserFlag(item, 1, in->readUint16BE()); setUserFlag(item, 2, in->readUint16BE()); setUserFlag(item, 3, in->readUint16BE()); setUserFlag(item, 4, in->readUint16BE()); setUserFlag(item, 5, in->readUint16BE()); setUserFlag(item, 6, in->readUint16BE()); setUserFlag(item, 7, in->readUint16BE()); SubUserFlag *subUserFlag = (SubUserFlag *)findChildOfType(item, kUserFlagType); subUserFlag->userItems[0] = (uint16)fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); fileReadItemID(in); } else if (type == kInheritType) { SubInherit *inherit = (SubInherit *)allocateChildBlock(item, kInheritType, sizeof(SubInherit)); inherit->inMaster = (uint16)fileReadItemID(in); } else { error("readItemChildren: invalid type %d", type); } } void AGOSEngine_Elvira2::readItemChildren(Common::SeekableReadStream *in, Item *item, uint type) { if (type == kRoomType) { uint fr1 = in->readUint16BE(); uint fr2 = in->readUint16BE(); uint i, size; uint j, k; SubRoom *subRoom; size = SubRoom_SIZE; for (i = 0, j = fr2; i != 6; i++, j >>= 2) if (j & 3) size += sizeof(subRoom->roomExit[0]); subRoom = (SubRoom *)allocateChildBlock(item, kRoomType, size); subRoom->subroutine_id = fr1; subRoom->roomExitStates = fr2; for (i = k = 0, j = fr2; i != 6; i++, j >>= 2) if (j & 3) subRoom->roomExit[k++] = (uint16)fileReadItemID(in); } else if (type == kObjectType) { uint32 fr = in->readUint32BE(); uint i, k, size; SubObject *subObject; size = SubObject_SIZE; for (i = 0; i != 16; i++) if (fr & (1 << i)) size += sizeof(subObject->objectFlagValue[0]); subObject = (SubObject *)allocateChildBlock(item, kObjectType, size); subObject->objectFlags = fr; k = 0; if (fr & 1) { subObject->objectFlagValue[k++] = (uint16)in->readUint32BE(); } for (i = 1; i != 16; i++) if (fr & (1 << i)) subObject->objectFlagValue[k++] = in->readUint16BE(); if (getGameType() != GType_ELVIRA2) subObject->objectName = (uint16)in->readUint32BE(); } else if (type == kSuperRoomType) { assert(getGameType() == GType_ELVIRA2); uint i, j, k, size; uint id, x, y, z; SubSuperRoom *subSuperRoom; id = in->readUint16BE(); x = in->readUint16BE(); y = in->readUint16BE(); z = in->readUint16BE(); j = x * y * z; size = SubSuperRoom_SIZE; for (i = 0; i != j; i++) size += sizeof(subSuperRoom->roomExitStates[0]); subSuperRoom = (SubSuperRoom *)allocateChildBlock(item, kSuperRoomType, size); subSuperRoom->subroutine_id = id; subSuperRoom->roomX = x; subSuperRoom->roomY = y; subSuperRoom->roomZ = z; for (i = k = 0; i != j; i++) subSuperRoom->roomExitStates[k++] = in->readUint16BE(); } else if (type == kContainerType) { SubContainer *container = (SubContainer *)allocateChildBlock(item, kContainerType, sizeof(SubContainer)); container->volume = in->readUint16BE(); container->flags = in->readUint16BE(); } else if (type == kChainType) { SubChain *chain = (SubChain *)allocateChildBlock(item, kChainType, sizeof(SubChain)); chain->chChained = (uint16)fileReadItemID(in); } else if (type == kUserFlagType) { setUserFlag(item, 0, in->readUint16BE()); setUserFlag(item, 1, in->readUint16BE()); setUserFlag(item, 2, in->readUint16BE()); setUserFlag(item, 3, in->readUint16BE()); } else if (type == kInheritType) { SubInherit *inherit = (SubInherit *)allocateChildBlock(item, kInheritType, sizeof(SubInherit)); inherit->inMaster = (uint16)fileReadItemID(in); } else { error("readItemChildren: invalid type %d", type); } } uint fileReadItemID(Common::SeekableReadStream *in) { uint32 val = in->readUint32BE(); if (val == 0xFFFFFFFF) return 0; return val + 2; } void AGOSEngine::openGameFile() { _gameFile = new Common::File(); _gameFile->open(getFileName(GAME_GMEFILE)); if (!_gameFile->isOpen()) error("openGameFile: Can't load game file '%s'", getFileName(GAME_GMEFILE)); uint32 size = _gameFile->readUint32LE(); _gameOffsetsPtr = (uint32 *)malloc(size); if (_gameOffsetsPtr == NULL) error("openGameFile: Out of memory, game offsets"); _gameFile->seek(0, SEEK_SET); for (uint r = 0; r < size / 4; r++) _gameOffsetsPtr[r] = _gameFile->readUint32LE(); } void AGOSEngine::readGameFile(void *dst, uint32 offs, uint32 size) { _gameFile->seek(offs, SEEK_SET); if (_gameFile->read(dst, size) != size) error("readGameFile: Read failed (%d,%d)", offs, size); } // Thanks to Stuart Caie for providing the original // C conversion upon which this decruncher is based. #define SD_GETBIT(var) do { \ if (!bits--) { \ s -= 4; \ if (s < src) \ return false; \ bb = READ_BE_UINT32(s); \ bits = 31; \ } \ (var) = bb & 1; \ bb >>= 1; \ }while (0) #define SD_GETBITS(var, nbits) do { \ bc = (nbits); \ (var) = 0; \ while (bc--) { \ (var) <<= 1; \ SD_GETBIT(bit); \ (var) |= bit; \ } \ }while (0) #define SD_TYPE_LITERAL (0) #define SD_TYPE_MATCH (1) bool AGOSEngine::decrunchFile(byte *src, byte *dst, uint32 size) { byte *s = src + size - 4; uint32 destlen = READ_BE_UINT32 (s); uint32 bb, x, y; byte *d = dst + destlen; byte bc, bit, bits, type; // Initialize bit buffer. s -= 4; bb = x = READ_BE_UINT32 (s); bits = 0; do { x >>= 1; bits++; } while (x); bits--; while (d > dst) { SD_GETBIT(x); if (x) { SD_GETBITS(x, 2); switch (x) { case 0: type = SD_TYPE_MATCH; x = 9; y = 2; break; case 1: type = SD_TYPE_MATCH; x = 10; y = 3; break; case 2: type = SD_TYPE_MATCH; x = 12; SD_GETBITS(y, 8); break; default: type = SD_TYPE_LITERAL; x = 8; y = 8; } } else { SD_GETBIT(x); if (x) { type = SD_TYPE_MATCH; x = 8; y = 1; } else { type = SD_TYPE_LITERAL; x = 3; y = 0; } } if (type == SD_TYPE_LITERAL) { SD_GETBITS(x, x); y += x; if ((int)(y + 1) > (d - dst)) return false; // Overflow? do { SD_GETBITS(x, 8); *--d = x; } while (y-- > 0); } else { if ((int)(y + 1) > (d - dst)) return false; // Overflow? SD_GETBITS(x, x); if ((d + x) > (dst + destlen)) return false; // Offset overflow? do { d--; *d = d[x]; } while (y-- > 0); } } // Successful decrunch. return true; } #undef SD_GETBIT #undef SD_GETBITS #undef SD_TYPE_LITERAL #undef SD_TYPE_MATCH static bool getBit(Common::Stack &dataList, uint32 &srcVal) { bool result = srcVal & 1; srcVal >>= 1; if (srcVal == 0) { srcVal = dataList.pop(); result = srcVal & 1; srcVal = (srcVal >> 1) | 0x80000000L; } return result; } static uint32 copyBits(Common::Stack &dataList, uint32 &srcVal, int numBits) { uint32 destVal = 0; for (int i = 0; i < numBits; ++i) { bool f = getBit(dataList, srcVal); destVal = (destVal << 1) | (f ? 1 : 0); } return destVal; } static void transferLoop(uint8 *dataOut, int &outIndex, uint32 destVal, int max) { assert(outIndex > max - 1); byte *pDest = dataOut + outIndex; for (int i = 0; (i <= max) && (outIndex > 0); ++i) { pDest = dataOut + --outIndex; *pDest = pDest[destVal]; } } void AGOSEngine::decompressPN(Common::Stack &dataList, uint8 *&dataOut, int &dataOutSize) { // Set up the output data area dataOutSize = dataList.pop(); dataOut = new uint8[dataOutSize]; int outIndex = dataOutSize; // Decompression routine uint32 srcVal = dataList.pop(); uint32 destVal; while (outIndex > 0) { uint32 numBits = 0; int count = 0; if (getBit(dataList, srcVal)) { destVal = copyBits(dataList, srcVal, 2); if (destVal < 2) { count = destVal + 2; destVal = copyBits(dataList, srcVal, destVal + 9); transferLoop(dataOut, outIndex, destVal, count); continue; } else if (destVal != 3) { count = copyBits(dataList, srcVal, 8); destVal = copyBits(dataList, srcVal, 8); transferLoop(dataOut, outIndex, destVal, count); continue; } else { numBits = 8; count = 8; } } else if (getBit(dataList, srcVal)) { destVal = copyBits(dataList, srcVal, 8); transferLoop(dataOut, outIndex, destVal, 1); continue; } else { numBits = 3; count = 0; } destVal = copyBits(dataList, srcVal, numBits); count += destVal; // Loop through extracting specified number of bytes for (int i = 0; i <= count; ++i) { // Shift 8 bits from the source to the destination for (int bitCtr = 0; bitCtr < 8; ++bitCtr) { bool flag = getBit(dataList, srcVal); destVal = (destVal << 1) | (flag ? 1 : 0); } dataOut[--outIndex] = destVal & 0xff; } } } void AGOSEngine::loadVGABeardFile(uint16 id) { uint32 offs, size; if (getFeatures() & GF_OLD_BUNDLE) { Common::File in; char filename[15]; if (id == 23) id = 112; else if (id == 328) id = 119; if (getPlatform() == Common::kPlatformAmiga) { if (getFeatures() & GF_TALKIE) sprintf(filename, "0%d.out", id); else sprintf(filename, "0%d.pkd", id); } else { sprintf(filename, "0%d.VGA", id); } if (!in.open(filename)) error("loadSimonVGAFile: Can't load %s", filename); size = in.size(); if (getFeatures() & GF_CRUNCHED) { byte *srcBuffer = (byte *)malloc(size); if (in.read(srcBuffer, size) != size) error("loadSimonVGAFile: Read failed"); decrunchFile(srcBuffer, _vgaBufferPointers[11].vgaFile2, size); free(srcBuffer); } else { if (in.read(_vgaBufferPointers[11].vgaFile2, size) != size) error("loadSimonVGAFile: Read failed"); } } else { offs = _gameOffsetsPtr[id]; size = _gameOffsetsPtr[id + 1] - offs; readGameFile(_vgaBufferPointers[11].vgaFile2, offs, size); } } void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) { Common::File in; char filename[15]; byte *dst; uint32 file, offs, srcSize, dstSize; uint extraBuffer = 0; if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) && id == 2 && type == 2) { // WORKAROUND: For the extra long strings in foreign languages // Allocate more space for text to cope with foreign languages that use // up more space than English. I hope 6400 bytes are enough. This number // is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki extraBuffer += 6400; } if (getFeatures() & GF_ZLIBCOMP) { loadOffsets(getFileName(GAME_GFXIDXFILE), id * 3 + type, file, offs, srcSize, dstSize); if (getPlatform() == Common::kPlatformAmiga) sprintf(filename, "GFX%d.VGA", file); else sprintf(filename, "graphics.vga"); dst = allocBlock(dstSize + extraBuffer); decompressData(filename, dst, offs, srcSize, dstSize); } else if (getFeatures() & GF_OLD_BUNDLE) { if (getPlatform() == Common::kPlatformAcorn) { sprintf(filename, "%.3d%d.DAT", id, type); } else if (getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformAtariST) { if (getFeatures() & GF_TALKIE) { sprintf(filename, "%.3d%d.out", id, type); } else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) { if (getPlatform() == Common::kPlatformAtariST) sprintf(filename, "%.2d%d.out", id, type); else sprintf(filename, "%c%d.out", 48 + id, type); } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) { sprintf(filename, "%.2d%d.pkd", id, type); } else if (getGameType() == GType_PN) { sprintf(filename, "%c%d.in", id + 48, type); } else { sprintf(filename, "%.3d%d.pkd", id, type); } } else { if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { sprintf(filename, "%.2d%d.VGA", id, type); } else if (getGameType() == GType_PN) { sprintf(filename, "%c%d.out", id + 48, type); } else { sprintf(filename, "%.3d%d.VGA", id, type); } } if (!in.open(filename)) { if (useError) error("loadVGAVideoFile: Can't load %s", filename); _block = _blockEnd = NULL; return; } dstSize = srcSize = in.size(); if (getGameType() == GType_PN && getPlatform() == Common::kPlatformDOS && id == 17 && type == 2) { // The A2.out file isn't compressed in PC version of Personal Nightmare dst = allocBlock(dstSize + extraBuffer); if (in.read(dst, dstSize) != dstSize) error("loadVGAVideoFile: Read failed"); } else if (getGameType() == GType_PN && (getFeatures() & GF_CRUNCHED)) { Common::Stack data; byte *dataOut = 0; int dataOutSize = 0; for (uint i = 0; i < srcSize / 4; ++i) { uint32 dataVal = in.readUint32BE(); // Correct incorrect byte, in corrupt 72.out file, included in some PC versions. if (dataVal == 168042714) data.push(168050906); else data.push(dataVal); } decompressPN(data, dataOut, dataOutSize); dst = allocBlock (dataOutSize + extraBuffer); memcpy(dst, dataOut, dataOutSize); delete[] dataOut; } else if (getFeatures() & GF_CRUNCHED) { byte *srcBuffer = (byte *)malloc(srcSize); if (in.read(srcBuffer, srcSize) != srcSize) error("loadVGAVideoFile: Read failed"); dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4); dst = allocBlock (dstSize + extraBuffer); decrunchFile(srcBuffer, dst, srcSize); free(srcBuffer); } else { dst = allocBlock(dstSize + extraBuffer); if (in.read(dst, dstSize) != dstSize) error("loadVGAVideoFile: Read failed"); } } else { id = id * 2 + (type - 1); offs = _gameOffsetsPtr[id]; dstSize = _gameOffsetsPtr[id + 1] - offs; if (!dstSize) { if (useError) error("loadVGAVideoFile: Can't load id %d type %d", id, type); _block = _blockEnd = NULL; return; } dst = allocBlock(dstSize + extraBuffer); readGameFile(dst, offs, dstSize); } } } // End of namespace AGOS