diff --git a/src/localisation/LanguagePack.cpp b/src/localisation/LanguagePack.cpp index e2a9ca60ad..cacc01da88 100644 --- a/src/localisation/LanguagePack.cpp +++ b/src/localisation/LanguagePack.cpp @@ -13,6 +13,9 @@ extern "C" { constexpr rct_string_id ObjectOverrideBase = 0x6000; constexpr int ObjectOverrideMaxStringCount = 4; +constexpr rct_string_id ScenarioOverrideBase = 0x7000; +constexpr int ScenarioOverrideMaxStringCount = 3; + LanguagePack *LanguagePack::FromFile(int id, const utf8 *path) { assert(path != NULL); @@ -54,6 +57,7 @@ LanguagePack::LanguagePack(int id, const utf8 *text) _stringData = NULL; _currentGroup = NULL; _currentObjectOverride = NULL; + _currentScenarioOverride = NULL; auto reader = UTF8StringReader(text); while (reader.CanRead()) { @@ -74,6 +78,14 @@ LanguagePack::LanguagePack(int id, const utf8 *text) } } } + for (size_t i = 0; i < _scenarioOverrides.size(); i++) { + for (int j = 0; j < ScenarioOverrideMaxStringCount; j++) { + const utf8 **strPtr = &(_scenarioOverrides[i].strings[j]); + if (*strPtr != NULL) { + *strPtr = (utf8*)(stringDataBaseAddress + (size_t)*strPtr); + } + } + } // Destruct the string builder to free memory _stringDataSB = StringBuilder(); @@ -86,7 +98,17 @@ LanguagePack::~LanguagePack() } const utf8 *LanguagePack::GetString(int stringId) const { - if (stringId >= ObjectOverrideBase) { + if (stringId >= ScenarioOverrideBase) { + int offset = stringId - ScenarioOverrideBase; + int ooIndex = offset / ScenarioOverrideMaxStringCount; + int ooStringIndex = offset % ScenarioOverrideMaxStringCount; + + if (_scenarioOverrides.size() > (size_t)ooIndex) { + return _scenarioOverrides[ooIndex].strings[ooStringIndex]; + } else { + return NULL; + } + }else if (stringId >= ObjectOverrideBase) { int offset = stringId - ObjectOverrideBase; int ooIndex = offset / ObjectOverrideMaxStringCount; int ooStringIndex = offset % ObjectOverrideMaxStringCount; @@ -124,6 +146,25 @@ rct_string_id LanguagePack::GetObjectOverrideStringId(const char *objectIdentifi return STR_NONE; } +rct_string_id LanguagePack::GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index) +{ + assert(scenarioFilename != NULL); + assert(index < ScenarioOverrideMaxStringCount); + + int ooIndex = 0; + for (const ScenarioOverride &scenarioOverride : _scenarioOverrides) { + if (_stricmp(scenarioOverride.filename, scenarioFilename) == 0) { + if (scenarioOverride.strings[index] == NULL) { + return STR_NONE; + } + return ScenarioOverrideBase + (ooIndex * ScenarioOverrideMaxStringCount) + index; + } + ooIndex++; + } + + return STR_NONE; +} + LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *objectIdentifier) { assert(objectIdentifier != NULL); @@ -137,6 +178,19 @@ LanguagePack::ObjectOverride *LanguagePack::GetObjectOverride(const char *object return false; } +LanguagePack::ScenarioOverride *LanguagePack::GetScenarioOverride(const utf8 *scenarioIdentifier) +{ + assert(scenarioIdentifier != NULL); + + for (size_t i = 0; i < _scenarioOverrides.size(); i++) { + ScenarioOverride *so = &_scenarioOverrides[i]; + if (_stricmp(so->name, scenarioIdentifier) == 0) { + return so; + } + } + return false; +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Parsing //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -209,7 +263,10 @@ void LanguagePack::ParseLine(IStringReader *reader) SkipToEndOfLine(reader); break; case '[': - ParseGroup(reader); + ParseGroupObject(reader); + break; + case '<': + ParseGroupScenario(reader); break; case '\r': case '\n': @@ -223,7 +280,7 @@ void LanguagePack::ParseLine(IStringReader *reader) } } -void LanguagePack::ParseGroup(IStringReader *reader) +void LanguagePack::ParseGroupObject(IStringReader *reader) { auto sb = StringBuilder(); int codepoint; @@ -253,6 +310,7 @@ void LanguagePack::ParseGroup(IStringReader *reader) if (sb.GetLength() == 8) { _currentGroup = sb.GetString(); _currentObjectOverride = GetObjectOverride(_currentGroup); + _currentScenarioOverride = NULL; if (_currentObjectOverride == NULL) { _objectOverrides.push_back(ObjectOverride()); _currentObjectOverride = &_objectOverrides[_objectOverrides.size() - 1]; @@ -263,6 +321,42 @@ void LanguagePack::ParseGroup(IStringReader *reader) } } +void LanguagePack::ParseGroupScenario(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->TryPeek(&codepoint)) { + if (IsNewLine(codepoint)) break; + + reader->Skip(); + if (codepoint == '>') { + closedCorrectly = true; + break; + } + sb.Append(codepoint); + } + + if (closedCorrectly) { + SafeFree(_currentGroup); + + _currentGroup = sb.GetString(); + _currentObjectOverride = NULL; + _currentScenarioOverride = GetScenarioOverride(_currentGroup); + if (_currentScenarioOverride == NULL) { + _scenarioOverrides.push_back(ScenarioOverride()); + _currentScenarioOverride = &_scenarioOverrides[_scenarioOverrides.size() - 1]; + memset(_currentScenarioOverride, 0, sizeof(ObjectOverride)); + _currentScenarioOverride->filename = sb.GetString(); + } + } +} + void LanguagePack::ParseString(IStringReader *reader) { auto sb = StringBuilder(); @@ -344,7 +438,11 @@ void LanguagePack::ParseString(IStringReader *reader) _strings[stringId] = relativeOffset; } else { - _currentObjectOverride->strings[stringId] = relativeOffset; + if (_currentObjectOverride != NULL) { + _currentObjectOverride->strings[stringId] = relativeOffset; + } else { + _currentScenarioOverride->strings[stringId] = relativeOffset; + } } _stringDataSB.Append(sb.GetBuffer()); diff --git a/src/localisation/LanguagePack.h b/src/localisation/LanguagePack.h index ba09b387a5..56a3479184 100644 --- a/src/localisation/LanguagePack.h +++ b/src/localisation/LanguagePack.h @@ -30,6 +30,7 @@ public: } rct_string_id GetObjectOverrideStringId(const char *objectIdentifier, int index); + rct_string_id GetScenarioOverrideStringId(const utf8 *scenarioFilename, int index); private: struct ObjectOverride { @@ -37,13 +38,27 @@ private: const utf8 *strings[4]; }; + struct ScenarioOverride { + const utf8 *filename; + union { + const utf8 *strings[3]; + struct { + const utf8 *name; + const utf8 *park; + const utf8 *details; + }; + }; + }; + int _id; utf8 *_stringData; std::vector _strings; std::vector _objectOverrides; + std::vector _scenarioOverrides; LanguagePack(int id, const utf8 *text); ObjectOverride *GetObjectOverride(const char *objectIdentifier); + ScenarioOverride *GetScenarioOverride(const utf8 *scenarioFilename); /////////////////////////////////////////////////////////////////////////// // Parsing @@ -51,9 +66,11 @@ private: StringBuilder _stringDataSB; utf8 *_currentGroup; ObjectOverride *_currentObjectOverride; + ScenarioOverride *_currentScenarioOverride; void ParseLine(IStringReader *reader); - void ParseGroup(IStringReader *reader); + void ParseGroupObject(IStringReader *reader); + void ParseGroupScenario(IStringReader *reader); void ParseString(IStringReader *reader); bool ParseToken(IStringReader *reader, uint32 *token); diff --git a/src/localisation/language.cpp b/src/localisation/language.cpp index 1f7904072f..5d3323408a 100644 --- a/src/localisation/language.cpp +++ b/src/localisation/language.cpp @@ -385,4 +385,15 @@ rct_string_id object_get_localised_text(uint8_t** pStringTable/*ebp*/, int type/ } } +bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds) +{ + outStringIds[0] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 0); + outStringIds[1] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 1); + outStringIds[2] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 2); + return + outStringIds[0] != (rct_string_id)STR_NONE || + outStringIds[1] != (rct_string_id)STR_NONE || + outStringIds[2] != (rct_string_id)STR_NONE; +} + } diff --git a/src/localisation/language.h b/src/localisation/language.h index 66b7928306..4cc301146e 100644 --- a/src/localisation/language.h +++ b/src/localisation/language.h @@ -82,4 +82,6 @@ int utf8_length(const utf8 *text); wchar_t *utf8_to_widechar(const utf8 *src); utf8 *widechar_to_utf8(const wchar_t *src); +bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds); + #endif diff --git a/src/scenario.c b/src/scenario.c index 0a8ddebea0..d8260bae5f 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -73,14 +73,29 @@ int scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *in SDL_RWclose(rw); RCT2_GLOBAL(0x009AA00C, uint8) = 0; - // Checks for a scenario string object (possibly for localisation) - if ((info->entry.flags & 0xFF) != 255) { - if (object_get_scenario_text(&info->entry)) { - rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*); - format_string(info->name, stex_entry->scenario_name, NULL); - format_string(info->details, stex_entry->details, NULL); - RCT2_GLOBAL(0x009AA00C, uint8) = stex_entry->var_06; - object_free_scenario_text(); + // Get filename + utf8 filename[MAX_PATH]; + strcpy(filename, path_get_filename(path)); + path_remove_extension(filename); + + rct_string_id localisedStringIds[3]; + if (language_get_localised_scenario_strings(filename, localisedStringIds)) { + if (localisedStringIds[0] != (rct_string_id)STR_NONE) { + strncpy(info->name, language_get_string(localisedStringIds[0]), 64); + } + if (localisedStringIds[2] != (rct_string_id)STR_NONE) { + strncpy(info->details, language_get_string(localisedStringIds[2]), 256); + } + } else { + // Checks for a scenario string object (possibly for localisation) + if ((info->entry.flags & 0xFF) != 255) { + if (object_get_scenario_text(&info->entry)) { + rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*); + format_string(info->name, stex_entry->scenario_name, NULL); + format_string(info->details, stex_entry->details, NULL); + RCT2_GLOBAL(0x009AA00C, uint8) = stex_entry->var_06; + object_free_scenario_text(); + } } } return 1; @@ -282,23 +297,45 @@ void scenario_begin() strcpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, s6Info->details); strcpy((char*)RCT2_ADDRESS_SCENARIO_NAME, s6Info->name); - rct_stex_entry* stex = g_stexEntries[0]; - if ((int)stex != -1) { - char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER; + { + // Get filename + utf8 filename[MAX_PATH]; + strcpy(filename, _scenarioFileName); + path_remove_extension(filename); - // Set localised park name - format_string(buffer, stex->park_name, 0); - park_set_name(buffer); + rct_string_id localisedStringIds[3]; + if (language_get_localised_scenario_strings(filename, localisedStringIds)) { + if (localisedStringIds[0] != (rct_string_id)STR_NONE) { + strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, language_get_string(localisedStringIds[0]), 31); + ((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0'; + } + if (localisedStringIds[1] != (rct_string_id)STR_NONE) { + park_set_name(language_get_string(localisedStringIds[1])); + } + if (localisedStringIds[2] != (rct_string_id)STR_NONE) { + strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, language_get_string(localisedStringIds[2]), 255); + ((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0'; + } + } else { + rct_stex_entry* stex = g_stexEntries[0]; + if ((int)stex != -1) { + char *buffer = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER; - // Set localised scenario name - format_string(buffer, stex->scenario_name, 0); - strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31); - ((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0'; + // Set localised park name + format_string(buffer, stex->park_name, 0); + park_set_name(buffer); - // Set localised scenario details - format_string(buffer, stex->details, 0); - strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, buffer, 255); - ((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0'; + // Set localised scenario name + format_string(buffer, stex->scenario_name, 0); + strncpy((char*)RCT2_ADDRESS_SCENARIO_NAME, buffer, 31); + ((char*)RCT2_ADDRESS_SCENARIO_NAME)[31] = '\0'; + + // Set localised scenario details + format_string(buffer, stex->details, 0); + strncpy((char*)RCT2_ADDRESS_SCENARIO_DETAILS, buffer, 255); + ((char*)RCT2_ADDRESS_SCENARIO_DETAILS)[255] = '\0'; + } + } } // Set the last saved game path