From 8bb5fa7f042b5068639718f49b1d66c9bf472bfc Mon Sep 17 00:00:00 2001 From: ManDude <7569514+ManDude@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:51:09 +0100 Subject: [PATCH] various subtitle editor fixes (#2941) - fix speaker names and time frames being uneditable in Jak 1 - added toggle to auto-selected a newly created scene as current - changed the subtitle summary format slightly. - current scene's name now appears in the UI --- .../tools/subtitle_editor/subtitle_editor.cpp | 67 +++++++++++-------- game/tools/subtitle_editor/subtitle_editor.h | 1 + goalc/data_compiler/game_text_common.cpp | 25 ++++++- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/game/tools/subtitle_editor/subtitle_editor.cpp b/game/tools/subtitle_editor/subtitle_editor.cpp index 990955f412..7db0b82278 100644 --- a/game/tools/subtitle_editor/subtitle_editor.cpp +++ b/game/tools/subtitle_editor/subtitle_editor.cpp @@ -99,7 +99,11 @@ void SubtitleEditor::draw_window() { } else { ImGui::PushStyleColor(ImGuiCol_Text, m_selected_text_color); } - if (ImGui::TreeNode("Currently Selected Cutscene")) { + if (ImGui::TreeNode( + "current_scene", "%s", + m_current_scene + ? fmt::format("Currently Selected Scene - {}", m_current_scene->m_name).c_str() + : "Currently Selected Scene")) { ImGui::PopStyleColor(); if (m_current_scene) { draw_subtitle_options(*m_current_scene, true); @@ -207,6 +211,8 @@ void SubtitleEditor::draw_speaker_options() { const auto& bank = m_subtitle_db.m_banks[m_current_language]; for (auto& [speaker_id, speaker_localized] : bank->m_speakers) { if (speaker_id == "none") { + // this is a special speaker that has ID zero and does not appear in-game + // it means there was no speaker for the line. (e.g. text-only, not vocal) continue; } // Insertion or deletion not needed here as it has to be wired up in .gc and C++ code @@ -238,9 +244,15 @@ void SubtitleEditor::draw_scene_section_header(const bool non_cutscenes) { new_scene.m_hint_id = 0; } m_subtitle_db.m_banks.at(m_current_language)->m_scenes.emplace(m_new_scene_name, new_scene); + if (m_new_scene_as_current) { + m_current_scene = + &m_subtitle_db.m_banks.at(m_current_language)->m_scenes.at(m_new_scene_name); + } + m_new_scene_name = ""; } - ImGui::NewLine(); + ImGui::SameLine(); + ImGui::Checkbox("Set Scene as Current", &m_new_scene_as_current); } ImGui::TreePop(); } @@ -343,7 +355,7 @@ std::string SubtitleEditor::subtitle_line_summary( std::string line_text = ""; if (!line.text.empty()) { if (m_truncate_summaries && line.text.size() > 30) { - line_text = line.text.substr(0, 30) + "..."; + line_text = line.text.substr(0, 27) + "..."; } else { line_text = line.text; } @@ -351,7 +363,7 @@ std::string SubtitleEditor::subtitle_line_summary( if (is_v1_format()) { line_text = "Clear Screen"; } else if (line_meta.merge) { - line_text = ""; + line_text = ""; } } // Append important info about the frame / speaker to the front @@ -359,21 +371,23 @@ std::string SubtitleEditor::subtitle_line_summary( // V1 if (is_v1_format()) { if (line.text.empty()) { - return fmt::format("{}] {}", info_header, line_text); + return fmt::format("[{}] {}", line_meta.frame_start, line_text); + } else { + return fmt::format( + "[{}] {}: {}", line_meta.frame_start, + m_subtitle_db.m_banks[m_current_language]->m_speakers.at(line_meta.speaker), line_text); } - return fmt::format("{}] {} - '{}'", info_header, - m_subtitle_db.m_banks[m_current_language]->m_speakers.at(line_meta.speaker), - line_text); } // V2 - if (line_meta.merge) { - return fmt::format("{}-{}] {} - {}", info_header, line_meta.frame_end, - m_subtitle_db.m_banks[m_current_language]->m_speakers.at(line_meta.speaker), + else { + auto speaker_text = + line_meta.speaker == "none" + ? "" + : fmt::format("{}: ", m_subtitle_db.m_banks[m_current_language]->m_speakers.at( + line_meta.speaker)); + return fmt::format("[{}-{}] {}{}", line_meta.frame_start, line_meta.frame_end, speaker_text, line_text); } - return fmt::format("{}-{}] {} - '{}'", info_header, line_meta.frame_end, - m_subtitle_db.m_banks[m_current_language]->m_speakers.at(line_meta.speaker), - line_text); } void SubtitleEditor::draw_subtitle_options(GameSubtitleSceneInfo& scene, bool current_scene) { @@ -435,16 +449,18 @@ void SubtitleEditor::draw_subtitle_options(GameSubtitleSceneInfo& scene, bool cu } bool hide_line_options = false; if (is_v1_format()) { - if (line_text.empty()) { - hide_line_options = true; - } else { - ImGui::InputInt("Starting Frame", &subtitle_line->metadata.frame_start, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); - } + ImGui::InputInt("Starting Frame", &frames[0], + ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); } else { ImGui::InputInt2("Start and End Frame", frames, ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); } + + // no text + text isn't going to be filled automatically by the game + if (line_text.empty() && !line_meta.merge) { + hide_line_options = true; + } + if (!hide_line_options) { if (ImGui::BeginCombo( "Speaker", @@ -461,15 +477,14 @@ void SubtitleEditor::draw_subtitle_options(GameSubtitleSceneInfo& scene, bool cu } ImGui::EndCombo(); } - ImGui::InputText("Text", &subtitle_line->text, - line_meta.merge ? ImGuiInputTextFlags_ReadOnly : 0); + ImGui::InputText("Text", &line_text, line_meta.merge ? ImGuiInputTextFlags_ReadOnly : 0); ImGui::Checkbox("Offscreen?", &subtitle_line->metadata.offscreen); if (!is_v1_format()) { ImGui::SameLine(); if (ImGui::Checkbox("Merge Text?", &subtitle_line->metadata.merge)) { // Clear text if they've checked it if (subtitle_line->metadata.merge) { - subtitle_line->text = ""; + line_text = ""; } } } @@ -533,7 +548,7 @@ void SubtitleEditor::draw_new_scene_line_form() { } } } - bool rendered_text_entry_btn = false; + // Validation: // - start frame > 0 // - end frame > start_frame @@ -546,7 +561,6 @@ void SubtitleEditor::draw_new_scene_line_form() { ImGui::Text("Can't add a new text entry with the current fields!"); ImGui::PopStyleColor(); } else { - rendered_text_entry_btn = true; if (ImGui::Button("Add Text Entry")) { m_current_scene->add_line(m_current_scene_text, m_current_scene_frames[0], m_current_scene_frames[1], m_current_scene_offscreen, @@ -559,9 +573,6 @@ void SubtitleEditor::draw_new_scene_line_form() { ImGui::Text("Can't add a clear screen entry with the current fields!"); ImGui::PopStyleColor(); } else { - if (rendered_text_entry_btn) { - ImGui::SameLine(); - } if (ImGui::Button("Add Clear Screen Entry")) { m_current_scene->add_line("", m_current_scene_frames[0], 0, m_current_scene_offscreen, "", false); diff --git a/game/tools/subtitle_editor/subtitle_editor.h b/game/tools/subtitle_editor/subtitle_editor.h index 566f7eb815..c5be261707 100644 --- a/game/tools/subtitle_editor/subtitle_editor.h +++ b/game/tools/subtitle_editor/subtitle_editor.h @@ -67,6 +67,7 @@ class SubtitleEditor { bool m_current_scene_offscreen = false; bool m_current_scene_merge = false; + bool m_new_scene_as_current = true; std::string m_new_scene_name = ""; std::string m_new_scene_id = "0"; diff --git a/goalc/data_compiler/game_text_common.cpp b/goalc/data_compiler/game_text_common.cpp index 3ff253ca22..7bcef3371d 100644 --- a/goalc/data_compiler/game_text_common.cpp +++ b/goalc/data_compiler/game_text_common.cpp @@ -99,7 +99,17 @@ void compile_text(GameTextDB& db, const std::string& output_prefix) { */ void compile_subtitles_v1(GameSubtitleDB& db, const std::string& output_prefix) { for (const auto& [lang, bank] : db.m_banks) { + // get font encoding information auto font = get_font_bank(bank->m_text_version); + + // convert speakers once. + auto speakers_converted = bank->m_speakers; + for (auto& [id, name] : speakers_converted) { + // convert name in-place. we copied the map earlier so this is safe. + // the subtitle lines have the speaker "id" stored in them, which is the map key here. + name = font->convert_utf8_to_game(name); + } + DataObjectGenerator gen; gen.add_type_tag("subtitle-text-info"); // type gen.add_word(bank->m_scenes.size()); // length @@ -128,9 +138,18 @@ void compile_subtitles_v1(GameSubtitleDB& db, const std::string& output_prefix) for (auto& subtitle : scene.m_lines) { gen.add_word(subtitle.metadata.frame_start); // frame gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(subtitle.text)); // line - gen.add_ref_to_string_in_pool( - font->convert_utf8_to_game(subtitle.metadata.speaker)); // speaker - gen.add_word(subtitle.metadata.offscreen); // offscreen + // speaker + if (subtitle.metadata.speaker.empty()) { + gen.add_ref_to_string_in_pool(""); + } else { + auto it = speakers_converted.find(subtitle.metadata.speaker); + if (it == speakers_converted.end()) { + throw std::runtime_error(fmt::format("in file `{}`: could not find speaker {}", + bank->m_file_path, subtitle.metadata.speaker)); + } + gen.add_ref_to_string_in_pool(it->second); + } + gen.add_word(subtitle.metadata.offscreen); // offscreen } }