diff --git a/devtools/create_ultima/archive.cpp b/devtools/create_ultima/archive.cpp deleted file mode 100644 index 3b23d904d83..00000000000 --- a/devtools/create_ultima/archive.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - // Disable symbol overrides so that we can use system headers. -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -// HACK to allow building with the SDL backend on MinGW -// see bug #1800764 "TOOLS: MinGW tools building broken" -#ifdef main -#undef main -#endif // main - -#include "archive.h" -#include "common/endian.h" - -#define ARCHIVE_IDENT "SULT" -#define ARCHIVE_VERSION 1 - -void Archive::save() { - // Write identifying string and archive version - _file.write(ARCHIVE_IDENT, 4); - _file.writeWord(ARCHIVE_VERSION); - _file.writeWord(_index.size()); - - // Figure out the size the entire index needs to be (i.e. where the data starts) - size_t dataOffset = 8; - for (uint idx = 0; idx < _index.size(); ++idx) - dataOffset += _index[idx].getIndexSize(); - dataOffset = ((dataOffset + 1) / 2) * 2; - - // Iterate through writing out index entries - for (uint idx = 0; idx < _index.size(); ++idx) { - ArchiveEntry &ae = _index[idx]; - ae._offset = dataOffset; - _file.writeString(ae._name.c_str()); - _file.writeLong(ae._offset); - _file.writeWord(ae._size); - - dataOffset += ae._size; - } - if (_file.pos() % 2) - _file.writeByte(0); - - // Write out the contents of each resource - for (uint idx = 0; idx < _index.size(); ++idx) { - const ArchiveEntry &ae = _index[idx]; - if (_file.pos() != ae._offset) - error("Incorrect offset"); - _file.write(ae._data, ae._size); - } -} - -bool Archive::open(const Common::String &name) { - return _file.open(name.c_str(), Common::kFileWriteMode); -} - -void Archive::close() { - if (_file.isOpen()) { - save(); - _file.close(); - } -} - -void Archive::add(const Common::String &name, Common::MemFile &f) { - _index.push_back(ArchiveEntry(name, f.getData(), f.size())); -} diff --git a/devtools/create_ultima/archive.h b/devtools/create_ultima/archive.h deleted file mode 100644 index be452f7b160..00000000000 --- a/devtools/create_ultima/archive.h +++ /dev/null @@ -1,90 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef CC_H -#define CC_H - -#include "file.h" -#include "str.h" -#include "common/array.h" - -class Archive { - /** - * Details of a single entry in the archive - */ - struct ArchiveEntry { - Common::String _name; - size_t _offset; - uint16 _size; - byte _data[65536]; - - ArchiveEntry() : _offset(0), _size(0) { - memset(_data, 0, 65536); - } - ArchiveEntry(const Common::String &name, const byte *data, uint32 size) : - _offset(0), _name(name), _size(size) { - memcpy(_data, data, size); - } - - /** - * Returns the size needed for saving the entry in the archive index - */ - size_t getIndexSize() const { return 6 + _name.size() + 1; } - }; -private: - Common::Array _index; - Common::File _file; -private: - /** - * Generates the archive and saves it to file - */ - void save(); -public: - /** - * Consstructor - */ - Archive() {} - - /** - * Destructor - */ - ~Archive() { - close(); - } - - /** - * Opens the archive for access - */ - bool open(const Common::String &name); - - /** - * Closes the archive - */ - void close(); - - /** - * Adds an entry to the CC - */ - void add(const Common::String &name, Common::MemFile &f); -}; - -#endif diff --git a/devtools/create_ultima/create_ultima.cpp b/devtools/create_ultima/create_ultima.cpp index 737ff728deb..a002c7b72fe 100644 --- a/devtools/create_ultima/create_ultima.cpp +++ b/devtools/create_ultima/create_ultima.cpp @@ -32,26 +32,17 @@ #include #include #include -#include "archive.h" -#include "ultima1_map.h" #include "ultima1_resources.h" #define VERSION_NUMBER 1 -void NORETURN_PRE error(const char *s, ...) { +void error(const char *s, ...) { printf("%s\n", s); exit(1); } int main(int argc, char *argv[]) { - Archive archive; - if (!archive.open("ultima.dat")) { - error("Could not open output file"); - } + extractUltima1Resources(); - writeUltima1Resources(archive); - writeUltima1EnhancedMap(archive); - - archive.close(); return 0; } diff --git a/devtools/create_ultima/ultima1_map.h b/devtools/create_ultima/create_ultima.h similarity index 89% rename from devtools/create_ultima/ultima1_map.h rename to devtools/create_ultima/create_ultima.h index e51093da382..7bcc3a736d7 100644 --- a/devtools/create_ultima/ultima1_map.h +++ b/devtools/create_ultima/create_ultima.h @@ -20,11 +20,9 @@ * */ -#ifndef CONSTANTS_H -#define CONSTANTS_H +#ifndef CREATE_ULTIMA_H +#define CREATE_ULTIMA_H -#include "archive.h" - -extern void writeUltima1EnhancedMap(Archive &a); +extern void error(const char *s, ...); #endif diff --git a/devtools/create_ultima/file.cpp b/devtools/create_ultima/file.cpp new file mode 100644 index 00000000000..08f1d293b92 --- /dev/null +++ b/devtools/create_ultima/file.cpp @@ -0,0 +1,72 @@ +/* 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 "file.h" + +void Surface::setPaletteEntry(byte index, byte r, byte g, byte b) { + byte *pal = _palette + index * 3; + pal[0] = r; + pal[1] = g; + pal[2] = b; +} + +void Surface::saveToFile(const char *filename) { + WriteFile f(filename); + f.writeByte('B'); + f.writeByte('M'); + f.writeLong(0x436 + _w * _h + 2); // File size + f.writeWord(0); // Custom 1 + f.writeWord(0); // Custom 2 + f.writeLong(0x436); // Pixels offset + + int pitch = _w; + if (pitch % 4) + pitch += 4 - (pitch % 4); + + f.writeLong(40); // Info size + f.writeLong(_w); // Width + f.writeLong(_h); // Height + f.writeWord(1); // # Planes + f.writeWord(8); // Bits per pixel + f.writeLong(0); // Compression + f.writeLong(pitch * _h); // Image size + f.writeLong(3790); // Pixels per meter X + f.writeLong(3800); // Pixels per meter Y + f.writeLong(0); // color count + f.writeLong(0); // important colors + + // Palette + byte *pal = _palette; + for (int idx = 0; idx < 256; ++idx, pal += 3) { + f.write(pal, 3); + f.writeByte(0); + } + + // Write out each line from the bottom up + for (int y = _h - 1; y >= 0; --y) { + byte *lineP = getBasePtr(0, y); + f.write(lineP, _w); + + if (_w % 4) + f.writeRepeating(0, 4 - (_w % 4)); + } +} diff --git a/devtools/create_ultima/file.h b/devtools/create_ultima/file.h index 5e1631fec11..7a28aadbfe3 100644 --- a/devtools/create_ultima/file.h +++ b/devtools/create_ultima/file.h @@ -20,261 +20,127 @@ * */ -#ifndef __FILE_H__ -#define __FILE_H__ +#ifndef FILE_H +#define FILE_H #include #include +#include +#include -#define FORBIDDEN_SYMBOL_ALLOW_ALL +typedef unsigned char byte; -#include "common/scummsys.h" -#include "common/endian.h" -#include "common/util.h" - -namespace Common { - -enum AccessMode { - kFileReadMode = 1, - kFileWriteMode = 2 -}; - -class Stream { +class File { +private: + FILE *_file; public: - Stream() {} - virtual ~Stream() {} - - virtual int seek(int offset, int whence = SEEK_SET) = 0; - virtual long read(void *buffer, size_t len) = 0; - virtual void write(const void *buffer, size_t len) = 0; - virtual uint pos() const = 0; - virtual uint size() const = 0; - virtual bool eof() const = 0; - - void skip(int offset) { - seek(offset, SEEK_CUR); + File(const char *filename) { + fopen_s(&_file, filename, "rb"); + assert(_file); } - void write(Stream &src, size_t len) { - for (size_t idx = 0; idx < len; ++idx) - writeByte(src.readByte()); + ~File() { + fclose(_file); } + + int seek(int offset, int origin = SEEK_SET) { + return fseek(_file, offset, origin); + } + 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); + int b; + fread(&b, 1, 1, _file); + return b; } - 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); + int readWord() { + byte b1, b2; + fread(&b1, 1, 1, _file); + fread(&b2, 1, 1, _file); + return b1 | (b2 << 8); } - void writeByte(byte v) { - write(&v, sizeof(byte)); - } - void writeShort(int8 v) { - write(&v, sizeof(int8)); - } - void writeByte(byte v, int len) { - byte *b = new byte[len]; - memset(b, v, len); - write(b, len); - delete[] b; - } - void writeWord(uint16 v) { - uint16 vTemp = TO_LE_16(v); - write(&vTemp, sizeof(uint16)); - } - void writeLong(uint v) { - uint vTemp = TO_LE_32(v); - write(&vTemp, sizeof(uint)); - } - void writeString(const char *msg) { - if (!msg) { - writeByte(0); - } else { - do { - writeByte(*msg); - } while (*msg++); - } + void read(void *buf, int size) { + fread(buf, 1, size, _file); } }; -class File : public Stream { +class WriteFile { private: - ::FILE *_f; + FILE *_file; public: - File() : _f(nullptr) {} - virtual ~File() { close(); } - - bool open(const char *filename, AccessMode mode = kFileReadMode) { - _f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb+"); - return (_f != NULL); + WriteFile(const char *filename) { + fopen_s(&_file, filename, "wb"); + assert(_file); } - void close() { - if (_f) - fclose(_f); - _f = nullptr; + ~WriteFile() { + fclose(_file); } - virtual int seek(int offset, int whence = SEEK_SET) { - return fseek(_f, offset, whence); + void writeByte(byte val) { + fwrite(&val, 1, 1, _file); } - virtual long read(void *buffer, size_t len) { - return fread(buffer, 1, len, _f); + + void writeWord(int val) { + int b1 = val & 0xff, b2 = (val >> 8) & 0xff; + fwrite(&b1, 1, 1, _file); + fwrite(&b2, 1, 1, _file); } - virtual void write(const void *buffer, size_t len) { - assert(_f); - fwrite(buffer, 1, len, _f); + + void writeLong(long val) { + writeWord(val & 0xffff); + writeWord((val >> 16) & 0xffff); } - virtual uint pos() const { - return ftell(_f); + + int write(void *buf, int size) { + return (int)fwrite(buf, 1, size, _file); } - virtual uint size() const { - uint currentPos = pos(); - fseek(_f, 0, SEEK_END); - uint result = pos(); - fseek(_f, currentPos, SEEK_SET); - return result; - } - virtual bool eof() const { - return feof(_f) != 0; - } - bool isOpen() const { - return _f != nullptr; + void writeRepeating(byte val, size_t count) { + while (count-- > 0) + writeByte(val); } }; -#define MAX_MEM_SIZE 65536 +/** + * Simple surface structure + */ +struct Surface { + int _w; + int _h; + byte *_pixels; + byte _palette[768]; -class MemFile : public Stream { -private: - byte _data[MAX_MEM_SIZE]; - size_t _size, _offset; -public: - MemFile() : _size(0), _offset(0) { - memset(_data, 0, MAX_MEM_SIZE); - } - MemFile(const byte *data, size_t size) : _size(size), _offset(0) { - memcpy(_data, data, size); - } - virtual ~MemFile() {} + Surface(int w, int h) : _w(w), _h(h) { + _pixels = new byte[w * h]; + memset(_pixels, 0xff, w * h); - bool open() { - memset(_data, 0, MAX_MEM_SIZE); - _size = _offset = 0; - return true; - } - void close() { - } - - virtual int seek(int offset, int whence = SEEK_SET) { - switch (whence) { - case SEEK_SET: _offset = whence; break; - case SEEK_CUR: _offset += whence; break; - case SEEK_END: _offset = _size + whence; break; + // Set a default palette + for (int idx = 0; idx < 256; ++idx) { + memset(_palette + idx * 3, idx, 3); } - - return _offset; - } - virtual long read(void *buffer, size_t len) { - len = MAX(len, _size - _offset); - memcpy(buffer, &_data[_offset], len); - return len; - } - virtual void write(const void *buffer, size_t len) { - assert(len <= (MAX_MEM_SIZE - _offset)); - memcpy(&_data[_offset], buffer, len); - _offset += len; - _size = MAX(_offset, _size); - } - virtual void write(Stream &src, size_t size) { - byte *buffer = new byte[size]; - src.read(buffer, size); - write(buffer, size); - delete[] buffer; + } - virtual uint pos() const { - return _offset; - } - virtual uint size() const { - return _size; - } - virtual bool eof() const { - return _offset >= _size; + ~Surface() { + delete[] _pixels; } - const byte *getData() const { return _data; } + Surface &operator=(const Surface &src) { + assert(src._w == _w && src._h == _h); + memcpy(_pixels, src._pixels, _w * _h); + return *this; + } - void syncString(const char *str) { - write(str, strlen(str) + 1); - } - void syncStrings(const char *const *str, int count) { - writeLong(MKTAG(count, 0, 0, 0)); - for (int idx = 0; idx < count; ++idx, ++str) - writeString(*str); - } - void syncStrings2D(const char *const *str, int count1, int count2) { - writeLong(MKTAG(count1, count2, 0, 0)); - for (int idx = 0; idx < count1 * count2; ++idx, ++str) - writeString(*str); - } - void syncNumber(const int val) { - writeLong(val); - } - void syncNumbers(const int *vals, int count) { - writeLong(MKTAG(count, 0, 0, 0)); - for (int idx = 0; idx < count; ++idx, ++vals) - writeLong(*vals); - } - void syncNumbers2D(const int *vals, int count1, int count2) { - writeLong(MKTAG(count1, count2, 0, 0)); - for (int idx = 0; idx < count1 * count2; ++idx, ++vals) - writeLong(*vals); - } - void syncNumbers3D(const int *vals, int count1, int count2, int count3) { - writeLong(MKTAG(count1, count2, count3, 0)); - for (int idx = 0; idx < count1 * count2 * count3; ++idx, ++vals) - writeLong(*vals); - } - void syncNumbers4D(const int *vals, int count1, int count2, int count3, int count4) { - writeLong(MKTAG(count1, count2, count3, count4)); - for (int idx = 0; idx < count1 * count2 * count3 * count4; ++idx, ++vals) - writeLong(*vals); - } - void syncBytes2D(const byte *vals, int count1, int count2) { - writeLong(MKTAG(count1, count2, 0, 0)); - write(vals, count1 * count2); + byte *getBasePtr(int x, int y) { + assert(y < _h); + return _pixels + (y * _w) + x; } + + void setPaletteEntry(byte index, byte r, byte g, byte b); + + /** + * Save to a BMP file + */ + void saveToFile(const char *filename); }; -} // End of namespace Common - #endif diff --git a/devtools/create_ultima/hash-str.h b/devtools/create_ultima/hash-str.h deleted file mode 100644 index b9f6d503f86..00000000000 --- a/devtools/create_ultima/hash-str.h +++ /dev/null @@ -1,86 +0,0 @@ -/* 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 { - uint operator()(const String& s) const { - return hashit(s.c_str()); - } -}; - -template<> -struct Hash { - uint operator()(const char *s) const { - return hashit(s); - } -}; - -// String map -- by default case insensitive -typedef HashMap StringMap; - - - -} // End of namespace Common - - -#endif diff --git a/devtools/create_ultima/hashmap.cpp b/devtools/create_ultima/hashmap.cpp deleted file mode 100644 index 99840993ce4..00000000000 --- a/devtools/create_ultima/hashmap.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* 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_ultima/hashmap.h b/devtools/create_ultima/hashmap.h deleted file mode 100644 index c8691aeb42b..00000000000 --- a/devtools/create_ultima/hashmap.h +++ /dev/null @@ -1,637 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -// The hash map (associative array) implementation in this file is -// based on the PyDict implementation of CPython. - -#ifndef COMMON_HASHMAP_H -#define COMMON_HASHMAP_H - -/** - * @def DEBUG_HASH_COLLISIONS - * Enable the following #define if you want to check how many collisions the - * code produces (many collisions indicate either a bad hash function, or a - * hash table that is too small). - */ -//#define DEBUG_HASH_COLLISIONS - -/** - * @def USE_HASHMAP_MEMORY_POOL - * Enable the following define to let HashMaps use a memory pool for the - nodes they contain. * This increases memory usage, but also can improve - speed quite a bit. - */ -#define USE_HASHMAP_MEMORY_POOL - - -#include "common/func.h" - -#ifdef DEBUG_HASH_COLLISIONS -#include "common/debug.h" -#endif - -#ifdef USE_HASHMAP_MEMORY_POOL -#include "memorypool.h" -#endif - - - -namespace Common { - -// The sgi IRIX MIPSpro Compiler has difficulties with nested templates. -// This and the other __sgi conditionals below work around these problems. -// The Intel C++ Compiler suffers from the same problems. -#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER) -template class IteratorImpl; -#endif - - -/** - * HashMap 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, 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 EqualFunc = EqualTo > -class HashMap { -public: - typedef uint size_type; - -private: - - typedef HashMap 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 _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 friend class IteratorImpl; -#endif - - /** - * Simple HashMap iterator implementation. - */ - template - class IteratorImpl { - friend class HashMap; -#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER) - template friend class Common::IteratorImpl; -#else - template 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 - IteratorImpl(const IteratorImpl &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 iterator; - typedef IteratorImpl 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 -HashMap::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 -HashMap::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 -HashMap::~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 -void HashMap::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 -void HashMap::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 -void HashMap::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 -typename HashMap::size_type HashMap::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 -typename HashMap::size_type HashMap::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 -bool HashMap::contains(const Key &key) const { - size_type ctr = lookup(key); - return (_storage[ctr] != NULL); -} - -template -Val &HashMap::operator[](const Key &key) { - return getVal(key); -} - -template -const Val &HashMap::operator[](const Key &key) const { - return getVal(key); -} - -template -Val &HashMap::getVal(const Key &key) { - size_type ctr = lookupAndCreateIfMissing(key); - assert(_storage[ctr] != NULL); - return _storage[ctr]->_value; -} - -template -const Val &HashMap::getVal(const Key &key) const { - return getVal(key, _defaultVal); -} - -template -const Val &HashMap::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 -void HashMap::setVal(const Key &key, const Val &val) { - size_type ctr = lookupAndCreateIfMissing(key); - assert(_storage[ctr] != NULL); - _storage[ctr]->_value = val; -} - -template -void HashMap::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 -void HashMap::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_ultima/memorypool.cpp b/devtools/create_ultima/memorypool.cpp deleted file mode 100644 index 13c640b6adc..00000000000 --- a/devtools/create_ultima/memorypool.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "memorypool.h" -#include "common/util.h" - -namespace Common { - -enum { - INITIAL_CHUNKS_PER_PAGE = 8 -}; - -static size_t adjustChunkSize(size_t chunkSize) { - // You must at least fit the pointer in the node (technically unneeded considering the next rounding statement) - chunkSize = MAX(chunkSize, sizeof(void *)); - // There might be an alignment problem on some platforms when trying to load a void* on a non natural boundary - // so we round to the next sizeof(void *) - chunkSize = (chunkSize + sizeof(void *) - 1) & (~(sizeof(void *) - 1)); - - return chunkSize; -} - - -MemoryPool::MemoryPool(size_t chunkSize) - : _chunkSize(adjustChunkSize(chunkSize)) { - - _next = NULL; - - _chunksPerPage = INITIAL_CHUNKS_PER_PAGE; -} - -MemoryPool::~MemoryPool() { -#if 0 - freeUnusedPages(); - if (!_pages.empty()) - warning("Memory leak found in pool"); -#endif - - for (size_t i = 0; i < _pages.size(); ++i) - ::free(_pages[i].start); -} - -void MemoryPool::allocPage() { - Page page; - - // Allocate a new page - page.numChunks = _chunksPerPage; - assert(page.numChunks * _chunkSize < 16*1024*1024); // Refuse to allocate pages bigger than 16 MB - - page.start = ::malloc(page.numChunks * _chunkSize); - assert(page.start); - _pages.push_back(page); - - - // Next time, we'll allocate a page twice as big as this one. - _chunksPerPage *= 2; - - // Add the page to the pool of free chunk - addPageToPool(page); -} - -void MemoryPool::addPageToPool(const Page &page) { - // Add all chunks of the new page to the linked list (pool) of free chunks - void *current = page.start; - for (size_t i = 1; i < page.numChunks; ++i) { - void *next = (byte *)current + _chunkSize; - *(void **)current = next; - - current = next; - } - - // Last chunk points to the old _next - *(void **)current = _next; - - // From now on, the first free chunk is the first chunk of the new page - _next = page.start; -} - -void *MemoryPool::allocChunk() { - // No free chunks left? Allocate a new page - if (!_next) - allocPage(); - - assert(_next); - void *result = _next; - _next = *(void **)result; - return result; -} - -void MemoryPool::freeChunk(void *ptr) { - // Add the chunk back to (the start of) the list of free chunks - *(void **)ptr = _next; - _next = ptr; -} - -// Technically not compliant C++ to compare unrelated pointers. In practice... -bool MemoryPool::isPointerInPage(void *ptr, const Page &page) { - return (ptr >= page.start) && (ptr < (char *)page.start + page.numChunks * _chunkSize); -} - -void MemoryPool::freeUnusedPages() { - //std::sort(_pages.begin(), _pages.end()); - Array numberOfFreeChunksPerPage; - numberOfFreeChunksPerPage.resize(_pages.size()); - for (size_t i = 0; i < numberOfFreeChunksPerPage.size(); ++i) { - numberOfFreeChunksPerPage[i] = 0; - } - - // Compute for each page how many chunks in it are still in use. - void *iterator = _next; - while (iterator) { - // TODO: This should be a binary search (requiring us to keep _pages sorted) - for (size_t i = 0; i < _pages.size(); ++i) { - if (isPointerInPage(iterator, _pages[i])) { - ++numberOfFreeChunksPerPage[i]; - break; - } - } - - iterator = *(void **)iterator; - } - - // Free all pages which are not in use. - size_t freedPagesCount = 0; - for (size_t i = 0; i < _pages.size(); ++i) { - if (numberOfFreeChunksPerPage[i] == _pages[i].numChunks) { - // Remove all chunks of this page from the list of free chunks - void **iter2 = &_next; - while (*iter2) { - if (isPointerInPage(*iter2, _pages[i])) - *iter2 = **(void ***)iter2; - else - iter2 = *(void ***)iter2; - } - - ::free(_pages[i].start); - ++freedPagesCount; - _pages[i].start = NULL; - } - } - -// debug("freed %d pages out of %d", (int)freedPagesCount, (int)_pages.size()); - - // Remove all now unused pages - size_t newSize = 0; - for (size_t i = 0; i < _pages.size(); ++i) { - if (_pages[i].start != NULL) { - if (newSize != i) - _pages[newSize] = _pages[i]; - ++newSize; - } - } - _pages.resize(newSize); - - // Reset _chunksPerPage - _chunksPerPage = INITIAL_CHUNKS_PER_PAGE; - for (size_t i = 0; i < _pages.size(); ++i) { - if (_chunksPerPage < _pages[i].numChunks) - _chunksPerPage = _pages[i].numChunks; - } -} - -} // End of namespace Common diff --git a/devtools/create_ultima/memorypool.h b/devtools/create_ultima/memorypool.h deleted file mode 100644 index c8a8fc7a535..00000000000 --- a/devtools/create_ultima/memorypool.h +++ /dev/null @@ -1,162 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef COMMON_MEMORYPOOL_H -#define COMMON_MEMORYPOOL_H - -#include "common/array.h" - - -namespace Common { - -/** - * This class provides a pool of memory 'chunks' of identical size. - * The size of a chunk is determined when creating the memory pool. - * - * Using a memory pool may yield better performance and memory usage - * when allocating and deallocating many memory blocks of equal size. - * E.g. the Common::String class uses a memory pool for the refCount - * variables (each the size of an int) it allocates for each string - * instance. - */ -class MemoryPool { -protected: - MemoryPool(const MemoryPool&); - MemoryPool& operator=(const MemoryPool&); - - struct Page { - void *start; - size_t numChunks; - }; - - const size_t _chunkSize; - Array _pages; - void *_next; - size_t _chunksPerPage; - - void allocPage(); - void addPageToPool(const Page &page); - bool isPointerInPage(void *ptr, const Page &page); - -public: - /** - * Constructor for a memory pool with the given chunk size. - * @param chunkSize the chunk size of this memory pool - */ - explicit MemoryPool(size_t chunkSize); - ~MemoryPool(); - - /** - * Allocate a new chunk from the memory pool. - */ - void *allocChunk(); - /** - * Return a chunk to the memory pool. The given pointer must have - * been obtained from calling the allocChunk() method of the very - * same MemoryPool instance. Passing any other pointer (e.g. to - * a chunk from another MemoryPool, or a malloc'ed memory block) - * will lead to undefined behavior and may result in a crash (if - * you are lucky) or in silent data corruption. - */ - void freeChunk(void *ptr); - - /** - * Perform garbage collection. The memory pool stores all the - * chunks it manages in memory 'pages' obtained via the classic - * memory allocation APIs (i.e. malloc/free). Ordinarily, once - * a page has been allocated, it won't be released again during - * the life time of the memory pool. The exception is when this - * method is called. - */ - void freeUnusedPages(); - - /** - * Return the chunk size used by this memory pool. - */ - size_t getChunkSize() const { return _chunkSize; } -}; - -/** - * This is a memory pool which already contains in itself some storage - * space for a fixed number of chunks. Thus if the memory pool is only - * lightly used, no malloc() calls have to be made at all. - */ -template -class FixedSizeMemoryPool : public MemoryPool { -private: - enum { - REAL_CHUNK_SIZE = (CHUNK_SIZE + sizeof(void *) - 1) & (~(sizeof(void *) - 1)) - }; - - byte _storage[NUM_INTERNAL_CHUNKS * REAL_CHUNK_SIZE]; -public: - FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) { - assert(REAL_CHUNK_SIZE == _chunkSize); - // Insert some static storage - Page internalPage = { _storage, NUM_INTERNAL_CHUNKS }; - addPageToPool(internalPage); - } -}; - -// Ensure NUM_INTERNAL_CHUNKS == 0 results in a compile error -template -class FixedSizeMemoryPool : public MemoryPool { -public: - FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {} -}; - -/** - * A memory pool for C++ objects. - */ -template -class ObjectPool : public FixedSizeMemoryPool { -public: - /** - * Return the memory chunk used as storage for the given object back - * to the pool, after calling its destructor. - */ - void deleteChunk(T *ptr) { - ptr->~T(); - this->freeChunk(ptr); - } -}; - -} // End of namespace Common - -/** - * A custom placement new operator, using an arbitrary MemoryPool. - * - * This *should* work with all C++ implementations, but may not. - * - * For details on using placement new for custom allocators, see e.g. - * - */ -inline void *operator new(size_t nbytes, Common::MemoryPool &pool) { - assert(nbytes <= pool.getChunkSize()); - return pool.allocChunk(); -} - -inline void operator delete(void *p, Common::MemoryPool &pool) { - pool.freeChunk(p); -} - -#endif diff --git a/devtools/create_ultima/module.mk b/devtools/create_ultima/module.mk index d92d98bb402..2e7aba3737d 100644 --- a/devtools/create_ultima/module.mk +++ b/devtools/create_ultima/module.mk @@ -2,12 +2,8 @@ MODULE := devtools/create_ultima MODULE_OBJS := \ create_ultima.o \ - archive.o \ - ultima1_map.o \ - ultima1_resources.o \ - hashmap.o \ - memorypool.o \ - str.o + file.o \ + ultima1_resources.o # Set the name of the executable TOOL_EXECUTABLE := create_ultima diff --git a/devtools/create_ultima/str.cpp b/devtools/create_ultima/str.cpp deleted file mode 100644 index 6aa66d0d203..00000000000 --- a/devtools/create_ultima/str.cpp +++ /dev/null @@ -1,786 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "common/hash-str.h" -#include "common/list.h" -#include "memorypool.h" -#include "common/str.h" -#include "common/util.h" - -namespace Common { - -MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now - -static uint32 computeCapacity(uint32 len) { - // By default, for the capacity we use the next multiple of 32 - return ((len + 32 - 1) & ~0x1F); -} - -String::String(const char *str) : _size(0), _str(_storage) { - if (str == 0) { - _storage[0] = 0; - _size = 0; - } else - initWithCStr(str, strlen(str)); -} - -String::String(const char *str, uint32 len) : _size(0), _str(_storage) { - initWithCStr(str, len); -} - -String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) { - assert(endP >= beginP); - initWithCStr(beginP, endP - beginP); -} - -void String::initWithCStr(const char *str, uint32 len) { - assert(str); - - // Init _storage member explicitly (ie. without calling its constructor) - // for GCC 2.95.x compatibility (see also tracker item #1602879). - _storage[0] = 0; - - _size = len; - - if (len >= _builtinCapacity) { - // Not enough internal storage, so allocate more - _extern._capacity = computeCapacity(len+1); - _extern._refCount = 0; - _str = new char[_extern._capacity]; - assert(_str != 0); - } - - // Copy the string into the storage area - memmove(_str, str, len); - _str[len] = 0; -} - -String::String(const String &str) - : _size(str._size) { - if (str.isStorageIntern()) { - // String in internal storage: just copy it - memcpy(_storage, str._storage, _builtinCapacity); - _str = _storage; - } else { - // String in external storage: use refcount mechanism - str.incRefCount(); - _extern._refCount = str._extern._refCount; - _extern._capacity = str._extern._capacity; - _str = str._str; - } - assert(_str != 0); -} - -String::String(char c) - : _size(0), _str(_storage) { - - _storage[0] = c; - _storage[1] = 0; - - _size = (c == 0) ? 0 : 1; -} - -String::~String() { - decRefCount(_extern._refCount); -} - -void String::makeUnique() { - ensureCapacity(_size, true); -} - -/** - * Ensure that enough storage is available to store at least new_size - * characters plus a null byte. In addition, if we currently share - * the storage with another string, unshare it, so that we can safely - * write to the storage. - */ -void String::ensureCapacity(uint32 new_size, bool keep_old) { - bool isShared; - uint32 curCapacity, newCapacity; - char *newStorage; - int *oldRefCount = _extern._refCount; - - if (isStorageIntern()) { - isShared = false; - curCapacity = _builtinCapacity; - } else { - isShared = (oldRefCount && *oldRefCount > 1); - curCapacity = _extern._capacity; - } - - // Special case: If there is enough space, and we do not share - // the storage, then there is nothing to do. - if (!isShared && new_size < curCapacity) - return; - - if (isShared && new_size < _builtinCapacity) { - // We share the storage, but there is enough internal storage: Use that. - newStorage = _storage; - newCapacity = _builtinCapacity; - } else { - // We need to allocate storage on the heap! - - // Compute a suitable new capacity limit - // If the current capacity is sufficient we use the same capacity - if (new_size < curCapacity) - newCapacity = curCapacity; - else - newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1)); - - // Allocate new storage - newStorage = new char[newCapacity]; - assert(newStorage); - } - - // Copy old data if needed, elsewise reset the new storage. - if (keep_old) { - assert(_size < newCapacity); - memcpy(newStorage, _str, _size + 1); - } else { - _size = 0; - newStorage[0] = 0; - } - - // Release hold on the old storage ... - decRefCount(oldRefCount); - - // ... in favor of the new storage - _str = newStorage; - - if (!isStorageIntern()) { - // Set the ref count & capacity if we use an external storage. - // It is important to do this *after* copying any old content, - // else we would override data that has not yet been copied! - _extern._refCount = 0; - _extern._capacity = newCapacity; - } -} - -void String::incRefCount() const { - assert(!isStorageIntern()); - if (_extern._refCount == 0) { - if (g_refCountPool == 0) { - g_refCountPool = new MemoryPool(sizeof(int)); - assert(g_refCountPool); - } - - _extern._refCount = (int *)g_refCountPool->allocChunk(); - *_extern._refCount = 2; - } else { - ++(*_extern._refCount); - } -} - -void String::decRefCount(int *oldRefCount) { - if (isStorageIntern()) - return; - - if (oldRefCount) { - --(*oldRefCount); - } - if (!oldRefCount || *oldRefCount <= 0) { - // The ref count reached zero, so we free the string storage - // and the ref count storage. - if (oldRefCount) { - assert(g_refCountPool); - g_refCountPool->freeChunk(oldRefCount); - } - delete[] _str; - - // Even though _str points to a freed memory block now, - // we do not change its value, because any code that calls - // decRefCount will have to do this afterwards anyway. - } -} - -String &String::operator=(const char *str) { - uint32 len = strlen(str); - ensureCapacity(len, false); - _size = len; - memmove(_str, str, len + 1); - return *this; -} - -String &String::operator=(const String &str) { - if (&str == this) - return *this; - - if (str.isStorageIntern()) { - decRefCount(_extern._refCount); - _size = str._size; - _str = _storage; - memcpy(_str, str._str, _size + 1); - } else { - str.incRefCount(); - decRefCount(_extern._refCount); - - _extern._refCount = str._extern._refCount; - _extern._capacity = str._extern._capacity; - _size = str._size; - _str = str._str; - } - - return *this; -} - -String &String::operator=(char c) { - decRefCount(_extern._refCount); - _str = _storage; - - _str[0] = c; - _str[1] = 0; - - _size = (c == 0) ? 0 : 1; - return *this; -} - -String &String::operator+=(const char *str) { - if (_str <= str && str <= _str + _size) - return operator+=(String(str)); - - int len = strlen(str); - if (len > 0) { - ensureCapacity(_size + len, true); - - memcpy(_str + _size, str, len + 1); - _size += len; - } - return *this; -} - -String &String::operator+=(const String &str) { - if (&str == this) - return operator+=(String(str)); - - int len = str._size; - if (len > 0) { - ensureCapacity(_size + len, true); - - memcpy(_str + _size, str._str, len + 1); - _size += len; - } - return *this; -} - -String &String::operator+=(char c) { - ensureCapacity(_size + 1, true); - - _str[_size++] = c; - _str[_size] = 0; - - return *this; -} - -bool String::hasPrefix(const String &x) const { - return hasPrefix(x.c_str()); -} - -bool String::hasPrefix(const char *x) const { - assert(x != 0); - // Compare x with the start of _str. - const char *y = c_str(); - while (*x && *x == *y) { - ++x; - ++y; - } - // It's a prefix, if and only if all letters in x are 'used up' before - // _str ends. - return *x == 0; -} - -bool String::hasSuffix(const String &x) const { - return hasSuffix(x.c_str()); -} - -bool String::hasSuffix(const char *x) const { - assert(x != 0); - // Compare x with the end of _str. - const uint32 x_size = strlen(x); - if (x_size > _size) - return false; - const char *y = c_str() + _size - x_size; - while (*x && *x == *y) { - ++x; - ++y; - } - // It's a suffix, if and only if all letters in x are 'used up' before - // _str ends. - return *x == 0; -} - -bool String::contains(const String &x) const { - return strstr(c_str(), x.c_str()) != NULL; -} - -bool String::contains(const char *x) const { - assert(x != 0); - return strstr(c_str(), x) != NULL; -} - -bool String::contains(char x) const { - return strchr(c_str(), x) != NULL; -} - -void String::deleteLastChar() { - if (_size > 0) - deleteChar(_size - 1); -} - -void String::deleteChar(uint32 p) { - assert(p < _size); - - makeUnique(); - while (p++ < _size) - _str[p - 1] = _str[p]; - _size--; -} - -void String::erase(uint32 p, uint32 len) { - assert(p < _size); - - makeUnique(); - // If len == npos or p + len is over the end, remove all the way to the end - if (len == npos || p + len >= _size) { - // Delete char at p as well. So _size = (p - 1) + 1 - _size = p; - // Null terminate - _str[_size] = 0; - return; - } - - for ( ; p + len <= _size; p++) { - _str[p] = _str[p + len]; - } - _size -= len; -} - -void String::clear() { - decRefCount(_extern._refCount); - - _size = 0; - _str = _storage; - _storage[0] = 0; -} - -void String::setChar(char c, uint32 p) { - assert(p < _size); - - makeUnique(); - _str[p] = c; -} - -void String::insertChar(char c, uint32 p) { - assert(p <= _size); - - ensureCapacity(_size + 1, true); - _size++; - for (uint32 i = _size; i > p; --i) - _str[i] = _str[i - 1]; - _str[p] = c; -} - -void String::toLowercase() { - makeUnique(); - for (uint32 i = 0; i < _size; ++i) - _str[i] = tolower(_str[i]); -} - -void String::toUppercase() { - makeUnique(); - for (uint32 i = 0; i < _size; ++i) - _str[i] = toupper(_str[i]); -} - -uint String::hash() const { - return hashit(c_str()); -} - -// static -String String::format(const char *fmt, ...) { - String output; - - va_list va; - va_start(va, fmt); - output = String::vformat(fmt, va); - va_end(va); - - return output; -} - -// static -String String::vformat(const char *fmt, va_list args) { - String output; - assert(output.isStorageIntern()); - - va_list va; - scumm_va_copy(va, args); - int len = vsnprintf(output._str, _builtinCapacity, fmt, va); - va_end(va); - - if (len == -1 || len == _builtinCapacity - 1) { - // MSVC and IRIX don't return the size the full string would take up. - // MSVC returns -1, IRIX returns the number of characters actually written, - // which is at the most the size of the buffer minus one, as the string is - // truncated to fit. - - // We assume MSVC failed to output the correct, null-terminated string - // if the return value is either -1 or size. - // For IRIX, because we lack a better mechanism, we assume failure - // if the return value equals size - 1. - // The downside to this is that whenever we try to format a string where the - // size is 1 below the built-in capacity, the size is needlessly increased. - - // Try increasing the size of the string until it fits. - int size = _builtinCapacity; - do { - size *= 2; - output.ensureCapacity(size - 1, false); - assert(!output.isStorageIntern()); - size = output._extern._capacity; - - scumm_va_copy(va, args); - len = vsnprintf(output._str, size, fmt, va); - va_end(va); - } while (len == -1 || len >= size - 1); - output._size = len; - } else if (len < (int)_builtinCapacity) { - // vsnprintf succeeded - output._size = len; - } else { - // vsnprintf didn't have enough space, so grow buffer - output.ensureCapacity(len, false); - scumm_va_copy(va, args); - int len2 = vsnprintf(output._str, len+1, fmt, va); - va_end(va); - assert(len == len2); - output._size = len2; - } - - return output; -} - - -#pragma mark - - -bool String::operator==(const String &x) const { - return equals(x); -} - -bool String::operator==(const char *x) const { - assert(x != 0); - return equals(x); -} - -bool String::operator!=(const String &x) const { - return !equals(x); -} - -bool String::operator !=(const char *x) const { - assert(x != 0); - return !equals(x); -} - -bool String::operator<(const String &x) const { - return compareTo(x) < 0; -} - -bool String::operator<=(const String &x) const { - return compareTo(x) <= 0; -} - -bool String::operator>(const String &x) const { - return compareTo(x) > 0; -} - -bool String::operator>=(const String &x) const { - return compareTo(x) >= 0; -} - -#pragma mark - - -bool operator==(const char* y, const String &x) { - return (x == y); -} - -bool operator!=(const char* y, const String &x) { - return x != y; -} - -#pragma mark - - -bool String::equals(const String &x) const { - return (0 == compareTo(x)); -} - -bool String::equals(const char *x) const { - assert(x != 0); - return (0 == compareTo(x)); -} - -bool String::equalsIgnoreCase(const String &x) const { - return (0 == compareToIgnoreCase(x)); -} - -bool String::equalsIgnoreCase(const char *x) const { - assert(x != 0); - return (0 == compareToIgnoreCase(x)); -} - -int String::compareTo(const String &x) const { - return compareTo(x.c_str()); -} - -int String::compareTo(const char *x) const { - assert(x != 0); - return strcmp(c_str(), x); -} - -int String::compareToIgnoreCase(const String &x) const { - return compareToIgnoreCase(x.c_str()); -} - -int String::compareToIgnoreCase(const char *x) const { - assert(x != 0); - return scumm_stricmp(c_str(), x); -} - -#pragma mark - - -String operator+(const String &x, const String &y) { - String temp(x); - temp += y; - return temp; -} - -String operator+(const char *x, const String &y) { - String temp(x); - temp += y; - return temp; -} - -String operator+(const String &x, const char *y) { - String temp(x); - temp += y; - return temp; -} - -String operator+(char x, const String &y) { - String temp(x); - temp += y; - return temp; -} - -String operator+(const String &x, char y) { - String temp(x); - temp += y; - return temp; -} - -String lastPathComponent(const String &path, const char sep) { - const char *str = path.c_str(); - const char *last = str + path.size(); - - // Skip over trailing slashes - while (last > str && *(last-1) == sep) - --last; - - // Path consisted of only slashes -> return empty string - if (last == str) - return String(); - - // Now scan the whole component - const char *first = last - 1; - while (first > str && *first != sep) - --first; - - if (*first == sep) - first++; - - return String(first, last); -} - -String normalizePath(const String &path, const char sep) { - if (path.empty()) - return path; - - const char *cur = path.c_str(); - String result; - - // If there is a leading slash, preserve that: - if (*cur == sep) { - result += sep; - // Skip over multiple leading slashes, so "//" equals "/" - while (*cur == sep) - ++cur; - } - - // Scan for path components till the end of the String - List 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_ultima/str.h b/devtools/create_ultima/str.h deleted file mode 100644 index 2f954dcfcaf..00000000000 --- a/devtools/create_ultima/str.h +++ /dev/null @@ -1,386 +0,0 @@ -/* 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 - -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_ultima/ultima1_map.cpp b/devtools/create_ultima/ultima1_map.cpp deleted file mode 100644 index f378def39ba..00000000000 --- a/devtools/create_ultima/ultima1_map.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - // Disable symbol overrides so that we can use system headers. -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -// HACK to allow building with the SDL backend on MinGW -// see bug #1800764 "TOOLS: MinGW tools building broken" -#ifdef main -#undef main -#endif // main - -#include "ultima1_map.h" -#include "file.h" - -void writeUltima1EnhancedMap(Archive &a) { - Common::MemFile map; - map.writeString("TEST"); - - a.add("ULTIMA1ENH/MAP", map); -} diff --git a/devtools/create_ultima/ultima1_resources.cpp b/devtools/create_ultima/ultima1_resources.cpp index acc5c2e9cfb..36e5d1ee287 100644 --- a/devtools/create_ultima/ultima1_resources.cpp +++ b/devtools/create_ultima/ultima1_resources.cpp @@ -20,22 +20,15 @@ * */ - // Disable symbol overrides so that we can use system headers. -#define FORBIDDEN_SYMBOL_ALLOW_ALL - -// HACK to allow building with the SDL backend on MinGW -// see bug #1800764 "TOOLS: MinGW tools building broken" -#ifdef main -#undef main -#endif // main - -#include "ultima1_resources.h" +#include "create_ultima.h" #include "file.h" #define FILE_BUFFER_SIZE 1024 #define MD5_COMPUTE_SIZE 1024 -uint32 computeMD5(Common::File &f) { +typedef unsigned int uint32; + +uint32 computeMD5(File &f) { uint32 total = 0; f.seek(0); for (int idx = 0; idx < MD5_COMPUTE_SIZE; ++idx) @@ -51,8 +44,12 @@ uint32 computeMD5(Common::File &f) { #define LOGO_WIDTH2 76 #define LOGO_TABLE1 0x3262 #define LOGO_TABLE2 0x4320 +#define FLAG_WIDTH 8 +#define FLAG_HEIGHT 8 +#define NUM_FLAGS 3 -void createLogo(Common::File &in, Common::MemFile &out) { +// Creates the logo.bmp file +void createLogo(File &in) { int offsets[LOGO_HEIGHT][2]; char buffer[LOGO_WIDTH1 + LOGO_WIDTH2 + 1]; @@ -64,6 +61,11 @@ void createLogo(Common::File &in, Common::MemFile &out) { for (int y = 0; y < LOGO_HEIGHT; ++y) offsets[y][1] = in.readWord(); + // Set up a surface + Surface out(LOGO_WIDTH1 + LOGO_WIDTH2, LOGO_HEIGHT); + out.setPaletteEntry(0, 0, 0, 0); + out.setPaletteEntry(1, 0xff, 0xff, 0xff); + // Convert the lines for (int y = 0; y < LOGO_HEIGHT; ++y) { in.seek(DATA_SEGMENT_OFFSET + offsets[y][0]); @@ -74,30 +76,40 @@ void createLogo(Common::File &in, Common::MemFile &out) { in.read(buffer + LOGO_WIDTH1, LOGO_WIDTH2 + 1); assert(buffer[LOGO_WIDTH1 + LOGO_WIDTH2] == '\0'); - for (int x = 0; x < (LOGO_WIDTH1 + LOGO_WIDTH2); ++x) - out.writeByte(buffer[x] == '*' ? 1 : 0); + byte *line = out.getBasePtr(0, y); + for (int x = 0; x < (LOGO_WIDTH1 + LOGO_WIDTH2); ++x, ++line) + *line = buffer[x] == '*' ? 1 : 0; } + + out.saveToFile("logo.bmp"); } -void writeUltima1Resources(Archive &a) { +// Creates the flags.bmp file +void createFlags(File &in) { + Surface out(FLAG_WIDTH * NUM_FLAGS, FLAG_HEIGHT); + out.setPaletteEntry(10, 0, 0, 0); + out.setPaletteEntry(11, 0xff, 0xff, 0xff); + + in.seek(DATA_SEGMENT_OFFSET + 0x124); + + for (int flagNum = 0; flagNum < NUM_FLAGS; ++flagNum) { + for (int yp = 0; yp < FLAG_HEIGHT; ++yp) { + byte *line = out.getBasePtr(flagNum * FLAG_WIDTH, yp); + for (int xp = 0; xp < FLAG_WIDTH; ++xp) + *line++ = in.readByte() ? 10 : 11; + } + } + + out.saveToFile("flags.bmp"); +} + +void extractUltima1Resources() { // Open up ultima1.exe for logo - Common::File u1; - if (!u1.open("Ultima 1/ultima.exe")) - error("Could not find ultima.exe"); + File u1("ultima.exe"); if (computeMD5(u1) != 64620) error("Unknown version of Ultima 1 ultima.exe"); - // Add the Origin logo - Common::MemFile logo; - createLogo(u1, logo); - a.add("ULTIMA1/LOGO", logo); - - // Add the flag data for the castle title screen - Common::MemFile flags; - u1.seek(DATA_SEGMENT_OFFSET + 0x124); - for (int idx = 0; idx < 64 * 3; ++idx) - flags.writeByte(u1.readByte() ? 10 : 11); - a.add("ULTIMA1/FLAGS", flags); - - u1.close(); + // Extract the Origin logo and flag animation data + createLogo(u1); + createFlags(u1); } diff --git a/devtools/create_ultima/ultima1_resources.h b/devtools/create_ultima/ultima1_resources.h index f79d86be242..b718da8e8cc 100644 --- a/devtools/create_ultima/ultima1_resources.h +++ b/devtools/create_ultima/ultima1_resources.h @@ -23,8 +23,6 @@ #ifndef ULTIMA1_RESOURCES_H #define ULTIMA1_RESOURCES_H -#include "archive.h" - -extern void writeUltima1Resources(Archive &a); +extern void extractUltima1Resources(); #endif