diff options
Diffstat (limited to 'engines/sky/disk.cpp')
-rw-r--r-- | engines/sky/disk.cpp | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/engines/sky/disk.cpp b/engines/sky/disk.cpp new file mode 100644 index 0000000000..3c39a287d0 --- /dev/null +++ b/engines/sky/disk.cpp @@ -0,0 +1,375 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/file.h" +#include "common/util.h" +#include "common/scummsys.h" +#include "sky/disk.h" +#include "sky/rnc_deco.h" +#include "sky/sky.h" +#include "sky/struc.h" + +namespace Sky { + +static const char *dataFilename = "sky.dsk"; +static const char *dinnerFilename = "sky.dnr"; + +Disk::Disk(const Common::String &gameDataPath) { + _dataDiskHandle = new Common::File(); + _dnrHandle = new Common::File(); + + _dnrHandle->open(dinnerFilename); + if (!_dnrHandle->isOpen()) + error("Could not open %s%s", gameDataPath.c_str(), dinnerFilename); + + if (!(_dinnerTableEntries = _dnrHandle->readUint32LE())) + error("Error reading from sky.dnr"); //even though it was opened correctly?! + + _dinnerTableArea = (uint8 *)malloc(_dinnerTableEntries * 8); + uint32 entriesRead = _dnrHandle->read(_dinnerTableArea, 8 * _dinnerTableEntries) / 8; + + if (entriesRead != _dinnerTableEntries) + error("entriesRead != dinnerTableEntries. [%d/%d]", entriesRead, _dinnerTableEntries); + + _dataDiskHandle->open(dataFilename); + if (!_dataDiskHandle->isOpen()) + error("Error opening %s%s", gameDataPath.c_str(), dataFilename); + + printf("Found BASS version v0.0%d (%d dnr entries)\n", determineGameVersion(), _dinnerTableEntries); + + memset(_buildList, 0, 60 * 2); + memset(_loadedFilesList, 0, 60 * 4); +} + +Disk::~Disk(void) { + if (_dnrHandle->isOpen()) + _dnrHandle->close(); + if (_dataDiskHandle->isOpen()) + _dataDiskHandle->close(); + fnFlushBuffers(); + free(_dinnerTableArea); + delete _dnrHandle; + delete _dataDiskHandle; +} + +bool Disk::fileExists(uint16 fileNr) { + return (getFileInfo(fileNr) != NULL); +} + +// allocate memory, load the file and return a pointer +uint8 *Disk::loadFile(uint16 fileNr) { + + uint8 cflag; + + debug(2, "load file %d,%d (%d)", (fileNr >> 11), (fileNr & 2047), fileNr); + + uint8 *fileInfoPtr = getFileInfo(fileNr); + if (fileInfoPtr == NULL) { + debug(1, "File %d not found", fileNr); + return NULL; + } + + uint32 fileFlags = READ_LE_UINT24(fileInfoPtr + 5); + uint32 fileSize = fileFlags & 0x03fffff; + uint32 fileOffset = READ_LE_UINT32(fileInfoPtr + 2) & 0x0ffffff; + + _lastLoadedFileSize = fileSize; + cflag = (uint8)((fileOffset >> 23) & 0x1); + fileOffset &= 0x7FFFFF; + + if (cflag) { + if (SkyEngine::_systemVars.gameVersion == 331) + fileOffset <<= 3; + else + fileOffset <<= 4; + } + + uint8 *fileDest = (uint8 *)malloc(fileSize + 4); // allocate memory for file + + _dataDiskHandle->seek(fileOffset, SEEK_SET); + + //now read in the data + int32 bytesRead = _dataDiskHandle->read(fileDest, fileSize); + + if (bytesRead != (int32)fileSize) + warning("Unable to read %d bytes from datadisk (%d bytes read)", fileSize, bytesRead); + + cflag = (uint8)((fileFlags >> 23) & 0x1); + //if cflag == 0 then file is compressed, 1 == uncompressed + + dataFileHeader *fileHeader = (dataFileHeader*)fileDest; + + if ((!cflag) && ((FROM_LE_16(fileHeader->flag) >> 7) & 1)) { + debug(2, "File is RNC compressed."); + + uint32 decompSize = (FROM_LE_16(fileHeader->flag) & ~0xFF) << 8; + decompSize |= FROM_LE_16(fileHeader->s_tot_size); + + uint8 *uncompDest = (uint8 *)malloc(decompSize); + + int32 unpackLen; + void *output, *input = fileDest + sizeof(dataFileHeader); + + if ((fileFlags >> 22) & 0x1) { //do we include the header? + // don't return the file's header + output = uncompDest; + unpackLen = _rncDecoder.unpackM1(input, output, 0); + } else { +#ifdef SCUMM_BIG_ENDIAN + // Convert dataFileHeader to BE (it only consists of 16 bit words) + uint16 *headPtr = (uint16 *)fileDest; + for (uint i = 0; i < sizeof(struct dataFileHeader) / 2; i++) + *(headPtr + i) = READ_LE_UINT16(headPtr + i); +#endif + + memcpy(uncompDest, fileDest, sizeof(dataFileHeader)); + output = uncompDest + sizeof(dataFileHeader); + unpackLen = _rncDecoder.unpackM1(input, output, 0); + if (unpackLen) + unpackLen += sizeof(dataFileHeader); + } + + debug(3, "UnpackM1 returned: %d", unpackLen); + + if (unpackLen == 0) { //Unpack returned 0: file was probably not packed. + free(uncompDest); + return fileDest; + } else { + if (unpackLen != (int32)decompSize) + debug(1, "ERROR: File %d: invalid decomp size! (was: %d, should be: %d)", fileNr, unpackLen, decompSize); + _lastLoadedFileSize = decompSize; + + free(fileDest); + return uncompDest; + } + } else { +#ifdef SCUMM_BIG_ENDIAN + if (!cflag) { + uint16 *headPtr = (uint16 *)fileDest; + for (uint i = 0; i < sizeof(struct dataFileHeader) / 2; i++) + *(headPtr + i) = READ_LE_UINT16(headPtr + i); + } +#endif + return fileDest; + } +} + +uint16 *Disk::loadScriptFile(uint16 fileNr) { + uint16 *buf = (uint16*)loadFile(fileNr); +#ifdef SCUMM_BIG_ENDIAN + for (uint i = 0; i < _lastLoadedFileSize / 2; i++) + buf[i] = FROM_LE_16(buf[i]); +#endif + return buf; +} + +uint8 *Disk::getFileInfo(uint16 fileNr) { + + uint16 i; + uint16 *dnrTbl16Ptr = (uint16 *)_dinnerTableArea; + + for (i = 0; i < _dinnerTableEntries; i++) { + if (READ_LE_UINT16(dnrTbl16Ptr) == fileNr) { + debug(2, "file %d found!", fileNr); + return (uint8 *)dnrTbl16Ptr; + } + dnrTbl16Ptr += 4; + } + + return 0; //not found +} + +void Disk::fnCacheChip(uint16 *fList) { + + // fnCacheChip is called after fnCacheFast + uint16 cnt = 0; + while (_buildList[cnt]) + cnt++; + uint16 fCnt = 0; + do { + _buildList[cnt + fCnt] = fList[fCnt] & 0x7FFFU; + fCnt++; + } while (fList[fCnt-1]); + fnCacheFiles(); +} + +void Disk::fnCacheFast(uint16 *fList) { + if (fList != NULL) { + uint8 cnt = 0; + do { + _buildList[cnt] = fList[cnt] & 0x7FFFU; + cnt++; + } while (fList[cnt-1]); + } +} + +void Disk::fnCacheFiles(void) { + + uint16 lCnt, bCnt, targCnt; + targCnt = lCnt = 0; + bool found; + while (_loadedFilesList[lCnt]) { + bCnt = 0; + found = false; + while (_buildList[bCnt] && (!found)) { + if ((_buildList[bCnt] & 0x7FFFU) == _loadedFilesList[lCnt]) + found = true; + else + bCnt++; + } + if (found) { + _loadedFilesList[targCnt] = _loadedFilesList[lCnt]; + targCnt++; + } else { + free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]); + SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL; + } + lCnt++; + } + _loadedFilesList[targCnt] = 0; // mark end of list + bCnt = 0; + while (_buildList[bCnt]) { + if ((_buildList[bCnt] & 0x7FF) == 0x7FF) { + // amiga dummy files + bCnt++; + continue; + } + lCnt = 0; + found = false; + while (_loadedFilesList[lCnt] && (!found)) { + if (_loadedFilesList[lCnt] == (_buildList[bCnt] & 0x7FFFU)) + found = true; + lCnt++; + } + if (found) { + bCnt++; + continue; + } + // ok, we really have to load the file. + _loadedFilesList[targCnt] = _buildList[bCnt] & 0x7FFFU; + targCnt++; + _loadedFilesList[targCnt] = 0; + SkyEngine::_itemList[_buildList[bCnt] & 2047] = (void**)loadFile(_buildList[bCnt] & 0x7FFF); + if (!SkyEngine::_itemList[_buildList[bCnt] & 2047]) + warning("fnCacheFiles: Disk::loadFile() returned NULL for file %d",_buildList[bCnt] & 0x7FFF); + bCnt++; + } + _buildList[0] = 0; +} + +void Disk::refreshFilesList(uint32 *list) { + + uint8 cnt = 0; + while (_loadedFilesList[cnt]) { + if (SkyEngine::_itemList[_loadedFilesList[cnt] & 2047]) + free(SkyEngine::_itemList[_loadedFilesList[cnt] & 2047]); + SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = NULL; + cnt++; + } + cnt = 0; + while (list[cnt]) { + _loadedFilesList[cnt] = list[cnt]; + SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = (void**)loadFile((uint16)(_loadedFilesList[cnt] & 0x7FFF)); + cnt++; + } + _loadedFilesList[cnt] = 0; +} + +void Disk::fnMiniLoad(uint16 fileNum) { + + uint16 cnt = 0; + while (_loadedFilesList[cnt]) { + if (_loadedFilesList[cnt] == fileNum) + return; + cnt++; + } + _loadedFilesList[cnt] = fileNum & 0x7FFFU; + _loadedFilesList[cnt + 1] = 0; + SkyEngine::_itemList[fileNum & 2047] = (void**)loadFile(fileNum); +} + +void Disk::fnFlushBuffers(void) { + + // dump all loaded sprites + uint8 lCnt = 0; + while (_loadedFilesList[lCnt]) { + free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]); + SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL; + lCnt++; + } + _loadedFilesList[0] = 0; +} + +void Disk::dumpFile(uint16 fileNr) { + char buf[128]; + Common::File out; + byte* filePtr; + + filePtr = loadFile(fileNr); + sprintf(buf, "dumps/file-%d.dmp", fileNr); + + if (!out.exists(buf, "")) { + if (out.open(buf, Common::File::kFileWriteMode, "")) + out.write(filePtr, _lastLoadedFileSize); + } + free(filePtr); +} + +uint32 Disk::determineGameVersion() { + //determine game version based on number of entries in dinner table + switch (_dinnerTableEntries) { + case 243: + // pc gamer demo (v0.0109) + return 109; + case 247: + //floppy demo (v0.0267) + return 267; + case 1404: + //floppy (v0.0288) + return 288; + case 1413: + //floppy (v0.0303) + return 303; + case 1445: + //floppy (v0.0331 or v0.0348) + if (_dataDiskHandle->size() == 8830435) + return 348; + else + return 331; + case 1711: + //cd demo (v0.0365) + return 365; + case 5099: + //cd (v0.0368) + return 368; + case 5097: + //cd (v0.0372) + return 372; + default: + //unknown version + error("Unknown game version! %d dinner table entries", _dinnerTableEntries); + break; + } +} + +} // End of namespace Sky |