scummvm/sky/disk.cpp
Chris Apers bb3278a088 PalmOS : ARM optimized unpacker
svn-id: r17077
2005-03-11 10:55:10 +00:00

458 lines
11 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2003-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "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"
#if defined(__PALM_OS__)
#include "arm/native.h"
#include "arm/macros.h"
#endif
namespace Sky {
static const char *dataFilename = "sky.dsk";
static const char *dinnerFilename = "sky.dnr";
Disk::Disk(const Common::String &gameDataPath) {
_prefRoot = NULL;
_dataDiskHandle = new File();
_dnrHandle = new File();
uint32 entriesRead;
_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);
entriesRead = _dnrHandle->read(_dinnerTableArea, 8 * _dinnerTableEntries) / 8;
if (entriesRead != _dinnerTableEntries)
warning("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) {
PrefFile *fEntry = _prefRoot;
while (fEntry) {
free(fEntry->data);
PrefFile *fTemp = fEntry;
fEntry = fEntry->next;
delete fTemp;
}
if (_dnrHandle->isOpen())
_dnrHandle->close();
if (_dataDiskHandle->isOpen())
_dataDiskHandle->close();
delete _dnrHandle;
delete _dataDiskHandle;
}
void Disk::flushPrefetched(void) {
PrefFile *fEntry = _prefRoot;
while (fEntry) {
free(fEntry->data);
PrefFile *fTemp = fEntry;
fEntry = fEntry->next;
delete fTemp;
}
_prefRoot = NULL;
}
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;
uint8 *prefData = givePrefetched(fileNr, &_lastLoadedFileSize);
if (prefData)
return prefData;
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;
#ifdef __PALM_OS__
ARM_START(RncDecoderType)
ARM_INIT(SKY_UNPACKM1)
ARM_ADDM(input)
ARM_ADDM(output)
ARM_CALL_VALUE(ARM_ENGINE, PNO_DATA(), unpackLen)
ARM_CONTINUE()
#endif
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);
#ifdef __PALM_OS__
ARM_START(RncDecoderType)
ARM_INIT(SKY_UNPACKM1)
ARM_ADDM(input)
ARM_ADDM(output)
ARM_CALL_VALUE(ARM_ENGINE, PNO_DATA(), unpackLen)
ARM_CONTINUE()
#endif
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;
}
}
void Disk::prefetchFile(uint16 fileNr) {
PrefFile **fEntry = &_prefRoot;
bool found = false;
while (*fEntry) {
if ((*fEntry)->fileNr == fileNr)
found = true;
fEntry = &((*fEntry)->next);
}
if (found) {
debug(1,"Disk::prefetchFile: File %d was already prefetched",fileNr);
return ;
}
uint8 *temp = loadFile(fileNr);
*fEntry = new PrefFile;
(*fEntry)->data = temp;
(*fEntry)->fileSize = _lastLoadedFileSize;
(*fEntry)->fileNr = fileNr;
(*fEntry)->next = NULL;
}
uint8 *Disk::givePrefetched(uint16 fileNr, uint32 *fSize) {
PrefFile **fEntry = &_prefRoot;
bool found = false;
while ((*fEntry) && (!found)) {
if ((*fEntry)->fileNr == fileNr)
found = true;
else
fEntry = &((*fEntry)->next);
}
if (!found) {
*fSize = 0;
return NULL;
}
uint8 *retPtr = (*fEntry)->data;
PrefFile *retStr = *fEntry;
*fEntry = (*fEntry)->next;
*fSize = retStr->fileSize;
delete retStr;
return retPtr;
}
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] = 0;
lCnt++;
}
_loadedFilesList[0] = 0;
}
void Disk::dumpFile(uint16 fileNr) {
char buf[128];
File out;
byte* filePtr;
filePtr = loadFile(fileNr);
sprintf(buf, "dumps/file-%d.dmp", fileNr);
if (!out.exists(buf, "")) {
if (out.open(buf, 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