DEVTOOLS: Creation of titanic.dat for holding static data
This commit is contained in:
parent
76b61324de
commit
2680caa5bd
12 changed files with 2970 additions and 0 deletions
205
devtools/create_titanic/create_titanic_dat.cpp
Normal file
205
devtools/create_titanic/create_titanic_dat.cpp
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Disable symbol overrides so that we can use system headers.
|
||||||
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
|
// HACK to allow building with the SDL backend on MinGW
|
||||||
|
// see bug #1800764 "TOOLS: MinGW tools building broken"
|
||||||
|
#ifdef main
|
||||||
|
#undef main
|
||||||
|
#endif // main
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "common/language.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "winexe_pe.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format of the access.dat file that will be created:
|
||||||
|
* 4 Bytes - Magic string 'SVTN' to identify valid data file
|
||||||
|
* 2 bytes - Version number
|
||||||
|
*
|
||||||
|
* Following is a series of index entries with the following fields:
|
||||||
|
* 4 bytes - offset in file of entry
|
||||||
|
* 4 bytes - size of entry in the file
|
||||||
|
* ASCIIZ - name of the resource
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define VERSION_NUMBER 1
|
||||||
|
|
||||||
|
Common::File inputFile, outputFile;
|
||||||
|
Common::PEResources res;
|
||||||
|
uint headerOffset = 6;
|
||||||
|
uint dataOffset = 0x200;
|
||||||
|
#define SEGMENT_OFFSET 0x401C00
|
||||||
|
|
||||||
|
void NORETURN_PRE error(const char *s, ...) {
|
||||||
|
printf("%s\n", s);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeEntryHeader(const char *name, uint offset, uint size) {
|
||||||
|
assert(headerOffset < 0x200);
|
||||||
|
outputFile.seek(headerOffset);
|
||||||
|
outputFile.writeLong(offset);
|
||||||
|
outputFile.writeLong(size);
|
||||||
|
outputFile.writeString(name);
|
||||||
|
|
||||||
|
headerOffset += 8 + strlen(name) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFinalEntryHeader() {
|
||||||
|
assert(headerOffset <= 0x1F8);
|
||||||
|
outputFile.seek(headerOffset);
|
||||||
|
outputFile.writeLong(0);
|
||||||
|
outputFile.writeLong(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeStringArray(const char *name, uint offset, int count) {
|
||||||
|
outputFile.seek(dataOffset);
|
||||||
|
|
||||||
|
inputFile.seek(offset);
|
||||||
|
uint *offsets = new uint[count];
|
||||||
|
for (int idx = 0; idx < count; ++idx)
|
||||||
|
offsets[idx] = inputFile.readLong();
|
||||||
|
|
||||||
|
// Iterate through reading each string
|
||||||
|
for (int idx = 0; idx < count; ++idx) {
|
||||||
|
if (offsets[idx]) {
|
||||||
|
inputFile.seek(offsets[idx] - SEGMENT_OFFSET);
|
||||||
|
outputFile.writeString(inputFile);
|
||||||
|
} else {
|
||||||
|
outputFile.writeString("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint size = outputFile.size() - dataOffset;
|
||||||
|
writeEntryHeader(name, dataOffset, size);
|
||||||
|
dataOffset += size;
|
||||||
|
|
||||||
|
delete[] offsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::WinResourceID getResId(uint id) {
|
||||||
|
return Common::WinResourceID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::WinResourceID getResId(const char *id) {
|
||||||
|
if (!strcmp(id, "Bitmap"))
|
||||||
|
return Common::WinResourceID(2);
|
||||||
|
|
||||||
|
return Common::WinResourceID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeResource(const char *name, Common::File *file) {
|
||||||
|
outputFile.seek(dataOffset);
|
||||||
|
outputFile.write(*file, file->size());
|
||||||
|
|
||||||
|
writeEntryHeader(name, dataOffset, file->size());
|
||||||
|
dataOffset += file->size();
|
||||||
|
delete file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeResource(const char *sectionStr, const uint32 resId) {
|
||||||
|
char nameBuffer[256];
|
||||||
|
sprintf(nameBuffer, "%s/%d", sectionStr, resId);
|
||||||
|
|
||||||
|
Common::File *file = res.getResource(getResId(sectionStr), resId);
|
||||||
|
assert(file);
|
||||||
|
writeResource(nameBuffer, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeResource(const char *sectionStr, const char *resId) {
|
||||||
|
char nameBuffer[256];
|
||||||
|
sprintf(nameBuffer, "%s/%s", sectionStr, resId);
|
||||||
|
|
||||||
|
Common::File *file = res.getResource(getResId(sectionStr),
|
||||||
|
Common::WinResourceID(resId));
|
||||||
|
assert(file);
|
||||||
|
writeResource(nameBuffer, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeHeader() {
|
||||||
|
// Write out magic string
|
||||||
|
const char *MAGIC_STR = "SVTN";
|
||||||
|
outputFile.write(MAGIC_STR, 4);
|
||||||
|
|
||||||
|
// Write out version number
|
||||||
|
outputFile.writeWord(VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeData() {
|
||||||
|
writeStringArray("TEXT/STRINGS1", 0x21B7C8, 376);
|
||||||
|
writeStringArray("TEXT/STRINGS2", 0x21BDB0, 218);
|
||||||
|
writeStringArray("TEXT/STRINGS3", 0x21C120, 1576);
|
||||||
|
writeStringArray("TEXT/STRINGS4", 0x21D9C8, 82);
|
||||||
|
|
||||||
|
writeResource("Bitmap", "BACKDROP");
|
||||||
|
writeResource("Bitmap", "EVILTWIN");
|
||||||
|
writeResource("Bitmap", "RESTORED");
|
||||||
|
writeResource("Bitmap", "RESTOREF");
|
||||||
|
writeResource("Bitmap", "RESTOREU");
|
||||||
|
writeResource("Bitmap", "STARTD");
|
||||||
|
writeResource("Bitmap", "STARTF");
|
||||||
|
writeResource("Bitmap", "STARTU");
|
||||||
|
writeResource("Bitmap", "TITANIC");
|
||||||
|
writeResource("Bitmap", 133);
|
||||||
|
writeResource("Bitmap", 164);
|
||||||
|
writeResource("Bitmap", 165);
|
||||||
|
|
||||||
|
writeResource("STFONT", 149);
|
||||||
|
writeResource("STFONT", 151);
|
||||||
|
writeResource("STFONT", 152);
|
||||||
|
writeResource("STFONT", 153);
|
||||||
|
|
||||||
|
writeResource("TEXT", "STVOCAB.TXT");
|
||||||
|
writeResource("TEXT", "JRQUOTES.TXT");
|
||||||
|
writeResource("TEXT", 155);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("Format: %s ST.exe titanic.dat\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inputFile.open(argv[1])) {
|
||||||
|
error("Could not open input file");
|
||||||
|
}
|
||||||
|
res.loadFromEXE(argv[1]);
|
||||||
|
|
||||||
|
if (!outputFile.open(argv[2], Common::kFileWriteMode)) {
|
||||||
|
error("Could not open output file");
|
||||||
|
}
|
||||||
|
|
||||||
|
writeHeader();
|
||||||
|
writeData();
|
||||||
|
writeFinalEntryHeader();
|
||||||
|
|
||||||
|
inputFile.close();
|
||||||
|
outputFile.close();
|
||||||
|
return 0;
|
||||||
|
}
|
213
devtools/create_titanic/file.h
Normal file
213
devtools/create_titanic/file.h
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __FILE_H__
|
||||||
|
#define __FILE_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/algorithm.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
enum AccessMode {
|
||||||
|
kFileReadMode = 1,
|
||||||
|
kFileWriteMode = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
class File {
|
||||||
|
private:
|
||||||
|
::FILE *_f;
|
||||||
|
const byte *_memPtr;
|
||||||
|
size_t _offset, _size;
|
||||||
|
public:
|
||||||
|
File() : _f(nullptr), _memPtr(nullptr), _offset(0), _size(0) {}
|
||||||
|
|
||||||
|
bool open(const char *filename, AccessMode mode = kFileReadMode) {
|
||||||
|
_memPtr = nullptr;
|
||||||
|
_f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb+");
|
||||||
|
return (_f != NULL);
|
||||||
|
}
|
||||||
|
bool open(const byte *data, uint size) {
|
||||||
|
close();
|
||||||
|
_f = nullptr;
|
||||||
|
_memPtr = data;
|
||||||
|
_size = size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
if (_f)
|
||||||
|
fclose(_f);
|
||||||
|
_f = nullptr;
|
||||||
|
delete[] _memPtr;
|
||||||
|
_memPtr = nullptr;
|
||||||
|
}
|
||||||
|
int seek(int offset, int whence = SEEK_SET) {
|
||||||
|
if (_f)
|
||||||
|
return fseek(_f, offset, whence);
|
||||||
|
|
||||||
|
switch (whence) {
|
||||||
|
case SEEK_SET:
|
||||||
|
_offset = offset;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
_offset += offset;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
_offset = _size + offset;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _offset;
|
||||||
|
}
|
||||||
|
void skip(int offset) {
|
||||||
|
if (_f)
|
||||||
|
fseek(_f, offset, SEEK_CUR);
|
||||||
|
else
|
||||||
|
_offset += offset;
|
||||||
|
}
|
||||||
|
long read(void *buffer, size_t len) {
|
||||||
|
if (_f)
|
||||||
|
return fread(buffer, 1, len, _f);
|
||||||
|
|
||||||
|
uint bytesToRead = CLIP(len, (size_t)0, _size - _offset);
|
||||||
|
memcpy(buffer, &_memPtr[_offset], bytesToRead);
|
||||||
|
_offset += bytesToRead;
|
||||||
|
return bytesToRead;
|
||||||
|
}
|
||||||
|
void write(const void *buffer, size_t len) {
|
||||||
|
assert(_f);
|
||||||
|
fwrite(buffer, 1, len, _f);
|
||||||
|
}
|
||||||
|
void write(File &src, size_t len) {
|
||||||
|
for (size_t idx = 0; idx < len; ++idx)
|
||||||
|
writeByte(src.readByte());
|
||||||
|
}
|
||||||
|
byte readByte() {
|
||||||
|
byte v;
|
||||||
|
read(&v, sizeof(byte));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
uint16 readWord() {
|
||||||
|
uint16 v;
|
||||||
|
read(&v, sizeof(uint16));
|
||||||
|
return FROM_LE_16(v);
|
||||||
|
}
|
||||||
|
uint readLong() {
|
||||||
|
uint v;
|
||||||
|
read(&v, sizeof(uint));
|
||||||
|
return FROM_LE_32(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint readUint16BE() {
|
||||||
|
uint16 v;
|
||||||
|
read(&v, sizeof(uint16));
|
||||||
|
return FROM_BE_16(v);
|
||||||
|
}
|
||||||
|
uint readUint16LE() {
|
||||||
|
uint16 v;
|
||||||
|
read(&v, sizeof(uint16));
|
||||||
|
return FROM_LE_16(v);
|
||||||
|
}
|
||||||
|
uint readUint32BE() {
|
||||||
|
uint32 v;
|
||||||
|
read(&v, sizeof(uint32));
|
||||||
|
return FROM_BE_32(v);
|
||||||
|
}
|
||||||
|
uint readUint32LE() {
|
||||||
|
uint32 v;
|
||||||
|
read(&v, sizeof(uint32));
|
||||||
|
return FROM_LE_32(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeByte(byte v) {
|
||||||
|
write(&v, sizeof(byte));
|
||||||
|
}
|
||||||
|
void writeByte(byte v, int len) {
|
||||||
|
byte *b = new byte[len];
|
||||||
|
memset(b, v, len);
|
||||||
|
write(b, len);
|
||||||
|
delete[] b;
|
||||||
|
}
|
||||||
|
void writeWord(uint16 v) {
|
||||||
|
uint16 vTemp = TO_LE_16(v);
|
||||||
|
write(&vTemp, sizeof(uint16));
|
||||||
|
}
|
||||||
|
void writeLong(uint v) {
|
||||||
|
uint vTemp = TO_LE_32(v);
|
||||||
|
write(&vTemp, sizeof(uint));
|
||||||
|
}
|
||||||
|
void writeString(const char *msg) {
|
||||||
|
if (!msg) {
|
||||||
|
writeByte(0);
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
writeByte(*msg);
|
||||||
|
} while (*msg++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void writeString(File &src) {
|
||||||
|
char c;
|
||||||
|
do {
|
||||||
|
c = src.readByte();
|
||||||
|
writeByte(c);
|
||||||
|
} while (c);
|
||||||
|
}
|
||||||
|
uint pos() const {
|
||||||
|
if (_f)
|
||||||
|
return ftell(_f);
|
||||||
|
else
|
||||||
|
return _offset;
|
||||||
|
}
|
||||||
|
uint size() const {
|
||||||
|
if (_f) {
|
||||||
|
uint currentPos = pos();
|
||||||
|
fseek(_f, 0, SEEK_END);
|
||||||
|
uint result = pos();
|
||||||
|
fseek(_f, currentPos, SEEK_SET);
|
||||||
|
return result;
|
||||||
|
} else if (_memPtr) {
|
||||||
|
return _size;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool eof() const {
|
||||||
|
if (_f)
|
||||||
|
return feof(_f) != 0;
|
||||||
|
else if (_memPtr)
|
||||||
|
return _offset >= _size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
86
devtools/create_titanic/hash-str.h
Normal file
86
devtools/create_titanic/hash-str.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_HASH_STR_H
|
||||||
|
#define COMMON_HASH_STR_H
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
uint hashit(const char *str);
|
||||||
|
uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
|
||||||
|
inline uint hashit(const String &str) { return hashit(str.c_str()); }
|
||||||
|
inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str()); }
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: The following functors obviously are not consistently named
|
||||||
|
|
||||||
|
struct CaseSensitiveString_EqualTo {
|
||||||
|
bool operator()(const String& x, const String& y) const { return x.equals(y); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CaseSensitiveString_Hash {
|
||||||
|
uint operator()(const String& x) const { return hashit(x.c_str()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct IgnoreCase_EqualTo {
|
||||||
|
bool operator()(const String& x, const String& y) const { return x.equalsIgnoreCase(y); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IgnoreCase_Hash {
|
||||||
|
uint operator()(const String& x) const { return hashit_lower(x.c_str()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Specalization of the Hash functor for String objects.
|
||||||
|
// We do case sensitve hashing here, because that is what
|
||||||
|
// the default EqualTo is compatible with. If one wants to use
|
||||||
|
// case insensitve hashing, then only because one wants to use
|
||||||
|
// IgnoreCase_EqualTo, and then one has to specify a custom
|
||||||
|
// hash anyway.
|
||||||
|
template<>
|
||||||
|
struct Hash<String> {
|
||||||
|
uint operator()(const String& s) const {
|
||||||
|
return hashit(s.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Hash<const char *> {
|
||||||
|
uint operator()(const char *s) const {
|
||||||
|
return hashit(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// String map -- by default case insensitive
|
||||||
|
typedef HashMap<String, String, IgnoreCase_Hash, IgnoreCase_EqualTo> StringMap;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
109
devtools/create_titanic/hashmap.cpp
Normal file
109
devtools/create_titanic/hashmap.cpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The hash map (associative array) implementation in this file is
|
||||||
|
// based on the PyDict implementation of CPython. The erase() method
|
||||||
|
// is based on example code in the Wikipedia article on Hash tables.
|
||||||
|
|
||||||
|
#include "common/hashmap.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
// Hash function for strings, taken from CPython.
|
||||||
|
uint hashit(const char *p) {
|
||||||
|
uint hash = *p << 7;
|
||||||
|
byte c;
|
||||||
|
int size = 0;
|
||||||
|
while ((c = *p++)) {
|
||||||
|
hash = (1000003 * hash) ^ c;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return hash ^ size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like hashit, but converts every char to lowercase before hashing.
|
||||||
|
uint hashit_lower(const char *p) {
|
||||||
|
uint hash = tolower(*p) << 7;
|
||||||
|
byte c;
|
||||||
|
int size = 0;
|
||||||
|
while ((c = *p++)) {
|
||||||
|
hash = (1000003 * hash) ^ tolower(c);
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
return hash ^ size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
static double
|
||||||
|
g_collisions = 0,
|
||||||
|
g_dummyHits = 0,
|
||||||
|
g_lookups = 0,
|
||||||
|
g_collPerLook = 0,
|
||||||
|
g_capacity = 0,
|
||||||
|
g_size = 0;
|
||||||
|
static int g_max_capacity = 0, g_max_size = 0;
|
||||||
|
static int g_totalHashmaps = 0;
|
||||||
|
static int g_stats[4] = {0,0,0,0};
|
||||||
|
|
||||||
|
void updateHashCollisionStats(int collisions, int dummyHits, int lookups, int arrsize, int nele) {
|
||||||
|
g_collisions += collisions;
|
||||||
|
g_lookups += lookups;
|
||||||
|
g_dummyHits += dummyHits;
|
||||||
|
if (lookups)
|
||||||
|
g_collPerLook += (double)collisions / (double)lookups;
|
||||||
|
g_capacity += arrsize;
|
||||||
|
g_size += nele;
|
||||||
|
g_totalHashmaps++;
|
||||||
|
|
||||||
|
if (3*nele <= 2*8)
|
||||||
|
g_stats[0]++;
|
||||||
|
if (3*nele <= 2*16)
|
||||||
|
g_stats[1]++;
|
||||||
|
if (3*nele <= 2*32)
|
||||||
|
g_stats[2]++;
|
||||||
|
if (3*nele <= 2*64)
|
||||||
|
g_stats[3]++;
|
||||||
|
|
||||||
|
g_max_capacity = MAX(g_max_capacity, arrsize);
|
||||||
|
g_max_size = MAX(g_max_size, nele);
|
||||||
|
|
||||||
|
debug("%d hashmaps: colls %.1f; dummies hit %.1f, lookups %.1f; ratio %.3f%%; size %f (max: %d); capacity %f (max: %d)",
|
||||||
|
g_totalHashmaps,
|
||||||
|
g_collisions / g_totalHashmaps,
|
||||||
|
g_dummyHits / g_totalHashmaps,
|
||||||
|
g_lookups / g_totalHashmaps,
|
||||||
|
100 * g_collPerLook / g_totalHashmaps,
|
||||||
|
g_size / g_totalHashmaps, g_max_size,
|
||||||
|
g_capacity / g_totalHashmaps, g_max_capacity);
|
||||||
|
debug(" %d less than %d; %d less than %d; %d less than %d; %d less than %d",
|
||||||
|
g_stats[0], 2*8/3,
|
||||||
|
g_stats[1],2*16/3,
|
||||||
|
g_stats[2],2*32/3,
|
||||||
|
g_stats[3],2*64/3);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// * Should record the maximal size of the map during its lifetime, not that at its death
|
||||||
|
// * Should do some statistics: how many maps are less than 2/3*8, 2/3*16, 2/3*32, ...
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // End of namespace Common
|
637
devtools/create_titanic/hashmap.h
Normal file
637
devtools/create_titanic/hashmap.h
Normal file
|
@ -0,0 +1,637 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The hash map (associative array) implementation in this file is
|
||||||
|
// based on the PyDict implementation of CPython.
|
||||||
|
|
||||||
|
#ifndef COMMON_HASHMAP_H
|
||||||
|
#define COMMON_HASHMAP_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def DEBUG_HASH_COLLISIONS
|
||||||
|
* Enable the following #define if you want to check how many collisions the
|
||||||
|
* code produces (many collisions indicate either a bad hash function, or a
|
||||||
|
* hash table that is too small).
|
||||||
|
*/
|
||||||
|
//#define DEBUG_HASH_COLLISIONS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def USE_HASHMAP_MEMORY_POOL
|
||||||
|
* Enable the following define to let HashMaps use a memory pool for the
|
||||||
|
nodes they contain. * This increases memory usage, but also can improve
|
||||||
|
speed quite a bit.
|
||||||
|
*/
|
||||||
|
#define USE_HASHMAP_MEMORY_POOL
|
||||||
|
|
||||||
|
|
||||||
|
#include "common/func.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
#include "common/debug.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_HASHMAP_MEMORY_POOL
|
||||||
|
#include "common/memorypool.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
// The sgi IRIX MIPSpro Compiler has difficulties with nested templates.
|
||||||
|
// This and the other __sgi conditionals below work around these problems.
|
||||||
|
// The Intel C++ Compiler suffers from the same problems.
|
||||||
|
#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
|
||||||
|
template<class T> class IteratorImpl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HashMap<Key,Val> maps objects of type Key to objects of type Val.
|
||||||
|
* For each used Key type, we need an "size_type hashit(Key,size_type)" function
|
||||||
|
* that computes a hash for the given Key object and returns it as an
|
||||||
|
* an integer from 0 to hashsize-1, and also an "equality functor".
|
||||||
|
* that returns true if if its two arguments are to be considered
|
||||||
|
* equal. Also, we assume that "=" works on Val objects for assignment.
|
||||||
|
*
|
||||||
|
* If aa is an HashMap<Key,Val>, then space is allocated each time aa[key] is
|
||||||
|
* referenced, for a new key. If the object is const, then an assertion is
|
||||||
|
* triggered instead. Hence if you are not sure whether a key is contained in
|
||||||
|
* the map, use contains() first to check for its presence.
|
||||||
|
*/
|
||||||
|
template<class Key, class Val, class HashFunc = Hash<Key>, class EqualFunc = EqualTo<Key> >
|
||||||
|
class HashMap {
|
||||||
|
public:
|
||||||
|
typedef uint size_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef HashMap<Key, Val, HashFunc, EqualFunc> HM_t;
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
const Key _key;
|
||||||
|
Val _value;
|
||||||
|
explicit Node(const Key &key) : _key(key), _value() {}
|
||||||
|
Node() : _key(), _value() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HASHMAP_PERTURB_SHIFT = 5,
|
||||||
|
HASHMAP_MIN_CAPACITY = 16,
|
||||||
|
|
||||||
|
// The quotient of the next two constants controls how much the
|
||||||
|
// internal storage of the hashmap may fill up before being
|
||||||
|
// increased automatically.
|
||||||
|
// Note: the quotient of these two must be between and different
|
||||||
|
// from 0 and 1.
|
||||||
|
HASHMAP_LOADFACTOR_NUMERATOR = 2,
|
||||||
|
HASHMAP_LOADFACTOR_DENOMINATOR = 3,
|
||||||
|
|
||||||
|
HASHMAP_MEMORYPOOL_SIZE = HASHMAP_MIN_CAPACITY * HASHMAP_LOADFACTOR_NUMERATOR / HASHMAP_LOADFACTOR_DENOMINATOR
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef USE_HASHMAP_MEMORY_POOL
|
||||||
|
ObjectPool<Node, HASHMAP_MEMORYPOOL_SIZE> _nodePool;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Node **_storage; ///< hashtable of size arrsize.
|
||||||
|
size_type _mask; ///< Capacity of the HashMap minus one; must be a power of two of minus one
|
||||||
|
size_type _size;
|
||||||
|
size_type _deleted; ///< Number of deleted elements (_dummyNodes)
|
||||||
|
|
||||||
|
HashFunc _hash;
|
||||||
|
EqualFunc _equal;
|
||||||
|
|
||||||
|
/** Default value, returned by the const getVal. */
|
||||||
|
const Val _defaultVal;
|
||||||
|
|
||||||
|
/** Dummy node, used as marker for erased objects. */
|
||||||
|
#define HASHMAP_DUMMY_NODE ((Node *)1)
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
mutable int _collisions, _lookups, _dummyHits;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Node *allocNode(const Key &key) {
|
||||||
|
#ifdef USE_HASHMAP_MEMORY_POOL
|
||||||
|
return new (_nodePool) Node(key);
|
||||||
|
#else
|
||||||
|
return new Node(key);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeNode(Node *node) {
|
||||||
|
if (node && node != HASHMAP_DUMMY_NODE)
|
||||||
|
#ifdef USE_HASHMAP_MEMORY_POOL
|
||||||
|
_nodePool.deleteChunk(node);
|
||||||
|
#else
|
||||||
|
delete node;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign(const HM_t &map);
|
||||||
|
size_type lookup(const Key &key) const;
|
||||||
|
size_type lookupAndCreateIfMissing(const Key &key);
|
||||||
|
void expandStorage(size_type newCapacity);
|
||||||
|
|
||||||
|
#if !defined(__sgi) || defined(__GNUC__)
|
||||||
|
template<class T> friend class IteratorImpl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple HashMap iterator implementation.
|
||||||
|
*/
|
||||||
|
template<class NodeType>
|
||||||
|
class IteratorImpl {
|
||||||
|
friend class HashMap;
|
||||||
|
#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
|
||||||
|
template<class T> friend class Common::IteratorImpl;
|
||||||
|
#else
|
||||||
|
template<class T> friend class IteratorImpl;
|
||||||
|
#endif
|
||||||
|
protected:
|
||||||
|
typedef const HashMap hashmap_t;
|
||||||
|
|
||||||
|
size_type _idx;
|
||||||
|
hashmap_t *_hashmap;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IteratorImpl(size_type idx, hashmap_t *hashmap) : _idx(idx), _hashmap(hashmap) {}
|
||||||
|
|
||||||
|
NodeType *deref() const {
|
||||||
|
assert(_hashmap != 0);
|
||||||
|
assert(_idx <= _hashmap->_mask);
|
||||||
|
Node *node = _hashmap->_storage[_idx];
|
||||||
|
assert(node != 0);
|
||||||
|
assert(node != HASHMAP_DUMMY_NODE);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
IteratorImpl() : _idx(0), _hashmap(0) {}
|
||||||
|
template<class T>
|
||||||
|
IteratorImpl(const IteratorImpl<T> &c) : _idx(c._idx), _hashmap(c._hashmap) {}
|
||||||
|
|
||||||
|
NodeType &operator*() const { return *deref(); }
|
||||||
|
NodeType *operator->() const { return deref(); }
|
||||||
|
|
||||||
|
bool operator==(const IteratorImpl &iter) const { return _idx == iter._idx && _hashmap == iter._hashmap; }
|
||||||
|
bool operator!=(const IteratorImpl &iter) const { return !(*this == iter); }
|
||||||
|
|
||||||
|
IteratorImpl &operator++() {
|
||||||
|
assert(_hashmap);
|
||||||
|
do {
|
||||||
|
_idx++;
|
||||||
|
} while (_idx <= _hashmap->_mask && (_hashmap->_storage[_idx] == 0 || _hashmap->_storage[_idx] == HASHMAP_DUMMY_NODE));
|
||||||
|
if (_idx > _hashmap->_mask)
|
||||||
|
_idx = (size_type)-1;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IteratorImpl operator++(int) {
|
||||||
|
IteratorImpl old = *this;
|
||||||
|
operator ++();
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef IteratorImpl<Node> iterator;
|
||||||
|
typedef IteratorImpl<const Node> const_iterator;
|
||||||
|
|
||||||
|
HashMap();
|
||||||
|
HashMap(const HM_t &map);
|
||||||
|
~HashMap();
|
||||||
|
|
||||||
|
HM_t &operator=(const HM_t &map) {
|
||||||
|
if (this == &map)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
// Remove the previous content and ...
|
||||||
|
clear();
|
||||||
|
delete[] _storage;
|
||||||
|
// ... copy the new stuff.
|
||||||
|
assign(map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const Key &key) const;
|
||||||
|
|
||||||
|
Val &operator[](const Key &key);
|
||||||
|
const Val &operator[](const Key &key) const;
|
||||||
|
|
||||||
|
Val &getVal(const Key &key);
|
||||||
|
const Val &getVal(const Key &key) const;
|
||||||
|
const Val &getVal(const Key &key, const Val &defaultVal) const;
|
||||||
|
void setVal(const Key &key, const Val &val);
|
||||||
|
|
||||||
|
void clear(bool shrinkArray = 0);
|
||||||
|
|
||||||
|
void erase(iterator entry);
|
||||||
|
void erase(const Key &key);
|
||||||
|
|
||||||
|
size_type size() const { return _size; }
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
// Find and return the first non-empty entry
|
||||||
|
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
|
||||||
|
if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE)
|
||||||
|
return iterator(ctr, this);
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
iterator end() {
|
||||||
|
return iterator((size_type)-1, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const {
|
||||||
|
// Find and return the first non-empty entry
|
||||||
|
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
|
||||||
|
if (_storage[ctr] && _storage[ctr] != HASHMAP_DUMMY_NODE)
|
||||||
|
return const_iterator(ctr, this);
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
const_iterator end() const {
|
||||||
|
return const_iterator((size_type)-1, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator find(const Key &key) {
|
||||||
|
size_type ctr = lookup(key);
|
||||||
|
if (_storage[ctr])
|
||||||
|
return iterator(ctr, this);
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator find(const Key &key) const {
|
||||||
|
size_type ctr = lookup(key);
|
||||||
|
if (_storage[ctr])
|
||||||
|
return const_iterator(ctr, this);
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: insert() method?
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return (_size == 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//-------------------------------------------------------
|
||||||
|
// HashMap functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base constructor, creates an empty hashmap.
|
||||||
|
*/
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap()
|
||||||
|
//
|
||||||
|
// We have to skip _defaultVal() on PS2 to avoid gcc 3.2.2 ICE
|
||||||
|
//
|
||||||
|
#ifdef __PLAYSTATION2__
|
||||||
|
{
|
||||||
|
#else
|
||||||
|
: _defaultVal() {
|
||||||
|
#endif
|
||||||
|
_mask = HASHMAP_MIN_CAPACITY - 1;
|
||||||
|
_storage = new Node *[HASHMAP_MIN_CAPACITY];
|
||||||
|
assert(_storage != NULL);
|
||||||
|
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
|
||||||
|
|
||||||
|
_size = 0;
|
||||||
|
_deleted = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_collisions = 0;
|
||||||
|
_lookups = 0;
|
||||||
|
_dummyHits = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor, creates a full copy of the given hashmap.
|
||||||
|
* We must provide a custom copy constructor as we use pointers
|
||||||
|
* to heap buffers for the internal storage.
|
||||||
|
*/
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
|
||||||
|
_defaultVal() {
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_collisions = 0;
|
||||||
|
_lookups = 0;
|
||||||
|
_dummyHits = 0;
|
||||||
|
#endif
|
||||||
|
assign(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor, frees all used memory.
|
||||||
|
*/
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
|
||||||
|
for (size_type ctr = 0; ctr <= _mask; ++ctr)
|
||||||
|
freeNode(_storage[ctr]);
|
||||||
|
|
||||||
|
delete[] _storage;
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
extern void updateHashCollisionStats(int, int, int, int, int);
|
||||||
|
updateHashCollisionStats(_collisions, _dummyHits, _lookups, _mask+1, _size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for assigning the content of another HashMap
|
||||||
|
* to this one.
|
||||||
|
*
|
||||||
|
* @note We do *not* deallocate the previous storage here -- the caller is
|
||||||
|
* responsible for doing that!
|
||||||
|
*/
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::assign(const HM_t &map) {
|
||||||
|
_mask = map._mask;
|
||||||
|
_storage = new Node *[_mask+1];
|
||||||
|
assert(_storage != NULL);
|
||||||
|
memset(_storage, 0, (_mask+1) * sizeof(Node *));
|
||||||
|
|
||||||
|
// Simply clone the map given to us, one by one.
|
||||||
|
_size = 0;
|
||||||
|
_deleted = 0;
|
||||||
|
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
|
||||||
|
if (map._storage[ctr] == HASHMAP_DUMMY_NODE) {
|
||||||
|
_storage[ctr] = HASHMAP_DUMMY_NODE;
|
||||||
|
_deleted++;
|
||||||
|
} else if (map._storage[ctr] != NULL) {
|
||||||
|
_storage[ctr] = allocNode(map._storage[ctr]->_key);
|
||||||
|
_storage[ctr]->_value = map._storage[ctr]->_value;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Perform a sanity check (to help track down hashmap corruption)
|
||||||
|
assert(_size == map._size);
|
||||||
|
assert(_deleted == map._deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) {
|
||||||
|
for (size_type ctr = 0; ctr <= _mask; ++ctr) {
|
||||||
|
freeNode(_storage[ctr]);
|
||||||
|
_storage[ctr] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_HASHMAP_MEMORY_POOL
|
||||||
|
_nodePool.freeUnusedPages();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (shrinkArray && _mask >= HASHMAP_MIN_CAPACITY) {
|
||||||
|
delete[] _storage;
|
||||||
|
|
||||||
|
_mask = HASHMAP_MIN_CAPACITY;
|
||||||
|
_storage = new Node *[HASHMAP_MIN_CAPACITY];
|
||||||
|
assert(_storage != NULL);
|
||||||
|
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
|
||||||
|
}
|
||||||
|
|
||||||
|
_size = 0;
|
||||||
|
_deleted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::expandStorage(size_type newCapacity) {
|
||||||
|
assert(newCapacity > _mask+1);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const size_type old_size = _size;
|
||||||
|
#endif
|
||||||
|
const size_type old_mask = _mask;
|
||||||
|
Node **old_storage = _storage;
|
||||||
|
|
||||||
|
// allocate a new array
|
||||||
|
_size = 0;
|
||||||
|
_deleted = 0;
|
||||||
|
_mask = newCapacity - 1;
|
||||||
|
_storage = new Node *[newCapacity];
|
||||||
|
assert(_storage != NULL);
|
||||||
|
memset(_storage, 0, newCapacity * sizeof(Node *));
|
||||||
|
|
||||||
|
// rehash all the old elements
|
||||||
|
for (size_type ctr = 0; ctr <= old_mask; ++ctr) {
|
||||||
|
if (old_storage[ctr] == NULL || old_storage[ctr] == HASHMAP_DUMMY_NODE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Insert the element from the old table into the new table.
|
||||||
|
// Since we know that no key exists twice in the old table, we
|
||||||
|
// can do this slightly better than by calling lookup, since we
|
||||||
|
// don't have to call _equal().
|
||||||
|
const size_type hash = _hash(old_storage[ctr]->_key);
|
||||||
|
size_type idx = hash & _mask;
|
||||||
|
for (size_type perturb = hash; _storage[idx] != NULL && _storage[idx] != HASHMAP_DUMMY_NODE; perturb >>= HASHMAP_PERTURB_SHIFT) {
|
||||||
|
idx = (5 * idx + perturb + 1) & _mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
_storage[idx] = old_storage[ctr];
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a sanity check: Old number of elements should match the new one!
|
||||||
|
// This check will fail if some previous operation corrupted this hashmap.
|
||||||
|
assert(_size == old_size);
|
||||||
|
|
||||||
|
delete[] old_storage;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
typename HashMap<Key, Val, HashFunc, EqualFunc>::size_type HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
|
||||||
|
const size_type hash = _hash(key);
|
||||||
|
size_type ctr = hash & _mask;
|
||||||
|
for (size_type perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
|
||||||
|
if (_storage[ctr] == NULL)
|
||||||
|
break;
|
||||||
|
if (_storage[ctr] == HASHMAP_DUMMY_NODE) {
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_dummyHits++;
|
||||||
|
#endif
|
||||||
|
} else if (_equal(_storage[ctr]->_key, key))
|
||||||
|
break;
|
||||||
|
|
||||||
|
ctr = (5 * ctr + perturb + 1) & _mask;
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_collisions++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_lookups++;
|
||||||
|
debug("collisions %d, dummies hit %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d",
|
||||||
|
_collisions, _dummyHits, _lookups, ((double) _collisions / (double)_lookups),
|
||||||
|
(const void *)this, _mask+1, _size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
typename HashMap<Key, Val, HashFunc, EqualFunc>::size_type HashMap<Key, Val, HashFunc, EqualFunc>::lookupAndCreateIfMissing(const Key &key) {
|
||||||
|
const size_type hash = _hash(key);
|
||||||
|
size_type ctr = hash & _mask;
|
||||||
|
const size_type NONE_FOUND = _mask + 1;
|
||||||
|
size_type first_free = NONE_FOUND;
|
||||||
|
bool found = false;
|
||||||
|
for (size_type perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
|
||||||
|
if (_storage[ctr] == NULL)
|
||||||
|
break;
|
||||||
|
if (_storage[ctr] == HASHMAP_DUMMY_NODE) {
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_dummyHits++;
|
||||||
|
#endif
|
||||||
|
if (first_free != _mask + 1)
|
||||||
|
first_free = ctr;
|
||||||
|
} else if (_equal(_storage[ctr]->_key, key)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr = (5 * ctr + perturb + 1) & _mask;
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_collisions++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_HASH_COLLISIONS
|
||||||
|
_lookups++;
|
||||||
|
debug("collisions %d, dummies hit %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d",
|
||||||
|
_collisions, _dummyHits, _lookups, ((double) _collisions / (double)_lookups),
|
||||||
|
(const void *)this, _mask+1, _size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!found && first_free != _mask + 1)
|
||||||
|
ctr = first_free;
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
if (_storage[ctr])
|
||||||
|
_deleted--;
|
||||||
|
_storage[ctr] = allocNode(key);
|
||||||
|
assert(_storage[ctr] != NULL);
|
||||||
|
_size++;
|
||||||
|
|
||||||
|
// Keep the load factor below a certain threshold.
|
||||||
|
// Deleted nodes are also counted
|
||||||
|
size_type capacity = _mask + 1;
|
||||||
|
if ((_size + _deleted) * HASHMAP_LOADFACTOR_DENOMINATOR >
|
||||||
|
capacity * HASHMAP_LOADFACTOR_NUMERATOR) {
|
||||||
|
capacity = capacity < 500 ? (capacity * 4) : (capacity * 2);
|
||||||
|
expandStorage(capacity);
|
||||||
|
ctr = lookup(key);
|
||||||
|
assert(_storage[ctr] != NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const {
|
||||||
|
size_type ctr = lookup(key);
|
||||||
|
return (_storage[ctr] != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator[](const Key &key) {
|
||||||
|
return getVal(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator[](const Key &key) const {
|
||||||
|
return getVal(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) {
|
||||||
|
size_type ctr = lookupAndCreateIfMissing(key);
|
||||||
|
assert(_storage[ctr] != NULL);
|
||||||
|
return _storage[ctr]->_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const {
|
||||||
|
return getVal(key, _defaultVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key, const Val &defaultVal) const {
|
||||||
|
size_type ctr = lookup(key);
|
||||||
|
if (_storage[ctr] != NULL)
|
||||||
|
return _storage[ctr]->_value;
|
||||||
|
else
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::setVal(const Key &key, const Val &val) {
|
||||||
|
size_type ctr = lookupAndCreateIfMissing(key);
|
||||||
|
assert(_storage[ctr] != NULL);
|
||||||
|
_storage[ctr]->_value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(iterator entry) {
|
||||||
|
// Check whether we have a valid iterator
|
||||||
|
assert(entry._hashmap == this);
|
||||||
|
const size_type ctr = entry._idx;
|
||||||
|
assert(ctr <= _mask);
|
||||||
|
Node * const node = _storage[ctr];
|
||||||
|
assert(node != NULL);
|
||||||
|
assert(node != HASHMAP_DUMMY_NODE);
|
||||||
|
|
||||||
|
// If we remove a key, we replace it with a dummy node.
|
||||||
|
freeNode(node);
|
||||||
|
_storage[ctr] = HASHMAP_DUMMY_NODE;
|
||||||
|
_size--;
|
||||||
|
_deleted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Key, class Val, class HashFunc, class EqualFunc>
|
||||||
|
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) {
|
||||||
|
|
||||||
|
size_type ctr = lookup(key);
|
||||||
|
if (_storage[ctr] == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we remove a key, we replace it with a dummy node.
|
||||||
|
freeNode(_storage[ctr]);
|
||||||
|
_storage[ctr] = HASHMAP_DUMMY_NODE;
|
||||||
|
_size--;
|
||||||
|
_deleted++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef HASHMAP_DUMMY_NODE
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
#endif
|
15
devtools/create_titanic/module.mk
Normal file
15
devtools/create_titanic/module.mk
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
MODULE := devtools/create_titanic
|
||||||
|
|
||||||
|
MODULE_OBJS := \
|
||||||
|
create_titanic_dat.o \
|
||||||
|
hashmap.o \
|
||||||
|
str.o \
|
||||||
|
winexe.o \
|
||||||
|
winexe_pe.o
|
||||||
|
|
||||||
|
# Set the name of the executable
|
||||||
|
TOOL_EXECUTABLE := create_titanic
|
||||||
|
|
||||||
|
# Include common rules
|
||||||
|
include $(srcdir)/rules.mk
|
786
devtools/create_titanic/str.cpp
Normal file
786
devtools/create_titanic/str.cpp
Normal file
|
@ -0,0 +1,786 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/hash-str.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/memorypool.h"
|
||||||
|
#include "common/str.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
|
||||||
|
|
||||||
|
static uint32 computeCapacity(uint32 len) {
|
||||||
|
// By default, for the capacity we use the next multiple of 32
|
||||||
|
return ((len + 32 - 1) & ~0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const char *str) : _size(0), _str(_storage) {
|
||||||
|
if (str == 0) {
|
||||||
|
_storage[0] = 0;
|
||||||
|
_size = 0;
|
||||||
|
} else
|
||||||
|
initWithCStr(str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
|
||||||
|
initWithCStr(str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
|
||||||
|
assert(endP >= beginP);
|
||||||
|
initWithCStr(beginP, endP - beginP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::initWithCStr(const char *str, uint32 len) {
|
||||||
|
assert(str);
|
||||||
|
|
||||||
|
// Init _storage member explicitly (ie. without calling its constructor)
|
||||||
|
// for GCC 2.95.x compatibility (see also tracker item #1602879).
|
||||||
|
_storage[0] = 0;
|
||||||
|
|
||||||
|
_size = len;
|
||||||
|
|
||||||
|
if (len >= _builtinCapacity) {
|
||||||
|
// Not enough internal storage, so allocate more
|
||||||
|
_extern._capacity = computeCapacity(len+1);
|
||||||
|
_extern._refCount = 0;
|
||||||
|
_str = new char[_extern._capacity];
|
||||||
|
assert(_str != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the string into the storage area
|
||||||
|
memmove(_str, str, len);
|
||||||
|
_str[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(const String &str)
|
||||||
|
: _size(str._size) {
|
||||||
|
if (str.isStorageIntern()) {
|
||||||
|
// String in internal storage: just copy it
|
||||||
|
memcpy(_storage, str._storage, _builtinCapacity);
|
||||||
|
_str = _storage;
|
||||||
|
} else {
|
||||||
|
// String in external storage: use refcount mechanism
|
||||||
|
str.incRefCount();
|
||||||
|
_extern._refCount = str._extern._refCount;
|
||||||
|
_extern._capacity = str._extern._capacity;
|
||||||
|
_str = str._str;
|
||||||
|
}
|
||||||
|
assert(_str != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::String(char c)
|
||||||
|
: _size(0), _str(_storage) {
|
||||||
|
|
||||||
|
_storage[0] = c;
|
||||||
|
_storage[1] = 0;
|
||||||
|
|
||||||
|
_size = (c == 0) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String::~String() {
|
||||||
|
decRefCount(_extern._refCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::makeUnique() {
|
||||||
|
ensureCapacity(_size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that enough storage is available to store at least new_size
|
||||||
|
* characters plus a null byte. In addition, if we currently share
|
||||||
|
* the storage with another string, unshare it, so that we can safely
|
||||||
|
* write to the storage.
|
||||||
|
*/
|
||||||
|
void String::ensureCapacity(uint32 new_size, bool keep_old) {
|
||||||
|
bool isShared;
|
||||||
|
uint32 curCapacity, newCapacity;
|
||||||
|
char *newStorage;
|
||||||
|
int *oldRefCount = _extern._refCount;
|
||||||
|
|
||||||
|
if (isStorageIntern()) {
|
||||||
|
isShared = false;
|
||||||
|
curCapacity = _builtinCapacity;
|
||||||
|
} else {
|
||||||
|
isShared = (oldRefCount && *oldRefCount > 1);
|
||||||
|
curCapacity = _extern._capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: If there is enough space, and we do not share
|
||||||
|
// the storage, then there is nothing to do.
|
||||||
|
if (!isShared && new_size < curCapacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isShared && new_size < _builtinCapacity) {
|
||||||
|
// We share the storage, but there is enough internal storage: Use that.
|
||||||
|
newStorage = _storage;
|
||||||
|
newCapacity = _builtinCapacity;
|
||||||
|
} else {
|
||||||
|
// We need to allocate storage on the heap!
|
||||||
|
|
||||||
|
// Compute a suitable new capacity limit
|
||||||
|
// If the current capacity is sufficient we use the same capacity
|
||||||
|
if (new_size < curCapacity)
|
||||||
|
newCapacity = curCapacity;
|
||||||
|
else
|
||||||
|
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
|
||||||
|
|
||||||
|
// Allocate new storage
|
||||||
|
newStorage = new char[newCapacity];
|
||||||
|
assert(newStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy old data if needed, elsewise reset the new storage.
|
||||||
|
if (keep_old) {
|
||||||
|
assert(_size < newCapacity);
|
||||||
|
memcpy(newStorage, _str, _size + 1);
|
||||||
|
} else {
|
||||||
|
_size = 0;
|
||||||
|
newStorage[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release hold on the old storage ...
|
||||||
|
decRefCount(oldRefCount);
|
||||||
|
|
||||||
|
// ... in favor of the new storage
|
||||||
|
_str = newStorage;
|
||||||
|
|
||||||
|
if (!isStorageIntern()) {
|
||||||
|
// Set the ref count & capacity if we use an external storage.
|
||||||
|
// It is important to do this *after* copying any old content,
|
||||||
|
// else we would override data that has not yet been copied!
|
||||||
|
_extern._refCount = 0;
|
||||||
|
_extern._capacity = newCapacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::incRefCount() const {
|
||||||
|
assert(!isStorageIntern());
|
||||||
|
if (_extern._refCount == 0) {
|
||||||
|
if (g_refCountPool == 0) {
|
||||||
|
g_refCountPool = new MemoryPool(sizeof(int));
|
||||||
|
assert(g_refCountPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
||||||
|
*_extern._refCount = 2;
|
||||||
|
} else {
|
||||||
|
++(*_extern._refCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::decRefCount(int *oldRefCount) {
|
||||||
|
if (isStorageIntern())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (oldRefCount) {
|
||||||
|
--(*oldRefCount);
|
||||||
|
}
|
||||||
|
if (!oldRefCount || *oldRefCount <= 0) {
|
||||||
|
// The ref count reached zero, so we free the string storage
|
||||||
|
// and the ref count storage.
|
||||||
|
if (oldRefCount) {
|
||||||
|
assert(g_refCountPool);
|
||||||
|
g_refCountPool->freeChunk(oldRefCount);
|
||||||
|
}
|
||||||
|
delete[] _str;
|
||||||
|
|
||||||
|
// Even though _str points to a freed memory block now,
|
||||||
|
// we do not change its value, because any code that calls
|
||||||
|
// decRefCount will have to do this afterwards anyway.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator=(const char *str) {
|
||||||
|
uint32 len = strlen(str);
|
||||||
|
ensureCapacity(len, false);
|
||||||
|
_size = len;
|
||||||
|
memmove(_str, str, len + 1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator=(const String &str) {
|
||||||
|
if (&str == this)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (str.isStorageIntern()) {
|
||||||
|
decRefCount(_extern._refCount);
|
||||||
|
_size = str._size;
|
||||||
|
_str = _storage;
|
||||||
|
memcpy(_str, str._str, _size + 1);
|
||||||
|
} else {
|
||||||
|
str.incRefCount();
|
||||||
|
decRefCount(_extern._refCount);
|
||||||
|
|
||||||
|
_extern._refCount = str._extern._refCount;
|
||||||
|
_extern._capacity = str._extern._capacity;
|
||||||
|
_size = str._size;
|
||||||
|
_str = str._str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator=(char c) {
|
||||||
|
decRefCount(_extern._refCount);
|
||||||
|
_str = _storage;
|
||||||
|
|
||||||
|
_str[0] = c;
|
||||||
|
_str[1] = 0;
|
||||||
|
|
||||||
|
_size = (c == 0) ? 0 : 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator+=(const char *str) {
|
||||||
|
if (_str <= str && str <= _str + _size)
|
||||||
|
return operator+=(String(str));
|
||||||
|
|
||||||
|
int len = strlen(str);
|
||||||
|
if (len > 0) {
|
||||||
|
ensureCapacity(_size + len, true);
|
||||||
|
|
||||||
|
memcpy(_str + _size, str, len + 1);
|
||||||
|
_size += len;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator+=(const String &str) {
|
||||||
|
if (&str == this)
|
||||||
|
return operator+=(String(str));
|
||||||
|
|
||||||
|
int len = str._size;
|
||||||
|
if (len > 0) {
|
||||||
|
ensureCapacity(_size + len, true);
|
||||||
|
|
||||||
|
memcpy(_str + _size, str._str, len + 1);
|
||||||
|
_size += len;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String &String::operator+=(char c) {
|
||||||
|
ensureCapacity(_size + 1, true);
|
||||||
|
|
||||||
|
_str[_size++] = c;
|
||||||
|
_str[_size] = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::hasPrefix(const String &x) const {
|
||||||
|
return hasPrefix(x.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::hasPrefix(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
// Compare x with the start of _str.
|
||||||
|
const char *y = c_str();
|
||||||
|
while (*x && *x == *y) {
|
||||||
|
++x;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
// It's a prefix, if and only if all letters in x are 'used up' before
|
||||||
|
// _str ends.
|
||||||
|
return *x == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::hasSuffix(const String &x) const {
|
||||||
|
return hasSuffix(x.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::hasSuffix(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
// Compare x with the end of _str.
|
||||||
|
const uint32 x_size = strlen(x);
|
||||||
|
if (x_size > _size)
|
||||||
|
return false;
|
||||||
|
const char *y = c_str() + _size - x_size;
|
||||||
|
while (*x && *x == *y) {
|
||||||
|
++x;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
// It's a suffix, if and only if all letters in x are 'used up' before
|
||||||
|
// _str ends.
|
||||||
|
return *x == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::contains(const String &x) const {
|
||||||
|
return strstr(c_str(), x.c_str()) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::contains(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return strstr(c_str(), x) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::contains(char x) const {
|
||||||
|
return strchr(c_str(), x) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::deleteLastChar() {
|
||||||
|
if (_size > 0)
|
||||||
|
deleteChar(_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::deleteChar(uint32 p) {
|
||||||
|
assert(p < _size);
|
||||||
|
|
||||||
|
makeUnique();
|
||||||
|
while (p++ < _size)
|
||||||
|
_str[p - 1] = _str[p];
|
||||||
|
_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::erase(uint32 p, uint32 len) {
|
||||||
|
assert(p < _size);
|
||||||
|
|
||||||
|
makeUnique();
|
||||||
|
// If len == npos or p + len is over the end, remove all the way to the end
|
||||||
|
if (len == npos || p + len >= _size) {
|
||||||
|
// Delete char at p as well. So _size = (p - 1) + 1
|
||||||
|
_size = p;
|
||||||
|
// Null terminate
|
||||||
|
_str[_size] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; p + len <= _size; p++) {
|
||||||
|
_str[p] = _str[p + len];
|
||||||
|
}
|
||||||
|
_size -= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::clear() {
|
||||||
|
decRefCount(_extern._refCount);
|
||||||
|
|
||||||
|
_size = 0;
|
||||||
|
_str = _storage;
|
||||||
|
_storage[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::setChar(char c, uint32 p) {
|
||||||
|
assert(p < _size);
|
||||||
|
|
||||||
|
makeUnique();
|
||||||
|
_str[p] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::insertChar(char c, uint32 p) {
|
||||||
|
assert(p <= _size);
|
||||||
|
|
||||||
|
ensureCapacity(_size + 1, true);
|
||||||
|
_size++;
|
||||||
|
for (uint32 i = _size; i > p; --i)
|
||||||
|
_str[i] = _str[i - 1];
|
||||||
|
_str[p] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::toLowercase() {
|
||||||
|
makeUnique();
|
||||||
|
for (uint32 i = 0; i < _size; ++i)
|
||||||
|
_str[i] = tolower(_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void String::toUppercase() {
|
||||||
|
makeUnique();
|
||||||
|
for (uint32 i = 0; i < _size; ++i)
|
||||||
|
_str[i] = toupper(_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint String::hash() const {
|
||||||
|
return hashit(c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
String String::format(const char *fmt, ...) {
|
||||||
|
String output;
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
output = String::vformat(fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
String String::vformat(const char *fmt, va_list args) {
|
||||||
|
String output;
|
||||||
|
assert(output.isStorageIntern());
|
||||||
|
|
||||||
|
va_list va;
|
||||||
|
scumm_va_copy(va, args);
|
||||||
|
int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
if (len == -1 || len == _builtinCapacity - 1) {
|
||||||
|
// MSVC and IRIX don't return the size the full string would take up.
|
||||||
|
// MSVC returns -1, IRIX returns the number of characters actually written,
|
||||||
|
// which is at the most the size of the buffer minus one, as the string is
|
||||||
|
// truncated to fit.
|
||||||
|
|
||||||
|
// We assume MSVC failed to output the correct, null-terminated string
|
||||||
|
// if the return value is either -1 or size.
|
||||||
|
// For IRIX, because we lack a better mechanism, we assume failure
|
||||||
|
// if the return value equals size - 1.
|
||||||
|
// The downside to this is that whenever we try to format a string where the
|
||||||
|
// size is 1 below the built-in capacity, the size is needlessly increased.
|
||||||
|
|
||||||
|
// Try increasing the size of the string until it fits.
|
||||||
|
int size = _builtinCapacity;
|
||||||
|
do {
|
||||||
|
size *= 2;
|
||||||
|
output.ensureCapacity(size - 1, false);
|
||||||
|
assert(!output.isStorageIntern());
|
||||||
|
size = output._extern._capacity;
|
||||||
|
|
||||||
|
scumm_va_copy(va, args);
|
||||||
|
len = vsnprintf(output._str, size, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
} while (len == -1 || len >= size - 1);
|
||||||
|
output._size = len;
|
||||||
|
} else if (len < (int)_builtinCapacity) {
|
||||||
|
// vsnprintf succeeded
|
||||||
|
output._size = len;
|
||||||
|
} else {
|
||||||
|
// vsnprintf didn't have enough space, so grow buffer
|
||||||
|
output.ensureCapacity(len, false);
|
||||||
|
scumm_va_copy(va, args);
|
||||||
|
int len2 = vsnprintf(output._str, len+1, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
assert(len == len2);
|
||||||
|
output._size = len2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
bool String::operator==(const String &x) const {
|
||||||
|
return equals(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator==(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return equals(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator!=(const String &x) const {
|
||||||
|
return !equals(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator !=(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return !equals(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator<(const String &x) const {
|
||||||
|
return compareTo(x) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator<=(const String &x) const {
|
||||||
|
return compareTo(x) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator>(const String &x) const {
|
||||||
|
return compareTo(x) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::operator>=(const String &x) const {
|
||||||
|
return compareTo(x) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
bool operator==(const char* y, const String &x) {
|
||||||
|
return (x == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const char* y, const String &x) {
|
||||||
|
return x != y;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
bool String::equals(const String &x) const {
|
||||||
|
return (0 == compareTo(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::equals(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return (0 == compareTo(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::equalsIgnoreCase(const String &x) const {
|
||||||
|
return (0 == compareToIgnoreCase(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool String::equalsIgnoreCase(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return (0 == compareToIgnoreCase(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::compareTo(const String &x) const {
|
||||||
|
return compareTo(x.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::compareTo(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return strcmp(c_str(), x);
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::compareToIgnoreCase(const String &x) const {
|
||||||
|
return compareToIgnoreCase(x.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::compareToIgnoreCase(const char *x) const {
|
||||||
|
assert(x != 0);
|
||||||
|
return scumm_stricmp(c_str(), x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
String operator+(const String &x, const String &y) {
|
||||||
|
String temp(x);
|
||||||
|
temp += y;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String operator+(const char *x, const String &y) {
|
||||||
|
String temp(x);
|
||||||
|
temp += y;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String operator+(const String &x, const char *y) {
|
||||||
|
String temp(x);
|
||||||
|
temp += y;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String operator+(char x, const String &y) {
|
||||||
|
String temp(x);
|
||||||
|
temp += y;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String operator+(const String &x, char y) {
|
||||||
|
String temp(x);
|
||||||
|
temp += y;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String lastPathComponent(const String &path, const char sep) {
|
||||||
|
const char *str = path.c_str();
|
||||||
|
const char *last = str + path.size();
|
||||||
|
|
||||||
|
// Skip over trailing slashes
|
||||||
|
while (last > str && *(last-1) == sep)
|
||||||
|
--last;
|
||||||
|
|
||||||
|
// Path consisted of only slashes -> return empty string
|
||||||
|
if (last == str)
|
||||||
|
return String();
|
||||||
|
|
||||||
|
// Now scan the whole component
|
||||||
|
const char *first = last - 1;
|
||||||
|
while (first > str && *first != sep)
|
||||||
|
--first;
|
||||||
|
|
||||||
|
if (*first == sep)
|
||||||
|
first++;
|
||||||
|
|
||||||
|
return String(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizePath(const String &path, const char sep) {
|
||||||
|
if (path.empty())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
const char *cur = path.c_str();
|
||||||
|
String result;
|
||||||
|
|
||||||
|
// If there is a leading slash, preserve that:
|
||||||
|
if (*cur == sep) {
|
||||||
|
result += sep;
|
||||||
|
// Skip over multiple leading slashes, so "//" equals "/"
|
||||||
|
while (*cur == sep)
|
||||||
|
++cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for path components till the end of the String
|
||||||
|
List<String> comps;
|
||||||
|
while (*cur != 0) {
|
||||||
|
const char *start = cur;
|
||||||
|
|
||||||
|
// Scan till the next path separator resp. the end of the string
|
||||||
|
while (*cur != sep && *cur != 0)
|
||||||
|
cur++;
|
||||||
|
|
||||||
|
const String component(start, cur);
|
||||||
|
|
||||||
|
if (component.empty() || component == ".") {
|
||||||
|
// Skip empty components and dot components
|
||||||
|
} else if (!comps.empty() && component == ".." && comps.back() != "..") {
|
||||||
|
// If stack is non-empty and top is not "..", remove top
|
||||||
|
comps.pop_back();
|
||||||
|
} else {
|
||||||
|
// Add the component to the stack
|
||||||
|
comps.push_back(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over separator chars
|
||||||
|
while (*cur == sep)
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, assemble all components back into a path
|
||||||
|
while (!comps.empty()) {
|
||||||
|
result += comps.front();
|
||||||
|
comps.pop_front();
|
||||||
|
if (!comps.empty())
|
||||||
|
result += sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t size) {
|
||||||
|
// Our backup of the source's start, we need this
|
||||||
|
// to calculate the source's length.
|
||||||
|
const char * const srcStart = src;
|
||||||
|
|
||||||
|
// In case a non-empty size was specified we
|
||||||
|
// copy over (size - 1) bytes at max.
|
||||||
|
if (size != 0) {
|
||||||
|
// Copy over (size - 1) bytes at max.
|
||||||
|
while (--size != 0) {
|
||||||
|
if ((*dst++ = *src) == 0)
|
||||||
|
break;
|
||||||
|
++src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the source string was longer than the
|
||||||
|
// destination, we need to add a terminating
|
||||||
|
// zero.
|
||||||
|
if (size == 0)
|
||||||
|
*dst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the terminating zero of the source
|
||||||
|
// string, we need this to determine the length
|
||||||
|
// of the source string.
|
||||||
|
while (*src)
|
||||||
|
++src;
|
||||||
|
|
||||||
|
// Return the source string's length.
|
||||||
|
return src - srcStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlcat(char *dst, const char *src, size_t size) {
|
||||||
|
// In case the destination buffer does not contain
|
||||||
|
// space for at least 1 character, we will just
|
||||||
|
// return the source string's length.
|
||||||
|
if (size == 0)
|
||||||
|
return strlen(src);
|
||||||
|
|
||||||
|
// Our backup of the source's start, we need this
|
||||||
|
// to calculate the source's length.
|
||||||
|
const char * const srcStart = src;
|
||||||
|
|
||||||
|
// Our backup of the destination's start, we need
|
||||||
|
// this to calculate the destination's length.
|
||||||
|
const char * const dstStart = dst;
|
||||||
|
|
||||||
|
// Search the end of the destination, but do not
|
||||||
|
// move past the terminating zero.
|
||||||
|
while (size-- != 0 && *dst != 0)
|
||||||
|
++dst;
|
||||||
|
|
||||||
|
// Calculate the destination's length;
|
||||||
|
const size_t dstLength = dst - dstStart;
|
||||||
|
|
||||||
|
// In case we reached the end of the destination
|
||||||
|
// buffer before we had a chance to append any
|
||||||
|
// characters we will just return the destination
|
||||||
|
// length plus the source string's length.
|
||||||
|
if (size == 0)
|
||||||
|
return dstLength + strlen(srcStart);
|
||||||
|
|
||||||
|
// Copy over all of the source that fits
|
||||||
|
// the destination buffer. We also need
|
||||||
|
// to take the terminating zero we will
|
||||||
|
// add into consideration.
|
||||||
|
while (size-- != 0 && *src != 0)
|
||||||
|
*dst++ = *src++;
|
||||||
|
*dst = 0;
|
||||||
|
|
||||||
|
// Move to the terminating zero of the source
|
||||||
|
// string, we need this to determine the length
|
||||||
|
// of the source string.
|
||||||
|
while (*src)
|
||||||
|
++src;
|
||||||
|
|
||||||
|
// Return the total length of the result string
|
||||||
|
return dstLength + (src - srcStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
// Portable implementation of stricmp / strcasecmp / strcmpi.
|
||||||
|
// TODO: Rename this to Common::strcasecmp
|
||||||
|
int scumm_stricmp(const char *s1, const char *s2) {
|
||||||
|
byte l1, l2;
|
||||||
|
do {
|
||||||
|
// Don't use ++ inside tolower, in case the macro uses its
|
||||||
|
// arguments more than once.
|
||||||
|
l1 = (byte)*s1++;
|
||||||
|
l1 = tolower(l1);
|
||||||
|
l2 = (byte)*s2++;
|
||||||
|
l2 = tolower(l2);
|
||||||
|
} while (l1 == l2 && l1 != 0);
|
||||||
|
return l1 - l2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Portable implementation of strnicmp / strncasecmp / strncmpi.
|
||||||
|
// TODO: Rename this to Common::strncasecmp
|
||||||
|
int scumm_strnicmp(const char *s1, const char *s2, uint n) {
|
||||||
|
byte l1, l2;
|
||||||
|
do {
|
||||||
|
if (n-- == 0)
|
||||||
|
return 0; // no difference found so far -> signal equality
|
||||||
|
|
||||||
|
// Don't use ++ inside tolower, in case the macro uses its
|
||||||
|
// arguments more than once.
|
||||||
|
l1 = (byte)*s1++;
|
||||||
|
l1 = tolower(l1);
|
||||||
|
l2 = (byte)*s2++;
|
||||||
|
l2 = tolower(l2);
|
||||||
|
} while (l1 == l2 && l1 != 0);
|
||||||
|
return l1 - l2;
|
||||||
|
}
|
386
devtools/create_titanic/str.h
Normal file
386
devtools/create_titanic/str.h
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_STRING_H
|
||||||
|
#define COMMON_STRING_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple string class for ScummVM. Provides automatic storage managment,
|
||||||
|
* and overloads several operators in a 'natural' fashion, mimicking
|
||||||
|
* the std::string class. Even provides simple iterators.
|
||||||
|
*
|
||||||
|
* This class tries to avoid allocating lots of small blocks on the heap,
|
||||||
|
* since that is inefficient on several platforms supported by ScummVM.
|
||||||
|
* Instead, small strings are stored 'inside' the string object (i.e. on
|
||||||
|
* the stack, for stack allocated objects), and only for strings exceeding
|
||||||
|
* a certain length do we allocate a buffer on the heap.
|
||||||
|
*
|
||||||
|
* The presence of \0 characters in the string will cause undefined
|
||||||
|
* behavior in some operations.
|
||||||
|
*/
|
||||||
|
class String {
|
||||||
|
public:
|
||||||
|
static const uint32 npos = 0xFFFFFFFF;
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* The size of the internal storage. Increasing this means less heap
|
||||||
|
* allocations are needed, at the cost of more stack memory usage,
|
||||||
|
* and of course lots of wasted memory. Empirically, 90% or more of
|
||||||
|
* all String instances are less than 32 chars long. If a platform
|
||||||
|
* is very short on stack space, it would be possible to lower this.
|
||||||
|
* A value of 24 still seems acceptable, though considerably worse,
|
||||||
|
* while 16 seems to be the lowest you want to go... Anything lower
|
||||||
|
* than 8 makes no sense, since that's the size of member _extern
|
||||||
|
* (on 32 bit machines; 12 bytes on systems with 64bit pointers).
|
||||||
|
*/
|
||||||
|
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the string. Stored to avoid having to call strlen
|
||||||
|
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
|
||||||
|
* on purpose :-).
|
||||||
|
*/
|
||||||
|
uint32 _size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to the actual string storage. Either points to _storage,
|
||||||
|
* or to a block allocated on the heap via malloc.
|
||||||
|
*/
|
||||||
|
char *_str;
|
||||||
|
|
||||||
|
|
||||||
|
union {
|
||||||
|
/**
|
||||||
|
* Internal string storage.
|
||||||
|
*/
|
||||||
|
char _storage[_builtinCapacity];
|
||||||
|
/**
|
||||||
|
* External string storage data -- the refcounter, and the
|
||||||
|
* capacity of the string _str points to.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
mutable int *_refCount;
|
||||||
|
uint32 _capacity;
|
||||||
|
} _extern;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isStorageIntern() const {
|
||||||
|
return _str == _storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Construct a new empty string. */
|
||||||
|
String() : _size(0), _str(_storage) { _storage[0] = 0; }
|
||||||
|
|
||||||
|
/** Construct a new string from the given NULL-terminated C string. */
|
||||||
|
String(const char *str);
|
||||||
|
|
||||||
|
/** Construct a new string containing exactly len characters read from address str. */
|
||||||
|
String(const char *str, uint32 len);
|
||||||
|
|
||||||
|
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
||||||
|
String(const char *beginP, const char *endP);
|
||||||
|
|
||||||
|
/** Construct a copy of the given string. */
|
||||||
|
String(const String &str);
|
||||||
|
|
||||||
|
/** Construct a string consisting of the given character. */
|
||||||
|
explicit String(char c);
|
||||||
|
|
||||||
|
~String();
|
||||||
|
|
||||||
|
String &operator=(const char *str);
|
||||||
|
String &operator=(const String &str);
|
||||||
|
String &operator=(char c);
|
||||||
|
String &operator+=(const char *str);
|
||||||
|
String &operator+=(const String &str);
|
||||||
|
String &operator+=(char c);
|
||||||
|
|
||||||
|
bool operator==(const String &x) const;
|
||||||
|
bool operator==(const char *x) const;
|
||||||
|
bool operator!=(const String &x) const;
|
||||||
|
bool operator!=(const char *x) const;
|
||||||
|
|
||||||
|
bool operator<(const String &x) const;
|
||||||
|
bool operator<=(const String &x) const;
|
||||||
|
bool operator>(const String &x) const;
|
||||||
|
bool operator>=(const String &x) const;
|
||||||
|
|
||||||
|
bool equals(const String &x) const;
|
||||||
|
bool equalsIgnoreCase(const String &x) const;
|
||||||
|
int compareTo(const String &x) const; // strcmp clone
|
||||||
|
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
||||||
|
|
||||||
|
bool equals(const char *x) const;
|
||||||
|
bool equalsIgnoreCase(const char *x) const;
|
||||||
|
int compareTo(const char *x) const; // strcmp clone
|
||||||
|
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
||||||
|
|
||||||
|
bool hasSuffix(const String &x) const;
|
||||||
|
bool hasSuffix(const char *x) const;
|
||||||
|
|
||||||
|
bool hasPrefix(const String &x) const;
|
||||||
|
bool hasPrefix(const char *x) const;
|
||||||
|
|
||||||
|
bool contains(const String &x) const;
|
||||||
|
bool contains(const char *x) const;
|
||||||
|
bool contains(char x) const;
|
||||||
|
|
||||||
|
inline const char *c_str() const { return _str; }
|
||||||
|
inline uint size() const { return _size; }
|
||||||
|
|
||||||
|
inline bool empty() const { return (_size == 0); }
|
||||||
|
char firstChar() const { return (_size > 0) ? _str[0] : 0; }
|
||||||
|
char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
|
||||||
|
|
||||||
|
char operator[](int idx) const {
|
||||||
|
assert(_str && idx >= 0 && idx < (int)_size);
|
||||||
|
return _str[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove the last character from the string. */
|
||||||
|
void deleteLastChar();
|
||||||
|
|
||||||
|
/** Remove the character at position p from the string. */
|
||||||
|
void deleteChar(uint32 p);
|
||||||
|
|
||||||
|
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
|
||||||
|
void erase(uint32 p, uint32 len = npos);
|
||||||
|
|
||||||
|
/** Set character c at position p, replacing the previous character there. */
|
||||||
|
void setChar(char c, uint32 p);
|
||||||
|
|
||||||
|
/** Insert character c before position p. */
|
||||||
|
void insertChar(char c, uint32 p);
|
||||||
|
|
||||||
|
/** Clears the string, making it empty. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/** Convert all characters in the string to lowercase. */
|
||||||
|
void toLowercase();
|
||||||
|
|
||||||
|
/** Convert all characters in the string to uppercase. */
|
||||||
|
void toUppercase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes trailing and leading whitespaces. Uses isspace() to decide
|
||||||
|
* what is whitespace and what not.
|
||||||
|
*/
|
||||||
|
void trim();
|
||||||
|
|
||||||
|
uint hash() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print formatted data into a String object. Similar to sprintf,
|
||||||
|
* except that it stores the result in (variably sized) String
|
||||||
|
* instead of a fixed size buffer.
|
||||||
|
*/
|
||||||
|
static String format(const char *fmt, ...) GCC_PRINTF(1,2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print formatted data into a String object. Similar to vsprintf,
|
||||||
|
* except that it stores the result in (variably sized) String
|
||||||
|
* instead of a fixed size buffer.
|
||||||
|
*/
|
||||||
|
static String vformat(const char *fmt, va_list args);
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef char value_type;
|
||||||
|
/**
|
||||||
|
* Unsigned version of the underlying type. This can be used to cast
|
||||||
|
* individual string characters to bigger integer types without sign
|
||||||
|
* extension happening.
|
||||||
|
*/
|
||||||
|
typedef unsigned char unsigned_type;
|
||||||
|
typedef char * iterator;
|
||||||
|
typedef const char * const_iterator;
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
// Since the user could potentially
|
||||||
|
// change the string via the returned
|
||||||
|
// iterator we have to assure we are
|
||||||
|
// pointing to a unique storage.
|
||||||
|
makeUnique();
|
||||||
|
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return begin() + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const {
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const {
|
||||||
|
return begin() + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void makeUnique();
|
||||||
|
void ensureCapacity(uint32 new_size, bool keep_old);
|
||||||
|
void incRefCount() const;
|
||||||
|
void decRefCount(int *oldRefCount);
|
||||||
|
void initWithCStr(const char *str, uint32 len);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append two strings to form a new (temp) string
|
||||||
|
String operator+(const String &x, const String &y);
|
||||||
|
|
||||||
|
String operator+(const char *x, const String &y);
|
||||||
|
String operator+(const String &x, const char *y);
|
||||||
|
|
||||||
|
String operator+(const String &x, char y);
|
||||||
|
String operator+(char x, const String &y);
|
||||||
|
|
||||||
|
// Some useful additional comparison operators for Strings
|
||||||
|
bool operator==(const char *x, const String &y);
|
||||||
|
bool operator!=(const char *x, const String &y);
|
||||||
|
|
||||||
|
// Utility functions to remove leading and trailing whitespaces
|
||||||
|
extern char *ltrim(char *t);
|
||||||
|
extern char *rtrim(char *t);
|
||||||
|
extern char *trim(char *t);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last component of a given path.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* /foo/bar.txt would return 'bar.txt'
|
||||||
|
* /foo/bar/ would return 'bar'
|
||||||
|
* /foo/./bar// would return 'bar'
|
||||||
|
*
|
||||||
|
* @param path the path of which we want to know the last component
|
||||||
|
* @param sep character used to separate path components
|
||||||
|
* @return The last component of the path.
|
||||||
|
*/
|
||||||
|
String lastPathComponent(const String &path, const char sep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a given path to a canonical form. In particular:
|
||||||
|
* - trailing separators are removed: /foo/bar/ -> /foo/bar
|
||||||
|
* - double separators (= empty components) are removed: /foo//bar -> /foo/bar
|
||||||
|
* - dot components are removed: /foo/./bar -> /foo/bar
|
||||||
|
*
|
||||||
|
* @todo remove double dot components: /foo/baz/../bar -> /foo/bar
|
||||||
|
*
|
||||||
|
* @param path the path to normalize
|
||||||
|
* @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
|
||||||
|
* @return the normalized path
|
||||||
|
*/
|
||||||
|
String normalizePath(const String &path, const char sep);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple DOS-style pattern matching function (understands * and ? like used in DOS).
|
||||||
|
* Taken from exult/files/listfiles.cc
|
||||||
|
*
|
||||||
|
* Token meaning:
|
||||||
|
* "*": any character, any amount of times.
|
||||||
|
* "?": any character, only once.
|
||||||
|
* "#": any decimal digit, only once.
|
||||||
|
*
|
||||||
|
* Example strings/patterns:
|
||||||
|
* String: monkey.s01 Pattern: monkey.s?? => true
|
||||||
|
* String: monkey.s101 Pattern: monkey.s?? => false
|
||||||
|
* String: monkey.s99 Pattern: monkey.s?1 => false
|
||||||
|
* String: monkey.s101 Pattern: monkey.s* => true
|
||||||
|
* String: monkey.s99 Pattern: monkey.s*1 => false
|
||||||
|
* String: monkey.s01 Pattern: monkey.s## => true
|
||||||
|
* String: monkey.s01 Pattern: monkey.### => false
|
||||||
|
*
|
||||||
|
* @param str Text to be matched against the given pattern.
|
||||||
|
* @param pat Glob pattern.
|
||||||
|
* @param ignoreCase Whether to ignore the case when doing pattern match
|
||||||
|
* @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly.
|
||||||
|
*
|
||||||
|
* @return true if str matches the pattern, false otherwise.
|
||||||
|
*/
|
||||||
|
bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take a 32 bit value and turn it into a four character string, where each of
|
||||||
|
* the four bytes is turned into one character. Most significant byte is printed
|
||||||
|
* first.
|
||||||
|
*/
|
||||||
|
String tag2string(uint32 tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy up to size - 1 characters from src to dst and also zero terminate the
|
||||||
|
* result. Note that src must be a zero terminated string.
|
||||||
|
*
|
||||||
|
* In case size is zero this function just returns the length of the source
|
||||||
|
* string.
|
||||||
|
*
|
||||||
|
* @note This is modeled after OpenBSD's strlcpy. See the manpage here:
|
||||||
|
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy
|
||||||
|
*
|
||||||
|
* @param dst The destination buffer.
|
||||||
|
* @param src The source string.
|
||||||
|
* @param size The size of the destination buffer.
|
||||||
|
* @return The length of the (non-truncated) result, i.e. strlen(src).
|
||||||
|
*/
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the string src to the string dst. Note that both src and dst must be
|
||||||
|
* zero terminated. The result will be zero terminated. At most
|
||||||
|
* "size - strlen(dst) - 1" bytes will be appended.
|
||||||
|
*
|
||||||
|
* In case the dst string does not contain a zero within the first "size" bytes
|
||||||
|
* the dst string will not be changed and size + strlen(src) is returned.
|
||||||
|
*
|
||||||
|
* @note This is modeled after OpenBSD's strlcat. See the manpage here:
|
||||||
|
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat
|
||||||
|
*
|
||||||
|
* @param dst The string the source string should be appended to.
|
||||||
|
* @param src The source string.
|
||||||
|
* @param size The (total) size of the destination buffer.
|
||||||
|
* @return The length of the (non-truncated) result. That is
|
||||||
|
* strlen(dst) + strlen(src). In case strlen(dst) > size
|
||||||
|
* size + strlen(src) is returned.
|
||||||
|
*/
|
||||||
|
size_t strlcat(char *dst, const char *src, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience wrapper for tag2string which "returns" a C string.
|
||||||
|
* Note: It is *NOT* safe to do anything with the return value other than directly
|
||||||
|
* copying or printing it.
|
||||||
|
*/
|
||||||
|
#define tag2str(x) Common::tag2string(x).c_str()
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
extern int scumm_stricmp(const char *s1, const char *s2);
|
||||||
|
extern int scumm_strnicmp(const char *s1, const char *s2, uint n);
|
||||||
|
|
||||||
|
#endif
|
83
devtools/create_titanic/winexe.cpp
Normal file
83
devtools/create_titanic/winexe.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
#include "winexe.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
WinResourceID &WinResourceID::operator=(const String &x) {
|
||||||
|
_name = x;
|
||||||
|
_idType = kIDTypeString;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WinResourceID &WinResourceID::operator=(uint32 x) {
|
||||||
|
_id = x;
|
||||||
|
_idType = kIDTypeNumerical;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinResourceID::operator==(const String &x) const {
|
||||||
|
return _idType == kIDTypeString && _name.equalsIgnoreCase(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinResourceID::operator==(const uint32 &x) const {
|
||||||
|
return _idType == kIDTypeNumerical && _id == x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WinResourceID::operator==(const WinResourceID &x) const {
|
||||||
|
if (_idType != x._idType)
|
||||||
|
return false;
|
||||||
|
if (_idType == kIDTypeString)
|
||||||
|
return _name.equalsIgnoreCase(x._name);
|
||||||
|
if (_idType == kIDTypeNumerical)
|
||||||
|
return _id == x._id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String WinResourceID::getString() const {
|
||||||
|
if (_idType != kIDTypeString)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 WinResourceID::getID() const {
|
||||||
|
if (_idType != kIDTypeNumerical)
|
||||||
|
return 0xffffffff;
|
||||||
|
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
String WinResourceID::toString() const {
|
||||||
|
if (_idType == kIDTypeString)
|
||||||
|
return _name;
|
||||||
|
else if (_idType == kIDTypeNumerical)
|
||||||
|
return String::format("0x%08x", _id);
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Common
|
70
devtools/create_titanic/winexe.h
Normal file
70
devtools/create_titanic/winexe.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_WINEXE_H
|
||||||
|
#define COMMON_WINEXE_H
|
||||||
|
|
||||||
|
#include "hash-str.h"
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
class WinResourceID {
|
||||||
|
public:
|
||||||
|
WinResourceID() { _idType = kIDTypeNull; }
|
||||||
|
WinResourceID(String x) { _idType = kIDTypeString; _name = x; }
|
||||||
|
WinResourceID(uint32 x) { _idType = kIDTypeNumerical; _id = x; }
|
||||||
|
|
||||||
|
WinResourceID &operator=(const String &x);
|
||||||
|
WinResourceID &operator=(uint32 x);
|
||||||
|
|
||||||
|
bool operator==(const String &x) const;
|
||||||
|
bool operator==(const uint32 &x) const;
|
||||||
|
bool operator==(const WinResourceID &x) const;
|
||||||
|
|
||||||
|
String getString() const;
|
||||||
|
uint32 getID() const;
|
||||||
|
String toString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** An ID Type. */
|
||||||
|
enum IDType {
|
||||||
|
kIDTypeNull, ///< No type set
|
||||||
|
kIDTypeNumerical, ///< A numerical ID.
|
||||||
|
kIDTypeString ///< A string ID.
|
||||||
|
} _idType;
|
||||||
|
|
||||||
|
String _name; ///< The resource's string ID.
|
||||||
|
uint32 _id; ///< The resource's numerical ID.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WinResourceID_Hash {
|
||||||
|
uint operator()(const WinResourceID &id) const { return hashit(id.toString()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WinResourceID_EqualTo {
|
||||||
|
bool operator()(const WinResourceID &id1, const WinResourceID &id2) const { return id1 == id2; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
#endif
|
260
devtools/create_titanic/winexe_pe.cpp
Normal file
260
devtools/create_titanic/winexe_pe.cpp
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
#include "str.h"
|
||||||
|
#include "winexe_pe.h"
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
PEResources::PEResources() {
|
||||||
|
_exe = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PEResources::~PEResources() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PEResources::clear() {
|
||||||
|
_sections.clear();
|
||||||
|
_resources.clear();
|
||||||
|
delete _exe; _exe = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PEResources::loadFromEXE(const String &fileName) {
|
||||||
|
if (fileName.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
File *file = new File();
|
||||||
|
|
||||||
|
if (!file->open(fileName.c_str())) {
|
||||||
|
delete file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadFromEXE(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PEResources::loadFromEXE(File *stream) {
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream->readUint16BE() != MKTAG16('M', 'Z'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->skip(58);
|
||||||
|
|
||||||
|
uint32 peOffset = stream->readUint32LE();
|
||||||
|
|
||||||
|
if (!peOffset || peOffset >= (uint32)stream->size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->seek(peOffset);
|
||||||
|
|
||||||
|
if (stream->readUint32BE() != MKTAG('P','E',0,0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->skip(2);
|
||||||
|
uint16 sectionCount = stream->readUint16LE();
|
||||||
|
stream->skip(12);
|
||||||
|
uint16 optionalHeaderSize = stream->readUint16LE();
|
||||||
|
stream->skip(optionalHeaderSize + 2);
|
||||||
|
|
||||||
|
// Read in all the sections
|
||||||
|
for (uint16 i = 0; i < sectionCount; i++) {
|
||||||
|
char sectionName[9];
|
||||||
|
stream->read(sectionName, 8);
|
||||||
|
sectionName[8] = 0;
|
||||||
|
|
||||||
|
Section section;
|
||||||
|
stream->skip(4);
|
||||||
|
section.virtualAddress = stream->readUint32LE();
|
||||||
|
section.size = stream->readUint32LE();
|
||||||
|
section.offset = stream->readUint32LE();
|
||||||
|
stream->skip(16);
|
||||||
|
|
||||||
|
_sections[sectionName] = section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, we require loading a resource section
|
||||||
|
if (!_sections.contains(".rsrc")) {
|
||||||
|
clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_exe = stream;
|
||||||
|
|
||||||
|
Section &resSection = _sections[".rsrc"];
|
||||||
|
parseResourceLevel(resSection, resSection.offset, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PEResources::parseResourceLevel(Section §ion, uint32 offset, int level) {
|
||||||
|
_exe->seek(offset + 12);
|
||||||
|
|
||||||
|
uint16 namedEntryCount = _exe->readUint16LE();
|
||||||
|
uint16 intEntryCount = _exe->readUint16LE();
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < (uint32)(namedEntryCount + intEntryCount); i++) {
|
||||||
|
uint32 value = _exe->readUint32LE();
|
||||||
|
|
||||||
|
WinResourceID id;
|
||||||
|
|
||||||
|
if (value & 0x80000000) {
|
||||||
|
value &= 0x7fffffff;
|
||||||
|
|
||||||
|
uint32 startPos = _exe->pos();
|
||||||
|
_exe->seek(section.offset + (value & 0x7fffffff));
|
||||||
|
|
||||||
|
// Read in the name, truncating from unicode to ascii
|
||||||
|
String name;
|
||||||
|
uint16 nameLength = _exe->readUint16LE();
|
||||||
|
while (nameLength--)
|
||||||
|
name += (char)(_exe->readUint16LE() & 0xff);
|
||||||
|
|
||||||
|
_exe->seek(startPos);
|
||||||
|
|
||||||
|
id = name;
|
||||||
|
} else {
|
||||||
|
id = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 nextOffset = _exe->readUint32LE();
|
||||||
|
uint32 lastOffset = _exe->pos();
|
||||||
|
|
||||||
|
if (level == 0)
|
||||||
|
_curType = id;
|
||||||
|
else if (level == 1)
|
||||||
|
_curName = id;
|
||||||
|
else if (level == 2)
|
||||||
|
_curLang = id;
|
||||||
|
|
||||||
|
if (level < 2) {
|
||||||
|
// Time to dive down further
|
||||||
|
parseResourceLevel(section, section.offset + (nextOffset & 0x7fffffff), level + 1);
|
||||||
|
} else {
|
||||||
|
_exe->seek(section.offset + nextOffset);
|
||||||
|
|
||||||
|
Resource resource;
|
||||||
|
resource.offset = _exe->readUint32LE() + section.offset - section.virtualAddress;
|
||||||
|
resource.size = _exe->readUint32LE();
|
||||||
|
|
||||||
|
_resources[_curType][_curName][_curLang] = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
_exe->seek(lastOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Array<WinResourceID> PEResources::getTypeList() const {
|
||||||
|
Array<WinResourceID> array;
|
||||||
|
|
||||||
|
if (!_exe)
|
||||||
|
return array;
|
||||||
|
|
||||||
|
for (TypeMap::const_iterator it = _resources.begin(); it != _resources.end(); it++)
|
||||||
|
array.push_back(it->_key);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Array<WinResourceID> PEResources::getNameList(const WinResourceID &type) const {
|
||||||
|
Array<WinResourceID> array;
|
||||||
|
|
||||||
|
if (!_exe || !_resources.contains(type))
|
||||||
|
return array;
|
||||||
|
|
||||||
|
const NameMap &nameMap = _resources[type];
|
||||||
|
|
||||||
|
for (NameMap::const_iterator it = nameMap.begin(); it != nameMap.end(); it++)
|
||||||
|
array.push_back(it->_key);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Array<WinResourceID> PEResources::getLangList(const WinResourceID &type, const WinResourceID &name) const {
|
||||||
|
Array<WinResourceID> array;
|
||||||
|
|
||||||
|
if (!_exe || !_resources.contains(type))
|
||||||
|
return array;
|
||||||
|
|
||||||
|
const NameMap &nameMap = _resources[type];
|
||||||
|
|
||||||
|
if (!nameMap.contains(name))
|
||||||
|
return array;
|
||||||
|
|
||||||
|
const LangMap &langMap = nameMap[name];
|
||||||
|
|
||||||
|
for (LangMap::const_iterator it = langMap.begin(); it != langMap.end(); it++)
|
||||||
|
array.push_back(it->_key);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *PEResources::getResource(const WinResourceID &type, const WinResourceID &name) {
|
||||||
|
Array<WinResourceID> langList = getLangList(type, name);
|
||||||
|
|
||||||
|
if (langList.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const Resource &resource = _resources[type][name][langList[0]];
|
||||||
|
byte *data = (byte *)malloc(resource.size);
|
||||||
|
_exe->seek(resource.offset);
|
||||||
|
_exe->read(data, resource.size);
|
||||||
|
|
||||||
|
File *file = new File();
|
||||||
|
file->open(data, resource.size);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *PEResources::getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang) {
|
||||||
|
if (!_exe || !_resources.contains(type))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const NameMap &nameMap = _resources[type];
|
||||||
|
|
||||||
|
if (!nameMap.contains(name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const LangMap &langMap = nameMap[name];
|
||||||
|
|
||||||
|
if (!langMap.contains(lang))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const Resource &resource = langMap[lang];
|
||||||
|
byte *data = (byte *)malloc(resource.size);
|
||||||
|
_exe->seek(resource.offset);
|
||||||
|
_exe->read(data, resource.size);
|
||||||
|
|
||||||
|
File *file = new File();
|
||||||
|
file->open(data, resource.size);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace Common
|
120
devtools/create_titanic/winexe_pe.h
Normal file
120
devtools/create_titanic/winexe_pe.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/* ScummVM - Graphic Adventure Engine
|
||||||
|
*
|
||||||
|
* ScummVM is the legal property of its developers, whose names
|
||||||
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||||
|
* file distributed with this source distribution.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_WINEXE_PE_H
|
||||||
|
#define COMMON_WINEXE_PE_H
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
#include "hash-str.h"
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "str.h"
|
||||||
|
#include "winexe.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template<class T> class Array;
|
||||||
|
class SeekableReadStream;
|
||||||
|
|
||||||
|
/** The default Windows PE resources. */
|
||||||
|
enum PEResourceType {
|
||||||
|
kPECursor = 0x01,
|
||||||
|
kPEBitmap = 0x02,
|
||||||
|
kPEIcon = 0x03,
|
||||||
|
kPEMenu = 0x04,
|
||||||
|
kPEDialog = 0x05,
|
||||||
|
kPEString = 0x06,
|
||||||
|
kPEFontDir = 0x07,
|
||||||
|
kPEFont = 0x08,
|
||||||
|
kPEAccelerator = 0x09,
|
||||||
|
kPERCData = 0x0A,
|
||||||
|
kPEMessageTable = 0x0B,
|
||||||
|
kPEGroupCursor = 0x0C,
|
||||||
|
kPEGroupIcon = 0x0E,
|
||||||
|
kPEVersion = 0x10,
|
||||||
|
kPEDlgInclude = 0x11,
|
||||||
|
kPEPlugPlay = 0x13,
|
||||||
|
kPEVXD = 0x14,
|
||||||
|
kPEAniCursor = 0x15,
|
||||||
|
kPEAniIcon = 0x16
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class able to load resources from a Windows Portable Executable, such
|
||||||
|
* as cursors, bitmaps, and sounds.
|
||||||
|
*/
|
||||||
|
class PEResources {
|
||||||
|
public:
|
||||||
|
PEResources();
|
||||||
|
~PEResources();
|
||||||
|
|
||||||
|
/** Clear all information. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/** Load from an EXE file. */
|
||||||
|
bool loadFromEXE(const String &fileName);
|
||||||
|
|
||||||
|
bool loadFromEXE(File *stream);
|
||||||
|
|
||||||
|
/** Return a list of resource types. */
|
||||||
|
const Array<WinResourceID> getTypeList() const;
|
||||||
|
|
||||||
|
/** Return a list of names for a given type. */
|
||||||
|
const Array<WinResourceID> getNameList(const WinResourceID &type) const;
|
||||||
|
|
||||||
|
/** Return a list of languages for a given type and name. */
|
||||||
|
const Array<WinResourceID> getLangList(const WinResourceID &type, const WinResourceID &name) const;
|
||||||
|
|
||||||
|
/** Return a stream to the specified resource, taking the first language found (or 0 if non-existent). */
|
||||||
|
File *getResource(const WinResourceID &type, const WinResourceID &name);
|
||||||
|
|
||||||
|
/** Return a stream to the specified resource (or 0 if non-existent). */
|
||||||
|
File *getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Section {
|
||||||
|
uint32 virtualAddress;
|
||||||
|
uint32 size;
|
||||||
|
uint32 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
HashMap<String, Section, IgnoreCase_Hash, IgnoreCase_EqualTo> _sections;
|
||||||
|
|
||||||
|
File *_exe;
|
||||||
|
|
||||||
|
void parseResourceLevel(Section §ion, uint32 offset, int level);
|
||||||
|
WinResourceID _curType, _curName, _curLang;
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
uint32 offset;
|
||||||
|
uint32 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef HashMap<WinResourceID, Resource, WinResourceID_Hash, WinResourceID_EqualTo> LangMap;
|
||||||
|
typedef HashMap<WinResourceID, LangMap, WinResourceID_Hash, WinResourceID_EqualTo> NameMap;
|
||||||
|
typedef HashMap<WinResourceID, NameMap, WinResourceID_Hash, WinResourceID_EqualTo> TypeMap;
|
||||||
|
|
||||||
|
TypeMap _resources;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace Common
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Add a link
Reference in a new issue