diff options
author | Paul Gilbert | 2016-05-15 18:43:33 -0400 |
---|---|---|
committer | Paul Gilbert | 2016-07-15 19:10:30 -0400 |
commit | 2680caa5bde09e3ecc7a1c8ef6c345e2bc9a134b (patch) | |
tree | 82eb9e38e1010b1033ce74418f4a3d917e28ee7a | |
parent | 76b61324de85c92302ed76b67a09c1c60c9b8470 (diff) | |
download | scummvm-rg350-2680caa5bde09e3ecc7a1c8ef6c345e2bc9a134b.tar.gz scummvm-rg350-2680caa5bde09e3ecc7a1c8ef6c345e2bc9a134b.tar.bz2 scummvm-rg350-2680caa5bde09e3ecc7a1c8ef6c345e2bc9a134b.zip |
DEVTOOLS: Creation of titanic.dat for holding static data
-rw-r--r-- | devtools/create_titanic/create_titanic_dat.cpp | 205 | ||||
-rw-r--r-- | devtools/create_titanic/file.h | 213 | ||||
-rw-r--r-- | devtools/create_titanic/hash-str.h | 86 | ||||
-rw-r--r-- | devtools/create_titanic/hashmap.cpp | 109 | ||||
-rw-r--r-- | devtools/create_titanic/hashmap.h | 637 | ||||
-rw-r--r-- | devtools/create_titanic/module.mk | 15 | ||||
-rw-r--r-- | devtools/create_titanic/str.cpp | 786 | ||||
-rw-r--r-- | devtools/create_titanic/str.h | 386 | ||||
-rw-r--r-- | devtools/create_titanic/winexe.cpp | 83 | ||||
-rw-r--r-- | devtools/create_titanic/winexe.h | 70 | ||||
-rw-r--r-- | devtools/create_titanic/winexe_pe.cpp | 260 | ||||
-rw-r--r-- | devtools/create_titanic/winexe_pe.h | 120 |
12 files changed, 2970 insertions, 0 deletions
diff --git a/devtools/create_titanic/create_titanic_dat.cpp b/devtools/create_titanic/create_titanic_dat.cpp new file mode 100644 index 0000000000..e41b125713 --- /dev/null +++ b/devtools/create_titanic/create_titanic_dat.cpp @@ -0,0 +1,205 @@ +/* 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 "common/language.h" +#include "common/rect.h" +#include "winexe_pe.h" +#include "file.h" + +/** + * Format of the access.dat file that will be created: + * 4 Bytes - Magic string 'SVTN' to identify valid data file + * 2 bytes - Version number + * + * Following is a series of index entries with the following fields: + * 4 bytes - offset in file of entry + * 4 bytes - size of entry in the file + * ASCIIZ - name of the resource + */ + +#define VERSION_NUMBER 1 + +Common::File inputFile, outputFile; +Common::PEResources res; +uint headerOffset = 6; +uint dataOffset = 0x200; +#define SEGMENT_OFFSET 0x401C00 + +void NORETURN_PRE error(const char *s, ...) { + printf("%s\n", s); + exit(1); +} + +void writeEntryHeader(const char *name, uint offset, uint size) { + assert(headerOffset < 0x200); + outputFile.seek(headerOffset); + outputFile.writeLong(offset); + outputFile.writeLong(size); + outputFile.writeString(name); + + headerOffset += 8 + strlen(name) + 1; +} + +void writeFinalEntryHeader() { + assert(headerOffset <= 0x1F8); + outputFile.seek(headerOffset); + outputFile.writeLong(0); + outputFile.writeLong(0); +} + +void writeStringArray(const char *name, uint offset, int count) { + outputFile.seek(dataOffset); + + inputFile.seek(offset); + uint *offsets = new uint[count]; + for (int idx = 0; idx < count; ++idx) + offsets[idx] = inputFile.readLong(); + + // Iterate through reading each string + for (int idx = 0; idx < count; ++idx) { + if (offsets[idx]) { + inputFile.seek(offsets[idx] - SEGMENT_OFFSET); + outputFile.writeString(inputFile); + } else { + outputFile.writeString(""); + } + } + + uint size = outputFile.size() - dataOffset; + writeEntryHeader(name, dataOffset, size); + dataOffset += size; + + delete[] offsets; +} + +Common::WinResourceID getResId(uint id) { + return Common::WinResourceID(id); +} + +Common::WinResourceID getResId(const char *id) { + if (!strcmp(id, "Bitmap")) + return Common::WinResourceID(2); + + return Common::WinResourceID(id); +} + +void writeResource(const char *name, Common::File *file) { + outputFile.seek(dataOffset); + outputFile.write(*file, file->size()); + + writeEntryHeader(name, dataOffset, file->size()); + dataOffset += file->size(); + delete file; +} + +void writeResource(const char *sectionStr, const uint32 resId) { + char nameBuffer[256]; + sprintf(nameBuffer, "%s/%d", sectionStr, resId); + + Common::File *file = res.getResource(getResId(sectionStr), resId); + assert(file); + writeResource(nameBuffer, file); +} + +void writeResource(const char *sectionStr, const char *resId) { + char nameBuffer[256]; + sprintf(nameBuffer, "%s/%s", sectionStr, resId); + + Common::File *file = res.getResource(getResId(sectionStr), + Common::WinResourceID(resId)); + assert(file); + writeResource(nameBuffer, file); +} + +void writeHeader() { + // Write out magic string + const char *MAGIC_STR = "SVTN"; + outputFile.write(MAGIC_STR, 4); + + // Write out version number + outputFile.writeWord(VERSION_NUMBER); +} + +void writeData() { + writeStringArray("TEXT/STRINGS1", 0x21B7C8, 376); + writeStringArray("TEXT/STRINGS2", 0x21BDB0, 218); + writeStringArray("TEXT/STRINGS3", 0x21C120, 1576); + writeStringArray("TEXT/STRINGS4", 0x21D9C8, 82); + + writeResource("Bitmap", "BACKDROP"); + writeResource("Bitmap", "EVILTWIN"); + writeResource("Bitmap", "RESTORED"); + writeResource("Bitmap", "RESTOREF"); + writeResource("Bitmap", "RESTOREU"); + writeResource("Bitmap", "STARTD"); + writeResource("Bitmap", "STARTF"); + writeResource("Bitmap", "STARTU"); + writeResource("Bitmap", "TITANIC"); + writeResource("Bitmap", 133); + writeResource("Bitmap", 164); + writeResource("Bitmap", 165); + + writeResource("STFONT", 149); + writeResource("STFONT", 151); + writeResource("STFONT", 152); + writeResource("STFONT", 153); + + writeResource("TEXT", "STVOCAB.TXT"); + writeResource("TEXT", "JRQUOTES.TXT"); + writeResource("TEXT", 155); +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + printf("Format: %s ST.exe titanic.dat\n", argv[0]); + exit(0); + } + + if (!inputFile.open(argv[1])) { + error("Could not open input file"); + } + res.loadFromEXE(argv[1]); + + if (!outputFile.open(argv[2], Common::kFileWriteMode)) { + error("Could not open output file"); + } + + writeHeader(); + writeData(); + writeFinalEntryHeader(); + + inputFile.close(); + outputFile.close(); + return 0; +} diff --git a/devtools/create_titanic/file.h b/devtools/create_titanic/file.h new file mode 100644 index 0000000000..e8d49600e8 --- /dev/null +++ b/devtools/create_titanic/file.h @@ -0,0 +1,213 @@ +/* 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/algorithm.h" + +namespace Common { + +enum AccessMode { + kFileReadMode = 1, + kFileWriteMode = 2 +}; + +class File { +private: + ::FILE *_f; + const byte *_memPtr; + size_t _offset, _size; +public: + File() : _f(nullptr), _memPtr(nullptr), _offset(0), _size(0) {} + + bool open(const char *filename, AccessMode mode = kFileReadMode) { + _memPtr = nullptr; + _f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb+"); + return (_f != NULL); + } + bool open(const byte *data, uint size) { + close(); + _f = nullptr; + _memPtr = data; + _size = size; + return true; + } + + void close() { + if (_f) + fclose(_f); + _f = nullptr; + delete[] _memPtr; + _memPtr = nullptr; + } + int seek(int offset, int whence = SEEK_SET) { + if (_f) + return fseek(_f, offset, whence); + + switch (whence) { + case SEEK_SET: + _offset = offset; + break; + case SEEK_CUR: + _offset += offset; + break; + case SEEK_END: + _offset = _size + offset; + break; + default: + break; + } + + return _offset; + } + void skip(int offset) { + if (_f) + fseek(_f, offset, SEEK_CUR); + else + _offset += offset; + } + long read(void *buffer, size_t len) { + if (_f) + return fread(buffer, 1, len, _f); + + uint bytesToRead = CLIP(len, (size_t)0, _size - _offset); + memcpy(buffer, &_memPtr[_offset], bytesToRead); + _offset += bytesToRead; + return bytesToRead; + } + void write(const void *buffer, size_t len) { + assert(_f); + fwrite(buffer, 1, len, _f); + } + void write(File &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 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++); + } + } + void writeString(File &src) { + char c; + do { + c = src.readByte(); + writeByte(c); + } while (c); + } + uint pos() const { + if (_f) + return ftell(_f); + else + return _offset; + } + uint size() const { + if (_f) { + uint currentPos = pos(); + fseek(_f, 0, SEEK_END); + uint result = pos(); + fseek(_f, currentPos, SEEK_SET); + return result; + } else if (_memPtr) { + return _size; + } else { + return 0; + } + } + bool eof() const { + if (_f) + return feof(_f) != 0; + else if (_memPtr) + return _offset >= _size; + return false; + } +}; + +} + +#endif diff --git a/devtools/create_titanic/hash-str.h b/devtools/create_titanic/hash-str.h new file mode 100644 index 0000000000..b9f6d503f8 --- /dev/null +++ b/devtools/create_titanic/hash-str.h @@ -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 diff --git a/devtools/create_titanic/hashmap.cpp b/devtools/create_titanic/hashmap.cpp new file mode 100644 index 0000000000..99840993ce --- /dev/null +++ b/devtools/create_titanic/hashmap.cpp @@ -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 diff --git a/devtools/create_titanic/hashmap.h b/devtools/create_titanic/hashmap.h new file mode 100644 index 0000000000..d7ba100571 --- /dev/null +++ b/devtools/create_titanic/hashmap.h @@ -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 "common/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 diff --git a/devtools/create_titanic/module.mk b/devtools/create_titanic/module.mk new file mode 100644 index 0000000000..05a4130dad --- /dev/null +++ b/devtools/create_titanic/module.mk @@ -0,0 +1,15 @@ + +MODULE := devtools/create_titanic + +MODULE_OBJS := \ + create_titanic_dat.o \ + hashmap.o \ + str.o \ + winexe.o \ + winexe_pe.o + +# Set the name of the executable +TOOL_EXECUTABLE := create_titanic + +# Include common rules +include $(srcdir)/rules.mk diff --git a/devtools/create_titanic/str.cpp b/devtools/create_titanic/str.cpp new file mode 100644 index 0000000000..14a6e505ce --- /dev/null +++ b/devtools/create_titanic/str.cpp @@ -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 "common/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; +} diff --git a/devtools/create_titanic/str.h b/devtools/create_titanic/str.h new file mode 100644 index 0000000000..2f954dcfca --- /dev/null +++ b/devtools/create_titanic/str.h @@ -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 diff --git a/devtools/create_titanic/winexe.cpp b/devtools/create_titanic/winexe.cpp new file mode 100644 index 0000000000..c23bd84a89 --- /dev/null +++ b/devtools/create_titanic/winexe.cpp @@ -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. + * + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "str.h" +#include "winexe.h" + +namespace Common { + +WinResourceID &WinResourceID::operator=(const String &x) { + _name = x; + _idType = kIDTypeString; + return *this; +} + +WinResourceID &WinResourceID::operator=(uint32 x) { + _id = x; + _idType = kIDTypeNumerical; + return *this; +} + +bool WinResourceID::operator==(const String &x) const { + return _idType == kIDTypeString && _name.equalsIgnoreCase(x); +} + +bool WinResourceID::operator==(const uint32 &x) const { + return _idType == kIDTypeNumerical && _id == x; +} + +bool WinResourceID::operator==(const WinResourceID &x) const { + if (_idType != x._idType) + return false; + if (_idType == kIDTypeString) + return _name.equalsIgnoreCase(x._name); + if (_idType == kIDTypeNumerical) + return _id == x._id; + return true; +} + +String WinResourceID::getString() const { + if (_idType != kIDTypeString) + return ""; + + return _name; +} + +uint32 WinResourceID::getID() const { + if (_idType != kIDTypeNumerical) + return 0xffffffff; + + return _id; +} + +String WinResourceID::toString() const { + if (_idType == kIDTypeString) + return _name; + else if (_idType == kIDTypeNumerical) + return String::format("0x%08x", _id); + + return ""; +} + +} // End of namespace Common diff --git a/devtools/create_titanic/winexe.h b/devtools/create_titanic/winexe.h new file mode 100644 index 0000000000..da99c769be --- /dev/null +++ b/devtools/create_titanic/winexe.h @@ -0,0 +1,70 @@ +/* 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_WINEXE_H +#define COMMON_WINEXE_H + +#include "hash-str.h" +#include "str.h" + +namespace Common { + +class WinResourceID { +public: + WinResourceID() { _idType = kIDTypeNull; } + WinResourceID(String x) { _idType = kIDTypeString; _name = x; } + WinResourceID(uint32 x) { _idType = kIDTypeNumerical; _id = x; } + + WinResourceID &operator=(const String &x); + WinResourceID &operator=(uint32 x); + + bool operator==(const String &x) const; + bool operator==(const uint32 &x) const; + bool operator==(const WinResourceID &x) const; + + String getString() const; + uint32 getID() const; + String toString() const; + +private: + /** An ID Type. */ + enum IDType { + kIDTypeNull, ///< No type set + kIDTypeNumerical, ///< A numerical ID. + kIDTypeString ///< A string ID. + } _idType; + + String _name; ///< The resource's string ID. + uint32 _id; ///< The resource's numerical ID. +}; + +struct WinResourceID_Hash { + uint operator()(const WinResourceID &id) const { return hashit(id.toString()); } +}; + +struct WinResourceID_EqualTo { + bool operator()(const WinResourceID &id1, const WinResourceID &id2) const { return id1 == id2; } +}; + +} // End of namespace Common + +#endif diff --git a/devtools/create_titanic/winexe_pe.cpp b/devtools/create_titanic/winexe_pe.cpp new file mode 100644 index 0000000000..16ad16ab63 --- /dev/null +++ b/devtools/create_titanic/winexe_pe.cpp @@ -0,0 +1,260 @@ +/* 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. + * + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "file.h" +#include "str.h" +#include "winexe_pe.h" +#include "common/array.h" +#include "common/endian.h" + +namespace Common { + +PEResources::PEResources() { + _exe = 0; +} + +PEResources::~PEResources() { + clear(); +} + +void PEResources::clear() { + _sections.clear(); + _resources.clear(); + delete _exe; _exe = 0; +} + +bool PEResources::loadFromEXE(const String &fileName) { + if (fileName.empty()) + return false; + + File *file = new File(); + + if (!file->open(fileName.c_str())) { + delete file; + return false; + } + + return loadFromEXE(file); +} + +bool PEResources::loadFromEXE(File *stream) { + clear(); + + if (!stream) + return false; + + if (stream->readUint16BE() != MKTAG16('M', 'Z')) + return false; + + stream->skip(58); + + uint32 peOffset = stream->readUint32LE(); + + if (!peOffset || peOffset >= (uint32)stream->size()) + return false; + + stream->seek(peOffset); + + if (stream->readUint32BE() != MKTAG('P','E',0,0)) + return false; + + stream->skip(2); + uint16 sectionCount = stream->readUint16LE(); + stream->skip(12); + uint16 optionalHeaderSize = stream->readUint16LE(); + stream->skip(optionalHeaderSize + 2); + + // Read in all the sections + for (uint16 i = 0; i < sectionCount; i++) { + char sectionName[9]; + stream->read(sectionName, 8); + sectionName[8] = 0; + + Section section; + stream->skip(4); + section.virtualAddress = stream->readUint32LE(); + section.size = stream->readUint32LE(); + section.offset = stream->readUint32LE(); + stream->skip(16); + + _sections[sectionName] = section; + } + + // Currently, we require loading a resource section + if (!_sections.contains(".rsrc")) { + clear(); + return false; + } + + _exe = stream; + + Section &resSection = _sections[".rsrc"]; + parseResourceLevel(resSection, resSection.offset, 0); + + return true; +} + +void PEResources::parseResourceLevel(Section §ion, uint32 offset, int level) { + _exe->seek(offset + 12); + + uint16 namedEntryCount = _exe->readUint16LE(); + uint16 intEntryCount = _exe->readUint16LE(); + + for (uint32 i = 0; i < (uint32)(namedEntryCount + intEntryCount); i++) { + uint32 value = _exe->readUint32LE(); + + WinResourceID id; + + if (value & 0x80000000) { + value &= 0x7fffffff; + + uint32 startPos = _exe->pos(); + _exe->seek(section.offset + (value & 0x7fffffff)); + + // Read in the name, truncating from unicode to ascii + String name; + uint16 nameLength = _exe->readUint16LE(); + while (nameLength--) + name += (char)(_exe->readUint16LE() & 0xff); + + _exe->seek(startPos); + + id = name; + } else { + id = value; + } + + uint32 nextOffset = _exe->readUint32LE(); + uint32 lastOffset = _exe->pos(); + + if (level == 0) + _curType = id; + else if (level == 1) + _curName = id; + else if (level == 2) + _curLang = id; + + if (level < 2) { + // Time to dive down further + parseResourceLevel(section, section.offset + (nextOffset & 0x7fffffff), level + 1); + } else { + _exe->seek(section.offset + nextOffset); + + Resource resource; + resource.offset = _exe->readUint32LE() + section.offset - section.virtualAddress; + resource.size = _exe->readUint32LE(); + + _resources[_curType][_curName][_curLang] = resource; + } + + _exe->seek(lastOffset); + } +} + +const Array<WinResourceID> PEResources::getTypeList() const { + Array<WinResourceID> array; + + if (!_exe) + return array; + + for (TypeMap::const_iterator it = _resources.begin(); it != _resources.end(); it++) + array.push_back(it->_key); + + return array; +} + +const Array<WinResourceID> PEResources::getNameList(const WinResourceID &type) const { + Array<WinResourceID> array; + + if (!_exe || !_resources.contains(type)) + return array; + + const NameMap &nameMap = _resources[type]; + + for (NameMap::const_iterator it = nameMap.begin(); it != nameMap.end(); it++) + array.push_back(it->_key); + + return array; +} + +const Array<WinResourceID> PEResources::getLangList(const WinResourceID &type, const WinResourceID &name) const { + Array<WinResourceID> array; + + if (!_exe || !_resources.contains(type)) + return array; + + const NameMap &nameMap = _resources[type]; + + if (!nameMap.contains(name)) + return array; + + const LangMap &langMap = nameMap[name]; + + for (LangMap::const_iterator it = langMap.begin(); it != langMap.end(); it++) + array.push_back(it->_key); + + return array; +} + +File *PEResources::getResource(const WinResourceID &type, const WinResourceID &name) { + Array<WinResourceID> langList = getLangList(type, name); + + if (langList.empty()) + return 0; + + const Resource &resource = _resources[type][name][langList[0]]; + byte *data = (byte *)malloc(resource.size); + _exe->seek(resource.offset); + _exe->read(data, resource.size); + + File *file = new File(); + file->open(data, resource.size); + return file; +} + +File *PEResources::getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang) { + if (!_exe || !_resources.contains(type)) + return 0; + + const NameMap &nameMap = _resources[type]; + + if (!nameMap.contains(name)) + return 0; + + const LangMap &langMap = nameMap[name]; + + if (!langMap.contains(lang)) + return 0; + + const Resource &resource = langMap[lang]; + byte *data = (byte *)malloc(resource.size); + _exe->seek(resource.offset); + _exe->read(data, resource.size); + + File *file = new File(); + file->open(data, resource.size); + return file; +} + +} // End of namespace Common diff --git a/devtools/create_titanic/winexe_pe.h b/devtools/create_titanic/winexe_pe.h new file mode 100644 index 0000000000..49e29b7a56 --- /dev/null +++ b/devtools/create_titanic/winexe_pe.h @@ -0,0 +1,120 @@ +/* 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_WINEXE_PE_H +#define COMMON_WINEXE_PE_H + +#include "file.h" +#include "hash-str.h" +#include "hashmap.h" +#include "str.h" +#include "winexe.h" + +namespace Common { + +template<class T> class Array; +class SeekableReadStream; + +/** The default Windows PE resources. */ +enum PEResourceType { + kPECursor = 0x01, + kPEBitmap = 0x02, + kPEIcon = 0x03, + kPEMenu = 0x04, + kPEDialog = 0x05, + kPEString = 0x06, + kPEFontDir = 0x07, + kPEFont = 0x08, + kPEAccelerator = 0x09, + kPERCData = 0x0A, + kPEMessageTable = 0x0B, + kPEGroupCursor = 0x0C, + kPEGroupIcon = 0x0E, + kPEVersion = 0x10, + kPEDlgInclude = 0x11, + kPEPlugPlay = 0x13, + kPEVXD = 0x14, + kPEAniCursor = 0x15, + kPEAniIcon = 0x16 +}; + +/** + * A class able to load resources from a Windows Portable Executable, such + * as cursors, bitmaps, and sounds. + */ +class PEResources { +public: + PEResources(); + ~PEResources(); + + /** Clear all information. */ + void clear(); + + /** Load from an EXE file. */ + bool loadFromEXE(const String &fileName); + + bool loadFromEXE(File *stream); + + /** Return a list of resource types. */ + const Array<WinResourceID> getTypeList() const; + + /** Return a list of names for a given type. */ + const Array<WinResourceID> getNameList(const WinResourceID &type) const; + + /** Return a list of languages for a given type and name. */ + const Array<WinResourceID> getLangList(const WinResourceID &type, const WinResourceID &name) const; + + /** Return a stream to the specified resource, taking the first language found (or 0 if non-existent). */ + File *getResource(const WinResourceID &type, const WinResourceID &name); + + /** Return a stream to the specified resource (or 0 if non-existent). */ + File *getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang); + +private: + struct Section { + uint32 virtualAddress; + uint32 size; + uint32 offset; + }; + + HashMap<String, Section, IgnoreCase_Hash, IgnoreCase_EqualTo> _sections; + + File *_exe; + + void parseResourceLevel(Section §ion, uint32 offset, int level); + WinResourceID _curType, _curName, _curLang; + + struct Resource { + uint32 offset; + uint32 size; + }; + + typedef HashMap<WinResourceID, Resource, WinResourceID_Hash, WinResourceID_EqualTo> LangMap; + typedef HashMap<WinResourceID, LangMap, WinResourceID_Hash, WinResourceID_EqualTo> NameMap; + typedef HashMap<WinResourceID, NameMap, WinResourceID_Hash, WinResourceID_EqualTo> TypeMap; + + TypeMap _resources; +}; + +} // End of namespace Common + +#endif |