mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 11:45:21 +00:00
added filesystem common code from scummvm (part 1)
This commit is contained in:
parent
0c1da3ed3a
commit
8c7012c803
47
common/error.h
Normal file
47
common/error.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_ERROR_H
|
||||
#define COMMON_ERROR_H
|
||||
|
||||
/**
|
||||
* This file contains enums with error codes commonly used.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Errors used in the SaveFileManager class.
|
||||
*/
|
||||
enum SFMError {
|
||||
SFM_NO_ERROR, //Default state, indicates no error has been recorded
|
||||
SFM_DIR_ACCESS, //stat(), mkdir()::EACCES: Search or write permission denied
|
||||
SFM_DIR_LINKMAX, //mkdir()::EMLINK: The link count of the parent directory would exceed {LINK_MAX}
|
||||
SFM_DIR_LOOP, //stat(), mkdir()::ELOOP: Too many symbolic links encountered while traversing the path
|
||||
SFM_DIR_NAMETOOLONG, //stat(), mkdir()::ENAMETOOLONG: The path name is too long
|
||||
SFM_DIR_NOENT, //stat(), mkdir()::ENOENT: A component of the path path does not exist, or the path is an empty string
|
||||
SFM_DIR_NOTDIR, //stat(), mkdir()::ENOTDIR: A component of the path prefix is not a directory
|
||||
SFM_DIR_ROFS //mkdir()::EROFS: The parent directory resides on a read-only file system
|
||||
};
|
||||
|
||||
#endif //COMMON_ERROR_H
|
542
common/file.cpp
Normal file
542
common/file.cpp
Normal file
@ -0,0 +1,542 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/util.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(MACOSX) || defined(IPHONE)
|
||||
#include "CoreFoundation/CoreFoundation.h"
|
||||
#endif
|
||||
|
||||
#ifdef __PLAYSTATION2__
|
||||
// for those replaced fopen/fread/etc functions
|
||||
typedef unsigned long uint64;
|
||||
typedef signed long int64;
|
||||
#include "backends/platform/ps2/fileio.h"
|
||||
|
||||
#define fopen(a, b) ps2_fopen(a, b)
|
||||
#define fclose(a) ps2_fclose(a)
|
||||
#define fseek(a, b, c) ps2_fseek(a, b, c)
|
||||
#define ftell(a) ps2_ftell(a)
|
||||
#define feof(a) ps2_feof(a)
|
||||
#define fread(a, b, c, d) ps2_fread(a, b, c, d)
|
||||
#define fwrite(a, b, c, d) ps2_fwrite(a, b, c, d)
|
||||
|
||||
//#define fprintf ps2_fprintf // used in common/util.cpp
|
||||
//#define fflush(a) ps2_fflush(a) // used in common/util.cpp
|
||||
|
||||
//#define fgetc(a) ps2_fgetc(a) // not used
|
||||
//#define fgets(a, b, c) ps2_fgets(a, b, c) // not used
|
||||
//#define fputc(a, b) ps2_fputc(a, b) // not used
|
||||
//#define fputs(a, b) ps2_fputs(a, b) // not used
|
||||
|
||||
//#define fsize(a) ps2_fsize(a) // not used -- and it is not a standard function either
|
||||
#endif
|
||||
|
||||
#ifdef __DS__
|
||||
|
||||
// These functions replease the standard library functions of the same name.
|
||||
// As this header is included after the standard one, I have the chance to #define
|
||||
// all of these to my own code.
|
||||
//
|
||||
// A #define is the only way, as redefinig the functions would cause linker errors.
|
||||
|
||||
// These functions need to be #undef'ed, as their original definition
|
||||
// in devkitarm is done with #includes (ugh!)
|
||||
#undef feof
|
||||
#undef clearerr
|
||||
//#undef getc
|
||||
//#undef ferror
|
||||
|
||||
#include "backends/fs/ds/ds-fs.h"
|
||||
|
||||
|
||||
//void std_fprintf(FILE* handle, const char* fmt, ...); // used in common/util.cpp
|
||||
//void std_fflush(FILE* handle); // used in common/util.cpp
|
||||
|
||||
//char* std_fgets(char* str, int size, FILE* file); // not used
|
||||
//int std_getc(FILE* handle); // not used
|
||||
//char* std_getcwd(char* dir, int dunno); // not used
|
||||
//void std_cwd(char* dir); // not used
|
||||
//int std_ferror(FILE* handle); // not used
|
||||
|
||||
// Only functions used in the ScummVM source have been defined here!
|
||||
#define fopen(name, mode) DS::std_fopen(name, mode)
|
||||
#define fclose(handle) DS::std_fclose(handle)
|
||||
#define fread(ptr, size, items, file) DS::std_fread(ptr, size, items, file)
|
||||
#define fwrite(ptr, size, items, file) DS::std_fwrite(ptr, size, items, file)
|
||||
#define feof(handle) DS::std_feof(handle)
|
||||
#define ftell(handle) DS::std_ftell(handle)
|
||||
#define fseek(handle, offset, whence) DS::std_fseek(handle, offset, whence)
|
||||
#define clearerr(handle) DS::std_clearerr(handle)
|
||||
|
||||
//#define printf(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__)
|
||||
|
||||
//#define fprintf(file, fmt, ...) { char str[128]; sprintf(str, fmt, ##__VA_ARGS__); DS::std_fwrite(str, strlen(str), 1, file); }
|
||||
//#define fflush(file) DS::std_fflush(file) // used in common/util.cpp
|
||||
|
||||
//#define fgets(str, size, file) DS::std_fgets(str, size, file) // not used
|
||||
//#define getc(handle) DS::std_getc(handle) // not used
|
||||
//#define getcwd(dir, dunno) DS::std_getcwd(dir, dunno) // not used
|
||||
//#define ferror(handle) DS::std_ferror(handle) // not used
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __SYMBIAN32__
|
||||
#undef feof
|
||||
#undef clearerr
|
||||
|
||||
#define FILE void
|
||||
|
||||
FILE* symbian_fopen(const char* name, const char* mode);
|
||||
void symbian_fclose(FILE* handle);
|
||||
size_t symbian_fread(const void* ptr, size_t size, size_t numItems, FILE* handle);
|
||||
size_t symbian_fwrite(const void* ptr, size_t size, size_t numItems, FILE* handle);
|
||||
bool symbian_feof(FILE* handle);
|
||||
long int symbian_ftell(FILE* handle);
|
||||
int symbian_fseek(FILE* handle, long int offset, int whence);
|
||||
void symbian_clearerr(FILE* handle);
|
||||
|
||||
// Only functions used in the ScummVM source have been defined here!
|
||||
#define fopen(name, mode) symbian_fopen(name, mode)
|
||||
#define fclose(handle) symbian_fclose(handle)
|
||||
#define fread(ptr, size, items, file) symbian_fread(ptr, size, items, file)
|
||||
#define fwrite(ptr, size, items, file) symbian_fwrite(ptr, size, items, file)
|
||||
#define feof(handle) symbian_feof(handle)
|
||||
#define ftell(handle) symbian_ftell(handle)
|
||||
#define fseek(handle, offset, whence) symbian_fseek(handle, offset, whence)
|
||||
#define clearerr(handle) symbian_clearerr(handle)
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
typedef HashMap<String, int> StringIntMap;
|
||||
|
||||
// The following two objects could be turned into static members of class
|
||||
// File. However, then we would be forced to #include hashmap in file.h
|
||||
// which seems to be a high price just for a simple beautification...
|
||||
static StringIntMap *_defaultDirectories;
|
||||
static StringMap *_filesMap;
|
||||
|
||||
static FILE *fopenNoCase(const String &filename, const String &directory, const char *mode) {
|
||||
FILE *file;
|
||||
String dirBuf(directory);
|
||||
String fileBuf(filename);
|
||||
|
||||
#if !defined(__GP32__) && !defined(PALMOS_MODE)
|
||||
// Add a trailing slash, if necessary.
|
||||
if (!dirBuf.empty()) {
|
||||
const char c = dirBuf.lastChar();
|
||||
if (c != ':' && c != '/' && c != '\\')
|
||||
dirBuf += '/';
|
||||
}
|
||||
#endif
|
||||
|
||||
// Append the filename to the path string
|
||||
String pathBuf(dirBuf);
|
||||
pathBuf += fileBuf;
|
||||
|
||||
//
|
||||
// Try to open the file normally
|
||||
//
|
||||
file = fopen(pathBuf.c_str(), mode);
|
||||
|
||||
//
|
||||
// Try again, with file name converted to upper case
|
||||
//
|
||||
if (!file) {
|
||||
fileBuf.toUppercase();
|
||||
pathBuf = dirBuf + fileBuf;
|
||||
file = fopen(pathBuf.c_str(), mode);
|
||||
}
|
||||
|
||||
//
|
||||
// Try again, with file name converted to lower case
|
||||
//
|
||||
if (!file) {
|
||||
fileBuf.toLowercase();
|
||||
pathBuf = dirBuf + fileBuf;
|
||||
file = fopen(pathBuf.c_str(), mode);
|
||||
}
|
||||
|
||||
//
|
||||
// Try again, with file name capitalized
|
||||
//
|
||||
if (!file) {
|
||||
fileBuf.toLowercase();
|
||||
fileBuf.setChar(toupper(fileBuf[0]),0);
|
||||
pathBuf = dirBuf + fileBuf;
|
||||
file = fopen(pathBuf.c_str(), mode);
|
||||
}
|
||||
|
||||
#ifdef __amigaos4__
|
||||
//
|
||||
// Work around for possibility that someone uses AmigaOS "newlib" build with SmartFileSystem (blocksize 512 bytes), leading
|
||||
// to buffer size being only 512 bytes. "Clib2" sets the buffer size to 8KB, resulting smooth movie playback. This forces the buffer
|
||||
// to be enough also when using "newlib" compile on SFS.
|
||||
//
|
||||
if (file) {
|
||||
setvbuf(file, NULL, _IOFBF, 8192);
|
||||
}
|
||||
#endif
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
void File::addDefaultDirectory(const String &directory) {
|
||||
FilesystemNode dir(directory);
|
||||
addDefaultDirectoryRecursive(dir, 1);
|
||||
}
|
||||
|
||||
void File::addDefaultDirectoryRecursive(const String &directory, int level, const String &prefix) {
|
||||
FilesystemNode dir(directory);
|
||||
addDefaultDirectoryRecursive(dir, level, prefix);
|
||||
}
|
||||
|
||||
void File::addDefaultDirectory(const FilesystemNode &directory) {
|
||||
addDefaultDirectoryRecursive(directory, 1);
|
||||
}
|
||||
|
||||
void File::addDefaultDirectoryRecursive(const FilesystemNode &dir, int level, const String &prefix) {
|
||||
if (level <= 0)
|
||||
return;
|
||||
|
||||
FSList fslist;
|
||||
if (!dir.getChildren(fslist, FilesystemNode::kListAll)) {
|
||||
// Failed listing the contents of this node, so it is either not a
|
||||
// directory, or just doesn't exist at all.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_defaultDirectories)
|
||||
_defaultDirectories = new StringIntMap;
|
||||
|
||||
// Do not add directories multiple times, unless this time they are added
|
||||
// with a bigger depth.
|
||||
const String &directory(dir.getPath());
|
||||
if (_defaultDirectories->contains(directory) && (*_defaultDirectories)[directory] >= level)
|
||||
return;
|
||||
(*_defaultDirectories)[directory] = level;
|
||||
|
||||
if (!_filesMap)
|
||||
_filesMap = new StringMap;
|
||||
|
||||
for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
if (file->isDirectory()) {
|
||||
addDefaultDirectoryRecursive(file->getPath(), level - 1, prefix + file->getName() + "/");
|
||||
} else {
|
||||
String lfn(prefix);
|
||||
lfn += file->getName();
|
||||
lfn.toLowercase();
|
||||
if (!_filesMap->contains(lfn)) {
|
||||
(*_filesMap)[lfn] = file->getPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void File::resetDefaultDirectories() {
|
||||
delete _defaultDirectories;
|
||||
delete _filesMap;
|
||||
|
||||
_defaultDirectories = 0;
|
||||
_filesMap = 0;
|
||||
}
|
||||
|
||||
File::File()
|
||||
: _handle(0), _ioFailed(false) {
|
||||
}
|
||||
|
||||
//#define DEBUG_FILE_REFCOUNT
|
||||
|
||||
File::~File() {
|
||||
#ifdef DEBUG_FILE_REFCOUNT
|
||||
warning("File::~File on file '%s'", _name.c_str());
|
||||
#endif
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool File::open(const String &filename, AccessMode mode) {
|
||||
assert(mode == kFileReadMode || mode == kFileWriteMode);
|
||||
|
||||
if (filename.empty()) {
|
||||
error("File::open: No filename was specified");
|
||||
}
|
||||
|
||||
if (_handle) {
|
||||
error("File::open: This file object already is opened (%s), won't open '%s'", _name.c_str(), filename.c_str());
|
||||
}
|
||||
|
||||
_name.clear();
|
||||
clearIOFailed();
|
||||
|
||||
String fname(filename);
|
||||
fname.toLowercase();
|
||||
|
||||
const char *modeStr = (mode == kFileReadMode) ? "rb" : "wb";
|
||||
if (mode == kFileWriteMode) {
|
||||
_handle = fopenNoCase(filename, "", modeStr);
|
||||
} else if (_filesMap && _filesMap->contains(fname)) {
|
||||
fname = (*_filesMap)[fname];
|
||||
//debug(3, "Opening hashed: %s", fname.c_str());
|
||||
_handle = fopen(fname.c_str(), modeStr);
|
||||
} else if (_filesMap && _filesMap->contains(fname + ".")) {
|
||||
// WORKAROUND: Bug #1458388: "SIMON1: Game Detection fails"
|
||||
// sometimes instead of "GAMEPC" we get "GAMEPC." (note trailing dot)
|
||||
fname = (*_filesMap)[fname + "."];
|
||||
//debug(3, "Opening hashed: %s", fname.c_str());
|
||||
_handle = fopen(fname.c_str(), modeStr);
|
||||
} else {
|
||||
|
||||
if (_defaultDirectories) {
|
||||
// Try all default directories
|
||||
StringIntMap::const_iterator x(_defaultDirectories->begin());
|
||||
for (; _handle == NULL && x != _defaultDirectories->end(); ++x) {
|
||||
_handle = fopenNoCase(filename, x->_key, modeStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: try the current directory
|
||||
if (_handle == NULL)
|
||||
_handle = fopenNoCase(filename, "", modeStr);
|
||||
|
||||
// Last last (really) resort: try looking inside the application bundle on Mac OS X for the lowercase file.
|
||||
#if defined(MACOSX) || defined(IPHONE)
|
||||
if (!_handle) {
|
||||
CFStringRef cfFileName = CFStringCreateWithBytes(NULL, (const UInt8 *)filename.c_str(), filename.size(), kCFStringEncodingASCII, false);
|
||||
CFURLRef fileUrl = CFBundleCopyResourceURL(CFBundleGetMainBundle(), cfFileName, NULL, NULL);
|
||||
if (fileUrl) {
|
||||
UInt8 buf[256];
|
||||
if (CFURLGetFileSystemRepresentation(fileUrl, false, (UInt8 *)buf, 256)) {
|
||||
_handle = fopen((char *)buf, modeStr);
|
||||
}
|
||||
CFRelease(fileUrl);
|
||||
}
|
||||
CFRelease(cfFileName);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (_handle == NULL) {
|
||||
/* if (mode == kFileReadMode)
|
||||
debug(2, "File %s not found", filename.c_str());
|
||||
else
|
||||
debug(2, "File %s not opened", filename.c_str());*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_name = filename;
|
||||
|
||||
#ifdef DEBUG_FILE_REFCOUNT
|
||||
warning("File::open on file '%s'", _name.c_str());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::open(const FilesystemNode &node, AccessMode mode) {
|
||||
assert(mode == kFileReadMode || mode == kFileWriteMode);
|
||||
|
||||
if (!node.exists()) {
|
||||
warning("File::open: Trying to open a FilesystemNode which does not exist");
|
||||
return false;
|
||||
} else if (node.isDirectory()) {
|
||||
warning("File::open: Trying to open a FilesystemNode which is a directory");
|
||||
return false;
|
||||
} /*else if (!node.isReadable() && mode == kFileReadMode) {
|
||||
warning("File::open: Trying to open an unreadable FilesystemNode object for reading");
|
||||
return false;
|
||||
} else if (!node.isWritable() && mode == kFileWriteMode) {
|
||||
warning("File::open: Trying to open an unwritable FilesystemNode object for writing");
|
||||
return false;
|
||||
}*/
|
||||
|
||||
String filename(node.getName());
|
||||
|
||||
if (_handle) {
|
||||
error("File::open: This file object already is opened (%s), won't open '%s'", _name.c_str(), filename.c_str());
|
||||
}
|
||||
|
||||
clearIOFailed();
|
||||
_name.clear();
|
||||
|
||||
const char *modeStr = (mode == kFileReadMode) ? "rb" : "wb";
|
||||
|
||||
_handle = fopen(node.getPath().c_str(), modeStr);
|
||||
|
||||
if (_handle == NULL) {
|
||||
/* if (mode == kFileReadMode)
|
||||
debug(2, "File %s not found", filename.c_str());
|
||||
else
|
||||
debug(2, "File %s not opened", filename.c_str());*/
|
||||
return false;
|
||||
}
|
||||
|
||||
_name = filename;
|
||||
|
||||
#ifdef DEBUG_FILE_REFCOUNT
|
||||
warning("File::open on file '%s'", _name.c_str());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::exists(const String &filename) {
|
||||
// First try to find the file via a FilesystemNode (in case an absolute
|
||||
// path was passed). This is only used to filter out directories.
|
||||
FilesystemNode file(filename);
|
||||
if (file.exists())
|
||||
return !file.isDirectory();
|
||||
|
||||
// See if the file is already mapped
|
||||
if (_filesMap && _filesMap->contains(filename)) {
|
||||
FilesystemNode file2((*_filesMap)[filename]);
|
||||
|
||||
if (file2.exists())
|
||||
return !file2.isDirectory();
|
||||
}
|
||||
|
||||
// Try all default directories
|
||||
if (_defaultDirectories) {
|
||||
StringIntMap::const_iterator i(_defaultDirectories->begin());
|
||||
for (; i != _defaultDirectories->end(); ++i) {
|
||||
FilesystemNode file2(i->_key + filename);
|
||||
|
||||
if(file2.exists())
|
||||
return !file2.isDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
//Try opening the file inside the local directory as a last resort
|
||||
File tmp;
|
||||
return tmp.open(filename, kFileReadMode);
|
||||
}
|
||||
|
||||
void File::close() {
|
||||
if (_handle)
|
||||
fclose((FILE *)_handle);
|
||||
_handle = NULL;
|
||||
}
|
||||
|
||||
bool File::isOpen() const {
|
||||
return _handle != NULL;
|
||||
}
|
||||
|
||||
bool File::ioFailed() const {
|
||||
return _ioFailed != 0;
|
||||
}
|
||||
|
||||
void File::clearIOFailed() {
|
||||
_ioFailed = false;
|
||||
}
|
||||
|
||||
bool File::eof() const {
|
||||
if (_handle == NULL) {
|
||||
error("File::eof: File is not open!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return feof((FILE *)_handle) != 0;
|
||||
}
|
||||
|
||||
uint32 File::pos() const {
|
||||
if (_handle == NULL) {
|
||||
error("File::pos: File is not open!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ftell((FILE *)_handle);
|
||||
}
|
||||
|
||||
uint32 File::size() const {
|
||||
if (_handle == NULL) {
|
||||
error("File::size: File is not open!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 oldPos = ftell((FILE *)_handle);
|
||||
fseek((FILE *)_handle, 0, SEEK_END);
|
||||
uint32 length = ftell((FILE *)_handle);
|
||||
fseek((FILE *)_handle, oldPos, SEEK_SET);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void File::seek(int32 offs, int whence) {
|
||||
if (_handle == NULL) {
|
||||
error("File::seek: File is not open!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fseek((FILE *)_handle, offs, whence) != 0)
|
||||
clearerr((FILE *)_handle);
|
||||
}
|
||||
|
||||
uint32 File::read(void *ptr, uint32 len) {
|
||||
byte *ptr2 = (byte *)ptr;
|
||||
uint32 real_len;
|
||||
|
||||
if (_handle == NULL) {
|
||||
error("File::read: File is not open!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
real_len = fread(ptr2, 1, len, (FILE *)_handle);
|
||||
if (real_len < len) {
|
||||
_ioFailed = true;
|
||||
}
|
||||
|
||||
return real_len;
|
||||
}
|
||||
|
||||
uint32 File::write(const void *ptr, uint32 len) {
|
||||
if (_handle == NULL) {
|
||||
error("File::write: File is not open!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if ((uint32)fwrite(ptr, 1, len, (FILE *)_handle) != len) {
|
||||
_ioFailed = true;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
} // End of namespace Common
|
122
common/file.h
Normal file
122
common/file.h
Normal file
@ -0,0 +1,122 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_FILE_H
|
||||
#define COMMON_FILE_H
|
||||
|
||||
#include "common/sys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
class FilesystemNode;
|
||||
|
||||
namespace Common {
|
||||
|
||||
class File : public SeekableReadStream, public WriteStream {
|
||||
protected:
|
||||
/** File handle to the actual file; 0 if no file is open. */
|
||||
void *_handle;
|
||||
|
||||
/** Status flag which tells about recent I/O failures. */
|
||||
bool _ioFailed;
|
||||
|
||||
/** The name of this file, for debugging. */
|
||||
String _name;
|
||||
|
||||
private:
|
||||
// Disallow copying File objects. There is not strict reason for this,
|
||||
// except that so far we never had real need for such a feature, and
|
||||
// code that accidentally copied File objects tended to break in strange
|
||||
// ways.
|
||||
File(const File &f);
|
||||
File &operator =(const File &f);
|
||||
|
||||
public:
|
||||
enum AccessMode {
|
||||
kFileReadMode = 1,
|
||||
kFileWriteMode = 2
|
||||
};
|
||||
|
||||
static void addDefaultDirectory(const String &directory);
|
||||
static void addDefaultDirectoryRecursive(const String &directory, int level = 4, const String &prefix = "");
|
||||
|
||||
static void addDefaultDirectory(const FilesystemNode &directory);
|
||||
static void addDefaultDirectoryRecursive(const FilesystemNode &directory, int level = 4, const String &prefix = "");
|
||||
|
||||
static void resetDefaultDirectories();
|
||||
|
||||
File();
|
||||
virtual ~File();
|
||||
|
||||
/**
|
||||
* Checks if a given file exists in any of the current default paths
|
||||
* (those were/are added by addDefaultDirectory and/or
|
||||
* addDefaultDirectoryRecursive).
|
||||
*
|
||||
* @param filename: the file to check for
|
||||
* @return: true if the file exists, else false
|
||||
*/
|
||||
static bool exists(const String &filename);
|
||||
|
||||
virtual bool open(const String &filename, AccessMode mode = kFileReadMode);
|
||||
virtual bool open(const FilesystemNode &node, AccessMode mode = kFileReadMode);
|
||||
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* Checks if the object opened a file successfully.
|
||||
*
|
||||
* @return: true if any file is opened, false otherwise.
|
||||
*/
|
||||
bool isOpen() const;
|
||||
|
||||
/**
|
||||
* Returns the filename of the opened file.
|
||||
*
|
||||
* @return: the filename
|
||||
*/
|
||||
const char *name() const { return _name.c_str(); }
|
||||
|
||||
bool ioFailed() const;
|
||||
void clearIOFailed();
|
||||
bool eos() const { return eof(); }
|
||||
|
||||
/**
|
||||
* Checks for end of file.
|
||||
*
|
||||
* @return: true if the end of file is reached, false otherwise.
|
||||
*/
|
||||
virtual bool eof() const;
|
||||
|
||||
virtual uint32 pos() const;
|
||||
virtual uint32 size() const;
|
||||
void seek(int32 offs, int whence = SEEK_SET);
|
||||
uint32 read(void *dataPtr, uint32 dataSize);
|
||||
uint32 write(const void *dataPtr, uint32 dataSize);
|
||||
};
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
205
common/fs.cpp
Normal file
205
common/fs.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "common/util.h"
|
||||
#include "engine/backend/driver.h"
|
||||
#include "engine/backend/fs/abstract-fs.h"
|
||||
#include "engine/backend/fs/fs-factory.h"
|
||||
|
||||
static bool matchString(const char *str, const char *pat) {
|
||||
const char *p = 0;
|
||||
const char *q = 0;
|
||||
|
||||
for (;;) {
|
||||
switch (*pat) {
|
||||
case '*':
|
||||
p = ++pat;
|
||||
q = str;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (*pat != *str) {
|
||||
if (p) {
|
||||
pat = p;
|
||||
str = ++q;
|
||||
if (!*str)
|
||||
return !*pat;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
// fallthrough
|
||||
case '?':
|
||||
if (!*str)
|
||||
return !*pat;
|
||||
pat++;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilesystemNode::FilesystemNode() {
|
||||
}
|
||||
|
||||
FilesystemNode::FilesystemNode(AbstractFilesystemNode *realNode)
|
||||
: _realNode(realNode) {
|
||||
}
|
||||
|
||||
FilesystemNode::FilesystemNode(const Common::String &p) {
|
||||
FilesystemFactory *factory = g_driver->getFilesystemFactory();
|
||||
AbstractFilesystemNode *tmp = 0;
|
||||
|
||||
if (p.empty() || p == ".")
|
||||
tmp = factory->makeCurrentDirectoryFileNode();
|
||||
else
|
||||
tmp = factory->makeFileNodePath(p);
|
||||
_realNode = Common::SharedPtr<AbstractFilesystemNode>(tmp);
|
||||
}
|
||||
|
||||
bool FilesystemNode::operator<(const FilesystemNode& node) const {
|
||||
if (isDirectory() != node.isDirectory())
|
||||
return isDirectory();
|
||||
|
||||
return strcasecmp(getDisplayName().c_str(), node.getDisplayName().c_str()) < 0;
|
||||
}
|
||||
|
||||
bool FilesystemNode::exists() const {
|
||||
if (_realNode == 0)
|
||||
return false;
|
||||
|
||||
return _realNode->exists();
|
||||
}
|
||||
|
||||
FilesystemNode FilesystemNode::getChild(const Common::String &n) const {
|
||||
if (_realNode == 0)
|
||||
return *this;
|
||||
|
||||
assert(_realNode->isDirectory());
|
||||
AbstractFilesystemNode *node = _realNode->getChild(n);
|
||||
return FilesystemNode(node);
|
||||
}
|
||||
|
||||
bool FilesystemNode::getChildren(FSList &fslist, ListMode mode, bool hidden) const {
|
||||
if (!_realNode || !_realNode->isDirectory())
|
||||
return false;
|
||||
|
||||
AbstractFSList tmp;
|
||||
|
||||
if (!_realNode->getChildren(tmp, mode, hidden))
|
||||
return false;
|
||||
|
||||
fslist.clear();
|
||||
for (AbstractFSList::iterator i = tmp.begin(); i != tmp.end(); ++i) {
|
||||
fslist.push_back(FilesystemNode(*i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String FilesystemNode::getDisplayName() const {
|
||||
assert(_realNode);
|
||||
return _realNode->getDisplayName();
|
||||
}
|
||||
|
||||
Common::String FilesystemNode::getName() const {
|
||||
assert(_realNode);
|
||||
return _realNode->getName();
|
||||
}
|
||||
|
||||
FilesystemNode FilesystemNode::getParent() const {
|
||||
if (_realNode == 0)
|
||||
return *this;
|
||||
|
||||
AbstractFilesystemNode *node = _realNode->getParent();
|
||||
if (node == 0) {
|
||||
return *this;
|
||||
} else {
|
||||
return FilesystemNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
Common::String FilesystemNode::getPath() const {
|
||||
assert(_realNode);
|
||||
return _realNode->getPath();
|
||||
}
|
||||
|
||||
bool FilesystemNode::isDirectory() const {
|
||||
if (_realNode == 0)
|
||||
return false;
|
||||
|
||||
return _realNode->isDirectory();
|
||||
}
|
||||
|
||||
bool FilesystemNode::isReadable() const {
|
||||
if (_realNode == 0)
|
||||
return false;
|
||||
|
||||
return _realNode->isReadable();
|
||||
}
|
||||
|
||||
bool FilesystemNode::isWritable() const {
|
||||
if (_realNode == 0)
|
||||
return false;
|
||||
|
||||
return _realNode->isWritable();
|
||||
}
|
||||
|
||||
bool FilesystemNode::lookupFile(FSList &results, const Common::String &p, bool hidden, bool exhaustive, int depth) const {
|
||||
if (!isDirectory())
|
||||
return false;
|
||||
|
||||
FSList children;
|
||||
FSList subdirs;
|
||||
Common::String pattern = p;
|
||||
|
||||
pattern.toUppercase();
|
||||
|
||||
// First match all files on this level
|
||||
getChildren(children, FilesystemNode::kListAll, hidden);
|
||||
for (FSList::iterator entry = children.begin(); entry != children.end(); ++entry) {
|
||||
if (entry->isDirectory()) {
|
||||
if (depth != 0)
|
||||
subdirs.push_back(*entry);
|
||||
} else {
|
||||
Common::String filename = entry->getName();
|
||||
filename.toUppercase();
|
||||
if (matchString(filename.c_str(), pattern.c_str())) {
|
||||
results.push_back(*entry);
|
||||
|
||||
if (!exhaustive)
|
||||
return true; // Abort on first match if no exhaustive search was requested
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now scan all subdirs
|
||||
for (FSList::iterator child = subdirs.begin(); child != subdirs.end(); ++child) {
|
||||
child->lookupFile(results, pattern, hidden, exhaustive, depth - 1);
|
||||
if (!exhaustive && !results.empty())
|
||||
return true; // Abort on first match if no exhaustive search was requested
|
||||
}
|
||||
|
||||
return !results.empty();
|
||||
}
|
228
common/fs.h
Normal file
228
common/fs.h
Normal file
@ -0,0 +1,228 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef COMMON_FS_H
|
||||
#define COMMON_FS_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
|
||||
//namespace Common {
|
||||
|
||||
class FilesystemNode;
|
||||
class AbstractFilesystemNode;
|
||||
|
||||
/**
|
||||
* List of multiple file system nodes. E.g. the contents of a given directory.
|
||||
* This is subclass instead of just a typedef so that we can use forward
|
||||
* declarations of it in other places.
|
||||
*/
|
||||
class FSList : public Common::Array<FilesystemNode> {};
|
||||
|
||||
/**
|
||||
* FilesystemNode provides an abstraction for file paths, allowing for portable
|
||||
* file system browsing. To this ends, multiple or single roots have to be supported
|
||||
* (compare Unix with a single root, Windows with multiple roots C:, D:, ...).
|
||||
*
|
||||
* To this end, we abstract away from paths; implementations can be based on
|
||||
* paths (and it's left to them whether / or \ or : is the path separator :-);
|
||||
* but it is also possible to use inodes or vrefs (MacOS 9) or anything else.
|
||||
*
|
||||
* NOTE: Backends still have to provide a way to extract a path from a FSIntern
|
||||
*
|
||||
* You may ask now: "isn't this cheating? Why do we go through all this when we use
|
||||
* a path in the end anyway?!?".
|
||||
* Well, for once as long as we don't provide our own file open/read/write API, we
|
||||
* still have to use fopen(). Since all our targets already support fopen(), it should
|
||||
* be possible to get a fopen() compatible string for any file system node.
|
||||
*
|
||||
* Secondly, with this abstraction layer, we still avoid a lot of complications based on
|
||||
* differences in FS roots, different path separators, or even systems with no real
|
||||
* paths (MacOS 9 doesn't even have the notion of a "current directory").
|
||||
* And if we ever want to support devices with no FS in the classical sense (Palm...),
|
||||
* we can build upon this.
|
||||
*
|
||||
* This class acts as a wrapper around the AbstractFilesystemNode class defined in backends/fs.
|
||||
*/
|
||||
class FilesystemNode {
|
||||
private:
|
||||
Common::SharedPtr<AbstractFilesystemNode> _realNode;
|
||||
FilesystemNode(AbstractFilesystemNode *realNode);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Flag to tell listDir() which kind of files to list.
|
||||
*/
|
||||
enum ListMode {
|
||||
kListFilesOnly = 1,
|
||||
kListDirectoriesOnly = 2,
|
||||
kListAll = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new pathless FilesystemNode. Since there's no path associated
|
||||
* with this node, path-related operations (i.e. exists(), isDirectory(),
|
||||
* getPath()) will always return false or raise an assertion.
|
||||
*/
|
||||
FilesystemNode();
|
||||
|
||||
/**
|
||||
* Create a new FilesystemNode referring to the specified path. This is
|
||||
* the counterpart to the path() method.
|
||||
*
|
||||
* If path is empty or equals ".", then a node representing the "current
|
||||
* directory" will be created. If that is not possible (since e.g. the
|
||||
* operating system doesn't support the concept), some other directory is
|
||||
* used (usually the root directory).
|
||||
*/
|
||||
explicit FilesystemNode(const Common::String &path);
|
||||
|
||||
virtual ~FilesystemNode() {}
|
||||
|
||||
/**
|
||||
* Compare the name of this node to the name of another. Directories
|
||||
* go before normal files.
|
||||
*/
|
||||
bool operator<(const FilesystemNode& node) const;
|
||||
|
||||
/**
|
||||
* Indicates whether the object referred by this path exists in the filesystem or not.
|
||||
*
|
||||
* @return bool true if the path exists, false otherwise.
|
||||
*/
|
||||
virtual bool exists() const;
|
||||
|
||||
/**
|
||||
* Fetch a child node of this node, with the given name. Only valid for
|
||||
* directory nodes (an assertion is triggered otherwise).
|
||||
* If no child node with the given name exists, an invalid node is returned.
|
||||
*/
|
||||
FilesystemNode getChild(const Common::String &name) const;
|
||||
|
||||
/**
|
||||
* Return a list of child nodes of this directory node. If called on a node
|
||||
* that does not represent a directory, false is returned.
|
||||
*
|
||||
* @return true if succesful, false otherwise (e.g. when the directory does not exist).
|
||||
*/
|
||||
virtual bool getChildren(FSList &fslist, ListMode mode = kListDirectoriesOnly, bool hidden = false) const;
|
||||
|
||||
/**
|
||||
* Return a human readable string for this node, usable for display (e.g.
|
||||
* in the GUI code). Do *not* rely on it being usable for anything else,
|
||||
* like constructing paths!
|
||||
*
|
||||
* @return the display name
|
||||
*/
|
||||
virtual Common::String getDisplayName() const;
|
||||
|
||||
/**
|
||||
* Return a string representation of the name of the file. This is can be
|
||||
* used e.g. by detection code that relies on matching the name of a given
|
||||
* file. But it is *not* suitable for use with fopen / File::open, nor
|
||||
* should it be archived.
|
||||
*
|
||||
* @return the file name
|
||||
*/
|
||||
virtual Common::String getName() const;
|
||||
|
||||
/**
|
||||
* Return a string representation of the file which can be passed to fopen(),
|
||||
* and is suitable for archiving (i.e. writing to the config file).
|
||||
* This will usually be a 'path' (hence the name of the method), but can
|
||||
* be anything that fulfills the above criterions.
|
||||
*
|
||||
* @note Do not assume that this string contains (back)slashes or any
|
||||
* other kind of 'path separators'.
|
||||
*
|
||||
* @return the 'path' represented by this filesystem node
|
||||
*/
|
||||
virtual Common::String getPath() const;
|
||||
|
||||
/**
|
||||
* Get the parent node of this node. If this node has no parent node,
|
||||
* then it returns a duplicate of this node.
|
||||
*/
|
||||
FilesystemNode getParent() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the path refers to a directory or not.
|
||||
*
|
||||
* @todo Currently we assume that a node that is not a directory
|
||||
* automatically is a file (ignoring things like symlinks or pipes).
|
||||
* That might actually be OK... but we could still add an isFile method.
|
||||
* Or even replace isDirectory by a getType() method that can return values like
|
||||
* kDirNodeType, kFileNodeType, kInvalidNodeType.
|
||||
*/
|
||||
virtual bool isDirectory() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the object referred by this path can be read from or not.
|
||||
*
|
||||
* If the path refers to a directory, readability implies being able to read
|
||||
* and list the directory entries.
|
||||
*
|
||||
* If the path refers to a file, readability implies being able to read the
|
||||
* contents of the file.
|
||||
*
|
||||
* @return bool true if the object can be read, false otherwise.
|
||||
*/
|
||||
virtual bool isReadable() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the object referred by this path can be written to or not.
|
||||
*
|
||||
* If the path refers to a directory, writability implies being able to modify
|
||||
* the directory entry (i.e. rename the directory, remove it or write files inside of it).
|
||||
*
|
||||
* If the path refers to a file, writability implies being able to write data
|
||||
* to the file.
|
||||
*
|
||||
* @return bool true if the object can be written to, false otherwise.
|
||||
*/
|
||||
virtual bool isWritable() const;
|
||||
|
||||
/**
|
||||
* Searches recursively for files matching the specified pattern inside this directory and
|
||||
* all its subdirectories. It is safe to call this method for non-directories, in this case
|
||||
* it will just return false.
|
||||
*
|
||||
* The files in each directory are scanned first. Other than that, a depth first search
|
||||
* is performed.
|
||||
*
|
||||
* @param results List to put the matches in.
|
||||
* @param pattern Pattern of the files to look for.
|
||||
* @param hidden Whether to search hidden files or not.
|
||||
* @param exhaustive Whether to continue searching after one match has been found.
|
||||
* @param depth How many levels to search through (-1 = search all subdirs, 0 = only the current one)
|
||||
*
|
||||
* @return true if matches could be found, false otherwise.
|
||||
*/
|
||||
virtual bool lookupFile(FSList &results, const Common::String &pattern, bool hidden, bool exhaustive, int depth = -1) const;
|
||||
};
|
||||
|
||||
//} // End of namespace Common
|
||||
|
||||
#endif //COMMON_FS_H
|
@ -2,11 +2,14 @@ MODULE := common
|
||||
|
||||
MODULE_OBJS := \
|
||||
debug.o \
|
||||
file.o \
|
||||
fs.o \
|
||||
hashmap.o \
|
||||
matrix3.o \
|
||||
matrix4.o \
|
||||
memorypool.o \
|
||||
str.o \
|
||||
stream.o \
|
||||
mutex.o
|
||||
|
||||
# Include common rules
|
||||
|
215
common/ptr.h
Normal file
215
common/ptr.h
Normal file
@ -0,0 +1,215 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef COMMON_PTR_H
|
||||
#define COMMON_PTR_H
|
||||
|
||||
#include "common/sys.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class SharedPtrDeletionInternal {
|
||||
public:
|
||||
virtual ~SharedPtrDeletionInternal() {}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class SharedPtrDeletionImpl : public SharedPtrDeletionInternal {
|
||||
public:
|
||||
SharedPtrDeletionImpl(T *ptr) : _ptr(ptr) {}
|
||||
~SharedPtrDeletionImpl() {
|
||||
// Checks if the supplied type is not just a plain
|
||||
// forward definition, taken from boost::checked_delete
|
||||
// This makes the user really aware what he tries to do
|
||||
// when using this with an incomplete type.
|
||||
typedef char completeCheck[sizeof(T) ? 1 : -1];
|
||||
(void)sizeof(completeCheck);
|
||||
delete _ptr;
|
||||
}
|
||||
private:
|
||||
T *_ptr;
|
||||
};
|
||||
|
||||
template<class T, class D>
|
||||
class SharedPtrDeletionDeleterImpl : public SharedPtrDeletionInternal {
|
||||
public:
|
||||
SharedPtrDeletionDeleterImpl(T *ptr, D d) : _ptr(ptr), _deleter(d) {}
|
||||
~SharedPtrDeletionDeleterImpl() { _deleter(_ptr); }
|
||||
private:
|
||||
T *_ptr;
|
||||
D _deleter;
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple shared pointer implementation modelled after boost.
|
||||
*
|
||||
* This object keeps track of the assigned pointer and automatically
|
||||
* frees it when no more SharedPtr references to it exist.
|
||||
*
|
||||
* To achieve that the object implements an internal reference counting.
|
||||
* Thus you should try to avoid using the plain pointer after assigning
|
||||
* it to a SharedPtr object for the first time. If you still use the
|
||||
* plain pointer be sure you do not delete it on your own. You may also
|
||||
* not use the plain pointer to create a new SharedPtr object, since that
|
||||
* would result in a double deletion of the pointer sooner or later.
|
||||
*
|
||||
* Example creation:
|
||||
* Common::SharedPtr<int> pointer(new int(1));
|
||||
* would create a pointer to int. Later on usage via *pointer is the same
|
||||
* as for a normal pointer. If you need to access the plain pointer value
|
||||
* itself later on use the get method. The class also supplies a operator
|
||||
* ->, which does the same as the -> operator on a normal pointer.
|
||||
*
|
||||
* Be sure you are using new to initialize the pointer you want to manage.
|
||||
* If you do not use new for allocating, you have to supply a deleter as
|
||||
* second parameter when creating a SharedPtr object. The deleter has to
|
||||
* implement operator() which takes the pointer it should free as argument.
|
||||
*
|
||||
* Note that you have to specify the type itself not the pointer type as
|
||||
* template parameter.
|
||||
*
|
||||
* When creating a SharedPtr object from a normal pointer you need a real
|
||||
* definition of the type you want SharedPtr to manage, a simple forward
|
||||
* definition is not enough.
|
||||
*
|
||||
* The class has implicit upcast support, so if you got a class B derived
|
||||
* from class A, you can assign a pointer to B without any problems to a
|
||||
* SharedPtr object with template parameter A. The very same applies to
|
||||
* assignment of a SharedPtr<B> object to a SharedPtr<A> object.
|
||||
*
|
||||
* There are also operators != and == to compare two SharedPtr objects
|
||||
* with compatible pointers. Comparison between a SharedPtr object and
|
||||
* a plain pointer is only possible via SharedPtr::get.
|
||||
*/
|
||||
template<class T>
|
||||
class SharedPtr {
|
||||
#if !((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95))
|
||||
template<class T2> friend class SharedPtr;
|
||||
#endif
|
||||
public:
|
||||
typedef int RefValue;
|
||||
typedef T ValueType;
|
||||
typedef T *Pointer;
|
||||
|
||||
SharedPtr() : _refCount(0), _deletion(0), _pointer(0) {}
|
||||
template<class T2> explicit SharedPtr(T2 *p) : _refCount(new RefValue(1)), _deletion(new SharedPtrDeletionImpl<T2>(p)), _pointer(p) {}
|
||||
template<class T2, class D> SharedPtr(T2 *p, D d) : _refCount(new RefValue(1)), _deletion(new SharedPtrDeletionDeleterImpl<T2, D>(p, d)), _pointer(p) {}
|
||||
|
||||
SharedPtr(const SharedPtr &r) : _refCount(r._refCount), _deletion(r._deletion), _pointer(r._pointer) { if (_refCount) ++(*_refCount); }
|
||||
template<class T2> SharedPtr(const SharedPtr<T2> &r) : _refCount(r._refCount), _deletion(r._deletion), _pointer(r._pointer) { if (_refCount) ++(*_refCount); }
|
||||
|
||||
~SharedPtr() { decRef(); }
|
||||
|
||||
SharedPtr &operator =(const SharedPtr &r) {
|
||||
if (r._refCount)
|
||||
++(*r._refCount);
|
||||
decRef();
|
||||
|
||||
_refCount = r._refCount;
|
||||
_deletion = r._deletion;
|
||||
_pointer = r._pointer;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T2>
|
||||
SharedPtr &operator =(const SharedPtr<T2> &r) {
|
||||
if (r._refCount)
|
||||
++(*r._refCount);
|
||||
decRef();
|
||||
|
||||
_refCount = r._refCount;
|
||||
_deletion = r._deletion;
|
||||
_pointer = r._pointer;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueType &operator *() const { assert(_pointer); return *_pointer; }
|
||||
Pointer operator ->() const { assert(_pointer); return _pointer; }
|
||||
|
||||
/**
|
||||
* Returns the plain pointer value. Be sure you know what you
|
||||
* do if you are continuing to use that pointer.
|
||||
*
|
||||
* @return the pointer the SharedPtr object manages
|
||||
*/
|
||||
Pointer get() const { return _pointer; }
|
||||
|
||||
/**
|
||||
* Implicit conversion operator to bool for convenience, to make
|
||||
* checks like "if (sharedPtr) ..." possible.
|
||||
*/
|
||||
operator bool() const { return _pointer != 0; }
|
||||
|
||||
/**
|
||||
* Checks if the SharedPtr object is the only object refering
|
||||
* to the assigned pointer. This should just be used for
|
||||
* debugging purposes.
|
||||
*/
|
||||
bool unique() const { return refCount() == 1; }
|
||||
|
||||
/**
|
||||
* Returns the number of references to the assigned pointer.
|
||||
* This should just be used for debugging purposes.
|
||||
*/
|
||||
RefValue refCount() const { return _refCount ? *_refCount : 0; }
|
||||
#if !((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95))
|
||||
private:
|
||||
#endif
|
||||
void decRef() {
|
||||
if (_refCount) {
|
||||
--(*_refCount);
|
||||
if (!*_refCount) {
|
||||
delete _refCount;
|
||||
delete _deletion;
|
||||
_deletion = 0;
|
||||
_refCount = 0;
|
||||
_pointer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefValue *_refCount;
|
||||
SharedPtrDeletionInternal *_deletion;
|
||||
T *_pointer;
|
||||
};
|
||||
|
||||
} // end of namespace Common
|
||||
|
||||
template<class T1, class T2>
|
||||
bool operator ==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
|
||||
return l.get() == r.get();
|
||||
}
|
||||
|
||||
template<class T1, class T2>
|
||||
bool operator !=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
|
||||
return l.get() != r.get();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
163
common/savefile.h
Normal file
163
common/savefile.h
Normal file
@ -0,0 +1,163 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_SAVEFILE_H
|
||||
#define COMMON_SAVEFILE_H
|
||||
|
||||
#include "common/noncopyable.h"
|
||||
#include "common/sys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/str.h"
|
||||
#include "common/error.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* A class which allows game engines to load game state data.
|
||||
* That typically means "save games", but also includes things like the
|
||||
* IQ points in Indy3.
|
||||
*/
|
||||
class InSaveFile : public SeekableReadStream {};
|
||||
|
||||
/**
|
||||
* A class which allows game engines to save game state data.
|
||||
* That typically means "save games", but also includes things like the
|
||||
* IQ points in Indy3.
|
||||
*/
|
||||
class OutSaveFile : public WriteStream {
|
||||
public:
|
||||
/**
|
||||
* Close this savefile, to be called right before destruction of this
|
||||
* savefile. The idea is that this ways, I/O errors that occur
|
||||
* during closing/flushing of the file can still be handled by the
|
||||
* game engine.
|
||||
*
|
||||
* By default, this just flushes the stream.
|
||||
*/
|
||||
virtual void finalize() {
|
||||
flush();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The SaveFileManager is serving as a factory for InSaveFile
|
||||
* and OutSaveFile objects.
|
||||
*
|
||||
* Engines and other code should use SaveFiles whenever they need to
|
||||
* store data which they need to be able to retrieve again later on --
|
||||
* i.e. typically save states, but also configuration files and similar
|
||||
* things.
|
||||
*
|
||||
* While not declared as a singleton, it is effectively used as such,
|
||||
* with OSystem::getSavefileManager returning a pointer to the single
|
||||
* SaveFileManager instances to be used.
|
||||
*/
|
||||
class SaveFileManager : NonCopyable {
|
||||
|
||||
protected:
|
||||
SFMError _error;
|
||||
String _errorDesc;
|
||||
|
||||
/**
|
||||
* Set some information about the last error which occurred .
|
||||
* @param error Code identifying the last error.
|
||||
* @param errorDesc String describing the last error.
|
||||
*/
|
||||
virtual void setError(SFMError error, const String &errorDesc) { _error = error; _errorDesc = errorDesc; }
|
||||
|
||||
public:
|
||||
virtual ~SaveFileManager() {}
|
||||
|
||||
/**
|
||||
* Clears the last set error code and string.
|
||||
*/
|
||||
virtual void clearError() { _error = SFM_NO_ERROR; _errorDesc = ""; }
|
||||
|
||||
/**
|
||||
* Returns the last occurred error code. If none occurred, returns SFM_NO_ERROR.
|
||||
*
|
||||
* @return A SFMError indicating the type of the last error.
|
||||
*/
|
||||
virtual SFMError getError() { return _error; }
|
||||
|
||||
/**
|
||||
* Returns the last occurred error description. If none occurred, returns 0.
|
||||
*
|
||||
* @return A string describing the last error.
|
||||
*/
|
||||
virtual String getErrorDesc() { return _errorDesc; }
|
||||
|
||||
/**
|
||||
* Returns the last occurred error description. If none occurred, returns 0.
|
||||
* Also clears the last error state and description.
|
||||
*
|
||||
* @return A string describing the last error.
|
||||
*/
|
||||
virtual String popErrorDesc();
|
||||
|
||||
/**
|
||||
* Open the savefile with the specified name in the given directory for saving.
|
||||
* @param name the name of the savefile
|
||||
* @return pointer to an OutSaveFile, or NULL if an error occured.
|
||||
*/
|
||||
virtual OutSaveFile *openForSaving(const char *name) = 0;
|
||||
|
||||
/**
|
||||
* Open the file with the specified name in the given directory for loading.
|
||||
* @param name the name of the savefile
|
||||
* @return pointer to an InSaveFile, or NULL if an error occured.
|
||||
*/
|
||||
virtual InSaveFile *openForLoading(const char *name) = 0;
|
||||
|
||||
/**
|
||||
* Removes the given savefile from the system.
|
||||
* @param name the name of the savefile to be removed.
|
||||
* @return true if no error occurred, false otherwise.
|
||||
*/
|
||||
virtual bool removeSavefile(const char *name) = 0;
|
||||
|
||||
/**
|
||||
* Renames the given savefile.
|
||||
* @param oldName Old name.
|
||||
* @param newName New name.
|
||||
* @return true if no error occurred. false otherwise.
|
||||
*/
|
||||
virtual bool renameSavefile(const char *oldName, const char *newName);
|
||||
|
||||
/**
|
||||
* Request a list of available savegames with a given DOS-style pattern,
|
||||
* also known as "glob" in the UNIX world. Refer to the Common::match()
|
||||
* function to learn about the precise pattern format.
|
||||
* @param pattern Pattern to match. Wildcards like * or ? are available.
|
||||
* @return list of strings for all present file names.
|
||||
* @see Common::match
|
||||
*/
|
||||
virtual Common::StringList listSavefiles(const char *pattern) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
98
common/singleton.h
Normal file
98
common/singleton.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_SINGLETON_H
|
||||
#define COMMON_SINGLETON_H
|
||||
|
||||
#include "common/noncopyable.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* Generic template base class for implementing the singleton design pattern.
|
||||
*/
|
||||
template<class T>
|
||||
class Singleton : NonCopyable {
|
||||
private:
|
||||
Singleton<T>(const Singleton<T> &);
|
||||
Singleton<T> &operator=(const Singleton<T> &);
|
||||
|
||||
static T *_singleton;
|
||||
|
||||
/**
|
||||
* The default object factory used by the template class Singleton.
|
||||
* By specialising this template function, one can make a singleton use a
|
||||
* custom object factory. For example, to support encapsulation, your
|
||||
* singleton class might be pure virtual (or "abstract" in Java terminology),
|
||||
* and you specialise makeInstance to return an instance of a subclass.
|
||||
*/
|
||||
//template <class T>
|
||||
#if defined (_WIN32_WCE) || defined (_MSC_VER) || defined (__WINS__)
|
||||
//FIXME evc4 and msvc7 doesn't like it as private member
|
||||
public:
|
||||
#endif
|
||||
static T *makeInstance() {
|
||||
return new T();
|
||||
}
|
||||
|
||||
static void destroyInstance() {
|
||||
delete _singleton;
|
||||
_singleton = 0;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
static T& instance() {
|
||||
// TODO: We aren't thread safe. For now we ignore it since the
|
||||
// only thing using this singleton template is the config manager,
|
||||
// and that is first instantiated long before any threads.
|
||||
// TODO: We don't leak, but the destruction order is nevertheless
|
||||
// semi-random. If we use multiple singletons, the destruction
|
||||
// order might become an issue. There are various approaches
|
||||
// to solve that problem, but for now this is sufficient
|
||||
if (!_singleton)
|
||||
_singleton = T::makeInstance();
|
||||
return *_singleton;
|
||||
}
|
||||
|
||||
static void destroy() {
|
||||
T::destroyInstance();
|
||||
}
|
||||
protected:
|
||||
Singleton<T>() { }
|
||||
#ifdef __SYMBIAN32__
|
||||
virtual ~Singleton() { }
|
||||
#else
|
||||
virtual ~Singleton<T>() { }
|
||||
#endif
|
||||
|
||||
typedef T SingletonBaseType;
|
||||
};
|
||||
|
||||
#define DECLARE_SINGLETON(T) template<> T *Common::Singleton<T>::_singleton = 0
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
190
common/stream.cpp
Normal file
190
common/stream.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/str.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void WriteStream::writeString(const String &str) {
|
||||
write(str.c_str(), str.size());
|
||||
}
|
||||
|
||||
MemoryReadStream *ReadStream::readStream(uint32 dataSize) {
|
||||
void *buf = malloc(dataSize);
|
||||
dataSize = read(buf, dataSize);
|
||||
assert(dataSize > 0);
|
||||
return new MemoryReadStream((byte *)buf, dataSize, true);
|
||||
}
|
||||
|
||||
|
||||
uint32 MemoryReadStream::read(void *dataPtr, uint32 dataSize) {
|
||||
// Read at most as many bytes as are still available...
|
||||
if (dataSize > _size - _pos)
|
||||
dataSize = _size - _pos;
|
||||
memcpy(dataPtr, _ptr, dataSize);
|
||||
|
||||
if (_encbyte) {
|
||||
byte *p = (byte *)dataPtr;
|
||||
byte *end = p + dataSize;
|
||||
while (p < end)
|
||||
*p++ ^= _encbyte;
|
||||
}
|
||||
|
||||
_ptr += dataSize;
|
||||
_pos += dataSize;
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
void MemoryReadStream::seek(int32 offs, int whence) {
|
||||
// Pre-Condition
|
||||
assert(_pos <= _size);
|
||||
switch (whence) {
|
||||
case SEEK_END:
|
||||
// SEEK_END works just like SEEK_SET, only 'reversed',
|
||||
// i.e. from the end.
|
||||
offs = _size - offs;
|
||||
// Fall through
|
||||
case SEEK_SET:
|
||||
_ptr = _ptrOrig + offs;
|
||||
_pos = offs;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
_ptr += offs;
|
||||
_pos += offs;
|
||||
break;
|
||||
}
|
||||
// Post-Condition
|
||||
assert(_pos <= _size);
|
||||
}
|
||||
|
||||
#define LF 0x0A
|
||||
#define CR 0x0D
|
||||
|
||||
char *SeekableReadStream::readLine(char *buf, size_t bufSize) {
|
||||
assert(buf && bufSize > 0);
|
||||
char *p = buf;
|
||||
size_t len = 0;
|
||||
char c;
|
||||
|
||||
if (buf == 0 || bufSize == 0 || eos()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We don't include the newline character(s) in the buffer, and we
|
||||
// always terminate it - we never read more than len-1 characters.
|
||||
|
||||
// EOF is treated as a line break, unless it was the first character
|
||||
// that was read.
|
||||
|
||||
// 0 is treated as a line break, even though it should never occur in
|
||||
// a text file.
|
||||
|
||||
// DOS and Windows use CRLF line breaks
|
||||
// Unix and OS X use LF line breaks
|
||||
// Macintosh before OS X uses CR line breaks
|
||||
|
||||
|
||||
c = readByte();
|
||||
if (eos() || ioFailed()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (!eos() && len + 1 < bufSize) {
|
||||
|
||||
if (ioFailed())
|
||||
return 0;
|
||||
|
||||
if (c == 0 || c == LF)
|
||||
break;
|
||||
|
||||
if (c == CR) {
|
||||
c = readByte();
|
||||
if (c != LF && !eos())
|
||||
seek(-1, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
*p++ = c;
|
||||
len++;
|
||||
|
||||
c = readByte();
|
||||
}
|
||||
|
||||
// This should fix a bug while using readLine with Common::File
|
||||
// it seems that it sets the eos flag after an invalid read
|
||||
// and at the same time the ioFailed flag
|
||||
// the config file parser fails out of that reason for the new themes
|
||||
if (eos()) {
|
||||
clearIOFailed();
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint32 SubReadStream::read(void *dataPtr, uint32 dataSize) {
|
||||
dataSize = MIN(dataSize, _end - _pos);
|
||||
|
||||
dataSize = _parentStream->read(dataPtr, dataSize);
|
||||
_pos += dataSize;
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
SeekableSubReadStream::SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool disposeParentStream)
|
||||
: SubReadStream(parentStream, end, disposeParentStream),
|
||||
_parentStream(parentStream),
|
||||
_begin(begin) {
|
||||
assert(_begin <= _end);
|
||||
_pos = _begin;
|
||||
_parentStream->seek(_pos);
|
||||
}
|
||||
|
||||
void SeekableSubReadStream::seek(int32 offset, int whence) {
|
||||
assert(_pos >= _begin);
|
||||
assert(_pos <= _end);
|
||||
|
||||
switch(whence) {
|
||||
case SEEK_END:
|
||||
offset = size() - offset;
|
||||
// fallthrough
|
||||
case SEEK_SET:
|
||||
_pos = _begin + offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
_pos += offset;
|
||||
}
|
||||
|
||||
assert(_pos >= _begin);
|
||||
assert(_pos <= _end);
|
||||
|
||||
_parentStream->seek(_pos);
|
||||
}
|
||||
|
||||
} // End of namespace Common
|
548
common/stream.h
Normal file
548
common/stream.h
Normal file
@ -0,0 +1,548 @@
|
||||
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
||||
*
|
||||
* Residual is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMMON_STREAM_H
|
||||
#define COMMON_STREAM_H
|
||||
|
||||
#include "common/sys.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
class String;
|
||||
class MemoryReadStream;
|
||||
|
||||
/**
|
||||
* Virtual base class for both ReadStream and WriteStream.
|
||||
*/
|
||||
class Stream {
|
||||
public:
|
||||
virtual ~Stream() {}
|
||||
|
||||
/**
|
||||
* Returns true if any I/O failure occurred.
|
||||
* This flag is never cleared automatically. In order to clear it,
|
||||
* client code has to call clearIOFailed() explicitly.
|
||||
*
|
||||
* @todo Instead of returning a plain bool, maybe we should define
|
||||
* a list of error codes which can be returned here.
|
||||
*/
|
||||
virtual bool ioFailed() const { return false; }
|
||||
|
||||
/**
|
||||
* Reset the I/O error status.
|
||||
*/
|
||||
virtual void clearIOFailed() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic interface for a writable data stream.
|
||||
*/
|
||||
class WriteStream : virtual public Stream {
|
||||
public:
|
||||
/**
|
||||
* Write data into the stream. Subclasses must implement this
|
||||
* method; all other write methods are implemented using it.
|
||||
*
|
||||
* @param dataPtr pointer to the data to be written
|
||||
* @param dataSize number of bytes to be written
|
||||
* @return the number of bytes which were actually written.
|
||||
*/
|
||||
virtual uint32 write(const void *dataPtr, uint32 dataSize) = 0;
|
||||
|
||||
/**
|
||||
* Commit any buffered data to the underlying channel or
|
||||
* storage medium; unbuffered streams can use the default
|
||||
* implementation.
|
||||
*/
|
||||
virtual void flush() {}
|
||||
|
||||
// The remaining methods all have default implementations; subclasses
|
||||
// need not (and should not) overload them.
|
||||
|
||||
void writeByte(byte value) {
|
||||
write(&value, 1);
|
||||
}
|
||||
|
||||
void writeSByte(int8 value) {
|
||||
write(&value, 1);
|
||||
}
|
||||
|
||||
void writeUint16LE(uint16 value) {
|
||||
writeByte((byte)(value & 0xff));
|
||||
writeByte((byte)(value >> 8));
|
||||
}
|
||||
|
||||
void writeUint32LE(uint32 value) {
|
||||
writeUint16LE((uint16)(value & 0xffff));
|
||||
writeUint16LE((uint16)(value >> 16));
|
||||
}
|
||||
|
||||
void writeUint16BE(uint16 value) {
|
||||
writeByte((byte)(value >> 8));
|
||||
writeByte((byte)(value & 0xff));
|
||||
}
|
||||
|
||||
void writeUint32BE(uint32 value) {
|
||||
writeUint16BE((uint16)(value >> 16));
|
||||
writeUint16BE((uint16)(value & 0xffff));
|
||||
}
|
||||
|
||||
void writeSint16LE(int16 value) {
|
||||
writeUint16LE((uint16)value);
|
||||
}
|
||||
|
||||
void writeSint32LE(int32 value) {
|
||||
writeUint32LE((uint32)value);
|
||||
}
|
||||
|
||||
void writeSint16BE(int16 value) {
|
||||
writeUint16BE((uint16)value);
|
||||
}
|
||||
|
||||
void writeSint32BE(int32 value) {
|
||||
writeUint32BE((uint32)value);
|
||||
}
|
||||
|
||||
void writeString(const String &str);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generic interface for a readable data stream.
|
||||
*/
|
||||
class ReadStream : virtual public Stream {
|
||||
public:
|
||||
/**
|
||||
* Returns true if the end of the stream has been reached.
|
||||
*/
|
||||
virtual bool eos() const = 0;
|
||||
|
||||
/**
|
||||
* Read data from the stream. Subclasses must implement this
|
||||
* method; all other read methods are implemented using it.
|
||||
*
|
||||
* @param dataPtr pointer to a buffer into which the data is read
|
||||
* @param dataSize number of bytes to be read
|
||||
* @return the number of bytes which were actually read.
|
||||
*/
|
||||
virtual uint32 read(void *dataPtr, uint32 dataSize) = 0;
|
||||
|
||||
|
||||
// The remaining methods all have default implementations; subclasses
|
||||
// need not (and should not) overload them.
|
||||
|
||||
/**
|
||||
* Read am unsigned byte from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
byte readByte() {
|
||||
byte b = 0;
|
||||
read(&b, 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed byte from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
int8 readSByte() {
|
||||
int8 b = 0;
|
||||
read(&b, 1);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned 16-bit word stored in little endian (LSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
uint16 readUint16LE() {
|
||||
uint16 a = readByte();
|
||||
uint16 b = readByte();
|
||||
return a | (b << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned 32-bit word stored in little endian (LSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
uint32 readUint32LE() {
|
||||
uint32 a = readUint16LE();
|
||||
uint32 b = readUint16LE();
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned 16-bit word stored in big endian (MSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
uint16 readUint16BE() {
|
||||
uint16 b = readByte();
|
||||
uint16 a = readByte();
|
||||
return a | (b << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned 32-bit word stored in big endian (MSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
uint32 readUint32BE() {
|
||||
uint32 b = readUint16BE();
|
||||
uint32 a = readUint16BE();
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed 16-bit word stored in little endian (LSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
int16 readSint16LE() {
|
||||
return (int16)readUint16LE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed 32-bit word stored in little endian (LSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
int32 readSint32LE() {
|
||||
return (int32)readUint32LE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed 16-bit word stored in big endian (MSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
int16 readSint16BE() {
|
||||
return (int16)readUint16BE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed 32-bit word stored in big endian (MSB first) order
|
||||
* from the stream and return it.
|
||||
* Performs no error checking. The return value is undefined
|
||||
* if a read error occurred (for which client code can check by
|
||||
* calling ioFailed()).
|
||||
*/
|
||||
int32 readSint32BE() {
|
||||
return (int32)readUint32BE();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the specified amount of data into a malloc'ed buffer
|
||||
* which then is wrapped into a MemoryReadStream.
|
||||
* The returned stream might contain less data than requested,
|
||||
* if reading more failed.
|
||||
*/
|
||||
MemoryReadStream *readStream(uint32 dataSize);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a seekable & readable data stream.
|
||||
*
|
||||
* @todo We really need better error handling here!
|
||||
* Like seek should somehow indicate whether it failed.
|
||||
*/
|
||||
class SeekableReadStream : virtual public ReadStream {
|
||||
public:
|
||||
|
||||
virtual uint32 pos() const = 0;
|
||||
virtual uint32 size() const = 0;
|
||||
|
||||
virtual void seek(int32 offset, int whence = SEEK_SET) = 0;
|
||||
|
||||
void skip(uint32 offset) { seek(offset, SEEK_CUR); }
|
||||
|
||||
/**
|
||||
* Read one line of text from a CR or CR/LF terminated plain text file.
|
||||
* This method is a rough analog of the (f)gets function.
|
||||
*
|
||||
* @param buf the buffer to store into
|
||||
* @param bufSize the size of the buffer
|
||||
* @return a pointer to the read string, or NULL if an error occurred
|
||||
* @note The line terminator (CR or CR/LF) is stripped and not inserted
|
||||
* into the buffer.
|
||||
*/
|
||||
virtual char *readLine(char *buf, size_t bufSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* SubReadStream provides access to a ReadStream restricted to the range
|
||||
* [currentPosition, currentPosition+end).
|
||||
* Manipulating the parent stream directly /will/ mess up a substream.
|
||||
* Likewise, manipulating two substreams of a parent stream will cause them to
|
||||
* step on each others toes.
|
||||
*/
|
||||
class SubReadStream : virtual public ReadStream {
|
||||
protected:
|
||||
ReadStream *_parentStream;
|
||||
uint32 _pos;
|
||||
uint32 _end;
|
||||
bool _disposeParentStream;
|
||||
public:
|
||||
SubReadStream(ReadStream *parentStream, uint32 end, bool disposeParentStream = false)
|
||||
: _parentStream(parentStream),
|
||||
_pos(0),
|
||||
_end(end),
|
||||
_disposeParentStream(disposeParentStream) {}
|
||||
~SubReadStream() {
|
||||
if (_disposeParentStream) delete _parentStream;
|
||||
}
|
||||
|
||||
virtual bool eos() const { return _pos == _end; }
|
||||
virtual uint32 read(void *dataPtr, uint32 dataSize);
|
||||
};
|
||||
|
||||
/*
|
||||
* SeekableSubReadStream provides access to a SeekableReadStream restricted to
|
||||
* the range [begin, end).
|
||||
* The same caveats apply to SeekableSubReadStream as do to SeekableReadStream.
|
||||
*/
|
||||
class SeekableSubReadStream : public SubReadStream, public SeekableReadStream {
|
||||
protected:
|
||||
SeekableReadStream *_parentStream;
|
||||
uint32 _begin;
|
||||
public:
|
||||
SeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool disposeParentStream = false);
|
||||
|
||||
virtual uint32 pos() const { return _pos - _begin; }
|
||||
virtual uint32 size() const { return _end - _begin; }
|
||||
|
||||
virtual void seek(int32 offset, int whence = SEEK_SET);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a wrapper around SeekableSubReadStream, but it adds non-endian
|
||||
* read methods whose endianness is set on the stream creation.
|
||||
*/
|
||||
class SeekableSubReadStreamEndian : public SeekableSubReadStream {
|
||||
public:
|
||||
bool _bigEndian;
|
||||
|
||||
SeekableSubReadStreamEndian(SeekableReadStream *parentStream, uint32 begin, uint32 end, bool bigEndian = false, bool disposeParentStream = false)
|
||||
: SeekableSubReadStream(parentStream, begin, end, disposeParentStream), _bigEndian(bigEndian) {
|
||||
}
|
||||
|
||||
inline uint16 readUint16() {
|
||||
return (_bigEndian) ? readUint16BE() : readUint16LE();
|
||||
}
|
||||
|
||||
inline uint32 readUint32() {
|
||||
return (_bigEndian) ? readUint32BE() : readUint32LE();
|
||||
}
|
||||
|
||||
inline int16 readSint16() {
|
||||
return (int16)readUint16();
|
||||
}
|
||||
|
||||
inline int32 readSint32() {
|
||||
return (int32)readUint32();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simple memory based 'stream', which implements the ReadStream interface for
|
||||
* a plain memory block.
|
||||
*/
|
||||
class MemoryReadStream : public SeekableReadStream {
|
||||
private:
|
||||
const byte * const _ptrOrig;
|
||||
const byte *_ptr;
|
||||
const uint32 _size;
|
||||
uint32 _pos;
|
||||
byte _encbyte;
|
||||
bool _disposeMemory;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* This constructor takes a pointer to a memory buffer and a length, and
|
||||
* wraps it. If disposeMemory is true, the MemoryReadStream takes ownership
|
||||
* of the buffer and hence free's it when destructed.
|
||||
*/
|
||||
MemoryReadStream(const byte *dataPtr, uint32 dataSize, bool disposeMemory = false) :
|
||||
_ptrOrig(dataPtr),
|
||||
_ptr(dataPtr),
|
||||
_size(dataSize),
|
||||
_pos(0),
|
||||
_encbyte(0),
|
||||
_disposeMemory(disposeMemory) {}
|
||||
|
||||
~MemoryReadStream() {
|
||||
if (_disposeMemory)
|
||||
free(const_cast<byte *>(_ptrOrig));
|
||||
}
|
||||
|
||||
void setEnc(byte value) { _encbyte = value; }
|
||||
|
||||
uint32 read(void *dataPtr, uint32 dataSize);
|
||||
|
||||
bool eos() const { return _pos == _size; }
|
||||
uint32 pos() const { return _pos; }
|
||||
uint32 size() const { return _size; }
|
||||
|
||||
void seek(int32 offs, int whence = SEEK_SET);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This is a wrapper around MemoryReadStream, but it adds non-endian
|
||||
* read methods whose endianness is set on the stream creation.
|
||||
*/
|
||||
class MemoryReadStreamEndian : public Common::MemoryReadStream {
|
||||
private:
|
||||
public:
|
||||
bool _bigEndian;
|
||||
MemoryReadStreamEndian(const byte *buf, uint32 len, bool bigEndian = false) : MemoryReadStream(buf, len), _bigEndian(bigEndian) {}
|
||||
|
||||
inline uint16 readUint16() {
|
||||
return (_bigEndian) ? readUint16BE() : readUint16LE();
|
||||
}
|
||||
|
||||
inline uint32 readUint32() {
|
||||
return (_bigEndian) ? readUint32BE() : readUint32LE();
|
||||
}
|
||||
|
||||
inline int16 readSint16() {
|
||||
return (int16)readUint16();
|
||||
}
|
||||
|
||||
inline int32 readSint32() {
|
||||
return (int32)readUint32();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple memory based 'stream', which implements the WriteStream interface for
|
||||
* a plain memory block.
|
||||
*/
|
||||
class MemoryWriteStream : public WriteStream {
|
||||
private:
|
||||
byte *_ptr;
|
||||
const uint32 _bufSize;
|
||||
uint32 _pos;
|
||||
public:
|
||||
MemoryWriteStream(byte *buf, uint32 len) : _ptr(buf), _bufSize(len), _pos(0) {}
|
||||
|
||||
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||
// Write at most as many bytes as are still available...
|
||||
if (dataSize > _bufSize - _pos)
|
||||
dataSize = _bufSize - _pos;
|
||||
memcpy(_ptr, dataPtr, dataSize);
|
||||
_ptr += dataSize;
|
||||
_pos += dataSize;
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
bool eos() const { return _pos == _bufSize; }
|
||||
uint32 pos() const { return _pos; }
|
||||
uint32 size() const { return _bufSize; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A sort of hybrid between MemoryWriteStream and Array classes. A stream
|
||||
* that grows as it's written to.
|
||||
*/
|
||||
class MemoryWriteStreamDynamic : public Common::WriteStream {
|
||||
private:
|
||||
uint32 _capacity;
|
||||
uint32 _size;
|
||||
byte *_ptr;
|
||||
byte *_data;
|
||||
uint32 _pos;
|
||||
bool _disposeMemory;
|
||||
|
||||
void ensureCapacity(uint32 new_len) {
|
||||
if (new_len <= _capacity)
|
||||
return;
|
||||
|
||||
byte *old_data = _data;
|
||||
|
||||
_capacity = new_len + 32;
|
||||
_data = new byte[_capacity];
|
||||
_ptr = _data + _pos;
|
||||
|
||||
if (old_data) {
|
||||
// Copy old data
|
||||
memcpy(_data, old_data, _size);
|
||||
delete[] old_data;
|
||||
}
|
||||
|
||||
_size = new_len;
|
||||
}
|
||||
public:
|
||||
MemoryWriteStreamDynamic(bool disposeMemory = false) : _capacity(0), _size(0), _ptr(0), _data(0), _pos(0), _disposeMemory(disposeMemory) {}
|
||||
|
||||
~MemoryWriteStreamDynamic() {
|
||||
if (_disposeMemory)
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||
ensureCapacity(_pos + dataSize);
|
||||
memcpy(_ptr, dataPtr, dataSize);
|
||||
_ptr += dataSize;
|
||||
_pos += dataSize;
|
||||
if (_pos > _size)
|
||||
_size = _pos;
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
bool eos() const { return false; }
|
||||
uint32 pos() const { return _pos; }
|
||||
uint32 size() const { return _size; }
|
||||
|
||||
byte *getData() { return _data; }
|
||||
};
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
#endif
|
@ -46,6 +46,7 @@ extern "C" int residual_main(int argc, char *argv[]);
|
||||
#pragma once
|
||||
#pragma warning( disable : 4068 ) // turn off "unknown pragma" warning
|
||||
#pragma warning( disable : 4244 ) // turn off "conversion type" warning
|
||||
#pragma warning( disable : 4250 ) // turn off "inherits via dominance" warning
|
||||
#endif
|
||||
|
||||
#ifndef LOCAL_PI
|
||||
|
@ -56,5 +56,4 @@ template<typename T> inline void SWAP(T &a, T &b) { T tmp = a; a = b; b = tmp; }
|
||||
#define round(x) ((x > 0.0) ? floor((x) + 0.5) : ceil((x) - 0.5))
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user