mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-04 16:26:53 +00:00
261 lines
7.3 KiB
C++
261 lines
7.3 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.
|
|
*
|
|
*/
|
|
|
|
#include "lure/strings.h"
|
|
#include "lure/disk.h"
|
|
#include "lure/lure.h"
|
|
#include "lure/res.h"
|
|
#include "lure/room.h"
|
|
#include "common/endian.h"
|
|
|
|
namespace Lure {
|
|
|
|
StringData *int_strings = NULL;
|
|
|
|
StringData::StringData() {
|
|
int_strings = this;
|
|
Disk &disk = Disk::getReference();
|
|
|
|
for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr) _chars[ctr] = NULL;
|
|
_numChars = 0;
|
|
_names = Disk::getReference().getEntry(NAMES_RESOURCE_ID);
|
|
_strings[0] = disk.getEntry(STRINGS_RESOURCE_ID);
|
|
_strings[1] = disk.getEntry(STRINGS_2_RESOURCE_ID);
|
|
_strings[2] = disk.getEntry(STRINGS_3_RESOURCE_ID);
|
|
|
|
// Add in the list of bit sequences, and what characters they represent
|
|
MemoryBlock *decoderList = disk.getEntry(STRING_DECODER_RESOURCE_ID);
|
|
|
|
const char *p = (const char *) decoderList->data();
|
|
while ((byte)*p != 0xff) {
|
|
char ascii = *p++;
|
|
add(p, ascii);
|
|
p += strlen(p) + 1;
|
|
}
|
|
|
|
delete decoderList;
|
|
}
|
|
|
|
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();
|
|
|
|
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; // for compilers that don't support NORETURN
|
|
}
|
|
|
|
void StringData::getString(uint16 stringId, char *dest, const char *hotspotName,
|
|
const char *characterName, int hotspotArticle, int characterArticle) {
|
|
debugC(ERROR_INTERMEDIATE, kLureDebugStrings,
|
|
"StringData::getString stringId=%xh hotspot=%d,%s character=%d,%s",
|
|
stringId, hotspotArticle, hotspotName, characterArticle, characterName);
|
|
StringList &stringList = Resources::getReference().stringList();
|
|
char ch;
|
|
strcpy(dest, "");
|
|
char *destPos = dest;
|
|
stringId &= 0x1fff; // Strip off any article identifier
|
|
if (stringId == 0) return;
|
|
|
|
bool includeArticles = initPosition(stringId);
|
|
uint32 charOffset = _srcPos - _stringTable;
|
|
uint8 charBitMask = _bitMask;
|
|
|
|
ch = readCharacter();
|
|
|
|
while (ch != '\0') {
|
|
if (ch == '%') {
|
|
// Copy over hotspot or action
|
|
ch = readCharacter();
|
|
const char *p = (ch == '1') ? hotspotName : characterName;
|
|
int article = !includeArticles ? 0 : ((ch == '1') ? hotspotArticle : characterArticle);
|
|
|
|
if (p != NULL) {
|
|
if (article > 0) {
|
|
strcpy(destPos, stringList.getString(S_ARTICLE_LIST + article - 1));
|
|
strcat(destPos, p);
|
|
} else {
|
|
strcpy(destPos, p);
|
|
}
|
|
destPos += strlen(destPos);
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh name=%s",
|
|
charOffset, charBitMask, (uint8)ch, p);
|
|
}
|
|
} else if ((uint8) ch >= 0xa0) {
|
|
const char *p = getName((uint8) ch - 0xa0);
|
|
strcpy(destPos, p);
|
|
destPos += strlen(p);
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh sequence='%s'",
|
|
charOffset, charBitMask, (uint8)ch, p);
|
|
} else {
|
|
*destPos++ = ch;
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh char=%c",
|
|
charOffset, charBitMask, (uint8)ch, ch);
|
|
}
|
|
|
|
charOffset = _srcPos - _stringTable;
|
|
charBitMask = _bitMask;
|
|
|
|
// WORKAROUND: Italian version had an unterminated Look description for Prisoner after cutting sack
|
|
if ((charOffset == 0x1a08) && (charBitMask == 1) &&
|
|
(LureEngine::getReference().getLanguage() == Common::IT_ITA))
|
|
// Hardcode for end of string
|
|
ch = '\0';
|
|
else
|
|
// All other character reads
|
|
ch = readCharacter();
|
|
}
|
|
|
|
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh EOS",
|
|
charOffset, charBitMask, ch);
|
|
*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 = READ_LE_UINT16(_names->data()) / 2;
|
|
if (nameIndex >= numNames)
|
|
error("Invalid name index was passed to getCharacterName");
|
|
|
|
uint16 nameStart = READ_LE_UINT16(_names->data() + (nameIndex * 2));
|
|
return (char *) (_names->data() + nameStart);
|
|
}
|
|
|
|
} // namespace Lure
|