re3/src/text/Text.cpp

531 lines
13 KiB
C++
Raw Normal View History

2020-04-26 20:49:24 +00:00
#include "common.h"
#include "FileMgr.h"
#ifdef MORE_LANGUAGES
#include "Game.h"
#endif
#include "Frontend.h"
#include "Messages.h"
#include "Text.h"
2020-05-17 22:28:40 +00:00
#include "Timer.h"
2020-04-26 20:49:24 +00:00
wchar WideErrorString[25];
2020-04-26 20:49:24 +00:00
CText TheText;
CText::CText(void)
{
encoding = 'e';
2020-05-17 22:28:40 +00:00
bHasMissionTextOffsets = false;
bIsMissionTextLoaded = false;
memset(szMissionTableName, 0, sizeof(szMissionTableName));
2020-04-26 20:49:24 +00:00
memset(WideErrorString, 0, sizeof(WideErrorString));
}
void
CText::Load(void)
{
2020-05-17 22:28:40 +00:00
char filename[32];
2020-07-22 11:56:28 +00:00
size_t offset;
2020-05-17 22:28:40 +00:00
int file;
bool tkey_loaded = false, tdat_loaded = false;
ChunkHeader m_ChunkHeader;
bIsMissionTextLoaded = false;
bHasMissionTextOffsets = false;
2020-04-26 20:49:24 +00:00
Unload();
CFileMgr::SetDir("TEXT");
2020-05-21 23:44:01 +00:00
switch(FrontEndMenuManager.m_PrefsLanguage){
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_AMERICAN:
2020-04-26 20:49:24 +00:00
sprintf(filename, "AMERICAN.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_FRENCH:
2020-04-26 20:49:24 +00:00
sprintf(filename, "FRENCH.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_GERMAN:
2020-04-26 20:49:24 +00:00
sprintf(filename, "GERMAN.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_ITALIAN:
2020-04-26 20:49:24 +00:00
sprintf(filename, "ITALIAN.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_SPANISH:
2020-04-26 20:49:24 +00:00
sprintf(filename, "SPANISH.GXT");
break;
#ifdef MORE_LANGUAGES
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_POLISH:
2020-04-26 20:49:24 +00:00
sprintf(filename, "POLISH.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_RUSSIAN:
2020-04-26 20:49:24 +00:00
sprintf(filename, "RUSSIAN.GXT");
break;
2020-06-27 21:01:51 +00:00
case CMenuManager::LANGUAGE_JAPANESE:
2020-04-26 20:49:24 +00:00
sprintf(filename, "JAPANESE.GXT");
break;
#endif
}
2020-05-17 22:28:40 +00:00
file = CFileMgr::OpenFile(filename, "rb");
2020-04-26 20:49:24 +00:00
offset = 0;
2020-05-17 22:28:40 +00:00
while (!tkey_loaded || !tdat_loaded) {
ReadChunkHeader(&m_ChunkHeader, file, &offset);
if (m_ChunkHeader.size != 0) {
if (strncmp(m_ChunkHeader.magic, "TABL", 4) == 0) {
MissionTextOffsets.Load(m_ChunkHeader.size, file, &offset, 0x58000);
bHasMissionTextOffsets = true;
} else if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) {
this->keyArray.Load(m_ChunkHeader.size, file, &offset);
tkey_loaded = true;
} else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) {
this->data.Load(m_ChunkHeader.size, file, &offset);
tdat_loaded = true;
} else {
CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR);
offset += m_ChunkHeader.size;
}
2020-04-26 20:49:24 +00:00
}
}
keyArray.Update(data.chars);
2020-05-17 22:28:40 +00:00
CFileMgr::CloseFile(file);
CFileMgr::SetDir("");
2020-04-26 20:49:24 +00:00
}
void
CText::Unload(void)
{
CMessages::ClearAllMessagesDisplayedByGame();
keyArray.Unload();
2020-05-17 22:28:40 +00:00
data.Unload();
mission_keyArray.Unload();
mission_data.Unload();
bIsMissionTextLoaded = false;
memset(szMissionTableName, 0, sizeof(szMissionTableName));
2020-04-26 20:49:24 +00:00
}
wchar*
CText::Get(const char *key)
{
2020-05-17 22:28:40 +00:00
uint8 result = false;
#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
2020-05-17 22:28:40 +00:00
wchar *outstr = keyArray.Search(key, data.chars, &result);
#else
wchar *outstr = keyArray.Search(key, &result);
#endif
if (!result && bHasMissionTextOffsets && bIsMissionTextLoaded)
#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
2020-05-17 22:28:40 +00:00
outstr = mission_keyArray.Search(key, mission_data.chars, &result);
2020-05-16 10:51:54 +00:00
#else
2020-05-17 22:28:40 +00:00
outstr = mission_keyArray.Search(key, &result);
2020-05-16 10:51:54 +00:00
#endif
2020-05-17 22:28:40 +00:00
return outstr;
2020-04-26 20:49:24 +00:00
}
wchar UpperCaseTable[128] = {
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
149, 173, 173, 175, 176, 177, 178, 179, 180, 181, 182,
183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
249, 250, 251, 252, 253, 254, 255
};
wchar FrenchUpperCaseTable[128] = {
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 65, 65, 65, 65, 132, 133, 69, 69, 69, 69, 73, 73,
73, 73, 79, 79, 79, 79, 85, 85, 85, 85, 173, 173, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
253, 254, 255
};
wchar
CText::GetUpperCase(wchar c)
{
switch (encoding)
{
case 'e':
if (c >= 'a' && c <= 'z')
return c - 32;
break;
case 'f':
if (c >= 'a' && c <= 'z')
return c - 32;
if (c >= 128 && c <= 255)
return FrenchUpperCaseTable[c-128];
break;
case 'g':
case 'i':
case 's':
if (c >= 'a' && c <= 'z')
return c - 32;
if (c >= 128 && c <= 255)
return UpperCaseTable[c-128];
break;
default:
break;
}
return c;
}
void
CText::UpperCase(wchar *s)
{
while(*s){
*s = GetUpperCase(*s);
s++;
}
}
2020-05-17 22:28:40 +00:00
void
CText::GetNameOfLoadedMissionText(char *outName)
{
strcpy(outName, szMissionTableName);
}
void
2020-07-22 11:56:28 +00:00
CText::ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *offset)
2020-05-17 22:28:40 +00:00
{
#ifdef THIS_IS_STUPID
2020-10-10 21:56:16 +00:00
char *_buf = (char*)buf;
for (int i = 0; i < sizeof(ChunkHeader); i++) {
CFileMgr::Read(file, &_buf[i], 1);
(*offset)++;
}
#else
2020-05-17 22:28:40 +00:00
// original code loops 8 times to read 1 byte with CFileMgr::Read, that's retarded
CFileMgr::Read(file, (char*)buf, sizeof(ChunkHeader));
*offset += sizeof(ChunkHeader);
2020-10-10 21:56:16 +00:00
#endif
2020-05-17 22:28:40 +00:00
}
void
CText::LoadMissionText(char *MissionTableName)
{
char filename[32];
2020-10-10 15:19:49 +00:00
CMessages::ClearAllMessagesDisplayedByGame();
2020-05-17 22:28:40 +00:00
mission_keyArray.Unload();
mission_data.Unload();
bool search_result = false;
int missionTableId = 0;
for (missionTableId = 0; missionTableId < MissionTextOffsets.size; missionTableId++) {
if (strncmp(MissionTextOffsets.data[missionTableId].szMissionName, MissionTableName, strlen(MissionTextOffsets.data[missionTableId].szMissionName)) == 0) {
search_result = true;
break;
}
}
if (!search_result) {
printf("CText::LoadMissionText - couldn't find %s", MissionTableName);
return;
}
CFileMgr::SetDir("TEXT");
2020-05-21 23:44:01 +00:00
switch (FrontEndMenuManager.m_PrefsLanguage) {
case CMenuManager::LANGUAGE_AMERICAN:
2020-05-17 22:28:40 +00:00
sprintf(filename, "AMERICAN.GXT");
break;
case CMenuManager::LANGUAGE_FRENCH:
2020-05-17 22:28:40 +00:00
sprintf(filename, "FRENCH.GXT");
break;
case CMenuManager::LANGUAGE_GERMAN:
2020-05-17 22:28:40 +00:00
sprintf(filename, "GERMAN.GXT");
break;
case CMenuManager::LANGUAGE_ITALIAN:
2020-05-17 22:28:40 +00:00
sprintf(filename, "ITALIAN.GXT");
break;
case CMenuManager::LANGUAGE_SPANISH:
2020-05-17 22:28:40 +00:00
sprintf(filename, "SPANISH.GXT");
break;
#ifdef MORE_LANGUAGES
2021-02-18 09:34:23 +00:00
case CMenuManager::LANGUAGE_POLISH:
2020-05-17 22:28:40 +00:00
sprintf(filename, "POLISH.GXT");
break;
2021-02-18 09:34:23 +00:00
case CMenuManager::LANGUAGE_RUSSIAN:
2020-05-17 22:28:40 +00:00
sprintf(filename, "RUSSIAN.GXT");
break;
2021-02-18 09:34:23 +00:00
case CMenuManager::LANGUAGE_JAPANESE:
2020-05-17 22:28:40 +00:00
sprintf(filename, "JAPANESE.GXT");
break;
#endif
}
CTimer::Suspend();
int file = CFileMgr::OpenFile(filename, "rb");
CFileMgr::Seek(file, MissionTextOffsets.data[missionTableId].offset, SEEK_SET);
char TableCheck[8];
CFileMgr::Read(file, TableCheck, 8);
if (strncmp(TableCheck, MissionTableName, 8) != 0)
printf("CText::LoadMissionText - expected to find %s in the text file", MissionTableName);
bool tkey_loaded = false, tdat_loaded = false;
ChunkHeader m_ChunkHeader;
while (!tkey_loaded || !tdat_loaded) {
2020-07-22 11:56:28 +00:00
size_t bytes_read = 0;
2020-05-17 22:28:40 +00:00
ReadChunkHeader(&m_ChunkHeader, file, &bytes_read);
if (m_ChunkHeader.size != 0) {
if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) {
2020-07-22 11:56:28 +00:00
size_t bytes_read = 0;
2020-05-17 22:28:40 +00:00
mission_keyArray.Load(m_ChunkHeader.size, file, &bytes_read);
tkey_loaded = true;
} else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) {
2020-07-22 11:56:28 +00:00
size_t bytes_read = 0;
2020-05-17 22:28:40 +00:00
mission_data.Load(m_ChunkHeader.size, file, &bytes_read);
tdat_loaded = true;
} else
CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR);
}
}
mission_keyArray.Update(mission_data.chars);
CFileMgr::CloseFile(file);
CTimer::Resume();
CFileMgr::SetDir("");
strcpy(szMissionTableName, MissionTableName);
bIsMissionTextLoaded = true;
}
2020-04-26 20:49:24 +00:00
void
2020-07-22 11:56:28 +00:00
CKeyArray::Load(size_t length, int file, size_t* offset)
2020-04-26 20:49:24 +00:00
{
2020-05-17 22:28:40 +00:00
char *rawbytes;
2020-04-26 20:49:24 +00:00
2020-07-22 11:56:28 +00:00
// You can make numEntries size_t if you want to exceed 32-bit boundaries, everything else should be ready.
numEntries = (int)(length / sizeof(CKeyEntry));
2020-04-26 20:49:24 +00:00
entries = new CKeyEntry[numEntries];
2020-05-17 22:28:40 +00:00
rawbytes = (char*)entries;
2020-04-26 20:49:24 +00:00
#ifdef THIS_IS_STUPID
2020-10-10 21:56:16 +00:00
for (uint32 i = 0; i < length; i++) {
2020-05-17 22:28:40 +00:00
CFileMgr::Read(file, &rawbytes[i], 1);
2020-10-10 21:56:16 +00:00
(*offset)++;
}
2020-05-17 22:28:40 +00:00
#else
CFileMgr::Read(file, rawbytes, length);
2020-10-10 21:56:16 +00:00
*offset += length;
2020-05-17 22:28:40 +00:00
#endif
2020-04-26 20:49:24 +00:00
}
void
CKeyArray::Unload(void)
{
delete[] entries;
entries = nil;
numEntries = 0;
}
void
CKeyArray::Update(wchar *chars)
{
#if !defined(FIX_BUGS) && !defined(FIX_BUGS_64)
2020-04-26 20:49:24 +00:00
int i;
for(i = 0; i < numEntries; i++)
entries[i].value = (wchar*)((uint8*)chars + (uintptr)entries[i].value);
2020-05-16 10:51:54 +00:00
#endif
2020-04-26 20:49:24 +00:00
}
CKeyEntry*
CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high)
{
int mid;
int diff;
if(low > high)
return nil;
mid = (low + high)/2;
diff = strcmp(key, entries[mid].key);
if(diff == 0)
return &entries[mid];
if(diff < 0)
return BinarySearch(key, entries, low, mid-1);
if(diff > 0)
return BinarySearch(key, entries, mid+1, high);
return nil;
}
wchar*
#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
2020-05-17 22:28:40 +00:00
CKeyArray::Search(const char *key, wchar *data, uint8 *result)
2020-05-16 10:51:54 +00:00
#else
2020-05-17 22:28:40 +00:00
CKeyArray::Search(const char *key, uint8 *result)
2020-05-16 10:51:54 +00:00
#endif
2020-04-26 20:49:24 +00:00
{
CKeyEntry *found;
char errstr[25];
int i;
#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
2020-05-16 10:51:54 +00:00
found = BinarySearch(key, entries, 0, numEntries-1);
2020-05-17 22:28:40 +00:00
if (found) {
*result = true;
2020-05-16 10:51:54 +00:00
return (wchar*)((uint8*)data + found->valueOffset);
2020-05-17 22:28:40 +00:00
}
2020-05-16 10:51:54 +00:00
#else
2020-04-26 20:49:24 +00:00
found = BinarySearch(key, entries, 0, numEntries-1);
2020-05-17 22:28:40 +00:00
if (found) {
*result = true;
2020-04-26 20:49:24 +00:00
return found->value;
2020-05-17 22:28:40 +00:00
}
2020-05-16 10:51:54 +00:00
#endif
2020-05-17 22:28:40 +00:00
*result = false;
#ifdef MASTER
sprintf(errstr, "");
2020-05-17 22:28:40 +00:00
#else
2020-04-26 20:49:24 +00:00
sprintf(errstr, "%s missing", key);
2020-05-17 22:28:40 +00:00
#endif // MASTER
2020-04-26 20:49:24 +00:00
for(i = 0; i < 25; i++)
WideErrorString[i] = errstr[i];
return WideErrorString;
}
void
2020-07-22 11:56:28 +00:00
CData::Load(size_t length, int file, size_t * offset)
2020-04-26 20:49:24 +00:00
{
2020-05-17 22:28:40 +00:00
char *rawbytes;
2020-04-26 20:49:24 +00:00
2020-07-22 11:56:28 +00:00
// You can make numChars size_t if you want to exceed 32-bit boundaries, everything else should be ready.
numChars = (int)(length / sizeof(wchar));
2020-04-26 20:49:24 +00:00
chars = new wchar[numChars];
2020-05-17 22:28:40 +00:00
rawbytes = (char*)chars;
2020-04-26 20:49:24 +00:00
#ifdef THIS_IS_STUPID
2020-10-10 21:56:16 +00:00
for(uint32 i = 0; i < length; i++){
2020-05-17 22:28:40 +00:00
CFileMgr::Read(file, &rawbytes[i], 1);
2020-10-10 21:56:16 +00:00
(*offset)++;
}
2020-05-17 22:28:40 +00:00
#else
CFileMgr::Read(file, rawbytes, length);
2020-10-10 21:56:16 +00:00
*offset += length;
2020-05-17 22:28:40 +00:00
#endif
2020-04-26 20:49:24 +00:00
}
void
CData::Unload(void)
{
delete[] chars;
chars = nil;
numChars = 0;
}
2020-10-11 07:39:04 +00:00
void
2020-07-22 11:56:28 +00:00
CMissionTextOffsets::Load(size_t table_size, int file, size_t *offset, int)
2020-05-17 22:28:40 +00:00
{
#ifdef THIS_IS_STUPID
2020-10-10 21:56:16 +00:00
size_t num_of_entries = table_size / sizeof(CMissionTextOffsets::Entry);
for (size_t mi = 0; mi < num_of_entries; mi++) {
for (uint32 i = 0; i < sizeof(data[mi].szMissionName); i++) {
CFileMgr::Read(file, &data[i].szMissionName[i], 1);
(*offset)++;
}
char* _buf = (char*)&data[mi].offset;
for (uint32 i = 0; i < sizeof(data[mi].offset); i++) {
CFileMgr::Read(file, &_buf[i], 1);
(*offset)++;
}
}
size = (uint16)num_of_entries;
#else
2020-05-17 22:28:40 +00:00
// not exact VC code but smaller and better :P
2020-07-22 11:56:28 +00:00
// You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready.
size = (uint16) (table_size / sizeof(CMissionTextOffsets::Entry));
2020-05-17 22:28:40 +00:00
CFileMgr::Read(file, (char*)data, sizeof(CMissionTextOffsets::Entry) * size);
*offset += sizeof(CMissionTextOffsets::Entry) * size;
2020-10-10 21:56:16 +00:00
#endif
2020-05-17 22:28:40 +00:00
}
2020-04-26 20:49:24 +00:00
char*
UnicodeToAscii(wchar *src)
{
static char aStr[256];
int len;
for(len = 0; *src != '\0' && len < 256-1; len++, src++)
#ifdef MORE_LANGUAGES
if(*src < 128 || ((CGame::russianGame || CGame::japaneseGame) && *src < 256))
#else
if(*src < 128)
#endif
aStr[len] = *src;
2020-10-10 21:56:16 +00:00
// convert to CP1252
else if(*src <= 131)
aStr[len] = *src + 64;
else if (*src <= 141)
aStr[len] = *src + 66;
else if (*src <= 145)
aStr[len] = *src + 68;
else if (*src <= 149)
aStr[len] = *src + 71;
else if (*src <= 154)
aStr[len] = *src + 73;
else if (*src <= 164)
aStr[len] = *src + 75;
else if (*src <= 168)
aStr[len] = *src + 77;
else if (*src <= 204)
aStr[len] = *src + 80;
else switch (*src) {
case 205: aStr[len] = 209; break;
case 206: aStr[len] = 241; break;
case 207: aStr[len] = 191; break;
default: aStr[len] = '#'; break;
}
2020-04-26 20:49:24 +00:00
aStr[len] = '\0';
return aStr;
}
char*
UnicodeToAsciiForSaveLoad(wchar *src)
{
static char aStr[256];
int len;
2020-10-10 21:56:16 +00:00
for(len = 0; *src != '\0' && len < 256; len++, src++)
2020-04-26 20:49:24 +00:00
if(*src < 256)
aStr[len] = *src;
else
aStr[len] = '#';
aStr[len] = '\0';
return aStr;
}
2020-06-27 21:01:51 +00:00
char*
UnicodeToAsciiForMemoryCard(wchar *src)
{
static char aStr[256];
int len;
for(len = 0; *src != '\0' && len < 256; len++, src++)
2020-06-28 05:32:56 +00:00
if(*src < 256)
2020-06-27 21:01:51 +00:00
aStr[len] = *src;
else
aStr[len] = '#';
aStr[len] = '\0';
return aStr;
}
2020-04-26 20:49:24 +00:00
void
TextCopy(wchar *dst, const wchar *src)
{
while((*dst++ = *src++) != '\0');
2020-10-10 15:19:49 +00:00
}