ULTIMA1: Beginnings of create_ultima tool

This commit is contained in:
Paul Gilbert 2018-07-08 20:06:10 -07:00 committed by Paul Gilbert
parent 75bf34d7da
commit 18c88beb9d
14 changed files with 2934 additions and 0 deletions

View File

@ -0,0 +1,83 @@
/* 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.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main
#include "archive.h"
#include "common/endian.h"
#define ARCHIVE_IDENT "SULT"
#define ARCHIVE_VERSION 1
void Archive::save() {
// Write identifying string and archive version
_file.write(ARCHIVE_IDENT, 4);
_file.writeWord(ARCHIVE_VERSION);
_file.writeWord(0);
// Figure out the size the entire index needs to be (i.e. where the data starts)
size_t dataOffset = 8;
for (uint idx = 0; idx < _index.size(); ++idx)
dataOffset += _index[idx].getIndexSize();
dataOffset = ((dataOffset + 1) / 2) * 2;
// Iterate through writing out index entries
for (uint idx = 0; idx < _index.size(); ++idx) {
ArchiveEntry &ae = _index[idx];
ae._offset = dataOffset;
_file.writeString(ae._name.c_str());
_file.writeLong(ae._offset);
_file.writeWord(ae._size);
}
if (_file.pos() % 2)
_file.writeByte(0);
// Write out the contents of each resource
for (uint idx = 0; idx < _index.size(); ++idx) {
const ArchiveEntry &ae = _index[idx];
if (_file.pos() != ae._offset)
error("Incorrect offset");
_file.write(ae._data, ae._size);
}
}
bool Archive::open(const Common::String &name) {
return _file.open(name.c_str(), Common::kFileWriteMode);
}
void Archive::close() {
if (_file.isOpen()) {
save();
_file.close();
}
}
void Archive::add(const Common::String &name, Common::MemFile &f) {
_index.push_back(ArchiveEntry(name, f.getData(), f.size()));
}

View File

@ -0,0 +1,90 @@
/* 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.
*
*/
#ifndef CC_H
#define CC_H
#include "file.h"
#include "str.h"
#include "common/array.h"
class Archive {
/**
* Details of a single entry in the archive
*/
struct ArchiveEntry {
Common::String _name;
size_t _offset;
uint16 _size;
byte _data[65536];
ArchiveEntry() : _offset(0), _size(0) {
memset(_data, 0, 65536);
}
ArchiveEntry(const Common::String &name, const byte *data, uint32 size) :
_offset(0), _name(name), _size(size) {
memcpy(_data, data, size);
}
/**
* Returns the size needed for saving the entry in the archive index
*/
size_t getIndexSize() const { return 6 + _name.size() + 1; }
};
private:
Common::Array<ArchiveEntry> _index;
Common::File _file;
private:
/**
* Generates the archive and saves it to file
*/
void save();
public:
/**
* Consstructor
*/
Archive() {}
/**
* Destructor
*/
~Archive() {
close();
}
/**
* Opens the archive for access
*/
bool open(const Common::String &name);
/**
* Closes the archive
*/
void close();
/**
* Adds an entry to the CC
*/
void add(const Common::String &name, Common::MemFile &f);
};
#endif

View File

@ -0,0 +1,55 @@
/* 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.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "archive.h"
#include "ultima1_map.h"
#define VERSION_NUMBER 1
void NORETURN_PRE error(const char *s, ...) {
printf("%s\n", s);
exit(1);
}
int main(int argc, char *argv[]) {
Archive archive;
if (!archive.open("ultima.dat")) {
error("Could not open output file");
}
writeUltima1EnhancedMap(archive);
archive.close();
return 0;
}

View File

@ -0,0 +1,273 @@
/* 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.
*
*/
#ifndef __FILE_H__
#define __FILE_H__
#include <stdio.h>
#include <stdlib.h>
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/util.h"
namespace Common {
enum AccessMode {
kFileReadMode = 1,
kFileWriteMode = 2
};
class Stream {
public:
Stream() {}
virtual ~Stream() {}
virtual int seek(int offset, int whence = SEEK_SET) = 0;
virtual long read(void *buffer, size_t len) = 0;
virtual void write(const void *buffer, size_t len) = 0;
virtual uint pos() const = 0;
virtual uint size() const = 0;
virtual bool eof() const = 0;
void skip(int offset) {
seek(offset, SEEK_CUR);
}
void write(Stream &src, size_t len) {
for (size_t idx = 0; idx < len; ++idx)
writeByte(src.readByte());
}
byte readByte() {
byte v;
read(&v, sizeof(byte));
return v;
}
uint16 readWord() {
uint16 v;
read(&v, sizeof(uint16));
return FROM_LE_16(v);
}
uint readLong() {
uint v;
read(&v, sizeof(uint));
return FROM_LE_32(v);
}
uint readUint16BE() {
uint16 v;
read(&v, sizeof(uint16));
return FROM_BE_16(v);
}
uint readUint16LE() {
uint16 v;
read(&v, sizeof(uint16));
return FROM_LE_16(v);
}
uint readUint32BE() {
uint32 v;
read(&v, sizeof(uint32));
return FROM_BE_32(v);
}
uint readUint32LE() {
uint32 v;
read(&v, sizeof(uint32));
return FROM_LE_32(v);
}
void writeByte(byte v) {
write(&v, sizeof(byte));
}
void writeShort(int8 v) {
write(&v, sizeof(int8));
}
void writeByte(byte v, int len) {
byte *b = new byte[len];
memset(b, v, len);
write(b, len);
delete[] b;
}
void writeWord(uint16 v) {
uint16 vTemp = TO_LE_16(v);
write(&vTemp, sizeof(uint16));
}
void writeLong(uint v) {
uint vTemp = TO_LE_32(v);
write(&vTemp, sizeof(uint));
}
void writeString(const char *msg) {
if (!msg) {
writeByte(0);
} else {
do {
writeByte(*msg);
} while (*msg++);
}
}
};
class File : public Stream {
private:
::FILE *_f;
public:
File() : _f(nullptr) {}
virtual ~File() { close(); }
bool open(const char *filename, AccessMode mode = kFileReadMode) {
_f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb+");
return (_f != NULL);
}
void close() {
if (_f)
fclose(_f);
_f = nullptr;
}
virtual int seek(int offset, int whence = SEEK_SET) {
return fseek(_f, offset, whence);
}
virtual long read(void *buffer, size_t len) {
return fread(buffer, 1, len, _f);
}
virtual void write(const void *buffer, size_t len) {
assert(_f);
fwrite(buffer, 1, len, _f);
}
virtual uint pos() const {
return ftell(_f);
}
virtual uint size() const {
uint currentPos = pos();
fseek(_f, 0, SEEK_END);
uint result = pos();
fseek(_f, currentPos, SEEK_SET);
return result;
}
virtual bool eof() const {
return feof(_f) != 0;
}
bool isOpen() const {
return _f != nullptr;
}
};
#define MAX_MEM_SIZE 65536
class MemFile : public Stream {
private:
byte _data[MAX_MEM_SIZE];
size_t _size, _offset;
public:
MemFile() : _size(0), _offset(0) {
memset(_data, 0, MAX_MEM_SIZE);
}
MemFile(const byte *data, size_t size) : _size(size), _offset(0) {
memcpy(_data, data, size);
}
virtual ~MemFile() {}
bool open() {
memset(_data, 0, MAX_MEM_SIZE);
_size = _offset = 0;
return true;
}
void close() {
}
virtual int seek(int offset, int whence = SEEK_SET) {
switch (whence) {
case SEEK_SET: _offset = whence; break;
case SEEK_CUR: _offset += whence; break;
case SEEK_END: _offset = _size + whence; break;
}
return _offset;
}
virtual long read(void *buffer, size_t len) {
len = MAX(len, _size - _offset);
memcpy(buffer, &_data[_offset], len);
return len;
}
virtual void write(const void *buffer, size_t len) {
assert(len <= (MAX_MEM_SIZE - _offset));
memcpy(&_data[_offset], buffer, len);
_offset += len;
_size = MAX(_offset, _size);
}
virtual uint pos() const {
return _offset;
}
virtual uint size() const {
return _size;
}
virtual bool eof() const {
return _offset >= _size;
}
const byte *getData() const { return _data; }
void syncString(const char *str) {
write(str, strlen(str) + 1);
}
void syncStrings(const char *const *str, int count) {
writeLong(MKTAG(count, 0, 0, 0));
for (int idx = 0; idx < count; ++idx, ++str)
writeString(*str);
}
void syncStrings2D(const char *const *str, int count1, int count2) {
writeLong(MKTAG(count1, count2, 0, 0));
for (int idx = 0; idx < count1 * count2; ++idx, ++str)
writeString(*str);
}
void syncNumber(const int val) {
writeLong(val);
}
void syncNumbers(const int *vals, int count) {
writeLong(MKTAG(count, 0, 0, 0));
for (int idx = 0; idx < count; ++idx, ++vals)
writeLong(*vals);
}
void syncNumbers2D(const int *vals, int count1, int count2) {
writeLong(MKTAG(count1, count2, 0, 0));
for (int idx = 0; idx < count1 * count2; ++idx, ++vals)
writeLong(*vals);
}
void syncNumbers3D(const int *vals, int count1, int count2, int count3) {
writeLong(MKTAG(count1, count2, count3, 0));
for (int idx = 0; idx < count1 * count2 * count3; ++idx, ++vals)
writeLong(*vals);
}
void syncNumbers4D(const int *vals, int count1, int count2, int count3, int count4) {
writeLong(MKTAG(count1, count2, count3, count4));
for (int idx = 0; idx < count1 * count2 * count3 * count4; ++idx, ++vals)
writeLong(*vals);
}
void syncBytes2D(const byte *vals, int count1, int count2) {
writeLong(MKTAG(count1, count2, 0, 0));
write(vals, count1 * count2);
}
};
} // End of namespace Common
#endif

View File

@ -0,0 +1,86 @@
/* 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.
*
*/
#ifndef COMMON_HASH_STR_H
#define COMMON_HASH_STR_H
#include "hashmap.h"
#include "str.h"
namespace Common {
uint hashit(const char *str);
uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
inline uint hashit(const String &str) { return hashit(str.c_str()); }
inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str()); }
// FIXME: The following functors obviously are not consistently named
struct CaseSensitiveString_EqualTo {
bool operator()(const String& x, const String& y) const { return x.equals(y); }
};
struct CaseSensitiveString_Hash {
uint operator()(const String& x) const { return hashit(x.c_str()); }
};
struct IgnoreCase_EqualTo {
bool operator()(const String& x, const String& y) const { return x.equalsIgnoreCase(y); }
};
struct IgnoreCase_Hash {
uint operator()(const String& x) const { return hashit_lower(x.c_str()); }
};
// Specalization of the Hash functor for String objects.
// We do case sensitve hashing here, because that is what
// the default EqualTo is compatible with. If one wants to use
// case insensitve hashing, then only because one wants to use
// IgnoreCase_EqualTo, and then one has to specify a custom
// hash anyway.
template<>
struct Hash<String> {
uint operator()(const String& s) const {
return hashit(s.c_str());
}
};
template<>
struct Hash<const char *> {
uint operator()(const char *s) const {
return hashit(s);
}
};
// String map -- by default case insensitive
typedef HashMap<String, String, IgnoreCase_Hash, IgnoreCase_EqualTo> StringMap;
} // End of namespace Common
#endif

View File

@ -0,0 +1,109 @@
/* 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.
*
*/
// The hash map (associative array) implementation in this file is
// based on the PyDict implementation of CPython. The erase() method
// is based on example code in the Wikipedia article on Hash tables.
#include "common/hashmap.h"
namespace Common {
// Hash function for strings, taken from CPython.
uint hashit(const char *p) {
uint hash = *p << 7;
byte c;
int size = 0;
while ((c = *p++)) {
hash = (1000003 * hash) ^ c;
size++;
}
return hash ^ size;
}
// Like hashit, but converts every char to lowercase before hashing.
uint hashit_lower(const char *p) {
uint hash = tolower(*p) << 7;
byte c;
int size = 0;
while ((c = *p++)) {
hash = (1000003 * hash) ^ tolower(c);
size++;
}
return hash ^ size;
}
#ifdef DEBUG_HASH_COLLISIONS
static double
g_collisions = 0,
g_dummyHits = 0,
g_lookups = 0,
g_collPerLook = 0,
g_capacity = 0,
g_size = 0;
static int g_max_capacity = 0, g_max_size = 0;
static int g_totalHashmaps = 0;
static int g_stats[4] = {0,0,0,0};
void updateHashCollisionStats(int collisions, int dummyHits, int lookups, int arrsize, int nele) {
g_collisions += collisions;
g_lookups += lookups;
g_dummyHits += dummyHits;
if (lookups)
g_collPerLook += (double)collisions / (double)lookups;
g_capacity += arrsize;
g_size += nele;
g_totalHashmaps++;
if (3*nele <= 2*8)
g_stats[0]++;
if (3*nele <= 2*16)
g_stats[1]++;
if (3*nele <= 2*32)
g_stats[2]++;
if (3*nele <= 2*64)
g_stats[3]++;
g_max_capacity = MAX(g_max_capacity, arrsize);
g_max_size = MAX(g_max_size, nele);
debug("%d hashmaps: colls %.1f; dummies hit %.1f, lookups %.1f; ratio %.3f%%; size %f (max: %d); capacity %f (max: %d)",
g_totalHashmaps,
g_collisions / g_totalHashmaps,
g_dummyHits / g_totalHashmaps,
g_lookups / g_totalHashmaps,
100 * g_collPerLook / g_totalHashmaps,
g_size / g_totalHashmaps, g_max_size,
g_capacity / g_totalHashmaps, g_max_capacity);
debug(" %d less than %d; %d less than %d; %d less than %d; %d less than %d",
g_stats[0], 2*8/3,
g_stats[1],2*16/3,
g_stats[2],2*32/3,
g_stats[3],2*64/3);
// TODO:
// * Should record the maximal size of the map during its lifetime, not that at its death
// * Should do some statistics: how many maps are less than 2/3*8, 2/3*16, 2/3*32, ...
}
#endif
} // End of namespace Common

View File

@ -0,0 +1,637 @@
/* 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.
*
*/
// The hash map (associative array) implementation in this file is
// based on the PyDict implementation of CPython.
#ifndef COMMON_HASHMAP_H
#define COMMON_HASHMAP_H
/**
* @def DEBUG_HASH_COLLISIONS
* Enable the following #define if you want to check how many collisions the
* code produces (many collisions indicate either a bad hash function, or a
* hash table that is too small).
*/
//#define DEBUG_HASH_COLLISIONS
/**
* @def USE_HASHMAP_MEMORY_POOL
* Enable the following define to let HashMaps use a memory pool for the
nodes they contain. * This increases memory usage, but also can improve
speed quite a bit.
*/
#define USE_HASHMAP_MEMORY_POOL
#include "common/func.h"
#ifdef DEBUG_HASH_COLLISIONS
#include "common/debug.h"
#endif
#ifdef USE_HASHMAP_MEMORY_POOL
#include "memorypool.h"
#endif
namespace Common {
// The sgi IRIX MIPSpro Compiler has difficulties with nested templates.
// This and the other __sgi conditionals below work around these problems.
// The Intel C++ Compiler suffers from the same problems.
#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
template<class T> class IteratorImpl;
#endif
/**
* HashMap<Key,Val> maps objects of type Key to objects of type Val.
* For each used Key type, we need an "size_type hashit(Key,size_type)" function
* that computes a hash for the given Key object and returns it as an
* an integer from 0 to hashsize-1, and also an "equality functor".
* that returns true if if its two arguments are to be considered
* equal. Also, we assume that "=" works on Val objects for assignment.
*
* If aa is an HashMap<Key,Val>, then space is allocated each time aa[key] is
* referenced, for a new key. If the object is const, then an assertion is
* triggered instead. Hence if you are not sure whether a key is contained in
* the map, use contains() first to check for its presence.
*/
template<class Key, class Val, class HashFunc = Hash<Key>, class EqualFunc = EqualTo<Key> >
class HashMap {
public:
typedef uint size_type;
private:
typedef HashMap<Key, Val, HashFunc, EqualFunc> HM_t;
struct Node {
const Key _key;
Val _value;
explicit Node(const Key &key) : _key(key), _value() {}
Node() : _key(), _value() {}
};
enum {
HASHMAP_PERTURB_SHIFT = 5,
HASHMAP_MIN_CAPACITY = 16,
// The quotient of the next two constants controls how much the
// internal storage of the hashmap may fill up before being
// increased automatically.
// Note: the quotient of these two must be between and different
// from 0 and 1.
HASHMAP_LOADFACTOR_NUMERATOR = 2,
HASHMAP_LOADFACTOR_DENOMINATOR = 3,
HASHMAP_MEMORYPOOL_SIZE = HASHMAP_MIN_CAPACITY * HASHMAP_LOADFACTOR_NUMERATOR / HASHMAP_LOADFACTOR_DENOMINATOR
};
#ifdef USE_HASHMAP_MEMORY_POOL
ObjectPool<Node, HASHMAP_MEMORYPOOL_SIZE> _nodePool;
#endif
Node **_storage; ///< hashtable of size arrsize.
size_type _mask; ///< Capacity of the HashMap minus one; must be a power of two of minus one
size_type _size;
size_type _deleted; ///< Number of deleted elements (_dummyNodes)
HashFunc _hash;
EqualFunc _equal;
/** Default value, returned by the const getVal. */
const Val _defaultVal;
/** Dummy node, used as marker for erased objects. */
#define HASHMAP_DUMMY_NODE ((Node *)1)
#ifdef DEBUG_HASH_COLLISIONS
mutable int _collisions, _lookups, _dummyHits;
#endif
Node *allocNode(const Key &key) {
#ifdef USE_HASHMAP_MEMORY_POOL
return new (_nodePool) Node(key);
#else
return new Node(key);
#endif
}
void freeNode(Node *node) {
if (node && node != HASHMAP_DUMMY_NODE)
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool.deleteChunk(node);
#else
delete node;
#endif
}
void assign(const HM_t &map);
size_type lookup(const Key &key) const;
size_type lookupAndCreateIfMissing(const Key &key);
void expandStorage(size_type newCapacity);
#if !defined(__sgi) || defined(__GNUC__)
template<class T> friend class IteratorImpl;
#endif
/**
* Simple HashMap iterator implementation.
*/
template<class NodeType>
class IteratorImpl {
friend class HashMap;
#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
template<class T> friend class Common::IteratorImpl;
#else
template<class T> friend class IteratorImpl;
#endif
protected:
typedef const HashMap hashmap_t;
size_type _idx;
hashmap_t *_hashmap;
protected:
IteratorImpl(size_type idx, hashmap_t *hashmap) : _idx(idx), _hashmap(hashmap) {}
NodeType *deref() const {
assert(_hashmap != 0);
assert(_idx <= _hashmap->_mask);
Node *node = _hashmap->_storage[_idx];
assert(node != 0);
assert(node != HASHMAP_DUMMY_NODE);
return node;
}
public:
IteratorImpl() : _idx(0), _hashmap(0) {}
template<class T>
IteratorImpl(const IteratorImpl<T> &c) : _idx(c._idx), _hashmap(c._hashmap) {}
NodeType &operator*() const { return *deref(); }
NodeType *operator->() const { return deref(); }
bool operator==(const IteratorImpl &iter) const { return _idx == iter._idx && _hashmap == iter._hashmap; }
bool operator!=(const IteratorImpl &iter) const { return !(*this == iter); }
IteratorImpl &operator++() {
assert(_hashmap);
do {
_idx++;
} while (_idx <= _hashmap->_mask && (_hashmap->_storage[_idx] == 0 || _hashmap->_storage[_idx] == HASHMAP_DUMMY_NODE));
if (_idx > _hashmap->_mask)
_idx = (size_type)-1;
return *this;
}
IteratorImpl operator++(int) {
IteratorImpl old = *this;
operator ++();
return old;
}
};
public:
typedef IteratorImpl<Node> iterator;
typedef IteratorImpl<const Node> const_iterator;
HashMap();
HashMap(const HM_t &map);
~HashMap();
HM_t &operator=(const HM_t &map) {
if (this == &map)
return *this;
// Remove the previous content and ...
clear();
delete[] _storage;
// ... copy the new stuff.
assign(map);
return *this;
}
bool contains(const Key &key) const;
Val &operator[](const Key &key);
const Val &operator[](const Key &key) const;
Val &getVal(const Key &key);
const Val &getVal(const Key &key) const;
const Val &getVal(const Key &key, const Val &defaultVal) const;
void setVal(const Key &key, const Val &val);
void clear(bool shrinkArray = 0);
void erase(iterator entry);
void erase(const Key &key);
size_type size() const { return _size; }
iterator begin() {
// Find and return the first non-empty entry
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE)
return iterator(ctr, this);
}
return end();
}
iterator end() {
return iterator((size_type)-1, this);
}
const_iterator begin() const {
// Find and return the first non-empty entry
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE)
return const_iterator(ctr, this);
}
return end();
}
const_iterator end() const {
return const_iterator((size_type)-1, this);
}
iterator find(const Key &key) {
size_type ctr = lookup(key);
if (_storage[ctr])
return iterator(ctr, this);
return end();
}
const_iterator find(const Key &key) const {
size_type ctr = lookup(key);
if (_storage[ctr])
return const_iterator(ctr, this);
return end();
}
// TODO: insert() method?
bool empty() const {
return (_size == 0);
}
};
//-------------------------------------------------------
// HashMap functions
/**
* Base constructor, creates an empty hashmap.
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap()
//
// We have to skip _defaultVal() on PS2 to avoid gcc 3.2.2 ICE
//
#ifdef __PLAYSTATION2__
{
#else
: _defaultVal() {
#endif
_mask = HASHMAP_MIN_CAPACITY - 1;
_storage = new Node *[HASHMAP_MIN_CAPACITY];
assert(_storage != NULL);
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
_size = 0;
_deleted = 0;
#ifdef DEBUG_HASH_COLLISIONS
_collisions = 0;
_lookups = 0;
_dummyHits = 0;
#endif
}
/**
* Copy constructor, creates a full copy of the given hashmap.
* We must provide a custom copy constructor as we use pointers
* to heap buffers for the internal storage.
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
_defaultVal() {
#ifdef DEBUG_HASH_COLLISIONS
_collisions = 0;
_lookups = 0;
_dummyHits = 0;
#endif
assign(map);
}
/**
* Destructor, frees all used memory.
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
for (size_type ctr = 0; ctr <= _mask; ++ctr)
freeNode(_storage[ctr]);
delete[] _storage;
#ifdef DEBUG_HASH_COLLISIONS
extern void updateHashCollisionStats(int, int, int, int, int);
updateHashCollisionStats(_collisions, _dummyHits, _lookups, _mask+1, _size);
#endif
}
/**
* Internal method for assigning the content of another HashMap
* to this one.
*
* @note We do *not* deallocate the previous storage here -- the caller is
* responsible for doing that!
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::assign(const HM_t &map) {
_mask = map._mask;
_storage = new Node *[_mask+1];
assert(_storage != NULL);
memset(_storage, 0, (_mask+1) * sizeof(Node *));
// Simply clone the map given to us, one by one.
_size = 0;
_deleted = 0;
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
if (map._storage[ctr] == HASHMAP_DUMMY_NODE) {
_storage[ctr] = HASHMAP_DUMMY_NODE;
_deleted++;
} else if (map._storage[ctr] != NULL) {
_storage[ctr] = allocNode(map._storage[ctr]->_key);
_storage[ctr]->_value = map._storage[ctr]->_value;
_size++;
}
}
// Perform a sanity check (to help track down hashmap corruption)
assert(_size == map._size);
assert(_deleted == map._deleted);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) {
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
freeNode(_storage[ctr]);
_storage[ctr] = NULL;
}
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool.freeUnusedPages();
#endif
if (shrinkArray && _mask >= HASHMAP_MIN_CAPACITY) {
delete[] _storage;
_mask = HASHMAP_MIN_CAPACITY;
_storage = new Node *[HASHMAP_MIN_CAPACITY];
assert(_storage != NULL);
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
}
_size = 0;
_deleted = 0;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::expandStorage(size_type newCapacity) {
assert(newCapacity > _mask+1);
#ifndef NDEBUG
const size_type old_size = _size;
#endif
const size_type old_mask = _mask;
Node **old_storage = _storage;
// allocate a new array
_size = 0;
_deleted = 0;
_mask = newCapacity - 1;
_storage = new Node *[newCapacity];
assert(_storage != NULL);
memset(_storage, 0, newCapacity * sizeof(Node *));
// rehash all the old elements
for (size_type ctr = 0; ctr <= old_mask; ++ctr) {
if (old_storage[ctr] == NULL || old_storage[ctr] == HASHMAP_DUMMY_NODE)
continue;
// Insert the element from the old table into the new table.
// Since we know that no key exists twice in the old table, we
// can do this slightly better than by calling lookup, since we
// don't have to call _equal().
const size_type hash = _hash(old_storage[ctr]->_key);
size_type idx = hash & _mask;
for (size_type perturb = hash; _storage[idx] != NULL && _storage[idx] != HASHMAP_DUMMY_NODE; perturb >>= HASHMAP_PERTURB_SHIFT) {
idx = (5 * idx + perturb + 1) & _mask;
}
_storage[idx] = old_storage[ctr];
_size++;
}
// Perform a sanity check: Old number of elements should match the new one!
// This check will fail if some previous operation corrupted this hashmap.
assert(_size == old_size);
delete[] old_storage;
return;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
typename HashMap<Key, Val, HashFunc, EqualFunc>::size_type HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
const size_type hash = _hash(key);
size_type ctr = hash & _mask;
for (size_type perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
if (_storage[ctr] == NULL)
break;
if (_storage[ctr] == HASHMAP_DUMMY_NODE) {
#ifdef DEBUG_HASH_COLLISIONS
_dummyHits++;
#endif
} else if (_equal(_storage[ctr]->_key, key))
break;
ctr = (5 * ctr + perturb + 1) & _mask;
#ifdef DEBUG_HASH_COLLISIONS
_collisions++;
#endif
}
#ifdef DEBUG_HASH_COLLISIONS
_lookups++;
debug("collisions %d, dummies hit %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d",
_collisions, _dummyHits, _lookups, ((double) _collisions / (double)_lookups),
(const void *)this, _mask+1, _size);
#endif
return ctr;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
typename HashMap<Key, Val, HashFunc, EqualFunc>::size_type HashMap<Key, Val, HashFunc, EqualFunc>::lookupAndCreateIfMissing(const Key &key) {
const size_type hash = _hash(key);
size_type ctr = hash & _mask;
const size_type NONE_FOUND = _mask + 1;
size_type first_free = NONE_FOUND;
bool found = false;
for (size_type perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
if (_storage[ctr] == NULL)
break;
if (_storage[ctr] == HASHMAP_DUMMY_NODE) {
#ifdef DEBUG_HASH_COLLISIONS
_dummyHits++;
#endif
if (first_free != _mask + 1)
first_free = ctr;
} else if (_equal(_storage[ctr]->_key, key)) {
found = true;
break;
}
ctr = (5 * ctr + perturb + 1) & _mask;
#ifdef DEBUG_HASH_COLLISIONS
_collisions++;
#endif
}
#ifdef DEBUG_HASH_COLLISIONS
_lookups++;
debug("collisions %d, dummies hit %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d",
_collisions, _dummyHits, _lookups, ((double) _collisions / (double)_lookups),
(const void *)this, _mask+1, _size);
#endif
if (!found && first_free != _mask + 1)
ctr = first_free;
if (!found) {
if (_storage[ctr])
_deleted--;
_storage[ctr] = allocNode(key);
assert(_storage[ctr] != NULL);
_size++;
// Keep the load factor below a certain threshold.
// Deleted nodes are also counted
size_type capacity = _mask + 1;
if ((_size + _deleted) * HASHMAP_LOADFACTOR_DENOMINATOR >
capacity * HASHMAP_LOADFACTOR_NUMERATOR) {
capacity = capacity < 500 ? (capacity * 4) : (capacity * 2);
expandStorage(capacity);
ctr = lookup(key);
assert(_storage[ctr] != NULL);
}
}
return ctr;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const {
size_type ctr = lookup(key);
return (_storage[ctr] != NULL);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator[](const Key &key) {
return getVal(key);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator[](const Key &key) const {
return getVal(key);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) {
size_type ctr = lookupAndCreateIfMissing(key);
assert(_storage[ctr] != NULL);
return _storage[ctr]->_value;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const {
return getVal(key, _defaultVal);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key, const Val &defaultVal) const {
size_type ctr = lookup(key);
if (_storage[ctr] != NULL)
return _storage[ctr]->_value;
else
return defaultVal;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::setVal(const Key &key, const Val &val) {
size_type ctr = lookupAndCreateIfMissing(key);
assert(_storage[ctr] != NULL);
_storage[ctr]->_value = val;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(iterator entry) {
// Check whether we have a valid iterator
assert(entry._hashmap == this);
const size_type ctr = entry._idx;
assert(ctr <= _mask);
Node * const node = _storage[ctr];
assert(node != NULL);
assert(node != HASHMAP_DUMMY_NODE);
// If we remove a key, we replace it with a dummy node.
freeNode(node);
_storage[ctr] = HASHMAP_DUMMY_NODE;
_size--;
_deleted++;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) {
size_type ctr = lookup(key);
if (_storage[ctr] == NULL)
return;
// If we remove a key, we replace it with a dummy node.
freeNode(_storage[ctr]);
_storage[ctr] = HASHMAP_DUMMY_NODE;
_size--;
_deleted++;
return;
}
#undef HASHMAP_DUMMY_NODE
} // End of namespace Common
#endif

View File

@ -0,0 +1,182 @@
/* 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.
*
*/
#include "memorypool.h"
#include "common/util.h"
namespace Common {
enum {
INITIAL_CHUNKS_PER_PAGE = 8
};
static size_t adjustChunkSize(size_t chunkSize) {
// You must at least fit the pointer in the node (technically unneeded considering the next rounding statement)
chunkSize = MAX(chunkSize, sizeof(void *));
// There might be an alignment problem on some platforms when trying to load a void* on a non natural boundary
// so we round to the next sizeof(void *)
chunkSize = (chunkSize + sizeof(void *) - 1) & (~(sizeof(void *) - 1));
return chunkSize;
}
MemoryPool::MemoryPool(size_t chunkSize)
: _chunkSize(adjustChunkSize(chunkSize)) {
_next = NULL;
_chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
}
MemoryPool::~MemoryPool() {
#if 0
freeUnusedPages();
if (!_pages.empty())
warning("Memory leak found in pool");
#endif
for (size_t i = 0; i < _pages.size(); ++i)
::free(_pages[i].start);
}
void MemoryPool::allocPage() {
Page page;
// Allocate a new page
page.numChunks = _chunksPerPage;
assert(page.numChunks * _chunkSize < 16*1024*1024); // Refuse to allocate pages bigger than 16 MB
page.start = ::malloc(page.numChunks * _chunkSize);
assert(page.start);
_pages.push_back(page);
// Next time, we'll allocate a page twice as big as this one.
_chunksPerPage *= 2;
// Add the page to the pool of free chunk
addPageToPool(page);
}
void MemoryPool::addPageToPool(const Page &page) {
// Add all chunks of the new page to the linked list (pool) of free chunks
void *current = page.start;
for (size_t i = 1; i < page.numChunks; ++i) {
void *next = (byte *)current + _chunkSize;
*(void **)current = next;
current = next;
}
// Last chunk points to the old _next
*(void **)current = _next;
// From now on, the first free chunk is the first chunk of the new page
_next = page.start;
}
void *MemoryPool::allocChunk() {
// No free chunks left? Allocate a new page
if (!_next)
allocPage();
assert(_next);
void *result = _next;
_next = *(void **)result;
return result;
}
void MemoryPool::freeChunk(void *ptr) {
// Add the chunk back to (the start of) the list of free chunks
*(void **)ptr = _next;
_next = ptr;
}
// Technically not compliant C++ to compare unrelated pointers. In practice...
bool MemoryPool::isPointerInPage(void *ptr, const Page &page) {
return (ptr >= page.start) && (ptr < (char *)page.start + page.numChunks * _chunkSize);
}
void MemoryPool::freeUnusedPages() {
//std::sort(_pages.begin(), _pages.end());
Array<size_t> numberOfFreeChunksPerPage;
numberOfFreeChunksPerPage.resize(_pages.size());
for (size_t i = 0; i < numberOfFreeChunksPerPage.size(); ++i) {
numberOfFreeChunksPerPage[i] = 0;
}
// Compute for each page how many chunks in it are still in use.
void *iterator = _next;
while (iterator) {
// TODO: This should be a binary search (requiring us to keep _pages sorted)
for (size_t i = 0; i < _pages.size(); ++i) {
if (isPointerInPage(iterator, _pages[i])) {
++numberOfFreeChunksPerPage[i];
break;
}
}
iterator = *(void **)iterator;
}
// Free all pages which are not in use.
size_t freedPagesCount = 0;
for (size_t i = 0; i < _pages.size(); ++i) {
if (numberOfFreeChunksPerPage[i] == _pages[i].numChunks) {
// Remove all chunks of this page from the list of free chunks
void **iter2 = &_next;
while (*iter2) {
if (isPointerInPage(*iter2, _pages[i]))
*iter2 = **(void ***)iter2;
else
iter2 = *(void ***)iter2;
}
::free(_pages[i].start);
++freedPagesCount;
_pages[i].start = NULL;
}
}
// debug("freed %d pages out of %d", (int)freedPagesCount, (int)_pages.size());
// Remove all now unused pages
size_t newSize = 0;
for (size_t i = 0; i < _pages.size(); ++i) {
if (_pages[i].start != NULL) {
if (newSize != i)
_pages[newSize] = _pages[i];
++newSize;
}
}
_pages.resize(newSize);
// Reset _chunksPerPage
_chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
for (size_t i = 0; i < _pages.size(); ++i) {
if (_chunksPerPage < _pages[i].numChunks)
_chunksPerPage = _pages[i].numChunks;
}
}
} // End of namespace Common

View File

@ -0,0 +1,162 @@
/* 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.
*
*/
#ifndef COMMON_MEMORYPOOL_H
#define COMMON_MEMORYPOOL_H
#include "common/array.h"
namespace Common {
/**
* This class provides a pool of memory 'chunks' of identical size.
* The size of a chunk is determined when creating the memory pool.
*
* Using a memory pool may yield better performance and memory usage
* when allocating and deallocating many memory blocks of equal size.
* E.g. the Common::String class uses a memory pool for the refCount
* variables (each the size of an int) it allocates for each string
* instance.
*/
class MemoryPool {
protected:
MemoryPool(const MemoryPool&);
MemoryPool& operator=(const MemoryPool&);
struct Page {
void *start;
size_t numChunks;
};
const size_t _chunkSize;
Array<Page> _pages;
void *_next;
size_t _chunksPerPage;
void allocPage();
void addPageToPool(const Page &page);
bool isPointerInPage(void *ptr, const Page &page);
public:
/**
* Constructor for a memory pool with the given chunk size.
* @param chunkSize the chunk size of this memory pool
*/
explicit MemoryPool(size_t chunkSize);
~MemoryPool();
/**
* Allocate a new chunk from the memory pool.
*/
void *allocChunk();
/**
* Return a chunk to the memory pool. The given pointer must have
* been obtained from calling the allocChunk() method of the very
* same MemoryPool instance. Passing any other pointer (e.g. to
* a chunk from another MemoryPool, or a malloc'ed memory block)
* will lead to undefined behavior and may result in a crash (if
* you are lucky) or in silent data corruption.
*/
void freeChunk(void *ptr);
/**
* Perform garbage collection. The memory pool stores all the
* chunks it manages in memory 'pages' obtained via the classic
* memory allocation APIs (i.e. malloc/free). Ordinarily, once
* a page has been allocated, it won't be released again during
* the life time of the memory pool. The exception is when this
* method is called.
*/
void freeUnusedPages();
/**
* Return the chunk size used by this memory pool.
*/
size_t getChunkSize() const { return _chunkSize; }
};
/**
* This is a memory pool which already contains in itself some storage
* space for a fixed number of chunks. Thus if the memory pool is only
* lightly used, no malloc() calls have to be made at all.
*/
template<size_t CHUNK_SIZE, size_t NUM_INTERNAL_CHUNKS = 32>
class FixedSizeMemoryPool : public MemoryPool {
private:
enum {
REAL_CHUNK_SIZE = (CHUNK_SIZE + sizeof(void *) - 1) & (~(sizeof(void *) - 1))
};
byte _storage[NUM_INTERNAL_CHUNKS * REAL_CHUNK_SIZE];
public:
FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {
assert(REAL_CHUNK_SIZE == _chunkSize);
// Insert some static storage
Page internalPage = { _storage, NUM_INTERNAL_CHUNKS };
addPageToPool(internalPage);
}
};
// Ensure NUM_INTERNAL_CHUNKS == 0 results in a compile error
template<size_t CHUNK_SIZE>
class FixedSizeMemoryPool<CHUNK_SIZE,0> : public MemoryPool {
public:
FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {}
};
/**
* A memory pool for C++ objects.
*/
template<class T, size_t NUM_INTERNAL_CHUNKS = 32>
class ObjectPool : public FixedSizeMemoryPool<sizeof(T), NUM_INTERNAL_CHUNKS> {
public:
/**
* Return the memory chunk used as storage for the given object back
* to the pool, after calling its destructor.
*/
void deleteChunk(T *ptr) {
ptr->~T();
this->freeChunk(ptr);
}
};
} // End of namespace Common
/**
* A custom placement new operator, using an arbitrary MemoryPool.
*
* This *should* work with all C++ implementations, but may not.
*
* For details on using placement new for custom allocators, see e.g.
* <http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14>
*/
inline void *operator new(size_t nbytes, Common::MemoryPool &pool) {
assert(nbytes <= pool.getChunkSize());
return pool.allocChunk();
}
inline void operator delete(void *p, Common::MemoryPool &pool) {
pool.freeChunk(p);
}
#endif

View File

@ -0,0 +1,15 @@
MODULE := devtools/create_ultima
MODULE_OBJS := \
create_ultima.o \
archive.o \
ultima1_map.o \
hashmap.o \
memorypool.o \
str.o
# Set the name of the executable
TOOL_EXECUTABLE := create_ultima
# Include common rules
include $(srcdir)/rules.mk

View File

@ -0,0 +1,786 @@
/* 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.
*
*/
#include "common/hash-str.h"
#include "common/list.h"
#include "memorypool.h"
#include "common/str.h"
#include "common/util.h"
namespace Common {
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
static uint32 computeCapacity(uint32 len) {
// By default, for the capacity we use the next multiple of 32
return ((len + 32 - 1) & ~0x1F);
}
String::String(const char *str) : _size(0), _str(_storage) {
if (str == 0) {
_storage[0] = 0;
_size = 0;
} else
initWithCStr(str, strlen(str));
}
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
initWithCStr(str, len);
}
String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
assert(endP >= beginP);
initWithCStr(beginP, endP - beginP);
}
void String::initWithCStr(const char *str, uint32 len) {
assert(str);
// Init _storage member explicitly (ie. without calling its constructor)
// for GCC 2.95.x compatibility (see also tracker item #1602879).
_storage[0] = 0;
_size = len;
if (len >= _builtinCapacity) {
// Not enough internal storage, so allocate more
_extern._capacity = computeCapacity(len+1);
_extern._refCount = 0;
_str = new char[_extern._capacity];
assert(_str != 0);
}
// Copy the string into the storage area
memmove(_str, str, len);
_str[len] = 0;
}
String::String(const String &str)
: _size(str._size) {
if (str.isStorageIntern()) {
// String in internal storage: just copy it
memcpy(_storage, str._storage, _builtinCapacity);
_str = _storage;
} else {
// String in external storage: use refcount mechanism
str.incRefCount();
_extern._refCount = str._extern._refCount;
_extern._capacity = str._extern._capacity;
_str = str._str;
}
assert(_str != 0);
}
String::String(char c)
: _size(0), _str(_storage) {
_storage[0] = c;
_storage[1] = 0;
_size = (c == 0) ? 0 : 1;
}
String::~String() {
decRefCount(_extern._refCount);
}
void String::makeUnique() {
ensureCapacity(_size, true);
}
/**
* Ensure that enough storage is available to store at least new_size
* characters plus a null byte. In addition, if we currently share
* the storage with another string, unshare it, so that we can safely
* write to the storage.
*/
void String::ensureCapacity(uint32 new_size, bool keep_old) {
bool isShared;
uint32 curCapacity, newCapacity;
char *newStorage;
int *oldRefCount = _extern._refCount;
if (isStorageIntern()) {
isShared = false;
curCapacity = _builtinCapacity;
} else {
isShared = (oldRefCount && *oldRefCount > 1);
curCapacity = _extern._capacity;
}
// Special case: If there is enough space, and we do not share
// the storage, then there is nothing to do.
if (!isShared && new_size < curCapacity)
return;
if (isShared && new_size < _builtinCapacity) {
// We share the storage, but there is enough internal storage: Use that.
newStorage = _storage;
newCapacity = _builtinCapacity;
} else {
// We need to allocate storage on the heap!
// Compute a suitable new capacity limit
// If the current capacity is sufficient we use the same capacity
if (new_size < curCapacity)
newCapacity = curCapacity;
else
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
// Allocate new storage
newStorage = new char[newCapacity];
assert(newStorage);
}
// Copy old data if needed, elsewise reset the new storage.
if (keep_old) {
assert(_size < newCapacity);
memcpy(newStorage, _str, _size + 1);
} else {
_size = 0;
newStorage[0] = 0;
}
// Release hold on the old storage ...
decRefCount(oldRefCount);
// ... in favor of the new storage
_str = newStorage;
if (!isStorageIntern()) {
// Set the ref count & capacity if we use an external storage.
// It is important to do this *after* copying any old content,
// else we would override data that has not yet been copied!
_extern._refCount = 0;
_extern._capacity = newCapacity;
}
}
void String::incRefCount() const {
assert(!isStorageIntern());
if (_extern._refCount == 0) {
if (g_refCountPool == 0) {
g_refCountPool = new MemoryPool(sizeof(int));
assert(g_refCountPool);
}
_extern._refCount = (int *)g_refCountPool->allocChunk();
*_extern._refCount = 2;
} else {
++(*_extern._refCount);
}
}
void String::decRefCount(int *oldRefCount) {
if (isStorageIntern())
return;
if (oldRefCount) {
--(*oldRefCount);
}
if (!oldRefCount || *oldRefCount <= 0) {
// The ref count reached zero, so we free the string storage
// and the ref count storage.
if (oldRefCount) {
assert(g_refCountPool);
g_refCountPool->freeChunk(oldRefCount);
}
delete[] _str;
// Even though _str points to a freed memory block now,
// we do not change its value, because any code that calls
// decRefCount will have to do this afterwards anyway.
}
}
String &String::operator=(const char *str) {
uint32 len = strlen(str);
ensureCapacity(len, false);
_size = len;
memmove(_str, str, len + 1);
return *this;
}
String &String::operator=(const String &str) {
if (&str == this)
return *this;
if (str.isStorageIntern()) {
decRefCount(_extern._refCount);
_size = str._size;
_str = _storage;
memcpy(_str, str._str, _size + 1);
} else {
str.incRefCount();
decRefCount(_extern._refCount);
_extern._refCount = str._extern._refCount;
_extern._capacity = str._extern._capacity;
_size = str._size;
_str = str._str;
}
return *this;
}
String &String::operator=(char c) {
decRefCount(_extern._refCount);
_str = _storage;
_str[0] = c;
_str[1] = 0;
_size = (c == 0) ? 0 : 1;
return *this;
}
String &String::operator+=(const char *str) {
if (_str <= str && str <= _str + _size)
return operator+=(String(str));
int len = strlen(str);
if (len > 0) {
ensureCapacity(_size + len, true);
memcpy(_str + _size, str, len + 1);
_size += len;
}
return *this;
}
String &String::operator+=(const String &str) {
if (&str == this)
return operator+=(String(str));
int len = str._size;
if (len > 0) {
ensureCapacity(_size + len, true);
memcpy(_str + _size, str._str, len + 1);
_size += len;
}
return *this;
}
String &String::operator+=(char c) {
ensureCapacity(_size + 1, true);
_str[_size++] = c;
_str[_size] = 0;
return *this;
}
bool String::hasPrefix(const String &x) const {
return hasPrefix(x.c_str());
}
bool String::hasPrefix(const char *x) const {
assert(x != 0);
// Compare x with the start of _str.
const char *y = c_str();
while (*x && *x == *y) {
++x;
++y;
}
// It's a prefix, if and only if all letters in x are 'used up' before
// _str ends.
return *x == 0;
}
bool String::hasSuffix(const String &x) const {
return hasSuffix(x.c_str());
}
bool String::hasSuffix(const char *x) const {
assert(x != 0);
// Compare x with the end of _str.
const uint32 x_size = strlen(x);
if (x_size > _size)
return false;
const char *y = c_str() + _size - x_size;
while (*x && *x == *y) {
++x;
++y;
}
// It's a suffix, if and only if all letters in x are 'used up' before
// _str ends.
return *x == 0;
}
bool String::contains(const String &x) const {
return strstr(c_str(), x.c_str()) != NULL;
}
bool String::contains(const char *x) const {
assert(x != 0);
return strstr(c_str(), x) != NULL;
}
bool String::contains(char x) const {
return strchr(c_str(), x) != NULL;
}
void String::deleteLastChar() {
if (_size > 0)
deleteChar(_size - 1);
}
void String::deleteChar(uint32 p) {
assert(p < _size);
makeUnique();
while (p++ < _size)
_str[p - 1] = _str[p];
_size--;
}
void String::erase(uint32 p, uint32 len) {
assert(p < _size);
makeUnique();
// If len == npos or p + len is over the end, remove all the way to the end
if (len == npos || p + len >= _size) {
// Delete char at p as well. So _size = (p - 1) + 1
_size = p;
// Null terminate
_str[_size] = 0;
return;
}
for ( ; p + len <= _size; p++) {
_str[p] = _str[p + len];
}
_size -= len;
}
void String::clear() {
decRefCount(_extern._refCount);
_size = 0;
_str = _storage;
_storage[0] = 0;
}
void String::setChar(char c, uint32 p) {
assert(p < _size);
makeUnique();
_str[p] = c;
}
void String::insertChar(char c, uint32 p) {
assert(p <= _size);
ensureCapacity(_size + 1, true);
_size++;
for (uint32 i = _size; i > p; --i)
_str[i] = _str[i - 1];
_str[p] = c;
}
void String::toLowercase() {
makeUnique();
for (uint32 i = 0; i < _size; ++i)
_str[i] = tolower(_str[i]);
}
void String::toUppercase() {
makeUnique();
for (uint32 i = 0; i < _size; ++i)
_str[i] = toupper(_str[i]);
}
uint String::hash() const {
return hashit(c_str());
}
// static
String String::format(const char *fmt, ...) {
String output;
va_list va;
va_start(va, fmt);
output = String::vformat(fmt, va);
va_end(va);
return output;
}
// static
String String::vformat(const char *fmt, va_list args) {
String output;
assert(output.isStorageIntern());
va_list va;
scumm_va_copy(va, args);
int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
va_end(va);
if (len == -1 || len == _builtinCapacity - 1) {
// MSVC and IRIX don't return the size the full string would take up.
// MSVC returns -1, IRIX returns the number of characters actually written,
// which is at the most the size of the buffer minus one, as the string is
// truncated to fit.
// We assume MSVC failed to output the correct, null-terminated string
// if the return value is either -1 or size.
// For IRIX, because we lack a better mechanism, we assume failure
// if the return value equals size - 1.
// The downside to this is that whenever we try to format a string where the
// size is 1 below the built-in capacity, the size is needlessly increased.
// Try increasing the size of the string until it fits.
int size = _builtinCapacity;
do {
size *= 2;
output.ensureCapacity(size - 1, false);
assert(!output.isStorageIntern());
size = output._extern._capacity;
scumm_va_copy(va, args);
len = vsnprintf(output._str, size, fmt, va);
va_end(va);
} while (len == -1 || len >= size - 1);
output._size = len;
} else if (len < (int)_builtinCapacity) {
// vsnprintf succeeded
output._size = len;
} else {
// vsnprintf didn't have enough space, so grow buffer
output.ensureCapacity(len, false);
scumm_va_copy(va, args);
int len2 = vsnprintf(output._str, len+1, fmt, va);
va_end(va);
assert(len == len2);
output._size = len2;
}
return output;
}
#pragma mark -
bool String::operator==(const String &x) const {
return equals(x);
}
bool String::operator==(const char *x) const {
assert(x != 0);
return equals(x);
}
bool String::operator!=(const String &x) const {
return !equals(x);
}
bool String::operator !=(const char *x) const {
assert(x != 0);
return !equals(x);
}
bool String::operator<(const String &x) const {
return compareTo(x) < 0;
}
bool String::operator<=(const String &x) const {
return compareTo(x) <= 0;
}
bool String::operator>(const String &x) const {
return compareTo(x) > 0;
}
bool String::operator>=(const String &x) const {
return compareTo(x) >= 0;
}
#pragma mark -
bool operator==(const char* y, const String &x) {
return (x == y);
}
bool operator!=(const char* y, const String &x) {
return x != y;
}
#pragma mark -
bool String::equals(const String &x) const {
return (0 == compareTo(x));
}
bool String::equals(const char *x) const {
assert(x != 0);
return (0 == compareTo(x));
}
bool String::equalsIgnoreCase(const String &x) const {
return (0 == compareToIgnoreCase(x));
}
bool String::equalsIgnoreCase(const char *x) const {
assert(x != 0);
return (0 == compareToIgnoreCase(x));
}
int String::compareTo(const String &x) const {
return compareTo(x.c_str());
}
int String::compareTo(const char *x) const {
assert(x != 0);
return strcmp(c_str(), x);
}
int String::compareToIgnoreCase(const String &x) const {
return compareToIgnoreCase(x.c_str());
}
int String::compareToIgnoreCase(const char *x) const {
assert(x != 0);
return scumm_stricmp(c_str(), x);
}
#pragma mark -
String operator+(const String &x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const char *x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const String &x, const char *y) {
String temp(x);
temp += y;
return temp;
}
String operator+(char x, const String &y) {
String temp(x);
temp += y;
return temp;
}
String operator+(const String &x, char y) {
String temp(x);
temp += y;
return temp;
}
String lastPathComponent(const String &path, const char sep) {
const char *str = path.c_str();
const char *last = str + path.size();
// Skip over trailing slashes
while (last > str && *(last-1) == sep)
--last;
// Path consisted of only slashes -> return empty string
if (last == str)
return String();
// Now scan the whole component
const char *first = last - 1;
while (first > str && *first != sep)
--first;
if (*first == sep)
first++;
return String(first, last);
}
String normalizePath(const String &path, const char sep) {
if (path.empty())
return path;
const char *cur = path.c_str();
String result;
// If there is a leading slash, preserve that:
if (*cur == sep) {
result += sep;
// Skip over multiple leading slashes, so "//" equals "/"
while (*cur == sep)
++cur;
}
// Scan for path components till the end of the String
List<String> comps;
while (*cur != 0) {
const char *start = cur;
// Scan till the next path separator resp. the end of the string
while (*cur != sep && *cur != 0)
cur++;
const String component(start, cur);
if (component.empty() || component == ".") {
// Skip empty components and dot components
} else if (!comps.empty() && component == ".." && comps.back() != "..") {
// If stack is non-empty and top is not "..", remove top
comps.pop_back();
} else {
// Add the component to the stack
comps.push_back(component);
}
// Skip over separator chars
while (*cur == sep)
cur++;
}
// Finally, assemble all components back into a path
while (!comps.empty()) {
result += comps.front();
comps.pop_front();
if (!comps.empty())
result += sep;
}
return result;
}
size_t strlcpy(char *dst, const char *src, size_t size) {
// Our backup of the source's start, we need this
// to calculate the source's length.
const char * const srcStart = src;
// In case a non-empty size was specified we
// copy over (size - 1) bytes at max.
if (size != 0) {
// Copy over (size - 1) bytes at max.
while (--size != 0) {
if ((*dst++ = *src) == 0)
break;
++src;
}
// In case the source string was longer than the
// destination, we need to add a terminating
// zero.
if (size == 0)
*dst = 0;
}
// Move to the terminating zero of the source
// string, we need this to determine the length
// of the source string.
while (*src)
++src;
// Return the source string's length.
return src - srcStart;
}
size_t strlcat(char *dst, const char *src, size_t size) {
// In case the destination buffer does not contain
// space for at least 1 character, we will just
// return the source string's length.
if (size == 0)
return strlen(src);
// Our backup of the source's start, we need this
// to calculate the source's length.
const char * const srcStart = src;
// Our backup of the destination's start, we need
// this to calculate the destination's length.
const char * const dstStart = dst;
// Search the end of the destination, but do not
// move past the terminating zero.
while (size-- != 0 && *dst != 0)
++dst;
// Calculate the destination's length;
const size_t dstLength = dst - dstStart;
// In case we reached the end of the destination
// buffer before we had a chance to append any
// characters we will just return the destination
// length plus the source string's length.
if (size == 0)
return dstLength + strlen(srcStart);
// Copy over all of the source that fits
// the destination buffer. We also need
// to take the terminating zero we will
// add into consideration.
while (size-- != 0 && *src != 0)
*dst++ = *src++;
*dst = 0;
// Move to the terminating zero of the source
// string, we need this to determine the length
// of the source string.
while (*src)
++src;
// Return the total length of the result string
return dstLength + (src - srcStart);
}
} // End of namespace Common
// Portable implementation of stricmp / strcasecmp / strcmpi.
// TODO: Rename this to Common::strcasecmp
int scumm_stricmp(const char *s1, const char *s2) {
byte l1, l2;
do {
// Don't use ++ inside tolower, in case the macro uses its
// arguments more than once.
l1 = (byte)*s1++;
l1 = tolower(l1);
l2 = (byte)*s2++;
l2 = tolower(l2);
} while (l1 == l2 && l1 != 0);
return l1 - l2;
}
// Portable implementation of strnicmp / strncasecmp / strncmpi.
// TODO: Rename this to Common::strncasecmp
int scumm_strnicmp(const char *s1, const char *s2, uint n) {
byte l1, l2;
do {
if (n-- == 0)
return 0; // no difference found so far -> signal equality
// Don't use ++ inside tolower, in case the macro uses its
// arguments more than once.
l1 = (byte)*s1++;
l1 = tolower(l1);
l2 = (byte)*s2++;
l2 = tolower(l2);
} while (l1 == l2 && l1 != 0);
return l1 - l2;
}

View File

@ -0,0 +1,386 @@
/* 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.
*
*/
#ifndef COMMON_STRING_H
#define COMMON_STRING_H
#include "common/scummsys.h"
#include <stdarg.h>
namespace Common {
/**
* Simple string class for ScummVM. Provides automatic storage managment,
* and overloads several operators in a 'natural' fashion, mimicking
* the std::string class. Even provides simple iterators.
*
* This class tries to avoid allocating lots of small blocks on the heap,
* since that is inefficient on several platforms supported by ScummVM.
* Instead, small strings are stored 'inside' the string object (i.e. on
* the stack, for stack allocated objects), and only for strings exceeding
* a certain length do we allocate a buffer on the heap.
*
* The presence of \0 characters in the string will cause undefined
* behavior in some operations.
*/
class String {
public:
static const uint32 npos = 0xFFFFFFFF;
protected:
/**
* The size of the internal storage. Increasing this means less heap
* allocations are needed, at the cost of more stack memory usage,
* and of course lots of wasted memory. Empirically, 90% or more of
* all String instances are less than 32 chars long. If a platform
* is very short on stack space, it would be possible to lower this.
* A value of 24 still seems acceptable, though considerably worse,
* while 16 seems to be the lowest you want to go... Anything lower
* than 8 makes no sense, since that's the size of member _extern
* (on 32 bit machines; 12 bytes on systems with 64bit pointers).
*/
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
/**
* Length of the string. Stored to avoid having to call strlen
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
* on purpose :-).
*/
uint32 _size;
/**
* Pointer to the actual string storage. Either points to _storage,
* or to a block allocated on the heap via malloc.
*/
char *_str;
union {
/**
* Internal string storage.
*/
char _storage[_builtinCapacity];
/**
* External string storage data -- the refcounter, and the
* capacity of the string _str points to.
*/
struct {
mutable int *_refCount;
uint32 _capacity;
} _extern;
};
inline bool isStorageIntern() const {
return _str == _storage;
}
public:
/** Construct a new empty string. */
String() : _size(0), _str(_storage) { _storage[0] = 0; }
/** Construct a new string from the given NULL-terminated C string. */
String(const char *str);
/** Construct a new string containing exactly len characters read from address str. */
String(const char *str, uint32 len);
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
String(const char *beginP, const char *endP);
/** Construct a copy of the given string. */
String(const String &str);
/** Construct a string consisting of the given character. */
explicit String(char c);
~String();
String &operator=(const char *str);
String &operator=(const String &str);
String &operator=(char c);
String &operator+=(const char *str);
String &operator+=(const String &str);
String &operator+=(char c);
bool operator==(const String &x) const;
bool operator==(const char *x) const;
bool operator!=(const String &x) const;
bool operator!=(const char *x) const;
bool operator<(const String &x) const;
bool operator<=(const String &x) const;
bool operator>(const String &x) const;
bool operator>=(const String &x) const;
bool equals(const String &x) const;
bool equalsIgnoreCase(const String &x) const;
int compareTo(const String &x) const; // strcmp clone
int compareToIgnoreCase(const String &x) const; // stricmp clone
bool equals(const char *x) const;
bool equalsIgnoreCase(const char *x) const;
int compareTo(const char *x) const; // strcmp clone
int compareToIgnoreCase(const char *x) const; // stricmp clone
bool hasSuffix(const String &x) const;
bool hasSuffix(const char *x) const;
bool hasPrefix(const String &x) const;
bool hasPrefix(const char *x) const;
bool contains(const String &x) const;
bool contains(const char *x) const;
bool contains(char x) const;
inline const char *c_str() const { return _str; }
inline uint size() const { return _size; }
inline bool empty() const { return (_size == 0); }
char firstChar() const { return (_size > 0) ? _str[0] : 0; }
char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
char operator[](int idx) const {
assert(_str && idx >= 0 && idx < (int)_size);
return _str[idx];
}
/** Remove the last character from the string. */
void deleteLastChar();
/** Remove the character at position p from the string. */
void deleteChar(uint32 p);
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
void erase(uint32 p, uint32 len = npos);
/** Set character c at position p, replacing the previous character there. */
void setChar(char c, uint32 p);
/** Insert character c before position p. */
void insertChar(char c, uint32 p);
/** Clears the string, making it empty. */
void clear();
/** Convert all characters in the string to lowercase. */
void toLowercase();
/** Convert all characters in the string to uppercase. */
void toUppercase();
/**
* Removes trailing and leading whitespaces. Uses isspace() to decide
* what is whitespace and what not.
*/
void trim();
uint hash() const;
/**
* Print formatted data into a String object. Similar to sprintf,
* except that it stores the result in (variably sized) String
* instead of a fixed size buffer.
*/
static String format(const char *fmt, ...) GCC_PRINTF(1,2);
/**
* Print formatted data into a String object. Similar to vsprintf,
* except that it stores the result in (variably sized) String
* instead of a fixed size buffer.
*/
static String vformat(const char *fmt, va_list args);
public:
typedef char value_type;
/**
* Unsigned version of the underlying type. This can be used to cast
* individual string characters to bigger integer types without sign
* extension happening.
*/
typedef unsigned char unsigned_type;
typedef char * iterator;
typedef const char * const_iterator;
iterator begin() {
// Since the user could potentially
// change the string via the returned
// iterator we have to assure we are
// pointing to a unique storage.
makeUnique();
return _str;
}
iterator end() {
return begin() + size();
}
const_iterator begin() const {
return _str;
}
const_iterator end() const {
return begin() + size();
}
protected:
void makeUnique();
void ensureCapacity(uint32 new_size, bool keep_old);
void incRefCount() const;
void decRefCount(int *oldRefCount);
void initWithCStr(const char *str, uint32 len);
};
// Append two strings to form a new (temp) string
String operator+(const String &x, const String &y);
String operator+(const char *x, const String &y);
String operator+(const String &x, const char *y);
String operator+(const String &x, char y);
String operator+(char x, const String &y);
// Some useful additional comparison operators for Strings
bool operator==(const char *x, const String &y);
bool operator!=(const char *x, const String &y);
// Utility functions to remove leading and trailing whitespaces
extern char *ltrim(char *t);
extern char *rtrim(char *t);
extern char *trim(char *t);
/**
* Returns the last component of a given path.
*
* Examples:
* /foo/bar.txt would return 'bar.txt'
* /foo/bar/ would return 'bar'
* /foo/./bar// would return 'bar'
*
* @param path the path of which we want to know the last component
* @param sep character used to separate path components
* @return The last component of the path.
*/
String lastPathComponent(const String &path, const char sep);
/**
* Normalize a given path to a canonical form. In particular:
* - trailing separators are removed: /foo/bar/ -> /foo/bar
* - double separators (= empty components) are removed: /foo//bar -> /foo/bar
* - dot components are removed: /foo/./bar -> /foo/bar
*
* @todo remove double dot components: /foo/baz/../bar -> /foo/bar
*
* @param path the path to normalize
* @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
* @return the normalized path
*/
String normalizePath(const String &path, const char sep);
/**
* Simple DOS-style pattern matching function (understands * and ? like used in DOS).
* Taken from exult/files/listfiles.cc
*
* Token meaning:
* "*": any character, any amount of times.
* "?": any character, only once.
* "#": any decimal digit, only once.
*
* Example strings/patterns:
* String: monkey.s01 Pattern: monkey.s?? => true
* String: monkey.s101 Pattern: monkey.s?? => false
* String: monkey.s99 Pattern: monkey.s?1 => false
* String: monkey.s101 Pattern: monkey.s* => true
* String: monkey.s99 Pattern: monkey.s*1 => false
* String: monkey.s01 Pattern: monkey.s## => true
* String: monkey.s01 Pattern: monkey.### => false
*
* @param str Text to be matched against the given pattern.
* @param pat Glob pattern.
* @param ignoreCase Whether to ignore the case when doing pattern match
* @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly.
*
* @return true if str matches the pattern, false otherwise.
*/
bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
/**
* Take a 32 bit value and turn it into a four character string, where each of
* the four bytes is turned into one character. Most significant byte is printed
* first.
*/
String tag2string(uint32 tag);
/**
* Copy up to size - 1 characters from src to dst and also zero terminate the
* result. Note that src must be a zero terminated string.
*
* In case size is zero this function just returns the length of the source
* string.
*
* @note This is modeled after OpenBSD's strlcpy. See the manpage here:
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy
*
* @param dst The destination buffer.
* @param src The source string.
* @param size The size of the destination buffer.
* @return The length of the (non-truncated) result, i.e. strlen(src).
*/
size_t strlcpy(char *dst, const char *src, size_t size);
/**
* Append the string src to the string dst. Note that both src and dst must be
* zero terminated. The result will be zero terminated. At most
* "size - strlen(dst) - 1" bytes will be appended.
*
* In case the dst string does not contain a zero within the first "size" bytes
* the dst string will not be changed and size + strlen(src) is returned.
*
* @note This is modeled after OpenBSD's strlcat. See the manpage here:
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat
*
* @param dst The string the source string should be appended to.
* @param src The source string.
* @param size The (total) size of the destination buffer.
* @return The length of the (non-truncated) result. That is
* strlen(dst) + strlen(src). In case strlen(dst) > size
* size + strlen(src) is returned.
*/
size_t strlcat(char *dst, const char *src, size_t size);
/**
* Convenience wrapper for tag2string which "returns" a C string.
* Note: It is *NOT* safe to do anything with the return value other than directly
* copying or printing it.
*/
#define tag2str(x) Common::tag2string(x).c_str()
} // End of namespace Common
extern int scumm_stricmp(const char *s1, const char *s2);
extern int scumm_strnicmp(const char *s1, const char *s2, uint n);
#endif

View File

@ -0,0 +1,40 @@
/* 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.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main
#include "ultima1_map.h"
#include "file.h"
void writeUltima1EnhancedMap(Archive &a) {
Common::MemFile map;
map.writeString("TEST");
a.add("ULTIMA1ENH/MAP", map);
}

View File

@ -0,0 +1,30 @@
/* 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.
*
*/
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include "archive.h"
extern void writeUltima1EnhancedMap(Archive &a);
#endif