Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

disk.cpp

/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sky/disk.cpp,v 1.70.2.1 2005/10/18 02:11:26 sev Exp $
 *
 */

#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"

#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 Common::File();
      _dnrHandle = new Common::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];
      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

Generated by  Doxygen 1.6.0   Back to index