diff --git a/src/string_ids.c b/src/string_ids.c index 0213473ef6..caf7ece170 100644 --- a/src/string_ids.c +++ b/src/string_ids.c @@ -1065,6 +1065,110 @@ char real_name_initials[] = { 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W' }; +#pragma region Format codes + +typedef struct { + char code; + char *token; +} format_code_token; + +format_code_token format_code_tokens[] = { + { FORMAT_MOVE_X, "MOVE_X" }, + { FORMAT_ADJUST_PALETTE, "ADJUST_PALETTE" }, + { FORMAT_NEWLINE, "NEWLINE" }, + { FORMAT_NEWLINE_SMALLER, "NEWLINE_SMALLER" }, + { FORMAT_TINYFONT, "TINYFONT" }, + { FORMAT_BIGFONT, "BIGFONT" }, + { FORMAT_MEDIUMFONT, "MEDIUMFONT" }, + { FORMAT_SMALLFONT, "SMALLFONT" }, + { FORMAT_OUTLINE, "OUTLINE" }, + { FORMAT_OUTLINE_OFF, "OUTLINE_OFF" }, + { FORMAT_WINDOW_COLOUR_1, "WINDOW_COLOUR_1" }, + { FORMAT_WINDOW_COLOUR_2, "WINDOW_COLOUR_2" }, + { FORMAT_WINDOW_COLOUR_3, "WINDOW_COLOUR_3" }, + { FORMAT_NEWLINE_X_Y, "NEWLINE_X_Y" }, + { FORMAT_INLINE_SPRITE, "INLINE_SPRITE" }, + { FORMAT_ENDQUOTES, "ENDQUOTES" }, + { FORMAT_ARGUMENT_CODE_START, "ARGUMENT_CODE_START" }, + { FORMAT_COMMA32, "COMMA32" }, + { FORMAT_INT32, "INT32" }, + { FORMAT_COMMA2DP32, "COMMA2DP32" }, + { FORMAT_COMMA16, "COMMA16" }, + { FORMAT_UINT16, "UINT16" }, + { FORMAT_CURRENCY2DP, "CURRENCY2DP" }, + { FORMAT_CURRENCY, "CURRENCY" }, + { FORMAT_STRINGID, "STRINGID" }, + { FORMAT_STRINGID2, "STRINGID2" }, + { FORMAT_STRING, "STRING" }, + { FORMAT_MONTHYEAR, "MONTHYEAR" }, + { FORMAT_MONTH, "MONTH" }, + { FORMAT_VELOCITY, "VELOCITY" }, + { FORMAT_POP16, "POP16" }, + { FORMAT_PUSH16, "PUSH16" }, + { FORMAT_DURATION, "DURATION" }, + { FORMAT_REALTIME, "REALTIME" }, + { FORMAT_LENGTH, "LENGTH" }, + { FORMAT_SPRITE, "SPRITE" }, + { FORMAT_COLOUR_CODE_START, "COLOUR_CODE_START" }, + { FORMAT_BLACK, "BLACK" }, + { FORMAT_GREY, "GREY" }, + { FORMAT_WHITE, "WHITE" }, + { FORMAT_RED, "RED" }, + { FORMAT_GREEN, "GREEN" }, + { FORMAT_YELLOW, "YELLOW" }, + { FORMAT_TOPAZ, "TOPAZ" }, + { FORMAT_CELADON, "CELADON" }, + { FORMAT_BABYBLUE, "BABYBLUE" }, + { FORMAT_PALELAVENDER, "PALELAVENDER" }, + { FORMAT_PALEGOLD, "PALEGOLD" }, + { FORMAT_LIGHTPINK, "LIGHTPINK" }, + { FORMAT_PEARLAQUA, "PEARLAQUA" }, + { FORMAT_PALESILVER, "PALESILVER" }, + { FORMAT_COLOUR_CODE_END, "COLOUR_CODE_END" }, + { FORMAT_AMINUSCULE, "AMINUSCULE" }, + { FORMAT_UP, "UP" }, + { FORMAT_POUND, "POUND" }, + { FORMAT_YEN, "YEN" }, + { FORMAT_COPYRIGHT, "COPYRIGHT" }, + { FORMAT_DOWN, "DOWN" }, + { FORMAT_LEFTGUILLEMET, "LEFTGUILLEMET" }, + { FORMAT_TICK, "TICK" }, + { FORMAT_CROSS, "CROSS" }, + { FORMAT_RIGHT, "RIGHT" }, + { FORMAT_DEGREE, "DEGREE" }, + { FORMAT_SQUARED, "SQUARED" }, + { FORMAT_OPENQUOTES, "OPENQUOTES" }, + { FORMAT_EURO, "EURO" }, + { FORMAT_APPROX, "APPROX" }, + { FORMAT_POWERNEGATIVEONE, "POWERNEGATIVEONE" }, + { FORMAT_BULLET, "BULLET" }, + { FORMAT_RIGHTGUILLEMET, "RIGHTGUILLEMET" }, + { FORMAT_SMALLUP, "SMALLUP" }, + { FORMAT_SMALLDOWN, "SMALLDOWN" }, + { FORMAT_LEFT, "LEFT" }, + { FORMAT_INVERTEDQUESTION, "INVERTEDQUESTION" } +}; + +char format_get_code(const char *token) +{ + int i; + for (i = 0; i < countof(format_code_tokens); i++) + if (strcmpi(token, format_code_tokens[i].token) == 0) + return format_code_tokens[i].code; + return 0; +} + +const char *format_get_token(char code) +{ + int i; + for (i = 0; i < countof(format_code_tokens); i++) + if (code == format_code_tokens[i].code) + return format_code_tokens[i].token; + return 0; +} + +#pragma endregion + void format_string_part_from_raw(char **dest, const char *src, char **args); void format_string_part(char **dest, rct_string_id format, char **args); @@ -1564,81 +1668,15 @@ void generate_string_file() fprintf(f, "STR_%04d :", i); while (*c != '\0') { - switch (*c) { - case 7: fputs("{TINYFONT}", f); break; - case 8: fputs("{BIGFONT}", f); break; - case 9: fputs("{MEDIUMFONT}", f); break; - case 10: fputs("{SMALLFONT}", f); break; - - case 11: fputs("{OUTLINE}", f); break; - - case 34: fputs("{ENDQUOTES}", f); break; - - case 123: fputs("{COMMA32}", f); break; - case 124: fputs("{INT32}", f); break; - case 125: fputs("{COMMA2DP32}", f); break; - case 126: fputs("{COMMA16}", f); break; - case 127: fputs("{UINT16}", f); break; - case 128: fputs("{CURRENCY2DP}", f); break; - case 129: fputs("{CURRENCY}", f); break; - case 130: fputs("{STRINGID}", f); break; - case 131: fputs("{STRINGID2}", f); break; - case 132: fputs("{STRING}", f); break; - case 133: fputs("{MONTHYEAR}", f); break; - case 134: fputs("{MONTH}", f); break; - case 135: fputs("{VELOCITY}", f); break; - case 136: fputs("{POP16}", f); break; - case 137: fputs("{PUSH16}", f); break; - case 138: fputs("{DURATION}", f); break; - case 139: fputs("{REALTIME}", f); break; - case 140: fputs("{LENGTH}", f); break; - case 141: fputs("{SPRITE}", f); break; - - case 142: fputs("{BLACK}", f); break; - case 143: fputs("{GREY}", f); break; - case 144: fputs("{WHITE}", f); break; - case 145: fputs("{RED}", f); break; - case 146: fputs("{GREEN}", f); break; - case 147: fputs("{YELLOW}", f); break; - case 148: fputs("{TOPAZ}", f); break; - case 149: fputs("{CELADON}", f); break; - case 150: fputs("{BABYBLUE}", f); break; - case 151: fputs("{PALELAVENDER}", f); break; - case 152: fputs("{PALEGOLD}", f); break; - case 153: fputs("{LIGHTPINK}", f); break; - case 154: fputs("{PEARLAQUA}", f); break; - case 155: fputs("{PALESILVER}", f); break; - - case 159: fputs("{AMINUSCULE}", f); break; - case 160: fputs("{UP}", f); break; - case 163: fputs("{POUND}", f); break; - case 165: fputs("{YEN}", f); break; - case 169: fputs("{COPYRIGHT}", f); break; - case 170: fputs("{DOWN}", f); break; - case 171: fputs("{LEFTGUILLEMET}", f); break; - case 172: fputs("{TICK}", f); break; - case 173: fputs("{CROSS}", f); break; - case 175: fputs("{RIGHT}", f); break; - case 176: fputs("{DEGREE}", f); break; - case 178: fputs("{SQUARED}", f); break; - case 180: fputs("{OPENQUOTES}", f); break; - case 181: fputs("{EURO}", f); break; - case 184: fputs("{APPROX}", f); break; - case 185: fputs("{POWERNEGATIVEONE}", f); break; - case 186: fputs("{BULLET}", f); break; - case 187: fputs("{RIGHTGUILLEMET}", f); break; - case 188: fputs("{SMALLUP}", f); break; - case 189: fputs("{SMALLDOWN}", f); break; - case 190: fputs("{LEFT}", f); break; - case 191: fputs("{INVERTEDQUESTION}", f); break; - default: + const char *token = format_get_token(*c); + if (token != NULL) { + fprintf(f, "{%s}", token); + } else { if (*c < 32 || *c > 127) fprintf(f, "{%d}", *c); else fputc(*c, f); - break; } - c++; } fputc('\n', f); @@ -1667,23 +1705,29 @@ char **language_strings = NULL; const char *get_string(rct_string_id id) { - const char *str = language_strings == NULL ? - RCT2_ADDRESS(0x009BF2D4, const char*)[id] : - language_strings[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; } +/** + * 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 completely ignored at the moment. It just parses each line from + * the colon and thus not allowing gaps in the string indices. + */ int language_open(const char *filename) { - // WIP, file can be loaded in but not in a suitable state. - // Due to loading an uncompiled text language file, tokens surrounded with braces need to be converted to the correct - // format character code. Therefore the language can not stay in its raw form and must be re-written as the text file is - // parsed. - - // get_string returns the original game string address if no language is loaded in. - return 0; - FILE *f = fopen(filename, "rb"); if (f == NULL) return 0; @@ -1697,18 +1741,46 @@ int language_open(const char *filename) language_strings = calloc(STR_COUNT, sizeof(char*)); - int i, stringIndex = 0, searchForEndLine = 0; + char *dst, *token; + char tokenBuffer[64]; + int i, stringIndex = 0, mode = 0; for (i = 0; i < language_buffer_size; i++) { - char *c = &language_buffer[i]; + char *src = &language_buffer[i]; - if (searchForEndLine) { - if (*c == '\n' || *c == '\r') { - *c = 0; - searchForEndLine = 0; + switch (mode) { + case 0: + // Search for colon + if (*src == ':') { + dst = src + 1; + language_strings[stringIndex++] = dst; + mode = 1; } - } else if (*c == ':') { - language_strings[stringIndex++] = c + 1; - searchForEndLine = 1; + break; + case 1: + // Copy string over, stop at line break + if (*src == '{') { + token = src + 1; + mode = 2; + } else if (*src == '\n' || *src == '\r') { + *dst = 0; + mode = 0; + } else { + *dst++ = *src; + } + break; + case 2: + // Read token, convert to code + if (*src == '}') { + 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; } } language_num_strings = stringIndex; diff --git a/src/string_ids.h b/src/string_ids.h index 0f8337cd35..de7114a9a1 100644 --- a/src/string_ids.h +++ b/src/string_ids.h @@ -760,7 +760,8 @@ enum { STR_PAGE_4 = STR_PAGE_1 + 3, STR_PAGE_5 = STR_PAGE_1 + 4, - STR_COUNT = 4000 + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working + STR_COUNT = 32768 }; #endif diff --git a/src/window_cheats.c b/src/window_cheats.c index e3a6de94a8..7c6453be6e 100644 --- a/src/window_cheats.c +++ b/src/window_cheats.c @@ -89,7 +89,7 @@ static rct_widget window_cheats_money_widgets[] = { { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, 2462}, // tab 1 { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, 2462}, // tab 2 { WWT_TAB, 1, 65, 95, 17, 43, 0x2000144E, 2462}, // tab 3 - { WWT_CLOSEBOX, 1, XPL(0), WPL(0), YPL(1), HPL(1), STR_VERY_HIGH, STR_VERY_HIGH}, // high money + { WWT_CLOSEBOX, 1, XPL(0), WPL(0), YPL(1), HPL(1), 2760, STR_VERY_HIGH}, // high money { WWT_CLOSEBOX, 1, XPL(0), WPL(0), YPL(3), HPL(3), STR_FREE, STR_FREE}, //Park Entrance Fee Toggle { WIDGETS_END }, };