From 952a4abdd1bc6f97efbde4921d842d54207fe4c9 Mon Sep 17 00:00:00 2001 From: IntelOrca Date: Mon, 31 Aug 2015 23:06:11 +0100 Subject: [PATCH] implement DAT translation, part 1 --- projects/openrct2.vcxproj | 2 + projects/openrct2.vcxproj.filters | 6 + src/localisation/LanguagePack.cpp | 200 ++++++++++++++++++++++++++++++ src/localisation/LanguagePack.h | 94 ++++++++++++++ src/localisation/language.c | 96 +++++++++----- 5 files changed, 370 insertions(+), 28 deletions(-) create mode 100644 src/localisation/LanguagePack.cpp create mode 100644 src/localisation/LanguagePack.h diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index 67022bd317..688622c8a5 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -65,6 +65,7 @@ + @@ -235,6 +236,7 @@ + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index b82c90de10..9cd193edaf 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -537,6 +537,9 @@ Source\World + + Source\Localisation + @@ -788,5 +791,8 @@ Source\Core + + Source\Localisation + \ No newline at end of file diff --git a/src/localisation/LanguagePack.cpp b/src/localisation/LanguagePack.cpp new file mode 100644 index 0000000000..a9deaa3781 --- /dev/null +++ b/src/localisation/LanguagePack.cpp @@ -0,0 +1,200 @@ +extern "C" { + #include "../common.h" + #include "../util/util.h" + #include "localisation.h" +} + +#include "LanguagePack.h" + +#include + +// TODO Move to separate file +class StringBuilder final { +public: + StringBuilder() + { + _buffer = NULL; + _capacity = 0; + _length = 0; + } + + StringBuilder(int capacity) : StringBuilder() + { + EnsureCapacity(capacity); + } + + ~StringBuilder() + { + if (_buffer != NULL) free(_buffer); + } + + utf8 *GetString() const + { + utf8 *result = (utf8*)malloc(_length + 1); + memcpy(result, _buffer, _length); + result[_length] = 0; + return result; + } + + void Append(int codepoint) + { + int codepointLength = utf8_get_codepoint_length(codepoint); + EnsureCapacity(_length + codepointLength + 1); + utf8_write_codepoint(_buffer + _length, codepoint); + _length += codepointLength; + } + + void Append(utf8 *text) + { + int textLength = strlen(text); + + EnsureCapacity(_length + textLength + 1); + memcpy(_buffer + _length, text, textLength); + _length += textLength; + } + +private: + utf8 *_buffer; + size_t _capacity; + size_t _length; + + void EnsureCapacity(size_t capacity) + { + if (_capacity > capacity) return; + + _capacity = max(8, _capacity); + while (_capacity < capacity) { + _capacity *= 2; + } + + if (_buffer == NULL) { + _buffer = (utf8*)malloc(_capacity); + } else { + _buffer = (utf8*)realloc(_buffer, _capacity); + } + } +}; + +LanguagePack *LanguagePack::FromFile(int id, const utf8 *path) +{ + assert(path != NULL); + + int fileLength; + utf8 *fileData; + + // Load file directly into memory + SDL_RWops *file = SDL_RWFromFile(path, "rb"); + if (file == NULL) { + log_error("Unable to open %s", path); + return NULL; + } + + fileLength = (int)SDL_RWsize(file); + fileData = (utf8*)malloc(fileLength); + SDL_RWread(file, fileData, fileLength, 1); + SDL_RWclose(file); + + // Parse the memory as text + LanguagePack *result = FromText(id, fileData); + free(fileData); + + return result; +} + +LanguagePack *LanguagePack::FromText(int id, const utf8 *text) +{ + return new LanguagePack(id, text); +} + +LanguagePack::LanguagePack(int id, const utf8 *text) +{ + assert(text != NULL); + + _id = id; + _stringData = NULL; + _currentGroup = NULL; + + auto reader = UTF8StringReader(text); + +} + +LanguagePack::~LanguagePack() +{ + SafeFree(_stringData); + SafeFree(_currentGroup); +} + +static void SkipWhitespace(IStringReader *reader) +{ + int codepoint; + while (reader->TryPeek(&codepoint)) { + if (codepoint == '\t' || codepoint == ' ' || codepoint == '\r' || codepoint == '\n') { + reader->Skip(); + } else { + break; + } + } +} + +static void SkipToEndOfLine(IStringReader *reader) +{ + int codepoint; + while (reader->TryPeek(&codepoint)) { + if (codepoint != '\r' && codepoint != '\n') { + reader->Skip(); + } else { + break; + } + } +} + +void LanguagePack::ParseLine(IStringReader *reader) +{ + SkipWhitespace(reader); + + int codepoint; + if (!reader->TryPeek(&codepoint)) + return; + + switch (codepoint) { + case '#': + SkipToEndOfLine(reader); + break; + case '[': + ParseGroup(reader); + break; + default: + ParseString(reader); + break; + } +} + +void LanguagePack::ParseGroup(IStringReader *reader) +{ + auto sb = StringBuilder(); + int codepoint; + + // Should have already deduced that the next codepoint is a [ + reader->Skip(); + + // Read string up to ] or line end + bool closedCorrectly = false; + while (reader->TryRead(&codepoint)) { + if (codepoint == '\n' || codepoint == '\r') break; + if (codepoint == ']') { + closedCorrectly = true; + break; + } + sb.Append(codepoint); + } + + if (closedCorrectly) { + SafeFree(_currentGroup); + _currentGroup = sb.GetString(); + } +} + +void LanguagePack::ParseString(IStringReader *reader) +{ + +} diff --git a/src/localisation/LanguagePack.h b/src/localisation/LanguagePack.h new file mode 100644 index 0000000000..a66bc02093 --- /dev/null +++ b/src/localisation/LanguagePack.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +extern "C" { + #include "../common.h" + #include "../util/util.h" + #include "localisation.h" +} + +struct IStringReader abstract { + virtual bool TryPeek(int *outCodepoint) abstract; + virtual bool TryRead(int *outCodepoint) abstract; + virtual void Skip() abstract; +}; + +// TODO Move to separate file in Core +class UTF8StringReader final : public IStringReader { +public: + UTF8StringReader(const utf8 *text) + { + // Skip UTF-8 byte order mark + if (strlen(text) >= 3 && utf8_is_bom(text)) { + text += 3; + } + + _text = text; + _current = text; + } + + bool TryPeek(int *outCodepoint) override + { + if (_current == NULL) return false; + + int codepoint = utf8_get_next(_current, NULL); + *outCodepoint = codepoint; + return true; + } + + bool TryRead(int *outCodepoint) override + { + if (_current == NULL) return false; + + int codepoint = utf8_get_next(_current, &_current); + *outCodepoint = codepoint; + if (codepoint == 0) { + _current = NULL; + return false; + } + return true; + } + + void Skip() override + { + int codepoint; + TryRead(&codepoint); + } + +private: + const utf8 *_text; + const utf8 *_current; +}; + +class LanguagePack final { +public: + static LanguagePack *FromFile(int id, const utf8 *path); + static LanguagePack *FromText(int id, const utf8 *text); + + ~LanguagePack(); + + int GetId() const { return _id; } + int GetCount() const { return _strings.size(); } + +private: + struct ObjectOverride { + char name[8]; + const utf8 *strings[4]; + }; + + int _id; + utf8 *_stringData; + std::vector _strings; + + LanguagePack(int id, const utf8 *text); + + /////////////////////////////////////////////////////////////////////////// + // Parsing + /////////////////////////////////////////////////////////////////////////// + utf8 *_currentGroup; + + void ParseLine(IStringReader *reader); + void ParseGroup(IStringReader *reader); + void ParseString(IStringReader *reader); +}; diff --git a/src/localisation/language.c b/src/localisation/language.c index 089f4f25b9..d2bc8557fe 100644 --- a/src/localisation/language.c +++ b/src/localisation/language.c @@ -228,6 +228,8 @@ static int language_open_file(const utf8 *filename, language_data *language) char *dst = NULL; char *token = NULL; char tokenBuffer[64]; + char groupBuffer[64]; + int groupLength = 0; int stringIndex = 0, mode = 0, stringId, maxStringId = 0; size_t i = 0; @@ -248,18 +250,39 @@ static int language_open_file(const utf8 *filename, language_data *language) // Search for a comment if (utf8Char == '#') { mode = 3; - } else if (utf8Char == ':' && stringId != -1) { - // Search for colon - dst = src + 1; - language->strings[stringId] = dst; - stringIndex++; - mode = 1; - } else if (!strncmp(src, "STR_", 4)){ - // Copy in the string number, 4 characters only - if (sscanf(src, "STR_%4d", &stringId) != 1) { - stringId = -1; - } else { - maxStringId = max(maxStringId, stringId); + } else if (utf8Char == '[') { + mode = 4; + } + + if (groupLength == 0) { + if (utf8Char == ':' && stringId != -1) { + // Search for colon + dst = src + 1; + language->strings[stringId] = dst; + stringIndex++; + mode = 1; + } else if (!strncmp(src, "STR_", 4)) { + // Copy in the string number, 4 characters only + if (sscanf(src, "STR_%4d", &stringId) != 1) { + stringId = -1; + } else { + maxStringId = max(maxStringId, stringId); + } + } + } else { + if (utf8Char == ':' && stringId != -1) { + // Search for colon + dst = src + 1; + language->strings[stringId] = dst; + stringIndex++; + mode = 1; + } else if (!strncmp(src, "STR_", 4)) { + // Copy in the string number, 4 characters only + if (sscanf(src, "STR_%4d", &stringId) != 1) { + stringId = -1; + } else { + maxStringId = max(maxStringId, stringId); + } } } break; @@ -295,6 +318,21 @@ static int language_open_file(const utf8 *filename, language_data *language) if (utf8Char == '\n' || utf8Char == '\r') { mode = 0; } + break; + case 4: + if (utf8Char == '\n' || utf8Char == '\r') { + groupLength = 0; + mode = 0; + } else if (utf8Char == ']') { + mode = 3; + } else { + if (groupLength < sizeof(groupBuffer) - 1) { + groupBuffer[groupLength + 0] = utf8Char; + groupBuffer[groupLength + 1] = 0; + groupLength++; + } + } + break; } } language->num_strings = maxStringId + 1; @@ -311,21 +349,6 @@ static void language_close(language_data *language) language->string_data_size = 0; } -const int OpenRCT2LangIdToObjectLangId[] = { - 0, - 0, - 1, - 3, - 6, - 2, - 0, - 0, - 4, - 7, - 5, - 13 -}; - #define STEX_BASE_STRING_ID 3447 #define NONSTEX_BASE_STRING_ID 3463 #define MAX_OBJECT_CACHED_STRINGS 2048 @@ -420,7 +443,7 @@ rct_string_id object_get_localised_text(uint8_t** pStringTable/*ebp*/, int type/ char *pString = NULL; int result = 0; bool isBlank; - + while ((languageId = *(*pStringTable)++) != RCT2_LANGUAGE_ID_END) { isBlank = true; @@ -461,6 +484,23 @@ rct_string_id object_get_localised_text(uint8_t** pStringTable/*ebp*/, int type/ while (*(*pStringTable)++ != 0); } + if (RCT2_GLOBAL(0x009ADAFC, uint8) == 0) { + if (type == OBJECT_TYPE_RIDE) { + char name[9]; + memcpy(name, object_entry_groups[type].entries[index].name, 8); + name[8] = 0; + + if (strcmp(name, "MGR1 ") == 0) { + switch (tableindex) { + case 0: return 824; + case 1: return 2142; + } + } + } + } else { + + } + // If not scenario text if (RCT2_GLOBAL(0x009ADAFC, uint8) == 0) { int stringid = NONSTEX_BASE_STRING_ID;