game: Support korean in Jak 2 and Jak 3 (#3988)

Resolves #3075 

TODO before merge:
- [x] Properly draw non-korean strings while in korean mode (language
selection)
- [x] Check jak 3
- [x] Translation scaffolding (allow korean characters, add to Crowdin,
fix japanese locale, etc)
- [x] Check translation of text lines
- [x] Check translation of subtitle lines
- [x] Cleanup PR / some performance optimization (it's take a bit too
long to build the text and it shouldn't since the information is in a
giant lookup table)
- [x] Wait until release is cut

I confirmed the font textures are identical between Jak 2 and Jak 3, so
thank god for that.

Some examples of converting the korean encoding to utf-8. These show off
all scenarios, pure korean / korean with ascii and japanese / korean
with replacements (flags):
<img width="316" height="611" alt="Screenshot 2025-07-26 191511"
src="https://github.com/user-attachments/assets/614383ba-8049-4bf4-937e-24ad3e605d41"
/>
<img width="254" height="220" alt="Screenshot 2025-07-26 191529"
src="https://github.com/user-attachments/assets/1f6e5a6c-8527-4f98-a988-925ec66e437d"
/>

And it working in game. `Input Options` is a custom not-yet-translated
string. It now shows up properly instead of a disgusting block of
glyphs, and all the original strings are hopefully the same
semantically!:
<img width="550" height="493" alt="Screenshot 2025-07-26 202838"
src="https://github.com/user-attachments/assets/9ebdf6c0-f5a3-4a30-84a1-e5840809a1a2"
/>

Quite the challenge. The crux of the problem is -- Naughty Dog came up
with their own encoding for representing korean syllable blocks, and
that source information is lost so it has to be reverse engineered.
Instead of trying to figure out their encoding from the text -- I went
at it from the angle of just "how do i draw every single korean
character using their glyph set".

One might think this is way too time consuming but it's important to
remember:
- Korean letters are designed to be composable from a relatively small
number of glyphs (more on this later)
- Someone at naughty dog did basically this exact process
- There is no other way! While there are loose patterns, there isn't an
overarching rhyme or reason, they just picked the right glyph for the
writing context (more on this later). And there are even situations
where there IS NO good looking glyph, or the one ND chose looks awful
and unreadable (we could technically fix this by adjusting the
positioning of the glyphs but....no more)!

Information on their encoding that gets passed to `convert-korean-text`:
- It's a raw stream of bytes
- It can contain normal font letters
- Every syllable block begins with: `0x04 <num_glyphs> <...the glyph
bytes...>`
- DO NOT confuse `num_glyphs` with num jamo, because some glyphs can
have multiple jamo!
- Every section of normal text starts with `0x03`. For example a space
would be `0x03 0x20`
- There are a very select few number of jamo glyphs on a secondary
texture page, these glyph bytes are preceeded with a `0x05`. These jamo
are a variant of some of the final vowels, moving them as low down as
possible.

Crash course on korean writing:
- Nice resource as this is basically what we are doing -
https://glyphsapp.com/learn/creating-a-hangeul-font
- Korean syllable blocks have either 2 or 3 jamo. Jamo are basically
letters and are the individual pieces that make up the syllable blocks.
- The jamo are split up into "initial", "medial" and "final" categories.
Within the "medial" category there are obvious visual variants:
  - Horizontal
  - Vertical
  - Combination (horizontal + a vertical)
- These jamo are laid out in 6 main pre-defined "orientations":
  - initial + vertical medial
  - initial + horizontal medial
  - initial + combination
  - initial + vertical medial + final
  - initial + horizontal medial + final
  - initial + combination + final
- Sometimes, for stylistic reasons, jamo will be written in different
ways (ie. if there is nothing below a vertical vowel will be extended).
  - Annoying, and ND's glyph set supports this stylistic choice!
- There are some combination of jamo that are never used, and some that
are only used for a single word in the entire language!

With all that in mind, my basic process was:
- Scan the game's entire corpus of korean text, that includes subtitles.
It's very easy to look at the font texture's glyphs and assign them to
their respective jamo
- This let me construct a mapping and see which glyphs were used under
which context
- I then shoved this information into a 2-D matrix in excel, and created
an in-game tool to check every single jamo permutation to fill in the
gaps / change them if naughty dogs was bad. Most of the time, ND's
encoding was fine.
-
https://docs.google.com/spreadsheets/d/e/2PACX-1vTtyMeb5-mL5rXseS9YllVj32BGCISOGZFic6nkRV5Er5aLZ9CLq1Hj_rTY7pRCn-wrQDH1rvTqUHwB/pubhtml?gid=886895534&single=true
anything in red is an addition / modification on my part.
- This was the most lengthy part but not as long as you may think, you
can do a lot of pruning. For example if you are checking a 3-jamo
variant (the ones with the most permutations) and you've verified that
the medial jamo is as far up vertically as it can be, and you are using
the lowest final jamo that are available -- there is nothing to check or
improve -- for better or worse! So those end up being the permutations
between the initial and medial instead of a three-way permutation
nightmare.
- Also, while it is a 2d matrix, there's a lot of pruning even within
that. For example, for the first 3 orientations, you dont have to care
about final vowels at all.
- At the end, I'm left with a lookup table that I can use the encode the
best looking korean syllable blocks possible given the context of the
jamo combination.
This commit is contained in:
Tyler Wilding
2025-08-16 19:35:47 -04:00
committed by GitHub
parent a470820b3e
commit 006d24b29a
53 changed files with 16295 additions and 2604 deletions
+1 -1
View File
@@ -86,4 +86,4 @@ __pycache__/
/TODO.md
unifont-15.0.03.ttf
*.diff
goalc-report.html
goalc-report.html
+2 -2
View File
@@ -8,10 +8,10 @@
"name": "run python script",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/scripts/gsrc/compare-compilation-outputs.py",
"program": "${workspaceFolder}/scripts/ci/lint-characters.py",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"args": []
"args": ["--fix"]
},
]
}
+5 -1
View File
@@ -83,7 +83,11 @@ add_library(common
util/dialogs.cpp
util/diff.cpp
util/FileUtil.cpp
util/FontUtils.cpp
util/font/dbs/font_db_jak1.cpp
util/font/dbs/font_db_jak2.cpp
util/font/dbs/font_db_jak3.cpp
util/font/font_utils_korean.cpp
util/font/font_utils.cpp
util/FrameLimiter.cpp
util/json_util.cpp
util/os.cpp
+10 -54
View File
@@ -13,7 +13,7 @@
#include "common/log/log.h"
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
@@ -156,39 +156,6 @@ Reader::Reader() {
for (const char* c = bonus; *c; c++) {
m_valid_symbols_chars[(int)*c] = true;
}
// table of characters that are valid in source code:
for (auto& x : m_valid_source_text_chars) {
x = false;
}
for (int i = ' '; i <= '~'; i++) {
m_valid_source_text_chars[i] = true;
}
m_valid_source_text_chars[(int)'\n'] = true;
m_valid_source_text_chars[(int)'\t'] = true;
m_valid_source_text_chars[(int)'\r'] = true;
// allow every character that gets transformed to something else
for (auto& [version, font] : g_font_banks) {
for (auto& remap : *font->encode_info()) {
for (auto rc : remap.chars) {
m_valid_source_text_chars[(u8)rc] = true;
}
}
for (auto& remap : *font->replace_info()) {
for (auto rc : remap.to) {
m_valid_source_text_chars[(u8)rc] = true;
}
for (auto rc : remap.from) {
m_valid_source_text_chars[(u8)rc] = true;
}
}
}
m_valid_source_text_chars[0] = false;
}
bool Reader::is_valid_source_char(char c) const {
return m_valid_source_text_chars[(u8)c];
}
/*!
@@ -266,17 +233,6 @@ Object Reader::internal_read(std::shared_ptr<SourceText> text,
fmt::format("Text file {} has invalid encoding", text->get_description()));
}
// validate the input
for (int offset = check_encoding ? 3 : 0; offset < text->get_size(); offset++) {
if (!is_valid_source_char(text->get_text()[offset])) {
// failed.
int line_number = text->get_line_idx(offset) + 1;
throw std::runtime_error(fmt::format("Invalid character found on line {} of {}: 0x{:x}",
line_number, text->get_description(),
(u8)text->get_text()[offset]));
}
}
// first create stream
TextStream ts(text);
@@ -301,15 +257,6 @@ Object Reader::internal_read(std::shared_ptr<SourceText> text,
}
}
bool Reader::check_string_is_valid(const std::string& str) const {
for (auto c : str) {
if (!is_valid_source_char(c)) {
return false;
}
}
return true;
}
/*!
* Given a stream starting at the first character of a token, get the token. Doesn't consume
* whitespace at the end and leaves the stream on the first character after the token.
@@ -905,4 +852,13 @@ std::string get_readable_string(const char* in) {
}
return result;
}
std::string get_byte_string(const char* in) {
std::string result;
while (*in) {
result += fmt::format("\\c{:02x}", uint8_t(*in));
in++;
}
return result;
}
} // namespace goos
+1 -4
View File
@@ -75,7 +75,6 @@ class Reader {
const std::optional<std::string>& string_name = {});
std::optional<Object> read_from_stdin(const std::string& prompt, REPL::Wrapper& repl);
Object read_from_file(const std::vector<std::string>& file_path, bool check_encoding = false);
bool check_string_is_valid(const std::string& str) const;
SymbolTable symbolTable;
TextDb db;
@@ -101,12 +100,10 @@ class Reader {
void add_reader_macro(const std::string& shortcut, std::string replacement);
bool m_valid_symbols_chars[256];
bool m_valid_source_text_chars[256];
bool is_valid_source_char(char c) const;
std::unordered_map<std::string, std::string> m_reader_macros;
};
std::string get_byte_string(const char* in);
std::string get_readable_string(const char* in);
} // namespace goos
@@ -2,7 +2,7 @@
#include "common/serialization/subtitles/subtitles.h"
#include "common/util/Assert.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "common/util/json_util.h"
struct SubtitleLineMetadata {
+37 -12
View File
@@ -2,6 +2,7 @@
#include "common/goos/ParseHelpers.h"
#include "common/goos/Reader.h"
#include "common/util/font/font_utils_korean.h"
int64_t get_int(const goos::Object& obj) {
if (obj.is_int()) {
@@ -44,7 +45,7 @@ std::string get_string(const goos::Object& x) {
void parse_text_goal(const goos::Object& data,
GameTextDB& db,
const GameTextDefinitionFile& /*file_info*/) {
const GameTextFontBank* font = nullptr;
GameTextFontBank* font = nullptr;
std::vector<std::shared_ptr<GameTextBank>> banks;
std::string possible_group_name;
@@ -122,9 +123,15 @@ void parse_text_goal(const goos::Object& data,
if (b_i >= int(banks.size())) {
throw std::runtime_error(fmt::format("Too many strings in text id #x{:x}", id));
}
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[b_i++]->set_line(id, line);
if (font->is_language_id_korean(b_i)) {
// korean changes differently!
auto line = font->convert_utf8_to_game_korean(entry.as_string()->data);
banks[b_i]->set_line(id, line);
} else {
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[b_i]->set_line(id, line);
}
b_i++;
} else {
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
}
@@ -165,8 +172,15 @@ void parse_text_goal(const goos::Object& data,
throw std::runtime_error(fmt::format("Too many strings in text id #x{:x}", id));
}
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[i++]->set_line(id, line);
if (font->is_language_id_korean(i)) {
// handle korean differently!
auto line = font->convert_utf8_to_game_korean(entry.as_string()->data);
banks[i]->set_line(id, line);
} else {
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[i]->set_line(id, line);
}
i++;
} else {
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
}
@@ -230,15 +244,20 @@ void parse_text_json(const nlohmann::json& json,
} else {
bank = db.bank_by_id(file_info.group_name.value(), file_info.language_id);
}
const GameTextFontBank* font = get_font_bank(file_info.text_version);
GameTextFontBank* font = get_font_bank(file_info.text_version);
// Parse the file
for (const auto& [text_id, text_value] : json.items()) {
auto line_id = std::stoi(text_id, nullptr, 16);
if (text_value.is_string()) {
// single line replacement
auto line = font->convert_utf8_to_game(text_value);
// TODO - lint duplicate line definitions across text files
bank->set_line(line_id, line);
if (font->is_language_id_korean(file_info.language_id)) {
auto line = font->convert_utf8_to_game_korean(text_value);
bank->set_line(line_id, line);
} else {
auto line = font->convert_utf8_to_game(text_value);
bank->set_line(line_id, line);
}
} else if (text_value.is_array()) {
// multi-line replacement starting from line_id
// (e.g. for Jak 1 credits, start from x0b00)
@@ -247,8 +266,14 @@ void parse_text_json(const nlohmann::json& json,
throw std::runtime_error(fmt::format(
"Non string provided for line {} / text id #x{} of _credits", idx, line_id));
}
auto line = font->convert_utf8_to_game(raw_line);
bank->set_line(line_id++, line); // increment line_id
if (font->is_language_id_korean(file_info.language_id)) {
auto line = font->convert_utf8_to_game_korean(raw_line);
bank->set_line(line_id++, line); // increment line_id
} else {
auto line = font->convert_utf8_to_game(raw_line);
bank->set_line(line_id++, line); // increment line_id
}
}
} else {
// Unexpected value type
+1 -1
View File
@@ -11,7 +11,7 @@
#include "common/goos/Object.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "common/util/json_util.h"
#include "common/versions/versions.h"
File diff suppressed because it is too large Load Diff
+26 -1
View File
@@ -36,6 +36,9 @@ class Trie {
// Get all objects starting with the given prefix.
std::vector<T*> lookup_prefix(const std::string& str) const;
// Finds the longest prefix match starting at offset `off` in string `in`.
const T* find_longest_prefix(const std::string& in, int off) const;
// Get all nodes in the tree.
std::vector<T*> get_all_nodes() const;
~Trie();
@@ -143,6 +146,23 @@ class Trie {
}
}
}
const T* find_longest_prefix(const std::string& in, int off) const {
const Node* node = this;
const T* best = nullptr;
for (int j = off; j < (int)in.size(); ++j) {
unsigned char c = static_cast<unsigned char>(in[j]);
node = node->children[c];
if (!node) {
break;
}
if (node->value) {
best = node->value;
}
}
return best;
}
};
Node m_root;
@@ -182,9 +202,14 @@ std::vector<T*> Trie<T>::lookup_prefix(const std::string& str) const {
return m_root.lookup_prefix(str.c_str());
}
template <typename T>
const T* Trie<T>::find_longest_prefix(const std::string& in, int off) const {
return m_root.find_longest_prefix(in, off);
}
template <typename T>
std::vector<T*> Trie<T>::get_all_nodes() const {
std::vector<T*> result;
m_root.get_all_children(result);
return result;
}
}
+596
View File
@@ -0,0 +1,596 @@
#include "font_db_jak1.h"
std::unordered_set<char> passthrus_jak1 = {'~', ' ', ',', '.', '-', '+', '(', ')', '!', ':', '?',
'=', '%', '*', '/', '#', ';', '<', '>', '@', '[', '_'};
std::vector<EncodeInfo> encode_info_jak1 = {
// random
{"ˇ", "\x10"}, // caron
{"`", "\x11"}, // grave accent
{"'", "\x12"}, // apostrophe
{"^", "\x13"}, // circumflex
{"<TIL>", "\x14"}, // tilde
{"¨", "\x15"}, // umlaut
{"º", "\x16"}, // numero/overring
{"¡", "\x17"}, // inverted exclamation mark
{"¿", "\x18"}, // inverted question mark
{"", "\x1a"}, // umi
{"Æ", "\x1b"}, // aesc
{"", "\x1c"}, // kai
{"Ç", "\x1d"}, // c-cedilla
{"", "\x1e"}, // gaku
{"ß", "\x1f"}, // eszett
{"", "\x24"}, // wa
{"", "\x26"}, // wo
{"", "\x27"}, // -n
{"", "\x5c"}, // iwa
{"", "\x5d"}, // kyuu
{"", "\x5e"}, // sora
//{"掘", "\x5f"}, // horu
{"", "\x60"}, // -wa
{"", "\x61"}, // utsu
{"", "\x62"}, // kashikoi
{"", "\x63"}, // mizuumi
{"", "\x64"}, // kuchi
{"", "\x65"}, // iku
{"", "\x66"}, // ai
{"", "\x67"}, // shi
{"", "\x68"}, // tera
{"", "\x69"}, // yama
{"", "\x6a"}, // mono
{"", "\x6b"}, // tokoro
{"", "\x6c"}, // kaku
{"", "\x6d"}, // shou
{"", "\x6e"}, // numa
{"", "\x6f"}, // ue
{"", "\x70"}, // shiro
{"", "\x71"}, // ba
{"", "\x72"}, // shutsu
{"", "\x73"}, // yami
{"", "\x74"}, // nokosu
{"", "\x75"}, // ki
{"", "\x76"}, // ya
{"", "\x77"}, // shita
{"", "\x78"}, // ie
{"", "\x79"}, // hi
{"", "\x7a"}, // hana
{"", "\x7b"}, // re
{"Œ", "\x7c"}, // oe
{"", "\x7d"}, // ro
{"", "\x7f"}, // ao
{"", "\x90"}, // nakaguro
{"", "\x91"}, // dakuten
{"", "\x92"}, // handakuten
{"", "\x93"}, // chouompu
{"", "\x94"}, // nijuukagikakko left
{"", "\x95"}, // nijuukagikakko right
// hiragana
{"", "\x96"}, // -a
{"", "\x97"}, // a
{"", "\x98"}, // -i
{"", "\x99"}, // i
{"", "\x9a"}, // -u
{"", "\x9b"}, // u
{"", "\x9c"}, // -e
{"", "\x9d"}, // e
{"", "\x9e"}, // -o
{"", "\x9f"}, // o
{"", "\xa0"}, // ka
{"", "\xa1"}, // ki
{"", "\xa2"}, // ku
{"", "\xa3"}, // ke
{"", "\xa4"}, // ko
{"", "\xa5"}, // sa
{"", "\xa6"}, // shi
{"", "\xa7"}, // su
{"", "\xa8"}, // se
{"", "\xa9"}, // so
{"", "\xaa"}, // ta
{"", "\xab"}, // chi
{"", "\xac"}, // sokuon
{"", "\xad"}, // tsu
{"", "\xae"}, // te
{"", "\xaf"}, // to
{"", "\xb0"}, // na
{"", "\xb1"}, // ni
{"", "\xb2"}, // nu
{"", "\xb3"}, // ne
{"", "\xb4"}, // no
{"", "\xb5"}, // ha
{"", "\xb6"}, // hi
{"", "\xb7"}, // hu
{"", "\xb8"}, // he
{"", "\xb9"}, // ho
{"", "\xba"}, // ma
{"", "\xbb"}, // mi
{"", "\xbc"}, // mu
{"", "\xbd"}, // me
{"", "\xbe"}, // mo
{"", "\xbf"}, // youon ya
{"", "\xc0"}, // ya
{"", "\xc1"}, // youon yu
{"", "\xc2"}, // yu
{"", "\xc3"}, // youon yo
{"", "\xc4"}, // yo
{"", "\xc5"}, // ra
{"", "\xc6"}, // ri
{"", "\xc7"}, // ru
{"", "\xc8"}, // re
{"", "\xc9"}, // ro
{"", "\xca"}, // -wa
{"", "\xcb"}, // wa
{"", "\xcc"}, // wo
{"", "\xcd"}, // -n
// katakana
{"", "\xce"}, // -a
{"", "\xcf"}, // a
{"", "\xd0"}, // -i
{"", "\xd1"}, // i
{"", "\xd2"}, // -u
{"", "\xd3"}, // u
{"", "\xd4"}, // -e
{"", "\xd5"}, // e
{"", "\xd6"}, // -o
{"", "\xd7"}, // o
{"", "\xd8"}, // ka
{"", "\xd9"}, // ki
{"", "\xda"}, // ku
{"", "\xdb"}, // ke
{"", "\xdc"}, // ko
{"", "\xdd"}, // sa
{"", "\xde"}, // shi
{"", "\xdf"}, // su
{"", "\xe0"}, // se
{"", "\xe1"}, // so
{"", "\xe2"}, // ta
{"", "\xe3"}, // chi
{"", "\xe4"}, // sokuon
{"", "\xe5"}, // tsu
{"", "\xe6"}, // te
{"", "\xe7"}, // to
{"", "\xe8"}, // na
{"", "\xe9"}, // ni
{"", "\xea"}, // nu
{"", "\xeb"}, // ne
{"", "\xec"}, // no
{"", "\xed"}, // ha
{"", "\xee"}, // hi
{"", "\xef"}, // hu
{"", "\xf0"}, // he
{"", "\xf1"}, // ho
{"", "\xf2"}, // ma
{"", "\xf3"}, // mi
{"", "\xf4"}, // mu
{"", "\xf5"}, // me
{"", "\xf6"}, // mo
{"", "\xf7"}, // youon ya
{"", "\xf8"}, // ya
{"", "\xf9"}, // youon yu
{"", "\xfa"}, // yu
{"", "\xfb"}, // youon yo
{"", "\xfc"}, // yo
{"", "\xfd"}, // ra
{"", "\xfe"}, // ri
{"", "\xff"}, // ru
// kanji 2
{"", "\x1\x01"}, // takara
{"", "\x1\x10"}, // ishi
{"", "\x1\x11"}, // aka
{"", "\x1\x12"}, // ato
{"", "\x1\x13"}, // kawa
{"", "\x1\x14"}, // ikusa
{"", "\x1\x15"}, // mura
{"", "\x1\x16"}, // tai
{"", "\x1\x17"}, // utena
{"", "\x1\x18"}, // osa
{"", "\x1\x19"}, // tori
{"", "\x1\x1a"}, // tei
{"", "\x1\x1b"}, // hora
{"", "\x1\x1c"}, // michi
{"", "\x1\x1d"}, // hatsu
{"", "\x1\x1e"}, // tobu
{"", "\x1\x1f"}, // fuku
{"", "\x1\xa0"}, // ike
{"", "\x1\xa1"}, // naka
{"", "\x1\xa2"}, // tou
{"", "\x1\xa3"}, // shima
{"", "\x1\xa4"}, // bu
{"", "\x1\xa5"}, // hou
{"", "\x1\xa6"}, // san
{"", "\x1\xa7"}, // kaerimiru
{"", "\x1\xa8"}, // chikara
{"", "\x1\xa9"}, // midori
{"", "\x1\xaa"}, // kishi
{"", "\x1\xab"}, // zou
{"", "\x1\xac"}, // tani
{"", "\x1\xad"}, // kokoro
{"", "\x1\xae"}, // mori
{"", "\x1\xaf"}, // mizu
{"", "\x1\xb0"}, // fune
{"", "\x1\xb1"}, // trademark
};
std::vector<ReplaceInfo> replace_info_jak1 = {
// other
{"A~Y~-21H~-5Vº~Z", "Å"},
{"N~Y~-6Hº~Z~+10H", ""},
{"O~Y~-16H~-1V/~Z", "Ø"},
{"A~Y~-6H~+3V,~Z", "Ą"},
{"E~Y~-6H~+2V,~Z", "Ę"},
{"L~Y~-16H~+0V/~Z", "Ł"},
{"Z~Y~-21H~-5Vº~Z", "Ż"},
{"E~Y~-20H~-5Vº~Z", "Ė"},
{"C~Y~-20H~-4Vˇ~Z", "Č"},
{"S~Y~-22H~-4Vˇ~Z", "Š"},
{"Z~Y~-22H~-4Vˇ~Z", "Ž"},
{"U~Y~-13H~+2V,~Z", "Ų"},
{"U~Y~-18H~-10V-~Z", "Ū"},
{"D~Y~-25H~-1V-~Z", "Đ"},
{"I~Y~-8H~+1V,~Z", "Į"},
// czech specific
{"U~Y~-23H~-5Vº~Z", "Ů"},
// tildes
{"N~Y~-22H~-4V<TIL>~Z", "Ñ"},
{"A~Y~-21H~-5V<TIL>~Z", "Ã"}, // custom
{"O~Y~-22H~-4V<TIL>~Z", "Õ"}, // custom
// acute accents
{"A~Y~-21H~-5V'~Z", "Á"},
{"E~Y~-22H~-5V'~Z", "É"},
{"I~Y~-19H~-5V'~Z", "Í"},
{"O~Y~-22H~-4V'~Z", "Ó"},
{"U~Y~-24H~-3V'~Z", "Ú"},
{"C~Y~-21H~-5V'~Z", "Ć"},
{"N~Y~-21H~-5V'~Z", "Ń"},
{"S~Y~-21H~-5V'~Z", "Ś"},
{"Z~Y~-21H~-5V'~Z", "Ź"},
// czech specific
{"Y~Y~-25H~-4V'~Z", "Ý"},
// double acute accents
{"O~Y~-28H~-4V'~-9H'~Z", "Ő"}, // custom
{"U~Y~-27H~-4V'~-12H'~Z", "Ű"}, // custom
// circumflex
{"A~Y~-20H~-4V^~Z", "Â"}, // custom
{"E~Y~-20H~-5V^~Z", "Ê"},
{"I~Y~-19H~-5V^~Z", "Î"},
{"O~Y~-20H~-4V^~Z", "Ô"}, // custom
{"U~Y~-24H~-3V^~Z", "Û"},
// grave accents
{"A~Y~-21H~-5V`~Z", "À"},
{"E~Y~-22H~-5V`~Z", "È"},
{"I~Y~-19H~-5V`~Z", "Ì"},
{"O~Y~-22H~-4V`~Z", "Ò"}, // custom
{"U~Y~-24H~-3V`~Z", "Ù"},
// umlaut
{"A~Y~-21H~-5V¨~Z", "Ä"},
{"E~Y~-20H~-5V¨~Z", "Ë"},
{"I~Y~-19H~-5V¨~Z", "Ï"}, // custom
{"O~Y~-22H~-4V¨~Z", "Ö"},
{"O~Y~-22H~-3V¨~Z", "ö"}, // dumb
{"U~Y~-22H~-3V¨~Z", "Ü"},
// caron - Ǎ ǎ Ě ě Ǧ ǧ Ǐ ǐ Ǒ ǒ Ǔ ǔ Y̌ y̌
{"A~Y~-20H~-4Vˇ~Z", "Ǎ"},
{"E~Y~-20H~-5Vˇ~Z", "Ě"},
{"G~Y~-20H~-5Vˇ~Z", "Ǧ"},
{"I~Y~-19H~-5Vˇ~Z", "Ǐ"},
{"O~Y~-20H~-4Vˇ~Z", "Ǒ"},
{"U~Y~-24H~-3Vˇ~Z", "Ǔ"},
{"Y~Y~-24H~-3Vˇ~Z", ""},
// czech specific - Č Ň Ř Š Ž Ť
{"C~Y~-25H~-9Vˇ~Z", "Č"},
{"N~Y~-23H~-5Vˇ~Z", "Ň"},
{"R~Y~-24H~-5Vˇ~Z", "Ř"},
{"S~Y~-24H~-5Vˇ~Z", "Š"},
{"T~Y~-23H~-5Vˇ~Z", "Ť"},
{"Z~Y~-23H~-5Vˇ~Z", "Ž"},
// dakuten katakana
{"~Yウ~Z゛", ""},
{"~Yカ~Z゛", ""},
{"~Yキ~Z゛", ""},
{"~Yク~Z゛", ""},
{"~Yケ~Z゛", ""},
{"~Yコ~Z゛", ""},
{"~Yサ~Z゛", ""},
{"~Yシ~Z゛", ""},
{"~Yス~Z゛", ""},
{"~Yセ~Z゛", ""},
{"~Yソ~Z゛", ""},
{"~Yタ~Z゛", ""},
{"~Yチ~Z゛", ""},
{"~Yツ~Z゛", ""},
{"~Yテ~Z゛", ""},
{"~Yト~Z゛", ""},
{"~Yハ~Z゛", ""},
{"~Yヒ~Z゛", ""},
{"~Yフ~Z゛", ""},
{"~Yヘ~Z゛", ""},
{"~Yホ~Z゛", ""},
// handakuten katakana
{"~Yハ~Z゜", ""},
{"~Yヒ~Z゜", ""},
{"~Yフ~Z゜", ""},
{"~Yヘ~Z゜", ""},
{"~Yホ~Z゜", ""},
// dakuten hiragana
{"~Yか~Z゛", ""},
{"~Yき~Z゛", ""},
{"~Yく~Z゛", ""},
{"~Yけ~Z゛", ""},
{"~Yこ~Z゛", ""},
{"~Yさ~Z゛", ""},
{"~Yし~Z゛", ""},
{"~Yす~Z゛", ""},
{"~Yせ~Z゛", ""},
{"~Yそ~Z゛", ""},
{"~Yた~Z゛", ""},
{"~Yち~Z゛", ""},
{"~Yつ~Z゛", ""},
{"~Yて~Z゛", ""},
{"~Yと~Z゛", ""},
{"~Yは~Z゛", ""},
{"~Yひ~Z゛", ""},
{"~Yふ~Z゛", ""},
{"~Yへ~Z゛", ""},
{"~Yほ~Z゛", ""},
// handakuten hiragana
{"~Yは~Z゜", ""},
{"~Yひ~Z゜", ""},
{"~Yふ~Z゜", ""},
{"~Yへ~Z゜", ""},
{"~Yほ~Z゜", ""},
// japanese punctuation
{",~+8H", ""},
{"~+8H ", " "},
// (hack) special case kanji
{"~~", ""},
// playstation buttons
{"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_X>"},
{"~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_TRIANGLE>"},
{"~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_CIRCLE>"},
{"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_SQUARE>"}, // custom
};
std::vector<EncodeInfo> encode_info_jak1_v2 = {
// random
{"_", "\x03"}, // large space
{"ˇ", "\x10"}, // caron
{"`", "\x11"}, // grave accent
{"'", "\x12"}, // apostrophe
{"^", "\x13"}, // circumflex
{"<TIL>", "\x14"}, // tilde
{"¨", "\x15"}, // umlaut
{"º", "\x16"}, // numero/overring
{"¡", "\x17"}, // inverted exclamation mark
{"¿", "\x18"}, // inverted question mark
{"", "\x1a"}, // umi
{"Æ", "\x1b"}, // aesc
{"", "\x1c"}, // kai
{"Ç", "\x1d"}, // c-cedilla
{"", "\x1e"}, // gaku
{"ß", "\x1f"}, // eszett
{"", "\x24"}, // wa
{"", "\x26"}, // wo
{"", "\x27"}, // -n
{"", "\x5c"}, // iwa
{"", "\x5d"}, // kyuu
{"", "\x5e"}, // sora
{"", "\x5f"}, // horu
{"", "\x60"}, // -wa
{"", "\x61"}, // utsu
{"", "\x62"}, // kashikoi
{"", "\x63"}, // mizuumi
{"", "\x64"}, // kuchi
{"", "\x65"}, // iku
{"", "\x66"}, // ai
{"", "\x67"}, // shi
{"", "\x68"}, // tera
{"", "\x69"}, // yama
{"", "\x6a"}, // mono
{"", "\x6b"}, // tokoro
{"", "\x6c"}, // kaku
{"", "\x6d"}, // shou
{"", "\x6e"}, // numa
{"", "\x6f"}, // ue
{"", "\x70"}, // shiro
{"", "\x71"}, // ba
{"", "\x72"}, // shutsu
{"", "\x73"}, // yami
{"", "\x74"}, // nokosu
{"", "\x75"}, // ki
{"", "\x76"}, // ya
{"", "\x77"}, // shita
{"", "\x78"}, // ie
{"", "\x79"}, // hi
{"", "\x7a"}, // hana
{"", "\x7b"}, // re
{"Œ", "\x7c"}, // oe
{"", "\x7d"}, // ro
{"", "\x7f"}, // ao
{"", "\x90"}, // nakaguro
{"", "\x91"}, // dakuten
{"", "\x92"}, // handakuten
{"", "\x93"}, // chouompu
{"", "\x94"}, // nijuukagikakko left
{"", "\x95"}, // nijuukagikakko right
// hiragana
{"", "\x96"}, // -a
{"", "\x97"}, // a
{"", "\x98"}, // -i
{"", "\x99"}, // i
{"", "\x9a"}, // -u
{"", "\x9b"}, // u
{"", "\x9c"}, // -e
{"", "\x9d"}, // e
{"", "\x9e"}, // -o
{"", "\x9f"}, // o
{"", "\xa0"}, // ka
{"", "\xa1"}, // ki
{"", "\xa2"}, // ku
{"", "\xa3"}, // ke
{"", "\xa4"}, // ko
{"", "\xa5"}, // sa
{"", "\xa6"}, // shi
{"", "\xa7"}, // su
{"", "\xa8"}, // se
{"", "\xa9"}, // so
{"", "\xaa"}, // ta
{"", "\xab"}, // chi
{"", "\xac"}, // sokuon
{"", "\xad"}, // tsu
{"", "\xae"}, // te
{"", "\xaf"}, // to
{"", "\xb0"}, // na
{"", "\xb1"}, // ni
{"", "\xb2"}, // nu
{"", "\xb3"}, // ne
{"", "\xb4"}, // no
{"", "\xb5"}, // ha
{"", "\xb6"}, // hi
{"", "\xb7"}, // hu
{"", "\xb8"}, // he
{"", "\xb9"}, // ho
{"", "\xba"}, // ma
{"", "\xbb"}, // mi
{"", "\xbc"}, // mu
{"", "\xbd"}, // me
{"", "\xbe"}, // mo
{"", "\xbf"}, // youon ya
{"", "\xc0"}, // ya
{"", "\xc1"}, // youon yu
{"", "\xc2"}, // yu
{"", "\xc3"}, // youon yo
{"", "\xc4"}, // yo
{"", "\xc5"}, // ra
{"", "\xc6"}, // ri
{"", "\xc7"}, // ru
{"", "\xc8"}, // re
{"", "\xc9"}, // ro
{"", "\xca"}, // -wa
{"", "\xcb"}, // wa
{"", "\xcc"}, // wo
{"", "\xcd"}, // -n
// katakana
{"", "\xce"}, // -a
{"", "\xcf"}, // a
{"", "\xd0"}, // -i
{"", "\xd1"}, // i
{"", "\xd2"}, // -u
{"", "\xd3"}, // u
{"", "\xd4"}, // -e
{"", "\xd5"}, // e
{"", "\xd6"}, // -o
{"", "\xd7"}, // o
{"", "\xd8"}, // ka
{"", "\xd9"}, // ki
{"", "\xda"}, // ku
{"", "\xdb"}, // ke
{"", "\xdc"}, // ko
{"", "\xdd"}, // sa
{"", "\xde"}, // shi
{"", "\xdf"}, // su
{"", "\xe0"}, // se
{"", "\xe1"}, // so
{"", "\xe2"}, // ta
{"", "\xe3"}, // chi
{"", "\xe4"}, // sokuon
{"", "\xe5"}, // tsu
{"", "\xe6"}, // te
{"", "\xe7"}, // to
{"", "\xe8"}, // na
{"", "\xe9"}, // ni
{"", "\xea"}, // nu
{"", "\xeb"}, // ne
{"", "\xec"}, // no
{"", "\xed"}, // ha
{"", "\xee"}, // hi
{"", "\xef"}, // hu
{"", "\xf0"}, // he
{"", "\xf1"}, // ho
{"", "\xf2"}, // ma
{"", "\xf3"}, // mi
{"", "\xf4"}, // mu
{"", "\xf5"}, // me
{"", "\xf6"}, // mo
{"", "\xf7"}, // youon ya
{"", "\xf8"}, // ya
{"", "\xf9"}, // youon yu
{"", "\xfa"}, // yu
{"", "\xfb"}, // youon yo
{"", "\xfc"}, // yo
{"", "\xfd"}, // ra
{"", "\xfe"}, // ri
{"", "\xff"}, // ru
// kanji 2
{"", "\x1\x01"}, // takara
{"", "\x1\x10"}, // ishi
{"", "\x1\x11"}, // aka
{"", "\x1\x12"}, // ato
{"", "\x1\x13"}, // kawa
{"", "\x1\x14"}, // ikusa
{"", "\x1\x15"}, // mura
{"", "\x1\x16"}, // tai
{"", "\x1\x17"}, // utena
{"", "\x1\x18"}, // osa
{"", "\x1\x19"}, // tori
{"", "\x1\x1a"}, // tei
{"", "\x1\x1b"}, // hora
{"", "\x1\x1c"}, // michi
{"", "\x1\x1d"}, // hatsu
{"", "\x1\x1e"}, // tobu
{"", "\x1\x1f"}, // fuku
{"", "\x1\xa0"}, // ike
{"", "\x1\xa1"}, // naka
{"", "\x1\xa2"}, // tou
{"", "\x1\xa3"}, // shima
{"", "\x1\xa4"}, // bu
{"", "\x1\xa5"}, // hou
{"", "\x1\xa6"}, // san
{"", "\x1\xa7"}, // kaerimiru
{"", "\x1\xa8"}, // chikara
{"", "\x1\xa9"}, // midori
{"", "\x1\xaa"}, // kishi
{"", "\x1\xab"}, // zou
{"", "\x1\xac"}, // tani
{"", "\x1\xad"}, // kokoro
{"", "\x1\xae"}, // mori
{"", "\x1\xaf"}, // mizu
{"", "\x1\xb0"}, // fune
{"", "\x1\xb1"}, // trademark
};
GameTextFontBank g_font_bank_jak1_v1(GameTextVersion::JAK1_V1,
&encode_info_jak1,
&replace_info_jak1,
&passthrus_jak1);
GameTextFontBank g_font_bank_jak1_v2(GameTextVersion::JAK1_V2,
&encode_info_jak1_v2,
&replace_info_jak1,
&passthrus_jak1);
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "common/util/font/font_utils.h"
/*!
* ===========================
* GAME TEXT FONT BANK - JAK 1
* ===========================
* This font is used in:
* - Jak & Daxter: The Precursor Legacy (Black Label)
*/
extern GameTextFontBank g_font_bank_jak1_v1;
/*!
* ================================
* GAME TEXT FONT BANK - JAK 1 (v2)
* ================================
* This font is used in:
* - Jak & Daxter: The Precursor Legacy (PAL)
* -   
* - Jak & Daxter: The Precursor Legacy (NTSC-U v2)
*
* It is the same as v1, but _ has been fixed and no longer overlaps
*/
extern GameTextFontBank g_font_bank_jak1_v2;
+866
View File
@@ -0,0 +1,866 @@
#include "font_db_jak2.h"
std::unordered_set<char> passthrus_jak2 = {'~', ' ', ',', '.', '-', '+', '(', ')',
'!', ':', '?', '=', '%', '*', '/', '#',
';', '<', '>', '@', '[', '_', ']'};
std::vector<ReplaceInfo> replace_info_jak2 = {
// other
{"A~Y~-21H~-5Vº~Z", "Å"},
{"N~Y~-6Hº~Z~+10H", ""},
{"~+4Vç~-4V", ",c"},
// added for translations TODO - check these for jak 2
{"O~Y~-25H~-1V/~Z", "Ø"},
{"o~Y~-23H~+4V/~Z", "ø"},
{"A~Y~-13H~+8V,~Z", "Ą"},
{"a~Y~-8H~+6V,~Z", "ą"},
{"E~Y~-6H~+8V,~Z", "Ę"},
{"e~Y~-10H~+7V,~Z", "ę"},
{"L~Y~-21H~+0V/~Z", "Ł"},
{"l~Y~-16H~+0V/~Z", "ł"}, // TODO - this one is ugly, font character addition (small slash)
{"Z~Y~-25H~-11Vº~Z", "Ż"},
{"z~Y~-23H~-5Vº~Z", "ż"},
{"a~Y~-25H~-5Vº~Z", "å"},
{"S~Y~-21H~-5V'~Z", "Ś"},
{"s~Y~-25H~-5V'~Z", "ś"},
{"n~Y~-25H~-5V'~Z", "ń"},
{"c~Y~-25H~-5V'~Z", "ć"},
{"o~Y~-24H~-4V<TIL>~Z", "õ"},
{"a~Y~-24H~-4V<TIL>~Z", "ã"},
{"O~Y~-28H~-4V'~-9H'~Z", "Ő"},
{"U~Y~-27H~-4V'~-12H'~Z", "Ű"},
{"o~Y~-28H~-4V'~-9H'~Z", "ő"},
{"u~Y~-28H~-4V'~-9H'~Z", "ű"},
{"E~Y~-22H~-11Vº~Z", "Ė"},
{"e~Y~-25H~-5Vº~Z", "ė"},
{"C~Y~-27H~-10Vˇ~Z", "Č"},
{"c~Y~-25H~-5Vˇ~Z", "č"},
{"S~Y~-24H~-10Vˇ~Z", "Š"},
{"s~Y~-22H~-4Vˇ~Z", "š"},
{"Z~Y~-25H~-10Vˇ~Z", "Ž"},
{"z~Y~-23H~-4Vˇ~Z", "ž"},
{"U~Y~-15H~+5V,~Z", "Ų"},
{"u~Y~-15H~+5V,~Z", "ų"},
{"U~Y~-20H~-18V-~Z", "Ū"},
{"u~Y~-18H~-15V-~Z", "ū"},
{"D~Y~-28H~-1V-~Z", "Đ"},
{"d~Y~-13H~-10V-~Z", "đ"},
{"I~Y~-8H~+4V,~Z", "Į"},
{"i~Y~-8H~+4V,~Z", "į"},
// czech specific
{"U~Y~-24H~-7Vº~Z", "Ů"},
{"u~Y~-23H~-5Vº~Z", "ů"},
{"t~Y~-7H~-21V,~Z", "ť"},
// tildes
{"N~Y~-22H~-4V<TIL>~Z", "Ñ"},
{"n~Y~-24H~-4V<TIL>~Z", "ñ"},
{"A~Y~-21H~-5V<TIL>~Z", "Ã"},
{"O~Y~-22H~-4V<TIL>~Z", "Õ"},
// acute accents
{"A~Y~-21H~-5V'~Z", "Á"},
{"A~Y~-26H~-8V'~Z", "<Á_V2>"}, // unfortunate...
{"a~Y~-25H~-5V'~Z", "á"},
{"E~Y~-23H~-9V'~Z", "É"},
{"e~Y~-26H~-5V'~Z", "é"},
{"I~Y~-19H~-5V'~Z", "Í"},
{"i~Y~-19H~-8V'~Z", "í"},
{"O~Y~-22H~-4V'~Z", "Ó"},
{"o~Y~-26H~-4V'~Z", "ó"},
{"U~Y~-24H~-3V'~Z", "Ú"},
{"u~Y~-24H~-3V'~Z", "ú"},
{"Z~Y~-24H~-3V'~Z", "Ź"},
{"z~Y~-24H~-3V'~Z", "ź"},
// czech specific
{"Y~Y~-26H~-5V'~Z", "Ý"},
{"~+7Vy~-7V~Y~-24H~-3V'~Z", "ý"},
// circumflex
{"A~Y~-20H~-4V^~Z", "Â"},
{"a~Y~-24H~-5V^~Z", "â"},
{"E~Y~-20H~-5V^~Z", "Ê"},
{"e~Y~-25H~-4V^~Z", "ê"},
{"I~Y~-19H~-5V^~Z", "Î"},
{"i~Y~-19H~-8V^~Z", "î"},
{"O~Y~-20H~-4V^~Z", "Ô"},
{"o~Y~-25H~-4V^~Z", "ô"},
{"U~Y~-24H~-3V^~Z", "Û"},
{"u~Y~-23H~-3V^~Z", "û"},
// grave accents
{"A~Y~-26H~-8V`~Z", "À"},
{"a~Y~-25H~-5V`~Z", "à"},
{"E~Y~-23H~-9V`~Z", "È"},
{"e~Y~-26H~-5V`~Z", "è"},
{"I~Y~-19H~-5V`~Z", "Ì"},
{"i~Y~-19H~-8V`~Z", "ì"},
{"O~Y~-22H~-4V`~Z", "Ò"},
{"o~Y~-26H~-4V`~Z", "ò"},
{"U~Y~-24H~-3V`~Z", "Ù"},
{"u~Y~-24H~-3V`~Z", "ù"},
// umlaut
{"A~Y~-26H~-8V¨~Z", "Ä"},
{"a~Y~-25H~-5V¨~Z", "ä"},
{"E~Y~-20H~-5V¨~Z", "Ë"},
{"e~Y~-25H~-5V¨~Z", "ë"},
{"I~Y~-19H~-5V¨~Z", "Ï"},
{"i~Y~-26H~-4V¨~Z", "ï"},
{"O~Y~-26H~-8V¨~Z", "Ö"},
{"o~Y~-26H~-4V¨~Z", "ö"},
{"U~Y~-25H~-8V¨~Z", "Ü"},
{"u~Y~-24H~-3V¨~Z", "ü"},
// caron - Ǎ ǎ Ě ě Ǧ ǧ Ǐ ǐ Ǒ ǒ Ǔ ǔ Y̌ y̌
{"A~Y~-25H~-9Vˇ~Z", "Ǎ"},
{"a~Y~-24H~-5Vˇ~Z", "ǎ"},
{"E~Y~-22H~-8Vˇ~Z", "Ě"},
{"e~Y~-25H~-4Vˇ~Z", "ě"},
{"G~Y~-24H~-8Vˇ~Z", "Ǧ"},
{"~+7Vg~-7V~Y~-25H~-4Vˇ~Z", "ǧ"},
{"I~Y~-19H~-8Vˇ~Z", "Ǐ"},
{"i~Y~-19H~-8Vˇ~Z", "ǐ"},
{"O~Y~-25H~-7Vˇ~Z", "Ǒ"},
{"o~Y~-25H~-4Vˇ~Z", "ǒ"},
{"U~Y~-25H~-6Vˇ~Z", "Ǔ"},
{"u~Y~-24H~-3Vˇ~Z", "ǔ"},
{"Y~Y~-25H~-5Vˇ~Z", ""},
{"~+7Vy~-7V~Y~-25H~-3Vˇ~Z", ""},
// czech specific - Č č Ň ň Ř ř Š š Ž ž Ť
{"C~Y~-25H~-9Vˇ~Z", "Č"},
{"c~Y~-24H~-5Vˇ~Z", "č"},
{"N~Y~-25H~-9Vˇ~Z", "Ň"},
{"n~Y~-24H~-5Vˇ~Z", "ň"},
{"R~Y~-25H~-9Vˇ~Z", "Ř"},
{"r~Y~-22H~-5Vˇ~Z", "ř"},
{"S~Y~-25H~-9Vˇ~Z", "Š"},
{"s~Y~-22H~-5Vˇ~Z", "š"},
{"T~Y~-24H~-7Vˇ~Z", "Ť"},
{"Z~Y~-25H~-9Vˇ~Z", "Ž"},
{"z~Y~-24H~-5Vˇ~Z", "ž"},
// dakuten katakana
{"~Yウ~Z゛", ""},
{"~Yカ~Z゛", ""},
{"~Yキ~Z゛", ""},
{"~Yク~Z゛", ""},
{"~Yケ~Z゛", ""},
{"~Yコ~Z゛", ""},
{"~Yサ~Z゛", ""},
{"~Yシ~Z゛", ""},
{"~Yス~Z゛", ""},
{"~Yセ~Z゛", ""},
{"~Yソ~Z゛", ""},
{"~Yタ~Z゛", ""},
{"~Yチ~Z゛", ""},
{"~Yツ~Z゛", ""},
{"~Yテ~Z゛", ""},
{"~Yト~Z゛", ""},
{"~Yハ~Z゛", ""},
{"~Yヒ~Z゛", ""},
{"~Yフ~Z゛", ""},
{"~Yヘ~Z゛", ""},
{"~Yホ~Z゛", ""},
// handakuten katakana
{"~Yハ~Z゜", ""},
{"~Yヒ~Z゜", ""},
{"~Yフ~Z゜", ""},
{"~Yヘ~Z゜", ""},
{"~Yホ~Z゜", ""},
// dakuten hiragana
{"~Yか~Z゛", ""},
{"~Yき~Z゛", ""},
{"~Yく~Z゛", ""},
{"~Yけ~Z゛", ""},
{"~Yこ~Z゛", ""},
{"~Yさ~Z゛", ""},
{"~Yし~Z゛", ""},
{"~Yす~Z゛", ""},
{"~Yせ~Z゛", ""},
{"~Yそ~Z゛", ""},
{"~Yた~Z゛", ""},
{"~Yち~Z゛", ""},
{"~Yつ~Z゛", ""},
{"~Yて~Z゛", ""},
{"~Yと~Z゛", ""},
{"~Yは~Z゛", ""},
{"~Yひ~Z゛", ""},
{"~Yふ~Z゛", ""},
{"~Yへ~Z゛", ""},
{"~Yほ~Z゛", ""},
// handakuten hiragana
{"~Yは~Z゜", ""},
{"~Yひ~Z゜", ""},
{"~Yふ~Z゜", ""},
{"~Yへ~Z゜", ""},
{"~Yほ~Z゜", ""},
// japanese punctuation
{",~+8H", ""},
{"~+8H ", " "},
// playstation buttons
// - face
{"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_X>"},
{"~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_TRIANGLE>"},
{"~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_CIRCLE>"},
{"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_SQUARE>"},
// - dpad
{"~Y~22L<PAD_PART_DPAD_L>~Z~3L~+17H~-13V<PAD_PART_DPAD_U>~Z~22L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_UP>"},
{"~Y~22L<PAD_PART_DPAD_L>~Z~3L~+17H~-13V<PAD_PART_DPAD_U>~Z~3L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_DOWN>"},
{"~Y~22L<PAD_PART_DPAD_L>~Z~22L~+17H~-13V<PAD_PART_DPAD_U>~Z~22L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_ANY>"},
// - shoulder
{"~Y~22L~-2H~-12V<PAD_PART_SHOULDER_TOP_LEFT><PAD_PART_SHOULDER_TOP_RIGHT>~Z~22L~-2H~+17V<PAD_"
"PART_SHOULDER_BOTTOM_LEFT><PAD_PART_SHOULDER_BOTTOM_RIGHT>~Z~1L~+4H~+3V<PAD_PART_L1_NAME>~Z~+"
"38H",
"<PAD_L1>"},
{"~Y~22L~-2H~-12V<PAD_PART_SHOULDER_TOP_LEFT><PAD_PART_SHOULDER_TOP_RIGHT>~Z~22L~-2H~+17V<PAD_"
"PART_SHOULDER_BOTTOM_LEFT><PAD_PART_SHOULDER_BOTTOM_RIGHT>~Z~1L~+6H~+3V<PAD_PART_R1_NAME>~Z~+"
"38H",
"<PAD_R1>"},
{"~Y~22L~-2H~-6V<PAD_PART_TRIGGER_TOP_LEFT><PAD_PART_TRIGGER_TOP_RIGHT>~Z~22L~-2H~+16V<PAD_"
"PART_TRIGGER_BOTTOM_LEFT><PAD_PART_TRIGGER_BOTTOM_RIGHT>~Z~1L~+5H~-2V<PAD_PART_R2_NAME>~Z~+"
"38H",
"<PAD_R2>"},
{"~Y~22L~-2H~-6V<PAD_PART_TRIGGER_TOP_LEFT><PAD_PART_TRIGGER_TOP_RIGHT>~Z~22L~-2H~+16V<PAD_"
"PART_TRIGGER_BOTTOM_LEFT><PAD_PART_TRIGGER_BOTTOM_RIGHT>~Z~1L~+5H~-2V<PAD_PART_L2_NAME>~Z~+"
"38H",
"<PAD_L2>"},
// - analog
{"~1L~+8H~Y<PAD_PART_STICK>~Z~6L~-16H<PAD_PART_STICK_LEFT>~Z~+16h~6L<PAD_PART_STICK_RIGHT>~Z~"
"6L~-15V<PAD_PART_STICK_DOWN>~Z~+13V~6L<PAD_PART_STICK_UP>~Z~-10H~+9V~6L<PAD_PART_STICK_UP_"
"LEFT>~Z~+10H~+9V~6L<PAD_PART_STICK_UP_RIGHT>~Z~-10H~-11V~6L<PAD_PART_STICK_DOWN_LEFT>~Z~+10H~"
"-11V~6L<PAD_PART_STICK_DOWN_RIGHT>~Z~+32H",
"<PAD_ANALOG_ANY>"},
{"~Y~1L~+8H<PAD_PART_STICK>~Z~6L~-8H<PAD_PART_STICK_LEFT>~Z~+24H~6L<PAD_PART_STICK_RIGHT>~Z~+"
"40H",
"<PAD_ANALOG_LEFT_RIGHT>"},
{"~Y~1L<PAD_PART_STICK>~Z~6L~-15V<PAD_PART_STICK_DOWN>~Z~+13V~6L<PAD_PART_STICK_UP>~Z~+26H",
"<PAD_ANALOG_UP_DOWN>"},
// icons
{"~Y~6L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_COMPLETE>"},
{"~Y~3L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_TODO>"},
// flags
{"~Y~6L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+15H~1L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+30H~3L<FLAG_"
"PART_VERT_STRIPE_LARGE>~Z~+45H",
"<FLAG_ITALIAN>"},
{"~Y~5L<FLAG_PART_FILL>~Z~3L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~5L<FLAG_PART_FILL>~Z~3L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~+26H",
"<FLAG_SPAIN>"},
{"~Y~39L~~~Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_HORZ_STRIPE_BOTTOM>~]~-1H~Y~39L~~~"
"Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_HORZ_STRIPE_BOTTOM>~Z~+26H",
"<FLAG_GERMAN>"},
{"~Y~7L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+15H~1L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+30H~3L<FLAG_"
"PART_VERT_STRIPE_LARGE>~Z~+47H",
"<FLAG_FRANCE>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_UK_CROSS_LEFT>~Z~7L<FLAG_PART_UK_FILL_LEFT>~]~-1H~Y~1L<"
"FLAG_PART_FILL>~Z~3L<FLAG_PART_UK_CROSS_RIGHT>~Z~7L<FLAG_PART_UK_FILL_RIGHT>~Z~+26H",
"<FLAG_UK>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_USA_STRIPES_LEFT>~Z~7L<FLAG_PART_USA_STARS>~]~-1H~Y~1L<"
"FLAG_PART_FILL>~Z~3L<FLAG_PART_USA_STRIPES_RIGHT>~Z~+26H",
"<FLAG_USA>"},
{"~Y~1L<FLAG_PART_FILL>~Z~39L<FLAG_PART_KOREA_TRIGRAMS_LEFT>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~39L<"
"FLAG_PART_KOREA_TRIGRAMS_RIGHT>~Z~-11H~7L<FLAG_PART_KOREA_CIRCLE_FILL>~Z~-11H~3L<FLAG_PART_"
"KOREA_CIRCLE_TOP>~Z~+26H",
"<FLAG_KOREA>"},
{"~Y~1L<FLAG_PART_FILL>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~-11H~3L<FLAG_PART_JAPAN_SUN>~Z~+26H",
"<FLAG_JAPAN>"},
{"~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~7L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_FINLAND>"},
{"~Y~7L<FLAG_PART_FILL>~Z~5L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~7L<FLAG_PART_FILL>~Z~5L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_SWEDEN>"},
{"~Y~3L<FLAG_PART_FILL>~Z~1L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~1L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~3L<FLAG_PART_FILL>~Z~1L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_DENMARK>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~-19H~1L<FLAG_PART_VERT_STRIPE_MIDDLE>~Z~-23H~7L<FLAG_PART_VERT_"
"STRIPE_RIGHT>~Z~-23H~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~"
"+26H",
"<FLAG_NORWAY>"},
{"~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~-19H~1L<FLAG_PART_VERT_STRIPE_MIDDLE>~Z~-23H~3L<FLAG_PART_VERT_"
"STRIPE_RIGHT>~Z~-23H~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~"
"+26H",
"<FLAG_ICELAND>"},
// korean jamo -- only relevant for the language selection since
// non-korean languages don't run through the `convert-korean-text` function and hence the
// encoding is "normal"
{"~Y~Z\\c03.~Z\\c03\\c1a~Z\\c03\\cc8~Y~Z\\c03œ~Z\\c03k~Z\\c03\\cde~Y~Z\\c03\\c0f~Z\\c03ç",
"<LANGUAGE_KOREAN>",
"~Y~Z\x03.~Z\x03\x1a~Z\x03\xc8~Y~Z\x03œ~Z\x03k~Z\x03\xde~Y~Z\x03\x0f~Z\x03ç"},
// weird stuff
// - descenders
{"~+7Vp~-7V", "p"},
{"~+7Vy~-7V", "y"},
{"~+7Vg~-7V", "g"},
{"~+7Vq~-7V", "q"},
{"~+1Vj~-1V", "j"},
{"\\\\",
"~%"}, // this is 2 slashes, duplicated because we use an escape sequence when decompiling
// - symbols and ligatures
{"~-4H~-3V<SOMETHING>~+3V~-4H",
"<SUPERSCRIPT_QUOTE>"}, // used for the 4<__> place in spanish. the 5th uses the same
// character but looks different...?
{"~Y~-6Hº~Z~+10H", "°"},
// Color / Emphasis
{"~[~0L", "<COLOR_DEFAULT>"},
{"~[~1L", "<COLOR_WHITE>"},
{"~[~2L", "<COLOR_TRANSPARENT>"},
{"~[~3L", "<COLOR_RED>"},
{"~[~4L", "<COLOR_ORANGE>"},
{"~[~5L", "<COLOR_YELLOW>"},
{"~[~6L", "<COLOR_GREEN>"},
{"~[~7L", "<COLOR_BLUE>"},
{"~[~8L", "<COLOR_CYAN>"},
{"~[~9L", "<COLOR_PINK>"},
{"~[~10L", "<COLOR_MENU-SELECTED>"},
{"~[~11L", "<COLOR_MENU-SELECTED-PARENT>"},
{"~[~12L", "<COLOR_MENU>"},
{"~[~13L", "<COLOR_MENU-PARENT>"},
{"~[~14L", "<COLOR_MENU-FUNC-BAD>"},
{"~[~15L", "<COLOR_MENU-FLAG-ON>"},
{"~[~16L", "<COLOR_MENU-FLAG-ON-PARENT>"},
{"~[~17L", "<COLOR_MENU-FLAG-OFF>"},
{"~[~18L", "<COLOR_MENU-FLAG-OFF-PARENT>"},
{"~[~19L", "<COLOR_MENU-INVALID>"},
{"~[~20L", "<COLOR_FLAT-YELLOW>"},
{"~[~21L", "<COLOR_COLOR-21>"},
{"~[~22L", "<COLOR_PAD-BACK>"},
{"~[~23L", "<COLOR_PAD-SHINE>"},
{"~[~24L", "<COLOR_PAD-SQUARE>"},
{"~[~25L", "<COLOR_PAD-CIRCLE>"},
{"~[~26L", "<COLOR_PAD-TRIANGLE>"},
{"~[~27L", "<COLOR_PAD-CROSS>"},
{"~[~28L", "<COLOR_PROGRESS-OLD-BLUE>"},
{"~[~29L", "<COLOR_PROGRESS-OLD-YELLOW>"},
{"~[~30L", "<COLOR_PROGRESS-OLD-SELECTED>"},
{"~[~31L", "<COLOR_PROGRESS-OLD-PERCENT>"},
{"~[~32L", "<COLOR_PROGRESS>"},
{"~[~33L", "<COLOR_PROGRESS-SELECTED>"},
{"~[~34L", "<COLOR_PROGRESS-FORCE-SELECTED>"},
{"~[~35L", "<COLOR_PROGRESS-OPTION-OFF>"},
{"~[~36L", "<COLOR_COLOR-36>"},
{"~[~37L", "<COLOR_CREDITS-STAFF-TITLE-1>"},
{"~[~38L", "<COLOR_CREDITS-STAFF-TITLE-2>"},
{"~[~39L", "<COLOR_COLOR-39>"}};
std::vector<EncodeInfo> encode_info_jak2 = {
{"ˇ", "\x10"}, // caron
{"`", "\x11"}, // grave accent
{"'", "\x12"}, // apostrophe
{"^", "\x13"}, // circumflex
{"<TIL>", "\x14"}, // tilde
{"¨", "\x15"}, // umlaut
{"º", "\x16"}, // numero/overring
{"¡", "\x17"}, // inverted exclamation mark
{"¿", "\x18"}, // inverted question mark
{"<SOMETHING>", "\x19"},
{"ç", "\x1d"}, // c-cedilla
{"Ç", "\x1e"}, // c-cedilla
{"ß", "\x1f"}, // eszett
{"œ", "\x5e"}, // ligature o+e
{"<FLAG_PART_HORZ_STRIPE_MIDDLE>", "\x7f"},
{"<FLAG_PART_HORZ_STRIPE_BOTTOM>", "\x80"},
{"<FLAG_PART_VERT_STRIPE_LARGE>", "\x81"},
{"<FLAG_PART_VERT_STRIPE_RIGHT>", "\x82"},
{"<FLAG_PART_VERT_STRIPE_LEFT>", "\x83"},
{"<FLAG_PART_VERT_STRIPE_MIDDLE>", "\x84"},
{"<FLAG_PART_FILL>", "\x85"},
{"<FLAG_PART_JAPAN_SUN>", "\x86"},
{"<FLAG_PART_KOREA_TRIGRAMS_LEFT>", "\x87"},
{"<FLAG_PART_KOREA_TRIGRAMS_RIGHT>", "\x88"},
{"<FLAG_PART_KOREA_CIRCLE_TOP>", "\x89"},
{"<FLAG_PART_KOREA_CIRCLE_FILL>", "\x8a"},
{"<FLAG_PART_TOP_BOTTOM_STRIPE>", "\x8b"},
{"<FLAG_PART_UK_CROSS_LEFT>", "\x8c"},
{"<FLAG_PART_UK_CROSS_RIGHT>", "\x8d"},
{"<FLAG_PART_UK_FILL_LEFT>", "\x8e"},
{"<FLAG_PART_UK_FILL_RIGHT>", "\x8f"},
{"<FLAG_PART_USA_STRIPES_RIGHT>", "\x90"},
{"<PAD_PART_STICK>", "\x91"},
{"<PAD_PART_SELECT>", "\x92"},
{"<PAD_PART_TRIGGER_BACK>", "\x93"},
{"<PAD_PART_R1_NAME>", "\x94"},
{"<PAD_PART_L1_NAME>", "\x95"},
{"<PAD_PART_R2_NAME>", "\x96"},
{"<PAD_PART_L2_NAME>", "\x97"},
{"<PAD_PART_STICK_UP>", "\x98"},
{"<PAD_PART_STICK_UP_RIGHT>", "\x99"},
{"<FLAG_PART_USA_STRIPES_LEFT>", "\x9a"},
{"<FLAG_PART_USA_STARS>", "\x9b"},
{"<PAD_PART_STICK_DOWN>", "\x9c"},
{"<PAD_PART_STICK_DOWN_LEFT>", "\x9d"},
{"<PAD_PART_STICK_LEFT>", "\x9e"},
{"<PAD_PART_STICK_UP_LEFT>", "\x9f"},
{"<PAD_PART_DPAD_D>", "\xa0"},
{"<PAD_PART_DPAD_L>", "\xa1"},
{"<PAD_PART_DPAD_U>", "\xa2"},
{"<PAD_PART_DPAD_R>", "\xa3"},
{"<PAD_PART_STICK_RIGHT>", "\xa4"},
{"<PAD_PART_STICK_DOWN_RIGHT>", "\xa5"},
{"<PAD_PART_SHOULDER_TOP_LEFT>", "\xa6"},
{"<PAD_PART_SHOULDER_TOP_RIGHT>", "\xa7"},
{"<PAD_PART_TRIGGER_TOP_LEFT>", "\xa8"},
{"<PAD_PART_TRIGGER_TOP_RIGHT>", "\xa9"},
{"<PAD_PART_TRIGGER_SHIM1>", "\xaa"},
{"<PAD_PART_TRIGGER_SHIM2>", "\xab"},
{"<PAD_PART_SHOULDER_SHIM2>", "\xac"},
{"<PAD_PART_SHOULDER_BOTTOM_LEFT>", "\xb0"},
{"<PAD_PART_SHOULDER_BOTTOM_RIGHT>", "\xb1"},
{"<PAD_PART_TRIGGER_BOTTOM_LEFT>", "\xb2"},
{"<PAD_PART_TRIGGER_BOTTOM_RIGHT>", "\xb3"},
// {"入", "\x1\x00"}},
// {"年", "\x1\x01"}},
// punctuation
{"", "\x1\x10"},
{"", "\x1\x11"},
{"", "\x1\x12"},
{"", "\x1\x13"},
{"", "\x1\x14"},
{"", "\x1\x15"},
// hiragana
{"", "\x1\x16"}, // -a
{"", "\x1\x17"}, // a
{"", "\x1\x18"}, // -i
{"", "\x1\x19"}, // i
{"", "\x1\x1a"}, // -u
{"", "\x1\x1b"}, // u
{"", "\x1\x1c"}, // -e
{"", "\x1\x1d"}, // e
{"", "\x1\x1e"}, // -o
{"", "\x1\x1f"}, // o
{"", "\x1\x20"}, // ka
{"", "\x1\x21"}, // ki
{"", "\x1\x22"}, // ku
{"", "\x1\x23"}, // ke
{"", "\x1\x24"}, // ko
{"", "\x1\x25"}, // sa
{"", "\x1\x26"}, // shi
{"", "\x1\x27"}, // su
{"", "\x1\x28"}, // se
{"", "\x1\x29"}, // so
{"", "\x1\x2a"}, // ta
{"", "\x1\x2b"}, // chi
{"", "\x1\x2c"}, // sokuon
{"", "\x1\x2d"}, // tsu
{"", "\x1\x2e"}, // te
{"", "\x1\x2f"}, // to
{"", "\x1\x30"}, // na
{"", "\x1\x31"}, // ni
{"", "\x1\x32"}, // nu
{"", "\x1\x33"}, // ne
{"", "\x1\x34"}, // no
{"", "\x1\x35"}, // ha
{"", "\x1\x36"}, // hi
{"", "\x1\x37"}, // fu
{"", "\x1\x38"}, // he
{"", "\x1\x39"}, // ho
{"", "\x1\x3a"}, // ma
{"", "\x1\x3b"}, // mi
{"", "\x1\x3c"}, // mu
{"", "\x1\x3d"}, // me
{"", "\x1\x3e"}, // mo
{"", "\x1\x3f"}, // youon ya
{"", "\x1\x40"}, // ya
{"", "\x1\x41"}, // youon yu
{"", "\x1\x42"}, // yu
{"", "\x1\x43"}, // youon yo
{"", "\x1\x44"}, // yo
{"", "\x1\x45"}, // ra
{"", "\x1\x46"}, // ri
{"", "\x1\x47"}, // ru
{"", "\x1\x48"}, // re
{"", "\x1\x49"}, // ro
{"", "\x1\x4a"}, // -wa
{"", "\x1\x4b"}, // wa
{"", "\x1\x4c"}, // wo
{"", "\x1\x4d"}, // -n
// katakana
{"", "\x1\x4e"}, // -a
{"", "\x1\x4f"}, // a
{"", "\x1\x50"}, // -i
{"", "\x1\x51"}, // i
{"", "\x1\x52"}, // -u
{"", "\x1\x53"}, // u
{"", "\x1\x54"}, // -e
{"", "\x1\x55"}, // e
{"", "\x1\x56"}, // -o
{"", "\x1\x57"}, // o
{"", "\x1\x58"}, // ka
{"", "\x1\x59"}, // ki
{"", "\x1\x5a"}, // ku
{"", "\x1\x5b"}, // ke
{"", "\x1\x5c"}, // ko
{"", "\x1\x5d"}, // sa
{"", "\x1\x5e"}, // shi
{"", "\x1\x5f"}, // su
{"", "\x1\x60"}, // se
{"", "\x1\x61"}, // so
{"", "\x1\x62"}, // ta
{"", "\x1\x63"}, // chi
{"", "\x1\x64"}, // sokuon
{"", "\x1\x65"}, // tsu
{"", "\x1\x66"}, // te
{"", "\x1\x67"}, // to
{"", "\x1\x68"}, // na
{"", "\x1\x69"}, // ni
{"", "\x1\x6a"}, // nu
{"", "\x1\x6b"}, // ne
{"", "\x1\x6c"}, // no
{"", "\x1\x6d"}, // ha
{"", "\x1\x6e"}, // hi
{"", "\x1\x6f"}, // fu
{"", "\x1\x70"}, // he
{"", "\x1\x71"}, // ho
{"", "\x1\x72"}, // ma
{"", "\x1\x73"}, // mi
{"", "\x1\x74"}, // mu
{"", "\x1\x75"}, // me
{"", "\x1\x76"}, // mo
{"", "\x1\x77"}, // youon ya
{"", "\x1\x78"}, // ya
{"", "\x1\x79"}, // youon yu
{"", "\x1\x7a"}, // yu
{"", "\x1\x7b"}, // youon yo
{"", "\x1\x7c"}, // yo
{"", "\x1\x7d"}, // ra
{"", "\x1\x7e"}, // ri
{"", "\x1\x7f"}, // ru
{"", "\x1\x80"}, // re
{"", "\x1\x81"}, // ro
{"", "\x1\x82"}, // -wa
{"", "\x1\x83"}, // wa
{"", "\x1\x84"}, // wo
{"", "\x1\x85"}, // -n
{"", "\x1\x8c"},
{"", "\x1\x8d"},
{"", "\x1\x8e"},
{"", "\x1\x8f"},
{"", "\x1\x90"},
{"", "\x1\x91"},
{"", "\x1\x92"},
{"", "\x1\x93"},
{"", "\x1\x94"},
{"", "\x1\x95"},
{"", "\x1\x96"},
{"", "\x1\x97"},
{"", "\x1\x98"},
{"", "\x1\x99"},
{"", "\x1\x9a"},
{"", "\x1\x9b"},
{"", "\x1\x9c"},
{"", "\x1\x9d"},
{"", "\x1\x9e"},
{"", "\x1\x9f"},
{"", "\x1\xa0"},
{"", "\x1\xa1"},
{"", "\x1\xa2"},
{"", "\x1\xa3"},
{"", "\x1\xa4"},
{"", "\x1\xa5"},
{"", "\x1\xa6"},
{"", "\x1\xa7"},
{"", "\x1\xa8"},
{"", "\x1\xa9"},
{"", "\x1\xaa"},
{"", "\x1\xab"},
{"", "\x1\xac"},
{"", "\x1\xad"},
{"", "\x1\xae"},
{"", "\x1\xaf"},
{"", "\x1\xb0"},
{"", "\x1\xb1"},
{"", "\x1\xb2"},
{"", "\x1\xb3"},
{"", "\x1\xb4"},
{"", "\x1\xb5"},
{"", "\x1\xb6"},
{"", "\x1\xb7"},
{"", "\x1\xb8"},
{"", "\x1\xb9"},
{"", "\x1\xba"},
{"", "\x1\xbb"},
{"", "\x1\xbc"},
{"", "\x1\xbd"},
{"", "\x1\xbe"},
{"", "\x1\xbf"},
{"使", "\x1\xc0"},
{"", "\x1\xc1"},
{"", "\x1\xc2"},
{"", "\x1\xc3"},
{"", "\x1\xc4"},
{"", "\x1\xc5"},
{"", "\x1\xc6"},
{"", "\x1\xc7"},
{"", "\x1\xc8"},
{"", "\x1\xc9"},
{"", "\x1\xca"},
{"", "\x1\xcb"},
{"", "\x1\xcc"},
{"", "\x1\xcd"},
{"", "\x1\xce"},
{"", "\x1\xcf"},
{"", "\x1\xd0"},
{"", "\x1\xd1"},
{"", "\x1\xd2"},
{"", "\x1\xd3"},
{"", "\x1\xd4"},
{"", "\x1\xd5"},
{"", "\x1\xd6"},
{"", "\x1\xd7"},
{"", "\x1\xd8"},
{"", "\x1\xd9"},
{"", "\x1\xda"},
{"", "\x1\xdb"},
{"", "\x1\xdc"},
{"", "\x1\xdd"},
{"", "\x1\xde"},
{"", "\x1\xdf"},
{"", "\x1\xe0"},
{"", "\x1\xe1"},
{"", "\x1\xe2"},
{"", "\x1\xe3"},
{"", "\x1\xe4"},
{"", "\x1\xe5"},
{"", "\x1\xe6"},
{"", "\x1\xe7"},
{"", "\x1\xe8"},
{"", "\x1\xe9"},
{"", "\x1\xea"},
{"", "\x1\xeb"},
{"", "\x1\xec"},
{"", "\x1\xed"},
{"", "\x1\xee"},
{"", "\x1\xef"},
{"", "\x1\xf0"},
{"", "\x1\xf1"},
{"", "\x1\xf2"},
{"", "\x1\xf3"},
{"", "\x1\xf4"},
{"", "\x1\xf5"},
{"", "\x1\xf6"},
{"", "\x1\xf7"},
{"", "\x1\xf8"},
{"", "\x1\xf9"},
{"", "\x1\xfa"},
{"", "\x1\xfb"},
{"", "\x1\xfc"},
{"", "\x1\xfd"},
{"", "\x1\xfe"},
{"", "\x1\xff"},
{"", "\x2\x10"},
{"", "\x2\x11"},
{"", "\x2\x12"},
{"", "\x2\x13"},
{"", "\x2\x14"},
{"", "\x2\x15"},
{"", "\x2\x16"},
{"", "\x2\x17"},
{"", "\x2\x18"},
{"", "\x2\x19"},
{"", "\x2\x1a"},
{"", "\x2\x1b"},
{"", "\x2\x1c"},
{"", "\x2\x1d"},
{"", "\x2\x1e"},
{"", "\x2\x1f"},
{"", "\x2\x20"},
{"", "\x2\x21"},
{"", "\x2\x22"},
{"", "\x2\x23"},
{"", "\x2\x24"},
{"", "\x2\x25"},
{"", "\x2\x26"},
{"", "\x2\x27"},
{"", "\x2\x28"},
{"", "\x2\x29"},
{"", "\x2\x2a"},
{"", "\x2\x2b"},
{"", "\x2\x2c"},
{"", "\x2\x2d"},
{"", "\x2\x2e"},
{"", "\x2\x2f"},
{"", "\x2\x30"},
{"", "\x2\x31"},
{"", "\x2\x32"},
{"", "\x2\x33"},
// {"成", "\x2\x34"},
{"", "\x2\x35"},
{"", "\x2\x36"},
{"", "\x2\x37"},
{"", "\x2\x38"},
{"", "\x2\x39"},
{"", "\x2\x3a"},
{"", "\x2\x3b"},
{"", "\x2\x3c"},
{"", "\x2\x3d"},
{"", "\x2\x3e"},
{"", "\x2\x3f"},
{"", "\x2\x40"},
{"", "\x2\x41"},
// {"乗", "\x2\x42"},
{"", "\x2\x43"},
{"", "\x2\x44"},
{"", "\x2\x45"},
{"", "\x2\x46"},
{"", "\x2\x47"},
{"", "\x2\x48"},
{"", "\x2\x49"},
{"", "\x2\x4a"},
{"", "\x2\x4b"},
// {"対", "\x2\x4c"},
{"", "\x2\x4d"},
{"", "\x2\x4e"},
{"", "\x2\x4f"},
{"", "\x2\x50"},
{"", "\x2\x56"},
{"", "\x2\x57"},
{"", "\x2\x58"},
{"", "\x2\x59"},
{"", "\x2\x5a"},
{"", "\x2\x5b"},
{"", "\x2\x5c"},
{"", "\x2\x5d"},
{"", "\x2\x5e"},
{"", "\x2\x5f"},
{"", "\x2\x60"},
{"", "\x2\x61"},
{"", "\x2\x62"},
{"", "\x2\x63"},
{"", "\x2\x64"},
// {"高", "\x2\x65"},
{"", "\x2\x66"},
{"", "\x2\x67"},
{"", "\x2\x68"},
{"", "\x2\x69"},
{"", "\x2\x6a"},
{"", "\x2\x6b"},
{"", "\x2\x6c"},
{"", "\x2\x6d"},
{"", "\x2\x6e"},
{"", "\x2\x6f"},
{"", "\x2\x70"},
{"", "\x2\x71"},
{"", "\x2\x72"},
{"", "\x2\x73"},
{"", "\x2\x74"},
{"", "\x2\x75"},
{"退", "\x2\x76"},
{"", "\x2\x77"},
{"", "\x2\x78"},
{"", "\x2\x79"},
{"", "\x2\x7a"},
{"", "\x2\x7b"},
{"", "\x2\x7c"},
{"", "\x2\x7d"},
{"", "\x2\x7e"},
{"", "\x2\x7f"},
{"", "\x2\x80"},
{"", "\x2\x81"},
{"", "\x2\x82"},
{"", "\x2\x83"},
{"", "\x2\x84"},
{"", "\x2\x85"},
{"", "\x2\x86"},
{"", "\x2\x88"},
{"", "\x2\x89"},
{"", "\x2\x8a"},
{"", "\x2\x8b"},
{"", "\x2\x8c"},
{"", "\x2\x8d"},
};
std::unordered_map<std::string, std::vector<std::string>> jamo_glyph_mappings_jak2 = {
{"0x06", {""}}, {"0x07", {""}}, {"0x08", {""}}, {"0x09", {""}},
{"0x0a", {""}}, {"0x0b", {""}}, {"0x0c", {""}}, {"0x0d", {""}},
{"0x0e", {""}}, {"0x0f", {""}}, {"0x10", {""}}, {"0x11", {""}},
{"0x12", {""}}, {"0x13", {""}}, {"0x14", {""}}, {"0x15", {""}},
{"0x16", {""}}, {"0x17", {""}}, {"0x18", {""}}, {"0x19", {""}},
{"0x1a", {""}}, {"0x1b", {""}}, {"0x1c", {""}}, {"0x1d", {""}},
{"0x1e", {""}}, {"0x1f", {""}}, {"0x20", {""}}, {"0x21", {""}},
{"0x22", {""}}, {"0x23", {""}}, {"0x24", {""}}, {"0x25", {""}},
{"0x26", {""}}, {"0x27", {""}}, {"0x28", {""}}, {"0x29", {""}},
{"0x2a", {""}}, {"0x2b", {""}}, {"0x2c", {""}}, {"0x2d", {""}},
{"0x2e", {""}}, {"0x2f", {""}}, {"0x30", {""}}, {"0x31", {""}},
{"0x32", {""}}, {"0x33", {""}}, {"0x34", {""}}, {"0x35", {""}},
{"0x36", {""}}, {"0x37", {""}}, {"0x38", {""}}, {"0x39", {""}},
{"0x3a", {""}}, {"0x3b", {""}}, {"0x3c", {""}}, {"0x3d", {""}},
{"0x3e", {""}}, {"0x3f", {""}}, {"0x40", {""}}, {"0x41", {""}},
{"0x42", {""}}, {"0x43", {""}}, {"0x44", {""}}, {"0x45", {""}},
{"0x46", {""}}, {"0x47", {""}}, {"0x48", {""}}, {"0x49", {""}},
{"0x4a", {""}}, {"0x4b", {""}}, {"0x4c", {""}}, {"0x4d", {""}},
{"0x4e", {""}}, {"0x4f", {""}}, {"0x50", {""}}, {"0x51", {""}},
{"0x52", {""}}, {"0x53", {""}}, {"0x54", {""}}, {"0x55", {""}},
{"0x56", {""}}, {"0x57", {""}}, {"0x58", {""}}, {"0x59", {""}},
{"0x5a", {""}}, {"0x5b", {""}}, {"0x5c", {""}}, {"0x5d", {""}},
{"0x5e", {""}}, {"0x5f", {""}}, {"0x60", {""}}, {"0x61", {""}},
{"0x62", {""}}, {"0x63", {""}}, {"0x64", {"", ""}}, {"0x65", {"", ""}},
{"0x66", {"", ""}}, {"0x67", {"", ""}}, {"0x68", {""}}, {"0x69", {""}},
{"0x6a", {""}}, {"0x6b", {""}}, {"0x6c", {""}}, {"0x6d", {""}},
{"0x6e", {""}}, {"0x6f", {""}}, {"0x70", {""}}, {"0x71", {""}},
{"0x72", {""}}, {"0x73", {""}}, {"0x74", {""}}, {"0x75", {""}},
{"0x76", {""}}, {"0x77", {""}}, {"0x78", {""}}, {"0x79", {""}},
{"0x7a", {""}}, {"0x7b", {""}}, {"0x7c", {""}}, {"0x7d", {""}},
{"0x7e", {""}}, {"0x7f", {""}}, {"0x80", {""}}, {"0x81", {""}},
{"0x82", {""}}, {"0x83", {""}}, {"0x84", {""}}, {"0x85", {""}},
{"0x86", {""}}, {"0x87", {""}}, {"0x88", {""}}, {"0x89", {"", ""}},
{"0x8a", {"", ""}}, {"0x8b", {""}}, {"0x8c", {""}}, {"0x8d", {""}},
{"0x8e", {""}}, {"0x8f", {"", ""}}, {"0x90", {"", ""}}, {"0x91", {""}},
{"0x92", {""}}, {"0x93", {""}}, {"0x94", {""}}, {"0x95", {""}},
{"0x96", {""}}, {"0x97", {""}}, {"0x98", {""}}, {"0x99", {""}},
{"0x9a", {""}}, {"0x9b", {""}}, {"0x9c", {""}}, {"0x9d", {""}},
{"0x9e", {""}}, {"0x9f", {""}}, {"0xa0", {""}}, {"0xa1", {""}},
{"0xa2", {""}}, {"0xa3", {""}}, {"0xa4", {""}}, {"0xa5", {""}},
{"0xa6", {""}}, {"0xa7", {""}}, {"0xa8", {""}}, {"0xa9", {""}},
{"0xaa", {""}}, {"0xab", {""}}, {"0xac", {""}}, {"0xad", {""}},
{"0xae", {""}}, {"0xaf", {""}}, {"0xb0", {""}}, {"0xb1", {""}},
{"0xb2", {""}}, {"0xb3", {""}}, {"0xb4", {""}}, {"0xb5", {""}},
{"0xb6", {""}}, {"0xb7", {""}}, {"0xb8", {""}}, {"0xb9", {""}},
{"0xba", {""}}, {"0xbb", {""}}, {"0xbc", {""}}, {"0xbd", {""}},
{"0xbe", {""}}, {"0xbf", {""}}, {"0xc0", {""}}, {"0xc1", {""}},
{"0xc2", {""}}, {"0xc3", {""}}, {"0xc4", {""}}, {"0xc5", {""}},
{"0xc6", {""}}, {"0xc7", {""}}, {"0xc8", {""}}, {"0xc9", {""}},
{"0xca", {""}}, {"0xcb", {""}}, {"0xcc", {""}}, {"0xcd", {""}},
{"0xce", {""}}, {"0xcf", {""}}, {"0xd0", {""}}, {"0xd1", {""}},
{"0xd2", {""}}, {"0xd3", {""}}, {"0xd4", {""}}, {"0xd5", {""}},
{"0xd6", {""}}, {"0xd7", {""}}, {"0xd8", {""}}, {"0xd9", {""}},
{"0xda", {""}}, {"0xdb", {""}}, {"0xdc", {""}}, {"0xdd", {""}},
{"0xde", {""}}, {"0xdf", {""}}, {"0xe0", {""}}, {"0xe1", {""}},
{"0xe2", {""}}, {"0xe3", {""}}, {"0xe4", {""}}, {"0xe5", {""}},
{"0xe6", {""}}, {"0xe7", {""}}, {"0xe8", {""}}, {"0xe9", {""}},
{"0xea", {""}}, {"0xeb", {""}}, {"0xec", {""}}, {"0xed", {""}},
{"0xee", {""}}, {"0xef", {""}}, {"0xf0", {""}}, {"0xf1", {""}},
{"0xf2", {""}}, {"0xf3", {""}}, {"0xf4", {""}}, {"0xf5", {""}},
{"0xf6", {""}}, {"0xf7", {""}}, {"0xf8", {""}}, {"0xf9", {""}},
{"0xfa", {""}}, {"0xfb", {""}}, {"0xfc", {""}}, {"0xfd", {""}},
{"0xfe", {""}}, {"0xff", {""}}, {"extra_0x86", {""}}, {"extra_0x87", {""}},
{"extra_0x88", {""}}, {"extra_0x89", {""}}, {"extra_0x8a", {""}}, {"extra_0x8b", {""}},
};
GameTextFontBank g_font_bank_jak2(GameTextVersion::JAK2,
&encode_info_jak2,
&replace_info_jak2,
&passthrus_jak2);
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "common/util/font/font_utils.h"
extern std::unordered_map<std::string, std::vector<std::string>> jamo_glyph_mappings_jak2;
/*!
* ================================
* GAME TEXT FONT BANK - JAK 2
* ================================
* This font is used in:
* - Jak II
* - Jak II: Renegade
* - 2
*/
extern GameTextFontBank g_font_bank_jak2;
+794
View File
@@ -0,0 +1,794 @@
#include "font_db_jak3.h"
std::unordered_set<char> passthrus_jak3 = {'~', ' ', ',', '.', '-', '+', '(', ')',
'!', ':', '?', '=', '%', '*', '/', '#',
';', '<', '>', '@', '[', '_', ']'};
std::vector<ReplaceInfo> replace_info_jak3 = {
// other
{"A~Y~-21H~-5Vº~Z", "Å"},
{"N~Y~-6Hº~Z~+10H", ""},
{"~+4Vç~-4V", ",c"},
// added for translations TODO - check these for jak 2
{"O~Y~-25H~-1V/~Z", "Ø"},
{"o~Y~-23H~+4V/~Z", "ø"},
{"A~Y~-13H~+8V,~Z", "Ą"},
{"a~Y~-8H~+6V,~Z", "ą"},
{"E~Y~-6H~+8V,~Z", "Ę"},
{"e~Y~-10H~+7V,~Z", "ę"},
{"L~Y~-21H~+0V/~Z", "Ł"},
{"l~Y~-16H~+0V/~Z", "ł"}, // TODO - this one is ugly, font character addition (small slash)
{"Z~Y~-25H~-11Vº~Z", "Ż"},
{"z~Y~-23H~-5Vº~Z", "ż"},
{"a~Y~-25H~-5Vº~Z", "å"},
{"S~Y~-21H~-5V'~Z", "Ś"},
{"s~Y~-25H~-5V'~Z", "ś"},
{"n~Y~-25H~-5V'~Z", "ń"},
{"c~Y~-25H~-5V'~Z", "ć"},
{"o~Y~-24H~-4V<TIL>~Z", "õ"},
{"a~Y~-24H~-4V<TIL>~Z", "ã"},
{"O~Y~-28H~-4V'~-9H'~Z", "Ő"},
{"U~Y~-27H~-4V'~-12H'~Z", "Ű"},
{"o~Y~-28H~-4V'~-9H'~Z", "ő"},
{"u~Y~-28H~-4V'~-9H'~Z", "ű"},
{"E~Y~-22H~-11Vº~Z", "Ė"},
{"e~Y~-25H~-5Vº~Z", "ė"},
{"C~Y~-27H~-10Vˇ~Z", "Č"},
{"c~Y~-25H~-5Vˇ~Z", "č"},
{"S~Y~-24H~-10Vˇ~Z", "Š"},
{"s~Y~-22H~-4Vˇ~Z", "š"},
{"Z~Y~-25H~-10Vˇ~Z", "Ž"},
{"z~Y~-23H~-4Vˇ~Z", "ž"},
{"U~Y~-15H~+5V,~Z", "Ų"},
{"u~Y~-15H~+5V,~Z", "ų"},
{"U~Y~-20H~-18V-~Z", "Ū"},
{"u~Y~-18H~-15V-~Z", "ū"},
{"D~Y~-28H~-1V-~Z", "Đ"},
{"d~Y~-13H~-10V-~Z", "đ"},
{"I~Y~-8H~+4V,~Z", "Į"},
{"i~Y~-8H~+4V,~Z", "į"},
// czech specific
{"U~Y~-24H~-7Vº~Z", "Ů"},
{"u~Y~-23H~-5Vº~Z", "ů"},
{"t~Y~-7H~-21V,~Z", "ť"},
// tildes
{"N~Y~-22H~-4V<TIL>~Z", "Ñ"},
{"n~Y~-24H~-4V<TIL>~Z", "ñ"},
{"A~Y~-21H~-5V<TIL>~Z", "Ã"},
{"O~Y~-22H~-4V<TIL>~Z", "Õ"},
// acute accents
{"A~Y~-21H~-5V'~Z", "Á"},
{"A~Y~-26H~-8V'~Z", "<Á_V2>"}, // unfortunate...
{"a~Y~-25H~-5V'~Z", "á"},
{"E~Y~-23H~-9V'~Z", "É"},
{"e~Y~-26H~-5V'~Z", "é"},
{"I~Y~-19H~-5V'~Z", "Í"},
{"i~Y~-19H~-8V'~Z", "í"},
{"O~Y~-22H~-4V'~Z", "Ó"},
{"o~Y~-26H~-4V'~Z", "ó"},
{"U~Y~-24H~-3V'~Z", "Ú"},
{"u~Y~-24H~-3V'~Z", "ú"},
{"Z~Y~-24H~-3V'~Z", "Ź"},
{"z~Y~-24H~-3V'~Z", "ź"},
// czech specific
{"Y~Y~-26H~-5V'~Z", "Ý"},
{"~+7Vy~-7V~Y~-24H~-3V'~Z", "ý"},
// circumflex
{"A~Y~-20H~-4V^~Z", "Â"},
{"a~Y~-24H~-5V^~Z", "â"},
{"E~Y~-20H~-5V^~Z", "Ê"},
{"e~Y~-25H~-4V^~Z", "ê"},
{"I~Y~-19H~-5V^~Z", "Î"},
{"i~Y~-19H~-8V^~Z", "î"},
{"O~Y~-20H~-4V^~Z", "Ô"},
{"o~Y~-25H~-4V^~Z", "ô"},
{"U~Y~-24H~-3V^~Z", "Û"},
{"u~Y~-23H~-3V^~Z", "û"},
// grave accents
{"A~Y~-26H~-8V`~Z", "À"},
{"a~Y~-25H~-5V`~Z", "à"},
{"E~Y~-23H~-9V`~Z", "È"},
{"e~Y~-26H~-5V`~Z", "è"},
{"I~Y~-19H~-5V`~Z", "Ì"},
{"i~Y~-19H~-8V`~Z", "ì"},
{"O~Y~-22H~-4V`~Z", "Ò"},
{"o~Y~-26H~-4V`~Z", "ò"},
{"U~Y~-24H~-3V`~Z", "Ù"},
{"u~Y~-24H~-3V`~Z", "ù"},
// umlaut
{"A~Y~-26H~-8V¨~Z", "Ä"},
{"a~Y~-25H~-5V¨~Z", "ä"},
{"E~Y~-20H~-5V¨~Z", "Ë"},
{"e~Y~-25H~-5V¨~Z", "ë"},
{"I~Y~-19H~-5V¨~Z", "Ï"},
{"i~Y~-26H~-4V¨~Z", "ï"},
{"O~Y~-26H~-8V¨~Z", "Ö"},
{"o~Y~-26H~-4V¨~Z", "ö"},
{"U~Y~-25H~-8V¨~Z", "Ü"},
{"u~Y~-24H~-3V¨~Z", "ü"},
// caron - Ǎ ǎ Ě ě Ǧ ǧ Ǐ ǐ Ǒ ǒ Ǔ ǔ Y̌ y̌
{"A~Y~-25H~-9Vˇ~Z", "Ǎ"},
{"a~Y~-24H~-5Vˇ~Z", "ǎ"},
{"E~Y~-22H~-8Vˇ~Z", "Ě"},
{"e~Y~-25H~-4Vˇ~Z", "ě"},
{"G~Y~-24H~-8Vˇ~Z", "Ǧ"},
{"~+7Vg~-7V~Y~-25H~-4Vˇ~Z", "ǧ"},
{"I~Y~-19H~-8Vˇ~Z", "Ǐ"},
{"i~Y~-19H~-8Vˇ~Z", "ǐ"},
{"O~Y~-25H~-7Vˇ~Z", "Ǒ"},
{"o~Y~-25H~-4Vˇ~Z", "ǒ"},
{"U~Y~-25H~-6Vˇ~Z", "Ǔ"},
{"u~Y~-24H~-3Vˇ~Z", "ǔ"},
{"Y~Y~-25H~-5Vˇ~Z", ""},
{"~+7Vy~-7V~Y~-25H~-3Vˇ~Z", ""},
// czech specific - Č č Ň ň Ř ř Š š Ž ž Ť
{"C~Y~-25H~-9Vˇ~Z", "Č"},
{"c~Y~-24H~-5Vˇ~Z", "č"},
{"N~Y~-25H~-9Vˇ~Z", "Ň"},
{"n~Y~-24H~-5Vˇ~Z", "ň"},
{"R~Y~-25H~-9Vˇ~Z", "Ř"},
{"r~Y~-22H~-5Vˇ~Z", "ř"},
{"S~Y~-25H~-9Vˇ~Z", "Š"},
{"s~Y~-22H~-5Vˇ~Z", "š"},
{"T~Y~-24H~-7Vˇ~Z", "Ť"},
{"Z~Y~-25H~-9Vˇ~Z", "Ž"},
{"z~Y~-24H~-5Vˇ~Z", "ž"},
// dakuten katakana
{"~Yウ~Z゛", ""},
{"~Yカ~Z゛", ""},
{"~Yキ~Z゛", ""},
{"~Yク~Z゛", ""},
{"~Yケ~Z゛", ""},
{"~Yコ~Z゛", ""},
{"~Yサ~Z゛", ""},
{"~Yシ~Z゛", ""},
{"~Yス~Z゛", ""},
{"~Yセ~Z゛", ""},
{"~Yソ~Z゛", ""},
{"~Yタ~Z゛", ""},
{"~Yチ~Z゛", ""},
{"~Yツ~Z゛", ""},
{"~Yテ~Z゛", ""},
{"~Yト~Z゛", ""},
{"~Yハ~Z゛", ""},
{"~Yヒ~Z゛", ""},
{"~Yフ~Z゛", ""},
{"~Yヘ~Z゛", ""},
{"~Yホ~Z゛", ""},
// handakuten katakana
{"~Yハ~Z゜", ""},
{"~Yヒ~Z゜", ""},
{"~Yフ~Z゜", ""},
{"~Yヘ~Z゜", ""},
{"~Yホ~Z゜", ""},
// dakuten hiragana
{"~Yか~Z゛", ""},
{"~Yき~Z゛", ""},
{"~Yく~Z゛", ""},
{"~Yけ~Z゛", ""},
{"~Yこ~Z゛", ""},
{"~Yさ~Z゛", ""},
{"~Yし~Z゛", ""},
{"~Yす~Z゛", ""},
{"~Yせ~Z゛", ""},
{"~Yそ~Z゛", ""},
{"~Yた~Z゛", ""},
{"~Yち~Z゛", ""},
{"~Yつ~Z゛", ""},
{"~Yて~Z゛", ""},
{"~Yと~Z゛", ""},
{"~Yは~Z゛", ""},
{"~Yひ~Z゛", ""},
{"~Yふ~Z゛", ""},
{"~Yへ~Z゛", ""},
{"~Yほ~Z゛", ""},
// handakuten hiragana
{"~Yは~Z゜", ""},
{"~Yひ~Z゜", ""},
{"~Yふ~Z゜", ""},
{"~Yへ~Z゜", ""},
{"~Yほ~Z゜", ""},
// japanese punctuation
{",~+8H", ""},
{"~+8H ", " "},
// playstation buttons
// - face
{"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_X>"},
{"~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_TRIANGLE>"},
{"~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_CIRCLE>"},
{"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_SQUARE>"},
// - dpad
{"~Y~22L<PAD_PART_DPAD_L>~Z~3L~+17H~-13V<PAD_PART_DPAD_U>~Z~22L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_UP>"},
{"~Y~22L<PAD_PART_DPAD_L>~Z~3L~+17H~-13V<PAD_PART_DPAD_U>~Z~3L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_DOWN>"},
{"~Y~22L<PAD_PART_DPAD_L>~Z~22L~+17H~-13V<PAD_PART_DPAD_U>~Z~22L~+17H~+14V<PAD_PART_DPAD_D>~Z~"
"22L~+32H<PAD_PART_DPAD_R>~Z~+56H",
"<PAD_DPAD_ANY>"},
// - shoulder
{"~Y~22L~-2H~-12V<PAD_PART_SHOULDER_TOP_LEFT><PAD_PART_SHOULDER_TOP_RIGHT>~Z~22L~-2H~+17V<PAD_"
"PART_SHOULDER_BOTTOM_LEFT><PAD_PART_SHOULDER_BOTTOM_RIGHT>~Z~1L~+4H~+3V<PAD_PART_L1_NAME>~Z~+"
"38H",
"<PAD_L1>"},
{"~Y~22L~-2H~-12V<PAD_PART_SHOULDER_TOP_LEFT><PAD_PART_SHOULDER_TOP_RIGHT>~Z~22L~-2H~+17V<PAD_"
"PART_SHOULDER_BOTTOM_LEFT><PAD_PART_SHOULDER_BOTTOM_RIGHT>~Z~1L~+6H~+3V<PAD_PART_R1_NAME>~Z~+"
"38H",
"<PAD_R1>"},
{"~Y~22L~-2H~-6V<PAD_PART_TRIGGER_TOP_LEFT><PAD_PART_TRIGGER_TOP_RIGHT>~Z~22L~-2H~+16V<PAD_"
"PART_TRIGGER_BOTTOM_LEFT><PAD_PART_TRIGGER_BOTTOM_RIGHT>~Z~1L~+5H~-2V<PAD_PART_R2_NAME>~Z~+"
"38H",
"<PAD_R2>"},
{"~Y~22L~-2H~-6V<PAD_PART_TRIGGER_TOP_LEFT><PAD_PART_TRIGGER_TOP_RIGHT>~Z~22L~-2H~+16V<PAD_"
"PART_TRIGGER_BOTTOM_LEFT><PAD_PART_TRIGGER_BOTTOM_RIGHT>~Z~1L~+5H~-2V<PAD_PART_L2_NAME>~Z~+"
"38H",
"<PAD_L2>"},
// - analog
{"~1L~+8H~Y<PAD_PART_STICK>~Z~6L~-16H<PAD_PART_STICK_LEFT>~Z~+16h~6L<PAD_PART_STICK_RIGHT>~Z~"
"6L~-15V<PAD_PART_STICK_DOWN>~Z~+13V~6L<PAD_PART_STICK_UP>~Z~-10H~+9V~6L<PAD_PART_STICK_UP_"
"LEFT>~Z~+10H~+9V~6L<PAD_PART_STICK_UP_RIGHT>~Z~-10H~-11V~6L<PAD_PART_STICK_DOWN_LEFT>~Z~+10H~"
"-11V~6L<PAD_PART_STICK_DOWN_RIGHT>~Z~+32H",
"<PAD_ANALOG_ANY>"},
{"~Y~1L~+8H<PAD_PART_STICK>~Z~6L~-8H<PAD_PART_STICK_LEFT>~Z~+24H~6L<PAD_PART_STICK_RIGHT>~Z~+"
"40H",
"<PAD_ANALOG_LEFT_RIGHT>"},
{"~Y~1L<PAD_PART_STICK>~Z~6L~-15V<PAD_PART_STICK_DOWN>~Z~+13V~6L<PAD_PART_STICK_UP>~Z~+26H",
"<PAD_ANALOG_UP_DOWN>"},
// icons
{"~Y~6L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_COMPLETE>"},
{"~Y~3L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_TODO>"},
// flags
{"~Y~6L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+15H~1L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+30H~3L<FLAG_"
"PART_VERT_STRIPE_LARGE>~Z~+45H",
"<FLAG_ITALIAN>"},
{"~Y~5L<FLAG_PART_FILL>~Z~3L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~5L<FLAG_PART_FILL>~Z~3L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~+26H",
"<FLAG_SPAIN>"},
{"~Y~39L~~~Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_HORZ_STRIPE_BOTTOM>~]~-1H~Y~39L~~~"
"Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_HORZ_STRIPE_BOTTOM>~Z~+26H",
"<FLAG_GERMAN>"},
{"~Y~7L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+15H~1L<FLAG_PART_VERT_STRIPE_LARGE>~Z~+30H~3L<FLAG_"
"PART_VERT_STRIPE_LARGE>~Z~+47H",
"<FLAG_FRANCE>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_UK_CROSS_LEFT>~Z~7L<FLAG_PART_UK_FILL_LEFT>~]~-1H~Y~1L<"
"FLAG_PART_FILL>~Z~3L<FLAG_PART_UK_CROSS_RIGHT>~Z~7L<FLAG_PART_UK_FILL_RIGHT>~Z~+26H",
"<FLAG_UK>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_USA_STRIPES_LEFT>~Z~7L<FLAG_PART_USA_STARS>~]~-1H~Y~1L<"
"FLAG_PART_FILL>~Z~3L<FLAG_PART_USA_STRIPES_RIGHT>~Z~+26H",
"<FLAG_USA>"},
{"~Y~1L<FLAG_PART_FILL>~Z~39L<FLAG_PART_KOREA_TRIGRAMS_LEFT>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~39L<"
"FLAG_PART_KOREA_TRIGRAMS_RIGHT>~Z~-11H~7L<FLAG_PART_KOREA_CIRCLE_FILL>~Z~-11H~3L<FLAG_PART_"
"KOREA_CIRCLE_TOP>~Z~+26H",
"<FLAG_KOREA>"},
{"~Y~1L<FLAG_PART_FILL>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~-11H~3L<FLAG_PART_JAPAN_SUN>~Z~+26H",
"<FLAG_JAPAN>"},
{"~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~7L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_FINLAND>"},
{"~Y~7L<FLAG_PART_FILL>~Z~5L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~5L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~7L<FLAG_PART_FILL>~Z~5L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_SWEDEN>"},
{"~Y~3L<FLAG_PART_FILL>~Z~1L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~1L<FLAG_PART_VERT_STRIPE_RIGHT>~]"
"~-1H~Y~3L<FLAG_PART_FILL>~Z~1L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~+26H",
"<FLAG_DENMARK>"},
{"~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~3L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~-19H~1L<FLAG_PART_VERT_STRIPE_MIDDLE>~Z~-23H~7L<FLAG_PART_VERT_"
"STRIPE_RIGHT>~Z~-23H~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~7L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~"
"+26H",
"<FLAG_NORWAY>"},
{"~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_PART_TOP_BOTTOM_STRIPE>~]~-1H~Y~1L<FLAG_PART_FILL>~Z~7L<FLAG_"
"PART_TOP_BOTTOM_STRIPE>~Z~-19H~1L<FLAG_PART_VERT_STRIPE_MIDDLE>~Z~-23H~3L<FLAG_PART_VERT_"
"STRIPE_RIGHT>~Z~-23H~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~3L<FLAG_PART_HORZ_STRIPE_MIDDLE>~Z~"
"+26H",
"<FLAG_ICELAND>"},
// weird stuff
// - descenders
{"~+7Vp~-7V", "p"},
{"~+7Vy~-7V", "y"},
{"~+7Vg~-7V", "g"},
{"~+7Vq~-7V", "q"},
{"~+1Vj~-1V", "j"},
{"\\\\",
"~%"}, // this is 2 slashes, duplicated because we use an escape sequence when decompiling
// - symbols and ligatures
{"~-4H~-3V<SOMETHING>~+3V~-4H",
"<SUPERSCRIPT_QUOTE>"}, // used for the 4<__> place in spanish. the 5th uses the same
// character but looks different...?
{"~Y~-6Hº~Z~+10H", "°"},
// Color / Emphasis
{"~[~0L", "<COLOR_DEFAULT>"},
{"~[~1L", "<COLOR_WHITE>"},
{"~[~2L", "<COLOR_TRANSPARENT>"},
{"~[~3L", "<COLOR_RED>"},
{"~[~4L", "<COLOR_ORANGE>"},
{"~[~5L", "<COLOR_YELLOW>"},
{"~[~6L", "<COLOR_GREEN>"},
{"~[~7L", "<COLOR_BLUE>"},
{"~[~8L", "<COLOR_CYAN>"},
{"~[~9L", "<COLOR_PINK>"},
{"~[~10L", "<COLOR_MENU-SELECTED>"},
{"~[~11L", "<COLOR_MENU-SELECTED-PARENT>"},
{"~[~12L", "<COLOR_MENU>"},
{"~[~13L", "<COLOR_MENU-PARENT>"},
{"~[~14L", "<COLOR_MENU-FUNC-BAD>"},
{"~[~15L", "<COLOR_MENU-FLAG-ON>"},
{"~[~16L", "<COLOR_MENU-FLAG-ON-PARENT>"},
{"~[~17L", "<COLOR_MENU-FLAG-OFF>"},
{"~[~18L", "<COLOR_MENU-FLAG-OFF-PARENT>"},
{"~[~19L", "<COLOR_MENU-INVALID>"},
{"~[~20L", "<COLOR_FLAT-YELLOW>"},
{"~[~21L", "<COLOR_COLOR-21>"},
{"~[~22L", "<COLOR_PAD-BACK>"},
{"~[~23L", "<COLOR_PAD-SHINE>"},
{"~[~24L", "<COLOR_PAD-SQUARE>"},
{"~[~25L", "<COLOR_PAD-CIRCLE>"},
{"~[~26L", "<COLOR_PAD-TRIANGLE>"},
{"~[~27L", "<COLOR_PAD-CROSS>"},
{"~[~28L", "<COLOR_PROGRESS-OLD-BLUE>"},
{"~[~29L", "<COLOR_PROGRESS-OLD-YELLOW>"},
{"~[~30L", "<COLOR_PROGRESS-OLD-SELECTED>"},
{"~[~31L", "<COLOR_PROGRESS-OLD-PERCENT>"},
{"~[~32L", "<COLOR_PROGRESS>"},
{"~[~33L", "<COLOR_PROGRESS-SELECTED>"},
{"~[~34L", "<COLOR_PROGRESS-FORCE-SELECTED>"},
{"~[~35L", "<COLOR_PROGRESS-OPTION-OFF>"},
{"~[~36L", "<COLOR_COLOR-36>"},
{"~[~37L", "<COLOR_CREDITS-STAFF-TITLE-1>"},
{"~[~38L", "<COLOR_CREDITS-STAFF-TITLE-2>"},
{"~[~39L", "<COLOR_COLOR-39>"}};
// TODO - cryllic
std::vector<EncodeInfo> encode_info_jak3 = {
{"ˇ", "\x10"}, // caron
{"`", "\x11"}, // grave accent
{"'", "\x12"}, // apostrophe
{"^", "\x13"}, // circumflex
{"<TIL>", "\x14"}, // tilde
{"¨", "\x15"}, // umlaut
{"º", "\x16"}, // numero/overring
{"¡", "\x17"}, // inverted exclamation mark
{"¿", "\x18"}, // inverted question mark
{"<SOMETHING>", "\x19"},
{"ç", "\x1d"}, // c-cedilla
{"Ç", "\x1e"}, // c-cedilla
{"ß", "\x1f"}, // eszett
{"œ", "\x5e"}, // ligature o+e
{"<FLAG_PART_HORZ_STRIPE_MIDDLE>", "\x7f"},
{"<FLAG_PART_HORZ_STRIPE_BOTTOM>", "\x80"},
{"<FLAG_PART_VERT_STRIPE_LARGE>", "\x81"},
{"<FLAG_PART_VERT_STRIPE_RIGHT>", "\x82"},
{"<FLAG_PART_VERT_STRIPE_LEFT>", "\x83"},
{"<FLAG_PART_VERT_STRIPE_MIDDLE>", "\x84"},
{"<FLAG_PART_FILL>", "\x85"},
{"<FLAG_PART_JAPAN_SUN>", "\x86"},
{"<FLAG_PART_KOREA_TRIGRAMS_LEFT>", "\x87"},
{"<FLAG_PART_KOREA_TRIGRAMS_RIGHT>", "\x88"},
{"<FLAG_PART_KOREA_CIRCLE_TOP>", "\x89"},
{"<FLAG_PART_KOREA_CIRCLE_FILL>", "\x8a"},
{"<FLAG_PART_TOP_BOTTOM_STRIPE>", "\x8b"},
{"<FLAG_PART_UK_CROSS_LEFT>", "\x8c"},
{"<FLAG_PART_UK_CROSS_RIGHT>", "\x8d"},
{"<FLAG_PART_UK_FILL_LEFT>", "\x8e"},
{"<FLAG_PART_UK_FILL_RIGHT>", "\x8f"},
{"<FLAG_PART_USA_STRIPES_RIGHT>", "\x90"},
{"<PAD_PART_STICK>", "\x91"},
{"<PAD_PART_SELECT>", "\x92"},
{"<PAD_PART_TRIGGER_BACK>", "\x93"},
{"<PAD_PART_R1_NAME>", "\x94"},
{"<PAD_PART_L1_NAME>", "\x95"},
{"<PAD_PART_R2_NAME>", "\x96"},
{"<PAD_PART_L2_NAME>", "\x97"},
{"<PAD_PART_STICK_UP>", "\x98"},
{"<PAD_PART_STICK_UP_RIGHT>", "\x99"},
{"<FLAG_PART_USA_STRIPES_LEFT>", "\x9a"},
{"<FLAG_PART_USA_STARS>", "\x9b"},
{"<PAD_PART_STICK_DOWN>", "\x9c"},
{"<PAD_PART_STICK_DOWN_LEFT>", "\x9d"},
{"<PAD_PART_STICK_LEFT>", "\x9e"},
{"<PAD_PART_STICK_UP_LEFT>", "\x9f"},
{"<PAD_PART_DPAD_D>", "\xa0"},
{"<PAD_PART_DPAD_L>", "\xa1"},
{"<PAD_PART_DPAD_U>", "\xa2"},
{"<PAD_PART_DPAD_R>", "\xa3"},
{"<PAD_PART_STICK_RIGHT>", "\xa4"},
{"<PAD_PART_STICK_DOWN_RIGHT>", "\xa5"},
{"<PAD_PART_SHOULDER_TOP_LEFT>", "\xa6"},
{"<PAD_PART_SHOULDER_TOP_RIGHT>", "\xa7"},
{"<PAD_PART_TRIGGER_TOP_LEFT>", "\xa8"},
{"<PAD_PART_TRIGGER_TOP_RIGHT>", "\xa9"},
{"<PAD_PART_TRIGGER_SHIM1>", "\xaa"},
{"<PAD_PART_TRIGGER_SHIM2>", "\xab"},
{"<PAD_PART_SHOULDER_SHIM2>", "\xac"},
{"<PAD_PART_SHOULDER_BOTTOM_LEFT>", "\xb0"},
{"<PAD_PART_SHOULDER_BOTTOM_RIGHT>", "\xb1"},
{"<PAD_PART_TRIGGER_BOTTOM_LEFT>", "\xb2"},
{"<PAD_PART_TRIGGER_BOTTOM_RIGHT>", "\xb3"},
// {"入", "\x1\x00"}},
// {"年", "\x1\x01"}},
// punctuation
{"", "\x1\x10"},
{"", "\x1\x11"},
{"", "\x1\x12"},
{"", "\x1\x13"},
{"", "\x1\x14"},
{"", "\x1\x15"},
// hiragana
{"", "\x1\x16"}, // -a
{"", "\x1\x17"}, // a
{"", "\x1\x18"}, // -i
{"", "\x1\x19"}, // i
{"", "\x1\x1a"}, // -u
{"", "\x1\x1b"}, // u
{"", "\x1\x1c"}, // -e
{"", "\x1\x1d"}, // e
{"", "\x1\x1e"}, // -o
{"", "\x1\x1f"}, // o
{"", "\x1\x20"}, // ka
{"", "\x1\x21"}, // ki
{"", "\x1\x22"}, // ku
{"", "\x1\x23"}, // ke
{"", "\x1\x24"}, // ko
{"", "\x1\x25"}, // sa
{"", "\x1\x26"}, // shi
{"", "\x1\x27"}, // su
{"", "\x1\x28"}, // se
{"", "\x1\x29"}, // so
{"", "\x1\x2a"}, // ta
{"", "\x1\x2b"}, // chi
{"", "\x1\x2c"}, // sokuon
{"", "\x1\x2d"}, // tsu
{"", "\x1\x2e"}, // te
{"", "\x1\x2f"}, // to
{"", "\x1\x30"}, // na
{"", "\x1\x31"}, // ni
{"", "\x1\x32"}, // nu
{"", "\x1\x33"}, // ne
{"", "\x1\x34"}, // no
{"", "\x1\x35"}, // ha
{"", "\x1\x36"}, // hi
{"", "\x1\x37"}, // fu
{"", "\x1\x38"}, // he
{"", "\x1\x39"}, // ho
{"", "\x1\x3a"}, // ma
{"", "\x1\x3b"}, // mi
{"", "\x1\x3c"}, // mu
{"", "\x1\x3d"}, // me
{"", "\x1\x3e"}, // mo
{"", "\x1\x3f"}, // youon ya
{"", "\x1\x40"}, // ya
{"", "\x1\x41"}, // youon yu
{"", "\x1\x42"}, // yu
{"", "\x1\x43"}, // youon yo
{"", "\x1\x44"}, // yo
{"", "\x1\x45"}, // ra
{"", "\x1\x46"}, // ri
{"", "\x1\x47"}, // ru
{"", "\x1\x48"}, // re
{"", "\x1\x49"}, // ro
{"", "\x1\x4a"}, // -wa
{"", "\x1\x4b"}, // wa
{"", "\x1\x4c"}, // wo
{"", "\x1\x4d"}, // -n
// katakana
{"", "\x1\x4e"}, // -a
{"", "\x1\x4f"}, // a
{"", "\x1\x50"}, // -i
{"", "\x1\x51"}, // i
{"", "\x1\x52"}, // -u
{"", "\x1\x53"}, // u
{"", "\x1\x54"}, // -e
{"", "\x1\x55"}, // e
{"", "\x1\x56"}, // -o
{"", "\x1\x57"}, // o
{"", "\x1\x58"}, // ka
{"", "\x1\x59"}, // ki
{"", "\x1\x5a"}, // ku
{"", "\x1\x5b"}, // ke
{"", "\x1\x5c"}, // ko
{"", "\x1\x5d"}, // sa
{"", "\x1\x5e"}, // shi
{"", "\x1\x5f"}, // su
{"", "\x1\x60"}, // se
{"", "\x1\x61"}, // so
{"", "\x1\x62"}, // ta
{"", "\x1\x63"}, // chi
{"", "\x1\x64"}, // sokuon
{"", "\x1\x65"}, // tsu
{"", "\x1\x66"}, // te
{"", "\x1\x67"}, // to
{"", "\x1\x68"}, // na
{"", "\x1\x69"}, // ni
{"", "\x1\x6a"}, // nu
{"", "\x1\x6b"}, // ne
{"", "\x1\x6c"}, // no
{"", "\x1\x6d"}, // ha
{"", "\x1\x6e"}, // hi
{"", "\x1\x6f"}, // fu
{"", "\x1\x70"}, // he
{"", "\x1\x71"}, // ho
{"", "\x1\x72"}, // ma
{"", "\x1\x73"}, // mi
{"", "\x1\x74"}, // mu
{"", "\x1\x75"}, // me
{"", "\x1\x76"}, // mo
{"", "\x1\x77"}, // youon ya
{"", "\x1\x78"}, // ya
{"", "\x1\x79"}, // youon yu
{"", "\x1\x7a"}, // yu
{"", "\x1\x7b"}, // youon yo
{"", "\x1\x7c"}, // yo
{"", "\x1\x7d"}, // ra
{"", "\x1\x7e"}, // ri
{"", "\x1\x7f"}, // ru
{"", "\x1\x80"}, // re
{"", "\x1\x81"}, // ro
{"", "\x1\x82"}, // -wa
{"", "\x1\x83"}, // wa
{"", "\x1\x84"}, // wo
{"", "\x1\x85"}, // -n
{"", "\x1\x8c"},
{"", "\x1\x8d"},
{"", "\x1\x8e"},
{"", "\x1\x8f"},
{"", "\x1\x90"},
{"", "\x1\x91"},
{"", "\x1\x92"},
{"", "\x1\x93"},
{"", "\x1\x94"},
{"", "\x1\x95"},
{"", "\x1\x96"},
{"", "\x1\x97"},
{"", "\x1\x98"},
{"", "\x1\x99"},
{"", "\x1\x9a"},
{"", "\x1\x9b"},
{"", "\x1\x9c"},
{"", "\x1\x9d"},
{"", "\x1\x9e"},
{"", "\x1\x9f"},
{"", "\x1\xa0"},
{"", "\x1\xa1"},
{"", "\x1\xa2"},
{"", "\x1\xa3"},
{"", "\x1\xa4"},
{"", "\x1\xa5"},
{"", "\x1\xa6"},
{"", "\x1\xa7"},
{"", "\x1\xa8"},
{"", "\x1\xa9"},
{"", "\x1\xaa"},
{"", "\x1\xab"},
{"", "\x1\xac"},
{"", "\x1\xad"},
{"", "\x1\xae"},
{"", "\x1\xaf"},
{"", "\x1\xb0"},
{"", "\x1\xb1"},
{"", "\x1\xb2"},
{"", "\x1\xb3"},
{"", "\x1\xb4"},
{"", "\x1\xb5"},
{"", "\x1\xb6"},
{"", "\x1\xb7"},
{"", "\x1\xb8"},
{"", "\x1\xb9"},
{"", "\x1\xba"},
{"", "\x1\xbb"},
{"", "\x1\xbc"},
{"", "\x1\xbd"},
{"", "\x1\xbe"},
{"", "\x1\xbf"},
{"使", "\x1\xc0"},
{"", "\x1\xc1"},
{"", "\x1\xc2"},
{"", "\x1\xc3"},
{"", "\x1\xc4"},
{"", "\x1\xc5"},
{"", "\x1\xc6"},
{"", "\x1\xc7"},
{"", "\x1\xc8"},
{"", "\x1\xc9"},
{"", "\x1\xca"},
{"", "\x1\xcb"},
{"", "\x1\xcc"},
{"", "\x1\xcd"},
{"", "\x1\xce"},
{"", "\x1\xcf"},
{"", "\x1\xd0"},
{"", "\x1\xd1"},
{"", "\x1\xd2"},
{"", "\x1\xd3"},
{"", "\x1\xd4"},
{"", "\x1\xd5"},
{"", "\x1\xd6"},
{"", "\x1\xd7"},
{"", "\x1\xd8"},
{"", "\x1\xd9"},
{"", "\x1\xda"},
{"", "\x1\xdb"},
{"", "\x1\xdc"},
{"", "\x1\xdd"},
{"", "\x1\xde"},
{"", "\x1\xdf"},
{"", "\x1\xe0"},
{"", "\x1\xe1"},
{"", "\x1\xe2"},
{"", "\x1\xe3"},
{"", "\x1\xe4"},
{"", "\x1\xe5"},
{"", "\x1\xe6"},
{"", "\x1\xe7"},
{"", "\x1\xe8"},
{"", "\x1\xe9"},
{"", "\x1\xea"},
{"", "\x1\xeb"},
{"", "\x1\xec"},
{"", "\x1\xed"},
{"", "\x1\xee"},
{"", "\x1\xef"},
{"", "\x1\xf0"},
{"", "\x1\xf1"},
{"", "\x1\xf2"},
{"", "\x1\xf3"},
{"", "\x1\xf4"},
{"", "\x1\xf5"},
{"", "\x1\xf6"},
{"", "\x1\xf7"},
{"", "\x1\xf8"},
{"", "\x1\xf9"},
{"", "\x1\xfa"},
{"", "\x1\xfb"},
{"", "\x1\xfc"},
{"", "\x1\xfd"},
{"", "\x1\xfe"},
{"", "\x1\xff"},
{"", "\x2\x10"},
{"", "\x2\x11"},
{"", "\x2\x12"},
{"", "\x2\x13"},
{"", "\x2\x14"},
{"", "\x2\x15"},
{"", "\x2\x16"},
{"", "\x2\x17"},
{"", "\x2\x18"},
{"", "\x2\x19"},
{"", "\x2\x1a"},
{"", "\x2\x1b"},
{"", "\x2\x1c"},
{"", "\x2\x1d"},
{"", "\x2\x1e"},
{"", "\x2\x1f"},
{"", "\x2\x20"},
{"", "\x2\x21"},
{"", "\x2\x22"},
{"", "\x2\x23"},
{"", "\x2\x24"},
{"", "\x2\x25"},
{"", "\x2\x26"},
{"", "\x2\x27"},
{"", "\x2\x28"},
{"", "\x2\x29"},
{"", "\x2\x2a"},
{"", "\x2\x2b"},
{"", "\x2\x2c"},
{"", "\x2\x2d"},
{"", "\x2\x2e"},
{"", "\x2\x2f"},
{"", "\x2\x30"},
{"", "\x2\x31"},
{"", "\x2\x32"},
{"", "\x2\x33"},
// {"成", "\x2\x34"},
{"", "\x2\x35"},
{"", "\x2\x36"},
{"", "\x2\x37"},
{"", "\x2\x38"},
{"", "\x2\x39"},
{"", "\x2\x3a"},
{"", "\x2\x3b"},
{"", "\x2\x3c"},
{"", "\x2\x3d"},
{"", "\x2\x3e"},
{"", "\x2\x3f"},
{"", "\x2\x40"},
{"", "\x2\x41"},
// {"乗", "\x2\x42"},
{"", "\x2\x43"},
{"", "\x2\x44"},
{"", "\x2\x45"},
{"", "\x2\x46"},
{"", "\x2\x47"},
{"", "\x2\x48"},
{"", "\x2\x49"},
{"", "\x2\x4a"},
{"", "\x2\x4b"},
// {"対", "\x2\x4c"},
{"", "\x2\x4d"},
{"", "\x2\x4e"},
{"", "\x2\x4f"},
{"", "\x2\x50"},
{"", "\x2\x56"},
{"", "\x2\x57"},
{"", "\x2\x58"},
{"", "\x2\x59"},
{"", "\x2\x5a"},
{"", "\x2\x5b"},
{"", "\x2\x5c"},
{"", "\x2\x5d"},
{"", "\x2\x5e"},
{"", "\x2\x5f"},
{"", "\x2\x60"},
{"", "\x2\x61"},
{"", "\x2\x62"},
{"", "\x2\x63"},
{"", "\x2\x64"},
// {"高", "\x2\x65"},
{"", "\x2\x66"},
{"", "\x2\x67"},
{"", "\x2\x68"},
{"", "\x2\x69"},
{"", "\x2\x6a"},
{"", "\x2\x6b"},
{"", "\x2\x6c"},
{"", "\x2\x6d"},
{"", "\x2\x6e"},
{"", "\x2\x6f"},
{"", "\x2\x70"},
{"", "\x2\x71"},
{"", "\x2\x72"},
{"", "\x2\x73"},
{"", "\x2\x74"},
{"", "\x2\x75"},
{"退", "\x2\x76"},
{"", "\x2\x77"},
{"", "\x2\x78"},
{"", "\x2\x79"},
{"", "\x2\x7a"},
{"", "\x2\x7b"},
{"", "\x2\x7c"},
{"", "\x2\x7d"},
{"", "\x2\x7e"},
{"", "\x2\x7f"},
{"", "\x2\x80"},
{"", "\x2\x81"},
{"", "\x2\x82"},
{"", "\x2\x83"},
{"", "\x2\x84"},
{"", "\x2\x85"},
{"", "\x2\x86"},
{"", "\x2\x88"},
{"", "\x2\x89"},
{"", "\x2\x8a"},
{"", "\x2\x8b"},
{"", "\x2\x8c"},
{"", "\x2\x8d"},
};
GameTextFontBank g_font_bank_jak3(GameTextVersion::JAK3,
&encode_info_jak3,
&replace_info_jak3,
&passthrus_jak3);
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "common/util/font/font_utils.h"
/*!
* ================================
* GAME TEXT FONT BANK - JAK 3
* ================================
* This font is used in:
* - Jak 3
*/
// TODO cyrillic
// TODO - current just using the jak 2 stuff (hopefully it's identical?)
extern GameTextFontBank g_font_bank_jak3;
+365
View File
@@ -0,0 +1,365 @@
/*!
* @file FontUtils.cpp
*
* Code for handling text and strings in Jak 1's "large font" format.
*
* MAKE SURE THIS FILE IS ENCODED IN UTF-8!!! The various strings here depend on it.
* Always verify the encoding if string detection suddenly goes awry.
*/
#include "font_utils.h"
#include <algorithm>
#include <stdexcept>
#include <string_view>
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "common/util/font/dbs/font_db_jak1.h"
#include "common/util/font/dbs/font_db_jak2.h"
#include "common/util/font/dbs/font_db_jak3.h"
#include "common/util/font/font_utils_korean.h"
#include "common/util/string_util.h"
#include "common/versions/versions.h"
#include "fmt/format.h"
void from_json(const json& j, KoreanLookupEntry& obj) {
json_deserialize_if_exists(defaultGlyph);
json_deserialize_if_exists(alternatives);
}
std::map<GameTextVersion, GameTextFontBank*> g_font_banks = {
{GameTextVersion::JAK1_V1, &g_font_bank_jak1_v1},
{GameTextVersion::JAK1_V2, &g_font_bank_jak1_v2},
{GameTextVersion::JAK2, &g_font_bank_jak2},
{GameTextVersion::JAK3, &g_font_bank_jak3}};
const std::unordered_map<std::string, GameTextVersion> sTextVerEnumMap = {
{"jak1-v1", GameTextVersion::JAK1_V1},
{"jak1-v2", GameTextVersion::JAK1_V2},
{"jak2", GameTextVersion::JAK2},
{"jak3", GameTextVersion::JAK3}};
const std::string& get_text_version_name(GameTextVersion version) {
for (auto& [name, ver] : sTextVerEnumMap) {
if (ver == version) {
return name;
}
}
throw std::runtime_error(fmt::format("invalid text version {}", fmt::underlying(version)));
}
GameTextVersion get_text_version_from_name(const std::string& name) {
return sTextVerEnumMap.at(name);
}
GameTextFontBank::GameTextFontBank(GameTextVersion version,
std::vector<EncodeInfo>* encode_info,
std::vector<ReplaceInfo>* replace_info,
std::unordered_set<char>* passthrus)
: m_version(version), m_passthrus(passthrus) {
// Insert the encode and replacement info into a Trie, much faster lookups that way
for (const auto& encoding : *encode_info) {
m_encode_to_utf8_trie.insert(encoding.game_bytes, encoding);
m_encode_to_game_trie.insert(encoding.utf8, encoding);
}
for (const auto& replacement : *replace_info) {
m_replace_to_utf8_trie.insert(replacement.game_encoding, replacement);
m_replace_to_game_trie.insert(replacement.utf8_string, replacement);
}
}
bool GameTextFontBank::is_language_id_korean(const int language_id) const {
if (m_version == GameTextVersion::JAK2 && language_id == 6) {
return true;
} else if (m_version == GameTextVersion::JAK3 && language_id == 7) {
return true;
}
return false;
}
GameTextFontBank* get_font_bank(GameTextVersion version) {
return g_font_banks.at(version);
}
GameTextFontBank* get_font_bank_from_game_version(GameVersion version) {
if (version == GameVersion::Jak1) {
// Jak 1 has been patched to use V2
return get_font_bank(GameTextVersion::JAK1_V2);
} else if (version == GameVersion::Jak2) {
auto font_bank = get_font_bank(GameTextVersion::JAK2);
return font_bank;
} else if (version == GameVersion::Jak3) {
return get_font_bank(GameTextVersion::JAK3);
} else {
ASSERT_MSG(false, "Unsupported game for get_font_bank_from_game_version");
}
}
GameTextFontBank* get_font_bank(const std::string& name) {
if (auto it = sTextVerEnumMap.find(name); it == sTextVerEnumMap.end()) {
throw std::runtime_error(fmt::format("unknown text version {}", name));
} else {
return get_font_bank(it->second);
}
}
bool font_bank_exists(GameTextVersion version) {
return g_font_banks.find(version) != g_font_banks.cend();
}
std::string GameTextFontBank::replace_to_game(const std::string& str) const {
std::string newstr;
newstr.reserve(str.size());
for (int i = 0; i < str.length();) {
const ReplaceInfo* remap = m_replace_to_game_trie.find_longest_prefix(str, i);
if (!remap) {
newstr.push_back(str[i]);
i += 1;
} else {
if (!remap->utf8_alternative.empty()) {
newstr.append(remap->utf8_alternative);
} else {
newstr.append(remap->game_encoding);
}
i += remap->utf8_string.size();
}
}
return newstr;
}
std::string GameTextFontBank::encode_utf8_to_game(const std::string& str) const {
std::string newstr;
newstr.reserve(str.size());
for (int i = 0; i < str.length();) {
auto match = m_encode_to_game_trie.find_longest_prefix(str, i);
if (!match) {
newstr.push_back(str[i]);
i += 1;
} else {
for (auto b : match->game_bytes) {
newstr.push_back(b);
}
i += match->utf8.size();
}
}
return newstr;
}
/*!
* Turn a normal readable string into a string readable in the in-game font encoding and converts
* \cXX escape sequences
*/
std::string GameTextFontBank::convert_utf8_to_game(const std::string& str) const {
return encode_utf8_to_game(replace_to_game(str));
}
std::string GameTextFontBank::replace_to_utf8(const std::string& str) const {
std::string result;
result.reserve(str.size());
for (size_t i = 0; i < str.size();) {
const ReplaceInfo* remap = m_replace_to_utf8_trie.find_longest_prefix(str, i);
if (!remap) {
result.push_back(str[i]);
i += 1;
} else {
result.append(remap->utf8_string);
i += remap->game_encoding.size();
}
}
return result;
}
bool GameTextFontBank::valid_char_range(const char& in) const {
if (m_version == GameTextVersion::JAK1_V1 || m_version == GameTextVersion::JAK1_V2) {
return ((in >= '0' && in <= '9') || (in >= 'A' && in <= 'Z') ||
m_passthrus->find(in) != m_passthrus->end()) &&
in != '\\';
} else if (m_version == GameTextVersion::JAK2 || m_version == GameTextVersion::JAK3 ||
m_version == GameTextVersion::JAKX) {
return ((in >= '0' && in <= '9') || (in >= 'A' && in <= 'Z') || (in >= 'a' && in <= 'z') ||
m_passthrus->find(in) != m_passthrus->end()) &&
in != '\\';
}
return false;
}
std::string GameTextFontBank::encode_game_to_utf8(const std::string& str) const {
std::string newstr;
newstr.reserve(str.size());
for (size_t i = 0; i < str.size();) {
auto encoding = m_encode_to_utf8_trie.find_longest_prefix(str, i);
if (!encoding) {
// No match: copy valid characters as-is, or escape unknown bytes
unsigned char c = static_cast<unsigned char>(str[i]);
if (valid_char_range(c) || c == '\n' || c == '\t' || c == '\\' || c == '"') {
newstr.push_back(c);
} else {
newstr += fmt::format("\\c{:02x}", c);
}
++i;
} else {
// Found a match: append its UTF-8 sequence
newstr.append(encoding->utf8);
i += encoding->game_bytes.size(); // advance past matched game bytes
}
}
return newstr;
}
std::string GameTextFontBank::convert_game_to_utf8(const char* in) const {
// Encode and apply replacement ONCE
std::string decoded = replace_to_utf8(encode_game_to_utf8(in));
// Escape special characters while writing directly into result
std::string result;
result.reserve(decoded.size());
for (size_t i = 0; i < decoded.size(); ++i) {
char c = decoded[i];
if (c == '\n') {
result += "\\n";
} else if (c == '\t') {
result += "\\t";
} else if (c == '\\') {
if (i < decoded.size() - 1 && decoded[i + 1] == 'c') {
result.push_back(c); // preserve \cXX
} else {
result += "\\\\";
}
} else if (c == '"') {
result += "\\\"";
} else {
result.push_back(c);
}
}
return result;
}
std::string GameTextFontBank::convert_utf8_to_game_korean(const std::string& str) {
ASSERT_MSG(m_version == GameTextVersion::JAK2 || m_version == GameTextVersion::JAK3,
"Korean is not supported for any game other than Jak 2 and Jak 3 right now");
if (!m_korean_db.has_value()) {
const auto db_file_path =
file_util::get_file_path({"game/assets/fonts/jak2_jak3_korean_db.json"});
if (file_util::file_exists(db_file_path)) {
auto raw_data = file_util::read_text_file(db_file_path);
auto json_data = parse_commented_json(raw_data, "jak2_jak3_korean_db.json");
std::unordered_map<std::string, KoreanLookupOrientations> temp_db;
json_data.get_to(temp_db);
m_korean_db = temp_db;
}
}
std::string output;
output.reserve(str.size());
std::string non_korean_buffer = "";
size_t i = 0;
while (i < str.size()) {
char32_t cp = str_util::next_utf8_char(str, i);
if (font_util_korean::is_korean_syllable(cp)) {
// flush any non-korean buffer
if (!non_korean_buffer.empty()) {
output += 0x3;
// encode / remap it
output += encode_utf8_to_game(replace_to_game(non_korean_buffer));
non_korean_buffer = "";
}
// write out the korean character
output += font_util_korean::game_encode_korean_syllable(str, cp, m_korean_db.value());
} else {
non_korean_buffer += str_util::utf8_encode(cp);
}
}
// flush any non-korean buffer
if (!non_korean_buffer.empty()) {
output += 0x3;
// encode / remap it
output += encode_utf8_to_game(replace_to_game(non_korean_buffer));
non_korean_buffer = "";
}
return output;
}
std::string GameTextFontBank::convert_korean_game_to_utf8(const char* in) const {
ASSERT_MSG(m_version == GameTextVersion::JAK2 || m_version == GameTextVersion::JAK3,
"Korean is not supported for any game other than Jak 2 and Jak 3 right now");
// Korean strings are fully bitstrings, in other words, it's just a bunch of bytes
// Some info on the layout:
// - Every korean syllable block starts with a `4`
// - The following byte indicates how many glyphs are drawn for that syllable block
// - Each jamo that makes up the syllable block follows as a single byte
// - Unless the jamo is part of the "extra" texture page, in which case it's preceeded by a `5`.
// There are very few jamo that are and they are only applicable for the final consonant
// - The korean strings can contain non-korean characters. These are preceeded by a `3`
// - For example a space would be `3 20`
// - It might be more accurate to say that a 3 signifies "consume characters as normal until
// something else is encountered (ie. flags or more complex font encodings)"
std::string result;
std::string_view str(in);
u64 index = 0;
u8 curr_byte = 0;
bool in_syllable_block = false;
std::string jamo_buffer = "";
std::string non_korean_buffer = "";
int num_syllable_glyphs = 0;
while (index < str.length()) {
curr_byte = str.at(index);
// new syllable block
if (curr_byte == 4) {
in_syllable_block = true;
if (index + 1 < str.length()) {
num_syllable_glyphs = str.at(index + 1);
index++;
}
index++;
// flush any non-korean characters
if (!non_korean_buffer.empty()) {
// handle remap
std::string remapped_str = replace_to_utf8(encode_game_to_utf8(non_korean_buffer));
result += remapped_str;
non_korean_buffer = "";
}
continue;
}
if (in_syllable_block) {
// extra page
std::string glyph_key;
u8 hex_byte = curr_byte;
if (curr_byte == 5 && index + 1 < str.length()) {
hex_byte = str.at(index + 1);
glyph_key = fmt::format("extra_0x{:02x}", hex_byte);
index++;
} else {
glyph_key = fmt::format("0x{:02x}", hex_byte);
}
const auto jamo_list = jamo_glyph_mappings_jak2.find(glyph_key);
ASSERT_MSG(jamo_list != jamo_glyph_mappings_jak2.end(),
fmt::format("{} not found in jamo glyph lookup table", glyph_key));
for (const auto& jamo : jamo_list->second) {
jamo_buffer += jamo;
}
num_syllable_glyphs--;
if (num_syllable_glyphs == 0) {
in_syllable_block = false;
result += font_util_korean::compose_korean_containing_text(jamo_buffer);
jamo_buffer = "";
}
} else {
if (curr_byte != 0x3) {
non_korean_buffer.push_back(curr_byte);
}
}
index++;
}
// flush any non-korean characters
if (!non_korean_buffer.empty()) {
// handle remap
std::string remapped_str = replace_to_utf8(encode_game_to_utf8(non_korean_buffer));
result += remapped_str;
non_korean_buffer = "";
}
return result;
}
@@ -1,23 +1,40 @@
#pragma once
/*!
* @file FontUtils.h
*
* Code for handling text and strings in Jak 1's "large font" format.
*
* MAKE SURE THIS FILE IS ENCODED IN UTF-8!!! The various strings here depend on it.
* Always verify the encoding if string detection suddenly goes awry.
*/
#include <map>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "common/common_types.h"
#include "common/util/Trie.h"
#include "common/util/font/font_utils_korean.h"
#include "common/util/json_util.h"
#include "common/versions/versions.h"
/*!
* What bytes a set of characters (UTF-8) correspond to. You can convert to and fro.
*/
struct EncodeInfo {
std::string utf8;
std::string game_bytes;
};
/*!
* Replace an unconventional string of characters with/from something more readable.
* For example, turns Ñ into N + ~ + a bunch of modifiers.
*/
struct ReplaceInfo {
std::string game_encoding;
std::string utf8_string;
// for some replacements, we want to replace with something different when going from utf8 than
// when we originally matched it this is mostly applicable for when we have to max a string with
// hex chars that have been converted into `\c00` format but we want to insert the actual raw hex
// bytes back.
std::string utf8_alternative = "";
};
// version of the game text file's text encoding. Not real, but we need to differentiate them
// somehow, since the encoding changes.
enum class GameTextVersion {
@@ -28,44 +45,26 @@ enum class GameTextVersion {
JAKX = 40 // jak x
};
extern const std::unordered_map<std::string, GameTextVersion> sTextVerEnumMap;
const std::string& get_text_version_name(GameTextVersion version);
GameTextVersion get_text_version_from_name(const std::string& name);
/*!
* What bytes a set of characters (UTF-8) correspond to. You can convert to and fro.
*/
struct EncodeInfo {
std::string chars;
std::vector<u8> bytes;
};
/*!
* Replace an unconventional string of characters with/from something more readable.
* For example, turns Ñ into N + ~ + a bunch of modifiers.
*/
struct ReplaceInfo {
std::string from;
std::string to;
};
/*!
* All the information to convert UTF-8 text into game text.
*/
class GameTextFontBank {
GameTextVersion m_version; // the version of the game text. we determine this ourselves.
std::vector<EncodeInfo>* m_encode_info;
std::vector<ReplaceInfo>* m_replace_info;
Trie<EncodeInfo> m_encode_to_utf8_trie;
Trie<EncodeInfo> m_encode_to_game_trie;
Trie<ReplaceInfo> m_replace_to_utf8_trie;
Trie<ReplaceInfo> m_replace_to_game_trie;
std::unordered_set<char>* m_passthrus;
// jamo=>6 orientations with their drawing info
std::optional<std::unordered_map<std::string, KoreanLookupOrientations>> m_korean_db =
std::nullopt;
const EncodeInfo* find_encode_to_utf8(const char* in) const;
const EncodeInfo* find_encode_to_game(const std::string& in, int off = 0) const;
const ReplaceInfo* find_replace_to_utf8(const std::string& in, int off = 0) const;
const ReplaceInfo* find_replace_to_game(const std::string& in, int off = 0) const;
std::string replace_to_utf8(std::string& str) const;
std::string replace_to_game(std::string& str) const;
std::string encode_utf8_to_game(std::string& str) const;
std::string replace_to_utf8(const std::string& str) const;
std::string replace_to_game(const std::string& str) const;
std::string encode_utf8_to_game(const std::string& str) const;
std::string encode_game_to_utf8(const std::string& str) const;
public:
GameTextFontBank(GameTextVersion version,
@@ -73,26 +72,34 @@ class GameTextFontBank {
std::vector<ReplaceInfo>* replace_info,
std::unordered_set<char>* passthrus);
const std::vector<EncodeInfo>* encode_info() const { return m_encode_info; }
const std::vector<ReplaceInfo>* replace_info() const { return m_replace_info; }
const std::unordered_set<char>* passthrus() const { return m_passthrus; }
GameTextVersion version() const { return m_version; }
// TODO - methods would help make this code a lot better for different game versions
// hacking it for now
bool valid_char_range(const char in) const;
bool valid_char_range(const char& in) const;
bool is_language_id_korean(const int language_id) const;
std::string convert_utf8_to_game(std::string str, bool escape = false) const;
std::string convert_utf8_to_game(const std::string& str) const;
std::string convert_game_to_utf8(const char* in) const;
std::string convert_utf8_to_game_korean(const std::string& str);
std::string convert_korean_game_to_utf8(const char* in) const;
};
extern GameTextFontBank g_font_bank_jak1_v1;
extern GameTextFontBank g_font_bank_jak1_v2;
extern GameTextFontBank g_font_bank_jak2;
extern std::map<GameTextVersion, GameTextFontBank*> g_font_banks;
extern const std::unordered_map<std::string, GameTextVersion> sTextVerEnumMap;
const std::string& get_text_version_name(GameTextVersion version);
GameTextVersion get_text_version_from_name(const std::string& name);
const GameTextFontBank* get_font_bank(GameTextVersion version);
const GameTextFontBank* get_font_bank_from_game_version(GameVersion version);
const GameTextFontBank* get_font_bank(const std::string& name);
/*!
* ========================
* GAME TEXT FONT BANK LIST
* ========================
* The list of available font banks and a couple of helper functions.
*/
extern std::map<GameTextVersion, GameTextFontBank*> g_font_banks;
GameTextFontBank* get_font_bank(GameTextVersion version);
GameTextFontBank* get_font_bank_from_game_version(GameVersion version);
GameTextFontBank* get_font_bank(const std::string& name);
bool font_bank_exists(GameTextVersion version);
+403
View File
@@ -0,0 +1,403 @@
/*
MIT License
Copyright (c) 2017 Jonghwan Hyeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
https://github.com/jonghwanhyeon/hangul-jamo/blob/main/LICENSE
Code converted to C++ and deals with UTF-8 input
*/
#include "font_utils_korean.h"
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/string_util.h"
#include "fmt/format.h"
//
// Reference: http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf#G24646
//
const int BASE_OF_SYLLABLES = 0xAC00;
const int BASE_OF_LEADING_CONSONANTS = 0x1100;
const int BASE_OF_VOWELS = 0x1161;
// One less than the beginning of the range of trailing consonants (0x11A8)
const int BASE_OF_TRAILING_CONSONANTS = 0x11A7;
const int NUMBER_OF_LEADING_CONSONANTS = 19;
const int NUMBER_OF_VOWELS = 21;
// One more than the number of trailing consonants
const int NUMBER_OF_TRAILING_CONSONANTS = 28;
const int NUMBER_OF_SYLLABLES_FOR_EACH_LEADING_CONSONANT =
NUMBER_OF_VOWELS * NUMBER_OF_TRAILING_CONSONANTS;
const int NUMBER_OF_SYLLABLES =
NUMBER_OF_LEADING_CONSONANTS * NUMBER_OF_SYLLABLES_FOR_EACH_LEADING_CONSONANT;
// TODO - fix all of these, these generated wrong
const std::vector<std::string> LEADING_CONSONANTS = {"", "", "", "", "", "", "",
"", "", "", "", "", "", "",
"", "", "", "", ""};
const std::unordered_map<std::string, int> INDEX_BY_LEADING_CONSONANT = []() {
std::unordered_map<std::string, int> m;
for (size_t i = 0; i < LEADING_CONSONANTS.size(); ++i) {
m[LEADING_CONSONANTS[i]] = static_cast<int>(i);
}
return m;
}();
const std::vector<std::string> VOWELS = {"", "", "", "", "", "", "",
"", "", "", "", "", "", "",
"", "", "", "", "", "", ""};
const std::unordered_map<std::string, int> INDEX_BY_VOWEL = []() {
std::unordered_map<std::string, int> m;
for (size_t i = 0; i < VOWELS.size(); ++i) {
m[VOWELS[i]] = static_cast<int>(i);
}
return m;
}();
const std::vector<std::string> TRAILING_CONSONANTS = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", ""};
const std::unordered_map<std::string, int> INDEX_BY_TRAILING_CONSONANT = []() {
std::unordered_map<std::string, int> m;
for (size_t i = 0; i < TRAILING_CONSONANTS.size(); ++i) {
m[TRAILING_CONSONANTS[i]] = static_cast<int>(i);
}
return m;
}();
bool font_util_korean::is_korean_syllable(char32_t syllable) {
int index_of_syllable = syllable - BASE_OF_SYLLABLES;
return 0 <= index_of_syllable && index_of_syllable < NUMBER_OF_SYLLABLES;
}
inline bool is_jamo_character(const std::string& character) {
return (std::find(LEADING_CONSONANTS.begin(), LEADING_CONSONANTS.end(), character) !=
LEADING_CONSONANTS.end()) ||
(std::find(VOWELS.begin(), VOWELS.end(), character) != VOWELS.end()) ||
(std::find(TRAILING_CONSONANTS.begin(), TRAILING_CONSONANTS.end(), character) !=
TRAILING_CONSONANTS.end());
}
inline std::string codepoint_to_utf8(char32_t cp) {
std::string result;
if (cp <= 0x7F) {
result += static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result += static_cast<char>(0xC0 | ((cp >> 6) & 0x1F));
result += static_cast<char>(0x80 | (cp & 0x3F));
} else if (cp <= 0xFFFF) {
result += static_cast<char>(0xE0 | ((cp >> 12) & 0x0F));
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
result += static_cast<char>(0x80 | (cp & 0x3F));
} else if (cp <= 0x10FFFF) {
result += static_cast<char>(0xF0 | ((cp >> 18) & 0x07));
result += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
result += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
result += static_cast<char>(0x80 | (cp & 0x3F));
}
return result;
}
inline std::string compose_jamo_characters(const std::string& leading_consonant,
const std::string& vowel,
const std::optional<std::string>& trailing_consonant) {
const int index_of_leading_consonant = INDEX_BY_LEADING_CONSONANT.at(leading_consonant) *
NUMBER_OF_SYLLABLES_FOR_EACH_LEADING_CONSONANT;
const int index_of_vowel = INDEX_BY_VOWEL.at(vowel) * NUMBER_OF_TRAILING_CONSONANTS;
const int index_of_leading_consonant_and_vowel = index_of_leading_consonant + index_of_vowel;
int index_of_trailing = trailing_consonant.has_value()
? INDEX_BY_TRAILING_CONSONANT.at(trailing_consonant.value())
: 0;
int index_of_syllable = index_of_leading_consonant_and_vowel + index_of_trailing;
return codepoint_to_utf8(BASE_OF_SYLLABLES + index_of_syllable);
}
std::vector<char32_t> utf8_to_codepoints(const std::string& text) {
std::vector<char32_t> codepoints;
size_t i = 0;
while (i < text.size()) {
char32_t cp = 0;
unsigned char c = text[i];
if (c < 0x80) {
cp = c;
++i;
} else if ((c >> 5) == 0x6) {
cp = ((c & 0x1F) << 6) | (text[i + 1] & 0x3F);
i += 2;
} else if ((c >> 4) == 0xE) {
cp = ((c & 0x0F) << 12) | ((text[i + 1] & 0x3F) << 6) | (text[i + 2] & 0x3F);
i += 3;
} else if ((c >> 3) == 0x1E) {
cp = ((c & 0x07) << 18) | ((text[i + 1] & 0x3F) << 12) | ((text[i + 2] & 0x3F) << 6) |
(text[i + 3] & 0x3F);
i += 4;
} else {
++i;
}
codepoints.push_back(cp);
}
return codepoints;
}
bool is_leading(char32_t c) {
// Replace with actual lookup (e.g. return LEADING_CONSONANTS.count(c) > 0;)
return (c >= 0x1100 && c <= 0x1112);
}
bool is_vowel(char32_t c) {
return (c >= 0x1161 && c <= 0x1175);
}
bool is_trailing(char32_t c) {
return (c >= 0x11A8 && c <= 0x11C2);
}
// Compose jamo into a syllable block
char32_t compose_jamo(char32_t lead, char32_t vowel, std::optional<char32_t> trail = std::nullopt) {
char32_t l_index = lead - 0x1100;
char32_t v_index = vowel - 0x1161;
char32_t t_index = trail ? (*trail - 0x11A7) : 0;
return 0xAC00 + (l_index * 21 * 28) + (v_index * 28) + t_index;
}
std::string font_util_korean::compose_korean_containing_text(const std::string& text) {
std::string output;
std::vector<char32_t> cps = utf8_to_codepoints(text);
size_t i = 0;
while (i < cps.size()) {
char32_t first = cps[i];
char32_t second = (i + 1 < cps.size()) ? cps[i + 1] : 0;
char32_t third = (i + 2 < cps.size()) ? cps[i + 2] : 0;
char32_t fourth = (i + 3 < cps.size()) ? cps[i + 3] : 0;
if (is_leading(first) && is_vowel(second) && is_leading(third) && is_vowel(fourth)) {
char32_t syllable = compose_jamo(first, second);
output += codepoint_to_utf8(syllable);
i += 2; // consume 2 codepoints
} else if (is_leading(first) && is_vowel(second) && is_trailing(third)) {
char32_t syllable = compose_jamo(first, second, third);
output += codepoint_to_utf8(syllable);
i += 3; // consume 3 codepoints
} else if (is_leading(first) && is_vowel(second)) {
char32_t syllable = compose_jamo(first, second);
output += codepoint_to_utf8(syllable);
i += 2; // consume 2 codepoints
} else {
output += codepoint_to_utf8(first);
i += 1;
}
}
return output;
}
// std::string font_util_korean::decompose_korean_containing_text(const std::string& text) {
// std::string output;
// size_t i = 0;
// while (i < text.size()) {
// char32_t cp = next_utf8_char(text, i);
// if (is_syllable(cp)) {
// // Decompose Hangul syllable block into jamo
// char32_t SIndex = cp - 0xAC00;
// char32_t L = 0x1100 + SIndex / (21 * 28);
// char32_t V = 0x1161 + (SIndex % (21 * 28)) / 28;
// char32_t T = 0x11A7 + (SIndex % 28);
// output += codepoint_to_utf8(L);
// output += codepoint_to_utf8(V);
// if (T != 0x11A7) { // has final consonant
// output += codepoint_to_utf8(T);
// }
// } else {
// // For other characters, re-encode as-is
// output += codepoint_to_utf8(cp);
// }
// }
// return output;
// }
bool is_median_vowel_vertical(char32_t vowel) {
if ((vowel >= 0x1161 && vowel <= 0x1168) || vowel == 0x1175) {
return true;
}
return false;
}
bool is_median_vowel_horizontal(char32_t vowel) {
if (vowel == 0x1169 || (vowel >= 0x116D && vowel <= 0x116E) ||
(vowel >= 0x1172 && vowel <= 0x1173)) {
return true;
}
return false;
}
bool is_median_vowel_combined(char32_t vowel) {
if (vowel >= 0x1161 && vowel <= 0x1175 && !is_median_vowel_vertical(vowel) &&
!is_median_vowel_horizontal(vowel)) {
return true;
}
return false;
}
std::string glyph_hex_string_to_int(const std::string& str) {
try {
std::string result;
if (str_util::starts_with(str, "extra_")) {
// handle glyphs on the secondary texture page
result += 0x5;
std::string temp = str;
str_util::replace(temp, "extra_", "");
result += std::stoi(temp, nullptr, 0);
} else {
result += std::stoi(str, nullptr, 0);
}
return result;
} catch (std::exception& e) {
lg::error("Unable to convert hex_string_to_int: {}", str);
throw;
}
}
std::string font_util_korean::game_encode_korean_syllable(
const std::string& context,
const char32_t cp,
const std::unordered_map<std::string, KoreanLookupOrientations>& db) {
std::string output;
// Decompose Hangul syllable block into jamo
char32_t syllable_index = cp - BASE_OF_SYLLABLES;
char32_t initial = BASE_OF_LEADING_CONSONANTS + syllable_index / (21 * 28);
char32_t median = BASE_OF_VOWELS + (syllable_index % (21 * 28)) / 28;
char32_t final = BASE_OF_TRAILING_CONSONANTS + (syllable_index % 28);
bool final_present = final != BASE_OF_TRAILING_CONSONANTS;
int orientation = -1;
// Figure out which orientation we are dealing with
if (!final_present) {
// orientations 0 - 2
if (is_median_vowel_vertical(median)) {
orientation = 0;
} else if (is_median_vowel_horizontal(median)) {
orientation = 1;
} else if (is_median_vowel_combined(median)) {
orientation = 2;
}
} else {
// orientations 3 - 5
if (is_median_vowel_vertical(median)) {
orientation = 3;
} else if (is_median_vowel_horizontal(median)) {
orientation = 4;
} else if (is_median_vowel_combined(median)) {
orientation = 5;
}
}
ASSERT_MSG(orientation != -1,
fmt::format("Unable to deduce drawing orientation for korean syllable block in '{}'",
context));
// now that we know the orientation, we can consult our lookup database to get the glyphs to
// use for all the involved jamo
//
// convert each jamo to utf-8 bytes since that is what our DB is encoded with.
// - first see if the jamo has an alternative drawing glyph, if not, use the default
//
// the order the glyphs are drawn in does not matter, as they all overlap anyway.
std::vector<std::string> glyphs_to_draw = {};
const auto initial_utf8 = codepoint_to_utf8(initial);
const auto median_utf8 = codepoint_to_utf8(median);
if (final == BASE_OF_TRAILING_CONSONANTS) { // no final consonant
const auto initial_alt_lookup_key = fmt::format("<G>,{}", median_utf8);
const auto& initial_alts = db.at(initial_utf8).at(orientation).alternatives;
if (initial_alts.contains(initial_alt_lookup_key)) {
glyphs_to_draw.push_back(glyph_hex_string_to_int(initial_alts.at(initial_alt_lookup_key)));
} else {
glyphs_to_draw.push_back(
glyph_hex_string_to_int(db.at(initial_utf8).at(orientation).defaultGlyph));
}
const auto median_alt_lookup_key = fmt::format("{},<G>", initial_utf8);
const auto& median_alts = db.at(median_utf8).at(orientation).alternatives;
if (median_alts.contains(median_alt_lookup_key)) {
glyphs_to_draw.push_back(glyph_hex_string_to_int(median_alts.at(median_alt_lookup_key)));
} else {
glyphs_to_draw.push_back(
glyph_hex_string_to_int(db.at(median_utf8).at(orientation).defaultGlyph));
}
} else {
const auto final_utf8 = codepoint_to_utf8(final);
const auto initial_alt_lookup_key = fmt::format("<G>,{},{}", median_utf8, final_utf8);
const auto& initial_alts = db.at(initial_utf8).at(orientation).alternatives;
if (initial_alts.contains(initial_alt_lookup_key)) {
glyphs_to_draw.push_back(glyph_hex_string_to_int(initial_alts.at(initial_alt_lookup_key)));
} else {
glyphs_to_draw.push_back(
glyph_hex_string_to_int(db.at(initial_utf8).at(orientation).defaultGlyph));
}
const auto median_alt_lookup_key = fmt::format("{},<G>,{}", initial_utf8, final_utf8);
const auto& median_alts = db.at(median_utf8).at(orientation).alternatives;
if (median_alts.contains(median_alt_lookup_key)) {
glyphs_to_draw.push_back(glyph_hex_string_to_int(median_alts.at(median_alt_lookup_key)));
} else {
glyphs_to_draw.push_back(
glyph_hex_string_to_int(db.at(median_utf8).at(orientation).defaultGlyph));
}
const auto final_alt_lookup_key = fmt::format("{},{},<G>", initial_utf8, median_utf8);
const auto& final_alts = db.at(final_utf8).at(orientation).alternatives;
if (final_alts.contains(final_alt_lookup_key)) {
glyphs_to_draw.push_back(glyph_hex_string_to_int(final_alts.at(final_alt_lookup_key)));
} else {
glyphs_to_draw.push_back(
glyph_hex_string_to_int(db.at(final_utf8).at(orientation).defaultGlyph));
}
}
// Get rid of any duplicates in the glyphs
std::vector<std::string> final_glyphs_to_draw = {};
for (const auto& glyph : glyphs_to_draw) {
if (std::find(final_glyphs_to_draw.begin(), final_glyphs_to_draw.end(), glyph) ==
final_glyphs_to_draw.end()) {
final_glyphs_to_draw.push_back(glyph);
}
}
output += 0x4;
output += final_glyphs_to_draw.size();
for (const auto& glyph : final_glyphs_to_draw) {
output += glyph;
}
return output;
}
+54
View File
@@ -0,0 +1,54 @@
/*
MIT License
Copyright (c) 2017 Jonghwan Hyeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
https://github.com/jonghwanhyeon/hangul-jamo/blob/main/LICENSE
Code converted to C++
*/
#pragma once
#include <vector>
#include "common/util/json_util.h"
struct KoreanLookupEntry {
// glyph to use if no relevant alternative exists
std::string defaultGlyph;
// context=>glyph
// ie. "<G>,\u1166"
// when wanting to draw a specific jamo, it's spot is indicated by the <G>
std::unordered_map<std::string, std::string> alternatives;
};
void from_json(const json& j, KoreanLookupEntry& obj);
typedef std::vector<KoreanLookupEntry> KoreanLookupOrientations;
namespace font_util_korean {
bool is_korean_syllable(char32_t syllable);
std::string compose_korean_containing_text(const std::string& text);
std::string game_encode_korean_syllable(
const std::string& context,
const char32_t cp,
const std::unordered_map<std::string, KoreanLookupOrientations>& db);
}; // namespace font_util_korean
+43
View File
@@ -254,4 +254,47 @@ std::string pad_right(const std::string& input, const int width, const char padd
return input + std::string(padding_width, padding_char);
}
}
char32_t next_utf8_char(const std::string& s, size_t& i) {
char32_t cp = 0;
unsigned char c = s[i];
if (c < 0x80) { // 1-byte ASCII
cp = c;
++i;
} else if ((c >> 5) == 0x6) { // 2-byte
cp = ((c & 0x1F) << 6) | (s[i + 1] & 0x3F);
i += 2;
} else if ((c >> 4) == 0xE) { // 3-byte
cp = ((c & 0x0F) << 12) | ((s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F);
i += 3;
} else if ((c >> 3) == 0x1E) { // 4-byte
cp = ((c & 0x07) << 18) | ((s[i + 1] & 0x3F) << 12) | ((s[i + 2] & 0x3F) << 6) |
(s[i + 3] & 0x3F);
i += 4;
} else {
// invalid
++i;
}
return cp;
}
std::string utf8_encode(char32_t cp) {
std::string out;
if (cp <= 0x7F) {
out += static_cast<char>(cp);
} else if (cp <= 0x7FF) {
out += static_cast<char>(0xC0 | ((cp >> 6) & 0x1F));
out += static_cast<char>(0x80 | (cp & 0x3F));
} else if (cp <= 0xFFFF) {
out += static_cast<char>(0xE0 | ((cp >> 12) & 0x0F));
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
out += static_cast<char>(0x80 | (cp & 0x3F));
} else if (cp <= 0x10FFFF) {
out += static_cast<char>(0xF0 | ((cp >> 18) & 0x07));
out += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
out += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
out += static_cast<char>(0x80 | (cp & 0x3F));
}
return out;
}
} // namespace str_util
+2
View File
@@ -36,4 +36,6 @@ std::string to_lower(const std::string& str);
bool hex_char(char c);
std::string titlize(const std::string& str);
std::string pad_right(const std::string& input, const int width, const char padding_char);
char32_t next_utf8_char(const std::string& s, size_t& i);
std::string utf8_encode(char32_t cp);
} // namespace str_util
+1 -1
View File
@@ -6,7 +6,7 @@
#include <vector>
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
namespace decompiler {
struct ObjectFileData;
+6 -2
View File
@@ -9,7 +9,8 @@
#include "common/goos/Reader.h"
#include "common/util/BitUtils.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "common/util/font/font_utils_korean.h"
#include "common/util/print_float.h"
#include "decompiler/ObjectFile/ObjectFileDB.h"
@@ -113,7 +114,10 @@ GameTextResult process_game_text(ObjectFileData& data, GameTextVersion version)
}
// escape characters
if (font_bank_exists(version)) {
if (get_font_bank(version)->is_language_id_korean(language)) {
// If we are doing korean, we process it differently
result.text[text_id] = get_font_bank(version)->convert_korean_game_to_utf8(text.c_str());
} else if (font_bank_exists(version)) {
result.text[text_id] = get_font_bank(version)->convert_game_to_utf8(text.c_str());
} else {
result.text[text_id] = goos::get_readable_string(text.c_str()); // HACK!
+1 -1
View File
@@ -2,7 +2,7 @@
#include <string>
#include <unordered_map>
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
namespace decompiler {
struct ObjectFileData;
File diff suppressed because it is too large Load Diff
@@ -2840,4 +2840,4 @@
"WOMAN": "NAINEN",
"YELLOW SAGE": "KELTAINEN TIETÄJÄ"
}
}
}
@@ -9829,4 +9829,4 @@
"youngsamos": "Nuori Samos",
"youngsamos-before-rescue": "Samos"
}
}
}
@@ -7732,4 +7732,4 @@
"youngsamos": "Giovane Samos",
"youngsamos-before-rescue": "Samos"
}
}
}
@@ -1,53 +0,0 @@
{
"1a3": "138",
"1b5": "161",
"133a": "1113",
"1200": "1031",
"1201": "1032",
"1202": "1034",
"1203": "1033",
"120c": "10c0",
"120d": "103b",
"120e": "1042",
"1215": "10d0",
"123a": "1035",
"123c": "1070",
"123d": "1071",
"123f": "107b",
"1242": "107c",
"1240": "107d",
"1241": "107e",
"1248": "1050",
"1249": "1051",
"124e": "100c",
"124d": "100e",
"124f": "100d",
"125e": "1097",
"1280": "1000",
"1286": "1001",
"1287": "1002",
"1289": "1030",
"128f": "100f",
"1290": "1500",
"1291": "1600",
"1292": "1007",
"1293": "160d",
"1294": "1601",
"1295": "1602",
"1296": "1603",
"1297": "1604",
"1299": "160e",
"129a": "160f",
"129b": "1610",
"129c": "1606",
"129d": "1607",
"129e": "1608",
"129f": "1609",
"1300": "1613",
"1301": "1611",
"1302": "160a",
"1303": "160b",
"1304": "160c",
"1307": "1614",
"1308": "1615"
}
-49
View File
@@ -1,49 +0,0 @@
# Jak 2 shares many of the same custom menu strings as jak 1 obviously
# this is to copy them over so they are already translated.
import json
import os
with open("temp-text-id-mapping.json", "r", encoding="utf-8") as f:
mapping = json.load(f)
with open("./text/game_custom_text_en-US.json", "r", encoding="utf-8") as f:
default_jak2_english = json.load(f)
def create_text_file(locale):
if not os.path.exists(f"./text/game_custom_text_{locale}.json"):
with open(f"./text/game_custom_text_{locale}.json", "w", encoding="utf-8") as f:
json.dump(default_jak2_english, f, indent=2, ensure_ascii=False)
with open(f"./text/game_custom_text_{locale}.json", "r", encoding="utf-8") as f:
jak2_file = json.load(f)
with open(
f"../jak1/text/game_custom_text_{locale}.json", "r", encoding="utf-8"
) as f:
jak1_file = json.load(f)
for jak2_id, jak1_id in mapping.items():
jak2_file[jak2_id] = jak1_file[jak1_id].title()
with open(f"./text/game_custom_text_{locale}.json", "w", encoding="utf-8") as f:
json.dump(jak2_file, f, indent=2, ensure_ascii=False)
create_text_file("ca-ES")
create_text_file("da-DK")
create_text_file("de-DE")
create_text_file("es-ES")
create_text_file("fi-FI")
create_text_file("fr-FR")
create_text_file("hu-HU")
create_text_file("is-IS")
create_text_file("it-IT")
create_text_file("ja-JP")
create_text_file("nl-NL")
create_text_file("no-NO")
create_text_file("pl-PL")
create_text_file("pt-PT")
create_text_file("pt-BR")
create_text_file("sv-SE")
+1 -1
View File
@@ -6,8 +6,8 @@
#include "common/log/log.h"
#include "common/symbols.h"
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/Timer.h"
#include "common/util/font/font_utils.h"
#include "common/util/string_util.h"
#include "game/external/discord.h"
+1 -1
View File
@@ -9,7 +9,7 @@
#include "common/log/log.h"
#include "common/symbols.h"
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "common/util/string_util.h"
#include "game/external/discord_jak2.h"
+1 -2
View File
@@ -6,10 +6,9 @@
#include "kscheme.h"
#include "common/symbols.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "game/external/discord.h"
#include "game/external/discord_jak1.h"
#include "game/external/discord_jak2.h"
#include "game/kernel/common/Symbol4.h"
#include "game/kernel/common/kmachine.h"
+1 -1
View File
@@ -6,7 +6,7 @@
#include "kscheme.h"
#include "common/symbols.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "game/external/discord_jak3.h"
#include "game/kernel/common/Symbol4.h"
+10
View File
@@ -764,6 +764,16 @@
(case (-> s3-0 type)
((string)
(when (= (-> *setting-control* user-default subtitle-language) (language-enum korean))
;; EXPORT SUBTITLE LINES
;; dont export the same string multiple times
;; (when (not (string= *pc-subtitle-export-temp-string* (the-as string s3-0)))
;; (clear *pc-subtitle-export-temp-string*)
;; (copy-string<-string *pc-subtitle-export-temp-string* (the-as string s3-0))
;; (format 0 "subtitle length: ~D~%" (length (the-as string s3-0)))
;; (let ((file (new 'stack 'file-stream "/Users/tyler/repos/jak-project/decompiler_out/jak2/assets/subtitles.txt" 'append)))
;; (format file "~S::~f::\"" (-> (the scene-player self) anim name) f30-0)
;; (log-string-bytes (the-as string s3-0) file)
;; (file-stream-close file)))
(set! s3-0 (convert-korean-text (the-as string s3-0)))
(let ((v1-27 s2-0))
(set! (-> v1-27 scale) 0.6)
+77 -54
View File
@@ -72,76 +72,98 @@
this
)
(defun convert-korean-text ((arg0 string))
(defun convert-korean-text ((src-str string))
"Converts the provided [[string]] of [[game-text]] into korean. Returns a [[string]]"
(local-vars (v1-21 int))
(let ((charp (-> arg0 data)))
*expanded-text-line0*
(let ((s4-0 0))
0
(let ((s1-0 0)
(s5-0 (length arg0))
)
;; iterate through the bytes in the string
(let ((src-ptr (-> src-str data)))
(let ((src-i 0))
(let ((dst-i 0)
(src-len (length src-str)))
;; use line0 unless expand flag is set
(set! *expand-buf-number* (logxor *expand-buf-number* 1))
(let ((s3-0 (if (zero? *expand-buf-number*)
*expanded-text-line0*
*expanded-text-line1*
)
)
)
(let ((s2-0 (+ (-> s3-0 allocated-length) -1)))
(clear s3-0)
(while (< s4-0 s5-0)
(let ((dst (if (zero? *expand-buf-number*)
*expanded-text-line0*
*expanded-text-line1*)))
(let ((dst-max (+ (-> dst allocated-length) -1)))
(clear dst)
(while (< src-i src-len)
(cond
((= (-> charp s4-0) 3)
(+! s4-0 1)
(while (and (< s4-0 s5-0) (!= (-> charp s4-0) 3) (!= (-> charp s4-0) 4))
(set! (-> s3-0 data s1-0) (-> charp s4-0))
(+! s4-0 1)
(+! s1-0 1)
)
)
;; code 3 = copy chars into destination until we reach a code 3 or 4
;; i believe this handles non-syllable blocks, like spaces, which are represented like `0x3 0x20`
((= (-> src-ptr src-i) 3)
(+! src-i 1)
(while (and (< src-i src-len)
(!= (-> src-ptr src-i) 3)
(!= (-> src-ptr src-i) 4))
(set! (-> dst data dst-i) (-> src-ptr src-i))
(+! src-i 1)
(+! dst-i 1)))
;; any other byte (for example #4, which is the start of all syllable blocks)
(else
(let ((v1-17 (+ s4-0 1)))
(-> charp v1-17)
(set! s4-0 (+ v1-17 1))
)
(set! (-> s3-0 data s1-0) (the-as uint 126))
(let ((v1-19 (+ s1-0 1)))
(set! (-> s3-0 data v1-19) (the-as uint 89))
(let ((v1-20 (+ v1-19 1)))
(while (and (< s4-0 s5-0) (< v1-20 s2-0) (!= (-> charp s4-0) 3) (!= (-> charp s4-0) 4))
;; look ahead 1 byte (bounds assumed)
(let ((next-byte (+ src-i 1)))
(-> src-ptr next-byte) ;; meaningless, left-over code?
;; set the src-index to the value of `next-byte`+1
;; so this would be...the number of characters (or perhaps the number of syllable elements) + 1
;; this is very important i feel
(set! src-i (+ next-byte 1)))
;; the idea here is we render the jamo all at the same position as the texture for each jamo accounts for their offset
;; and then advance when we're done.
;; we use a code of 4 to mark the beginning of a jamo sequence, a.k.a. one hangeul letter.
;; there is jamo at code-page 1 (#x100-#x1ff) and code-page 3 (#x300-#x3ff), though only a handful are in code-page 1.
;; since this requires two bytes per jamo, we assume the jamo is in code-page 3 by default and thus only have to read 1 byte per jamo.
;; however, if we read a code of '5' then that means it is in code-page 1, which means we read 2 bytes.
;; the jamo sequence terminates when we reach a code 3 (copy chars) or code 4 (new letter).
;; when the sequence terminates, we move the font trans forwards and move on with our life.
;; save font trans (~Y)
(set! (-> dst data dst-i) (the-as uint 126)) ;; ~
(let ((v1-19 (+ dst-i 1)))
(set! (-> dst data v1-19) (the-as uint 89)) ;; Y
(let ((dest-index (+ v1-19 1)))
;; inner while loop, go until 0x3 or 0x4, or end of the string
(while (and (< src-i src-len)
(< dest-index dst-max)
(!= (-> src-ptr src-i) 3)
(!= (-> src-ptr src-i) 4))
(cond
((= (-> charp s4-0) 5)
(set! (-> s3-0 data v1-20) (the-as uint 1))
(+! s4-0 1)
(set! v1-21 (+ v1-20 1))
;; we hit a 0x5
((= (-> src-ptr src-i) 5)
;; code-page `0x1` byte
(set! (-> dst data dest-index) (the-as uint 1))
;; increment the src-index since we just manually wrote a `0x1` in place of the `0x5`
(+! src-i 1)
(set! v1-21 (+ dest-index 1))
)
(else
(set! (-> s3-0 data v1-20) (the-as uint 3))
(set! v1-21 (+ v1-20 1))
;; code-page `0x3` byte
(set! (-> dst data dest-index) (the-as uint 3))
(set! v1-21 (+ dest-index 1))
)
)
(set! (-> s3-0 data v1-21) (-> charp s4-0))
(+! s4-0 1)
;; now add the byte that we are looking at the current index
(set! (-> dst data v1-21) (-> src-ptr src-i))
(+! src-i 1)
;; restore font trans (~Z)
(let ((v1-22 (+ v1-21 1)))
(set! (-> s3-0 data v1-22) (the-as uint 126))
(set! (-> dst data v1-22) (the-as uint 126)) ;; ~
(let ((v1-23 (+ v1-22 1)))
(set! (-> s3-0 data v1-23) (the-as uint 90))
(set! v1-20 (+ v1-23 1))
(set! (-> dst data v1-23) (the-as uint 90)) ;; Z
(set! dest-index (+ v1-23 1))
)
)
)
(set! (-> s3-0 data v1-20) (the-as uint 126))
(let ((v1-24 (+ v1-20 1)))
(set! (-> s3-0 data v1-24) (the-as uint 43))
;; advance font trans by 26 (~+26H)
(set! (-> dst data dest-index) (the-as uint 126)) ;; ~
(let ((v1-24 (+ dest-index 1)))
(set! (-> dst data v1-24) (the-as uint 43)) ;; +
(let ((v1-25 (+ v1-24 1)))
(set! (-> s3-0 data v1-25) (the-as uint 50))
(set! (-> dst data v1-25) (the-as uint 50)) ;; 2
(let ((v1-26 (+ v1-25 1)))
(set! (-> s3-0 data v1-26) (the-as uint 54))
(set! (-> dst data v1-26) (the-as uint 54)) ;; 6
(let ((v1-27 (+ v1-26 1)))
(set! (-> s3-0 data v1-27) (the-as uint 72))
(set! s1-0 (+ v1-27 1))
(set! (-> dst data v1-27) (the-as uint 72)) ;; H
(set! dst-i (+ v1-27 1))
)
)
)
@@ -152,8 +174,9 @@
)
)
)
(set! (-> s3-0 data s1-0) (the-as uint 0))
s3-0
;; null terminate string
(set! (-> dst data dst-i) (the-as uint 0))
dst
)
)
)
+22
View File
@@ -5,6 +5,27 @@
;; name in dgo: gstring
;; dgos: KERNEL
(defun print-string-bytes ((arg0 string))
(let ((str-ptr (-> arg0 data))
(first-line-break #f))
(while (and (nonzero? (-> str-ptr 0)))
(when (and first-line-break (or (= (-> str-ptr 0) #x4) (= (-> str-ptr 0) #x59)))
(format #t "~%"))
(when (or (= (-> str-ptr 0) #x4) (= (-> str-ptr 0) #x59))
(set! first-line-break #t))
(format #t "0x~X " (-> str-ptr 0))
(set! str-ptr (&-> str-ptr 1))))
(format #t "\"~%")
(none))
(defun log-string-bytes ((arg0 string) (arg1 object))
(let ((str-ptr (-> arg0 data)))
(while (and (nonzero? (-> str-ptr 0)))
(format arg1 "0x~X " (-> str-ptr 0))
(set! str-ptr (&-> str-ptr 1))))
(format arg1 "~%~%")
(none))
;; DECOMP BEGINS
(defmethod length ((this string))
@@ -824,5 +845,6 @@
)
(define *pc-encoded-temp-string* (new 'global 'string 2048 (the-as string #f)))
;; (define *pc-subtitle-export-temp-string* (new 'global 'string 2048 (the-as string #f)))
(kmemclose)
+24 -16
View File
@@ -332,18 +332,26 @@
(set! (-> line text) (-> obj movie-line))
(return (the string #f))))
(cond
((= (pc-subtitle2-speaker none) (-> line speaker))
;; there's no speaker so who cares.
(string-format "~S" (-> line text)))
((or (= #t (-> *pc-settings* subtitle-speaker?))
(and (= 'auto (-> *pc-settings* subtitle-speaker?)) (subtitle2-flags? line offscreen)))
;; there is a speaker and we do want it.
;; we use color 33 which gets set at runtime to any color we want
(string-format "~33L~S:~0L ~S" (get-speaker *subtitle2-text* (-> line speaker)) (-> line text)))
(else
(string-format "~S" (-> line text)))
)
;; if we are in korean language mode and printing hint subtitles (which were never in the original game)
;; then the text did NOT come from `scene.gc` so we must convert it
;;
;; this code is somewhat of a mess, but modifying the line text itself was proving to be a problem
;; clean it up if you wish!
(let ((convert-to-korean? (and (not (-> obj movie-mode?))
(= (-> *setting-control* user-default subtitle-language) (language-enum korean)))))
(cond
((= (pc-subtitle2-speaker none) (-> line speaker))
;; there's no speaker so who cares.
(string-format "~S" (if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))
((or (= #t (-> *pc-settings* subtitle-speaker?))
(and (= 'auto (-> *pc-settings* subtitle-speaker?)) (subtitle2-flags? line offscreen)))
;; there is a speaker and we do want it.
;; we use color 33 which gets set at runtime to any color we want
(string-format "~33L~S:~0L ~S"
(if convert-to-korean? (convert-korean-text (get-speaker *subtitle2-text* (-> line speaker))) (get-speaker *subtitle2-text* (-> line speaker)))
(if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))
(else
(string-format "~S" (if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))))
*temp-string*)
(defbehavior current-subtitle2-pos subtitle2 ((id sound-id))
@@ -480,9 +488,9 @@
(when (= (gui-status active) (get-status *gui-control* (-> self gui-id)))
(true! subtitles-drawn?)
(protect (*display-text-box*)
(set! *display-text-box* (or *display-text-box* PC_SUBTITLE_DEBUG))
(set-speaker-color (-> line-queue elts i line speaker))
(print-game-text *temp-string* (-> self font) #f 44 (bucket-id debug-no-zbuf2))))
(set! *display-text-box* (or *display-text-box* PC_SUBTITLE_DEBUG))
(set-speaker-color (-> line-queue elts i line speaker))
(print-game-text *temp-string* (-> self font) #f 44 (bucket-id debug-no-zbuf2))))
)
;; save this for later usage
@@ -851,7 +859,7 @@
(set! (-> self have-subtitles?) #f)
(set! (-> self movie-mode?) #f)
(set! (-> self movie-line) (new 'process 'string (+ 7 (* 15 16)) (the string #f)))
(set! (-> self movie-line) (new 'process 'string 1024 (the string #f)))
(set! (-> self current-debug-scene) 0)
(set! (-> self current-debug-line) 0)
@@ -98,6 +98,8 @@
(set-origin! fnt (get-screen-x 0.5) (+ FONT_ENCODE_TEXT_Y 75))
(set-scale! fnt 0.75)
(draw-string (lookup-text! *common-text* (the text-id *text-id-val*) #f) buf fnt)
(set-origin! fnt (get-screen-x 0.5) (+ FONT_ENCODE_TEXT_Y 100))
(draw-string (convert-korean-text "\\c04\\c03\\c79\\c7f\\cee\\c04\\c02\\c72\\c83\\c04\\c02\\c2e\\c1a\\c04\\c02\\c06\\c1c") buf fnt)
)
)
)
+155
View File
@@ -0,0 +1,155 @@
;;-*-Lisp-*-
(in-package goal)
;; To run this:
#|
(mng) ;; build the engine
(lt) ;; connect to the runtime
(ml "goal_src/jak2/pc/util/korean-tester.gc") ;; build and load this file.
|#
(declare-file (debug))
(defconstant KOREAN_TESTER_TEXT_Y (get-screen-y 0.5))
(defun korean-tester-test-stop ()
"stop the encode test proc"
(kill-by-name "korean-tester" *active-pool*)
)
(define *korean-tester-initial-jamos-o1* (new 'static 'boxed-array :type int64 ))
(define *korean-tester-initial-jamos-o2* (new 'static 'boxed-array :type int64 ))
(define *korean-tester-initial-jamos-o3* (new 'static 'boxed-array :type int64 #x8b #xb3 #x92 #x93 #x94 #x95 #x96 #x97 #x98 #x99 #x9a #x9b #x9c #x9d #x9e #x8c #x9f #xa0 #xa1))
(define *korean-tester-initial-jamos-o4* (new 'static 'boxed-array :type int64 #x6 #x34 #x8 #x9 #xa #xb #xc #x0d #xe #x20 #x21 #xf #x10 #x11 #x2d #x12 #x13 #x14 #x2e))
(define *korean-tester-initial-jamos-o5* (new 'static 'boxed-array :type int64 #x5e #x65 #x69 #x70 #x71 #x72 #x73 #x74 #x75 #x76 #x77 #x78 #x79 #x7a #x7b #x5f #x7c #x7d #x7e))
(define *korean-tester-initial-jamos-o6* (new 'static 'boxed-array :type int64 #x8b #x91 #x92 #x93 #x94 #x95 #x96 #x97 #x98 #x99 #x9a #x9b #x9c #x9d #x9e #x8c #x9f #xa0 #xa1))
(define *korean-tester-initial-jamos-o6-alt* (new 'static 'boxed-array :type int64 #xae #xb3 #xb4 #xb5 #xb6 #xb7 #xb2 #xb8 #x98 #xb9 #xba #xbb #xbc #xbd #xbe #xaf #xbf #xa0 #xc0))
(define *korean-tester-middle-vertical-jamos-o1* (new 'static 'boxed-array :type int64 #x1a #x46 #x1b #x47 #x1d #x48 #x1e #x49 #x1c))
(define *korean-tester-middle-horizontal-jamos-o2* (new 'static 'boxed-array :type int64 #x82 #x83 #x87 #x88 #x84))
(define *korean-tester-middle-vertical-jamos-o4* (new 'static 'boxed-array :type int64 #x15 #x42 #x16 #x47 #x18 #x44 #x19 #x45 #x17))
(define *korean-tester-middle-horizontal-jamos-o5* (new 'static 'boxed-array :type int64 #x7f #x80 #x85 #x86 #x81))
(define *korean-tester-final-jamos-o4* (new 'static 'boxed-array :type int64 #xc5 #xc6 #xc7 #xc8 #xc9 #xca #xcb #xcc #xcd #xce #xcf #xe7 #xd0 #xe9 #xd1 #xd2 #xd3 #xd4 #xd5 #xd6 #xd7 #xd8 #xd9 #xda #xdb #xdc #xdd))
(define *korean-tester-final-jamos-o5* (new 'static 'boxed-array :type int64 #xde #xdf #xe0 #xe1 #xc9 #xe2 #xe3 #xe4 #xe5 #xe6 #xcf #xe7 #xe8 #xe9 #xea #xeb #xec #xd4 #xed #xfc #xee #xef #xf0 #xf1 #xf2 #xf3 #xf4))
(define *korean-tester-final-jamos-o6* (new 'static 'boxed-array :type int64 #xf5 #xdf #xe0 #xf6 #xc9 #xe2 #xe3 #xf7 #xe5 #xf8 #xcf #xe7 #xe8 #xe9 #xea #xf9 #xfa #xd4 #xfb #xfc #xfd #xef #xf0 #xf1 #xf2 #xf3 #xf4))
(define *korean-tester-tmp-str* (new 'global 'string 4 (the-as string #f)))
(define *korean-tester-static-combined-jamo* (new 'static 'boxed-array :type int64 #xa3 #xa7))
(define *korean-tester-static-jamo-1* #x84)
(define *korean-tester-static-jamo-2* #x0)
(define *korean-tester-static-jamo-3* #x0)
(define *korean-tester-current-jamo-coll-1* *korean-tester-initial-jamos-o6-alt*)
(define *korean-tester-current-jamo-index-1* 0)
(define *korean-tester-current-jamo-coll-2* *korean-tester-final-jamos-o6*)
(define *korean-tester-current-jamo-index-2* 0)
(defun korean-tester-test-start ()
"start the encode test proc"
(korean-tester-test-stop)
(process-spawn-function process :name "korean-tester"
(lambda :behavior process ()
(let ((fnt (new 'stack 'font-context *font-default-matrix* (get-screen-x 0.35) (get-screen-y 0.4) 0.0
(font-color red) (font-flags shadow kerning large middle)))
)
(set-width! fnt (get-screen-x 0.8))
(set-height! fnt (get-screen-y 0.1))
(loop
(suspend)
(if (or (cpad-pressed? 0 left))
(-! *korean-tester-current-jamo-index-1* 1))
(if (or (cpad-pressed? 0 right))
(+! *korean-tester-current-jamo-index-1* 1))
(minmax! *korean-tester-current-jamo-index-1* #x0 (dec (-> *korean-tester-current-jamo-coll-1* length)))
(if (or (cpad-pressed? 0 down))
(-! *korean-tester-current-jamo-index-2* 1))
(if (or (cpad-pressed? 0 up))
(+! *korean-tester-current-jamo-index-2* 1))
(minmax! *korean-tester-current-jamo-index-2* #x0 (dec (-> *korean-tester-current-jamo-coll-2* length)))
(with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf))
(bucket-id debug-no-zbuf2))
;; print out the current pure hex values
(set-scale! fnt 0.5)
(set-origin! fnt (get-screen-x 0.5) (- KOREAN_TESTER_TEXT_Y 10))
;; - 2 glyphs
;; (draw-string (string-format "0x~X, 0x~X" *korean-tester-static-jamo-1* (-> *korean-tester-current-jamo-coll* *korean-tester-current-jamo-index*)) buf fnt)
;; - 3 glyphs
;; (draw-string (string-format "0x~X , 0x~X , 0x~X"
;; *korean-tester-static-jamo-1*
;; (-> *korean-tester-current-jamo-coll-1* *korean-tester-current-jamo-index-1*)
;; (-> *korean-tester-current-jamo-coll-2* *korean-tester-current-jamo-index-2*)) buf fnt)
;; - 3 glyphs combined
(draw-string (string-format "0x~X , 0x~X"
(-> *korean-tester-current-jamo-coll-1* *korean-tester-current-jamo-index-1*)
(-> *korean-tester-current-jamo-coll-2* *korean-tester-current-jamo-index-2*)) buf fnt)
;; print out the korean symbol now
(set-scale! fnt 1.0)
(clear *korean-tester-tmp-str*)
;; - 2 glyphs
;; (set! (-> *korean-tester-tmp-str* data 0) 4)
;; (set! (-> *korean-tester-tmp-str* data 1) 2)
;; (set! (-> *korean-tester-tmp-str* data 2) *korean-tester-static-jamo-1*)
;; (if (= *korean-tester-static-jamo-2* #x0)
;; (set! (-> *korean-tester-tmp-str* data 3) (-> *korean-tester-current-jamo-coll* *korean-tester-current-jamo-index*))
;; (set! (-> *korean-tester-tmp-str* data 3) *korean-tester-static-jamo-2*))
;; (set! (-> *korean-tester-tmp-str* data 4) 0)
;; - 3 glyphs
;; (set! (-> *korean-tester-tmp-str* data 0) 4)
;; (set! (-> *korean-tester-tmp-str* data 1) 3)
;; (set! (-> *korean-tester-tmp-str* data 2) *korean-tester-static-jamo-1*)
;; (if (= *korean-tester-static-jamo-2* #x0)
;; (set! (-> *korean-tester-tmp-str* data 3) (-> *korean-tester-current-jamo-coll-1* *korean-tester-current-jamo-index-1*))
;; (set! (-> *korean-tester-tmp-str* data 3) *korean-tester-static-jamo-2*))
;; (if (= *korean-tester-static-jamo-3* #x0)
;; (set! (-> *korean-tester-tmp-str* data 4) (-> *korean-tester-current-jamo-coll-2* *korean-tester-current-jamo-index-2*))
;; (set! (-> *korean-tester-tmp-str* data 4) *korean-tester-static-jamo-3*))
;; (set! (-> *korean-tester-tmp-str* data 5) 0)
;; - 3 glyphs (combined static)
(set! (-> *korean-tester-tmp-str* data 0) 4)
(if (= 2 (-> *korean-tester-static-combined-jamo* length))
(begin
(set! (-> *korean-tester-tmp-str* data 1) 4)
(set! (-> *korean-tester-tmp-str* data 2) (-> *korean-tester-static-combined-jamo* 0))
(set! (-> *korean-tester-tmp-str* data 3) (-> *korean-tester-static-combined-jamo* 1))
(if (= *korean-tester-static-jamo-2* #x0)
(set! (-> *korean-tester-tmp-str* data 4) (-> *korean-tester-current-jamo-coll-1* *korean-tester-current-jamo-index-1*))
(set! (-> *korean-tester-tmp-str* data 4) *korean-tester-static-jamo-2*))
(if (= *korean-tester-static-jamo-3* #x0)
(set! (-> *korean-tester-tmp-str* data 5) (-> *korean-tester-current-jamo-coll-2* *korean-tester-current-jamo-index-2*))
(set! (-> *korean-tester-tmp-str* data 5) *korean-tester-static-jamo-3*))
(set! (-> *korean-tester-tmp-str* data 6) 0))
(begin
(set! (-> *korean-tester-tmp-str* data 1) 3)
(set! (-> *korean-tester-tmp-str* data 2) (-> *korean-tester-static-combined-jamo* 0))
(if (= *korean-tester-static-jamo-2* #x0)
(set! (-> *korean-tester-tmp-str* data 3) (-> *korean-tester-current-jamo-coll-1* *korean-tester-current-jamo-index-1*))
(set! (-> *korean-tester-tmp-str* data 3) *korean-tester-static-jamo-2*))
(if (= *korean-tester-static-jamo-3* #x0)
(set! (-> *korean-tester-tmp-str* data 4) (-> *korean-tester-current-jamo-coll-2* *korean-tester-current-jamo-index-2*))
(set! (-> *korean-tester-tmp-str* data 4) *korean-tester-static-jamo-3*))
(set! (-> *korean-tester-tmp-str* data 5) 0)))
;; (log-string-bytes *korean-tester-tmp-str* 0)
(set-origin! fnt (get-screen-x 0.5) (+ KOREAN_TESTER_TEXT_Y 10))
(draw-string (convert-korean-text *korean-tester-tmp-str*) buf fnt)
)
)
)
)
)
)
(korean-tester-test-start)
+108
View File
@@ -0,0 +1,108 @@
;;-*-Lisp-*-
(in-package goal)
;; To run this:
#|
(mng) ;; build the engine
(lt) ;; connect to the runtime
(ml "goal_src/jak2/pc/util/string-viewer.gc") ;; build and load this file.
|#
(declare-file (debug))
(define *string-viewer-text-id-val* #x0100)
(defconstant STRING_VIEWER_TEXT_Y (get-screen-y 0.05))
(defun string-viewer-test-stop ()
"stop the encode test proc"
(kill-by-name "string-viewer" *active-pool*)
)
(define *string-viewer-tmp-str* (new 'global 'string 128 (the-as string #f)))
(defun string-viewer-test-start ()
"start the encode test proc"
(string-viewer-test-stop)
(process-spawn-function process :name "string-viewer"
(lambda :behavior process ()
(let ((fnt (new 'stack 'font-context *font-default-matrix* (get-screen-x 0.35) (get-screen-y 0.4) 0.0
(font-color red) (font-flags shadow kerning large middle)))
)
(set-width! fnt (get-screen-x 0.8))
(set-height! fnt (get-screen-y 0.1))
(loop
(suspend)
(if (or (cpad-pressed? 0 left) (cpad-hold? 0 l1))
(-! *string-viewer-text-id-val* 1))
(if (or (cpad-pressed? 0 right) (cpad-hold? 0 r1))
(+! *string-viewer-text-id-val* 1))
(minmax! *string-viewer-text-id-val* #x0 #x134c)
(with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf))
(bucket-id debug-no-zbuf2))
(set-scale! fnt 0.5)
(set-origin! fnt (get-screen-x 0.5) (- STRING_VIEWER_TEXT_Y 10))
(draw-string (string-format "Text ID: ~x" *string-viewer-text-id-val*) buf fnt)
(set-origin! fnt (get-screen-x 0.5) (+ STRING_VIEWER_TEXT_Y 10))
(draw-string "English:" buf fnt)
(set-origin! fnt (get-screen-x 0.5) (+ STRING_VIEWER_TEXT_Y 30))
(draw-string (lookup-text! *fallback-text* (the text-id *string-viewer-text-id-val*) #f) buf fnt)
;; Assumes you have your text set to korean
(set-origin! fnt (get-screen-x 0.5) (+ STRING_VIEWER_TEXT_Y 50))
(draw-string "Korean:" buf fnt)
(set-scale! fnt 1.0)
(set-origin! fnt (get-screen-x 0.5) (+ STRING_VIEWER_TEXT_Y 75))
(draw-string (lookup-text! *common-text* (the text-id *string-viewer-text-id-val*) #f) buf fnt)
(lookup-text! *common-text* (the text-id *string-viewer-text-id-val*) #t)
;; draw the bytes of each character
(set-scale! fnt 0.35)
(set-origin! fnt (get-screen-x 0.5) (+ STRING_VIEWER_TEXT_Y 115))
(clear *pc-cpp-temp-string*)
(clear *string-viewer-tmp-str*)
(let ((raw-str (lookup-text-string *common-text* (the text-id *string-viewer-text-id-val*))))
(let ((str-ptr (-> raw-str data))
(first-line-break #f)
(y-origin (+ STRING_VIEWER_TEXT_Y 115))
(last-byte (the-as uint 0)))
(while (and (nonzero? (-> str-ptr 0)))
(when (and first-line-break (= (-> str-ptr 0) #x4))
(draw-string (string-format "~S~S~S~S" "~[~1L" *string-viewer-tmp-str* "~[~6L" *pc-cpp-temp-string*) buf fnt)
(set! y-origin (+ y-origin 15))
(set-origin! fnt (get-screen-x 0.5) y-origin)
(clear *pc-cpp-temp-string*)
(clear *string-viewer-tmp-str*))
(when (= (-> str-ptr 0) #x4)
(set! first-line-break #t)
(format *string-viewer-tmp-str* "0x~X " (-> str-ptr 0)))
(when (and (or (= (-> str-ptr 0) #x3) (= (-> str-ptr 0) #x2)) (= last-byte #x4))
(format *string-viewer-tmp-str* "0x~X " (-> str-ptr 0)))
(when (and (not (= (-> str-ptr 0) #x4))
(not (and (or (= (-> str-ptr 0) #x3) (= (-> str-ptr 0) #x2)) (= last-byte #x4))))
(format *pc-cpp-temp-string* "0x~X " (-> str-ptr 0)))
(set! last-byte (-> str-ptr 0))
(set! str-ptr (&-> str-ptr 1)))
(draw-string (string-format "~S~S~S~S" "~[~1L" *string-viewer-tmp-str* "~[~6L" *pc-cpp-temp-string*) buf fnt)
)
)
(clear *temp-string*)
)
)
)
)
)
)
(string-viewer-test-start)
+15 -14
View File
@@ -461,24 +461,25 @@ This gives us more freedom to write code how we want.
(text-id progress-language-portuguese)
(text-id progress-language-dutch)
(text-id progress-language-english-uk)
; (text-id language-name-finnish)
; (text-id language-name-swedish)
; (text-id language-name-danish)
; (text-id language-name-norwegian)
; (text-id language-name-dutch)
; (text-id language-name-br-portuguese)
; (text-id language-name-hungarian)
; (text-id language-name-catalan)
; (text-id language-name-icelandic)
; (text-id language-name-polish)
; (text-id language-name-lithuanian)
; (text-id language-name-czech)
; (text-id language-name-croatian)
; (text-id language-name-galician)
(text-id language-name-finnish)
(text-id language-name-swedish)
(text-id language-name-danish)
(text-id language-name-norwegian)
(text-id language-name-dutch)
(text-id language-name-br-portuguese)
(text-id language-name-hungarian)
(text-id language-name-catalan)
(text-id language-name-icelandic)
(text-id language-name-polish)
(text-id language-name-lithuanian)
(text-id language-name-czech)
(text-id language-name-croatian)
(text-id language-name-galician)
)
:get-item-index-fn (lambda () (-> *pc-settings* subtitle-language))
:on-confirm (lambda ((index int) (the-progress progress))
(set! (-> *pc-settings* subtitle-language) (the-as pc-language index))
(set! (-> *setting-control* user-default subtitle-language) (the-as language-enum index))
(pc-settings-save))))
(defmacro game-options-pc-sound-language ()
+24 -16
View File
@@ -327,18 +327,26 @@
(set! (-> line text) (-> obj movie-line))
(return (the string #f))))
(cond
((= (pc-subtitle3-speaker none) (-> line speaker))
;; there's no speaker so who cares.
(string-format "~S" (-> line text)))
((or (= #t (-> *pc-settings* subtitle-speaker?))
(and (= 'auto (-> *pc-settings* subtitle-speaker?)) (subtitle3-flags? line offscreen)))
;; there is a speaker and we do want it.
;; we use color 42 which gets set at runtime to any color we want
(string-format "~42L~S:~0L ~S" (get-speaker *subtitle3-text* (-> line speaker)) (-> line text)))
(else
(string-format "~S" (-> line text)))
)
;; if we are in korean language mode and printing hint subtitles (which were never in the original game)
;; then the text did NOT come from `scene.gc` so we must convert it
;;
;; this code is somewhat of a mess, but modifying the line text itself was proving to be a problem
;; clean it up if you wish!
(let ((convert-to-korean? (and (not (-> obj movie-mode?))
(= (-> *setting-control* user-default subtitle-language) (language-enum korean)))))
(cond
((= (pc-subtitle3-speaker none) (-> line speaker))
;; there's no speaker so who cares.
(string-format "~S" (if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))
((or (= #t (-> *pc-settings* subtitle-speaker?))
(and (= 'auto (-> *pc-settings* subtitle-speaker?)) (subtitle3-flags? line offscreen)))
;; there is a speaker and we do want it.
;; we use color 33 which gets set at runtime to any color we want
(string-format "~33L~S:~0L ~S"
(if convert-to-korean? (convert-korean-text (get-speaker *subtitle3-text* (-> line speaker))) (get-speaker *subtitle3-text* (-> line speaker)))
(if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))
(else
(string-format "~S" (if convert-to-korean? (convert-korean-text (-> line text)) (-> line text))))))
*temp-string*)
(defbehavior current-subtitle3-pos subtitle3 ((id sound-id))
@@ -475,9 +483,9 @@
(when (= (gui-status active) (get-status *gui-control* (-> self gui-id)))
(true! subtitles-drawn?)
(protect (*display-text-box*)
(set! *display-text-box* (or *display-text-box* PC_SUBTITLE_DEBUG))
(set-speaker-color (-> line-queue elts i line speaker))
(print-game-text *temp-string* (-> self font) #f 44 (bucket-id debug-no-zbuf2))))
(set! *display-text-box* (or *display-text-box* PC_SUBTITLE_DEBUG))
(set-speaker-color (-> line-queue elts i line speaker))
(print-game-text *temp-string* (-> self font) #f 44 (bucket-id debug-no-zbuf2))))
)
;; save this for later usage
@@ -846,7 +854,7 @@
(set! (-> self have-subtitles?) #f)
(set! (-> self movie-mode?) #f)
(set! (-> self movie-line) (new 'process 'string (+ 7 (* 15 16)) (the string #f)))
(set! (-> self movie-line) (new 'process 'string 1024 (the string #f)))
(set! (-> self current-debug-scene) 0)
(set! (-> self current-debug-line) 0)
@@ -619,19 +619,12 @@ std::vector<symbol_info::SymbolInfo*> Compiler::lookup_symbol_info_by_prefix(
std::set<std::string> Compiler::lookup_symbol_names_starting_with(const std::string& prefix,
int max_count) const {
if (m_goos.reader.check_string_is_valid(prefix)) {
return m_symbol_info.lookup_names_starting_with(prefix, max_count);
}
return {};
return m_symbol_info.lookup_names_starting_with(prefix, max_count);
}
std::vector<symbol_info::SymbolInfo*> Compiler::lookup_exact_name_info(
const std::string& name) const {
if (m_goos.reader.check_string_is_valid(name)) {
return m_symbol_info.lookup_exact_name(name);
} else {
return {};
}
return m_symbol_info.lookup_exact_name(name);
}
std::optional<TypeSpec> Compiler::lookup_typespec(const std::string& symbol_name) {
+19 -22
View File
@@ -21,8 +21,9 @@
#include "common/goos/ParseHelpers.h"
#include "common/goos/Reader.h"
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
#include "common/util/json_util.h"
#include "common/util/string_util.h"
#include "game/runtime.h"
@@ -30,19 +31,6 @@
namespace {
// TODO - replace with str_util::to_upper?
std::string uppercase(const std::string& in) {
std::string result;
result.reserve(in.size());
for (auto c : in) {
if (c >= 'a' && c <= 'z') {
c -= ('a' - 'A');
}
result.push_back(c);
}
return result;
}
/*
(deftype game-text (structure)
((id uint32 :offset-assert 0)
@@ -86,8 +74,8 @@ void compile_text(GameTextDB& db, const std::string& output_prefix) {
file_util::create_dir_if_needed(file_util::get_file_path({"out", output_prefix, "iso"}));
file_util::write_binary_file(
file_util::get_file_path(
{"out", output_prefix, "iso", fmt::format("{}{}.TXT", lang, uppercase(group_name))}),
file_util::get_file_path({"out", output_prefix, "iso",
fmt::format("{}{}.TXT", lang, str_util::to_upper(group_name))}),
data.data(), data.size());
}
}
@@ -157,8 +145,8 @@ void compile_subtitles_v1(GameSubtitleDB& db, const std::string& output_prefix)
file_util::create_dir_if_needed(file_util::get_file_path({"out", output_prefix, "iso"}));
file_util::write_binary_file(
file_util::get_file_path(
{"out", output_prefix, "iso", fmt::format("{}{}.TXT", lang, uppercase("subtit"))}),
file_util::get_file_path({"out", output_prefix, "iso",
fmt::format("{}{}.TXT", lang, str_util::to_upper("subtit"))}),
data.data(), data.size());
}
}
@@ -202,7 +190,12 @@ void compile_subtitles_v2(GameSubtitleDB& db, const std::string& output_prefix)
if (line.metadata.merge) {
gen.add_symbol_link("#f");
} else {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(line.text)); // line text
if (font->is_language_id_korean(lang)) {
gen.add_ref_to_string_in_pool(
font->convert_utf8_to_game_korean(line.text)); // line text
} else {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(line.text)); // line text
}
}
u16 speaker = bank->speaker_enum_value_from_name(line.metadata.speaker);
u16 flags = 0;
@@ -218,7 +211,11 @@ void compile_subtitles_v2(GameSubtitleDB& db, const std::string& output_prefix)
for (auto& speaker_localized : localized_speakers) {
// No need to check for invalid speakers here, they are checked at the scene line level above
// and throw an error
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(speaker_localized));
if (font->is_language_id_korean(lang)) {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game_korean(speaker_localized));
} else {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(speaker_localized));
}
}
auto data = gen.generate_v2();
@@ -226,8 +223,8 @@ void compile_subtitles_v2(GameSubtitleDB& db, const std::string& output_prefix)
file_util::create_dir_if_needed(file_util::get_file_path({"out", output_prefix, "iso"}));
auto file_name = get_text_version_name(bank->m_text_version) == "jak3" ? "subti3" : "subti2";
file_util::write_binary_file(
file_util::get_file_path(
{"out", output_prefix, "iso", fmt::format("{}{}.TXT", lang, uppercase(file_name))}),
file_util::get_file_path({"out", output_prefix, "iso",
fmt::format("{}{}.TXT", lang, str_util::to_upper(file_name))}),
data.data(), data.size());
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
#include "common/serialization/subtitles/subtitles_v2.h"
#include "common/serialization/text/text_ser.h"
#include "common/util/Assert.h"
#include "common/util/FontUtils.h"
#include "common/util/font/font_utils.h"
void compile_game_text(const std::vector<GameTextDefinitionFile>& filenames,
GameTextDB& db,
+4
View File
@@ -119,7 +119,9 @@ void CodeTester::clear() {
* Execute the buffered code with no arguments, return the value of RAX.
*/
u64 CodeTester::execute() {
// clang-format off
return ((u64(*)())code_buffer)();
// clang-format on
}
/*!
@@ -127,7 +129,9 @@ u64 CodeTester::execute() {
* arguments will appear in (will handle windows/linux differences)
*/
u64 CodeTester::execute(u64 in0, u64 in1, u64 in2, u64 in3) {
// clang-format off
return ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3);
// clang-format on
}
/*!
+2
View File
@@ -40,7 +40,9 @@ class CodeTester {
*/
template <typename T>
T execute_ret(u64 in0, u64 in1, u64 in2, u64 in3) {
// clang-format off
u64 result_u64 = ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3);
// clang-format on
T result_T;
memcpy(&result_T, &result_u64, sizeof(T));
return result_T;
+1
View File
@@ -6,6 +6,7 @@
#include "common/repl/repl_wrapper.h"
#include "common/util/FileUtil.h"
#include "common/util/diff.h"
#include "common/util/font/font_utils_korean.h"
#include "common/util/string_util.h"
#include "common/util/term_util.h"
#include "common/util/unicode_util.h"
File diff suppressed because it is too large Load Diff
+80 -183
View File
@@ -9,8 +9,6 @@ parser.add_argument("--fix", action="store_true")
parser.set_defaults(fix=False)
args = parser.parse_args()
# TODO - trim strings
# fmt: off
JAK1_ALLOWED_CHARACTERS = [
"_", # NOTE - not an actual underscore, adds a long space!
@@ -52,7 +50,6 @@ JAK1_AUTO_REPLACEMENTS = {
"": "?"
}
# TODO - check for korean text
JAK2_ALLOWED_CHARACTERS = [
"_", # NOTE - not an actual underscore, adds a long space!
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
@@ -115,57 +112,57 @@ JAK2_AUTO_REPLACEMENTS = {
}
# fmt: on
invalid_characters_found = False
return_error = False
# TODO - reduce duplication
def is_korean_syllable(char):
return '\uAC00' <= char <= '\uD7A3'
def is_char_allowed(game_name, char, allowed_characters):
if game_name == "jak1":
return char in allowed_characters
return char in allowed_characters or is_korean_syllable(char)
def jak1_is_allowed_code(pos, text):
def is_allowed_code(pos, text, allowed_codes):
# Find any occurences of allowed codes in the string
# if the position overlaps with these occurrences, it's allowed
for code in JAK1_ALLOWED_CODES:
for code in allowed_codes:
for match in re.finditer(code, text):
if pos >= match.start() and pos <= match.end():
return match.end()
return -1
def jak1_char_allowed(char):
return char in JAK1_ALLOWED_CHARACTERS
def jak1_fix_character(char):
def fix_character(game_name, char, allowed_characters, auto_replacements):
# First let's try upper-casing it, if that's allowed, let's use that instead
upper_case = char.upper()
if jak1_char_allowed(upper_case):
if is_char_allowed(game_name, upper_case, allowed_characters):
return upper_case
if char in JAK1_AUTO_REPLACEMENTS:
return JAK1_AUTO_REPLACEMENTS[char]
if char in auto_replacements:
return auto_replacements[char]
return char
def jak1_replace_character(string, position, new_character):
def replace_character(string, position, new_character):
string_list = list(string)
string_list[position] = new_character
new_string = "".join(string_list)
return new_string
def lint_jak1_characters(text):
def lint_characters(game_name, text, allowed_characters, allowed_codes, auto_replacements):
invalid_characters_found = False
pos = 0
while pos < len(text):
character = text[pos]
if not jak1_char_allowed(character):
if not is_char_allowed(game_name, character, allowed_characters):
# Check to see if it's an allowed code
code_end_pos = jak1_is_allowed_code(pos, text)
code_end_pos = is_allowed_code(pos, text, allowed_codes)
if code_end_pos == -1:
# If we are fixing instances, attempt to do so
char_fixed = False
if args.fix:
new_char = jak1_fix_character(character)
new_char = fix_character(game_name, character, allowed_characters, auto_replacements)
if new_char != character:
text = jak1_replace_character(text, pos, new_char)
text = replace_character(text, pos, new_char)
char_fixed = True
if not char_fixed:
print(
@@ -173,7 +170,7 @@ def lint_jak1_characters(text):
character, text, text
)
)
# text = jak1_replace_character(text, pos, "?")
# text = replace_character(text, pos, "?")
invalid_characters_found = True
pos = pos + 1
else:
@@ -183,175 +180,75 @@ def lint_jak1_characters(text):
pos = pos + 1
return invalid_characters_found, text
def fix_games_translations(game_name, allowed_characters, allowed_codes, auto_replacements):
global return_error
print(f"Checking {game_name} translations")
# Iterate through the translations making sure there are no characters that are not allowed
text_files = glob.glob(f"./game/assets/{game_name}/text/*.json")
# Iterate through the translations making sure there are no characters that are not allowed
text_files = glob.glob("./game/assets/jak1/text/*.json")
for text_file in text_files:
print("Checking {}".format(text_file))
with open(text_file, encoding="utf-8") as f:
file_data = json.load(f)
for id, text in file_data.items():
invalid_chars_exist, new_text = lint_jak1_characters(text)
for text_file in text_files:
print("Checking {}".format(text_file))
with open(text_file, encoding="utf-8") as f:
file_data = json.load(f)
for id, text in file_data.items():
invalid_chars_exist, new_text = lint_characters(game_name, text, allowed_characters, allowed_codes, auto_replacements)
if args.fix:
file_data[id] = new_text
if invalid_chars_exist:
return_error = True
if args.fix:
file_data[id] = new_text
if invalid_chars_exist:
invalid_characters_found = True
if args.fix:
# save the modified file back out
with open(text_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
# save the modified file back out
with open(text_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
subtitle_files = glob.glob("./game/assets/jak1/subtitle/*lines*.json")
subtitle_files = glob.glob(f"./game/assets/{game_name}/subtitle/*lines*.json")
for subtitle_file in subtitle_files:
print("Checking {}...".format(subtitle_file))
with open(subtitle_file, encoding="utf-8") as f:
file_data = json.load(f)
# Check Speakers
for id, text in file_data["speakers"].items():
invalid_chars_exist, new_text = lint_jak1_characters(text)
if args.fix and new_text != text:
file_data["speakers"][id] = new_text
if invalid_chars_exist:
invalid_characters_found = True
# Check Lines
for id, lines in file_data["cutscenes"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_jak1_characters(line)
if args.fix and new_text != line:
lines[i] = new_text
for subtitle_file in subtitle_files:
print("Checking {}...".format(subtitle_file))
with open(subtitle_file, encoding="utf-8") as f:
file_data = json.load(f)
# Check Speakers
for id, text in file_data["speakers"].items():
invalid_chars_exist, new_text = lint_characters(game_name, text, allowed_characters, allowed_codes, auto_replacements)
if args.fix and new_text != text:
file_data["speakers"][id] = new_text
if invalid_chars_exist:
invalid_characters_found = True
for id, lines in file_data["hints"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_jak1_characters(line)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
invalid_characters_found = True
if args.fix:
# save the modified file back out
with open(subtitle_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
def jak2_is_allowed_code(pos, text):
# Find any occurences of allowed codes in the string
# if the position overlaps with these occurrences, it's allowed
for code in JAK2_ALLOWED_CODES:
for match in re.finditer(code, text):
if pos >= match.start() and pos <= match.end():
return match.end()
return -1
def jak2_char_allowed(char):
return char in JAK2_ALLOWED_CHARACTERS
def jak2_fix_character(char):
if char in JAK2_AUTO_REPLACEMENTS:
return JAK2_AUTO_REPLACEMENTS[char]
return char
def jak2_replace_character(string, position, new_character):
string_list = list(string)
string_list[position] = new_character
new_string = "".join(string_list)
return new_string
def lint_jak2_characters(text):
invalid_characters_found = False
pos = 0
while pos < len(text):
character = text[pos]
if not jak2_char_allowed(character):
# Check to see if it's an allowed code
code_end_pos = jak2_is_allowed_code(pos, text)
if code_end_pos == -1:
# If we are fixing instances, attempt to do so
char_fixed = False
if args.fix:
new_char = jak2_fix_character(character)
if new_char != character:
text = jak2_replace_character(text, pos, new_char)
char_fixed = True
if not char_fixed:
print(
"Character '{}' not allowed - Found in {} in string {}".format(
character, text, text
)
)
# text = jak2_replace_character(text, pos, "?")
invalid_characters_found = True
pos = pos + 1
else:
# advance to the end of the code and continue checking
pos = code_end_pos
return_error = True
# Check Lines
for id, lines in file_data["cutscenes"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_characters(game_name, line, allowed_characters, allowed_codes, auto_replacements)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
return_error = True
if game_name == "jak1":
for id, lines in file_data["hints"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_characters(game_name, line, allowed_characters, allowed_codes, auto_replacements)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
return_error = True
else:
pos = pos + 1
return invalid_characters_found, text
# Iterate through the translations making sure there are no characters that are not allowed
text_files = glob.glob("./game/assets/jak2/text/*.json")
for text_file in text_files:
print("Checking {}".format(text_file))
with open(text_file, encoding="utf-8") as f:
file_data = json.load(f)
for id, text in file_data.items():
invalid_chars_exist, new_text = lint_jak2_characters(text)
for id, lines in file_data["other"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_characters(game_name, line, allowed_characters, allowed_codes, auto_replacements)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
return_error = True
if args.fix:
file_data[id] = new_text
if invalid_chars_exist:
invalid_characters_found = True
if args.fix:
# save the modified file back out
with open(text_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
# save the modified file back out
with open(subtitle_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
subtitle_files = glob.glob("./game/assets/jak2/subtitle/*lines*.json")
fix_games_translations("jak1", JAK1_ALLOWED_CHARACTERS, JAK1_ALLOWED_CODES, JAK1_AUTO_REPLACEMENTS)
fix_games_translations("jak2", JAK2_ALLOWED_CHARACTERS, JAK2_ALLOWED_CODES, JAK2_AUTO_REPLACEMENTS)
for subtitle_file in subtitle_files:
print("Checking {}...".format(subtitle_file))
with open(subtitle_file, encoding="utf-8") as f:
file_data = json.load(f)
# Check Speakers
for id, text in file_data["speakers"].items():
invalid_chars_exist, new_text = lint_jak2_characters(text)
if args.fix and new_text != text:
file_data["speakers"][id] = new_text
if invalid_chars_exist:
invalid_characters_found = True
# Check Lines
for id, lines in file_data["cutscenes"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_jak2_characters(line)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
invalid_characters_found = True
for id, lines in file_data["other"].items():
for i, line in enumerate(lines):
invalid_chars_exist, new_text = lint_jak2_characters(line)
if args.fix and new_text != line:
lines[i] = new_text
if invalid_chars_exist:
invalid_characters_found = True
if args.fix:
# save the modified file back out
with open(subtitle_file, "w", encoding="utf-8") as f:
json.dump(file_data, f, indent=2, ensure_ascii=False)
f.write("\n")
if invalid_characters_found:
if return_error:
print("Invalid characters were found, see above")
exit(1)
else:
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB