aboutsummaryrefslogtreecommitdiff
path: root/devtools
diff options
context:
space:
mode:
authorPaul Gilbert2018-02-20 18:50:17 -0500
committerPaul Gilbert2018-02-23 15:23:20 -0500
commit900ef91aca039dc23e4cf300070f7fa5f6f3a335 (patch)
tree3fb56a267ea2d8d3b36689eb302d44238645879e /devtools
parent1b85d1dbc966b909f9ff9c9cc0eaac4b8ab872ae (diff)
downloadscummvm-rg350-900ef91aca039dc23e4cf300070f7fa5f6f3a335.tar.gz
scummvm-rg350-900ef91aca039dc23e4cf300070f7fa5f6f3a335.tar.bz2
scummvm-rg350-900ef91aca039dc23e4cf300070f7fa5f6f3a335.zip
XEEN: Creation of create_xeen tool
Diffstat (limited to 'devtools')
-rw-r--r--devtools/create_project/xcode.cpp1
-rw-r--r--devtools/create_xeen/cc.cpp116
-rw-r--r--devtools/create_xeen/cc.h84
-rw-r--r--devtools/create_xeen/constants.cpp2098
-rw-r--r--devtools/create_xeen/constants.h31
-rw-r--r--devtools/create_xeen/create_xeen.cpp65
-rw-r--r--devtools/create_xeen/file.h264
-rw-r--r--devtools/create_xeen/hash-str.h86
-rw-r--r--devtools/create_xeen/hashmap.cpp109
-rw-r--r--devtools/create_xeen/hashmap.h637
-rw-r--r--devtools/create_xeen/memorypool.cpp182
-rw-r--r--devtools/create_xeen/memorypool.h162
-rw-r--r--devtools/create_xeen/module.mk15
-rw-r--r--devtools/create_xeen/str.cpp786
-rw-r--r--devtools/create_xeen/str.h386
15 files changed, 5022 insertions, 0 deletions
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index fb4dc5ef5b..fb459da55d 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -764,6 +764,7 @@ XcodeProvider::ValueList& XcodeProvider::getResourceFiles() const {
files.push_back("dists/engine-data/toon.dat");
files.push_back("dists/engine-data/wintermute.zip");
files.push_back("dists/engine-data/macventure.dat");
+ files.push_back("dists/engine-data/xeen.ccs");
files.push_back("dists/pred.dic");
files.push_back("icons/scummvm.icns");
}
diff --git a/devtools/create_xeen/cc.cpp b/devtools/create_xeen/cc.cpp
new file mode 100644
index 0000000000..4020301412
--- /dev/null
+++ b/devtools/create_xeen/cc.cpp
@@ -0,0 +1,116 @@
+/* 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 "cc.h"
+#include "common/endian.h"
+
+uint16 CCArchive::convertNameToId(const Common::String &resourceName) {
+ if (resourceName.empty())
+ return 0xffff;
+
+ Common::String name = resourceName;
+ name.toUppercase();
+
+ // Check if a resource number is being directly specified
+ if (name.size() == 4) {
+ char *endPtr;
+ uint16 num = (uint16)strtol(name.c_str(), &endPtr, 16);
+ if (!*endPtr)
+ return num;
+ }
+
+ const byte *msgP = (const byte *)name.c_str();
+ int total = *msgP++;
+ for (; *msgP; total += *msgP++) {
+ // Rotate the bits in 'total' right 7 places
+ total = (total & 0x007F) << 9 | (total & 0xFF80) >> 7;
+ }
+
+ return total;
+}
+
+void CCArchive::saveIndex() {
+ // Fill up the data for the index entries into a raw data block
+ byte *rawIndex = new byte[_index.size() * 8];
+ byte b;
+
+ byte *entryP = rawIndex;
+ uint entryOffset = 8 * _index.size() + 2;
+ for (uint i = 0; i < _index.size(); ++i, entryP += 8) {
+ CCEntry &entry = _index[i];
+ entry._offset = entryOffset;
+ entryOffset += entry._size;
+
+ WRITE_LE_UINT16(&entryP[0], entry._id);
+ WRITE_LE_UINT32(&entryP[2], entry._offset);
+ WRITE_LE_UINT16(&entryP[5], entry._size);
+ entryP[7] = 0;
+ }
+
+ // Encrypt the index
+ int seed = 0xac;
+ for (uint i = 0; i < _index.size() * 8; ++i, seed += 0x67) {
+ b = (rawIndex[i] - seed) & 0xff;
+ b = (byte)((b >> 2) | (b << 6));
+
+ assert(rawIndex[i] == (byte)((((b << 2) | (b >> 6)) + seed) & 0xff));
+ rawIndex[i] = b;
+ }
+
+ // Write out the number of entries and the encrypted index data
+ _file.writeWord(_index.size());
+ _file.write(rawIndex, _index.size() * 8);
+
+ delete[] rawIndex;
+}
+
+void CCArchive::saveEntries() {
+ for (uint idx = 0; idx < _index.size(); ++idx) {
+ CCEntry &ccEntry = _index[idx];
+
+ // Encrypt the entry
+ for (int i = 0; i < ccEntry._size; ++i)
+ ccEntry._data[i] ^= 0x35;
+
+ // Write out the entry
+ _file.seek(ccEntry._offset);
+ _file.write(ccEntry._data, ccEntry._size);
+ }
+}
+
+void CCArchive::close() {
+ saveIndex();
+ saveEntries();
+}
+
+void CCArchive::add(const Common::String &name, Common::MemFile &f) {
+ _index.push_back(CCEntry(convertNameToId(name), f.getData(), f.size()));
+}
diff --git a/devtools/create_xeen/cc.h b/devtools/create_xeen/cc.h
new file mode 100644
index 0000000000..192a7d5c37
--- /dev/null
+++ b/devtools/create_xeen/cc.h
@@ -0,0 +1,84 @@
+/* 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"
+
+/**
+ * Details of a single entry in a CC file index
+ */
+struct CCEntry {
+ uint16 _id;
+ int _offset;
+ uint16 _size;
+ byte _data[MAX_MEM_SIZE];
+
+ CCEntry() : _id(0), _offset(0), _size(0) {
+ memset(_data, 0, MAX_MEM_SIZE);
+ }
+ CCEntry(uint16 id, const byte *data, uint32 size) :
+ _id(id), _offset(0), _size(size) {
+ memcpy(_data, data, size);
+ }
+};
+
+class CCArchive {
+private:
+ Common::Array<CCEntry> _index;
+ Common::File &_file;
+private:
+ /**
+ * Convert a resource name to it's equivalent hash key
+ */
+ uint16 convertNameToId(const Common::String &resourceName);
+
+ /**
+ * Saves the index to the file
+ */
+ void saveIndex();
+
+ /**
+ * Saves the individual entries to the file
+ */
+ void saveEntries();
+public:
+ /**
+ * Constructor
+ */
+ CCArchive(Common::File &file) : _file(file) {}
+
+ /**
+ * Finishes the CC file, writing out the resulting content
+ */
+ void close();
+
+ /**
+ * Adds an entry to the CC
+ */
+ void add(const Common::String &name, Common::MemFile &f);
+};
+
+#endif
diff --git a/devtools/create_xeen/constants.cpp b/devtools/create_xeen/constants.cpp
new file mode 100644
index 0000000000..a74455822d
--- /dev/null
+++ b/devtools/create_xeen/constants.cpp
@@ -0,0 +1,2098 @@
+/* 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 "file.h"
+#include "constants.h"
+
+enum MagicSpell {
+ MS_AcidSpray = 0, MS_Awaken = 1, MS_BeastMaster = 2, MS_Bless = 3,
+ MS_Clairvoyance = 4, MS_ColdRay = 5, MS_CreateFood = 6,
+ MS_CureDisease = 7, MS_CureParalysis = 8, MS_CurePoison = 9,
+ MS_CureWounds = 10, MS_DancingSword = 11, MS_DayOfProtection = 12,
+ MS_DayOfSorcery = 13, MS_DeadlySwarm = 14, MS_DetectMonster = 15,
+ MS_DivineIntervention = 16, MS_DragonSleep = 17, MS_ElementalStorm = 18,
+ MS_EnchantItem = 19, MS_EnergyBlast = 20, MS_Etheralize = 21,
+ MS_FantasticFreeze = 22, MS_FieryFlail = 23, MS_FingerOfDeath = 24,
+ MS_Fireball = 25, MS_FirstAid = 26, MS_FlyingFist = 27,
+ MS_FrostBite = 28, MS_GolemStopper = 29, MS_Heroism = 30,
+ MS_HolyBonus = 31, MS_HolyWord = 32, MS_Hynotize = 33,
+ MS_IdentifyMonster = 34, MS_Implosion = 35, MS_Incinerate = 36,
+ MS_Inferno = 37, MS_InsectSpray = 38, MS_ItemToGold = 39,
+ MS_Jump = 40, MS_Levitate = 41, MS_Light = 42, MS_LightningBolt = 43,
+ MS_LloydsBeacon = 44, MS_MagicArrow = 45, MS_MassDistortion = 46,
+ MS_MegaVolts = 47, MS_MoonRay = 48, MS_NaturesCure = 49, MS_Pain = 50,
+ MS_PoisonVolley = 51, MS_PowerCure = 52, MS_PowerShield = 53,
+ MS_PrismaticLight = 54, MS_ProtFromElements = 55, MS_RaiseDead = 56,
+ MS_RechargeItem = 57, MS_Resurrection = 58, MS_Revitalize = 59,
+ MS_Shrapmetal = 60, MS_Sleep = 61, MS_Sparks = 62, MS_StarBurst = 63,
+ MS_StoneToFlesh = 64, MS_SunRay = 65, MS_SuperShelter = 66,
+ MS_SuppressDisease = 67, MS_SuppressPoison = 68, MS_Teleport = 69,
+ MS_TimeDistortion = 70, MS_TownPortal = 71, MS_ToxicCloud = 72,
+ MS_TurnUndead = 73, MS_WalkOnWater = 74, MS_WizardEye = 75,
+ NO_SPELL = 76
+};
+
+const char *const CREDITS =
+ "\013""012\010""000\003""c\014""35Designed and Directed By:\n"
+ "\014""17Jon Van Caneghem\003""l\n"
+ "\n"
+ "\t025\014""35Programming:\n"
+ "\t035\014""17Mark Caldwell\n"
+ "\t035Dave Hathaway\n"
+ "\n"
+ "\t025\014""35Sound System & FX:\n"
+ "\t035\014""17Mike Heilemann\n"
+ "\n"
+ "\t025\014""35Music & Speech:\n"
+ "\t035\014""17Tim Tully\n"
+ "\n"
+ "\t025\014""35Writing:\n"
+ "\t035\014""17Paul Rattner\n"
+ "\t035Debbie Van Caneghem\n"
+ "\t035Jon Van Caneghem\013""012\n"
+ "\n"
+ "\n"
+ "\t180\014""35Graphics:\n"
+ "\t190\014""17Jonathan P. Gwyn\n"
+ "\t190Bonita Long-Hemsath\n"
+ "\t190Julia Ulano\n"
+ "\t190Ricardo Barrera\n"
+ "\n"
+ "\t180\014""35Testing:\n"
+ "\t190\014""17Benjamin Bent\n"
+ "\t190Christian Dailey\n"
+ "\t190Mario Escamilla\n"
+ "\t190Marco Hunter\n"
+ "\t190Robert J. Lupo\n"
+ "\t190Clayton Retzer\n"
+ "\t190David Vela\003""c";
+
+const char *const OPTIONS_TITLE =
+ "\x0D\x01\003""c\014""dMight and Magic Options\n"
+ "World of Xeen\x02\n"
+ "\v117Copyright (c) 1993 NWC, Inc.\n"
+ "All Rights Reserved\x01";
+
+const char *const THE_PARTY_NEEDS_REST = "\x0B""012The Party needs rest!";
+
+const char *const WHO_WILL = "\x03""c\x0B""000\x09""000%s\x0A\x0A"
+ "Who will\x0A%s?\x0A\x0B""055F1 - F%d";
+
+const char *const HOW_MUCH = "\x3""cHow Much\n\n";
+
+const char *const WHATS_THE_PASSWORD = "What's the Password?";
+
+const char *const IN_NO_CONDITION = "\x0B""007%s is not in any condition to perform actions!";
+
+const char *const NOTHING_HERE = "\x03""c\x0B""010Nothing here.";
+
+const char *const TERRAIN_TYPES[6] = {
+ "town", "cave", "towr", "cstl", "dung", "scfi"
+};
+
+const char *const OUTDOORS_WALL_TYPES[16] = {
+ nullptr, "mount", "ltree", "dtree", "grass", "snotree", "dsnotree",
+ "snomnt", "dedltree", "mount", "lavamnt", "palm", "dmount", "dedltree",
+ "dedltree", "dedltree"
+};
+
+const char *const SURFACE_NAMES[16] = {
+ "water.srf", "dirt.srf", "grass.srf", "snow.srf", "swamp.srf",
+ "lava.srf", "desert.srf", "road.srf", "dwater.srf", "tflr.srf",
+ "sky.srf", "croad.srf", "sewer.srf", "cloud.srf", "scortch.srf",
+ "space.srf"
+};
+
+const char *const WHO_ACTIONS[32] = {
+ "search", "open", "drink", "mine", "touch", "read", "learn", "take",
+ "bang", "steal", "bribe", "pay", "sit", "try", "turn", "bathe",
+ "destroy", "pull", "descend", "toss a coin", "pray", "join", "act",
+ "play", "push", "rub", "pick", "eat", "sign", "close", "look", "try"
+};
+
+const char *const WHO_WILL_ACTIONS[4] = {
+ "Open Grate", "Open Door", "Open Scroll", "Select Char"
+};
+
+const byte SYMBOLS[20][64] = {
+ { // 0
+ 0x00, 0x00, 0xA8, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x00, 0xA8, 0x9E, 0x9C, 0x9C, 0x9E, 0x9E, 0x9E,
+ 0xAC, 0x9C, 0xA4, 0xAC, 0xAC, 0x9A, 0x9A, 0x9A, 0xAC, 0x9E, 0xAC, 0xA8, 0xA8, 0xA6, 0x97, 0x98,
+ 0xAC, 0xA0, 0xAC, 0xAC, 0xA4, 0xA6, 0x98, 0x99, 0x00, 0xAC, 0xA0, 0xA0, 0xA8, 0xAC, 0x9A, 0x9A,
+ 0x00, 0x00, 0xAC, 0xAC, 0xAC, 0xA4, 0x9B, 0x9A, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xA0, 0x9B, 0x9B,
+ },
+ { // 1
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x99, 0x9A, 0x9A, 0x99, 0x99, 0x99, 0x9A, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x99, 0x98, 0x98, 0x99, 0x98, 0x98, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9B, 0x9B, 0x9C, 0x9B, 0x9A, 0x9C, 0x9A, 0x9B, 0x9A, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9B,
+ },
+ { // 2
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x99, 0x98, 0x98, 0x99, 0x98, 0x98, 0x97, 0x98, 0x98,
+ 0x99, 0x98, 0x98, 0x98, 0x99, 0x99, 0x98, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9B, 0x9B, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9B, 0x99, 0x9A, 0x9B, 0x9B, 0x9A, 0x9A, 0x99, 0x9A,
+ },
+ { // 3
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x99, 0x9A, 0x9A, 0x9A, 0x99, 0x99, 0x99, 0x9A, 0x98, 0x98, 0x97, 0x97, 0x98, 0x98, 0x98, 0x98,
+ 0x99, 0x99, 0x98, 0x99, 0x98, 0x98, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9B, 0x9C, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x99, 0x99, 0x9A,
+ },
+ { // 4
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9A, 0x9A, 0x9A, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x99, 0x99, 0x98, 0x99, 0x99, 0x98, 0x98, 0x98, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9C, 0x9B, 0x9B, 0x9C, 0x9B, 0x9B, 0x9B, 0x9A, 0x99, 0x9B, 0x9B, 0x9A, 0x99, 0x9A, 0x9A,
+ },
+ { // 5
+ 0xA4, 0xA4, 0xA8, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x9E, 0x9E, 0xA0, 0xA8, 0xAC, 0x00, 0x00,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9E, 0xAC, 0x00, 0x97, 0x97, 0x97, 0x98, 0x9C, 0x9C, 0xA0, 0xAC,
+ 0x99, 0x98, 0x99, 0x99, 0x99, 0x9B, 0xA0, 0xAC, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, 0xA0, 0xAC,
+ 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0xA0, 0xAC, 0x00, 0x99, 0x9A, 0x9A, 0x9B, 0x9B, 0xA4, 0xAC, 0x00,
+ },
+ { // 6
+ 0x00, 0x00, 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x00, 0x00, 0x00, 0xAC, 0xA0, 0x9C, 0x9B, 0x99,
+ 0x00, 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x98, 0x99, 0x99,
+ 0x00, 0xAC, 0xA0, 0x9C, 0x9C, 0xA0, 0x9C, 0x9A, 0x00, 0x00, 0xAC, 0xA4, 0xA0, 0x99, 0x99, 0x99,
+ 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99,
+ },
+ { // 7
+ 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0xAC, 0xA4, 0x9C, 0x9C, 0x99, 0x99,
+ 0x00, 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x99,
+ 0x00, 0x00, 0xAC, 0xA0, 0x9B, 0xA0, 0x9E, 0x9C, 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x9C, 0x99, 0x99,
+ },
+ { // 8
+ 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x9B, 0x99, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x9C, 0x99, 0x00, 0xAC, 0xA4, 0x9C, 0x99, 0x9E, 0x9C, 0x99,
+ },
+ { // 9
+ 0x00, 0x00, 0xAC, 0xA4, 0xA0, 0x9C, 0x99, 0x99, 0x00, 0xAC, 0xA0, 0x9C, 0x9C, 0xA0, 0x9C, 0x9A,
+ 0xAC, 0xA4, 0x9C, 0x9A, 0x99, 0x99, 0x99, 0x99, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xAC, 0xA4, 0x9C, 0x99, 0x99, 0x99, 0x99, 0x99, 0x00, 0xAC, 0xA0, 0x9C, 0x99, 0x99, 0x99, 0x99,
+ 0x00, 0xAC, 0xA4, 0x9C, 0x9A, 0x9C, 0x99, 0x99, 0x00, 0x00, 0xAC, 0xA0, 0x9C, 0x9A, 0x99, 0x99,
+ },
+ { // 10
+ 0x99, 0x99, 0x99, 0x9A, 0xA0, 0xAC, 0x00, 0x00, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x00,
+ 0x99, 0x99, 0x9C, 0x9E, 0xA4, 0xAC, 0x00, 0x00, 0x99, 0x99, 0x9C, 0x99, 0x9C, 0xA4, 0xAC, 0x00,
+ 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x00,
+ 0x99, 0x99, 0x99, 0xA0, 0xA4, 0xAC, 0x00, 0x00, 0x9A, 0x9B, 0x9E, 0x9C, 0x9C, 0xA4, 0xAC, 0x00,
+ },
+ { // 11
+ 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0x9E, 0xAC,
+ 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA4, 0xAC, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC,
+ 0x9C, 0x99, 0x99, 0x99, 0x9C, 0x9C, 0xA4, 0xAC, 0x99, 0x9E, 0x9E, 0x9C, 0x9C, 0xA0, 0xAC, 0x00,
+ },
+ { // 12
+ 0x99, 0x99, 0x9C, 0xA0, 0xA4, 0xAC, 0x00, 0x00, 0x9B, 0x9C, 0x9E, 0x9C, 0x9C, 0xA4, 0xAC, 0x00,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0xA0, 0xAC, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC,
+ 0x99, 0x99, 0x99, 0x99, 0x9C, 0x9C, 0xA4, 0xAC, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xA4, 0xAC, 0x00,
+ 0x99, 0x99, 0x9C, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC,
+ },
+ { // 13
+ 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC, 0x00, 0x00,
+ 0x99, 0x9B, 0x9C, 0xA0, 0xA4, 0xAC, 0x00, 0x00, 0x99, 0x99, 0x9A, 0x99, 0x9C, 0xA0, 0xAC, 0x00,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA4, 0xAC, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9C, 0xA0, 0xAC,
+ 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9C, 0xA4, 0xAC, 0x99, 0x99, 0x99, 0x9A, 0x9C, 0xA4, 0xAC, 0x00,
+ },
+ { // 14
+ 0x00, 0x00, 0xAC, 0x9E, 0x9C, 0x9C, 0x9C, 0x9B, 0x00, 0xAC, 0x9C, 0xA0, 0x9E, 0xA4, 0xA4, 0xA4,
+ 0xAC, 0x9C, 0xA4, 0xAC, 0xAC, 0xAC, 0x9C, 0x9E, 0xAC, 0xA0, 0xAC, 0xA8, 0x9E, 0xA8, 0xAC, 0x99,
+ 0xAC, 0x9E, 0xAC, 0xA8, 0xAC, 0x9E, 0xA4, 0xAC, 0xAC, 0xA4, 0xA0, 0xAC, 0xAC, 0xA0, 0xA4, 0xAC,
+ 0x00, 0xAC, 0xA4, 0xA0, 0xA0, 0xA4, 0xAC, 0xA4, 0x00, 0x00, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ },
+ { // 15
+ 0x9C, 0x9C, 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9B, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0x9E, 0x9E, 0x9E, 0x9C, 0x9E, 0x9E, 0x9E, 0x9E, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x99, 0x98,
+ 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x9E, 0x9E, 0xA0,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ },
+ { // 16
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9B, 0x9C, 0x9C, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9E, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0xA0, 0xA0, 0xA0, 0x9E, 0xA0, 0x9E, 0x9E, 0xA0,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ },
+ { // 17
+ 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9C, 0x9B, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0x9E, 0x9E, 0x9E, 0x9C, 0x9C, 0x9C, 0x9E, 0x9E, 0x98, 0x98, 0x98, 0x99, 0x9A, 0x9A, 0x99, 0x98,
+ 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9C, 0xA0, 0x9E, 0x9E, 0xA0, 0xA0, 0xA0, 0xA0, 0x9E,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ },
+ { // 18
+ 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9C, 0x9C, 0x9C, 0x9E, 0x98, 0x98, 0x98, 0x98, 0x9A, 0x9A, 0x98, 0x99,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9B, 0x9C, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0xA0, 0xA0, 0xA0,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ },
+ { // 19
+ 0x9C, 0x9B, 0x9C, 0x9C, 0xA0, 0xA4, 0xAC, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0x00, 0x00,
+ 0x9E, 0x9E, 0x9C, 0x9C, 0x9E, 0xA0, 0xAC, 0x00, 0x99, 0x98, 0x98, 0x99, 0x9A, 0x9A, 0xA0, 0xAC,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0xA0, 0xAC, 0xA0, 0xA0, 0x9E, 0xA0, 0xA0, 0xA0, 0xA0, 0xAC,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xAC, 0x00, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0x00, 0x00,
+ }
+};
+
+const byte TEXT_COLORS[40][4] = {
+ { 0x00, 0x19, 0x19, 0x19 },
+ { 0x00, 0x08, 0x08, 0x08 },
+ { 0x00, 0x0F, 0x0F, 0x0F },
+ { 0x00, 0x15, 0x15, 0x15 },
+ { 0x00, 0x01, 0x01, 0x01 },
+ { 0x00, 0x21, 0x21, 0x21 },
+ { 0x00, 0x26, 0x26, 0x26 },
+ { 0x00, 0x2B, 0x2B, 0x2B },
+ { 0x00, 0x31, 0x31, 0x31 },
+ { 0x00, 0x36, 0x36, 0x36 },
+ { 0x00, 0x3D, 0x3D, 0x3D },
+ { 0x00, 0x41, 0x41, 0x41 },
+ { 0x00, 0x46, 0x46, 0x46 },
+ { 0x00, 0x4C, 0x4C, 0x4C },
+ { 0x00, 0x50, 0x50, 0x50 },
+ { 0x00, 0x55, 0x55, 0x55 },
+ { 0x00, 0x5D, 0x5D, 0x5D },
+ { 0x00, 0x60, 0x60, 0x60 },
+ { 0x00, 0x65, 0x65, 0x65 },
+ { 0x00, 0x6C, 0x6C, 0x6C },
+ { 0x00, 0x70, 0x70, 0x70 },
+ { 0x00, 0x75, 0x75, 0x75 },
+ { 0x00, 0x7B, 0x7B, 0x7B },
+ { 0x00, 0x80, 0x80, 0x80 },
+ { 0x00, 0x85, 0x85, 0x85 },
+ { 0x00, 0x8D, 0x8D, 0x8D },
+ { 0x00, 0x90, 0x90, 0x90 },
+ { 0x00, 0x97, 0x97, 0x97 },
+ { 0x00, 0x9D, 0x9D, 0x9D },
+ { 0x00, 0xA4, 0xA4, 0xA4 },
+ { 0x00, 0xAB, 0xAB, 0xAB },
+ { 0x00, 0xB0, 0xB0, 0xB0 },
+ { 0x00, 0xB6, 0xB6, 0xB6 },
+ { 0x00, 0xBD, 0xBD, 0xBD },
+ { 0x00, 0xC0, 0xC0, 0xC0 },
+ { 0x00, 0xC6, 0xC6, 0xC6 },
+ { 0x00, 0xCD, 0xCD, 0xCD },
+ { 0x00, 0xD0, 0xD0, 0xD0 },
+ { 0x00, 0xD6, 0xD6, 0xD6 },
+ { 0x00, 0xDB, 0xDB, 0xDB },
+};
+
+const char *const DIRECTION_TEXT_UPPER[4] = { "NORTH", "EAST", "SOUTH", "WEST" };
+
+const char *const DIRECTION_TEXT[4] = { "North", "East", "South", "West" };
+
+const char *const RACE_NAMES[5] = { "Human", "Elf", "Dwarf", "Gnome", "H-Orc" };
+
+const int RACE_HP_BONUSES[5] = { 0, -2, 1, -1, 2 };
+
+const int RACE_SP_BONUSES[5][2] = {
+ { 0, 0 }, { 2, 0 }, { -1, -1 }, { 1, 1 }, { -2, -2 }
+};
+
+const char *const ALIGNMENT_NAMES[3] = { "Good", "Neutral", "Evil" };
+
+const char *const SEX_NAMES[2] = { "Male", "Female" };
+
+const char *const SKILL_NAMES[18] = {
+ "Thievery\t100", "Arms Master", "Astrologer", "Body Builder", "Cartographer",
+ "Crusader", "Direction Sense", "Linguist", "Merchant", "Mountaineer",
+ "Navigator", "Path Finder", "Prayer Master", "Prestidigitator",
+ "Swimmer", "Tracker", "Spot Secret Door", "Danger Sense"
+};
+
+const char *const CLASS_NAMES[11] = {
+ "Knight", "Paladin", "Archer", "Cleric", "Sorcerer", "Robber",
+ "Ninja", "Barbarian", "Druid", "Ranger", nullptr
+};
+
+const int CLASS_EXP_LEVELS[10] = {
+ 1500, 2000, 2000, 1500, 2000, 1000, 1500, 1500, 1500, 2000
+};
+
+const char *const CONDITION_NAMES[17] = {
+ "Cursed", "Heart Broken", "Weak", "Poisoned", "Diseased",
+ "Insane", "In Love", "Drunk", "Asleep", "Depressed", "Confused",
+ "Paralyzed", "Unconscious", "Dead", "Stone", "Eradicated", "Good"
+};
+
+const int CONDITION_COLORS[17] = {
+ 9, 9, 9, 9, 9, 9, 9, 9, 32, 32, 32, 32, 6, 6, 6, 6, 15
+};
+
+const char *const GOOD = "Good";
+
+const char *const BLESSED = "\n\t020Blessed\t095%+d";
+
+const char *const POWER_SHIELD = "\n\t020Power Shield\t095%+d";
+
+const char *const HOLY_BONUS = "\n\t020Holy Bonus\t095%+d";
+
+const char *const HEROISM = "\n\t020Heroism\t095%+d";
+
+const char *const IN_PARTY = "\014""15In Party\014""d";
+
+const char *const PARTY_DETAILS = "\015\003l\002\014""00"
+ "\013""001""\011""035%s"
+ "\013""009""\011""035%s"
+ "\013""017""\011""035%s"
+ "\013""025""\011""035%s"
+ "\013""001""\011""136%s"
+ "\013""009""\011""136%s"
+ "\013""017""\011""136%s"
+ "\013""025""\011""136%s"
+ "\013""044""\011""035%s"
+ "\013""052""\011""035%s"
+ "\013""060""\011""035%s"
+ "\013""068""\011""035%s"
+ "\013""044""\011""136%s"
+ "\013""052""\011""136%s"
+ "\013""060""\011""136%s"
+ "\013""068""\011""136%s";
+const char *const PARTY_DIALOG_TEXT =
+ "%s\x2\x3""c\v106\t013Up\t048Down\t083\f37D\fdel\t118\f37R\fdem"
+ "\t153\f37C\fdreate\t188E\f37x\fdit\x1";
+
+const int FACE_CONDITION_FRAMES[17] = {
+ 2, 2, 2, 1, 1, 4, 4, 4, 3, 2, 4, 3, 3, 5, 6, 7, 0
+};
+
+const int CHAR_FACES_X[6] = { 10, 45, 81, 117, 153, 189 };
+
+const int HP_BARS_X[6] = { 13, 50, 86, 122, 158, 194 };
+
+const char *const NO_ONE_TO_ADVENTURE_WITH = "You have no one to adventure with";
+
+const char *const YOUR_ROSTER_IS_FULL = "Your Roster is full!";
+
+const byte DARKNESS_XLAT[3][256] = {
+ {
+ 0, 25, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 44, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 60, 61, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 92, 93, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 108, 109, 110, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 140, 141, 142, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 168, 169, 170, 171, 172, 173, 174, 175, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 188, 189, 190, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 204, 205, 206, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 220, 221, 222, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 236, 237, 238, 239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 252, 253, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, {
+ 0, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0,
+ 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0,
+ 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0,
+ 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0,
+ 104, 105, 106, 107, 108, 109, 110, 111, 0, 0, 0, 0, 0, 0, 0, 0,
+ 120, 121, 122, 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0,
+ 136, 137, 138, 139, 140, 141, 142, 143, 0, 0, 0, 0, 0, 0, 0, 0,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 184, 185, 186, 187, 188, 189, 190, 191, 0, 0, 0, 0, 0, 0, 0, 0,
+ 200, 201, 202, 203, 204, 205, 206, 207, 0, 0, 0, 0, 0, 0, 0, 0,
+ 216, 217, 218, 219, 220, 221, 222, 223, 0, 0, 0, 0, 0, 0, 0, 0,
+ 232, 233, 234, 235, 236, 237, 238, 239, 0, 0, 0, 0, 0, 0, 0, 0,
+ 248, 249, 250, 251, 252, 253, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0
+ }, {
+ 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 0, 0, 0, 0,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, 0,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 0, 0, 0, 0,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 0, 0, 0, 0,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 0, 0, 0, 0,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 0, 0, 0, 0, 0, 0, 0, 0,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 0, 0, 0, 0,
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 0, 0, 0, 0,
+ 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 0, 0, 0, 0,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 0, 0, 0, 0,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 0, 0, 0, 0
+ }
+};
+
+const char *const PLEASE_WAIT = "\014""d\003""c\011""000"
+ "\013""002Please Wait...";
+
+const char *const OOPS = "\003""c\011""000\013""002Oops...";
+
+const int SCREEN_POSITIONING_X[4][48] = {
+ {
+ -1, 0, 0, 0, 1, -1, 0, 0, 0, 1, -2, -1,
+ -1, 0, 0, 0, 1, 1, 2, -4, -3, -3, -2, -2,
+ -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4,
+ -3, -2, -1, 0, 0, 1, 2, 3, -4, 4, 0, 0
+ }, {
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 1
+ }, {
+ 1, 0, 0, 0, -1, 1, 0, 0, 0, -1, 2, 1,
+ 1, 0, 0, 0, -1, -1, -2, 4, 3, 3, 2, 2,
+ 1, 1, 0, 0, 0, -1, -1, -2, -2, -3, -3, -4,
+ 3, 2, 1, 0, 0, -1, -2, -3, 4, -4, 0, 0
+ }, {
+ 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, -1
+ }
+};
+
+const int SCREEN_POSITIONING_Y[4][48] = {
+ {
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 1
+ }, {
+ 1, 0, 0, 0, -1, 1, 0, 0, 0, -1, 2, 1,
+ 1, 0, 0, 0, -1, -1, -2, 4, 3, 3, 2, 2,
+ 1, 1, 0, 0, 0, -1, -1, -2, -2, -3, -3, -4,
+ 3, 2, 1, 0, 0, -1, -2, -3, 4, -4, 0, 0
+ }, {
+ 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3,
+ -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
+ -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, -1
+ }, {
+ -1, 0, 0, 0, 1, -1, 0, 0, 0, 1, -2, -1,
+ -1, 0, 0, 0, 1, 1, 2, -4, -3, -3, -2, -2,
+ -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4,
+ -3, -2, -1, 0, 0, 1, 2, 3, -4, 4, 0, 0
+ }
+};
+
+const int MONSTER_GRID_BITMASK[12] = {
+ 0xC, 8, 4, 0, 0xF, 0xF000, 0xF00, 0xF0, 0xF00, 0xF0, 0x0F, 0xF000
+};
+
+const int INDOOR_OBJECT_X[2][12] = {
+ { 5, -7, -112, 98, -8, -65, 49, -9, -34, 16, -58, 40 },
+ { -35, -35, -142, 68, -35, -95, 19, -35, -62, -14, -98, 16 }
+};
+
+const int MAP_OBJECT_Y[2][12] = {
+ { 2, 25, 25, 25, 50, 50, 50, 58, 58, 58, 58, 58 },
+ { -65, -6, -6, -6, 36, 36, 36, 54, 54, 54, 54, 54 }
+};
+
+const int INDOOR_MONSTERS_Y[4] = { 2, 34, 53, 59 };
+
+const int OUTDOOR_OBJECT_X[2][12] = {
+ { -5, -7, -112, 98, -8, -77, 61, -9, -43, 25, -74, 56 },
+ { -35, -35, -142, 68, -35, -95, 19, -35, -62, -24, -98, 16 }
+};
+
+const int OUTDOOR_MONSTER_INDEXES[26] = {
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 69, 70,
+ 71, 72, 73, 74, 75, 90, 91, 92, 93, 94, 112, 115, 118
+};
+
+const int OUTDOOR_MONSTERS_Y[26] = {
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 53, 53,
+ 53, 53, 53, 53, 53, 34, 34, 34, 34, 34, 2, 2, 2
+};
+
+const int DIRECTION_ANIM_POSITIONS[4][4] = {
+ { 0, 1, 2, 3 }, { 3, 0, 1, 2 }, { 2, 3, 0, 1 }, { 1, 2, 3, 0 }
+};
+
+const byte WALL_SHIFTS[4][48] = {
+ {
+ 12, 0, 12, 8, 12, 12, 0, 12, 8, 12, 12, 0,
+ 12, 0, 12, 8, 12, 8, 12, 12, 0, 12, 0, 12,
+ 0, 12, 0, 12, 8, 12, 8, 12, 8, 12, 8, 12,
+ 0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 4, 4
+ }, {
+ 8, 12, 8, 4, 8, 8, 12, 8, 4, 8, 8, 12,
+ 8, 12, 8, 4, 8, 4, 8, 8, 12, 8, 12, 8,
+ 12, 8, 12, 8, 4, 8, 4, 8, 4, 8, 4, 8,
+ 12, 12, 12, 12, 4, 4, 4, 4, 0, 0, 0, 0
+ }, {
+ 4, 8, 4, 0, 4, 4, 8, 4, 0, 4, 4, 8,
+ 4, 8, 4, 0, 4, 0, 4, 4, 8, 4, 8, 4,
+ 8, 4, 8, 4, 0, 4, 0, 4, 0, 4, 0, 4,
+ 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 12, 12
+ }, {
+ 0, 4, 0, 12, 0, 0, 4, 0, 12, 0, 0, 4,
+ 0, 4, 0, 12, 0, 12, 0, 0, 4, 0, 4, 0,
+ 4, 0, 4, 0, 12, 0, 12, 0, 12, 0, 12, 0,
+ 4, 4, 4, 4, 12, 12, 12, 12, 0, 0, 8, 8
+ }
+};
+
+const int DRAW_NUMBERS[25] = {
+ 36, 37, 38, 43, 42, 41,
+ 39, 20, 22, 24, 33, 31,
+ 29, 26, 10, 11, 18, 16,
+ 13, 5, 9, 6, 0, 4, 1
+};
+
+const int DRAW_FRAMES[25][2] = {
+ { 18, 24 }, { 19, 23 }, { 20, 22 }, { 24, 18 }, { 23, 19 }, { 22, 20 },
+ { 21, 21 }, { 11, 17 }, { 12, 16 }, { 13, 15 }, { 17, 11 }, { 16, 12 },
+ { 15, 13 }, { 14, 14 }, { 6, 10 }, { 7, 9 }, { 10, 6 }, { 9, 7 },
+ { 8, 8 }, { 3, 5 }, { 5, 3 }, { 4, 4 }, { 0, 2 }, { 2, 0 },
+ { 1, 1 }
+};
+
+const int COMBAT_FLOAT_X[8] = { -2, -1, 0, 1, 2, 1, 0, -1 };
+
+const int COMBAT_FLOAT_Y[8] = { -2, 0, 2, 0, -1, 0, 2, 0 };
+
+const int MONSTER_EFFECT_FLAGS[15][8] = {
+ { 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B },
+ { 0x10C, 0x10D, 0x10E, 0x10F, 0x0, 0x0, 0x0, 0x0 },
+ { 0x110, 0x111, 0x112, 0x113, 0x0, 0x0, 0x0, 0x0 },
+ { 0x114, 0x115, 0x116, 0x117, 0x0, 0x0, 0x0, 0x0 },
+ { 0x200, 0x201, 0x202, 0x203, 0x0, 0x0, 0x0, 0x0 },
+ { 0x300, 0x301, 0x302, 0x303, 0x400, 0x401, 0x402, 0x403 },
+ { 0x500, 0x501, 0x502, 0x503, 0x0, 0x0, 0x0, 0x0 },
+ { 0x600, 0x601, 0x602, 0x603, 0x0, 0x0, 0x0, 0x0 },
+ { 0x604, 0x605, 0x606, 0x607, 0x608, 0x609, 0x60A, 0x60B },
+ { 0x60C, 0x60D, 0x60E, 0x60F, 0x0, 0x0, 0x0, 0x0 },
+ { 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 },
+ { 0x101, 0x101, 0x101, 0x101, 0x101, 0x101, 0x101, 0x101 },
+ { 0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102 },
+ { 0x103, 0x103, 0x103, 0x103, 0x103, 0x103, 0x103, 0x103 },
+ { 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108 }
+};
+
+const int SPELLS_ALLOWED[3][40] = {
+ {
+ 0, 1, 2, 3, 5, 6, 7, 8, 9, 10,
+ 12, 14, 16, 23, 26, 27, 28, 30, 31, 32,
+ 33, 42, 46, 48, 49, 50, 52, 55, 56, 58,
+ 59, 62, 64, 65, 67, 68, 71, 73, 74, 76
+ }, {
+ 1, 4, 11, 13, 15, 17, 18, 19, 20, 21,
+ 22, 24, 25, 29, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 47, 51, 53, 54,
+ 57, 60, 61, 63, 66, 69, 70, 72, 75, 76
+ }, {
+ 0, 1, 2, 3, 4, 5, 7, 9, 10, 20,
+ 25, 26, 27, 28, 30, 31, 34, 38, 40, 41,
+ 42, 43, 44, 45, 49, 50, 52, 53, 55, 59,
+ 60, 61, 62, 67, 68, 72, 73, 74, 75, 76
+ }
+};
+
+const int BASE_HP_BY_CLASS[10] = { 10, 8, 7, 5, 4, 8, 7, 12, 6, 9 };
+
+const int AGE_RANGES[10] = { 1, 6, 11, 18, 36, 51, 76, 101, 201, 0xffff };
+
+const int AGE_RANGES_ADJUST[2][10] = {
+ { -250, -50, -20, -10, 0, -2, -5, -10, -20, -50 },
+ { -250, -50, -20, -10, 0, 2, 5, 10, 20, 50 }
+};
+
+const int STAT_VALUES[24] = {
+ 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 25, 30, 35, 40,
+ 50, 75, 100, 125, 150, 175, 200, 225, 250,
+};
+
+const int STAT_BONUSES[24] = {
+ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20
+};
+
+const int ELEMENTAL_CATEGORIES[6] = { 8, 15, 20, 25, 33, 36 };
+
+const int ATTRIBUTE_CATEGORIES[10] = {
+ 9, 17, 25, 33, 39, 45, 50, 56, 61, 72 };
+
+const int ATTRIBUTE_BONUSES[72] = {
+ 2, 3, 5, 8, 12, 17, 23, 30, 38, 47, // Might bonus
+ 2, 3, 5, 8, 12, 17, 23, 30, // INT bonus
+ 2, 3, 5, 8, 12, 17, 23, 30, // PER bonus
+ 2, 3, 5, 8, 12, 17, 23, 30, // SPD bonus
+ 3, 5, 10, 15, 20, 30, // ACC bonus
+ 5, 10, 15, 20, 25, 30, // LUC bonus
+ 4, 6, 10, 20, 50, // HP bonus
+ 4, 8, 12, 16, 20, 25, // SP bonus
+ 2, 4, 6, 10, 16, // AC bonus
+ 4, 6, 8, 10, 12, 14, 16, 18, 20, 25 // Thievery bonus
+};
+
+const int ELEMENTAL_RESISTENCES[37] = {
+ 0, 5, 7, 9, 12, 15, 20, 25, 30, 5, 7, 9, 12, 15, 20, 25,
+ 5, 10, 15, 20, 25, 10, 15, 20, 25, 40, 5, 7, 9, 11, 13, 15, 20, 25,
+ 5, 10, 20
+};
+
+const int ELEMENTAL_DAMAGE[37] = {
+ 0, 2, 3, 4, 5, 10, 15, 20, 30, 2, 3, 4, 5, 10, 15, 20, 2, 4, 5, 10, 20,
+ 2, 4, 8, 16, 32, 2, 3, 4, 5, 10, 15, 20, 30, 5, 10, 25
+};
+
+const int WEAPON_DAMAGE_BASE[35] = {
+ 0, 3, 2, 3, 2, 2, 4, 1, 2, 4, 2, 3,
+ 2, 2, 1, 1, 1, 1, 4, 4, 3, 2, 4, 2,
+ 2, 2, 5, 3, 3, 3, 3, 5, 4, 2, 6
+};
+
+const int WEAPON_DAMAGE_MULTIPLIER[35] = {
+ 0, 3, 3, 4, 5, 4, 2, 3, 3, 3, 3, 3,
+ 2, 4, 10, 6, 8, 9, 4, 3, 6, 8, 5, 6,
+ 4, 5, 3, 5, 6, 7, 2, 2, 2, 2, 4
+};
+
+const int METAL_DAMAGE[22] = {
+ -3, -6, -4, -2, 2, 4, 6, 8, 10, 0, 1,
+ 1, 2, 2, 3, 4, 5, 12, 15, 20, 30, 50
+};
+
+const int METAL_DAMAGE_PERCENT[22] = {
+ 253, 252, 3, 2, 1, 2, 3, 4, 6, 0, 1,
+ 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10
+};
+
+const int METAL_LAC[9] = { -3, 0, -2, -1, 1, 2, 4, 6, 8 };
+
+const int ARMOR_STRENGTHS[14] = { 0, 2, 4, 5, 6, 7, 8, 10, 4, 2, 1, 1, 1, 1 };
+
+const int MAKE_ITEM_ARR1[6] = { 0, 8, 15, 20, 25, 33 };
+
+const int MAKE_ITEM_ARR2[6][7][2] = {
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 5, 8 }, { 8, 8 } },
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 6, 7 }, { 7, 7 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 5 }, { 4, 5 }, { 5, 5 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 4, 5 }, { 5, 5 } },
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 5, 8 }, { 8, 8 } },
+ { { 0, 0 }, { 1, 1 }, { 1, 1 }, { 1, 2 }, { 2, 2 }, { 2, 3 }, { 3, 3 } }
+};
+
+const int MAKE_ITEM_ARR3[10][7][2] = {
+ { { 0, 0 }, { 1, 4 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 6, 10 }, { 10, 10 } },
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 5, 8 }, { 8, 8 } },
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 5, 8 }, { 8, 8 } },
+ { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 6 }, { 4, 7 }, { 5, 8 }, { 8, 8 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 5 }, { 4, 6 }, { 6, 6 } },
+ { { 0, 0 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 }, { 5, 6 }, { 6, 6 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 4, 5 }, { 5, 5 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 5 }, { 4, 6 }, { 6, 6 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 4, 5 }, { 5, 5 } },
+ { { 0, 0 }, { 1, 2 }, { 1, 4 }, { 3, 6 }, { 5, 8 }, { 7, 10 }, { 10, 10 } }
+};
+
+const int MAKE_ITEM_ARR4[2][7][2] = {
+ { { 0, 0 }, { 1, 4 }, { 3, 7 }, { 4, 8 }, { 5, 9 }, { 8, 9 }, { 9, 9 } },
+ { { 0, 0 }, { 1, 4 }, { 2, 6 }, { 4, 7 }, { 6, 10 }, { 9, 13 }, { 13, 13 } }
+};
+
+
+const int MAKE_ITEM_ARR5[8][2] = {
+ { 0, 0 }, { 1, 15 }, { 16, 30 }, { 31, 40 }, { 41, 50 },
+ { 51, 60 }, { 61, 73 }, { 61, 73 }
+};
+
+const int OUTDOOR_DRAWSTRUCT_INDEXES[44] = {
+ 37, 38, 39, 40, 41, 44, 42, 43, 47, 45, 46,
+ 48, 49, 52, 50, 51, 66, 67, 68, 69, 70, 71,
+ 72, 75, 73, 74, 87, 88, 89, 90, 91, 94, 92,
+ 93, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120
+};
+
+const int TOWN_MAXES[2][11] = {
+ { 23, 13, 32, 16, 26, 16, 16, 16, 16, 16, 16 },
+ { 26, 19, 48, 27, 26, 37, 16, 16, 16, 16, 16 }
+};
+
+const char *const TOWN_ACTION_MUSIC[2][7] = {
+ { "bank.m", "smith.m", "guild.m", "tavern.m",
+ "temple.m", "grounds.m", "endgame.m" },
+ { "bank.m", "sf09.m", "guild.m", "tavern.m",
+ "temple.m", "smith.m", "endgame.m" }
+};
+
+const char *const TOWN_ACTION_SHAPES[7] = {
+ "bnkr", "blck", "gild", "tvrn", "tmpl", "trng", "eface08"
+};
+
+const int TOWN_ACTION_FILES[2][7] = {
+ { 3, 2, 4, 2, 4, 2, 1 }, { 5, 3, 7, 5, 4, 6, 1 }
+};
+
+const char *const BANK_TEXT = "\xD\x2\x3""c\xB""122\t013"
+ "\xC""37D\xC""dep\t040\xC""37W\xC""dith\t067ESC"
+ "\x1\t000\xB""000Bank of Xeen\xB""015\n"
+ "Bank\x3l\n"
+ "Gold\x3r\t000%s\x3l\n"
+ "Gems\x3r\t000%s\x3""c\n"
+ "\n"
+ "Party\x3l\n"
+ "Gold\x3r\t000%s\x3l\n"
+ "Gems\x3r\t000%s";
+
+const char *const BLACKSMITH_TEXT = "\x01\x0D\x03""c\x0B""000\x09""000"
+ "Store Options for\x09""039\x0B""027%s\x03""l\x0B""046\n"
+ "\x09""011\x0C""37B\x0C""drowse\n"
+ "\x09""000\x0B""090Gold\x03r\x09""000%s"
+ "\x02\x03""c\x0B""122\x09""040ESC\x01";
+
+const char *const GUILD_NOT_MEMBER_TEXT =
+ "\n\nYou have to be a member to shop here.";
+
+const char *const GUILD_TEXT = "\x03""c\x0B""027\x09""039%s"
+ "\x03l\x0B""046\n"
+ "\x09""012\x0C""37B\x0C""duy Spells\n"
+ "\x09""012\x0C""37S\x0C""dpell Info";
+
+const char *const TAVERN_TEXT =
+ "\x0D\x03""c\x0B""000\x09""000Tavern Options for\x09""039"
+ "\x0B""027%s%s\x03l\x09""000"
+ "\x0B""090Gold\x03r\x09""000%s\x02\x03""c\x0B""122"
+ "\x09""021\x0C""37S\x0C""dign in\x09""060ESC\x01";
+
+const char *const FOOD_AND_DRINK =
+ "\x03l\x09""017\x0B""046\x0C""37D\x0C""drink\n"
+ "\x09""017\x0C""37F\x0C""dood\n"
+ "\x09""017\x0C""37T\x0C""dip\n"
+ "\x09""017\x0C""37R\x0C""dumors";
+
+const char *const GOOD_STUFF = "\n"
+ "\n"
+ "Good Stuff\n"
+ "\n"
+ "Hit a key!";
+
+const char *const HAVE_A_DRINK = "\n\nHave a Drink\n\nHit a key!";
+
+const char *const YOURE_DRUNK = "\n\nYou're Drunk\n\nHit a key!";
+
+const int TAVERN_EXIT_LIST[2][6][5][2] = {
+ {
+ { { 21, 17 }, { 0, 0 }, { 20, 3 }, { 0, 0 }, { 0, 0 } },
+ { { 13, 4 }, { 0, 0 }, { 19, 9 }, { 0, 0 }, { 0, 0 } },
+ { { 20, 10 }, { 12, 8 }, { 5, 26 }, { 3, 4 }, { 7, 5 } },
+ { { 18, 4 }, { 0, 0 }, { 19, 16 }, { 0, 0 }, { 11, 12 } },
+ { { 15, 21 }, { 0, 0 }, { 13, 21 }, { 0, 0 }, { 0, 0 } },
+ { { 10, 8 }, { 0, 0 }, { 15, 12 }, { 0, 0 }, { 0, 0 } },
+ }, {
+ { { 21, 17 }, { 0, 0 }, { 20, 3 }, { 0, 0 }, { 0, 0 } },
+ { { 13, 4 }, { 0, 0 }, { 19, 9 }, { 0, 0 }, { 0, 0 } },
+ { { 20, 10 }, { 12, 8 }, { 5, 26 }, { 3, 4 }, { 7, 5 } },
+ { { 17, 24 }, { 14, 13 }, { 0, 0 }, { 0, 0 }, { 9, 4 } },
+ { { 15, 21 }, { 0, 0 }, { 13, 21 }, { 0, 0 }, { 0, 0 } },
+ { { 10, 8 }, { 0, 0 }, { 15, 12 }, { 0, 0 }, { 0, 0 } }
+ }
+};
+
+const char *const TEMPLE_TEXT =
+ "\x0D\x03""c\x0B""000\x09""000Temple Options for"
+ "\x09""039\x0B""027%s\x03l\x09""000\x0B""046"
+ "\x0C""37H\x0C""deal\x03r\x09""000%lu\x03l\n"
+ "\x0C""37D\x0C""donation\x03r\x09""000%lu\x03l\n"
+ "\x0C""37U\x0C""dnCurse\x03r\x09""000%s"
+ "\x03l\x09""000\x0B""090Gold\x03r\x09""000%s"
+ "\x02\x03""c\x0B""122\x09""040ESC\x01";
+
+const char *const EXPERIENCE_FOR_LEVEL =
+ "%s needs %lu experience for level %u.";
+
+const char *const LEARNED_ALL = "%s has learned all we can teach!";
+
+const char *const ELIGIBLE_FOR_LEVEL = "%s is eligible for level %d.";
+
+const char *const TRAINING_TEXT =
+ "\x0D\x03""cTraining Options\n"
+ "\n"
+ "%s\x03l\x0B""090\x09""000Gold\x03r\x09"
+ "000%s\x02\x03""c\x0B""122\x09""021"
+ "\x0C""37T\x0C""drain\x09""060ESC\x01";
+
+const char *const GOLD_GEMS =
+ "\x03""c\x0B""000\x09""000%s\x03l\n"
+ "\n"
+ "Gold\x03r\x09""000%s\x03l\n"
+ "Gems\x03r\x09""000%s\x02\x03""c\x0B""096\x09""013G"
+ "\x0C""37o\x0C""dld\x09""040G\x0C\x03""7e"
+ "\x0C""dms\x09""067ESC\x01";
+
+const char *const GOLD_GEMS_2 =
+ "\x09""000\x0B""000\x03""c%s\x03l\n"
+ "\n"
+ "\x04""077Gold\x03r\x09""000%s\x03l\n"
+ "\x04""077Gems\x03r\x09""000%s\x03l\x09""000\x0B""051\x04""077\n"
+ "\x04""077";
+
+const char *const DEPOSIT_WITHDRAWL[2] = { "Deposit", "Withdrawl" };
+
+const char *const NOT_ENOUGH_X_IN_THE_Y =
+ "\x03""c\x0B""012Not enough %s in the %s!\x03l";
+
+const char *const NO_X_IN_THE_Y = "\x03""c\x0B""012No %s in the %s!\x03l";
+
+const char *const STAT_NAMES[16] = {
+ "Might", "Intellect", "Personality", "Endurance", "Speed",
+ "Accuracy", "Luck", "Age", "Level", "Armor Class", "Hit Points",
+ "Spell Points", "Resistances", "Skills", "Awards", "Experience"
+};
+
+const char *const CONSUMABLE_NAMES[4] = { "Gold", "Gems", "Food", "Condition" };
+
+const char *const WHERE_NAMES[2] = { "Party", "Bank" };
+
+const char *const AMOUNT = "\x03""c\x09""000\x0B""051Amount\x03l\n";
+
+const char *const FOOD_PACKS_FULL = "\v007Your food packs are already full!";
+
+const char *const BUY_SPELLS =
+ "\x03""c\x0B""027\x09""039%s\x03l\x0B""046\n"
+ "\x09""012\x0C""37B\x0C""duy Spells\n"
+ "\x09""012\x0C""37S\x0C""dpell Info";
+
+const char *const GUILD_OPTIONS =
+ "\x0D\x0C""00\x03""c\x0B""000\x09""000Guild Options for%s"
+ "\x03l\x09""000\x0B""090Gold"
+ "\x03r\x09""000%s\x02\x03""c\x0B""122\x09""040ESC\x01";
+
+const int MISC_SPELL_INDEX[74] = {
+ NO_SPELL, MS_Light, MS_Awaken, MS_MagicArrow,
+ MS_FirstAid, MS_FlyingFist, MS_EnergyBlast, MS_Sleep,
+ MS_Revitalize, MS_CureWounds, MS_Sparks, MS_Shrapmetal,
+ MS_InsectSpray, MS_ToxicCloud, MS_ProtFromElements, MS_Pain,
+ MS_Jump, MS_BeastMaster, MS_Clairvoyance, MS_TurnUndead,
+ MS_Levitate, MS_WizardEye, MS_Bless, MS_IdentifyMonster,
+ MS_LightningBolt, MS_HolyBonus, MS_PowerCure, MS_NaturesCure,
+ MS_LloydsBeacon, MS_PowerShield, MS_Heroism, MS_Hynotize,
+ MS_WalkOnWater, MS_FrostBite, MS_DetectMonster, MS_Fireball,
+ MS_ColdRay, MS_CurePoison, MS_AcidSpray, MS_TimeDistortion,
+ MS_DragonSleep, MS_CureDisease, MS_Teleport, MS_FingerOfDeath,
+ MS_CureParalysis, MS_GolemStopper, MS_PoisonVolley, MS_DeadlySwarm,
+ MS_SuperShelter, MS_DayOfProtection, MS_DayOfSorcery, MS_CreateFood,
+ MS_FieryFlail, MS_RechargeItem, MS_FantasticFreeze, MS_TownPortal,
+ MS_StoneToFlesh, MS_RaiseDead, MS_Etheralize, MS_DancingSword,
+ MS_MoonRay, MS_MassDistortion, MS_PrismaticLight, MS_EnchantItem,
+ MS_Incinerate, MS_HolyWord, MS_Resurrection, MS_ElementalStorm,
+ MS_MegaVolts, MS_Inferno, MS_SunRay, MS_Implosion,
+ MS_StarBurst, MS_DivineIntervention
+};
+
+const int SPELL_COSTS[77] = {
+ 8, 1, 5, -2, 5, -2, 20, 10, 12, 8, 3,
+ - 3, 75, 40, 12, 6, 200, 10, 100, 30, -1, 30,
+ 15, 25, 10, -2, 1, 2, 7, 20, -2, -2, 100,
+ 15, 5, 100, 35, 75, 5, 20, 4, 5, 1, -2,
+ 6, 2, 75, 40, 60, 6, 4, 25, -2, -2, 60,
+ - 1, 50, 15, 125, 2, -1, 3, -1, 200, 35, 150,
+ 15, 5, 4, 10, 8, 30, 4, 5, 7, 5, 0
+};
+
+const int DARK_SPELL_RANGES[12][2] = {
+ { 0, 20 }, { 16, 35 }, { 27, 37 }, { 29, 39 },
+ { 0, 17 }, { 14, 34 }, { 26, 37 }, { 29, 39 },
+ { 0, 20 }, { 16, 35 }, { 27, 37 }, { 29, 39 }
+};
+
+const int CLOUDS_SPELL_OFFSETS[5][20] = {
+ {
+ 1, 10, 20, 26, 27, 38, 40, 42, 45, 50,
+ 55, 59, 60, 61, 62, 68, 72, 75, 77, 77
+ }, {
+ 3, 4, 5, 14, 15, 25, 30, 31, 34, 41,
+ 49, 51, 53, 67, 73, 75, -1, -1, -1, -1
+ }, {
+ 4, 8, 9, 12, 13, 22, 23, 24, 28, 34,
+ 41, 44, 52, 70, 73, 74, -1, -1, -1, -1
+ }, {
+ 6, 7, 9, 11, 12, 13, 17, 21, 22, 24,
+ 29, 36, 56, 58, 64, 71, -1, -1, -1, -1
+ }, {
+ 6, 7, 9, 11, 12, 13, 18, 21, 29, 32,
+ 36, 37, 46, 51, 56, 58, 69, -1, -1, -1
+ }
+};
+
+const int DARK_SPELL_OFFSETS[3][39] = {
+ {
+ 42, 1, 26, 59, 27, 10, 50, 68, 55, 62, 67, 73, 2,
+ 5, 3, 31, 30, 52, 49, 28, 74, 0, 9, 7, 14, 8,
+ 33, 6, 23, 71, 64, 56, 48, 46, 12, 32, 58, 65, 16
+ }, {
+ 42, 1, 45, 61, 72, 40, 20, 60, 38, 41, 75, 34, 4,
+ 43, 25, 53, 44, 15, 70, 17, 24, 69, 22, 66, 57, 11,
+ 29, 39, 51, 21, 19, 36, 47, 13, 54, 37, 18, 35, 63
+ }, {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38
+ }
+};
+
+const int SPELL_GEM_COST[77] = {
+ 0, 0, 2, 1, 2, 4, 5, 0, 0, 0, 0, 10, 10, 10, 0, 0, 20, 4, 10, 20, 1, 10,
+ 5, 5, 4, 2, 0, 0, 0, 10, 3, 1, 20, 4, 0, 20, 10, 10, 1, 10, 0, 0, 0, 2,
+ 2, 0, 10, 10, 10, 0, 0, 10, 3, 2, 10, 1, 10, 10, 20, 0, 0, 1, 1, 20, 5, 20,
+ 5, 0, 0, 0, 0, 5, 1, 2, 0, 2, 0
+};
+
+const char *const NOT_A_SPELL_CASTER = "Not a spell caster...";
+
+const char *const SPELLS_FOR = "\xD\xC""d%s\x2\x3""c\x9""000\xB""002Spells for %s";
+
+const char *const SPELL_LINES_0_TO_9 =
+ "\x2\x3l\xB""015\x9""0011\n2\n3\n4\n5\n6\n7\n8\n9\n0";
+
+const char *const SPELLS_DIALOG_SPELLS = "\x3l\xB""015"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l\n"
+ "\x9""010\xC""%2u%s\xC""d\x3l"
+ "\x9""004\xB""110%s - %lu\x1";
+
+const char *const SPELL_PTS = "Spell Pts";
+
+const char *const GOLD = "Gold";
+
+const char *const SPELLS_PRESS_A_KEY =
+ "\x3""c\xC""09%s\xC""d\x3l\n"
+ "\n"
+ "%s\x3""c\x9""000\xB""100Press a Key!";
+
+const char *const SPELLS_PURCHASE =
+ "\x3l\xB""000\x9""000\xC""d%s Do you wish to purchase "
+ "\xC""09%s\xC""d for %u?";
+
+const char *const MAP_TEXT =
+ "\x3""c\xB""000\x9""000%s\x3l\xB""139"
+ "\x9""000X = %d\x3r\x9""000Y = %d\x3""c\x9""000%s";
+
+const char *const LIGHT_COUNT_TEXT = "\x3l\n\n\t024Light\x3r\t124%d";
+
+const char *const FIRE_RESISTENCE_TEXT = "%c%sFire%s%u";
+
+const char *const ELECRICITY_RESISTENCE_TEXT = "%c%sElectricity%s%u";
+
+const char *const COLD_RESISTENCE_TEXT = "c%sCold%s%u";
+
+const char *const POISON_RESISTENCE_TEXT = "%c%sPoison/Acid%s%u";
+
+const char *const CLAIRVOYANCE_TEXT = "%c%sClairvoyance%s";
+
+const char *const LEVITATE_TEXT = "%c%sLevitate%s";
+
+const char *const WALK_ON_WATER_TEXT = "%c%sWalk on Water";
+
+const char *const GAME_INFORMATION =
+ "\xD\x3""c\x9""000\xB""001\xC""37%s of Xeen\xC""d\n"
+ "Game Information\n"
+ "\n"
+ "Today is \xC""37%ssday\xC""d\n"
+ "\n"
+ "\x9""032Time\x9""072Day\x9""112Year\n"
+ "\x9""032\xC""37%d:%02d%c\x9""072%u\x9""112%u\xC""d%s";
+
+const char *const WORLD_GAME_TEXT = "World";
+const char *const DARKSIDE_GAME_TEXT = "Darkside";
+const char *const CLOUDS_GAME_TEXT = "Clouds";
+const char *const SWORDS_GAME_TEXT = "Swords";
+
+const char *const WEEK_DAY_STRINGS[10] = {
+ "Ten", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"
+};
+
+const char *const CHARACTER_DETAILS =
+ "\x3l\xB""041\x9""196%s\x9""000\xB""002%s : %s %s %s"
+ "\x3r\x9""053\xB""028\xC%02u%u\xC""d\x9""103\xC""%02u%u\xC""d"
+ "\x3l\x9""131\xC""%02u%d\xC""d\x9""196\xC""15%lu\xC""d\x3r"
+ "\x9""053\xB""051\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d"
+ "\x3l\x9""131\xC""%02u%u\xC""d\x9""196\xC""15%lu\xC""d"
+ "\x3r\x9""053\xB""074\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d"
+ "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""15%lu\xC""d"
+ "\x3r\x9""053\xB""097\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d"
+ "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""15%u day%c\xC""d"
+ "\x3r\x9""053\xB""120\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d"
+ "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""%02u%s\xC""d"
+ "\x9""230%s%s%s%s\xC""d";
+
+const char *const PARTY_GOLD = "Party Gold";
+
+const char *const PLUS_14 = "14+";
+
+const char *const CHARACTER_TEMPLATE =
+ "\x1\xC""00\xD\x3l\x9""029\xB""018Mgt\x9""080Acy\x9""131H.P.\x9""196Experience"
+ "\x9""029\xB""041Int\x9""080Lck\x9""131S.P.\x9""029\xB""064Per\x9""080Age"
+ "\x9""131Resis\x9""196Party Gems\x9""029\xB""087End\x9""080Lvl\x9""131Skills"
+ "\x9""196Party Food\x9""029\xB""110Spd\x9""080AC\x9""131Awrds\x9""196Condition\x3""c"
+ "\x9""290\xB""025\xC""37I\xC""dtem\x9""290\xB""057\xC""37Q"
+ "\xC""duick\x9""290\xB""089\xC""37E\xC""dxch\x9""290\xB""121Exit\x3l%s";
+
+const char *const EXCHANGING_IN_COMBAT = "\x3""c\xB""007\x9""000Exchanging in combat is not allowed!";
+
+const char *const CURRENT_MAXIMUM_RATING_TEXT = "\x2\x3""c%s\n"
+ "Current / Maximum\n"
+ "\x3r\x9""054%lu\x3l\x9""058/ %lu\n"
+ "\x3""cRating: %s";
+
+const char *const CURRENT_MAXIMUM_TEXT = "\x2\x3""c%s\n"
+ "Current / Maximum\n"
+ "\x3r\x9""054%u\x3l\x9""058/ %u";
+
+const char *const RATING_TEXT[24] = {
+ "Nonexistant", "Very Poor", "Poor", "Very Low", "Low", "Averarage", "Good",
+ "Very Good", "High", "Very High", "Great", "Super", "Amazing", "Incredible",
+ "Gigantic", "Fantastic", "Astoundig", "Astonishing", "Monumental", "Tremendous",
+ "Collosal", "Awesome", "AweInspiring", "aUltimate"
+};
+
+const char *const AGE_TEXT = "\x2\x3""c%s\n"
+ "Current / Natural\n"
+ "\x3r\x9""057%u\x3l\x9""061/ %u\n"
+ "\x3""cBorn: %u / %u\x1";
+
+const char *const LEVEL_TEXT =
+ "\x2\x3""c%s\n"
+ "Current / Maximum\n"
+ "\x3r\x9""054%u\x3l\x9""058/ %u\n"
+ "\x3""c%u Attack%s/Round\x1";
+
+const char *const RESISTENCES_TEXT =
+ "\x2\x3""c%s\x3l\n"
+ "\x9""020Fire\x9""100%u\n"
+ "\x9""020Cold\x9""100%u\n"
+ "\x9""020Electricity\x9""100%u\n"
+ "\x9""020Poison\x9""100%u\n"
+ "\x9""020Energy\x9""100%u\n"
+ "\x9""020Magic\x9""100%u";
+
+const char *const NONE = "\n\x9""020";
+
+const char *const EXPERIENCE_TEXT = "\x2\x3""c%s\x3l\n"
+ "\x9""010Current:\x9""070%lu\n"
+ "\x9""010Next Level:\x9""070%s\x1";
+
+const char *const ELIGIBLE = "\xC""12Eligible\xC""d";
+
+const char *const IN_PARTY_IN_BANK =
+ "\x2\x3""cParty %s\n"
+ "%lu on hand\n"
+ "%lu in bank\x1\x3l";
+
+const char *const FOOD_TEXT =
+ "\x2\x3""cParty %s\n"
+ "%u on hand\n"
+ "Enough for %u day%s\x3l";
+
+const char *const EXCHANGE_WITH_WHOM = "\t010\v005Exchange with whom?";
+
+const char *const QUICK_REF_LINE =
+ "\xB%3d\x9""007%u)\x9""027%s\x9""110%c%c%c\x3r\x9""160\xC%02u%u\xC""d"
+ "\x3l\x9""170\xC%02u%d\xC""d\x9""208\xC%02u%u\xC""d\x9""247\xC"
+ "%02u%u\xC""d\x9""270\xC%02u%c%c%c%c\xC""d";
+
+const char *const QUICK_REFERENCE =
+ "\xD\x3""cQuick Reference Chart\xB""012\x3l"
+ "\x9""007#\x9""027Name\x9""110Cls\x9""140Lvl\x9""176H.P."
+ "\x9""212S.P.\x9""241A.C.\x9""270Cond"
+ "%s%s%s%s%s%s%s%s"
+ "\xB""110\x9""064\x3""cGold\x9""144Gems\x9""224Food\xB""119"
+ "\x9""064\xC""15%lu\x9""144%lu\x9""224%u day%s\xC""d";
+
+const int BLACKSMITH_MAP_IDS[2][4] = { { 28, 30, 73, 49 }, { 29, 31, 37, 43 } };
+
+const char *const ITEMS_DIALOG_TEXT1 =
+ "\r\x2\x3""c\v021\t017\f37W\fdeap\t051\f37A\fdrmor\t085A"
+ "\f37c\fdces\t119\f37M\fdisc\t153%s\t187%s\t221%s"
+ "\t255%s\t289Exit";
+const char *const ITEMS_DIALOG_TEXT2 =
+ "\r\x2\x3""c\v021\t017\f37W\fdeap\t051\f37A\fdrmor\t085A"
+ "\f37c\fdces\t119\f37M\fdisc\t153\f37%s\t289Exit";
+const char *const ITEMS_DIALOG_LINE1 = "\x3r\f%02u\f023%2d)\x3l\t028%s\n";
+const char *const ITEMS_DIALOG_LINE2 = "\x3r\f%02u\t023%2d)\x3l\t028%s\x3r\t000%lu\n";
+
+const char *const BTN_BUY = "\f37B\fduy";
+const char *const BTN_SELL = "\f37S\fdell";
+const char *const BTN_IDENTIFY = "\f37I\fddentify";
+const char *const BTN_FIX = "\f37F\fdix";
+const char *const BTN_USE = "\f37U\fdse";
+const char *const BTN_EQUIP = "\f37E\fdquip";
+const char *const BTN_REMOVE = "\f37R\fdem";
+const char *const BTN_DISCARD = "\f37D\fdisc";
+const char *const BTN_QUEST = "\f37Q\fduest";
+const char *const BTN_ENCHANT = "E\fdnchant";
+const char *const BTN_RECHARGE = "R\fdechrg";
+const char *const BTN_GOLD = "G\fdold";
+
+const char *const ITEM_BROKEN = "\f32broken ";
+const char *const ITEM_CURSED = "\f09cursed ";
+const char *const BONUS_NAMES[7] = {
+ "", "Dragon Slayer", "Undead Eater", "Golem Smasher",
+ "Bug Zapper", "Monster Masher", "Beast Bopper"
+};
+
+const char *const WEAPON_NAMES[35] = {
+ nullptr, "long sword ", "short sword ", "broad sword ", "scimitar ",
+ "cutlass ", "sabre ", "club ", "hand axe ", "katana ", "nunchakas ",
+ "wakazashi ", "dagger ", "mace ", "flail ", "cudgel ", "maul ", "spear ",
+ "bardiche ", "glaive ", "halberd ", "pike ", "flamberge ", "trident ",
+ "staff ", "hammer ", "naginata ", "battle axe ", "grand axe ", "great axe ",
+ "short bow ", "long bow ", "crossbow ", "sling ", "Xeen Slayer Sword"
+};
+
+const char *const ARMOR_NAMES[14] = {
+ nullptr, "robes ", "sale armor ", "ring mail ", "chain mail ",
+ "splint mail ", "plate mail ", "plate armor ", "shield ",
+ "helm ", "boots ", "cloak ", "cape ", "gauntlets "
+};
+
+const char *const ACCESSORY_NAMES[11] = {
+ nullptr, "ring ", "belt ", "broach ", "medal ", "charm ", "cameo ",
+ "scarab ", "pendant ", "necklace ", "amulet "
+};
+
+const char *const MISC_NAMES[22] = {
+ nullptr, "rod ", "jewel ", "gem ", "box ", "orb ", "horn ", "coin ",
+ "wand ", "whistle ", "potion ", "scroll ", "RogueVM",
+ "bogusg", "bogus", "bogus", "bogus", "bogus",
+ "bogus", "bogus", "bogus", "bogus"
+};
+
+const char *const ELEMENTAL_NAMES[6] = {
+ "Fire", "Elec", "Cold", "Acid/Poison", "Energy", "Magic"
+};
+
+const char *const ATTRIBUTE_NAMES[10] = {
+ "might", "Intellect", "Personality", "Speed", "accuracy", "Luck",
+ "Hit Points", "Spell Points", "Armor Class", "Thievery"
+};
+
+const char *const EFFECTIVENESS_NAMES[7] = {
+ nullptr, "Dragons", "Undead", "Golems", "Bugs", "Monsters", "Beasts"
+};
+
+const char *const QUEST_ITEM_NAMES[85] = {
+ "Deed to New Castle",
+ "Crystal Key to Witch Tower",
+ "Skeleton Key to Darzog's Tower",
+ "Enchanted Key to Tower of High Magic",
+ "Jeweled Amulet of the Northern Sphinx",
+ "Stone of a Thousand Terrors",
+ "Golem Stone of Admittance",
+ "Yak Stone of Opening",
+ "Xeen's Scepter of Temporal Distortion",
+ "Alacorn of Falista",
+ "Elixir of Restoration",
+ "Wand of Faery Magic",
+ "Princess Roxanne's Tiara",
+ "Holy Book of Elvenkind",
+ "Scarab of Imaging",
+ "Crystals of Piezoelectricity",
+ "Scroll of Insight",
+ "Phirna Root",
+ "Orothin's Bone Whistle",
+ "Barok's Magic Pendant",
+ "Ligono's Missing Skull",
+ "Last Flower of Summer",
+ "Last Raindrop of Spring",
+ "Last Snowflake of Winter",
+ "Last Leaf of Autumn",
+ "Ever Hot Lava Rock",
+ "King's Mega Credit",
+ "Excavation Permit",
+ "Cupie Doll",
+ "Might Doll",
+ "Speed Doll",
+ "Endurance Doll",
+ "Accuracy Doll",
+ "Luck Doll",
+ "Widget",
+ "Pass to Castleview",
+ "Pass to Sandcaster",
+ "Pass to Lakeside",
+ "Pass to Necropolis",
+ "Pass to Olympus",
+ "Key to Great Western Tower",
+ "Key to Great Southern Tower",
+ "Key to Great Eastern Tower",
+ "Key to Great Northern Tower",
+ "Key to Ellinger's Tower",
+ "Key to Dragon Tower",
+ "Key to Darkstone Tower",
+ "Key to Temple of Bark",
+ "Key to Dungeon of Lost Souls",
+ "Key to Ancient Pyramid",
+ "Key to Dungeon of Death",
+ "Amulet of the Southern Sphinx",
+ "Dragon Pharoah's Orb",
+ "Cube of Power",
+ "Chime of Opening",
+ "Gold ID Card",
+ "Silver ID Card",
+ "Vulture Repellant",
+ "Bridle",
+ "Enchanted Bridle",
+ "Treasure Map (Goto E1 x1, y11)",
+ "",
+ "Fake Map",
+ "Onyx Necklace",
+ "Dragon Egg",
+ "Tribble",
+ "Golden Pegasus Statuette",
+ "Golden Dragon Statuette",
+ "Golden Griffin Statuette",
+ "Chalice of Protection",
+ "Jewel of Ages",
+ "Songbird of Serenity",
+ "Sandro's Heart",
+ "Ector's Ring",
+ "Vespar's Emerald Handle",
+ "Queen Kalindra's Crown",
+ "Caleb's Magnifying Glass",
+ "Soul Box",
+ "Soul Box with Corak inside",
+ "Ruby Rock",
+ "Emerald Rock",
+ "Sapphire Rock",
+ "Diamond Rock",
+ "Monga Melon",
+ "Energy Disk"
+};
+
+const int WEAPON_BASE_COSTS[35] = {
+ 0, 50, 15, 100, 80, 40, 60, 1, 10, 150, 30, 60, 8, 50,
+ 100, 15, 30, 15, 200, 80, 250, 150, 400, 100, 40, 120,
+ 300, 100, 200, 300, 25, 100, 50, 15, 0
+};
+const int ARMOR_BASE_COSTS[14] = {
+ 0, 20, 100, 200, 400, 600, 1000, 2000, 100, 60, 40, 250, 200, 100
+};
+const int ACCESSORY_BASE_COSTS[11] = {
+ 0, 100, 100, 250, 100, 50, 300, 200, 500, 1000, 2000
+};
+const int MISC_MATERIAL_COSTS[22] = {
+ 0, 50, 1000, 500, 10, 100, 20, 10, 50, 10, 10, 100,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+const int MISC_BASE_COSTS[76] = {
+ 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 300, 300, 300, 300, 300,
+ 300, 300, 300, 300, 300, 400, 400, 400, 400, 400, 400, 400,
+ 400, 400, 400, 500, 500, 500, 500, 500, 500, 500, 500, 500,
+ 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
+ 600, 600, 600, 600
+};
+const int METAL_BASE_MULTIPLIERS[22] = {
+ 10, 25, 5, 75, 2, 5, 10, 20, 50, 2, 3, 5, 10, 20, 30, 40,
+ 50, 60, 70, 80, 90, 100
+};
+const int ITEM_SKILL_DIVISORS[4] = { 1, 2, 100, 10 };
+
+const int RESTRICTION_OFFSETS[4] = { 0, 35, 49, 60 };
+
+const int ITEM_RESTRICTIONS[86] = {
+ 0, 86, 86, 86, 86, 86, 86, 0, 6, 239, 239, 239, 2, 4, 4, 4, 4,
+ 6, 70, 70, 70, 70, 94, 70, 0, 4, 239, 86, 86, 86, 70, 70, 70, 70,
+ 0, 0, 0, 68, 100, 116, 125, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+const char *const NOT_PROFICIENT =
+ "\t000\v007\x3""c%ss are not proficient with a %s!";
+
+const char *const NO_ITEMS_AVAILABLE = "\x3""c\n"
+ "\t000No items available.";
+
+const char *const CATEGORY_NAMES[4] = { "Weapons", "Armor", "Accessories", "Miscellaneous" };
+
+const char *const X_FOR_THE_Y =
+ "\x1\fd\r%s\v000\t000%s for %s the %s%s\v011\x2%s%s%s%s%s%s%s%s%s\x1\fd";
+
+const char *const X_FOR_Y =
+ "\x1\xC""d\r\x3l\v000\t000%s for %s\x3r\t000%s\x3l\v011\x2%s%s%s%s%s%s%s%s%s\x1\xC""d";
+
+const char *const X_FOR_Y_GOLD =
+ "\x1\fd\r\x3l\v000\t000%s for %s\t150Gold - %lu%s\x3l\v011"
+ "\x2%s%s%s%s%s%s%s%s%s\x1\fd";
+
+const char *const FMT_CHARGES = "\x3rr\t000Charges\x3l";
+
+const char *const AVAILABLE_GOLD_COST =
+ "\x1\fd\r\x3l\v000\t000Available %s\t150Gold - %lu\x3r\t000Cost"
+ "\x3l\v011\x2%s%s%s%s%s%s%s%s%s\x1\xC""d";
+
+const char *const CHARGES = "Charges";
+
+const char *const COST = "Cost";
+
+const char *const ITEM_ACTIONS[7] = {
+ "Equip", "Remove", "Use", "Discard", "Enchant", "Recharge", "Gold"
+};
+const char *const WHICH_ITEM = "\t010\v005%s which item?";
+
+const char *const WHATS_YOUR_HURRY = "\v007What's your hurry?\n"
+ "Wait till you get out of here!";
+
+const char *const USE_ITEM_IN_COMBAT =
+ "\v007To use an item in Combat, invoke the Use command on your turn!";
+
+const char *const NO_SPECIAL_ABILITIES = "\v005\x3""c%s\fdhas no special abilities!";
+
+const char *const CANT_CAST_WHILE_ENGAGED = "\x3""c\v007Can't cast %s while engaged!";
+
+const char *const EQUIPPED_ALL_YOU_CAN = "\x3""c\v007You have equipped all the %ss you can!";
+const char *const REMOVE_X_TO_EQUIP_Y = "\x3""c\v007You must remove %sto equip %s\x8!";
+const char *const RING = "ring";
+const char *const MEDAL = "medal";
+
+const char *const CANNOT_REMOVE_CURSED_ITEM = "\x3""You cannot remove a cursed item!";
+
+const char *const CANNOT_DISCARD_CURSED_ITEM = "\3x""cYou cannot discard a cursed item!";
+
+const char *const PERMANENTLY_DISCARD = "\v000\t000\x03lPermanently discard %s\fd?";
+
+const char *const BACKPACK_IS_FULL = "\v005\x3""c\fd%s's backpack is full.";
+
+const char *const CATEGORY_BACKPACK_IS_FULL[4] = {
+ "\v010\t000\x3""c%s's weapons backpack is full.",
+ "\v010\t000\x3""c%s's armor backpack is full.",
+ "\v010\t000\x3""c%s's accessories backpack is full.",
+ "\v010\t000\x3""c%s's miscellaneous backpack is full."
+};
+
+const char *const BUY_X_FOR_Y_GOLD = "\x3l\v000\t000\fdBuy %s\fd for %lu gold?";
+
+const char *const SELL_X_FOR_Y_GOLD = "\x3l\v000\t000\fdSell %s\fd for %lu gold?";
+
+const char *const NO_NEED_OF_THIS = "\v005\x3""c\fdWe have no need of this %s\f!";
+
+const char *const NOT_RECHARGABLE = "\v012\x3""c\fdNot Rechargeable. %s";
+
+const char *const NOT_ENCHANTABLE = "\v012\t000\x3""cNot Enchantable. %s";
+
+const char *const SPELL_FAILED = "Spell Failed!";
+
+const char *const ITEM_NOT_BROKEN = "\fdThat item is not broken!";
+
+const char *const FIX_IDENTIFY[2] = { "Fix", "Identify" };
+
+const char *const FIX_IDENTIFY_GOLD = "\x3l\v000\t000%s %s\fd for %lu gold?";
+
+const char *const IDENTIFY_ITEM_MSG = "\fd\v000\t000\x3""cIdentify Item\x3l\n"
+ "\n"
+ "\v012%s\fd\n"
+ "\n"
+ "%s";
+
+const char *const ITEM_DETAILS =
+ "Proficient Classes\t132:\t140%s\n"
+ "to Hit Modifier\t132:\t140%s\n"
+ "Physical Damage\t132:\t140%s\n"
+ "Elemental Damage\t132:\t140%s\n"
+ "Elemental Resistance\t132:\t140%s\n"
+ "Armor Class Bonus\t132:\t140%s\n"
+ "Attribute Bonus\t132:\t140%s\n"
+ "Special Power\t132:\t140%s";
+
+const char *const ALL = "All";
+const char *const FIELD_NONE = "None";
+const char *const DAMAGE_X_TO_Y = "%d to %d";
+const char *const ELEMENTAL_XY_DAMAGE = "%+d %s Damage";
+const char *const ATTR_XY_BONUS = "%+d %s";
+const char *const EFFECTIVE_AGAINST = "x3 vs %s";
+
+const char *const QUESTS_DIALOG_TEXT =
+ "\r\x2\x3""c\v021\t017\f37I\fdtems\t085\f37Q\fduests\t153"
+ "\f37A\fduto Notes 221\f37U\fdp\t255\f37D\fdown"
+ "\t289Exit";
+const char *const CLOUDS_OF_XEEN_LINE = "\b \b*-- \f04Clouds of Xeen\fd --";
+const char *const DARKSIDE_OF_XEEN_LINE = "\b \b*-- \f04Darkside of Xeen\fd --";
+
+const char *const NO_QUEST_ITEMS =
+ "\r\x3""c\v000 000Quest Items\x3l\x2\n"
+ "\n"
+ "\x3""cNo Quest Items";
+const char *const NO_CURRENT_QUESTS =
+ "\x3""c\v000\t000\n"
+ "\n"
+ "No Current Quests";
+const char *const NO_AUTO_NOTES = "\x3""cNo Auto Notes";
+
+const char *const QUEST_ITEMS_DATA =
+ "\r\x1\fd\x3""c\v000\t000Quest Items\x3l\x2\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s\n"
+ "\f04 * \fd%s";
+const char *const CURRENT_QUESTS_DATA =
+ "\r\x1\fd\x3""c\t000\v000Current Quests\x3l\x2\n"
+ "%s\n"
+ "\n"
+ "%s\n"
+ "\n"
+ "%s";
+const char *const AUTO_NOTES_DATA =
+ "\r\x1\fd\x3""c\t000\v000Auto Notes\x3l\x2\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l\n"
+ "%s\x3l";
+
+const char *const REST_COMPLETE =
+ "\v000\t0008 hours pass. Rest complete.\n"
+ "%s\n"
+ "%d food consumed.";
+const char *const PARTY_IS_STARVING = "\f07The Party is Starving!\fd";
+const char *const HIT_SPELL_POINTS_RESTORED = "Hit Pts and Spell Pts restored.";
+const char *const TOO_DANGEROUS_TO_REST = "Too dangerous to rest here!";
+const char *const SOME_CHARS_MAY_DIE = "Some Chars may die. Rest anyway?";
+
+const char *const CANT_DISMISS_LAST_CHAR = "You cannot dismiss your last character!";
+
+const char *const REMOVE_DELETE[2] = { "Remove", "Delete" };
+
+const char *const REMOVE_OR_DELETE_WHICH = "\x3l\t010\v005%s which character?";
+
+const char *const YOUR_PARTY_IS_FULL = "\v007Your party is full!";
+
+const char *const HAS_SLAYER_SWORD =
+ "\v000\t000This character has the Xeen Slayer Sword and cannot be deleted!";
+const char *const SURE_TO_DELETE_CHAR =
+ "Are you sure you want to delete %s the %s?";
+
+const char *const CREATE_CHAR_DETAILS =
+ "\f04\x3""c\x2\t144\v119\f37R\f04oll\t144\v149\f37C\f04reate"
+ "\t144\v179\f37ESC\f04\x3l\x1\t195\v021\f37M\f04gt"
+ "\t195\v045\f37I\f04nt\t195\v069\f37P\f04er\t195\v093\f37E\f04nd"
+ "\t195\v116\f37S\f04pd\t195\v140\f37A\f04cy\t195\v164\f37L\f04ck%s";
+
+const char *const NEW_CHAR_STATS =
+ "\f04\x3l\t022\v148Race\t055: %s\n"
+ "\t022Sex\t055: %s\n"
+ "\t022Class\t055:\n"
+ "\x3r\t215\v031%d\t215\v055%d\t215\v079%d\t215\v103%d\t215\v127%d"
+ "\t215\v151%d\t215\v175%d\x3l\t242\v020\f%.2dKnight\t242\v031\f%.2d"
+ "Paladin\t242\v042\f%.2dArcher\t242\v053\f%.2dCleric\t242\v064\f%.2d"
+ "Sorcerer\t242\v075\f%.2dRobber\t242\v086\f%.2dNinja\t242\v097\f%.2d"
+ "Barbarian\t242\v108\f%.2dDruid\t242\v119\f%.2dRanger\f04\x3""c"
+ "\t265\v142Skills\x3l\t223\v155%s\t223\v170%s%s";
+
+const char *const NAME_FOR_NEW_CHARACTER =
+ "\x3""cEnter a Name for this Character\n\n";
+const char *const SELECT_CLASS_BEFORE_SAVING =
+ "\v006\x3""cSelect a Class before saving.\x3l";
+const char *const EXCHANGE_ATTR_WITH = "Exchange %s with...";
+
+const int NEW_CHAR_SKILLS[10] = { 1, 5, -1, -1, 4, 0, 0, -1, 6, 11 };
+const int NEW_CHAR_SKILLS_LEN[10] = { 11, 8, 0, 0, 12, 8, 8, 0, 9, 11 };
+const int NEW_CHAR_RACE_SKILLS[10] = { 14, -1, 17, 16, -1, 0, 0, 0, 0, 0 };
+
+const int RACE_MAGIC_RESISTENCES[5] = { 7, 5, 20, 0, 0 };
+const int RACE_FIRE_RESISTENCES[5] = { 7, 0, 2, 5, 10 };
+const int RACE_ELECTRIC_RESISTENCES[5] = { 7, 0, 2, 5, 10 };
+const int RACE_COLD_RESISTENCES[5] = { 7, 0, 2, 5, 10 };
+const int RACE_ENERGY_RESISTENCES[5] = { 7, 5, 2, 5, 0 };
+const int RACE_POISON_RESISTENCES[5] = { 7, 0, 2, 20, 0 };
+const int NEW_CHARACTER_SPELLS[10][4] = {
+ { -1, -1, -1, -1 },
+ { 21, -1, -1, -1 },
+ { 22, -1, -1, -1 },
+ { 21, 1, 14, -1 },
+ { 22, 0, 25, -1 },
+ { -1, -1, -1, -1 },
+ { -1, -1, -1, -1 },
+ { -1, -1, -1, -1 },
+ { 20, 1, 11, 23 },
+ { 20, 1, -1, -1 }
+};
+
+const char *const COMBAT_DETAILS = "\r\f00\x3""c\v000\t000\x2""Combat%s%s%s\x1";
+
+const char *NOT_ENOUGH_TO_CAST = "\x3""c\v010Not enough %s to Cast %s";
+const char *SPELL_CAST_COMPONENTS[2] = { "Spell Points", "Gems" };
+
+const char *const CAST_SPELL_DETAILS =
+ "\r\x2\x3""c\v122\t013\f37C\fdast\t040\f37N\fdew"
+ "\t067ESC\x1\t000\v000\x3""cCast Spell\n"
+ "\n"
+ "%s\x3l\n"
+ "\n"
+ "Spell Ready:\x3""c\n"
+ "\n"
+ "\f09%s\fd\x2\x3l\n"
+ "\v082Cost\x3r\t000%u/%u\x3l\n"
+ "Cur SP\x3r\t000%u\x1";
+
+const char *const PARTY_FOUND =
+ "\x3""cThe Party Found:\n"
+ "\n"
+ "\x3r\t000%lu Gold\n"
+ "%lu Gems";
+
+const char *const BACKPACKS_FULL_PRESS_KEY =
+ "\v007\f12Warning! BackPacks Full!\fd\n"
+ "Press a Key";
+
+const char *const HIT_A_KEY = "\x3l\v120\t000\x4""077\x3""c\f37Hit a key\xC""d";
+
+const char *const GIVE_TREASURE_FORMATTING =
+ "\x3l\v060\t000\x4""077\n"
+ "\x4""077\n"
+ "\x4""077\n"
+ "\x4""077\n"
+ "\x4""077\n"
+ "\x4""077";
+
+const char *const X_FOUND_Y = "\v060\t000\x3""c%s found: %s";
+
+const char *const ON_WHO = "\x3""c\v009On Who?";
+
+const char *const WHICH_ELEMENT1 =
+ "\r\x3""c\x1Which Element?\x2\v034\t014\f15F\fdire\t044"
+ "\f15E\fdlec\t074\f15C\fdold\t104\f15A\fdcid\x1";
+
+const char *const WHICH_ELEMENT2 =
+ "\r\x3""cWhich Element?', 2, 0Bh, '034\t014\f15F\fdire\t044"
+ "\f15E\fdlec\t074\f15C\fdold\t104\f15A\fdcid\x1";
+
+const char *const DETECT_MONSTERS = "\x3""cDetect Monsters";
+
+const char *const LLOYDS_BEACON =
+ "\r\x3""c\v000\t000\x1Lloyd's Beacon\n"
+ "\n"
+ "Last Location\n"
+ "\n"
+ "%s\x3l\n"
+ "x = %d\x3r\t000y = %d\x3""c\x2\v122\t021\f15S\fdet\t060\f15R\fdeturn\x1";
+
+const char *const HOW_MANY_SQUARES = "\x3""cTeleport\nHow many squares %s (1-9)";
+
+const char *const TOWN_PORTAL =
+ "\x3""cTown Portal\x3l\n"
+ "\n"
+ "\t0101. %s\n"
+ "\t0102. %s\n"
+ "\t0103. %s\n"
+ "\t0104. %s\n"
+ "\t0105. %s\x3""c\n"
+ "\n"
+ "To which Town (1-5)\n"
+ "\n";
+
+const int TOWN_MAP_NUMBERS[2][5] = {
+ { 28, 29, 30, 31, 32 }, { 29, 31, 33, 35, 37 }
+};
+
+const char *const MONSTER_DETAILS =
+ "\x3l\n"
+ "%s\x3""c\t100%s\t140%u\t180%u\x3r\t000%s";
+
+const char *const MONSTER_SPECIAL_ATTACKS[23] = {
+ "None", "Magic", "Fire", "Elec", "Cold", "Poison", "Energy", "Disease",
+ "Insane", "Asleep", "CurseItm", "InLove", "DrnSPts", "Curse", "Paralys",
+ "Uncons", "Confuse", "BrkWpn", "Weak", "Erad", "Age+5", "Dead", "Stone"
+};
+
+const char *const IDENTIFY_MONSTERS =
+ "Name\x3""c\t100HP\t140AC\t177#Atks\x3r\t000Special%s%s%s";
+
+const char *const EVENT_SAMPLES[6] = {
+ "ahh.voc", "whereto.voc", "gulp.voc", "null.voc", "scream.voc", "laff1.voc"
+};
+
+const char *const MOONS_NOT_ALIGNED =
+"\x3""c\xB""012\t000The moons are not aligned. Passage to the %s is unavailable";
+
+const char *const AWARDS_FOR =
+ "\r\x1\fd\x3""c\v000\t000Awards for %s the %s\x3""l\x2\n"
+ "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\x1";
+
+const char *const AWARDS_TEXT =
+ "\r\x2\x3""c\xB""021\t221\xC""37U\xC""dp\t255\xC""37D\xC""down\t289Exit";
+
+const char *const NO_AWARDS = "\x3""cNo Awards";
+
+const char *const WARZONE_BATTLE_MASTER = "The Warzone\n\t125Battle Master";
+
+const char *const WARZONE_MAXED = "What! You again? Go pick on someone your own size!";
+
+const char *const WARZONE_LEVEL = "What level of monsters? (1-10)\n";
+
+const char *const WARZONE_HOW_MANY = "How many monsters? (1-20)\n";
+
+const char *const PICKS_THE_LOCK = "\x3""c\xB""010%s picks the lock!\nPress any key.";
+
+const char *const UNABLE_TO_PICK_LOCK = "\x3""c\v010%s was unable to pick the lock!\nPress any key.";
+
+const char *const CONTROL_PANEL_TEXT =
+ "\x1\xC""00\x3""c\xB""000\t000Control Panel\x3r"
+ "\xB""022\t045\xC""06L\xC""doad:\t124\xC""06E\xC""dfx:"
+ "\xB""041\t045\xC""06S\xC""dave:\t124\xC""06M\xC""dusic:"
+ "\xB""060\t045\xC""06Q\xC""duit:"
+ "\xB""080\t084Mr \xC""06W\xC""dizard:%s\t000\x1";
+const char *const CONTROL_PANEL_BUTTONS =
+ "\x3""c\f11"
+ "\xB""022\t062load\t141%s"
+ "\xB""041\t062save\t141%s"
+ "\xB""060\t062exit"
+ "\xB""079\t102Help\xC""d";
+const char *const ON = "\f15on\f11";
+const char *const OFF = "\f32off\f11";
+const char *const CONFIRM_QUIT = "Are you sure you want to quit?";
+const char *const MR_WIZARD =
+ "Are you sure you want Mr.Wizard's Help ?";
+const char *const NO_LOADING_IN_COMBAT =
+ "No Loading Allowed in Combat!";
+const char *const NO_SAVING_IN_COMBAT =
+ "No Saving Allowed in Combat!";
+const char *const QUICK_FIGHT_TEXT = "\r\fd\x3""c\v000\t000QuickFight Options\n\n"
+ "%s\x3l\n\n"
+ "Current\x3r\n"
+ "\t000%s\x2\x3""c\v122\t021\f37N\f04ext\t060Exit\x1";
+const char *const QUICK_FIGHT_OPTIONS[4] = { "Attack", "Cast", "Block", "Run" };
+
+const char *const WORLD_END_TEXT[9] = {
+ "\n\n\n\n\n\n\n"
+ "Congratulations Adventurers!\n\n"
+ "Let the unification ceremony begin!",
+ "And so the call went out to the people throughout the lands of Xeen"
+ " that the prophecy was nearing completion.",
+ "They came in great numbers to witness the momentous occasion.",
+ "\xB""026The Dragon Pharoah presided over the ceremony.",
+ "\xB""026Queen Kalindra presented the Cube of Power.",
+ "\xB""026Prince Roland presented the Xeen Sceptre.",
+ "\xB""026Together, they placed the Cube of Power...",
+ "\xB""026and the Sceptre, onto the Altar of Joining.",
+ "With the prophecy complete, the two sides of Xeen were united as one",
+};
+
+const char *const WORLD_CONGRATULATIONS =
+ "\x3""cCongratulations\n\n"
+ "Your Final Score is:\n\n"
+ "%010lu\n"
+ "\x3l\n"
+ "Please send this score to the Ancient's Headquarters where "
+ "you'll be added to the Hall of Legends!\n\n"
+ "Ancient's Headquarters\n"
+ "New World Computing, Inc.\n"
+ "P.O. Box 4302\n"
+ "Hollywood, CA 90078";
+const char *const WORLD_CONGRATULATIONS2 =
+ "\n\n\n\n\n\n"
+ "But wait... there's more!\n"
+ "\n\n"
+ "Include the message\n"
+ "\"%s\"\n"
+ "with your final score and receive a special bonus.";
+const char *const CLOUDS_CONGRATULATIONS1 =
+ "\xC""23\x3l"
+ "\xB""000\t000Please send this score to the Ancient's Headquarters "
+ "where you'll be added to the Hall of Legends!\xC""33\x3""c"
+ "\xB""070\t000Press a Key";
+const char *const CLOUDS_CONGRATULATIONS2 =
+ "\xC""23\x3l"
+ "\xB""000\t000Ancient's Headquarters\n"
+ "New World Computing, Inc.\n"
+ "P.O. Box 4302\n"
+ "Hollywood, CA 90078-4302\xC""33\x3""c"
+ "\xB""070\t000Press a Key";
+const char *const GOOBER[3] = {
+ "", "I am a Goober!", "I am a Super Goober!"
+};
+
+const char *const MUSIC_FILES1[5] = {
+ "outdoors.m", "town.m", "cavern.m", "dungeon.m", "castle.m"
+};
+
+const char *const MUSIC_FILES2[6][7] = {
+ { "outday1.m", "outday2.m", "outday4.m", "outnght1.m",
+ "outnght2.m", "outnght4.m", "daydesrt.m" },
+ { "townday1.m", "twnwlk.m", "newbrigh.m", "twnnitea.m",
+ "twnniteb.m", "twnwlk.m", "townday1.m" },
+ { "cavern1.m", "cavern2.m", "cavern3a.m", "cavern1.m",
+ "cavern2.m", "cavern3a.m", "cavern1.m" },
+ { "dngon1.m", "dngon2.m", "dngon3.m", "dngon1.m",
+ "dngon2.m", "dngon3.m", "dngon1.m" },
+ { "cstl1rev.m", "cstl2rev.m", "cstl3rev.m", "cstl1rev.m",
+ "cstl2rev.m", "cstl3rev.m", "cstl1rev.m" },
+ { "sf05.m", "sf05.m", "sf05.m", "sf05.m", "sf05.m", "sf05.m", "sf05.m" }
+};
+
+void writeConstants(CCArchive &cc) {
+ Common::MemFile file;
+ file.syncString(CREDITS);
+ file.syncString(OPTIONS_TITLE);
+ file.syncString(THE_PARTY_NEEDS_REST);
+ file.syncString(WHO_WILL);
+ file.syncString(HOW_MUCH);
+ file.syncString(WHATS_THE_PASSWORD);
+ file.syncString(IN_NO_CONDITION);
+ file.syncString(NOTHING_HERE);
+ file.syncStrings(TERRAIN_TYPES, 6);
+ file.syncStrings(OUTDOORS_WALL_TYPES, 16);
+ file.syncStrings(SURFACE_NAMES, 16);
+ file.syncStrings(WHO_ACTIONS, 32);
+ file.syncStrings(WHO_WILL_ACTIONS, 4);
+ file.syncBytes2D((const byte *)SYMBOLS, 20, 64);
+ file.syncBytes2D((const byte *)TEXT_COLORS, 40, 4);
+ file.syncStrings(DIRECTION_TEXT_UPPER, 4);
+ file.syncStrings(DIRECTION_TEXT, 4);
+ file.syncStrings(RACE_NAMES, 5);
+ file.syncNumbers(RACE_HP_BONUSES, 5);
+ file.syncNumbers2D((const int *)RACE_SP_BONUSES, 5, 2);
+ file.syncStrings(CLASS_NAMES, 11);
+ file.syncNumbers(CLASS_EXP_LEVELS, 10);
+ file.syncStrings(ALIGNMENT_NAMES, 3);
+ file.syncStrings(SEX_NAMES, 2);
+ file.syncStrings(SKILL_NAMES, 18);
+ file.syncStrings(CONDITION_NAMES, 17);
+ file.syncNumbers(CONDITION_COLORS, 17);
+ file.syncString(GOOD);
+ file.syncString(BLESSED);
+ file.syncString(POWER_SHIELD);
+ file.syncString(HOLY_BONUS);
+ file.syncString(HEROISM);
+ file.syncString(IN_PARTY);
+ file.syncString(PARTY_DETAILS);
+ file.syncString(PARTY_DIALOG_TEXT);
+ file.syncNumbers(FACE_CONDITION_FRAMES, 17);
+ file.syncNumbers(CHAR_FACES_X, 6);
+ file.syncNumbers(HP_BARS_X, 6);
+ file.syncString(NO_ONE_TO_ADVENTURE_WITH);
+ file.syncBytes2D((const byte *)DARKNESS_XLAT, 3, 256);
+ file.syncString(YOUR_ROSTER_IS_FULL);
+ file.syncString(PLEASE_WAIT);
+ file.syncString(OOPS);
+ file.syncNumbers2D((const int *)SCREEN_POSITIONING_X, 4, 48);
+ file.syncNumbers2D((const int *)SCREEN_POSITIONING_Y, 4, 48);
+ file.syncNumbers(MONSTER_GRID_BITMASK, 12);
+ file.syncNumbers2D((const int *)INDOOR_OBJECT_X, 2, 12);
+ file.syncNumbers2D((const int *)MAP_OBJECT_Y, 2, 12);
+ file.syncNumbers(INDOOR_MONSTERS_Y, 4);
+ file.syncNumbers2D((const int *)OUTDOOR_OBJECT_X, 2, 12);
+ file.syncNumbers(OUTDOOR_MONSTER_INDEXES, 26);
+ file.syncNumbers(OUTDOOR_MONSTERS_Y, 26);
+ file.syncNumbers2D((const int *)DIRECTION_ANIM_POSITIONS, 4, 4);
+ file.syncBytes2D((const byte *)WALL_SHIFTS, 4, 48);
+ file.syncNumbers(DRAW_NUMBERS, 25);
+ file.syncNumbers2D((const int *)DRAW_FRAMES, 25, 2);
+ file.syncNumbers(COMBAT_FLOAT_X, 8);
+ file.syncNumbers(COMBAT_FLOAT_Y, 8);
+ file.syncNumbers2D((const int *)MONSTER_EFFECT_FLAGS, 15, 8);
+ file.syncNumbers2D((const int *)SPELLS_ALLOWED, 3, 40);
+ file.syncNumbers(BASE_HP_BY_CLASS, 10);
+ file.syncNumbers(AGE_RANGES, 10);
+ file.syncNumbers2D((const int *)AGE_RANGES_ADJUST, 2, 10);
+ file.syncNumbers(STAT_VALUES, 24);
+ file.syncNumbers(STAT_BONUSES, 24);
+ file.syncNumbers(ELEMENTAL_CATEGORIES, 6);
+ file.syncNumbers(ATTRIBUTE_CATEGORIES, 10);
+ file.syncNumbers(ATTRIBUTE_BONUSES, 72);
+ file.syncNumbers(ELEMENTAL_RESISTENCES, 37);
+ file.syncNumbers(ELEMENTAL_DAMAGE, 37);
+ file.syncNumbers(WEAPON_DAMAGE_BASE, 35);
+ file.syncNumbers(WEAPON_DAMAGE_MULTIPLIER, 35);
+ file.syncNumbers(METAL_DAMAGE, 22);
+ file.syncNumbers(METAL_DAMAGE_PERCENT, 22);
+ file.syncNumbers(METAL_LAC, 9);
+ file.syncNumbers(ARMOR_STRENGTHS, 14);
+ file.syncNumbers(MAKE_ITEM_ARR1, 6);
+ file.syncNumbers3D((const int *)MAKE_ITEM_ARR2, 6, 7, 2);
+ file.syncNumbers3D((const int *)MAKE_ITEM_ARR3, 10, 7, 2);
+ file.syncNumbers3D((const int *)MAKE_ITEM_ARR4, 2, 7, 2);
+ file.syncNumbers2D((const int *)MAKE_ITEM_ARR5, 8, 2);
+ file.syncNumbers(OUTDOOR_DRAWSTRUCT_INDEXES, 44);
+ file.syncNumbers2D((const int *)TOWN_MAXES, 2, 11);
+ file.syncStrings2D((const char *const *)TOWN_ACTION_MUSIC, 2, 7);
+ file.syncStrings(TOWN_ACTION_SHAPES, 7);
+ file.syncNumbers2D((const int *)TOWN_ACTION_FILES, 2, 7);
+ file.syncString(BANK_TEXT);
+ file.syncString(BLACKSMITH_TEXT);
+ file.syncString(GUILD_NOT_MEMBER_TEXT);
+ file.syncString(GUILD_TEXT);
+ file.syncString(TAVERN_TEXT);
+ file.syncString(GOOD_STUFF);
+ file.syncString(HAVE_A_DRINK);
+ file.syncString(YOURE_DRUNK);
+ file.syncNumbers4D((const int *)TAVERN_EXIT_LIST, 2, 6, 5, 2);
+ file.syncString(FOOD_AND_DRINK);
+ file.syncString(TEMPLE_TEXT);
+ file.syncString(EXPERIENCE_FOR_LEVEL);
+ file.syncString(LEARNED_ALL);
+ file.syncString(ELIGIBLE_FOR_LEVEL);
+ file.syncString(TRAINING_TEXT);
+ file.syncString(GOLD_GEMS);
+ file.syncString(GOLD_GEMS_2);
+ file.syncString(DEPOSIT_WITHDRAWL[2]);
+ file.syncString(NOT_ENOUGH_X_IN_THE_Y);
+ file.syncString(NO_X_IN_THE_Y);
+ file.syncString(STAT_NAMES[16]);
+ file.syncString(CONSUMABLE_NAMES[4]);
+ file.syncString(WHERE_NAMES[2]);
+ file.syncString(AMOUNT);
+ file.syncString(FOOD_PACKS_FULL);
+ file.syncString(BUY_SPELLS);
+ file.syncString(GUILD_OPTIONS);
+ file.syncNumbers((const int *)MISC_SPELL_INDEX, 74);
+ file.syncNumbers((const int *)SPELL_COSTS, 77);
+ file.syncNumbers2D((const int *)CLOUDS_SPELL_OFFSETS, 5, 20);
+ file.syncNumbers2D((const int *)DARK_SPELL_OFFSETS, 3, 39);
+ file.syncNumbers2D((const int *)DARK_SPELL_RANGES, 12, 2);
+ file.syncNumbers((const int *)SPELL_GEM_COST, 77);
+ file.syncString(NOT_A_SPELL_CASTER);
+ file.syncString(SPELLS_FOR);
+ file.syncString(SPELL_LINES_0_TO_9);
+ file.syncString(SPELLS_DIALOG_SPELLS);
+ file.syncString(SPELL_PTS);
+ file.syncString(GOLD);
+ file.syncString(SPELLS_PRESS_A_KEY);
+ file.syncString(SPELLS_PURCHASE);
+ file.syncString(MAP_TEXT);
+ file.syncString(LIGHT_COUNT_TEXT);
+ file.syncString(FIRE_RESISTENCE_TEXT);
+ file.syncString(ELECRICITY_RESISTENCE_TEXT);
+ file.syncString(COLD_RESISTENCE_TEXT);
+ file.syncString(POISON_RESISTENCE_TEXT);
+ file.syncString(CLAIRVOYANCE_TEXT);
+ file.syncString(LEVITATE_TEXT);
+ file.syncString(WALK_ON_WATER_TEXT);
+ file.syncString(GAME_INFORMATION);
+ file.syncString(WORLD_GAME_TEXT);
+ file.syncString(DARKSIDE_GAME_TEXT);
+ file.syncString(CLOUDS_GAME_TEXT);
+ file.syncString(SWORDS_GAME_TEXT);
+ file.syncStrings(WEEK_DAY_STRINGS, 10);
+ file.syncString(CHARACTER_DETAILS);
+ file.syncString(PARTY_GOLD);
+ file.syncString(PLUS_14);
+ file.syncString(CHARACTER_TEMPLATE);
+ file.syncString(EXCHANGING_IN_COMBAT);
+ file.syncString(CURRENT_MAXIMUM_RATING_TEXT);
+ file.syncString(CURRENT_MAXIMUM_TEXT);
+ file.syncStrings(RATING_TEXT, 24);
+ file.syncString(AGE_TEXT);
+ file.syncString(LEVEL_TEXT);
+ file.syncString(RESISTENCES_TEXT);
+ file.syncString(NONE);
+ file.syncString(EXPERIENCE_TEXT);
+ file.syncString(ELIGIBLE);
+ file.syncString(IN_PARTY_IN_BANK);
+ file.syncString(FOOD_TEXT);
+ file.syncString(EXCHANGE_WITH_WHOM);
+ file.syncString(QUICK_REF_LINE);
+ file.syncString(QUICK_REFERENCE);
+ file.syncNumbers2D((const int *)BLACKSMITH_MAP_IDS, 2, 4);
+ file.syncString(ITEMS_DIALOG_TEXT1);
+ file.syncString(ITEMS_DIALOG_TEXT2);
+ file.syncString(ITEMS_DIALOG_LINE1);
+ file.syncString(ITEMS_DIALOG_LINE2);
+ file.syncString(BTN_BUY);
+ file.syncString(BTN_SELL);
+ file.syncString(BTN_IDENTIFY);
+ file.syncString(BTN_FIX);
+ file.syncString(BTN_USE);
+ file.syncString(BTN_EQUIP);
+ file.syncString(BTN_REMOVE);
+ file.syncString(BTN_DISCARD);
+ file.syncString(BTN_QUEST);
+ file.syncString(BTN_ENCHANT);
+ file.syncString(BTN_RECHARGE);
+ file.syncString(BTN_GOLD);
+ file.syncString(ITEM_BROKEN);
+ file.syncString(ITEM_CURSED);
+ file.syncStrings(BONUS_NAMES, 7);
+ file.syncStrings(WEAPON_NAMES, 35);
+ file.syncStrings(ARMOR_NAMES, 14);
+ file.syncStrings(ACCESSORY_NAMES, 11);
+ file.syncStrings(MISC_NAMES, 22);
+ file.syncStrings(ELEMENTAL_NAMES, 6);
+ file.syncStrings(ATTRIBUTE_NAMES, 10);
+ file.syncStrings(EFFECTIVENESS_NAMES, 7);
+ file.syncStrings(QUEST_ITEM_NAMES, 85);
+ file.syncNumbers((const int *)WEAPON_BASE_COSTS, 35);
+ file.syncNumbers((const int *)ARMOR_BASE_COSTS, 14);
+ file.syncNumbers((const int *)ACCESSORY_BASE_COSTS, 11);
+ file.syncNumbers((const int *)MISC_MATERIAL_COSTS, 22);
+ file.syncNumbers((const int *)MISC_BASE_COSTS, 76);
+ file.syncNumbers((const int *)METAL_BASE_MULTIPLIERS, 22);
+ file.syncNumbers((const int *)ITEM_SKILL_DIVISORS, 4);
+ file.syncNumbers((const int *)RESTRICTION_OFFSETS, 4);
+ file.syncNumbers((const int *)ITEM_RESTRICTIONS, 86);
+ file.syncString(NOT_PROFICIENT);
+ file.syncString(NO_ITEMS_AVAILABLE);
+ file.syncStrings(CATEGORY_NAMES, 4);
+ file.syncString(X_FOR_THE_Y);
+ file.syncString(X_FOR_Y);
+ file.syncString(X_FOR_Y_GOLD);
+ file.syncString(FMT_CHARGES);
+ file.syncString(AVAILABLE_GOLD_COST);
+ file.syncString(CHARGES);
+ file.syncString(COST);
+ file.syncStrings(ITEM_ACTIONS, 7);
+ file.syncString(WHICH_ITEM);
+ file.syncString(WHATS_YOUR_HURRY);
+ file.syncString(USE_ITEM_IN_COMBAT);
+ file.syncString(NO_SPECIAL_ABILITIES);
+ file.syncString(CANT_CAST_WHILE_ENGAGED);
+ file.syncString(EQUIPPED_ALL_YOU_CAN);
+ file.syncString(REMOVE_X_TO_EQUIP_Y);
+ file.syncString(RING);
+ file.syncString(MEDAL);
+ file.syncString(CANNOT_REMOVE_CURSED_ITEM);
+ file.syncString(CANNOT_DISCARD_CURSED_ITEM);
+ file.syncString(PERMANENTLY_DISCARD);
+ file.syncString(BACKPACK_IS_FULL);
+ file.syncStrings(CATEGORY_BACKPACK_IS_FULL, 4);
+ file.syncString(BUY_X_FOR_Y_GOLD);
+ file.syncString(SELL_X_FOR_Y_GOLD);
+ file.syncString(NO_NEED_OF_THIS);
+ file.syncString(NOT_RECHARGABLE);
+ file.syncString(SPELL_FAILED);
+ file.syncString(NOT_ENCHANTABLE);
+ file.syncString(ITEM_NOT_BROKEN);
+ file.syncStrings(FIX_IDENTIFY, 2);
+ file.syncString(FIX_IDENTIFY_GOLD);
+ file.syncString(IDENTIFY_ITEM_MSG);
+ file.syncString(ITEM_DETAILS);
+ file.syncString(ALL);
+ file.syncString(FIELD_NONE);
+ file.syncString(DAMAGE_X_TO_Y);
+ file.syncString(ELEMENTAL_XY_DAMAGE);
+ file.syncString(ATTR_XY_BONUS);
+ file.syncString(EFFECTIVE_AGAINST);
+ file.syncString(QUESTS_DIALOG_TEXT);
+ file.syncString(CLOUDS_OF_XEEN_LINE);
+ file.syncString(DARKSIDE_OF_XEEN_LINE);
+ file.syncString(NO_QUEST_ITEMS);
+ file.syncString(NO_CURRENT_QUESTS);
+ file.syncString(NO_AUTO_NOTES);
+ file.syncString(QUEST_ITEMS_DATA);
+ file.syncString(CURRENT_QUESTS_DATA);
+ file.syncString(AUTO_NOTES_DATA);
+ file.syncString(REST_COMPLETE);
+ file.syncString(PARTY_IS_STARVING);
+ file.syncString(HIT_SPELL_POINTS_RESTORED);
+ file.syncString(TOO_DANGEROUS_TO_REST);
+ file.syncString(SOME_CHARS_MAY_DIE);
+ file.syncString(CANT_DISMISS_LAST_CHAR);
+ file.syncStrings(REMOVE_DELETE, 2);
+ file.syncString(REMOVE_OR_DELETE_WHICH);
+ file.syncString(YOUR_PARTY_IS_FULL);
+ file.syncString(HAS_SLAYER_SWORD);
+ file.syncString(SURE_TO_DELETE_CHAR);
+ file.syncString(CREATE_CHAR_DETAILS);
+ file.syncString(NEW_CHAR_STATS);
+ file.syncString(NAME_FOR_NEW_CHARACTER);
+ file.syncString(SELECT_CLASS_BEFORE_SAVING);
+ file.syncString(EXCHANGE_ATTR_WITH);
+ file.syncNumbers((const int *)NEW_CHAR_SKILLS, 10);
+ file.syncNumbers((const int *)NEW_CHAR_SKILLS_LEN, 10);
+ file.syncNumbers((const int *)NEW_CHAR_RACE_SKILLS, 10);
+ file.syncNumbers((const int *)RACE_MAGIC_RESISTENCES, 5);
+ file.syncNumbers((const int *)RACE_FIRE_RESISTENCES, 5);
+ file.syncNumbers((const int *)RACE_ELECTRIC_RESISTENCES, 5);
+ file.syncNumbers((const int *)RACE_COLD_RESISTENCES, 5);
+ file.syncNumbers((const int *)RACE_ENERGY_RESISTENCES, 5);
+ file.syncNumbers((const int *)RACE_POISON_RESISTENCES, 5);
+ file.syncNumbers2D((const int *)NEW_CHARACTER_SPELLS, 10, 4);
+ file.syncString(COMBAT_DETAILS);
+ file.syncString(NOT_ENOUGH_TO_CAST);
+ file.syncStrings(SPELL_CAST_COMPONENTS, 2);
+ file.syncString(CAST_SPELL_DETAILS);
+ file.syncString(PARTY_FOUND);
+ file.syncString(BACKPACKS_FULL_PRESS_KEY);
+ file.syncString(HIT_A_KEY);
+ file.syncString(GIVE_TREASURE_FORMATTING);
+ file.syncString(X_FOUND_Y);
+ file.syncString(ON_WHO);
+ file.syncString(WHICH_ELEMENT1);
+ file.syncString(WHICH_ELEMENT2);
+ file.syncString(DETECT_MONSTERS);
+ file.syncString(LLOYDS_BEACON);
+ file.syncString(HOW_MANY_SQUARES);
+ file.syncString(TOWN_PORTAL);
+ file.syncNumbers2D((const int *)TOWN_MAP_NUMBERS, 2, 5);
+ file.syncString(MONSTER_DETAILS);
+ file.syncStrings(MONSTER_SPECIAL_ATTACKS, 23);
+ file.syncString(IDENTIFY_MONSTERS);
+ file.syncStrings(EVENT_SAMPLES, 6);
+ file.syncString(MOONS_NOT_ALIGNED);
+ file.syncString(AWARDS_FOR);
+ file.syncString(AWARDS_TEXT);
+ file.syncString(NO_AWARDS);
+ file.syncString(WARZONE_BATTLE_MASTER);
+ file.syncString(WARZONE_MAXED);
+ file.syncString(WARZONE_LEVEL);
+ file.syncString(WARZONE_HOW_MANY);
+ file.syncString(PICKS_THE_LOCK);
+ file.syncString(UNABLE_TO_PICK_LOCK);
+ file.syncString(CONTROL_PANEL_TEXT);
+ file.syncString(CONTROL_PANEL_BUTTONS);
+ file.syncString(ON);
+ file.syncString(OFF);
+ file.syncString(CONFIRM_QUIT);
+ file.syncString(MR_WIZARD);
+ file.syncString(NO_LOADING_IN_COMBAT);
+ file.syncString(NO_SAVING_IN_COMBAT);
+ file.syncString(QUICK_FIGHT_TEXT);
+ file.syncStrings(QUICK_FIGHT_OPTIONS, 4);
+ file.syncStrings(WORLD_END_TEXT, 9);
+ file.syncString(WORLD_CONGRATULATIONS);
+ file.syncString(WORLD_CONGRATULATIONS2);
+ file.syncString(CLOUDS_CONGRATULATIONS1);
+ file.syncString(CLOUDS_CONGRATULATIONS2);
+ file.syncStrings(GOOBER, 3);
+ file.syncStrings(MUSIC_FILES1, 5);
+ file.syncStrings2D((const char *const *)MUSIC_FILES2, 6, 7);
+
+ cc.add("CONSTANTS", file);
+}
diff --git a/devtools/create_xeen/constants.h b/devtools/create_xeen/constants.h
new file mode 100644
index 0000000000..32177e80ac
--- /dev/null
+++ b/devtools/create_xeen/constants.h
@@ -0,0 +1,31 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include "common/scummsys.h"
+#include "cc.h"
+
+extern void writeConstants(CCArchive &cc);
+
+#endif
diff --git a/devtools/create_xeen/create_xeen.cpp b/devtools/create_xeen/create_xeen.cpp
new file mode 100644
index 0000000000..94af74f517
--- /dev/null
+++ b/devtools/create_xeen/create_xeen.cpp
@@ -0,0 +1,65 @@
+/* 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 "cc.h"
+#include "file.h"
+#include "constants.h"
+
+#define VERSION_NUMBER 1
+
+Common::File outputFile;
+
+void NORETURN_PRE error(const char *s, ...) {
+ printf("%s\n", s);
+ exit(1);
+}
+
+void writeVersion(CCArchive &cc) {
+ Common::MemFile f;
+ f.writeLong(VERSION_NUMBER);
+ cc.add("VERSION", f);
+}
+
+int main(int argc, char *argv[]) {
+ if (!outputFile.open(argc == 1 ? "xeen.ccs" : argv[1], Common::kFileWriteMode)) {
+ error("Could not open input file");
+ }
+
+ CCArchive cc(outputFile);
+ writeVersion(cc);
+ writeConstants(cc);
+
+ cc.close();
+ return 0;
+}
diff --git a/devtools/create_xeen/file.h b/devtools/create_xeen/file.h
new file mode 100644
index 0000000000..8fdb9b44a9
--- /dev/null
+++ b/devtools/create_xeen/file.h
@@ -0,0 +1,264 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __FILE_H__
+#define __FILE_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/util.h"
+
+namespace Common {
+
+enum AccessMode {
+ kFileReadMode = 1,
+ kFileWriteMode = 2
+};
+
+class Stream {
+public:
+ Stream() {}
+ virtual ~Stream() {}
+
+ virtual int seek(int offset, int whence = SEEK_SET) = 0;
+ virtual long read(void *buffer, size_t len) = 0;
+ virtual void write(const void *buffer, size_t len) = 0;
+ virtual uint pos() const = 0;
+ virtual uint size() const = 0;
+ virtual bool eof() const = 0;
+
+ void skip(int offset) {
+ seek(offset, SEEK_CUR);
+ }
+ void write(Stream &src, size_t len) {
+ for (size_t idx = 0; idx < len; ++idx)
+ writeByte(src.readByte());
+ }
+ byte readByte() {
+ byte v;
+ read(&v, sizeof(byte));
+ return v;
+ }
+ uint16 readWord() {
+ uint16 v;
+ read(&v, sizeof(uint16));
+ return FROM_LE_16(v);
+ }
+ uint readLong() {
+ uint v;
+ read(&v, sizeof(uint));
+ return FROM_LE_32(v);
+ }
+
+ uint readUint16BE() {
+ uint16 v;
+ read(&v, sizeof(uint16));
+ return FROM_BE_16(v);
+ }
+ uint readUint16LE() {
+ uint16 v;
+ read(&v, sizeof(uint16));
+ return FROM_LE_16(v);
+ }
+ uint readUint32BE() {
+ uint32 v;
+ read(&v, sizeof(uint32));
+ return FROM_BE_32(v);
+ }
+ uint readUint32LE() {
+ uint32 v;
+ read(&v, sizeof(uint32));
+ return FROM_LE_32(v);
+ }
+
+ void writeByte(byte v) {
+ write(&v, sizeof(byte));
+ }
+ void writeByte(byte v, int len) {
+ byte *b = new byte[len];
+ memset(b, v, len);
+ write(b, len);
+ delete[] b;
+ }
+ void writeWord(uint16 v) {
+ uint16 vTemp = TO_LE_16(v);
+ write(&vTemp, sizeof(uint16));
+ }
+ void writeLong(uint v) {
+ uint vTemp = TO_LE_32(v);
+ write(&vTemp, sizeof(uint));
+ }
+ void writeString(const char *msg) {
+ if (!msg) {
+ writeByte(0);
+ }
+ else {
+ do {
+ writeByte(*msg);
+ } while (*msg++);
+ }
+ }
+};
+
+class File : public Stream {
+private:
+ ::FILE *_f;
+public:
+ File() : _f(nullptr) {}
+ virtual ~File() { close(); }
+
+ bool open(const char *filename, AccessMode mode = kFileReadMode) {
+ _f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb+");
+ return (_f != NULL);
+ }
+ void close() {
+ if (_f)
+ fclose(_f);
+ _f = nullptr;
+ }
+
+ virtual int seek(int offset, int whence = SEEK_SET) {
+ return fseek(_f, offset, whence);
+ }
+ virtual long read(void *buffer, size_t len) {
+ return fread(buffer, 1, len, _f);
+ }
+ virtual void write(const void *buffer, size_t len) {
+ assert(_f);
+ fwrite(buffer, 1, len, _f);
+ }
+ virtual uint pos() const {
+ return ftell(_f);
+ }
+ virtual uint size() const {
+ uint currentPos = pos();
+ fseek(_f, 0, SEEK_END);
+ uint result = pos();
+ fseek(_f, currentPos, SEEK_SET);
+ return result;
+ }
+ virtual bool eof() const {
+ return feof(_f) != 0;
+ }
+};
+
+#define MAX_MEM_SIZE 65536
+
+class MemFile : public Stream {
+private:
+ byte _data[MAX_MEM_SIZE];
+ size_t _size, _offset;
+public:
+ MemFile() : _size(0), _offset(0) {
+ memset(_data, 0, MAX_MEM_SIZE);
+ }
+ virtual ~MemFile() {}
+
+ bool open() {
+ memset(_data, 0, MAX_MEM_SIZE);
+ _size = _offset = 0;
+ }
+ void close() {
+ }
+
+ virtual int seek(int offset, int whence = SEEK_SET) {
+ switch (whence) {
+ case SEEK_SET: _offset = whence; break;
+ case SEEK_CUR: _offset += whence; break;
+ case SEEK_END: _offset = _size + whence; break;
+ }
+
+ return _offset;
+ }
+ virtual long read(void *buffer, size_t len) {
+ len = MAX(len, _size - _offset);
+ memcpy(buffer, &_data[_offset], len);
+ return len;
+ }
+ virtual void write(const void *buffer, size_t len) {
+ assert(len <= (MAX_MEM_SIZE - _offset));
+ memcpy(&_data[_offset], buffer, len);
+ _offset += len;
+ _size = MAX(_offset, _size);
+ }
+ virtual uint pos() const {
+ return _offset;
+ }
+ virtual uint size() const {
+ return _size;
+ }
+ virtual bool eof() const {
+ return _offset >= _size;
+ }
+
+ const byte *getData() const { return _data; }
+
+ void syncString(const char *str) {
+ write(str, strlen(str) + 1);
+ }
+ void syncStrings(const char *const *str, int count) {
+ writeLong(MKTAG(count, 0, 0, 0));
+ for (int idx = 0; idx < count; ++idx, ++str)
+ writeString(*str);
+ }
+ void syncStrings2D(const char *const *str, int count1, int count2) {
+ writeLong(MKTAG(count1, count2, 0, 0));
+ for (int idx = 0; idx < count1 * count2; ++idx, ++str)
+ writeString(*str);
+ }
+ void syncNumber(const int val) {
+ writeLong(val);
+ }
+ void syncNumbers(const int *vals, int count) {
+ writeLong(MKTAG(count, 0, 0, 0));
+ for (int idx = 0; idx < count; ++idx, ++vals)
+ writeLong(*vals);
+ }
+ void syncNumbers2D(const int *vals, int count1, int count2) {
+ writeLong(MKTAG(count1, count2, 0, 0));
+ for (int idx = 0; idx < count1 * count2; ++idx, ++vals)
+ writeLong(*vals);
+ }
+ void syncNumbers3D(const int *vals, int count1, int count2, int count3) {
+ writeLong(MKTAG(count1, count2, count3, 0));
+ for (int idx = 0; idx < count1 * count2 * count3; ++idx, ++vals)
+ writeLong(*vals);
+ }
+ void syncNumbers4D(const int *vals, int count1, int count2, int count3, int count4) {
+ writeLong(MKTAG(count1, count2, count3, count4));
+ for (int idx = 0; idx < count1 * count2 * count3 * count4; ++idx, ++vals)
+ writeLong(*vals);
+ }
+ void syncBytes2D(const byte *vals, int count1, int count2) {
+ writeLong(MKTAG(count1, count2, 0, 0));
+ write(vals, count1 * count2);
+ }
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/devtools/create_xeen/hash-str.h b/devtools/create_xeen/hash-str.h
new file mode 100644
index 0000000000..b9f6d503f8
--- /dev/null
+++ b/devtools/create_xeen/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_xeen/hashmap.cpp b/devtools/create_xeen/hashmap.cpp
new file mode 100644
index 0000000000..99840993ce
--- /dev/null
+++ b/devtools/create_xeen/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_xeen/hashmap.h b/devtools/create_xeen/hashmap.h
new file mode 100644
index 0000000000..c8691aeb42
--- /dev/null
+++ b/devtools/create_xeen/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 "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_xeen/memorypool.cpp b/devtools/create_xeen/memorypool.cpp
new file mode 100644
index 0000000000..13c640b6ad
--- /dev/null
+++ b/devtools/create_xeen/memorypool.cpp
@@ -0,0 +1,182 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "memorypool.h"
+#include "common/util.h"
+
+namespace Common {
+
+enum {
+ INITIAL_CHUNKS_PER_PAGE = 8
+};
+
+static size_t adjustChunkSize(size_t chunkSize) {
+ // You must at least fit the pointer in the node (technically unneeded considering the next rounding statement)
+ chunkSize = MAX(chunkSize, sizeof(void *));
+ // There might be an alignment problem on some platforms when trying to load a void* on a non natural boundary
+ // so we round to the next sizeof(void *)
+ chunkSize = (chunkSize + sizeof(void *) - 1) & (~(sizeof(void *) - 1));
+
+ return chunkSize;
+}
+
+
+MemoryPool::MemoryPool(size_t chunkSize)
+ : _chunkSize(adjustChunkSize(chunkSize)) {
+
+ _next = NULL;
+
+ _chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
+}
+
+MemoryPool::~MemoryPool() {
+#if 0
+ freeUnusedPages();
+ if (!_pages.empty())
+ warning("Memory leak found in pool");
+#endif
+
+ for (size_t i = 0; i < _pages.size(); ++i)
+ ::free(_pages[i].start);
+}
+
+void MemoryPool::allocPage() {
+ Page page;
+
+ // Allocate a new page
+ page.numChunks = _chunksPerPage;
+ assert(page.numChunks * _chunkSize < 16*1024*1024); // Refuse to allocate pages bigger than 16 MB
+
+ page.start = ::malloc(page.numChunks * _chunkSize);
+ assert(page.start);
+ _pages.push_back(page);
+
+
+ // Next time, we'll allocate a page twice as big as this one.
+ _chunksPerPage *= 2;
+
+ // Add the page to the pool of free chunk
+ addPageToPool(page);
+}
+
+void MemoryPool::addPageToPool(const Page &page) {
+ // Add all chunks of the new page to the linked list (pool) of free chunks
+ void *current = page.start;
+ for (size_t i = 1; i < page.numChunks; ++i) {
+ void *next = (byte *)current + _chunkSize;
+ *(void **)current = next;
+
+ current = next;
+ }
+
+ // Last chunk points to the old _next
+ *(void **)current = _next;
+
+ // From now on, the first free chunk is the first chunk of the new page
+ _next = page.start;
+}
+
+void *MemoryPool::allocChunk() {
+ // No free chunks left? Allocate a new page
+ if (!_next)
+ allocPage();
+
+ assert(_next);
+ void *result = _next;
+ _next = *(void **)result;
+ return result;
+}
+
+void MemoryPool::freeChunk(void *ptr) {
+ // Add the chunk back to (the start of) the list of free chunks
+ *(void **)ptr = _next;
+ _next = ptr;
+}
+
+// Technically not compliant C++ to compare unrelated pointers. In practice...
+bool MemoryPool::isPointerInPage(void *ptr, const Page &page) {
+ return (ptr >= page.start) && (ptr < (char *)page.start + page.numChunks * _chunkSize);
+}
+
+void MemoryPool::freeUnusedPages() {
+ //std::sort(_pages.begin(), _pages.end());
+ Array<size_t> numberOfFreeChunksPerPage;
+ numberOfFreeChunksPerPage.resize(_pages.size());
+ for (size_t i = 0; i < numberOfFreeChunksPerPage.size(); ++i) {
+ numberOfFreeChunksPerPage[i] = 0;
+ }
+
+ // Compute for each page how many chunks in it are still in use.
+ void *iterator = _next;
+ while (iterator) {
+ // TODO: This should be a binary search (requiring us to keep _pages sorted)
+ for (size_t i = 0; i < _pages.size(); ++i) {
+ if (isPointerInPage(iterator, _pages[i])) {
+ ++numberOfFreeChunksPerPage[i];
+ break;
+ }
+ }
+
+ iterator = *(void **)iterator;
+ }
+
+ // Free all pages which are not in use.
+ size_t freedPagesCount = 0;
+ for (size_t i = 0; i < _pages.size(); ++i) {
+ if (numberOfFreeChunksPerPage[i] == _pages[i].numChunks) {
+ // Remove all chunks of this page from the list of free chunks
+ void **iter2 = &_next;
+ while (*iter2) {
+ if (isPointerInPage(*iter2, _pages[i]))
+ *iter2 = **(void ***)iter2;
+ else
+ iter2 = *(void ***)iter2;
+ }
+
+ ::free(_pages[i].start);
+ ++freedPagesCount;
+ _pages[i].start = NULL;
+ }
+ }
+
+// debug("freed %d pages out of %d", (int)freedPagesCount, (int)_pages.size());
+
+ // Remove all now unused pages
+ size_t newSize = 0;
+ for (size_t i = 0; i < _pages.size(); ++i) {
+ if (_pages[i].start != NULL) {
+ if (newSize != i)
+ _pages[newSize] = _pages[i];
+ ++newSize;
+ }
+ }
+ _pages.resize(newSize);
+
+ // Reset _chunksPerPage
+ _chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
+ for (size_t i = 0; i < _pages.size(); ++i) {
+ if (_chunksPerPage < _pages[i].numChunks)
+ _chunksPerPage = _pages[i].numChunks;
+ }
+}
+
+} // End of namespace Common
diff --git a/devtools/create_xeen/memorypool.h b/devtools/create_xeen/memorypool.h
new file mode 100644
index 0000000000..c8a8fc7a53
--- /dev/null
+++ b/devtools/create_xeen/memorypool.h
@@ -0,0 +1,162 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef COMMON_MEMORYPOOL_H
+#define COMMON_MEMORYPOOL_H
+
+#include "common/array.h"
+
+
+namespace Common {
+
+/**
+ * This class provides a pool of memory 'chunks' of identical size.
+ * The size of a chunk is determined when creating the memory pool.
+ *
+ * Using a memory pool may yield better performance and memory usage
+ * when allocating and deallocating many memory blocks of equal size.
+ * E.g. the Common::String class uses a memory pool for the refCount
+ * variables (each the size of an int) it allocates for each string
+ * instance.
+ */
+class MemoryPool {
+protected:
+ MemoryPool(const MemoryPool&);
+ MemoryPool& operator=(const MemoryPool&);
+
+ struct Page {
+ void *start;
+ size_t numChunks;
+ };
+
+ const size_t _chunkSize;
+ Array<Page> _pages;
+ void *_next;
+ size_t _chunksPerPage;
+
+ void allocPage();
+ void addPageToPool(const Page &page);
+ bool isPointerInPage(void *ptr, const Page &page);
+
+public:
+ /**
+ * Constructor for a memory pool with the given chunk size.
+ * @param chunkSize the chunk size of this memory pool
+ */
+ explicit MemoryPool(size_t chunkSize);
+ ~MemoryPool();
+
+ /**
+ * Allocate a new chunk from the memory pool.
+ */
+ void *allocChunk();
+ /**
+ * Return a chunk to the memory pool. The given pointer must have
+ * been obtained from calling the allocChunk() method of the very
+ * same MemoryPool instance. Passing any other pointer (e.g. to
+ * a chunk from another MemoryPool, or a malloc'ed memory block)
+ * will lead to undefined behavior and may result in a crash (if
+ * you are lucky) or in silent data corruption.
+ */
+ void freeChunk(void *ptr);
+
+ /**
+ * Perform garbage collection. The memory pool stores all the
+ * chunks it manages in memory 'pages' obtained via the classic
+ * memory allocation APIs (i.e. malloc/free). Ordinarily, once
+ * a page has been allocated, it won't be released again during
+ * the life time of the memory pool. The exception is when this
+ * method is called.
+ */
+ void freeUnusedPages();
+
+ /**
+ * Return the chunk size used by this memory pool.
+ */
+ size_t getChunkSize() const { return _chunkSize; }
+};
+
+/**
+ * This is a memory pool which already contains in itself some storage
+ * space for a fixed number of chunks. Thus if the memory pool is only
+ * lightly used, no malloc() calls have to be made at all.
+ */
+template<size_t CHUNK_SIZE, size_t NUM_INTERNAL_CHUNKS = 32>
+class FixedSizeMemoryPool : public MemoryPool {
+private:
+ enum {
+ REAL_CHUNK_SIZE = (CHUNK_SIZE + sizeof(void *) - 1) & (~(sizeof(void *) - 1))
+ };
+
+ byte _storage[NUM_INTERNAL_CHUNKS * REAL_CHUNK_SIZE];
+public:
+ FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {
+ assert(REAL_CHUNK_SIZE == _chunkSize);
+ // Insert some static storage
+ Page internalPage = { _storage, NUM_INTERNAL_CHUNKS };
+ addPageToPool(internalPage);
+ }
+};
+
+// Ensure NUM_INTERNAL_CHUNKS == 0 results in a compile error
+template<size_t CHUNK_SIZE>
+class FixedSizeMemoryPool<CHUNK_SIZE,0> : public MemoryPool {
+public:
+ FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {}
+};
+
+/**
+ * A memory pool for C++ objects.
+ */
+template<class T, size_t NUM_INTERNAL_CHUNKS = 32>
+class ObjectPool : public FixedSizeMemoryPool<sizeof(T), NUM_INTERNAL_CHUNKS> {
+public:
+ /**
+ * Return the memory chunk used as storage for the given object back
+ * to the pool, after calling its destructor.
+ */
+ void deleteChunk(T *ptr) {
+ ptr->~T();
+ this->freeChunk(ptr);
+ }
+};
+
+} // End of namespace Common
+
+/**
+ * A custom placement new operator, using an arbitrary MemoryPool.
+ *
+ * This *should* work with all C++ implementations, but may not.
+ *
+ * For details on using placement new for custom allocators, see e.g.
+ * <http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14>
+ */
+inline void *operator new(size_t nbytes, Common::MemoryPool &pool) {
+ assert(nbytes <= pool.getChunkSize());
+ return pool.allocChunk();
+}
+
+inline void operator delete(void *p, Common::MemoryPool &pool) {
+ pool.freeChunk(p);
+}
+
+#endif
diff --git a/devtools/create_xeen/module.mk b/devtools/create_xeen/module.mk
new file mode 100644
index 0000000000..d382fc6bd6
--- /dev/null
+++ b/devtools/create_xeen/module.mk
@@ -0,0 +1,15 @@
+MODULE := devtools/create_xeen
+
+MODULE_OBJS := \
+ create_xeen.o \
+ cc.o \
+ constants.o \
+ hashmap.o \
+ memorypool.o \
+ str.o
+
+# Set the name of the executable
+TOOL_EXECUTABLE := create_xeen
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/devtools/create_xeen/str.cpp b/devtools/create_xeen/str.cpp
new file mode 100644
index 0000000000..6aa66d0d20
--- /dev/null
+++ b/devtools/create_xeen/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 "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_xeen/str.h b/devtools/create_xeen/str.h
new file mode 100644
index 0000000000..2f954dcfca
--- /dev/null
+++ b/devtools/create_xeen/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