mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-05 09:10:29 +00:00
901 lines
25 KiB
C++
901 lines
25 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This file is based on WME Lite.
|
|
* http://dead-code.org/redir.php?target=wmelite
|
|
* Copyright (c) 2011 Jan Nedoma
|
|
*/
|
|
|
|
#include "engines/wintermute/dcgf.h"
|
|
#include "engines/wintermute/Base/BFileManager.h"
|
|
#include "engines/wintermute/utils/StringUtil.h"
|
|
#include "engines/wintermute/utils/PathUtil.h"
|
|
#include "engines/wintermute/Base/file/BDiskFile.h"
|
|
#include "engines/wintermute/Base/file/BSaveThumbFile.h"
|
|
#include "engines/wintermute/Base/BFileEntry.h"
|
|
#include "engines/wintermute/Base/file/BPkgFile.h"
|
|
#include "engines/wintermute/Base/BResources.h"
|
|
#include "engines/wintermute/Base/BPackage.h"
|
|
#include "engines/wintermute/Base/BRegistry.h"
|
|
#include "engines/wintermute/Base/BGame.h"
|
|
#include "engines/wintermute/dcpackage.h"
|
|
#include "engines/wintermute/utils/utils.h"
|
|
#include "engines/wintermute/PlatformSDL.h"
|
|
#include "engines/wintermute/wintermute.h"
|
|
#include "common/str.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/util.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/system.h"
|
|
#include "common/fs.h"
|
|
#include "common/file.h"
|
|
#include "common/savefile.h"
|
|
|
|
namespace WinterMute {
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
CBFileManager::CBFileManager(CBGame *inGame): CBBase(inGame) {
|
|
_basePath = NULL;
|
|
|
|
initPaths();
|
|
registerPackages();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
CBFileManager::~CBFileManager() {
|
|
cleanup();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::cleanup() {
|
|
// delete registered paths
|
|
for (int i = 0; i < _singlePaths.GetSize(); i++)
|
|
delete [] _singlePaths[i];
|
|
_singlePaths.RemoveAll();
|
|
|
|
for (int i = 0; i < _packagePaths.GetSize(); i++)
|
|
delete [] _packagePaths[i];
|
|
_packagePaths.RemoveAll();
|
|
|
|
|
|
// delete file entries
|
|
_filesIter = _files.begin();
|
|
while (_filesIter != _files.end()) {
|
|
delete _filesIter->_value;
|
|
_filesIter++;
|
|
}
|
|
_files.clear();
|
|
|
|
// close open files
|
|
for (int i = 0; i < _openFiles.GetSize(); i++) {
|
|
delete _openFiles[i];
|
|
}
|
|
_openFiles.RemoveAll();
|
|
|
|
|
|
// delete packages
|
|
for (int i = 0; i < _packages.GetSize(); i++)
|
|
delete _packages[i];
|
|
_packages.RemoveAll();
|
|
|
|
delete[] _basePath;
|
|
_basePath = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
#define MAX_FILE_SIZE 10000000
|
|
//////////////////////////////////////////////////////////////////////
|
|
byte *CBFileManager::readWholeFile(const Common::String &filename, uint32 *size, bool mustExist) {
|
|
|
|
byte *buffer = NULL;
|
|
|
|
Common::SeekableReadStream *file = openFile(filename);
|
|
if (!file) {
|
|
if (mustExist) Game->LOG(0, "Error opening file '%s'", filename.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
if (File->GetSize() > MAX_FILE_SIZE) {
|
|
Game->LOG(0, "File '%s' exceeds the maximum size limit (%d bytes)", Filename, MAX_FILE_SIZE);
|
|
CloseFile(File);
|
|
return NULL;
|
|
}
|
|
*/
|
|
|
|
|
|
buffer = new byte[file->size() + 1];
|
|
if (buffer == NULL) {
|
|
Game->LOG(0, "Error allocating buffer for file '%s' (%d bytes)", filename.c_str(), file->size() + 1);
|
|
closeFile(file);
|
|
return NULL;
|
|
}
|
|
|
|
if (file->read(buffer, file->size()) != file->size()) {
|
|
Game->LOG(0, "Error reading file '%s'", filename.c_str());
|
|
closeFile(file);
|
|
delete [] buffer;
|
|
return NULL;
|
|
};
|
|
|
|
buffer[file->size()] = '\0';
|
|
if (size != NULL) *size = file->size();
|
|
closeFile(file);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
Common::SeekableReadStream *CBFileManager::loadSaveGame(const Common::String &filename) {
|
|
Common::SaveFileManager *saveMan = g_wintermute->getSaveFileMan();
|
|
Common::InSaveFile *file = saveMan->openForLoading(filename);
|
|
return file;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::saveFile(const Common::String &filename, byte *Buffer, uint32 BufferSize, bool Compressed, byte *PrefixBuffer, uint32 PrefixSize) {
|
|
// TODO
|
|
warning("Implement SaveFile");
|
|
|
|
Common::SaveFileManager *saveMan = g_wintermute->getSaveFileMan();
|
|
Common::OutSaveFile *file = saveMan->openForSaving(filename);
|
|
file->write(PrefixBuffer, PrefixSize);
|
|
file->write(Buffer, BufferSize);
|
|
file->finalize();
|
|
delete file;
|
|
#if 0
|
|
RestoreCurrentDir();
|
|
|
|
CBUtils::CreatePath(filename, false);
|
|
|
|
FILE *f = fopen(filename, "wb");
|
|
if (!f) {
|
|
Game->LOG(0, "Error opening file '%s' for writing.", filename);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (PrefixBuffer && PrefixSize) {
|
|
fwrite(PrefixBuffer, PrefixSize, 1, f);
|
|
}
|
|
|
|
if (Compressed) {
|
|
uint32 CompSize = BufferSize + (BufferSize / 100) + 12; // 1% extra space
|
|
byte *CompBuffer = new byte[CompSize];
|
|
if (!CompBuffer) {
|
|
Game->LOG(0, "Error allocating compression buffer while saving '%s'", filename);
|
|
Compressed = false;
|
|
} else {
|
|
if (compress(CompBuffer, (uLongf *)&CompSize, Buffer, BufferSize) == Z_OK) {
|
|
uint32 magic = DCGF_MAGIC;
|
|
fwrite(&magic, sizeof(uint32), 1, f);
|
|
magic = COMPRESSED_FILE_MAGIC;
|
|
fwrite(&magic, sizeof(uint32), 1, f);
|
|
|
|
uint32 DataOffset = 5 * sizeof(uint32);
|
|
fwrite(&DataOffset, sizeof(uint32), 1, f);
|
|
|
|
fwrite(&CompSize, sizeof(uint32), 1, f);
|
|
fwrite(&BufferSize, sizeof(uint32), 1, f);
|
|
|
|
fwrite(CompBuffer, CompSize, 1, f);
|
|
} else {
|
|
Game->LOG(0, "Error compressing data while saving '%s'", filename);
|
|
Compressed = false;
|
|
}
|
|
|
|
delete [] CompBuffer;
|
|
}
|
|
}
|
|
|
|
if (!Compressed) fwrite(Buffer, BufferSize, 1, f);
|
|
|
|
fclose(f);
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::requestCD(int cd, char *packageFile, char *filename) {
|
|
// unmount all non-local packages
|
|
for (int i = 0; i < _packages.GetSize(); i++) {
|
|
if (_packages[i]->_cD > 0) _packages[i]->close();
|
|
}
|
|
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::addPath(TPathType type, const Common::String &path) {
|
|
if (path.c_str() == NULL || strlen(path.c_str()) < 1) return E_FAIL;
|
|
|
|
bool slashed = (path[path.size() - 1] == '\\' || path[path.size() - 1] == '/');
|
|
|
|
char *buffer = new char [strlen(path.c_str()) + 1 + (slashed ? 0 : 1)];
|
|
if (buffer == NULL) return E_FAIL;
|
|
|
|
strcpy(buffer, path.c_str());
|
|
if (!slashed) strcat(buffer, "\\");
|
|
//CBPlatform::strlwr(buffer);
|
|
|
|
switch (type) {
|
|
case PATH_SINGLE:
|
|
_singlePaths.Add(buffer);
|
|
break;
|
|
case PATH_PACKAGE:
|
|
_packagePaths.Add(buffer);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::reloadPaths() {
|
|
// delete registered paths
|
|
for (int i = 0; i < _singlePaths.GetSize(); i++)
|
|
delete [] _singlePaths[i];
|
|
_singlePaths.RemoveAll();
|
|
|
|
for (int i = 0; i < _packagePaths.GetSize(); i++)
|
|
delete [] _packagePaths[i];
|
|
_packagePaths.RemoveAll();
|
|
|
|
return initPaths();
|
|
}
|
|
|
|
|
|
#define TEMP_BUFFER_SIZE 32768
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::initPaths() {
|
|
restoreCurrentDir();
|
|
|
|
AnsiString pathList;
|
|
int numPaths;
|
|
|
|
// single files paths
|
|
pathList = Game->_registry->ReadString("Resource", "CustomPaths", "");
|
|
numPaths = CBUtils::strNumEntries(pathList.c_str(), ';');
|
|
|
|
for (int i = 0; i < numPaths; i++) {
|
|
char *path = CBUtils::strEntry(i, pathList.c_str(), ';');
|
|
if (path && strlen(path) > 0) {
|
|
addPath(PATH_SINGLE, path);
|
|
}
|
|
delete[] path;
|
|
path = NULL;
|
|
}
|
|
addPath(PATH_SINGLE, ".\\");
|
|
|
|
|
|
// package files paths
|
|
addPath(PATH_PACKAGE, "./");
|
|
|
|
/*#ifdef __APPLE__
|
|
// search .app path and Resources dir in the bundle
|
|
CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
|
CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
|
|
const char *pathPtr = CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding());
|
|
|
|
#ifdef __IPHONE__
|
|
AddPath(PATH_PACKAGE, pathPtr);
|
|
AddPath(PATH_SINGLE, pathPtr);
|
|
#else
|
|
char bundlePath[MAX_PATH];
|
|
|
|
sprintf(bundlePath, "%s/../", pathPtr);
|
|
AddPath(PATH_PACKAGE, bundlePath);
|
|
AddPath(PATH_SINGLE, bundlePath);
|
|
|
|
sprintf(bundlePath, "%s/Contents/Resources/", pathPtr);
|
|
AddPath(PATH_PACKAGE, bundlePath);
|
|
AddPath(PATH_SINGLE, bundlePath);
|
|
|
|
|
|
CFRelease(appUrlRef);
|
|
CFRelease(macPath);
|
|
#endif
|
|
#endif*/
|
|
|
|
|
|
pathList = Game->_registry->ReadString("Resource", "PackagePaths", "");
|
|
numPaths = CBUtils::strNumEntries(pathList.c_str(), ';');
|
|
|
|
for (int i = 0; i < numPaths; i++) {
|
|
char *path = CBUtils::strEntry(i, pathList.c_str(), ';');
|
|
if (path && strlen(path) > 0) {
|
|
addPath(PATH_PACKAGE, path);
|
|
}
|
|
delete[] path;
|
|
path = NULL;
|
|
}
|
|
addPath(PATH_PACKAGE, "data");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::registerPackages() {
|
|
restoreCurrentDir();
|
|
|
|
Game->LOG(0, "Scanning packages...");
|
|
warning("Scanning packages");
|
|
|
|
Common::ArchiveMemberList files;
|
|
SearchMan.listMatchingMembers(files, "*.dcp");
|
|
|
|
for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); it++) {
|
|
registerPackage((*it)->getName().c_str());
|
|
}
|
|
#if 0
|
|
AnsiString extension = AnsiString(PACKAGE_EXTENSION);
|
|
|
|
for (int i = 0; i < _packagePaths.GetSize(); i++) {
|
|
boost::filesystem::path absPath = boost::filesystem::syste_complete(_packagePaths[i]);
|
|
|
|
//Game->LOG(0, "Scanning: %s", absPath.string().c_str());
|
|
//printf("Scanning: %s\n", absPath.string().c_str());
|
|
|
|
if (!exists(absPath)) continue;
|
|
|
|
// scan files
|
|
boost::filesystem::directory_iterator endIter;
|
|
for (boost::filesystem::directory_iterator dit(absPath); dit != endIter; ++dit) {
|
|
if (!is_directory((*dit).status())) {
|
|
AnsiString fileName = (*dit).path().string();
|
|
|
|
if (!IsValidPackage(fileName)) continue;
|
|
|
|
warning("%s", fileName.c_str());
|
|
//printf("%s\n", fileName.c_str());
|
|
if (!StringUtil::CompareNoCase(extension, PathUtil::GetExtension(fileName))) continue;
|
|
warning("Registered");
|
|
RegisterPackage(absPath.string().c_str(), dit->path().filename().string().c_str());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
warning(" Registered %d files in %d package(s)", _files.size(), _packages.GetSize());
|
|
Game->LOG(0, " Registered %d files in %d package(s)", _files.size(), _packages.GetSize());
|
|
|
|
warning(" Registered %d files in %d package(s)", _files.size(), _packages.GetSize());
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::registerPackage(const Common::String &filename , bool searchSignature) {
|
|
// FILE *f = fopen(filename, "rb");
|
|
Common::File *package = new Common::File();
|
|
package->open(filename);
|
|
if (!package->isOpen()) {
|
|
Game->LOG(0, " Error opening package file '%s'. Ignoring.", filename.c_str());
|
|
return S_OK;
|
|
}
|
|
|
|
uint32 absoluteOffset = 0;
|
|
bool boundToExe = false;
|
|
|
|
if (searchSignature) {
|
|
uint32 Offset;
|
|
if (!findPackageSignature(package, &Offset)) {
|
|
delete package;
|
|
return S_OK;
|
|
} else {
|
|
package->seek(Offset, SEEK_SET);
|
|
absoluteOffset = Offset;
|
|
boundToExe = true;
|
|
}
|
|
}
|
|
|
|
TPackageHeader hdr;
|
|
hdr.readFromStream(package);
|
|
// package->read(&hdr, sizeof(TPackageHeader), 1, f);
|
|
if (hdr.Magic1 != PACKAGE_MAGIC_1 || hdr.Magic2 != PACKAGE_MAGIC_2 || hdr.PackageVersion > PACKAGE_VERSION) {
|
|
Game->LOG(0, " Invalid header in package file '%s'. Ignoring.", filename.c_str());
|
|
delete package;
|
|
return S_OK;
|
|
}
|
|
|
|
if (hdr.PackageVersion != PACKAGE_VERSION) {
|
|
Game->LOG(0, " Warning: package file '%s' is outdated.", filename.c_str());
|
|
}
|
|
|
|
// new in v2
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
uint32 dirOffset;
|
|
dirOffset = package->readUint32LE();
|
|
dirOffset += absoluteOffset;
|
|
package->seek(dirOffset, SEEK_SET);
|
|
}
|
|
|
|
for (uint32 i = 0; i < hdr.NumDirs; i++) {
|
|
CBPackage *pkg = new CBPackage(Game);
|
|
if (!pkg) return E_FAIL;
|
|
|
|
pkg->_boundToExe = boundToExe;
|
|
|
|
// read package info
|
|
byte nameLength = package->readByte();
|
|
pkg->_name = new char[nameLength];
|
|
package->read(pkg->_name, nameLength);
|
|
pkg->_cD = package->readByte();
|
|
pkg->_priority = hdr.Priority;
|
|
|
|
if (!hdr.MasterIndex) pkg->_cD = 0; // override CD to fixed disk
|
|
_packages.Add(pkg);
|
|
|
|
|
|
// read file entries
|
|
uint32 NumFiles = package->readUint32LE();
|
|
|
|
for (uint32 j = 0; j < NumFiles; j++) {
|
|
char *name;
|
|
uint32 offset, length, compLength, flags, timeDate1, timeDate2;
|
|
|
|
nameLength = package->readByte();
|
|
name = new char[nameLength];
|
|
package->read(name, nameLength);
|
|
|
|
// v2 - xor name
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
for (int k = 0; k < nameLength; k++) {
|
|
((byte *)name)[k] ^= 'D';
|
|
}
|
|
}
|
|
|
|
// some old version of ProjectMan writes invalid directory entries
|
|
// so at least prevent strupr from corrupting memory
|
|
name[nameLength - 1] = '\0';
|
|
|
|
|
|
CBPlatform::strupr(name);
|
|
|
|
offset = package->readUint32LE();
|
|
offset += absoluteOffset;
|
|
length = package->readUint32LE();
|
|
compLength = package->readUint32LE();
|
|
flags = package->readUint32LE();
|
|
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
timeDate1 = package->readUint32LE();
|
|
timeDate2 = package->readUint32LE();
|
|
}
|
|
_filesIter = _files.find(name);
|
|
if (_filesIter == _files.end()) {
|
|
CBFileEntry *file = new CBFileEntry(Game);
|
|
file->_package = pkg;
|
|
file->_offset = offset;
|
|
file->_length = length;
|
|
file->_compressedLength = compLength;
|
|
file->_flags = flags;
|
|
|
|
_files[name] = file;
|
|
} else {
|
|
// current package has lower CD number or higher priority, than the registered
|
|
if (pkg->_cD < _filesIter->_value->_package->_cD || pkg->_priority > _filesIter->_value->_package->_priority) {
|
|
_filesIter->_value->_package = pkg;
|
|
_filesIter->_value->_offset = offset;
|
|
_filesIter->_value->_length = length;
|
|
_filesIter->_value->_compressedLength = compLength;
|
|
_filesIter->_value->_flags = flags;
|
|
}
|
|
}
|
|
delete [] name;
|
|
}
|
|
}
|
|
|
|
|
|
delete package;
|
|
return S_OK;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::registerPackage(const char *Path, const char *name, bool SearchSignature) {
|
|
// TODO
|
|
error("Implement RegisterPackage, this is the old one");
|
|
#if 0
|
|
char Filename[MAX_PATH];
|
|
sprintf(filename, "%s%s", Path, name);
|
|
|
|
FILE *f = fopen(filename, "rb");
|
|
if (!f) {
|
|
Game->LOG(0, " Error opening package file '%s'. Ignoring.", filename);
|
|
return S_OK;
|
|
}
|
|
|
|
uint32 AbsoluteOffset = 0;
|
|
bool BoundToExe = false;
|
|
|
|
if (SearchSignature) {
|
|
uint32 Offset;
|
|
if (!FindPackageSignature(f, &Offset)) {
|
|
fclose(f);
|
|
return S_OK;
|
|
} else {
|
|
fseek(f, Offset, SEEK_SET);
|
|
AbsoluteOffset = Offset;
|
|
BoundToExe = true;
|
|
}
|
|
}
|
|
|
|
TPackageHeader hdr;
|
|
fread(&hdr, sizeof(TPackageHeader), 1, f);
|
|
if (hdr.Magic1 != PACKAGE_MAGIC_1 || hdr.Magic2 != PACKAGE_MAGIC_2 || hdr.PackageVersion > PACKAGE_VERSION) {
|
|
Game->LOG(0, " Invalid header in package file '%s'. Ignoring.", filename);
|
|
fclose(f);
|
|
return S_OK;
|
|
}
|
|
|
|
if (hdr.PackageVersion != PACKAGE_VERSION) {
|
|
Game->LOG(0, " Warning: package file '%s' is outdated.", filename);
|
|
}
|
|
|
|
// new in v2
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
uint32 DirOffset;
|
|
fread(&DirOffset, sizeof(uint32), 1, f);
|
|
DirOffset += AbsoluteOffset;
|
|
fseek(f, DirOffset, SEEK_SET);
|
|
}
|
|
|
|
for (int i = 0; i < hdr.NumDirs; i++) {
|
|
CBPackage *pkg = new CBPackage(Game);
|
|
if (!pkg) return E_FAIL;
|
|
|
|
pkg->_boundToExe = BoundToExe;
|
|
|
|
// read package info
|
|
byte nameLength;
|
|
fread(&nameLength, sizeof(byte), 1, f);
|
|
pkg->_name = new char[nameLength];
|
|
fread(pkg->_name, nameLength, 1, f);
|
|
fread(&pkg->_cD, sizeof(byte), 1, f);
|
|
pkg->_priority = hdr.Priority;
|
|
|
|
if (!hdr.MasterIndex) pkg->_cD = 0; // override CD to fixed disk
|
|
_packages.Add(pkg);
|
|
|
|
|
|
// read file entries
|
|
uint32 NumFiles;
|
|
fread(&NumFiles, sizeof(uint32), 1, f);
|
|
|
|
for (int j = 0; j < NumFiles; j++) {
|
|
char *name;
|
|
uint32 Offset, Length, CompLength, Flags, TimeDate1, TimeDate2;
|
|
|
|
fread(&nameLength, sizeof(byte), 1, f);
|
|
name = new char[nameLength];
|
|
fread(name, nameLength, 1, f);
|
|
|
|
// v2 - xor name
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
for (int k = 0; k < nameLength; k++) {
|
|
((byte *)name)[k] ^= 'D';
|
|
}
|
|
}
|
|
|
|
// some old version of ProjectMan writes invalid directory entries
|
|
// so at least prevent strupr from corrupting memory
|
|
name[nameLength - 1] = '\0';
|
|
|
|
|
|
CBPlatform::strupr(name);
|
|
|
|
fread(&Offset, sizeof(uint32), 1, f);
|
|
Offset += AbsoluteOffset;
|
|
fread(&Length, sizeof(uint32), 1, f);
|
|
fread(&CompLength, sizeof(uint32), 1, f);
|
|
fread(&Flags, sizeof(uint32), 1, f);
|
|
|
|
if (hdr.PackageVersion == PACKAGE_VERSION) {
|
|
fread(&TimeDate1, sizeof(uint32), 1, f);
|
|
fread(&TimeDate2, sizeof(uint32), 1, f);
|
|
}
|
|
_filesIter = _files.find(name);
|
|
if (_filesIter == _files.end()) {
|
|
CBFileEntry *file = new CBFileEntry(Game);
|
|
file->_package = pkg;
|
|
file->_offset = Offset;
|
|
file->_length = Length;
|
|
file->_compressedLength = CompLength;
|
|
file->_flags = Flags;
|
|
|
|
_files[name] = file;
|
|
} else {
|
|
// current package has lower CD number or higher priority, than the registered
|
|
if (pkg->_cD < _filesIter->_value->_package->_cD || pkg->_priority > _filesIter->_value->_package->_priority) {
|
|
_filesIter->_value->_package = pkg;
|
|
_filesIter->_value->_offset = Offset;
|
|
_filesIter->_value->_length = Length;
|
|
_filesIter->_value->_compressedLength = CompLength;
|
|
_filesIter->_value->_flags = Flags;
|
|
}
|
|
}
|
|
delete [] name;
|
|
}
|
|
}
|
|
|
|
|
|
fclose(f);
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBFileManager::isValidPackage(const AnsiString &fileName) const {
|
|
AnsiString plainName = PathUtil::getFileNameWithoutExtension(fileName);
|
|
|
|
// check for device-type specific packages
|
|
if (StringUtil::startsWith(plainName, "xdevice_", true)) {
|
|
return StringUtil::compareNoCase(plainName, "xdevice_" + Game->getDeviceType());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Common::File *CBFileManager::openPackage(const Common::String &name) {
|
|
//TODO: Is it really necessary to do this when we have the ScummVM-system?
|
|
|
|
//RestoreCurrentDir();
|
|
|
|
Common::File *ret = new Common::File();
|
|
char filename[MAX_PATH];
|
|
|
|
for (int i = 0; i < _packagePaths.GetSize(); i++) {
|
|
sprintf(filename, "%s%s.%s", _packagePaths[i], name.c_str(), PACKAGE_EXTENSION);
|
|
ret->open(filename);
|
|
if (ret->isOpen()) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
sprintf(filename, "%s.%s", name.c_str(), PACKAGE_EXTENSION);
|
|
ret->open(filename);
|
|
if (ret->isOpen()) {
|
|
return ret;
|
|
}
|
|
warning("CBFileManager::OpenPackage - Couldn't load file %s", name.c_str());
|
|
delete ret;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Common::File *CBFileManager::openSingleFile(const Common::String &name) {
|
|
restoreCurrentDir();
|
|
|
|
Common::File *ret = NULL;
|
|
char filename[MAX_PATH];
|
|
|
|
for (int i = 0; i < _singlePaths.GetSize(); i++) {
|
|
sprintf(filename, "%s%s", _singlePaths[i], name.c_str());
|
|
ret->open(filename);
|
|
if (ret->isOpen())
|
|
return ret;
|
|
}
|
|
|
|
// didn't find in search paths, try to open directly
|
|
ret->open(name);
|
|
if (ret->isOpen()) {
|
|
return ret;
|
|
} else {
|
|
delete ret;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBFileManager::getFullPath(const Common::String &filename, char *fullname) {
|
|
restoreCurrentDir();
|
|
|
|
Common::File f;
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < _singlePaths.GetSize(); i++) {
|
|
sprintf(fullname, "%s%s", _singlePaths[i], filename.c_str());
|
|
f.open(fullname);
|
|
if (f.isOpen()) {
|
|
f.close();
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
f.open(filename.c_str());
|
|
if (f.isOpen()) {
|
|
f.close();
|
|
found = true;
|
|
strcpy(fullname, filename.c_str());
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBFileEntry *CBFileManager::getPackageEntry(const Common::String &filename) {
|
|
char *upc_name = new char[strlen(filename.c_str()) + 1];
|
|
strcpy(upc_name, filename.c_str());
|
|
CBPlatform::strupr(upc_name);
|
|
|
|
CBFileEntry *ret = NULL;
|
|
_filesIter = _files.find(upc_name);
|
|
if (_filesIter != _files.end()) ret = _filesIter->_value;
|
|
|
|
delete [] upc_name;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CBFileManager::hasFile(const Common::String &filename) {
|
|
//TODO: Do this in a much simpler fashion
|
|
Common::SeekableReadStream *stream = openFile(filename, true, false);
|
|
if (!stream) {
|
|
return false;
|
|
}
|
|
delete stream;
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Common::SeekableReadStream *CBFileManager::openFile(const Common::String &filename, bool absPathWarning, bool keepTrackOf) {
|
|
if (strcmp(filename.c_str(), "") == 0) return NULL;
|
|
//Game->LOG(0, "open file: %s", filename);
|
|
/*#ifdef __WIN32__
|
|
if (Game->_dEBUG_DebugMode && Game->_dEBUG_AbsolutePathWarning && AbsPathWarning) {
|
|
char Drive[_MAX_DRIVE];
|
|
_splitpath(filename, Drive, NULL, NULL, NULL);
|
|
if (Drive[0] != '\0') {
|
|
Game->LOG(0, "WARNING: Referencing absolute path '%s'. The game will NOT work on another computer.", filename);
|
|
}
|
|
}
|
|
#endif*/
|
|
|
|
Common::SeekableReadStream *File = openFileRaw(filename);
|
|
if (File && keepTrackOf) _openFiles.Add(File);
|
|
return File;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::closeFile(Common::SeekableReadStream *File) {
|
|
for (int i = 0; i < _openFiles.GetSize(); i++) {
|
|
if (_openFiles[i] == File) {
|
|
delete _openFiles[i];
|
|
_openFiles.RemoveAt(i);
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
Common::SeekableReadStream *CBFileManager::openFileRaw(const Common::String &filename) {
|
|
restoreCurrentDir();
|
|
|
|
Common::SeekableReadStream *ret = NULL;
|
|
|
|
if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) {
|
|
CBSaveThumbFile *SaveThumbFile = new CBSaveThumbFile(Game);
|
|
if (SUCCEEDED(SaveThumbFile->open(filename))) {
|
|
ret = SaveThumbFile->getMemStream();
|
|
}
|
|
delete SaveThumbFile;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
ret = openDiskFile(filename, this);
|
|
if (ret) return ret;
|
|
|
|
ret = openPkgFile(filename, this);
|
|
if (ret) return ret;
|
|
|
|
ret = CBResources::getFile(filename);
|
|
if (ret) return ret;
|
|
|
|
warning("BFileManager::OpenFileRaw - Failed to open %s", filename.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::restoreCurrentDir() {
|
|
if (!_basePath) return S_OK;
|
|
else {
|
|
/*if (!chdir(_basePath)) return S_OK;
|
|
else return E_FAIL;*/
|
|
warning("CBFileManager::RestoreCurrentDir - ignored");
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
HRESULT CBFileManager::setBasePath(const Common::String &path) {
|
|
cleanup();
|
|
|
|
if (path.c_str()) {
|
|
_basePath = new char[path.size() + 1];
|
|
strcpy(_basePath, path.c_str());
|
|
}
|
|
|
|
initPaths();
|
|
registerPackages();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CBFileManager::findPackageSignature(Common::File *f, uint32 *offset) {
|
|
byte buf[32768];
|
|
|
|
byte signature[8];
|
|
((uint32 *)signature)[0] = PACKAGE_MAGIC_1;
|
|
((uint32 *)signature)[1] = PACKAGE_MAGIC_2;
|
|
|
|
uint32 fileSize = f->size();
|
|
|
|
int startPos = 1024 * 1024;
|
|
|
|
uint32 bytesRead = startPos;
|
|
|
|
while (bytesRead < fileSize - 16) {
|
|
int ToRead = MIN((unsigned int)32768, fileSize - bytesRead);
|
|
f->seek(startPos, SEEK_SET);
|
|
int ActuallyRead = f->read(buf, ToRead);
|
|
if (ActuallyRead != ToRead) return false;
|
|
|
|
for (int i = 0; i < ToRead - 8; i++)
|
|
if (!memcmp(buf + i, signature, 8)) {
|
|
*offset = startPos + i;
|
|
return true;
|
|
}
|
|
|
|
bytesRead = bytesRead + ToRead - 16;
|
|
startPos = startPos + ToRead - 16;
|
|
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
} // end of namespace WinterMute
|