mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-27 16:54:52 +01:00
implement DAT translation, part 1
This commit is contained in:
@@ -65,6 +65,7 @@
|
||||
<ClCompile Include="..\src\localisation\currency.c" />
|
||||
<ClCompile Include="..\src\localisation\date.c" />
|
||||
<ClCompile Include="..\src\localisation\language.c" />
|
||||
<ClCompile Include="..\src\localisation\LanguagePack.cpp" />
|
||||
<ClCompile Include="..\src\localisation\localisation.c" />
|
||||
<ClCompile Include="..\src\localisation\real_names.c" />
|
||||
<ClCompile Include="..\src\localisation\user.c" />
|
||||
@@ -235,6 +236,7 @@
|
||||
<ClInclude Include="..\src\localisation\date.h" />
|
||||
<ClInclude Include="..\src\localisation\format_codes.h" />
|
||||
<ClInclude Include="..\src\localisation\language.h" />
|
||||
<ClInclude Include="..\src\localisation\LanguagePack.h" />
|
||||
<ClInclude Include="..\src\localisation\localisation.h" />
|
||||
<ClInclude Include="..\src\localisation\string_ids.h" />
|
||||
<ClInclude Include="..\src\management\award.h" />
|
||||
|
||||
@@ -537,6 +537,9 @@
|
||||
<ClCompile Include="..\src\world\particle.c">
|
||||
<Filter>Source\World</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\localisation\LanguagePack.cpp">
|
||||
<Filter>Source\Localisation</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\management\award.h">
|
||||
@@ -788,5 +791,8 @@
|
||||
<ClInclude Include="..\src\core\Exception.hpp">
|
||||
<Filter>Source\Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\localisation\LanguagePack.h">
|
||||
<Filter>Source\Localisation</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
200
src/localisation/LanguagePack.cpp
Normal file
200
src/localisation/LanguagePack.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
extern "C" {
|
||||
#include "../common.h"
|
||||
#include "../util/util.h"
|
||||
#include "localisation.h"
|
||||
}
|
||||
|
||||
#include "LanguagePack.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
}
|
||||
94
src/localisation/LanguagePack.h
Normal file
94
src/localisation/LanguagePack.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<utf8*> _strings;
|
||||
|
||||
LanguagePack(int id, const utf8 *text);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Parsing
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
utf8 *_currentGroup;
|
||||
|
||||
void ParseLine(IStringReader *reader);
|
||||
void ParseGroup(IStringReader *reader);
|
||||
void ParseString(IStringReader *reader);
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user