mirror of
https://github.com/open-goal/jak-project
synced 2026-07-28 15:18:47 -04:00
patch more text lines, special handling for credits
This commit is contained in:
@@ -82,7 +82,7 @@ void parse_text(const goos::Object& data, GameTextVersion text_ver, GameTextDB&
|
||||
for_each_in_list(data.as_pair()->cdr, [&](const goos::Object& obj) {
|
||||
if (obj.is_pair()) {
|
||||
auto& head = car(obj);
|
||||
if (head.is_symbol() && head.as_symbol()->name == "language-id") {
|
||||
if (head.is_symbol("language-id")) {
|
||||
if (banks.size() != 0) {
|
||||
throw std::runtime_error("Languages have been set multiple times.");
|
||||
}
|
||||
@@ -104,7 +104,7 @@ void parse_text(const goos::Object& data, GameTextVersion text_ver, GameTextDB&
|
||||
banks.push_back(db.bank_by_id(possible_group_name, lang));
|
||||
}
|
||||
});
|
||||
} else if (head.is_symbol() && head.as_symbol()->name == "group-name") {
|
||||
} else if (head.is_symbol("group-name")) {
|
||||
if (!possible_group_name.empty()) {
|
||||
throw std::runtime_error("group-name has been set multiple times.");
|
||||
}
|
||||
@@ -118,6 +118,56 @@ void parse_text(const goos::Object& data, GameTextVersion text_ver, GameTextDB&
|
||||
if (!cdr(cdr(obj)).is_empty_list()) {
|
||||
throw std::runtime_error("group-name has too many arguments");
|
||||
}
|
||||
} else if (head.is_symbol("credits")) {
|
||||
// parse a "credits" object. it's a list of lines where the ID automatically increments, and
|
||||
// empty lines are skipped
|
||||
if (banks.size() == 0) {
|
||||
throw std::runtime_error("At least one language must be set before defining entries.");
|
||||
}
|
||||
|
||||
if (cdr(obj).is_empty_list() || cdr(cdr(obj)).is_empty_list() ||
|
||||
!car(cdr(obj)).is_symbol(":begin") || !car(cdr(cdr(obj))).is_int()) {
|
||||
throw std::runtime_error("Invalid credits begin param");
|
||||
}
|
||||
|
||||
const auto& it = cdr(cdr(obj));
|
||||
int begin_id = car(it).as_int();
|
||||
int id = begin_id - 1;
|
||||
for_each_in_list(cdr(it), [&](const goos::Object& entry) {
|
||||
++id;
|
||||
if (entry.is_string()) {
|
||||
if (entry.as_string()->data.empty()) {
|
||||
// empty string! just advance
|
||||
return;
|
||||
}
|
||||
|
||||
auto line = font->convert_utf8_to_game(entry.as_string()->data);
|
||||
// add to all langs
|
||||
for (auto& bank : banks) {
|
||||
bank->set_line(id, line);
|
||||
}
|
||||
} else if (entry.is_pair()) {
|
||||
int b_i = 0;
|
||||
for_each_in_list(entry, [&](const goos::Object& entry) {
|
||||
if (entry.is_string()) {
|
||||
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);
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
|
||||
}
|
||||
});
|
||||
if (b_i != int(banks.size())) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Not enough strings specified in text id #x{:x}", id));
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
else if (head.is_int()) {
|
||||
|
||||
@@ -647,7 +647,7 @@ std::string ObjectFileDB::process_game_text_files(const Config& cfg) {
|
||||
if (text_by_language_by_id.empty()) {
|
||||
return {};
|
||||
}
|
||||
return write_game_text(cfg, text_by_language_by_id);
|
||||
return write_game_text(cfg.text_version, text_by_language_by_id);
|
||||
}
|
||||
|
||||
std::string ObjectFileDB::process_game_count_file() {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"disassemble_code": false,
|
||||
|
||||
// Run the decompiler
|
||||
"decompile_code": true,
|
||||
"decompile_code": false,
|
||||
|
||||
// run the first pass of the decompiler
|
||||
"find_functions": true,
|
||||
|
||||
@@ -24,6 +24,10 @@ DecompilerLabel get_label(ObjectFileData& data, const LinkedWord& word) {
|
||||
return data.linked_data.labels.at(word.label_id());
|
||||
}
|
||||
|
||||
static const std::unordered_map<GameTextVersion, std::pair<int, int>> sTextCreditsIDs = {
|
||||
{GameTextVersion::JAK1_V1, {0xb00, 0xf00}},
|
||||
{GameTextVersion::JAK1_V2, {0xb00, 0xf00}}};
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
@@ -133,7 +137,7 @@ GameTextResult process_game_text(ObjectFileData& data, GameTextVersion version)
|
||||
}
|
||||
|
||||
std::string write_game_text(
|
||||
const Config& /*cfg*/,
|
||||
GameTextVersion version,
|
||||
const std::unordered_map<int, std::unordered_map<int, std::string>>& data) {
|
||||
// first sort languages:
|
||||
std::vector<int> languages;
|
||||
@@ -144,9 +148,30 @@ std::string write_game_text(
|
||||
|
||||
// build map
|
||||
std::map<int, std::vector<std::string>> text_by_id;
|
||||
std::map<int, std::vector<std::string>> text_by_id_credits;
|
||||
std::map<int, std::vector<std::string>> text_by_id_post_credits;
|
||||
int last_credits_id = 0;
|
||||
int credits_begin = 0;
|
||||
int credits_end = 0;
|
||||
if (auto it = sTextCreditsIDs.find(version); it != sTextCreditsIDs.end()) {
|
||||
credits_begin = it->second.first;
|
||||
credits_end = it->second.second;
|
||||
}
|
||||
for (auto lang : languages) {
|
||||
for (auto& text : data.at(lang)) {
|
||||
text_by_id[text.first].push_back(text.second);
|
||||
for (auto& [id, text] : data.at(lang)) {
|
||||
if (id < credits_begin) {
|
||||
// comes before credits
|
||||
text_by_id[id].push_back(text);
|
||||
} else if (id < credits_end) {
|
||||
// comes before credits
|
||||
text_by_id_credits[id].push_back(text);
|
||||
if (id > last_credits_id) {
|
||||
last_credits_id = id;
|
||||
}
|
||||
} else {
|
||||
// comes after credits
|
||||
text_by_id_post_credits[id].push_back(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +190,48 @@ std::string write_game_text(
|
||||
}
|
||||
result += ")\n\n";
|
||||
}
|
||||
if (text_by_id_credits.size() > 0) {
|
||||
result += fmt::format("(credits :begin #x{:04x}\n ", sTextCreditsIDs.at(version).first);
|
||||
|
||||
for (int id = sTextCreditsIDs.at(version).first; id <= last_credits_id; ++id) {
|
||||
// check if the line exists first
|
||||
if (text_by_id_credits.count(id) == 0) {
|
||||
result += fmt::format("\"\"\n ");
|
||||
continue;
|
||||
}
|
||||
// check if all lines are identical first
|
||||
bool diff_langs = false;
|
||||
bool is_first = true;
|
||||
std::string last_lang;
|
||||
for (auto& y : text_by_id_credits.at(id)) {
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
} else if (last_lang != y) {
|
||||
diff_langs = true;
|
||||
break;
|
||||
}
|
||||
last_lang = y;
|
||||
}
|
||||
// now write them
|
||||
if (!diff_langs) {
|
||||
result += fmt::format("\"{}\"\n ", last_lang);
|
||||
} else {
|
||||
result += fmt::format("(");
|
||||
for (auto& y : text_by_id_credits.at(id)) {
|
||||
result += fmt::format("\"{}\"\n ", y);
|
||||
}
|
||||
result += ")\n ";
|
||||
}
|
||||
}
|
||||
result += ")\n\n";
|
||||
}
|
||||
for (auto& x : text_by_id_post_credits) {
|
||||
result += fmt::format("(#x{:04x}\n ", x.first);
|
||||
for (auto& y : x.second) {
|
||||
result += fmt::format("\"{}\"\n ", y);
|
||||
}
|
||||
result += ")\n\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ struct GameTextResult {
|
||||
|
||||
GameTextResult process_game_text(ObjectFileData& data, GameTextVersion version);
|
||||
std::string write_game_text(
|
||||
const Config& cfg,
|
||||
GameTextVersion version,
|
||||
const std::unordered_map<int, std::unordered_map<int, std::string>>& data);
|
||||
} // namespace decompiler
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
(text
|
||||
;; NOTE : we compile using the fixed v2 encoding because it's what we use.
|
||||
(jak1-v2 "assets/game_text.txt") ;; this is the decompiler-generated file!
|
||||
;; "patch" files so we can fix some errors and maintain consistency
|
||||
(jak1-v2 "game/assets/jak1/text/text_patch_fr.gs")
|
||||
(jak1-v2 "game/assets/jak1/text/text_patch_de.gs")
|
||||
(jak1-v2 "game/assets/jak1/text/text_patch_ja.gs")
|
||||
;; add custom files down here
|
||||
(jak1-v2 "game/assets/jak1/text/game_text_en.gs")
|
||||
(jak1-v2 "game/assets/jak1/text/game_text_ja.gs")
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
(group-name "common")
|
||||
(language-id 5)
|
||||
|
||||
;; -----------------
|
||||
;; fixes
|
||||
|
||||
; 闇の眷者 ゴルとマイアの城
|
||||
(#x122 "闇の賢者 ゴルとマイアの城")
|
||||
(#x801 "闇の賢者 ゴルとマイアの城")
|
||||
|
||||
;; -----------------
|
||||
;; progress menu (insanity)
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
(group-name "common")
|
||||
(language-id 2)
|
||||
|
||||
;; -----------------
|
||||
;; official fixes
|
||||
|
||||
; SUCHE DIE VERSTECKTE ENERGIEZELLE!
|
||||
(#x012f "FINDE DIE VERSTECKTE ENERGIEZELLE!")
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
(group-name "common")
|
||||
(language-id 1)
|
||||
|
||||
;; -----------------
|
||||
;; official fixes
|
||||
|
||||
; ATTRAPÉ(S):
|
||||
(#x02b4 "ATTRAPÉ(S)_:")
|
||||
; RATÉ(S):
|
||||
(#x02b5 "RATÉ(S)_:")
|
||||
; PERDU!
|
||||
(#x02b6 "PERDU_!")
|
||||
; RAMÈNE 90 ORBES À LA GÉOLOGISTE
|
||||
(#x0301 "RAMÈNE 90 ORBES À LA GÉOLOGUE")
|
||||
; RETOURNE VOIR LA GÉOLOGISTE
|
||||
(#x0311 "RETOURNE VOIR LA GÉOLOGUE")
|
||||
@@ -0,0 +1,26 @@
|
||||
(group-name "common")
|
||||
(language-id 5)
|
||||
|
||||
;; -----------------
|
||||
;; official fixes
|
||||
|
||||
; <PAD_TRIANGLE>ボタンで やめる
|
||||
(#x011d "<PAD_TRIANGLE>で やめる")
|
||||
; ジャックXダクスターの ゲームデータを セーブするには ~DKBの 空きようりょうが ひつようです
|
||||
(#x0133 "ジャックXダクスターの ゲームデータを セーブするには ~DKBいじょうの 空きようりょうが ひつようです")
|
||||
; メモリーカードスロット~Dに ジャックXダクスターの データのはいった メモリーカードを さしてください
|
||||
(#x0143 "メモリーカードスロット~Dに ジャックXダクスターの データのはいった メモリーカード(PS2)を さしてください")
|
||||
|
||||
(#x0400 "90コのオーブを 宝石掘りにわたせ")
|
||||
(#x0402 "ダークエコ発掘3部隊を すべてたおせ")
|
||||
(#x0403 "ダークエコ発掘2部隊を すべてたおせ")
|
||||
(#x0404 "ダークエコ発掘隊の さいごの1隊をたおせ")
|
||||
(#x0409 "宝石掘りたちの おたからをまもれ")
|
||||
(#x041f "ダークエコ発掘隊を たおせ")
|
||||
|
||||
;; -----------------
|
||||
;; fixes
|
||||
|
||||
; 闇の眷者 ゴルとマイアの城
|
||||
(#x122 "闇の賢者 ゴルとマイアの城")
|
||||
(#x801 "闇の賢者 ゴルとマイアの城")
|
||||
Reference in New Issue
Block a user