mirror of https://github.com/OpenRCT2/OpenRCT2.git
219 lines
6.2 KiB
C
219 lines
6.2 KiB
C
/*****************************************************************************
|
|
* Copyright (c) 2014 Ted John
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
*
|
|
* This file is part of OpenRCT2.
|
|
*
|
|
* OpenRCT2 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "addresses.h"
|
|
#include "language.h"
|
|
#include "string_ids.h"
|
|
|
|
const char *language_names[LANGUAGE_COUNT] = {
|
|
"", // LANGUAGE_UNDEFINED
|
|
"English (UK)", // LANGUAGE_ENGLISH_UK
|
|
"English (US)", // LANGUAGE_ENGLISH_US
|
|
"Nederlands", // LANGUAGE_DUTCH
|
|
"Fran\u00E7ais", // LANGUAGE_FRENCH
|
|
"Magyar", // LANGUAGE_HUNGARIAN
|
|
"Polski", // LANGUAGE_POLISH
|
|
"Espa\u00F1ol", // LANGUAGE_SPANISH
|
|
"Svenska" // LANGUAGE_SWEDISH
|
|
};
|
|
|
|
const char *language_filenames[LANGUAGE_COUNT] = {
|
|
"", // LANGUAGE_UNDEFINED
|
|
"english_uk", // LANGUAGE_ENGLISH_UK
|
|
"english_us", // LANGUAGE_ENGLISH_US
|
|
"dutch", // LANGUAGE_DUTCH
|
|
"french", // LANGUAGE_FRENCH
|
|
"hungarian", // LANGUAGE_HUNGARIAN
|
|
"polish", // LANGUAGE_POLISH
|
|
"spanish_sp", // LANGUAGE_SPANISH
|
|
"swedish" // LANGUAGE_SWEDISH
|
|
};
|
|
|
|
int gCurrentLanguage = LANGUAGE_UNDEFINED;
|
|
|
|
// Buffer storing all the string data
|
|
long language_buffer_size = 0;
|
|
char *language_buffer = NULL;
|
|
|
|
// List of string pointers into the string data
|
|
int language_num_strings = 0;
|
|
char **language_strings = NULL;
|
|
|
|
static int language_open_file(const char *filename);
|
|
|
|
static int utf8_get_next(char *char_ptr, char **nextchar_ptr)
|
|
{
|
|
int result;
|
|
int numBytes;
|
|
|
|
if (!(char_ptr[0] & 0x80)) {
|
|
result = char_ptr[0];
|
|
numBytes = 1;
|
|
} else if (!(char_ptr[0] & 0x20)) {
|
|
result = ((char_ptr[0] & 0x1F) << 6) | (char_ptr[1] & 0x3F);
|
|
numBytes = 2;
|
|
}
|
|
|
|
if (nextchar_ptr != NULL)
|
|
*nextchar_ptr = char_ptr + numBytes;
|
|
return result;
|
|
}
|
|
|
|
const char *language_get_string(rct_string_id id)
|
|
{
|
|
const char *rct = RCT2_ADDRESS(0x009BF2D4, const char*)[id];
|
|
const char *openrct = language_strings == NULL ? NULL : language_strings[id];
|
|
const char *str = (openrct == NULL || strlen(openrct) == 0 ? rct : openrct);
|
|
return str == NULL ? "" : str;
|
|
}
|
|
|
|
int language_open(int id)
|
|
{
|
|
char filename[_MAX_PATH];
|
|
|
|
language_close();
|
|
if (id == LANGUAGE_UNDEFINED)
|
|
return 1;
|
|
|
|
sprintf(filename, "data/language/%s.txt", language_filenames[id]);
|
|
if (language_open_file(filename)) {
|
|
gCurrentLanguage = id;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Partial support to open a uncompiled language file which parses tokens and converts them to the corresponding character
|
|
* code. Due to resource strings (strings in scenarios and objects) being written to the original game's string table,
|
|
* get_string will use those if the same entry in the loaded language is empty.
|
|
*
|
|
* Unsure at how the original game decides which entries to write resource strings to, but this could affect adding new
|
|
* strings for the time being. Further investigation is required.
|
|
*
|
|
* Also note that all strings are currently still ASCII. It probably can't be converted to UTF-8 until all game functions that
|
|
* read / write strings in some way is decompiled. The original game used a DIY extended 8-bit extended ASCII set for special
|
|
* characters, format codes and accents.
|
|
*
|
|
* In terms of reading the language files, the STR_XXXX part is read and XXXX becomes the string id number. Everything after the
|
|
* colon and before the new line will be saved as the string. Tokens are written with inside curly braces {TOKEN}.
|
|
* Use # at the beginning of a line to leave a comment.
|
|
*/
|
|
static int language_open_file(const char *filename)
|
|
{
|
|
FILE *f = fopen(filename, "rb");
|
|
if (f == NULL)
|
|
return 0;
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
language_buffer_size = ftell(f);
|
|
language_buffer = calloc(1, language_buffer_size);
|
|
fseek(f, 0, SEEK_SET);
|
|
fread(language_buffer, language_buffer_size, 1, f);
|
|
fclose(f);
|
|
|
|
language_strings = calloc(STR_COUNT, sizeof(char*));
|
|
|
|
char *dst, *token;
|
|
char tokenBuffer[64];
|
|
int i, stringIndex = 0, mode = 0, string_no;
|
|
for (i = 0; i < language_buffer_size; i++) {
|
|
char *src = &language_buffer[i];
|
|
|
|
// Handle UTF-8
|
|
char *srcNext;
|
|
int utf8Char = utf8_get_next(src, &srcNext);
|
|
i += srcNext - src - 1;
|
|
if (utf8Char > 0xFF)
|
|
utf8Char = '?';
|
|
else if (utf8Char > 0x7F)
|
|
utf8Char &= 0xFF;
|
|
|
|
switch (mode) {
|
|
case 0:
|
|
// Search for a comment
|
|
if (utf8Char == '#') {
|
|
mode = 3;
|
|
} else if (utf8Char == ':' && string_no != -1) {
|
|
// Search for colon
|
|
dst = src + 1;
|
|
language_strings[string_no] = dst;
|
|
stringIndex++;
|
|
mode = 1;
|
|
} else if (!strncmp(src, "STR_", 4)){
|
|
// Copy in the string number, 4 characters only
|
|
if (sscanf(src, "STR_%4d", &string_no) != 1) {
|
|
string_no = -1;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
// Copy string over, stop at line break
|
|
if (utf8Char == '{') {
|
|
token = src + 1;
|
|
mode = 2;
|
|
} else if (utf8Char == '\n' || *src == '\r') {
|
|
*dst = 0;
|
|
mode = 0;
|
|
} else {
|
|
*dst++ = utf8Char;
|
|
}
|
|
break;
|
|
case 2:
|
|
// Read token, convert to code
|
|
if (utf8Char == '}') {
|
|
int tokenLength = min(src - token, sizeof(tokenBuffer) - 1);
|
|
memcpy(tokenBuffer, token, tokenLength);
|
|
tokenBuffer[tokenLength] = 0;
|
|
char code = format_get_code(tokenBuffer);
|
|
if (code == 0)
|
|
code = atoi(tokenBuffer);
|
|
*dst++ = code;
|
|
mode = 1;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (utf8Char == '\n' || utf8Char == '\r') {
|
|
mode = 0;
|
|
}
|
|
}
|
|
}
|
|
language_num_strings = stringIndex;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void language_close()
|
|
{
|
|
if (language_buffer != NULL)
|
|
free(language_buffer);
|
|
language_buffer = NULL;
|
|
language_buffer_size = 0;
|
|
|
|
if (language_strings != NULL)
|
|
free(language_strings);
|
|
language_strings = NULL;
|
|
language_num_strings = 0;
|
|
|
|
gCurrentLanguage = LANGUAGE_UNDEFINED;
|
|
} |