diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp
index faf6cdd31b..975bdd46fd 100644
--- a/src/openrct2/Game.cpp
+++ b/src/openrct2/Game.cpp
@@ -38,7 +38,6 @@
#include "interface/Screenshot.h"
#include "interface/Viewport.h"
#include "interface/Window.h"
-#include "localisation/Localisation.h"
#include "management/Finance.h"
#include "management/Marketing.h"
#include "management/Research.h"
@@ -48,6 +47,7 @@
#include "object/ObjectList.h"
#include "object/WaterEntry.h"
#include "platform/Platform.h"
+#include "rct12/CSStringConverter.h"
#include "ride/Ride.h"
#include "ride/RideRatings.h"
#include "ride/Station.h"
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index 76a0ec26bb..6261afd5a4 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -282,7 +282,6 @@
-
@@ -388,6 +387,7 @@
+
@@ -811,8 +811,6 @@
-
-
@@ -906,6 +904,7 @@
+
diff --git a/src/openrct2/localisation/Convert.cpp b/src/openrct2/localisation/Convert.cpp
deleted file mode 100644
index 77c50b1853..0000000000
--- a/src/openrct2/localisation/Convert.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*****************************************************************************
- * Copyright (c) 2014-2024 OpenRCT2 developers
- *
- * For a complete list of all authors, please refer to contributors.md
- * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
- *
- * OpenRCT2 is licensed under the GNU General Public License version 3.
- *****************************************************************************/
-
-#include "../core/String.hpp"
-#include "ConversionTables.h"
-#include "Language.h"
-
-#include
-#include
-
-/**
- * Decodes an RCT2 string to a wide char string still in the original code page.
- * An RCT2 string is a multi-byte string where every two-byte code point is preceded with a byte value of 255.
- */
-static std::wstring DecodeToWideChar(std::string_view src)
-{
- std::wstring decoded;
- decoded.reserve(src.size());
- for (auto it = src.begin(); it != src.end();)
- {
- uint8_t c = *it++;
- if (c == 255)
- {
- // Push next two characters
- uint8_t a = 0;
- uint8_t b = 0;
- if (it != src.end())
- {
- a = *it++;
- if (it != src.end())
- {
- b = *it++;
- }
- else
- {
- // 2nd byte for double byte character is missing
- break;
- }
- }
- else
- {
- // 1st byte for double byte character is missing
- break;
- }
-
- wchar_t cp = (a << 8) | b;
- decoded.push_back(cp);
- }
- else
- {
- // Push character
- decoded.push_back(c);
- }
- }
- return decoded;
-}
-
-static std::string DecodeToMultiByte(std::string_view src)
-{
- auto wide = DecodeToWideChar(src);
- std::string result;
- result.reserve(wide.size());
- for (auto cc : wide)
- {
- if (cc <= 255)
- {
- result.push_back(cc);
- }
- else
- {
- result.push_back((cc >> 8) & 0xFF);
- result.push_back(cc & 0xFF);
- }
- }
- return result;
-}
-
-static int32_t GetCodePageForRCT2Language(RCT2LanguageId languageId)
-{
- switch (languageId)
- {
- case RCT2LanguageId::Japanese:
- return OpenRCT2::CodePage::CP_932;
- case RCT2LanguageId::ChineseSimplified:
- return OpenRCT2::CodePage::CP_936;
- case RCT2LanguageId::Korean:
- return OpenRCT2::CodePage::CP_949;
- case RCT2LanguageId::ChineseTraditional:
- return OpenRCT2::CodePage::CP_950;
- default:
- return OpenRCT2::CodePage::CP_1252;
- }
-}
-
-template static std::string DecodeConvertWithTable(std::string_view src, TConvertFunc func)
-{
- auto decoded = DecodeToWideChar(src);
- std::wstring u16;
- u16.reserve(decoded.size());
- for (auto cc : decoded)
- {
- u16.push_back(func(cc));
- }
- return String::ToUtf8(u16);
-}
-
-std::string RCT2StringToUTF8(std::string_view src, RCT2LanguageId languageId)
-{
- auto codePage = GetCodePageForRCT2Language(languageId);
- if (codePage == OpenRCT2::CodePage::CP_1252)
- {
- // The code page used by RCT2 was not quite 1252 as some codes were used for Polish characters.
- return DecodeConvertWithTable(src, EncodingConvertRCT2ToUnicode);
- }
-
- auto decoded = DecodeToMultiByte(src);
- return String::ConvertToUtf8(decoded, codePage);
-}
diff --git a/src/openrct2/localisation/Language.h b/src/openrct2/localisation/Language.h
index b4214dd162..c679b5f95b 100644
--- a/src/openrct2/localisation/Language.h
+++ b/src/openrct2/localisation/Language.h
@@ -14,7 +14,6 @@
#include "../localisation/StringIdType.h"
#include
-#include
enum
{
@@ -49,7 +48,7 @@ enum
LANGUAGE_COUNT
};
-enum class RCT2LanguageId
+enum class RCT2LanguageId : uint8_t
{
EnglishUK,
EnglishUS,
@@ -89,7 +88,6 @@ uint8_t LanguageGetIDFromLocale(const char* locale);
const char* LanguageGetString(StringId id);
bool LanguageOpen(int32_t id);
-std::string RCT2StringToUTF8(std::string_view src, RCT2LanguageId languageId);
bool LanguageGetLocalisedScenarioStrings(const utf8* scenarioFilename, StringId* outStringIds);
void LanguageFreeObjectString(StringId stringId);
StringId LanguageAllocateObjectString(const std::string& target);
diff --git a/src/openrct2/object/StringTable.cpp b/src/openrct2/object/StringTable.cpp
index 6130c4b3c6..bbc3f64398 100644
--- a/src/openrct2/object/StringTable.cpp
+++ b/src/openrct2/object/StringTable.cpp
@@ -13,9 +13,8 @@
#include "../core/IStream.hpp"
#include "../core/Json.hpp"
#include "../core/String.hpp"
-#include "../localisation/Language.h"
-#include "../localisation/LanguagePack.h"
#include "../localisation/LocalisationService.h"
+#include "../rct12/CSStringConverter.h"
#include "Object.h"
static constexpr uint8_t RCT2ToOpenRCT2LanguageId[] = {
diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp
index cabdb9ec21..ade14719de 100644
--- a/src/openrct2/rct1/S4Importer.cpp
+++ b/src/openrct2/rct1/S4Importer.cpp
@@ -38,7 +38,6 @@
#include "../entity/Staff.h"
#include "../interface/Window.h"
#include "../localisation/Date.h"
-#include "../localisation/Localisation.h"
#include "../management/Award.h"
#include "../management/Finance.h"
#include "../management/Marketing.h"
@@ -49,6 +48,7 @@
#include "../object/ObjectRepository.h"
#include "../peep/PeepAnimationData.h"
#include "../peep/RideUseSystem.h"
+#include "../rct12/CSStringConverter.h"
#include "../rct12/EntryList.h"
#include "../ride/RideData.h"
#include "../ride/Station.h"
diff --git a/src/openrct2/localisation/ConversionTables.cpp b/src/openrct2/rct12/CSStringConverter.cpp
similarity index 58%
rename from src/openrct2/localisation/ConversionTables.cpp
rename to src/openrct2/rct12/CSStringConverter.cpp
index 4643628a97..a9e7229135 100644
--- a/src/openrct2/localisation/ConversionTables.cpp
+++ b/src/openrct2/rct12/CSStringConverter.cpp
@@ -7,12 +7,16 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
-#include "ConversionTables.h"
+#include "CSStringConverter.h"
-#include "FormatCodes.h"
+#include "../core/String.hpp"
+#include "../localisation/FormatCodes.h"
+#include "../localisation/Language.h"
#include
#include
+#include
+#include
struct EncodingConvertEntry
{
@@ -124,3 +128,112 @@ wchar_t EncodingConvertRCT2ToUnicode(wchar_t rct2str)
return rct2str;
return entry->unicode;
}
+
+/**
+ * Decodes an RCT2 string to a wide char string still in the original code page.
+ * An RCT2 string is a multi-byte string where every two-byte code point is preceded with a byte value of 255.
+ */
+static std::wstring DecodeToWideChar(std::string_view src)
+{
+ std::wstring decoded;
+ decoded.reserve(src.size());
+ for (auto it = src.begin(); it != src.end();)
+ {
+ uint8_t c = *it++;
+ if (c == 255)
+ {
+ // Push next two characters
+ uint8_t a = 0;
+ uint8_t b = 0;
+ if (it != src.end())
+ {
+ a = *it++;
+ if (it != src.end())
+ {
+ b = *it++;
+ }
+ else
+ {
+ // 2nd byte for double byte character is missing
+ break;
+ }
+ }
+ else
+ {
+ // 1st byte for double byte character is missing
+ break;
+ }
+
+ wchar_t cp = (a << 8) | b;
+ decoded.push_back(cp);
+ }
+ else
+ {
+ // Push character
+ decoded.push_back(c);
+ }
+ }
+ return decoded;
+}
+
+static std::string DecodeToMultiByte(std::string_view src)
+{
+ auto wide = DecodeToWideChar(src);
+ std::string result;
+ result.reserve(wide.size());
+ for (auto cc : wide)
+ {
+ if (cc <= 255)
+ {
+ result.push_back(cc);
+ }
+ else
+ {
+ result.push_back((cc >> 8) & 0xFF);
+ result.push_back(cc & 0xFF);
+ }
+ }
+ return result;
+}
+
+static int32_t GetCodePageForRCT2Language(RCT2LanguageId languageId)
+{
+ switch (languageId)
+ {
+ case RCT2LanguageId::Japanese:
+ return OpenRCT2::CodePage::CP_932;
+ case RCT2LanguageId::ChineseSimplified:
+ return OpenRCT2::CodePage::CP_936;
+ case RCT2LanguageId::Korean:
+ return OpenRCT2::CodePage::CP_949;
+ case RCT2LanguageId::ChineseTraditional:
+ return OpenRCT2::CodePage::CP_950;
+ default:
+ return OpenRCT2::CodePage::CP_1252;
+ }
+}
+
+template static std::string DecodeConvertWithTable(std::string_view src, TConvertFunc func)
+{
+ auto decoded = DecodeToWideChar(src);
+ std::wstring u16;
+ u16.reserve(decoded.size());
+ for (auto cc : decoded)
+ {
+ u16.push_back(func(cc));
+ }
+ return String::ToUtf8(u16);
+}
+
+std::string RCT2StringToUTF8(std::string_view src, RCT2LanguageId languageId)
+{
+ auto codePage = GetCodePageForRCT2Language(languageId);
+ if (codePage == OpenRCT2::CodePage::CP_1252)
+ {
+ // The code page used by RCT2 was not quite 1252 as some codes were used for Polish characters.
+ return DecodeConvertWithTable(src, EncodingConvertRCT2ToUnicode);
+ }
+
+ auto decoded = DecodeToMultiByte(src);
+ return String::ConvertToUtf8(decoded, codePage);
+}
diff --git a/src/openrct2/localisation/ConversionTables.h b/src/openrct2/rct12/CSStringConverter.h
similarity index 75%
rename from src/openrct2/localisation/ConversionTables.h
rename to src/openrct2/rct12/CSStringConverter.h
index ce2e7538b2..e301a8be20 100644
--- a/src/openrct2/localisation/ConversionTables.h
+++ b/src/openrct2/rct12/CSStringConverter.h
@@ -9,4 +9,11 @@
#pragma once
+#include
+#include
+
+enum class RCT2LanguageId : uint8_t;
+
wchar_t EncodingConvertRCT2ToUnicode(wchar_t rct2str);
+
+std::string RCT2StringToUTF8(std::string_view src, RCT2LanguageId languageId);
diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp
index 3af6d4b165..da95ad54be 100644
--- a/src/openrct2/rct12/RCT12.cpp
+++ b/src/openrct2/rct12/RCT12.cpp
@@ -11,9 +11,9 @@
#include "../core/String.hpp"
#include "../localisation/Formatting.h"
-#include "../localisation/Localisation.h"
#include "../object/ObjectList.h"
#include "../rct1/Tables.h"
+#include "../rct12/CSStringConverter.h"
#include "../rct2/RCT2.h"
#include "../ride/Ride.h"
#include "../ride/Track.h"
diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp
index 7b00114641..ce39a4a8dd 100644
--- a/src/openrct2/rct2/S6Importer.cpp
+++ b/src/openrct2/rct2/S6Importer.cpp
@@ -50,6 +50,7 @@
#include "../object/ObjectRepository.h"
#include "../object/WallSceneryEntry.h"
#include "../peep/RideUseSystem.h"
+#include "../rct12/CSStringConverter.h"
#include "../rct12/EntryList.h"
#include "../rct12/RCT12.h"
#include "../rct12/SawyerChunkReader.h"
diff --git a/src/openrct2/scenario/ScenarioRepository.cpp b/src/openrct2/scenario/ScenarioRepository.cpp
index 63388f7eb4..d593b40191 100644
--- a/src/openrct2/scenario/ScenarioRepository.cpp
+++ b/src/openrct2/scenario/ScenarioRepository.cpp
@@ -23,10 +23,9 @@
#include "../core/Numerics.hpp"
#include "../core/Path.hpp"
#include "../core/String.hpp"
-#include "../localisation/Language.h"
-#include "../localisation/Localisation.h"
#include "../localisation/LocalisationService.h"
#include "../platform/Platform.h"
+#include "../rct12/CSStringConverter.h"
#include "../rct12/RCT12.h"
#include "../rct12/SawyerChunkReader.h"
#include "../rct2/RCT2.h"
diff --git a/test/tests/LocalisationTest.cpp b/test/tests/LocalisationTest.cpp
index 04a0859183..920e6377ab 100644
--- a/test/tests/LocalisationTest.cpp
+++ b/test/tests/LocalisationTest.cpp
@@ -9,6 +9,7 @@
#include "helpers/StringHelpers.hpp"
#include "openrct2/localisation/Language.h"
+#include "openrct2/rct12/CSStringConverter.h"
#include