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 /devtools | |
| 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
Diffstat (limited to 'devtools')
| -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  | 
