mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 23:57:32 +00:00
218e132e37
svn-id: r27024
318 lines
8.2 KiB
C++
318 lines
8.2 KiB
C++
/* 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "lure/strings.h"
|
|
#include "lure/disk.h"
|
|
#include "lure/res.h"
|
|
#include "lure/room.h"
|
|
#include "common/endian.h"
|
|
|
|
namespace Lure {
|
|
|
|
StringData *int_strings = NULL;
|
|
|
|
StringData::StringData() {
|
|
int_strings = this;
|
|
|
|
for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr) _chars[ctr] = NULL;
|
|
_numChars = 0;
|
|
_names = Disk::getReference().getEntry(NAMES_RESOURCE_ID);
|
|
_strings[0] = Disk::getReference().getEntry(STRINGS_RESOURCE_ID);
|
|
_strings[1] = Disk::getReference().getEntry(STRINGS_2_RESOURCE_ID);
|
|
_strings[2] = Disk::getReference().getEntry(STRINGS_3_RESOURCE_ID);
|
|
|
|
// Add in the list of bit sequences, and what characters they represent
|
|
add("00", ' ');
|
|
add("0100", 'e');
|
|
add("0101", 'o');
|
|
add("0110", 't');
|
|
add("01110", 'a');
|
|
add("01111", 'n');
|
|
add("1000", 's');
|
|
add("1001", 'i');
|
|
add("1010", 'r');
|
|
add("10110", 'h');
|
|
add("101110", 'u');
|
|
add("1011110", 'l');
|
|
add("1011111", 'd');
|
|
add("11000", 'y');
|
|
add("110010", 'g');
|
|
add("110011", '\0');
|
|
add("110100", 'w');
|
|
add("110101", 'c');
|
|
add("110110", 'f');
|
|
add("1101110", '.');
|
|
add("1101111", 'm');
|
|
add("111000", 'p');
|
|
add("111001", 'b');
|
|
add("1110100", ',');
|
|
add("1110101", 'k');
|
|
add("1110110", '\'');
|
|
add("11101110", 'I');
|
|
add("11101111", 'v');
|
|
add("1111000", '!');
|
|
add("1111001", '\xb4');
|
|
add("11110100", 'T');
|
|
add("11110101", '\xb5');
|
|
add("11110110", '?');
|
|
add("111101110", '\xb2');
|
|
add("111101111", '\xb3');
|
|
add("11111000", 'W');
|
|
add("111110010", 'H');
|
|
add("111110011", 'A');
|
|
add("111110100", '\xb1');
|
|
add("111110101", 'S');
|
|
add("111110110", 'Y');
|
|
add("1111101110", 'G');
|
|
add("11111011110", 'M');
|
|
add("11111011111", 'N');
|
|
add("111111000", 'O');
|
|
add("1111110010", 'E');
|
|
add("1111110011", 'L');
|
|
add("1111110100", '-');
|
|
add("1111110101", 'R');
|
|
add("1111110110", 'B');
|
|
add("11111101110", 'D');
|
|
add("11111101111", '\xa6');
|
|
add("1111111000", 'C');
|
|
add("11111110010", 'x');
|
|
add("11111110011", 'j');
|
|
add("1111111010", '\xac');
|
|
add("11111110110", '\xa3');
|
|
add("111111101110", 'P');
|
|
add("111111101111", 'U');
|
|
add("11111111000", 'q');
|
|
add("11111111001", '\xad');
|
|
add("111111110100", 'F');
|
|
add("111111110101", '1');
|
|
add("111111110110", '\xaf');
|
|
add("1111111101110", ';');
|
|
add("1111111101111", 'z');
|
|
add("111111111000", '\xa5');
|
|
add("1111111110010", '2');
|
|
add("1111111110011", '\xb0');
|
|
add("111111111010", 'K');
|
|
add("1111111110110", '%');
|
|
add("11111111101110", '\xa2');
|
|
add("11111111101111", '5');
|
|
add("1111111111000", ':');
|
|
add("1111111111001", 'J');
|
|
add("1111111111010", 'V');
|
|
add("11111111110110", '6');
|
|
add("11111111110111", '3');
|
|
add("1111111111100", '\xab');
|
|
add("11111111111010", '\xae');
|
|
add("111111111110110", '0');
|
|
add("111111111110111", '4');
|
|
add("11111111111100", '7');
|
|
add("111111111111010", '9');
|
|
add("111111111111011", '"');
|
|
add("111111111111100", '8');
|
|
add("111111111111101", '\xa7');
|
|
add("1111111111111100", '/');
|
|
add("1111111111111101", 'Q');
|
|
add("11111111111111100", '\xa8');
|
|
add("11111111111111101", '(');
|
|
add("111111111111111100", ')');
|
|
add("111111111111111101", '\x99');
|
|
add("11111111111111111", '\xa9');
|
|
}
|
|
|
|
StringData::~StringData() {
|
|
int_strings = NULL;
|
|
|
|
for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr)
|
|
if (_chars[ctr]) delete _chars[ctr];
|
|
else break;
|
|
|
|
delete _names;
|
|
delete _strings[0];
|
|
delete _strings[1];
|
|
delete _strings[2];
|
|
}
|
|
|
|
StringData &StringData::getReference() {
|
|
return *int_strings;
|
|
}
|
|
|
|
void StringData::add(const char *sequence, char ascii) {
|
|
uint32 value = 0;
|
|
|
|
for (uint8 index = 0; index < strlen(sequence); ++index) {
|
|
if (sequence[index] == '1')
|
|
value |= (1 << index);
|
|
else if (sequence[index] != '0')
|
|
error("Invalid character in string bit-stream sequence");
|
|
}
|
|
|
|
if (_numChars == MAX_NUM_CHARS)
|
|
error("Max characters too lower in string decoder");
|
|
_chars[_numChars++] = new CharacterEntry(strlen(sequence), value, ascii);
|
|
}
|
|
|
|
byte StringData::readBit() {
|
|
byte result = ((*_srcPos & _bitMask) != 0) ? 1 : 0;
|
|
_bitMask >>= 1;
|
|
if (_bitMask == 0) {
|
|
_bitMask = 0x80;
|
|
++_srcPos;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool StringData::initPosition(uint16 stringId) {
|
|
uint16 roomNumber = Room::getReference().roomNumber();
|
|
byte *stringTable;
|
|
|
|
if ((roomNumber >= 0x2A) && (stringId >= STRING_ID_RANGE) && (stringId < STRING_ID_UPPER))
|
|
stringId = 0x76;
|
|
if ((roomNumber < 0x2A) && (stringId >= STRING_ID_UPPER))
|
|
stringId = 0x76;
|
|
|
|
if (stringId < STRING_ID_RANGE)
|
|
stringTable = _strings[0]->data();
|
|
else if (stringId < STRING_ID_RANGE*2) {
|
|
stringId -= STRING_ID_RANGE;
|
|
stringTable = _strings[1]->data();
|
|
} else {
|
|
stringId -= STRING_ID_RANGE * 2;
|
|
stringTable = _strings[2]->data();
|
|
}
|
|
|
|
_srcPos = stringTable + 4;
|
|
|
|
uint32 total = 0;
|
|
int numLoops = stringId >> 5;
|
|
for (int ctr = 0; ctr < numLoops; ++ctr) {
|
|
total += READ_LE_UINT16(_srcPos);
|
|
_srcPos += sizeof(uint16);
|
|
}
|
|
|
|
numLoops = stringId & 0x1f;
|
|
if (numLoops!= 0) {
|
|
byte *tempPtr = stringTable + (stringId & 0xffe0) + READ_LE_UINT16(stringTable);
|
|
|
|
for (int ctr = 0; ctr < numLoops; ++ctr) {
|
|
byte v = *tempPtr++;
|
|
if ((v & 0x80) == 0) {
|
|
total += v;
|
|
} else {
|
|
total += (v & 0x7f) << 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
_bitMask = 0x80;
|
|
|
|
if ((total & 3) != 0)
|
|
_bitMask >>= (total & 3) * 2;
|
|
|
|
_srcPos = stringTable + (total >> 2) + READ_LE_UINT16(stringTable + 2);
|
|
|
|
// Final positioning to start of string
|
|
for (;;) {
|
|
if (readBit() == 0) break;
|
|
_srcPos += 2;
|
|
}
|
|
return readBit() != 0;
|
|
}
|
|
|
|
// readCharatcer
|
|
// Reads the next character from the input bit stream
|
|
|
|
char StringData::readCharacter() {
|
|
uint32 searchValue = 0;
|
|
|
|
// Loop through an increasing number of bits
|
|
|
|
for (uint8 numBits = 1; numBits <= 18; ++numBits) {
|
|
searchValue |= readBit() << (numBits - 1);
|
|
|
|
// Scan through list for a match
|
|
for (int index = 0; _chars[index] != NULL; ++index) {
|
|
if ((_chars[index]->_numBits == numBits) &&
|
|
(_chars[index]->_sequence == searchValue))
|
|
return _chars[index]->_ascii;
|
|
}
|
|
}
|
|
|
|
error("Unknown bit sequence encountered when decoding string");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void StringData::getString(uint16 stringId, char *dest, const char *hotspotName,
|
|
const char *characterName, int hotspotArticle, int characterArticle) {
|
|
const char *articles[4] = {"the ", "a ", "an ", ""};
|
|
char ch;
|
|
strcpy(dest, "");
|
|
char *destPos = dest;
|
|
stringId &= 0x1fff; // Strip off any article identifier
|
|
if (stringId == 0) return;
|
|
|
|
bool includeArticles = initPosition(stringId);
|
|
|
|
ch = readCharacter();
|
|
while (ch != '\0') {
|
|
if (ch == '%') {
|
|
// Copy over hotspot or action
|
|
ch = readCharacter();
|
|
const char *p = (ch == '1') ? hotspotName : characterName;
|
|
int article = !includeArticles ? 3 : ((ch == 1) ? hotspotArticle : characterArticle);
|
|
|
|
if (p != NULL) {
|
|
strcpy(destPos, articles[article]);
|
|
strcat(destPos, p);
|
|
destPos += strlen(destPos);
|
|
}
|
|
} else if ((uint8) ch >= 0xa0) {
|
|
const char *p = getName((uint8) ch - 0xa0);
|
|
strcpy(destPos, p);
|
|
destPos += strlen(p);
|
|
} else {
|
|
*destPos++ = ch;
|
|
}
|
|
|
|
ch = readCharacter();
|
|
}
|
|
|
|
*destPos = '\0';
|
|
}
|
|
|
|
// getName
|
|
// Returns the name or fragment of word at the specified index in the names resource
|
|
|
|
char *StringData::getName(uint8 nameIndex) {
|
|
uint16 numNames = *((uint16 *) _names->data()) / 2;
|
|
if (nameIndex >= numNames)
|
|
error("Invalid name index was passed to getCharacterName");
|
|
|
|
uint16 nameStart = *((uint16 *) (_names->data() + (nameIndex * 2)));
|
|
return (char *) (_names->data() + nameStart);
|
|
}
|
|
|
|
} // namespace Lure
|