From 523a3e8df7c1696f813e74fdc856511e5d6219aa Mon Sep 17 00:00:00 2001 From: PTwr Date: Mon, 18 Sep 2023 11:45:50 +0200 Subject: [PATCH 01/28] basing subtitle display --- Source/Core/Core/Core.cpp | 2 + Source/Core/Core/HW/DVD/FileMonitor.cpp | 14 +- Source/Core/DolphinLib.vcxproj | 24 +- Source/Core/VideoCommon/OnScreenUI.cpp | 2 + .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 221 ++++++++++++++++++ .../VideoCommon/OsdSubtitles/OsdSubtitles.h | 10 + 6 files changed, 258 insertions(+), 15 deletions(-) create mode 100644 Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp create mode 100644 Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 5396c8439b27..68f776914857 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -97,6 +97,7 @@ #ifdef ANDROID #include "jni/AndroidCommon/IDCache.h" #endif +#include namespace Core { @@ -581,6 +582,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // Clear on screen messages that haven't expired OSD::ClearMessages(); + OSDSubtitles::ClearMessages(); // The config must be restored only after the whole HW has shut down, // not when it is still running. diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index f4bc53a09717..030c2ce1a1b5 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -17,6 +17,7 @@ #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" +#include namespace FileMonitor { @@ -52,9 +53,12 @@ FileLogger::~FileLogger() = default; void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) { + // Do nothing if the log isn't selected - if (!Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::FILEMON, - Common::Log::LogLevel::LWARNING)) + auto logEnabled = Common::Log::LogManager::GetInstance()->IsEnabled( + Common::Log::LogType::FILEMON, Common::Log::LogLevel::LWARNING); + auto subtitlesEnabled = true; //TODO settings for subtitles + if (!(logEnabled || subtitlesEnabled)) { return; } @@ -81,7 +85,13 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {}", size_string, path); if (IsSoundFile(path)) + { INFO_LOG_FMT(FILEMON, "{}", log_string); + if (subtitlesEnabled) + { + OSDSubtitles::AddSubtitle(path); + } + } else WARN_LOG_FMT(FILEMON, "{}", log_string); diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 827652cae736..23766a1e8204 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -69,20 +69,22 @@ - + + + + + + + - - - + - + ]]> @@ -90,10 +92,6 @@ - + \ No newline at end of file diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 1919149629f6..053961b65e1a 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -29,6 +29,7 @@ #include #include +#include "OsdSubtitles\OsdSubtitles.h" namespace VideoCommon { @@ -333,6 +334,7 @@ void OnScreenUI::Finalize() g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); DrawDebugText(); OSD::DrawMessages(); + OSDSubtitles::DrawMessages(); ImGui::Render(); } diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp new file mode 100644 index 000000000000..2e61047c35e0 --- /dev/null +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -0,0 +1,221 @@ +#include "OsdSubtitles.h" + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/Timer.h" + +#include +#include +#include +#include +#include + +namespace OSDSubtitles +{ +constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages. +constexpr float TOP_MARGIN = 10.0f; // Pixels above the first OSD message. +constexpr float WINDOW_PADDING = 4.0f; // Pixels between subsequent OSD messages. +constexpr float MESSAGE_FADE_TIME = 1000.f; // Ms to fade OSD messages at the end of their life. +constexpr float MESSAGE_DROP_TIME = 5000.f; // Ms to drop OSD messages that has yet to ever render. + +static ImVec4 ARGBToImVec4(const u32 argb) +{ + return ImVec4(static_cast((argb >> 16) & 0xFF) / 255.0f, + static_cast((argb >> 8) & 0xFF) / 255.0f, + static_cast((argb >> 0) & 0xFF) / 255.0f, + static_cast((argb >> 24) & 0xFF) / 255.0f); +} + +const std::string TranslationFile = "C:\\games\\wii\\translate.json"; + +// TODO check if just using OnScreenDisplay::Message will be enough +struct Subtitle +{ + Subtitle() = default; + Subtitle(std::string text_, u32 duration_, u32 color_) + : text(std::move(text_)), duration(duration_), color(color_) + { + timer.Start(); + } + s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } + std::string text; + Common::Timer timer; + u32 duration = 0; + bool ever_drawn = false; + u32 color = 0; + + // TODO Position from json? +}; + +// TODO check if thers some ready io helper to use for configs? +auto read_file(std::string_view path, bool throwOnMissingFile = false) -> std::string +{ + constexpr auto read_size = std::size_t(4096); + auto stream = std::ifstream(path.data()); + stream.exceptions(std::ios_base::badbit); + + if (not stream) + { + if (throwOnMissingFile) + { + throw std::ios_base::failure("Subtitle file does not exist"); + } + else + { + return ""; + } + } + + INFO_LOG_FMT(FILEMON, "{}", "reading file"); + + auto out = std::string(); + auto buf = std::string(read_size, '\0'); + while (stream.read(&buf[0], read_size)) + { + out.append(buf, 0, stream.gcount()); + } + out.append(buf, 0, stream.gcount()); + return out; +} + +picojson::value Translations; +bool isInitialized = false; +static std::mutex init_mutex; +static std::mutex subtitles_mutex; +void InitTranslations(const std::string& filename) +{ + std::lock_guard lock{init_mutex}; + + if (isInitialized) + { + //another call already did the init + return; + } + + auto json = read_file(filename); + + std::string err = picojson::parse(Translations, json); + if (!err.empty()) + { + std::cerr << err << std::endl; + } + + isInitialized = true; +} + +static std::multimap currentSubtitles; +void AddSubtitle(std::string soundFile) +{ + if (!isInitialized) + { + InitTranslations(TranslationFile); + } + + auto tlnode = Translations.get(soundFile); + if (tlnode.is()) + { + auto tl = tlnode.get("Translation"); + if (tl.is()) + { + //check if subtitle is still on screen + if (currentSubtitles.contains(soundFile)) + { + if (tlnode.get("AllowDuplicate").evaluate_as_boolean() == false) + { + return; + } + } + + auto msnode = tlnode.get("Miliseconds"); + //TODO allow for text/hex color (web codes?) + auto colornode = tlnode.get("Color"); + + u32 ms = msnode.is() ? msnode.get() : 3000; + u32 argb = colornode.is() ? colornode.get() : 4294967040; + + currentSubtitles.emplace(soundFile, Subtitle(std::move(tl.to_str()), ms, argb)); + } + } +} + +static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int time_left) +{ + // We have to provide a window name, and these shouldn't be duplicated. + // So instead, we generate a name based on the number of messages drawn. + const std::string window_name = fmt::format("osd_subtitle_{}", index); + + // The size must be reset, otherwise the length of old messages could influence new ones. + ImGui::SetNextWindowPos(position); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); + + // Gradually fade old messages away (except in their first frame) + const float fade_time = std::max(std::min(MESSAGE_FADE_TIME, (float)msg.duration), 1.f); + const float alpha = std::clamp(time_left / fade_time, 0.f, 1.f); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, msg.ever_drawn ? alpha : 1.0); + + float window_height = 0.0f; + if (ImGui::Begin(window_name.c_str(), nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) + { + // Use %s in case message contains %. + ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); + window_height = + ImGui::GetWindowSize().y + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.y); + } + + ImGui::End(); + ImGui::PopStyleVar(); + + msg.ever_drawn = true; + + return window_height; +} + +void DrawMessages() +{ + const bool draw_messages = true; //Config::Get(Config::MAIN_OSD_MESSAGES); + const float current_x = 0; + //LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + s_obscured_pixels_left; + float current_y = 0; // TOP_MARGIN* ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top; + int index = 0; + + std::lock_guard lock{subtitles_mutex}; + + for (auto it = currentSubtitles.begin(); it != currentSubtitles.end();) + { + Subtitle& msg = it->second; //?!!?!? second?! + const s64 time_left = msg.TimeRemaining(); + + // Make sure we draw them at least once if they were printed with 0ms, + // unless enough time has expired, in that case, we drop them + if (time_left <= 0 && (msg.ever_drawn || -time_left >= MESSAGE_DROP_TIME)) + { + it = currentSubtitles.erase(it); + continue; + } + else + { + ++it; + } + + if (draw_messages) + current_y += DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); + } +} + +void ClearMessages() +{ + std::lock_guard lock{subtitles_mutex}; + currentSubtitles.clear(); +} + +} // namespace OSD diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h new file mode 100644 index 000000000000..8806d7e50d27 --- /dev/null +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h @@ -0,0 +1,10 @@ +#pragma once +#include +namespace OSDSubtitles +{ +void AddSubtitle(std::string soundFile); + +void DrawMessages(); + +void ClearMessages(); +} // namespace OSD From b5bc249d7a1fc2d230035313226f246c102812dd Mon Sep 17 00:00:00 2001 From: PTwr Date: Mon, 18 Sep 2023 13:52:43 +0200 Subject: [PATCH 02/28] fixed json parsing --- .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 68 ++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp index 2e61047c35e0..7ed472e0ee67 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -11,11 +11,12 @@ #include "Common/Timer.h" #include +#include #include #include #include -#include +// TODO unify with OSD, will require different positioning for specific messagetype? namespace OSDSubtitles { constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages. @@ -72,8 +73,6 @@ auto read_file(std::string_view path, bool throwOnMissingFile = false) -> std::s } } - INFO_LOG_FMT(FILEMON, "{}", "reading file"); - auto out = std::string(); auto buf = std::string(read_size, '\0'); while (stream.read(&buf[0], read_size)) @@ -88,13 +87,15 @@ picojson::value Translations; bool isInitialized = false; static std::mutex init_mutex; static std::mutex subtitles_mutex; + +// TODO init on rom load void InitTranslations(const std::string& filename) { std::lock_guard lock{init_mutex}; if (isInitialized) { - //another call already did the init + // another call already did the init return; } @@ -118,28 +119,38 @@ void AddSubtitle(std::string soundFile) } auto tlnode = Translations.get(soundFile); - if (tlnode.is()) + if (tlnode.is() && tlnode.contains("Translation")) { auto tl = tlnode.get("Translation"); if (tl.is()) { - //check if subtitle is still on screen - if (currentSubtitles.contains(soundFile)) + std::lock_guard lock{subtitles_mutex}; { - if (tlnode.get("AllowDuplicate").evaluate_as_boolean() == false) + // check if subtitle is still on screen + if (currentSubtitles.count(soundFile) > 0) { - return; + if (!tlnode.contains("AllowDuplicate")) + { + return; + } + + auto dupsNode = tlnode.get("AllowDuplicate"); + if (dupsNode.is() || + (dupsNode.is() && !tlnode.get("AllowDuplicate").get())) + { + return; + } } - } - auto msnode = tlnode.get("Miliseconds"); - //TODO allow for text/hex color (web codes?) - auto colornode = tlnode.get("Color"); + auto msnode = tlnode.get("Miliseconds"); + // TODO allow for text/hex color (web codes?) + auto colornode = tlnode.get("Color"); - u32 ms = msnode.is() ? msnode.get() : 3000; - u32 argb = colornode.is() ? colornode.get() : 4294967040; + u32 ms = msnode.is() ? msnode.get() : 3000; + u32 argb = colornode.is() ? colornode.get() : 4294967040; - currentSubtitles.emplace(soundFile, Subtitle(std::move(tl.to_str()), ms, argb)); + currentSubtitles.emplace(soundFile, Subtitle(std::move(tl.to_str()), ms, argb)); + } } } } @@ -151,7 +162,7 @@ static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int t const std::string window_name = fmt::format("osd_subtitle_{}", index); // The size must be reset, otherwise the length of old messages could influence new ones. - ImGui::SetNextWindowPos(position); + //ImGui::SetNextWindowPos(position); ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); // Gradually fade old messages away (except in their first frame) @@ -160,6 +171,7 @@ static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int t ImGui::PushStyleVar(ImGuiStyleVar_Alpha, msg.ever_drawn ? alpha : 1.0); float window_height = 0.0f; + float window_width = 0.0f; if (ImGui::Begin(window_name.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | @@ -170,6 +182,13 @@ static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int t ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); window_height = ImGui::GetWindowSize().y + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.y); + window_width = + ImGui::GetWindowSize().x + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.x); + + float x_center = ImGui::GetIO().DisplaySize.x / 2.0; + + auto subtitlePos = ImVec2(x_center - window_width / 2, position.y - window_height); + ImGui::SetWindowPos(window_name.c_str(), subtitlePos); } ImGui::End(); @@ -182,17 +201,20 @@ static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int t void DrawMessages() { - const bool draw_messages = true; //Config::Get(Config::MAIN_OSD_MESSAGES); + const bool draw_messages = true; // Config::Get(Config::MAIN_OSD_MESSAGES); const float current_x = 0; - //LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + s_obscured_pixels_left; - float current_y = 0; // TOP_MARGIN* ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top; + // LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + s_obscured_pixels_left; + float current_y = + 0; // TOP_MARGIN* ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top; int index = 0; + current_y = ImGui::GetIO().DisplaySize.y; + std::lock_guard lock{subtitles_mutex}; for (auto it = currentSubtitles.begin(); it != currentSubtitles.end();) { - Subtitle& msg = it->second; //?!!?!? second?! + Subtitle& msg = it->second; const s64 time_left = msg.TimeRemaining(); // Make sure we draw them at least once if they were printed with 0ms, @@ -208,7 +230,7 @@ void DrawMessages() } if (draw_messages) - current_y += DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); + current_y -= DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); } } @@ -218,4 +240,4 @@ void ClearMessages() currentSubtitles.clear(); } -} // namespace OSD +} // namespace OSDSubtitles From 846fb8749f900a7f81d7abdad804277b5480b685 Mon Sep 17 00:00:00 2001 From: PTwr Date: Mon, 18 Sep 2023 13:59:39 +0200 Subject: [PATCH 03/28] newest subtitles always on bottom --- Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp index 7ed472e0ee67..0fd3b6bca094 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -212,7 +212,8 @@ void DrawMessages() std::lock_guard lock{subtitles_mutex}; - for (auto it = currentSubtitles.begin(); it != currentSubtitles.end();) + for (auto it = currentSubtitles.rbegin(); + it != currentSubtitles.rend();) { Subtitle& msg = it->second; const s64 time_left = msg.TimeRemaining(); @@ -221,7 +222,7 @@ void DrawMessages() // unless enough time has expired, in that case, we drop them if (time_left <= 0 && (msg.ever_drawn || -time_left >= MESSAGE_DROP_TIME)) { - it = currentSubtitles.erase(it); + currentSubtitles.erase(std::next(it).base()); continue; } else From 1eab7a4f95133e18e5630c1f11674bb59d2ff025 Mon Sep 17 00:00:00 2001 From: PTwr Date: Tue, 19 Sep 2023 14:12:57 +0200 Subject: [PATCH 04/28] multizone osd --- Source/Core/VideoCommon/OnScreenDisplay.cpp | 161 ++++++++++++++---- Source/Core/VideoCommon/OnScreenDisplay.h | 59 ++++++- .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 29 +++- .../VideoCommon/OsdSubtitles/OsdSubtitles.h | 4 +- 4 files changed, 212 insertions(+), 41 deletions(-) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index e860c53ec0e8..64e681f973c7 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -29,22 +29,11 @@ constexpr float MESSAGE_DROP_TIME = 5000.f; // Ms to drop OSD messages that has static std::atomic s_obscured_pixels_left = 0; static std::atomic s_obscured_pixels_top = 0; -struct Message -{ - Message() = default; - Message(std::string text_, u32 duration_, u32 color_) - : text(std::move(text_)), duration(duration_), color(color_) - { - timer.Start(); - } - s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } - std::string text; - Common::Timer timer; - u32 duration = 0; - bool ever_drawn = false; - u32 color = 0; -}; -static std::multimap s_messages; +// default message stack +// static std::multimap s_messages; +static MessageStack defaultMessageStack = MessageStack(); +static std::map messageStacks; + static std::mutex s_messages_mutex; static ImVec4 ARGBToImVec4(const u32 argb) @@ -55,14 +44,15 @@ static ImVec4 ARGBToImVec4(const u32 argb) static_cast((argb >> 24) & 0xFF) / 255.0f); } -static float DrawMessage(int index, Message& msg, const ImVec2& position, int time_left) +static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int time_left, + MessageStack& messageStack) { // We have to provide a window name, and these shouldn't be duplicated. // So instead, we generate a name based on the number of messages drawn. - const std::string window_name = fmt::format("osd_{}", index); + const std::string window_name = fmt::format("osd_{}_{}", messageStack.name, index); // The size must be reset, otherwise the length of old messages could influence new ones. - ImGui::SetNextWindowPos(position); + //ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); // Gradually fade old messages away (except in their first frame) @@ -71,6 +61,7 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti ImGui::PushStyleVar(ImGuiStyleVar_Alpha, msg.ever_drawn ? alpha : 1.0); float window_height = 0.0f; + float window_width = 0.0f; if (ImGui::Begin(window_name.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | @@ -79,8 +70,40 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti { // Use %s in case message contains %. ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); + window_width = + ImGui::GetWindowSize().x + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.x); window_height = ImGui::GetWindowSize().y + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.y); + + float x_pos = position.x; + float y_pos = position.y; + + if (messageStack.centered) + { + if (messageStack.isVertical()) + { + float x_center = ImGui::GetIO().DisplaySize.x / 2.0; + x_pos = x_center - window_width / 2; + } + else + { + float y_center = ImGui::GetIO().DisplaySize.y / 2.0; + y_pos = y_center - window_height / 2; + } + } + + if (messageStack.dir == MessageStackDirection::Leftward) + { + x_pos -= window_width; + } + if (messageStack.dir == MessageStackDirection::Upward) + { + y_pos -= window_height; + } + + auto windowPos = ImVec2(x_pos, y_pos); + INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", windowPos.x, windowPos.y); + ImGui::SetWindowPos(window_name.c_str(), windowPos); } ImGui::End(); @@ -88,34 +111,76 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti msg.ever_drawn = true; - return window_height; + return ImVec2(window_width, window_height); } -void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb) +void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, + std::string messageStack) { std::lock_guard lock{s_messages_mutex}; - s_messages.erase(type); - s_messages.emplace(type, Message(std::move(message), ms, argb)); + if (messageStacks.find(messageStack) == messageStacks.end()) + { + defaultMessageStack.s_messages.erase(type); + defaultMessageStack.s_messages.emplace(type, Message(std::move(message), ms, argb)); + } + else + { + messageStacks["messageStack"].s_messages.erase(type); + messageStacks["messageStack"].s_messages.emplace(type, Message(std::move(message), ms, argb)); + } } -void AddMessage(std::string message, u32 ms, u32 argb) +void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack) { std::lock_guard lock{s_messages_mutex}; - s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb)); + if (messageStacks.contains(messageStack)) + { + INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", message, messageStack); + messageStacks[messageStack].s_messages.emplace(MessageType::Typeless, + Message(std::move(message), ms, argb)); + } + else + { + INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", message, "defaultMessageStack"); + defaultMessageStack.s_messages.emplace(MessageType::Typeless, + Message(std::move(message), ms, argb)); + } } -void DrawMessages() +void AddMessageStack(MessageStack info) +{ + // TODO handle dups + messageStacks[info.name] = info; +} +void DrawMessages(MessageStack& messageStack) { const bool draw_messages = Config::Get(Config::MAIN_OSD_MESSAGES); - const float current_x = - LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + s_obscured_pixels_left; - float current_y = TOP_MARGIN * ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top; + float current_x = LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + + s_obscured_pixels_left + messageStack.initialPosOffset.x; + float current_y = TOP_MARGIN * ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top + + messageStack.initialPosOffset.y; int index = 0; - std::lock_guard lock{s_messages_mutex}; + if (messageStack.dir == MessageStackDirection::Leftward) + { + current_x = ImGui::GetIO().DisplaySize.x - current_x; + } + if (messageStack.dir == MessageStackDirection::Upward) + { + current_y = ImGui::GetIO().DisplaySize.y - current_y; + } - for (auto it = s_messages.begin(); it != s_messages.end();) + std::lock_guard lock{s_messages_mutex}; + for (auto it = (messageStack.reversed ? messageStack.s_messages.end() : + messageStack.s_messages.begin()); + it != + (messageStack.reversed ? messageStack.s_messages.begin() : messageStack.s_messages.end());) { + if (messageStack.reversed) + { + --it; + } + Message& msg = it->second; const s64 time_left = msg.TimeRemaining(); @@ -123,23 +188,49 @@ void DrawMessages() // unless enough time has expired, in that case, we drop them if (time_left <= 0 && (msg.ever_drawn || -time_left >= MESSAGE_DROP_TIME)) { - it = s_messages.erase(it); + it = messageStack.s_messages.erase(it); continue; } - else + else if (!messageStack.reversed) { ++it; } if (draw_messages) - current_y += DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); + { + auto messageSize = + DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left, messageStack); + + if (messageStack.isVertical()) + { + current_y += + messageStack.dir == OSD::MessageStackDirection::Upward ? -messageSize.y : messageSize.y; + } + else + { + current_x += messageStack.dir == OSD::MessageStackDirection::Leftward ? -messageSize.x : + messageSize.x; + } + } + } +} +void DrawMessages() +{ + DrawMessages(defaultMessageStack); + for (auto& [name, stack] : messageStacks) + { + DrawMessages(stack); } } void ClearMessages() { std::lock_guard lock{s_messages_mutex}; - s_messages.clear(); + defaultMessageStack.s_messages.clear(); + for (auto& [name, stack] : messageStacks) + { + stack.s_messages.clear(); + } } void SetObscuredPixelsLeft(int width) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 187c7fb31725..2d2d0709e9f8 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -7,9 +7,19 @@ #include #include "Common/CommonTypes.h" +#include "Common/Timer.h" +#include + namespace OSD { +enum class MessageStackDirection +{ + Downward = 1, + Upward = 2, + Rightward = 4, + Leftward = 8, +}; enum class MessageType { NetPlayPing, @@ -19,6 +29,48 @@ enum class MessageType // displayed before other messages Typeless, }; +//TODO clean this mess +struct Message +{ + Message() = default; + Message(std::string text_, u32 duration_, u32 color_) + : text(std::move(text_)), duration(duration_), color(color_) + { + timer.Start(); + } + s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } + std::string text; + Common::Timer timer; + u32 duration = 0; + bool ever_drawn = false; + u32 color = 0; +}; +struct MessageStack +{ + ImVec2 initialPosOffset; + MessageStackDirection dir; + bool centered; + bool reversed; + std::string name; + + MessageStack() : MessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} + MessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, + bool _reversed, std::string _name) + { + initialPosOffset = ImVec2(_x_offset, _y_offset); + dir = _dir; + centered = _centered; + reversed = _reversed; + name = _name; + } + + std::multimap s_messages; + + bool isVertical() + { + return dir == MessageStackDirection::Downward || dir == MessageStackDirection::Upward; + } +}; namespace Color { @@ -35,10 +87,13 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration +void AddMessageStack(MessageStack info); + // On-screen message display (colored yellow by default) -void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW); +void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, + std::string messageStack = ""); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, - u32 argb = Color::YELLOW); + u32 argb = Color::YELLOW, std::string messageStack = ""); // Draw the current messages on the screen. Only call once per frame. void DrawMessages(); diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp index 0fd3b6bca094..e2259db355b9 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -15,6 +15,7 @@ #include #include #include +#include // TODO unify with OSD, will require different positioning for specific messagetype? namespace OSDSubtitles @@ -112,6 +113,29 @@ void InitTranslations(const std::string& filename) static std::multimap currentSubtitles; void AddSubtitle(std::string soundFile) +{ + if (!isInitialized) + { + auto stack = OSD::MessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "subtitles"); + OSD::AddMessageStack(stack); + + auto stack2 = OSD::MessageStack(0, 100, OSD::MessageStackDirection::Leftward, false, true, + "leftward"); + OSD::AddMessageStack(stack2); + + auto stack3 = OSD::MessageStack(0, 200, OSD::MessageStackDirection::Rightward, false, false, + "rightward"); + OSD::AddMessageStack(stack3); + + isInitialized = true; + } + OSD::AddMessage("default log: " + soundFile, 10000, OSD::Color::GREEN, ""); + OSD::AddMessage("subtitle zone: " + soundFile, 10000, OSD::Color::CYAN, "subtitles"); + OSD::AddMessage("leftward", 10000, OSD::Color::YELLOW, "leftward"); + OSD::AddMessage("rightward", 10000, OSD::Color::RED, "rightward"); +} + +void _AddSubtitle(std::string soundFile) { if (!isInitialized) { @@ -162,7 +186,7 @@ static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int t const std::string window_name = fmt::format("osd_subtitle_{}", index); // The size must be reset, otherwise the length of old messages could influence new ones. - //ImGui::SetNextWindowPos(position); + // ImGui::SetNextWindowPos(position); ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); // Gradually fade old messages away (except in their first frame) @@ -212,8 +236,7 @@ void DrawMessages() std::lock_guard lock{subtitles_mutex}; - for (auto it = currentSubtitles.rbegin(); - it != currentSubtitles.rend();) + for (auto it = currentSubtitles.rbegin(); it != currentSubtitles.rend();) { Subtitle& msg = it->second; const s64 time_left = msg.TimeRemaining(); diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h index 8806d7e50d27..979ff2292cb8 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h @@ -1,10 +1,12 @@ #pragma once #include + namespace OSDSubtitles { + void AddSubtitle(std::string soundFile); void DrawMessages(); void ClearMessages(); -} // namespace OSD +} // namespace OSDSubtitles From 358474f7cbd88d7b74830f535acfdacfd06cfd3f Mon Sep 17 00:00:00 2001 From: PTwr Date: Tue, 19 Sep 2023 16:11:02 +0200 Subject: [PATCH 05/28] subtitles in multizone osd --- Source/Core/VideoCommon/OnScreenDisplay.cpp | 23 ++++++-- Source/Core/VideoCommon/OnScreenDisplay.h | 26 ++++++--- .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 58 +++++++++++++------ 3 files changed, 77 insertions(+), 30 deletions(-) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 64e681f973c7..d1d979459726 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -102,7 +102,6 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t } auto windowPos = ImVec2(x_pos, y_pos); - INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", windowPos.x, windowPos.y); ImGui::SetWindowPos(window_name.c_str(), windowPos); } @@ -115,33 +114,47 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t } void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, - std::string messageStack) + std::string messageStack, bool preventDuplicate) { std::lock_guard lock{s_messages_mutex}; if (messageStacks.find(messageStack) == messageStacks.end()) { + if (preventDuplicate && messageStacks[messageStack].hasMessage(message, type)) + { + return; + } defaultMessageStack.s_messages.erase(type); defaultMessageStack.s_messages.emplace(type, Message(std::move(message), ms, argb)); } else { + if (preventDuplicate && messageStacks[messageStack].hasMessage(message, type)) + { + return; + } messageStacks["messageStack"].s_messages.erase(type); messageStacks["messageStack"].s_messages.emplace(type, Message(std::move(message), ms, argb)); } } -void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack) +void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate) { std::lock_guard lock{s_messages_mutex}; if (messageStacks.contains(messageStack)) { - INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", message, messageStack); + if (preventDuplicate && messageStacks[messageStack].hasMessage(message, MessageType::Typeless)) + { + return; + } messageStacks[messageStack].s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb)); } else { - INFO_LOG_FMT(FILEMON, "Displaying message '{}' in '{}'", message, "defaultMessageStack"); + if (preventDuplicate && messageStacks[messageStack].hasMessage(message, MessageType::Typeless)) + { + return; + } defaultMessageStack.s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb)); } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 2d2d0709e9f8..794cf4cd4e08 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -6,10 +6,9 @@ #include #include +#include #include "Common/CommonTypes.h" #include "Common/Timer.h" -#include - namespace OSD { @@ -29,7 +28,7 @@ enum class MessageType // displayed before other messages Typeless, }; -//TODO clean this mess +// TODO clean this mess struct Message { Message() = default; @@ -53,6 +52,8 @@ struct MessageStack bool reversed; std::string name; + std::multimap s_messages; + MessageStack() : MessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} MessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, bool _reversed, std::string _name) @@ -64,12 +65,22 @@ struct MessageStack name = _name; } - std::multimap s_messages; - bool isVertical() { return dir == MessageStackDirection::Downward || dir == MessageStackDirection::Upward; } + + bool hasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) + { + for (auto it = s_messages.begin(); it != s_messages.end(); ++it) + { + if (message == it->second.text) + { + return true; + } + } + return false; + } }; namespace Color @@ -91,9 +102,10 @@ void AddMessageStack(MessageStack info); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, - std::string messageStack = ""); + std::string messageStack = "", bool preventDuplicate = false); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, - u32 argb = Color::YELLOW, std::string messageStack = ""); + u32 argb = Color::YELLOW, std::string messageStack = "", + bool preventDuplicate = false); // Draw the current messages on the screen. Only call once per frame. void DrawMessages(); diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp index e2259db355b9..a213195dd941 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -127,12 +127,42 @@ void AddSubtitle(std::string soundFile) "rightward"); OSD::AddMessageStack(stack3); + InitTranslations(TranslationFile); + isInitialized = true; + } + + auto tlnode = Translations.get(soundFile); + if (tlnode.is() && tlnode.contains("Translation")) + { + auto tl = tlnode.get("Translation"); + if (tl.is()) + { + std::lock_guard lock{subtitles_mutex}; + { + bool allowDups = false; + auto dupsNode = tlnode.get("AllowDuplicate"); + if (dupsNode.is()) + { + allowDups = tlnode.get("AllowDuplicate").get(); + } + + auto msnode = tlnode.get("Miliseconds"); + // TODO allow for text/hex color (web codes?) + auto colornode = tlnode.get("Color"); + + u32 ms = msnode.is() ? msnode.get() : 3000; + u32 argb = colornode.is() ? colornode.get() : 4294967040; + + //currentSubtitles.emplace(tl.to_str(), Subtitle(std::move(tl.to_str()), ms, argb)); + + //OSD::AddMessage("default log: " + soundFile, 10000, OSD::Color::GREEN, "", true); + OSD::AddMessage(tl.to_str(), ms, argb, "subtitles", true); + //OSD::AddMessage("leftward", 10000, OSD::Color::YELLOW, "leftward", true); + //OSD::AddMessage("rightward", 10000, OSD::Color::RED, "rightward", true); + } + } } - OSD::AddMessage("default log: " + soundFile, 10000, OSD::Color::GREEN, ""); - OSD::AddMessage("subtitle zone: " + soundFile, 10000, OSD::Color::CYAN, "subtitles"); - OSD::AddMessage("leftward", 10000, OSD::Color::YELLOW, "leftward"); - OSD::AddMessage("rightward", 10000, OSD::Color::RED, "rightward"); } void _AddSubtitle(std::string soundFile) @@ -150,20 +180,12 @@ void _AddSubtitle(std::string soundFile) { std::lock_guard lock{subtitles_mutex}; { - // check if subtitle is still on screen - if (currentSubtitles.count(soundFile) > 0) + + bool allowDups = false; + auto dupsNode = tlnode.get("AllowDuplicate"); + if (dupsNode.is()) { - if (!tlnode.contains("AllowDuplicate")) - { - return; - } - - auto dupsNode = tlnode.get("AllowDuplicate"); - if (dupsNode.is() || - (dupsNode.is() && !tlnode.get("AllowDuplicate").get())) - { - return; - } + allowDups = tlnode.get("AllowDuplicate").get(); } auto msnode = tlnode.get("Miliseconds"); @@ -173,7 +195,7 @@ void _AddSubtitle(std::string soundFile) u32 ms = msnode.is() ? msnode.get() : 3000; u32 argb = colornode.is() ? colornode.get() : 4294967040; - currentSubtitles.emplace(soundFile, Subtitle(std::move(tl.to_str()), ms, argb)); + currentSubtitles.emplace(tl.to_str(), Subtitle(std::move(tl.to_str()), ms, argb)); } } } From 09f014d40580ae0f2eae8b86d6bb86322caa21b4 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 20 Sep 2023 12:46:23 +0200 Subject: [PATCH 06/28] make osdsubtitles use shared osd --- Source/Core/Core/Boot/Boot.cpp | 5 + Source/Core/Core/Core.cpp | 1 - Source/Core/VideoCommon/OnScreenUI.cpp | 1 - .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 208 ++---------------- .../VideoCommon/OsdSubtitles/OsdSubtitles.h | 5 +- 5 files changed, 28 insertions(+), 192 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 86501bd910e8..fa00b50aef34 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -56,6 +56,7 @@ #include "DiscIO/RiivolutionPatcher.h" #include "DiscIO/VolumeDisc.h" #include "DiscIO/VolumeWad.h" +#include static std::vector ReadM3UFile(const std::string& m3u_path, const std::string& folder_path) @@ -510,6 +511,7 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard, bool operator()(BootParameters::Disc& disc) const { NOTICE_LOG_FMT(BOOT, "Booting from disc: {}", disc.path); + const DiscIO::VolumeDisc* volume = SetDisc(std::move(disc.volume), disc.auto_disc_change_paths); @@ -520,6 +522,9 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard, return false; SConfig::OnNewTitleLoad(guard); + + OSDSubtitles::TryInitTranslations(disc.path + ".translation.json"); + return true; } diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 68f776914857..7177a57a3418 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -582,7 +582,6 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // Clear on screen messages that haven't expired OSD::ClearMessages(); - OSDSubtitles::ClearMessages(); // The config must be restored only after the whole HW has shut down, // not when it is still running. diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 053961b65e1a..869138b6208a 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -334,7 +334,6 @@ void OnScreenUI::Finalize() g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); DrawDebugText(); OSD::DrawMessages(); - OSDSubtitles::DrawMessages(); ImGui::Render(); } diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp index a213195dd941..03adab197e79 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp @@ -10,54 +10,24 @@ #include "Common/Config/Config.h" #include "Common/Timer.h" +#include #include #include #include #include #include -#include -// TODO unify with OSD, will require different positioning for specific messagetype? +// TODO move to plugin? subtitling on file load is specific to 0079 game namespace OSDSubtitles { -constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages. -constexpr float TOP_MARGIN = 10.0f; // Pixels above the first OSD message. -constexpr float WINDOW_PADDING = 4.0f; // Pixels between subsequent OSD messages. -constexpr float MESSAGE_FADE_TIME = 1000.f; // Ms to fade OSD messages at the end of their life. -constexpr float MESSAGE_DROP_TIME = 5000.f; // Ms to drop OSD messages that has yet to ever render. - -static ImVec4 ARGBToImVec4(const u32 argb) -{ - return ImVec4(static_cast((argb >> 16) & 0xFF) / 255.0f, - static_cast((argb >> 8) & 0xFF) / 255.0f, - static_cast((argb >> 0) & 0xFF) / 255.0f, - static_cast((argb >> 24) & 0xFF) / 255.0f); -} - const std::string TranslationFile = "C:\\games\\wii\\translate.json"; -// TODO check if just using OnScreenDisplay::Message will be enough -struct Subtitle -{ - Subtitle() = default; - Subtitle(std::string text_, u32 duration_, u32 color_) - : text(std::move(text_)), duration(duration_), color(color_) - { - timer.Start(); - } - s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } - std::string text; - Common::Timer timer; - u32 duration = 0; - bool ever_drawn = false; - u32 color = 0; - - // TODO Position from json? -}; - // TODO check if thers some ready io helper to use for configs? auto read_file(std::string_view path, bool throwOnMissingFile = false) -> std::string { + auto stack = OSD::MessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "subtitles"); + OSD::AddMessageStack(stack); + constexpr auto read_size = std::size_t(4096); auto stream = std::ifstream(path.data()); stream.exceptions(std::ios_base::badbit); @@ -85,14 +55,14 @@ auto read_file(std::string_view path, bool throwOnMissingFile = false) -> std::s } picojson::value Translations; -bool isInitialized = false; +static bool isInitialized = false; static std::mutex init_mutex; -static std::mutex subtitles_mutex; // TODO init on rom load -void InitTranslations(const std::string& filename) +void TryInitTranslations(const std::string& filename) { std::lock_guard lock{init_mutex}; + isInitialized = false; if (isInitialized) { @@ -102,85 +72,37 @@ void InitTranslations(const std::string& filename) auto json = read_file(filename); + if (json == "") + { + return; + } + std::string err = picojson::parse(Translations, json); if (!err.empty()) { std::cerr << err << std::endl; + return; } isInitialized = true; } -static std::multimap currentSubtitles; void AddSubtitle(std::string soundFile) { if (!isInitialized) { - auto stack = OSD::MessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "subtitles"); - OSD::AddMessageStack(stack); - - auto stack2 = OSD::MessageStack(0, 100, OSD::MessageStackDirection::Leftward, false, true, - "leftward"); - OSD::AddMessageStack(stack2); - - auto stack3 = OSD::MessageStack(0, 200, OSD::MessageStackDirection::Rightward, false, false, - "rightward"); - OSD::AddMessageStack(stack3); - - InitTranslations(TranslationFile); - - isInitialized = true; - } - - auto tlnode = Translations.get(soundFile); - if (tlnode.is() && tlnode.contains("Translation")) - { - auto tl = tlnode.get("Translation"); - if (tl.is()) - { - std::lock_guard lock{subtitles_mutex}; - { - bool allowDups = false; - auto dupsNode = tlnode.get("AllowDuplicate"); - if (dupsNode.is()) - { - allowDups = tlnode.get("AllowDuplicate").get(); - } - - auto msnode = tlnode.get("Miliseconds"); - // TODO allow for text/hex color (web codes?) - auto colornode = tlnode.get("Color"); - - u32 ms = msnode.is() ? msnode.get() : 3000; - u32 argb = colornode.is() ? colornode.get() : 4294967040; - - //currentSubtitles.emplace(tl.to_str(), Subtitle(std::move(tl.to_str()), ms, argb)); - - //OSD::AddMessage("default log: " + soundFile, 10000, OSD::Color::GREEN, "", true); - OSD::AddMessage(tl.to_str(), ms, argb, "subtitles", true); - //OSD::AddMessage("leftward", 10000, OSD::Color::YELLOW, "leftward", true); - //OSD::AddMessage("rightward", 10000, OSD::Color::RED, "rightward", true); - } - } - } -} - -void _AddSubtitle(std::string soundFile) -{ - if (!isInitialized) - { - InitTranslations(TranslationFile); + return; } - auto tlnode = Translations.get(soundFile); - if (tlnode.is() && tlnode.contains("Translation")) + // TODO parse json to structs during init, this suuucks + if (Translations.contains(soundFile)) { - auto tl = tlnode.get("Translation"); - if (tl.is()) + auto tlnode = Translations.get(soundFile); + if (tlnode.is() && tlnode.contains("Translation")) { - std::lock_guard lock{subtitles_mutex}; + auto tl = tlnode.get("Translation"); + if (tl.is()) { - bool allowDups = false; auto dupsNode = tlnode.get("AllowDuplicate"); if (dupsNode.is()) @@ -195,95 +117,9 @@ void _AddSubtitle(std::string soundFile) u32 ms = msnode.is() ? msnode.get() : 3000; u32 argb = colornode.is() ? colornode.get() : 4294967040; - currentSubtitles.emplace(tl.to_str(), Subtitle(std::move(tl.to_str()), ms, argb)); + OSD::AddMessage(tl.to_str(), ms, argb, "subtitles", true); } } } } - -static float DrawMessage(int index, Subtitle& msg, const ImVec2& position, int time_left) -{ - // We have to provide a window name, and these shouldn't be duplicated. - // So instead, we generate a name based on the number of messages drawn. - const std::string window_name = fmt::format("osd_subtitle_{}", index); - - // The size must be reset, otherwise the length of old messages could influence new ones. - // ImGui::SetNextWindowPos(position); - ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); - - // Gradually fade old messages away (except in their first frame) - const float fade_time = std::max(std::min(MESSAGE_FADE_TIME, (float)msg.duration), 1.f); - const float alpha = std::clamp(time_left / fade_time, 0.f, 1.f); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, msg.ever_drawn ? alpha : 1.0); - - float window_height = 0.0f; - float window_width = 0.0f; - if (ImGui::Begin(window_name.c_str(), nullptr, - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | - ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) - { - // Use %s in case message contains %. - ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); - window_height = - ImGui::GetWindowSize().y + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.y); - window_width = - ImGui::GetWindowSize().x + (WINDOW_PADDING * ImGui::GetIO().DisplayFramebufferScale.x); - - float x_center = ImGui::GetIO().DisplaySize.x / 2.0; - - auto subtitlePos = ImVec2(x_center - window_width / 2, position.y - window_height); - ImGui::SetWindowPos(window_name.c_str(), subtitlePos); - } - - ImGui::End(); - ImGui::PopStyleVar(); - - msg.ever_drawn = true; - - return window_height; -} - -void DrawMessages() -{ - const bool draw_messages = true; // Config::Get(Config::MAIN_OSD_MESSAGES); - const float current_x = 0; - // LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + s_obscured_pixels_left; - float current_y = - 0; // TOP_MARGIN* ImGui::GetIO().DisplayFramebufferScale.y + s_obscured_pixels_top; - int index = 0; - - current_y = ImGui::GetIO().DisplaySize.y; - - std::lock_guard lock{subtitles_mutex}; - - for (auto it = currentSubtitles.rbegin(); it != currentSubtitles.rend();) - { - Subtitle& msg = it->second; - const s64 time_left = msg.TimeRemaining(); - - // Make sure we draw them at least once if they were printed with 0ms, - // unless enough time has expired, in that case, we drop them - if (time_left <= 0 && (msg.ever_drawn || -time_left >= MESSAGE_DROP_TIME)) - { - currentSubtitles.erase(std::next(it).base()); - continue; - } - else - { - ++it; - } - - if (draw_messages) - current_y -= DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left); - } -} - -void ClearMessages() -{ - std::lock_guard lock{subtitles_mutex}; - currentSubtitles.clear(); -} - } // namespace OSDSubtitles diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h index 979ff2292cb8..1a836d90c7e7 100644 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h +++ b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h @@ -3,10 +3,7 @@ namespace OSDSubtitles { - +void TryInitTranslations(const std::string& filename); void AddSubtitle(std::string soundFile); -void DrawMessages(); - -void ClearMessages(); } // namespace OSDSubtitles From a17c36890788e0d9a0203e1e99fb3cadfc0ea27f Mon Sep 17 00:00:00 2001 From: PTwr Date: Tue, 26 Sep 2023 12:04:58 +0200 Subject: [PATCH 07/28] subtitles moved to plugin system --- Source/Core/Common/CommonPaths.h | 1 + Source/Core/Common/FileUtil.cpp | 1 + Source/Core/Common/FileUtil.h | 1 + Source/Core/Core/Boot/Boot.cpp | 6 +- Source/Core/Core/ConfigManager.cpp | 2 + Source/Core/Core/Core.cpp | 4 +- Source/Core/Core/HW/DVD/DVDThread.cpp | 2 + Source/Core/Core/HW/DVD/FileMonitor.cpp | 14 +- Source/Core/DolphinLib.vcxproj | 6 +- Source/Core/Plugins/PluginInterface.h | 35 +++++ Source/Core/Plugins/PluginLoader.cpp | 142 ++++++++++++++++++ Source/Core/Plugins/PluginLoader.h | 17 +++ Source/Core/VideoCommon/OnScreenDisplay.cpp | 68 +++------ .../Core/VideoCommon/OnScreenDisplay.enum.h | 20 +++ Source/Core/VideoCommon/OnScreenDisplay.h | 33 ++-- Source/Core/VideoCommon/OnScreenUI.cpp | 1 - .../VideoCommon/OsdSubtitles/OsdSubtitles.cpp | 125 --------------- .../VideoCommon/OsdSubtitles/OsdSubtitles.h | 9 -- 18 files changed, 266 insertions(+), 221 deletions(-) create mode 100644 Source/Core/Plugins/PluginInterface.h create mode 100644 Source/Core/Plugins/PluginLoader.cpp create mode 100644 Source/Core/Plugins/PluginLoader.h create mode 100644 Source/Core/VideoCommon/OnScreenDisplay.enum.h delete mode 100644 Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp delete mode 100644 Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 44ada6dd192b..8ce89b8b9dc6 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -94,6 +94,7 @@ #define DYNAMICINPUT_DIR "DynamicInputTextures" #define GRAPHICSMOD_DIR "GraphicMods" #define WIISDSYNC_DIR "WiiSDSync" +#define PLUGIN_DIR "Plugins" // This one is only used to remove it if it was present #define SHADERCACHE_LEGACY_DIR "ShaderCache" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index f5842c14f93a..f7e9a9fa952e 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -864,6 +864,7 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_WFSROOT_IDX] = s_user_paths[D_USER_IDX] + WFSROOT_DIR DIR_SEP; s_user_paths[D_BACKUP_IDX] = s_user_paths[D_USER_IDX] + BACKUP_DIR DIR_SEP; s_user_paths[D_RESOURCEPACK_IDX] = s_user_paths[D_USER_IDX] + RESOURCEPACK_DIR DIR_SEP; + s_user_paths[D_PLUGINS_IDX] = s_user_paths[D_USER_IDX] + PLUGIN_DIR DIR_SEP; s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP; s_user_paths[D_GRAPHICSMOD_IDX] = s_user_paths[D_LOAD_IDX] + GRAPHICSMOD_DIR DIR_SEP; s_user_paths[D_WIISDCARDSYNCFOLDER_IDX] = s_user_paths[D_LOAD_IDX] + WIISDSYNC_DIR DIR_SEP; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 8d5f312d65ab..c217c69b2a18 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -62,6 +62,7 @@ enum D_WFSROOT_IDX, D_BACKUP_IDX, D_RESOURCEPACK_IDX, + D_PLUGINS_IDX, D_DYNAMICINPUT_IDX, D_GRAPHICSMOD_IDX, D_GBAUSER_IDX, diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index fa00b50aef34..3e99de84525a 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -56,7 +56,7 @@ #include "DiscIO/RiivolutionPatcher.h" #include "DiscIO/VolumeDisc.h" #include "DiscIO/VolumeWad.h" -#include +#include static std::vector ReadM3UFile(const std::string& m3u_path, const std::string& folder_path) @@ -522,8 +522,8 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard, return false; SConfig::OnNewTitleLoad(guard); - - OSDSubtitles::TryInitTranslations(disc.path + ".translation.json"); + Plugins::Init(); + Plugins::OnGameLoad(disc.path.c_str()); return true; } diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 27b7b2a51bd3..b44e71379146 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -60,6 +60,8 @@ #include "DiscIO/Volume.h" #include "DiscIO/VolumeWad.h" +#include "Plugins/PluginLoader.h" + SConfig* SConfig::m_Instance; SConfig::SConfig() diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 7177a57a3418..3879ddbbfc56 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -97,7 +97,7 @@ #ifdef ANDROID #include "jni/AndroidCommon/IDCache.h" #endif -#include +#include namespace Core { @@ -292,6 +292,8 @@ void Stop() // - Hammertime! if (GetState() == State::Stopping || GetState() == State::Uninitialized) return; + Plugins::Cleanup(); + #ifdef USE_RETRO_ACHIEVEMENTS AchievementManager::GetInstance()->CloseGame(); #endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index d861bba2eb42..9e04578b664c 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -33,6 +33,7 @@ #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" +#include namespace DVD { @@ -343,6 +344,7 @@ void DVDThread::DVDThreadMain() while (m_request_queue.Pop(request)) { m_file_logger.Log(*m_disc, request.partition, request.dvd_offset); + Plugins::OnFileAccess(*m_disc, request.partition, request.dvd_offset); std::vector buffer(request.length); if (!m_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 030c2ce1a1b5..f4bc53a09717 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -17,7 +17,6 @@ #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" -#include namespace FileMonitor { @@ -53,12 +52,9 @@ FileLogger::~FileLogger() = default; void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) { - // Do nothing if the log isn't selected - auto logEnabled = Common::Log::LogManager::GetInstance()->IsEnabled( - Common::Log::LogType::FILEMON, Common::Log::LogLevel::LWARNING); - auto subtitlesEnabled = true; //TODO settings for subtitles - if (!(logEnabled || subtitlesEnabled)) + if (!Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::FILEMON, + Common::Log::LogLevel::LWARNING)) { return; } @@ -85,13 +81,7 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {}", size_string, path); if (IsSoundFile(path)) - { INFO_LOG_FMT(FILEMON, "{}", log_string); - if (subtitlesEnabled) - { - OSDSubtitles::AddSubtitle(path); - } - } else WARN_LOG_FMT(FILEMON, "{}", log_string); diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 23766a1e8204..b4448d3e5bd1 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,10 +70,12 @@ - + + + - + diff --git a/Source/Core/Plugins/PluginInterface.h b/Source/Core/Plugins/PluginInterface.h new file mode 100644 index 000000000000..a80f2731ddc5 --- /dev/null +++ b/Source/Core/Plugins/PluginInterface.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include +#include "picojson.h" + +namespace Plugins +{ +class IPluginInterface_Events +{ +public: + IPluginInterface_Events(){}; + virtual ~IPluginInterface_Events(){}; + virtual void OnGameLoad(const char* gamePath) = 0; + virtual void OnGameClose(const char* gamePath) = 0; + virtual void OnFileAccess(const char* path) = 0; +}; + +class IAPI +{ +public: + IAPI(){}; + virtual ~IAPI(){}; + virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, + bool centered, bool reversed, const char* name) = 0; + virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, + bool preventDuplicate) = 0; + virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, + const char* messageStack, bool preventDuplicate) = 0; + virtual const char* GetGameId() = 0; +}; + + +typedef IPluginInterface_Events*(__stdcall* GetPluginInterface_Events)(IAPI* api); +} // namespace Plugins diff --git a/Source/Core/Plugins/PluginLoader.cpp b/Source/Core/Plugins/PluginLoader.cpp new file mode 100644 index 000000000000..26431b1eb0aa --- /dev/null +++ b/Source/Core/Plugins/PluginLoader.cpp @@ -0,0 +1,142 @@ + +#include "PluginLoader.h" +#include +#include +#include +#include +#include +#include +#include +#include "PluginInterface.h" +#include "Common/FileUtil.h" + +// TODO multiplatform +#include + +namespace Plugins +{ +std::vector EventPlugins; +std::vector DllHandles; + + +class API : public IAPI +{ +public: + const char* gameId; + API() : IAPI() { gameId = SConfig::GetInstance().GetGameID().c_str(); } + + virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, + bool centered, bool reversed, const char* name) override + { + auto stack = OSD::OSDMessageStack(x_offset, y_offset, dir, centered, reversed, std::move(name)); + //auto stack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "R79JAF_subtitles"); + OSD::AddMessageStack(stack); + } + virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, + bool preventDuplicate) override + { + OSD::AddMessage(message, ms, argb, messageStack == 0 ? "" : messageStack, preventDuplicate); + //OSD::AddMessage(message, ms, argb, "R79JAF_subtitles", false); + } + virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, + const char* messageStack, bool preventDuplicate) override + { + //NOTICE_LOG_FMT(BOOT, "Calling {}", "AddTypedMessage"); + OSD::AddTypedMessage(type, std::move(message), ms, argb, std::move(messageStack), + preventDuplicate); + } + virtual const char* GetGameId() override + { + //NOTICE_LOG_FMT(BOOT, "Calling {}", "GetGameId"); + return gameId; + } +}; +API* apiInstance = 0; + +// TODO get rom path insteead of getting disc as param +void Init() +{ + Cleanup(); + + apiInstance = new API(); + + auto dlldir = File::GetUserPath(D_PLUGINS_IDX); + + if (!std::filesystem::exists(dlldir)) + { + return; + } + for (const auto& entry : std::filesystem::directory_iterator(dlldir)) + { + if (entry.path().extension() == ".dll") + { + OSD::AddMessage("Loading plugin library " + entry.path().string()); + HMODULE dllHandle = LoadLibrary(entry.path().c_str()); + + auto interfaceGetter = + (GetPluginInterface_Events)GetProcAddress(dllHandle, "GetPluginInterface_Events"); + if (interfaceGetter) + { + //OSD::AddMessage("found dll method"); + auto pluginInterface = interfaceGetter(apiInstance); + if (pluginInterface != 0) + { + //OSD::AddMessage("storing plugin interface"); + EventPlugins.push_back(pluginInterface); + } + } + } + } +} +void Cleanup() +{ + if (apiInstance != 0) + { + delete apiInstance; + } + + EventPlugins.clear(); + + for (auto dllHandle : DllHandles) + { + FreeLibrary(dllHandle); + } + DllHandles.clear(); +} + +void OnGameLoad(const char* path) +{ + for (auto i : EventPlugins) + { + i->OnGameLoad(path); + } +} +void OnGameClose(const char* path) +{ + for (auto i : EventPlugins) + { + i->OnGameClose(path); + } +} + +void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) +{ + if (!EventPlugins.empty()) + { + const DiscIO::FileSystem* file_system = volume.GetFileSystem(partition); + if (!file_system) + { + //ignore invalid calls + return; + } + + const std::unique_ptr file_info = file_system->FindFileInfo(offset); + const char* path = file_info->GetPath().c_str(); + + for (auto i : EventPlugins) + { + i->OnFileAccess(path); + } + } +} +} // namespace Plugins diff --git a/Source/Core/Plugins/PluginLoader.h b/Source/Core/Plugins/PluginLoader.h new file mode 100644 index 000000000000..1273630af6ca --- /dev/null +++ b/Source/Core/Plugins/PluginLoader.h @@ -0,0 +1,17 @@ +#pragma once +#include +namespace Plugins +{ +/// +/// Performs Cleanup and Loads plugin libraries +/// +void Init(); +/// +/// Frees loaded libraries +/// +void Cleanup(); + +void OnGameLoad(const char* path); +void OnGameClose(const char* path); +void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset); +}; // namespace Plugins diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index d1d979459726..7b3df361f62a 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -31,8 +31,8 @@ static std::atomic s_obscured_pixels_top = 0; // default message stack // static std::multimap s_messages; -static MessageStack defaultMessageStack = MessageStack(); -static std::map messageStacks; +static OSDMessageStack defaultMessageStack = OSDMessageStack(); +static std::map messageStacks; static std::mutex s_messages_mutex; @@ -45,7 +45,7 @@ static ImVec4 ARGBToImVec4(const u32 argb) } static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int time_left, - MessageStack& messageStack) + OSDMessageStack& messageStack) { // We have to provide a window name, and these shouldn't be duplicated. // So instead, we generate a name based on the number of messages drawn. @@ -117,55 +117,35 @@ void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate) { std::lock_guard lock{s_messages_mutex}; - if (messageStacks.find(messageStack) == messageStacks.end()) + + OSDMessageStack* stack = &defaultMessageStack; + if (messageStacks.contains(messageStack)) { - if (preventDuplicate && messageStacks[messageStack].hasMessage(message, type)) - { - return; - } - defaultMessageStack.s_messages.erase(type); - defaultMessageStack.s_messages.emplace(type, Message(std::move(message), ms, argb)); + stack = &messageStacks[messageStack]; } - else + + if (preventDuplicate && stack->hasMessage(message, type)) { - if (preventDuplicate && messageStacks[messageStack].hasMessage(message, type)) - { - return; - } - messageStacks["messageStack"].s_messages.erase(type); - messageStacks["messageStack"].s_messages.emplace(type, Message(std::move(message), ms, argb)); + return; + } + if (type != MessageType::Typeless) + { + stack->messages.erase(type); } + stack->messages.emplace(type, Message(std::move(message), ms, argb)); } void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate) { - std::lock_guard lock{s_messages_mutex}; - if (messageStacks.contains(messageStack)) - { - if (preventDuplicate && messageStacks[messageStack].hasMessage(message, MessageType::Typeless)) - { - return; - } - messageStacks[messageStack].s_messages.emplace(MessageType::Typeless, - Message(std::move(message), ms, argb)); - } - else - { - if (preventDuplicate && messageStacks[messageStack].hasMessage(message, MessageType::Typeless)) - { - return; - } - defaultMessageStack.s_messages.emplace(MessageType::Typeless, - Message(std::move(message), ms, argb)); - } + AddTypedMessage(MessageType::Typeless, message, ms, argb, messageStack, preventDuplicate); } -void AddMessageStack(MessageStack info) +void AddMessageStack(OSDMessageStack info) { // TODO handle dups messageStacks[info.name] = info; } -void DrawMessages(MessageStack& messageStack) +void DrawMessages(OSDMessageStack& messageStack) { const bool draw_messages = Config::Get(Config::MAIN_OSD_MESSAGES); float current_x = LEFT_MARGIN * ImGui::GetIO().DisplayFramebufferScale.x + @@ -184,10 +164,10 @@ void DrawMessages(MessageStack& messageStack) } std::lock_guard lock{s_messages_mutex}; - for (auto it = (messageStack.reversed ? messageStack.s_messages.end() : - messageStack.s_messages.begin()); + for (auto it = (messageStack.reversed ? messageStack.messages.end() : + messageStack.messages.begin()); it != - (messageStack.reversed ? messageStack.s_messages.begin() : messageStack.s_messages.end());) + (messageStack.reversed ? messageStack.messages.begin() : messageStack.messages.end());) { if (messageStack.reversed) { @@ -201,7 +181,7 @@ void DrawMessages(MessageStack& messageStack) // unless enough time has expired, in that case, we drop them if (time_left <= 0 && (msg.ever_drawn || -time_left >= MESSAGE_DROP_TIME)) { - it = messageStack.s_messages.erase(it); + it = messageStack.messages.erase(it); continue; } else if (!messageStack.reversed) @@ -239,10 +219,10 @@ void DrawMessages() void ClearMessages() { std::lock_guard lock{s_messages_mutex}; - defaultMessageStack.s_messages.clear(); + defaultMessageStack.messages.clear(); for (auto& [name, stack] : messageStacks) { - stack.s_messages.clear(); + stack.messages.clear(); } } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.enum.h b/Source/Core/VideoCommon/OnScreenDisplay.enum.h new file mode 100644 index 000000000000..cae56c8d8fb0 --- /dev/null +++ b/Source/Core/VideoCommon/OnScreenDisplay.enum.h @@ -0,0 +1,20 @@ +#pragma once +namespace OSD +{ +enum class MessageStackDirection +{ + Downward = 1, + Upward = 2, + Rightward = 4, + Leftward = 8, +}; +enum class MessageType +{ + NetPlayPing, + NetPlayBuffer, + + // This entry must be kept last so that persistent typed messages are + // displayed before other messages + Typeless, +}; +} diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 794cf4cd4e08..123a1065d5c0 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -9,25 +9,10 @@ #include #include "Common/CommonTypes.h" #include "Common/Timer.h" +#include "OnScreenDisplay.enum.h" namespace OSD { -enum class MessageStackDirection -{ - Downward = 1, - Upward = 2, - Rightward = 4, - Leftward = 8, -}; -enum class MessageType -{ - NetPlayPing, - NetPlayBuffer, - - // This entry must be kept last so that persistent typed messages are - // displayed before other messages - Typeless, -}; // TODO clean this mess struct Message { @@ -44,18 +29,18 @@ struct Message bool ever_drawn = false; u32 color = 0; }; -struct MessageStack +class OSDMessageStack { +public: ImVec2 initialPosOffset; MessageStackDirection dir; bool centered; bool reversed; std::string name; - - std::multimap s_messages; - - MessageStack() : MessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} - MessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, + std::multimap messages; + + OSDMessageStack() : OSDMessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} + OSDMessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, bool _reversed, std::string _name) { initialPosOffset = ImVec2(_x_offset, _y_offset); @@ -72,7 +57,7 @@ struct MessageStack bool hasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) { - for (auto it = s_messages.begin(); it != s_messages.end(); ++it) + for (auto it = messages.begin(); it != messages.end(); ++it) { if (message == it->second.text) { @@ -98,7 +83,7 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration -void AddMessageStack(MessageStack info); +void AddMessageStack(OSDMessageStack info); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 869138b6208a..1919149629f6 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -29,7 +29,6 @@ #include #include -#include "OsdSubtitles\OsdSubtitles.h" namespace VideoCommon { diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp deleted file mode 100644 index 03adab197e79..000000000000 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "OsdSubtitles.h" - -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/Config/Config.h" -#include "Common/Timer.h" - -#include -#include -#include -#include -#include -#include - -// TODO move to plugin? subtitling on file load is specific to 0079 game -namespace OSDSubtitles -{ -const std::string TranslationFile = "C:\\games\\wii\\translate.json"; - -// TODO check if thers some ready io helper to use for configs? -auto read_file(std::string_view path, bool throwOnMissingFile = false) -> std::string -{ - auto stack = OSD::MessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "subtitles"); - OSD::AddMessageStack(stack); - - constexpr auto read_size = std::size_t(4096); - auto stream = std::ifstream(path.data()); - stream.exceptions(std::ios_base::badbit); - - if (not stream) - { - if (throwOnMissingFile) - { - throw std::ios_base::failure("Subtitle file does not exist"); - } - else - { - return ""; - } - } - - auto out = std::string(); - auto buf = std::string(read_size, '\0'); - while (stream.read(&buf[0], read_size)) - { - out.append(buf, 0, stream.gcount()); - } - out.append(buf, 0, stream.gcount()); - return out; -} - -picojson::value Translations; -static bool isInitialized = false; -static std::mutex init_mutex; - -// TODO init on rom load -void TryInitTranslations(const std::string& filename) -{ - std::lock_guard lock{init_mutex}; - isInitialized = false; - - if (isInitialized) - { - // another call already did the init - return; - } - - auto json = read_file(filename); - - if (json == "") - { - return; - } - - std::string err = picojson::parse(Translations, json); - if (!err.empty()) - { - std::cerr << err << std::endl; - return; - } - - isInitialized = true; -} - -void AddSubtitle(std::string soundFile) -{ - if (!isInitialized) - { - return; - } - - // TODO parse json to structs during init, this suuucks - if (Translations.contains(soundFile)) - { - auto tlnode = Translations.get(soundFile); - if (tlnode.is() && tlnode.contains("Translation")) - { - auto tl = tlnode.get("Translation"); - if (tl.is()) - { - bool allowDups = false; - auto dupsNode = tlnode.get("AllowDuplicate"); - if (dupsNode.is()) - { - allowDups = tlnode.get("AllowDuplicate").get(); - } - - auto msnode = tlnode.get("Miliseconds"); - // TODO allow for text/hex color (web codes?) - auto colornode = tlnode.get("Color"); - - u32 ms = msnode.is() ? msnode.get() : 3000; - u32 argb = colornode.is() ? colornode.get() : 4294967040; - - OSD::AddMessage(tl.to_str(), ms, argb, "subtitles", true); - } - } - } -} -} // namespace OSDSubtitles diff --git a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h b/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h deleted file mode 100644 index 1a836d90c7e7..000000000000 --- a/Source/Core/VideoCommon/OsdSubtitles/OsdSubtitles.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include - -namespace OSDSubtitles -{ -void TryInitTranslations(const std::string& filename); -void AddSubtitle(std::string soundFile); - -} // namespace OSDSubtitles From 4e857341413c55b67982577bd6924e57f04d7fd4 Mon Sep 17 00:00:00 2001 From: PTwr Date: Sun, 1 Oct 2023 20:37:20 +0200 Subject: [PATCH 08/28] bugfix on restarting game --- Source/Core/Plugins/PluginLoader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Core/Plugins/PluginLoader.cpp b/Source/Core/Plugins/PluginLoader.cpp index 26431b1eb0aa..4d56b3c84281 100644 --- a/Source/Core/Plugins/PluginLoader.cpp +++ b/Source/Core/Plugins/PluginLoader.cpp @@ -72,6 +72,7 @@ void Init() { OSD::AddMessage("Loading plugin library " + entry.path().string()); HMODULE dllHandle = LoadLibrary(entry.path().c_str()); + DllHandles.push_back(dllHandle); auto interfaceGetter = (GetPluginInterface_Events)GetProcAddress(dllHandle, "GetPluginInterface_Events"); @@ -102,6 +103,12 @@ void Cleanup() FreeLibrary(dllHandle); } DllHandles.clear(); + + if (apiInstance != 0) + { + delete apiInstance; + apiInstance = 0; + } } void OnGameLoad(const char* path) From a291d75e0eb5c584f52494c42f17bc160e3d93b2 Mon Sep 17 00:00:00 2001 From: PTwr Date: Sun, 1 Oct 2023 20:38:11 +0200 Subject: [PATCH 09/28] text scalling in subtitles --- Source/Core/Plugins/PluginInterface.h | 4 +-- Source/Core/Plugins/PluginLoader.cpp | 32 +++++++-------------- Source/Core/VideoCommon/OnScreenDisplay.cpp | 10 ++++--- Source/Core/VideoCommon/OnScreenDisplay.h | 9 +++--- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/Source/Core/Plugins/PluginInterface.h b/Source/Core/Plugins/PluginInterface.h index a80f2731ddc5..de465f8be380 100644 --- a/Source/Core/Plugins/PluginInterface.h +++ b/Source/Core/Plugins/PluginInterface.h @@ -24,9 +24,9 @@ class IAPI virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, bool centered, bool reversed, const char* name) = 0; virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, - bool preventDuplicate) = 0; + bool preventDuplicate, float scale) = 0; virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, - const char* messageStack, bool preventDuplicate) = 0; + const char* messageStack, bool preventDuplicate, float scale) = 0; virtual const char* GetGameId() = 0; }; diff --git a/Source/Core/Plugins/PluginLoader.cpp b/Source/Core/Plugins/PluginLoader.cpp index 4d56b3c84281..ff33d8e6281e 100644 --- a/Source/Core/Plugins/PluginLoader.cpp +++ b/Source/Core/Plugins/PluginLoader.cpp @@ -5,10 +5,11 @@ #include #include #include +#include #include #include -#include "PluginInterface.h" #include "Common/FileUtil.h" +#include "PluginInterface.h" // TODO multiplatform #include @@ -18,7 +19,6 @@ namespace Plugins std::vector EventPlugins; std::vector DllHandles; - class API : public IAPI { public: @@ -26,28 +26,26 @@ class API : public IAPI API() : IAPI() { gameId = SConfig::GetInstance().GetGameID().c_str(); } virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, - bool centered, bool reversed, const char* name) override + bool centered, bool reversed, const char* name) override { auto stack = OSD::OSDMessageStack(x_offset, y_offset, dir, centered, reversed, std::move(name)); - //auto stack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, "R79JAF_subtitles"); OSD::AddMessageStack(stack); } virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, - bool preventDuplicate) override + bool preventDuplicate, float scale) override { - OSD::AddMessage(message, ms, argb, messageStack == 0 ? "" : messageStack, preventDuplicate); - //OSD::AddMessage(message, ms, argb, "R79JAF_subtitles", false); + OSD::AddMessage(message, ms, argb, messageStack == 0 ? "" : messageStack, preventDuplicate, + scale); } virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, - const char* messageStack, bool preventDuplicate) override + const char* messageStack, bool preventDuplicate, + float scale) override { - //NOTICE_LOG_FMT(BOOT, "Calling {}", "AddTypedMessage"); OSD::AddTypedMessage(type, std::move(message), ms, argb, std::move(messageStack), - preventDuplicate); + preventDuplicate, scale); } virtual const char* GetGameId() override - { - //NOTICE_LOG_FMT(BOOT, "Calling {}", "GetGameId"); + {; return gameId; } }; @@ -70,7 +68,6 @@ void Init() { if (entry.path().extension() == ".dll") { - OSD::AddMessage("Loading plugin library " + entry.path().string()); HMODULE dllHandle = LoadLibrary(entry.path().c_str()); DllHandles.push_back(dllHandle); @@ -78,11 +75,9 @@ void Init() (GetPluginInterface_Events)GetProcAddress(dllHandle, "GetPluginInterface_Events"); if (interfaceGetter) { - //OSD::AddMessage("found dll method"); auto pluginInterface = interfaceGetter(apiInstance); if (pluginInterface != 0) { - //OSD::AddMessage("storing plugin interface"); EventPlugins.push_back(pluginInterface); } } @@ -91,11 +86,6 @@ void Init() } void Cleanup() { - if (apiInstance != 0) - { - delete apiInstance; - } - EventPlugins.clear(); for (auto dllHandle : DllHandles) @@ -133,7 +123,7 @@ void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partiti const DiscIO::FileSystem* file_system = volume.GetFileSystem(partition); if (!file_system) { - //ignore invalid calls + // ignore invalid calls return; } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 7b3df361f62a..f758839133bf 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -68,6 +68,8 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) { + //TODO fractional scaling based on viewport size instead of screen pixels? + ImGui::SetWindowFontScale(msg.scale); // Use %s in case message contains %. ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); window_width = @@ -114,7 +116,7 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t } void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, - std::string messageStack, bool preventDuplicate) + std::string messageStack, bool preventDuplicate, float scale) { std::lock_guard lock{s_messages_mutex}; @@ -132,12 +134,12 @@ void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, { stack->messages.erase(type); } - stack->messages.emplace(type, Message(std::move(message), ms, argb)); + stack->messages.emplace(type, Message(std::move(message), ms, argb, scale)); } -void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate) +void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate, float scale) { - AddTypedMessage(MessageType::Typeless, message, ms, argb, messageStack, preventDuplicate); + AddTypedMessage(MessageType::Typeless, message, ms, argb, messageStack, preventDuplicate, scale); } void AddMessageStack(OSDMessageStack info) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 123a1065d5c0..4ca4e38ecec8 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -17,8 +17,8 @@ namespace OSD struct Message { Message() = default; - Message(std::string text_, u32 duration_, u32 color_) - : text(std::move(text_)), duration(duration_), color(color_) + Message(std::string text_, u32 duration_, u32 color_, float scale) + : text(std::move(text_)), duration(duration_), color(color_), scale(scale) { timer.Start(); } @@ -28,6 +28,7 @@ struct Message u32 duration = 0; bool ever_drawn = false; u32 color = 0; + float scale = 1; }; class OSDMessageStack { @@ -87,10 +88,10 @@ void AddMessageStack(OSDMessageStack info); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, - std::string messageStack = "", bool preventDuplicate = false); + std::string messageStack = "", bool preventDuplicate = false, float scale = 1); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, std::string messageStack = "", - bool preventDuplicate = false); + bool preventDuplicate = false, float scale = 1); // Draw the current messages on the screen. Only call once per frame. void DrawMessages(); From 2f70286503e983900d2d107b011ed4cbbffd97aa Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 4 Oct 2023 21:53:46 +0200 Subject: [PATCH 10/28] log improvement --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index f4bc53a09717..3cd1a2e94d4b 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -45,6 +45,19 @@ static bool IsSoundFile(const std::string& filename) return extensions.find(extension) != extensions.end(); } +// Filtered files +static bool IsVideoFile(const std::string& filename) +{ + std::string extension; + SplitPath(filename, nullptr, nullptr, &extension); + Common::ToLower(&extension); + + static const std::unordered_set extensions = { + ".thp", // 1Wii/Game Cube Video File + }; + + return extensions.find(extension) != extensions.end(); +} FileLogger::FileLogger() = default; @@ -79,9 +92,16 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string size_string = Common::ThousandSeparate(file_info->GetSize() / 1000, 7); const std::string path = file_info->GetPath(); - const std::string log_string = fmt::format("{} kB {}", size_string, path); + const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); + + //if (path.contains("BGM") || path.contains("bgm")) + // return; + + //TODO just use colors instead of this horrid hack if (IsSoundFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string); + else if (IsVideoFile(path)) + NOTICE_LOG_FMT(FILEMON, "{}", log_string); else WARN_LOG_FMT(FILEMON, "{}", log_string); From 4467449003ddc2a1ba6b082fbb22f907e0b530cc Mon Sep 17 00:00:00 2001 From: PTwr Date: Thu, 5 Oct 2023 04:50:35 +0200 Subject: [PATCH 11/28] Subtitles integrated back into Dolphin --- Source/Core/Common/CommonPaths.h | 2 +- Source/Core/Common/FileUtil.cpp | 2 +- Source/Core/Common/FileUtil.h | 2 +- Source/Core/Common/Logging/Log.h | 1 + Source/Core/Common/Logging/LogManager.cpp | 1 + Source/Core/Core/Boot/Boot.cpp | 3 - Source/Core/Core/ConfigManager.cpp | 3 +- Source/Core/Core/Core.cpp | 3 - Source/Core/Core/HW/DVD/DVDThread.cpp | 4 +- Source/Core/DolphinLib.vcxproj | 10 +- Source/Core/Plugins/PluginInterface.h | 35 --- Source/Core/Plugins/PluginLoader.cpp | 139 ---------- Source/Core/Plugins/PluginLoader.h | 17 -- Source/Core/Subtitles/Subtitles.cpp | 293 ++++++++++++++++++++++ Source/Core/Subtitles/Subtitles.h | 10 + Source/Core/Subtitles/TranslationEntry.h | 75 ++++++ Source/Core/UICommon/UICommon.cpp | 1 + 17 files changed, 395 insertions(+), 206 deletions(-) delete mode 100644 Source/Core/Plugins/PluginInterface.h delete mode 100644 Source/Core/Plugins/PluginLoader.cpp delete mode 100644 Source/Core/Plugins/PluginLoader.h create mode 100644 Source/Core/Subtitles/Subtitles.cpp create mode 100644 Source/Core/Subtitles/Subtitles.h create mode 100644 Source/Core/Subtitles/TranslationEntry.h diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 8ce89b8b9dc6..6783c91a29f9 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -94,7 +94,7 @@ #define DYNAMICINPUT_DIR "DynamicInputTextures" #define GRAPHICSMOD_DIR "GraphicMods" #define WIISDSYNC_DIR "WiiSDSync" -#define PLUGIN_DIR "Plugins" +#define SUBTITLE_DIR "Subtitles" // This one is only used to remove it if it was present #define SHADERCACHE_LEGACY_DIR "ShaderCache" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index f7e9a9fa952e..61f1334ebbac 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -864,7 +864,7 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[D_WFSROOT_IDX] = s_user_paths[D_USER_IDX] + WFSROOT_DIR DIR_SEP; s_user_paths[D_BACKUP_IDX] = s_user_paths[D_USER_IDX] + BACKUP_DIR DIR_SEP; s_user_paths[D_RESOURCEPACK_IDX] = s_user_paths[D_USER_IDX] + RESOURCEPACK_DIR DIR_SEP; - s_user_paths[D_PLUGINS_IDX] = s_user_paths[D_USER_IDX] + PLUGIN_DIR DIR_SEP; + s_user_paths[D_SUBTITLES_IDX] = s_user_paths[D_LOAD_IDX] + SUBTITLE_DIR DIR_SEP; s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP; s_user_paths[D_GRAPHICSMOD_IDX] = s_user_paths[D_LOAD_IDX] + GRAPHICSMOD_DIR DIR_SEP; s_user_paths[D_WIISDCARDSYNCFOLDER_IDX] = s_user_paths[D_LOAD_IDX] + WIISDSYNC_DIR DIR_SEP; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index c217c69b2a18..15302ed95723 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -62,7 +62,7 @@ enum D_WFSROOT_IDX, D_BACKUP_IDX, D_RESOURCEPACK_IDX, - D_PLUGINS_IDX, + D_SUBTITLES_IDX, D_DYNAMICINPUT_IDX, D_GRAPHICSMOD_IDX, D_GBAUSER_IDX, diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index 29a65a972df1..2fa2fc2cec2f 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -30,6 +30,7 @@ enum class LogType : int DYNA_REC, EXPANSIONINTERFACE, FILEMON, + SUBTITLES, FRAMEDUMP, GDB_STUB, GPFIFO, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index 80e7db895e1e..794be38c2c2f 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -114,6 +114,7 @@ LogManager::LogManager() m_log[LogType::DYNA_REC] = {"JIT", "JIT Dynamic Recompiler"}; m_log[LogType::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"}; m_log[LogType::FILEMON] = {"FileMon", "File Monitor"}; + m_log[LogType::SUBTITLES] = {"Subtitles", "Subtitles"}; m_log[LogType::FRAMEDUMP] = {"FRAMEDUMP", "FrameDump"}; m_log[LogType::GDB_STUB] = {"GDB_STUB", "GDB Stub"}; m_log[LogType::GPFIFO] = {"GP", "GatherPipe FIFO"}; diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 3e99de84525a..8c94fbe5a74d 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -56,7 +56,6 @@ #include "DiscIO/RiivolutionPatcher.h" #include "DiscIO/VolumeDisc.h" #include "DiscIO/VolumeWad.h" -#include static std::vector ReadM3UFile(const std::string& m3u_path, const std::string& folder_path) @@ -522,8 +521,6 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard, return false; SConfig::OnNewTitleLoad(guard); - Plugins::Init(); - Plugins::OnGameLoad(disc.path.c_str()); return true; } diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index b44e71379146..0c49befc179f 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -60,7 +60,7 @@ #include "DiscIO/Volume.h" #include "DiscIO/VolumeWad.h" -#include "Plugins/PluginLoader.h" +#include SConfig* SConfig::m_Instance; @@ -211,6 +211,7 @@ void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard) PatchEngine::Reload(); HiresTexture::Update(); WC24PatchEngine::Reload(); + Subtitles::Reload(); } void SConfig::LoadDefaults() diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 3879ddbbfc56..5396c8439b27 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -97,7 +97,6 @@ #ifdef ANDROID #include "jni/AndroidCommon/IDCache.h" #endif -#include namespace Core { @@ -292,8 +291,6 @@ void Stop() // - Hammertime! if (GetState() == State::Stopping || GetState() == State::Uninitialized) return; - Plugins::Cleanup(); - #ifdef USE_RETRO_ACHIEVEMENTS AchievementManager::GetInstance()->CloseGame(); #endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 9e04578b664c..96f4cf7fa880 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -33,7 +33,7 @@ #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" -#include +#include namespace DVD { @@ -344,7 +344,7 @@ void DVDThread::DVDThreadMain() while (m_request_queue.Pop(request)) { m_file_logger.Log(*m_disc, request.partition, request.dvd_offset); - Plugins::OnFileAccess(*m_disc, request.partition, request.dvd_offset); + Subtitles::OnFileAccess(*m_disc, request.partition, request.dvd_offset); std::vector buffer(request.length); if (!m_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index b4448d3e5bd1..1abe928b9497 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,12 +70,16 @@ + + + + + + - - - + diff --git a/Source/Core/Plugins/PluginInterface.h b/Source/Core/Plugins/PluginInterface.h deleted file mode 100644 index de465f8be380..000000000000 --- a/Source/Core/Plugins/PluginInterface.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include -#include -#include -#include "picojson.h" - -namespace Plugins -{ -class IPluginInterface_Events -{ -public: - IPluginInterface_Events(){}; - virtual ~IPluginInterface_Events(){}; - virtual void OnGameLoad(const char* gamePath) = 0; - virtual void OnGameClose(const char* gamePath) = 0; - virtual void OnFileAccess(const char* path) = 0; -}; - -class IAPI -{ -public: - IAPI(){}; - virtual ~IAPI(){}; - virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, - bool centered, bool reversed, const char* name) = 0; - virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, - bool preventDuplicate, float scale) = 0; - virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, - const char* messageStack, bool preventDuplicate, float scale) = 0; - virtual const char* GetGameId() = 0; -}; - - -typedef IPluginInterface_Events*(__stdcall* GetPluginInterface_Events)(IAPI* api); -} // namespace Plugins diff --git a/Source/Core/Plugins/PluginLoader.cpp b/Source/Core/Plugins/PluginLoader.cpp deleted file mode 100644 index ff33d8e6281e..000000000000 --- a/Source/Core/Plugins/PluginLoader.cpp +++ /dev/null @@ -1,139 +0,0 @@ - -#include "PluginLoader.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "Common/FileUtil.h" -#include "PluginInterface.h" - -// TODO multiplatform -#include - -namespace Plugins -{ -std::vector EventPlugins; -std::vector DllHandles; - -class API : public IAPI -{ -public: - const char* gameId; - API() : IAPI() { gameId = SConfig::GetInstance().GetGameID().c_str(); } - - virtual void AddOSDMessageStack(float x_offset, float y_offset, OSD::MessageStackDirection dir, - bool centered, bool reversed, const char* name) override - { - auto stack = OSD::OSDMessageStack(x_offset, y_offset, dir, centered, reversed, std::move(name)); - OSD::AddMessageStack(stack); - } - virtual void AddMessage(const char* message, u32 ms, u32 argb, const char* messageStack, - bool preventDuplicate, float scale) override - { - OSD::AddMessage(message, ms, argb, messageStack == 0 ? "" : messageStack, preventDuplicate, - scale); - } - virtual void AddTypedMessage(OSD::MessageType type, char* message, u32 ms, u32 argb, - const char* messageStack, bool preventDuplicate, - float scale) override - { - OSD::AddTypedMessage(type, std::move(message), ms, argb, std::move(messageStack), - preventDuplicate, scale); - } - virtual const char* GetGameId() override - {; - return gameId; - } -}; -API* apiInstance = 0; - -// TODO get rom path insteead of getting disc as param -void Init() -{ - Cleanup(); - - apiInstance = new API(); - - auto dlldir = File::GetUserPath(D_PLUGINS_IDX); - - if (!std::filesystem::exists(dlldir)) - { - return; - } - for (const auto& entry : std::filesystem::directory_iterator(dlldir)) - { - if (entry.path().extension() == ".dll") - { - HMODULE dllHandle = LoadLibrary(entry.path().c_str()); - DllHandles.push_back(dllHandle); - - auto interfaceGetter = - (GetPluginInterface_Events)GetProcAddress(dllHandle, "GetPluginInterface_Events"); - if (interfaceGetter) - { - auto pluginInterface = interfaceGetter(apiInstance); - if (pluginInterface != 0) - { - EventPlugins.push_back(pluginInterface); - } - } - } - } -} -void Cleanup() -{ - EventPlugins.clear(); - - for (auto dllHandle : DllHandles) - { - FreeLibrary(dllHandle); - } - DllHandles.clear(); - - if (apiInstance != 0) - { - delete apiInstance; - apiInstance = 0; - } -} - -void OnGameLoad(const char* path) -{ - for (auto i : EventPlugins) - { - i->OnGameLoad(path); - } -} -void OnGameClose(const char* path) -{ - for (auto i : EventPlugins) - { - i->OnGameClose(path); - } -} - -void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) -{ - if (!EventPlugins.empty()) - { - const DiscIO::FileSystem* file_system = volume.GetFileSystem(partition); - if (!file_system) - { - // ignore invalid calls - return; - } - - const std::unique_ptr file_info = file_system->FindFileInfo(offset); - const char* path = file_info->GetPath().c_str(); - - for (auto i : EventPlugins) - { - i->OnFileAccess(path); - } - } -} -} // namespace Plugins diff --git a/Source/Core/Plugins/PluginLoader.h b/Source/Core/Plugins/PluginLoader.h deleted file mode 100644 index 1273630af6ca..000000000000 --- a/Source/Core/Plugins/PluginLoader.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -namespace Plugins -{ -/// -/// Performs Cleanup and Loads plugin libraries -/// -void Init(); -/// -/// Frees loaded libraries -/// -void Cleanup(); - -void OnGameLoad(const char* path); -void OnGameClose(const char* path); -void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset); -}; // namespace Plugins diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp new file mode 100644 index 000000000000..b5b712741c41 --- /dev/null +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -0,0 +1,293 @@ +#include "Subtitles.h" +#include "TranslationEntry.h" +#include "picojson.h" + +#include +#include "Core/ConfigManager.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace Subtitles +{ +bool _messageStacksInitialized = false; +bool _subtitlesInitialized = false; +std::map Translations; + +void Info(std::string msg) +{ + OSD::AddMessage(msg, 2000, OSD::Color::GREEN); + INFO_LOG_FMT(SUBTITLES, "{}", msg); +} +void Error(std::string err) +{ + OSD::AddMessage(err, 2000, OSD::Color::RED); + ERROR_LOG_FMT(SUBTITLES, "{}", err); +} + +void DeserializeSubtitlesJson(std::string json) +{ + if (json == "") + { + return; + } + + picojson::value v; + std::string err = picojson::parse(v, json); + if (!err.empty()) + { + Error("Subtitle JSON Error: " + err); + return; + } + + if (!v.is()) + { + Error("Subtitle JSON Error: Not an array"); + return; + } + + auto arr = v.get(); + for (auto item : arr) + { + auto FileName = item.get("FileName"); + auto Translation = item.get("Translation"); + auto Miliseconds = item.get("Miliseconds"); + auto Color = item.get("Color"); + auto Enabled = item.get("Enabled"); + auto AllowDuplicate = item.get("AllowDuplicate"); + auto Scale = item.get("Scale"); + auto Offset = item.get("Offset"); + auto OffsetEnd = item.get("OffsetEnd"); + auto DisplayOnTop = item.get("DisplayOnTop"); + + if (!FileName.is() || !Translation.is()) + { + continue; + } + + auto tl = TranslationEntry(FileName.to_str(), Translation.to_str(), + Miliseconds.is() ? Miliseconds.get() : + OSD::Duration::SHORT, + Color.is() ? Color.get() : OSD::Color::CYAN, + Enabled.is() ? Enabled.get() : true, + AllowDuplicate.is() ? AllowDuplicate.get() : false, + Scale.is() ? Scale.get() : 1, + Offset.is() ? Offset.get() : 0, + OffsetEnd.is() ? OffsetEnd.get() : 0, + DisplayOnTop.is() ? DisplayOnTop.get() : false); + + if (tl.Enabled) + { + //TranslationEntryGroup group = Translations.find(tl.Filename)->second; + //group.Add(tl); + Translations[tl.Filename].Add(tl); + } + } +} + +void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, std::string filter) +{ + for (const auto& child : folder.children) + { + if (child.isDirectory) + { + RecursivelyReadTranslationJsons(child, filter); + } + else + { + auto filepath = child.physicalName; + std::string extension; + SplitPath(filepath, nullptr, nullptr, &extension); + Common::ToLower(&extension); + + if (extension == filter) + { + Info("Reading translations from: " + filepath); + + std::string json; + File::ReadFileToString(filepath, json); + + DeserializeSubtitlesJson(json); + } + } + } +} + +void IniitalizeOSDMessageStacks() +{ + if (_messageStacksInitialized) + return; + + auto bottomstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, + BottomOSDStackName); + OSD::AddMessageStack(bottomstack); + auto topstack = + OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, TopOSDStackName); + OSD::AddMessageStack(topstack); + _messageStacksInitialized = true; +} + +void LoadSubtitlesForGame(std::string gameId) +{ + _subtitlesInitialized = false; + Translations.clear(); + + auto subtitleDir = File::GetUserPath(D_SUBTITLES_IDX) + gameId; + + auto fileEnumerator = File::ScanDirectoryTree(subtitleDir, true); + RecursivelyReadTranslationJsons(fileEnumerator, ".json"); + + if (Translations.empty()) + return; + + // ensure stuff is sorted, can't trust crap people will do in text files :) + std::for_each(Translations.begin(), Translations.end(), + [](std::pair& t) { t.second.Sort(); }); + + IniitalizeOSDMessageStacks(); + + _subtitlesInitialized = true; +} + +void Reload() +{ + std::string game_id = SConfig::GetInstance().GetGameID(); + LoadSubtitlesForGame(game_id); + + /* auto group = Translations["str/0000.thp"]; + + INFO_LOG_FMT(SUBTITLES, "0 = {} {}", group.translationLines[0].Offset, group.translationLines[0].Text); + INFO_LOG_FMT(SUBTITLES, "1 = {} {}", group.translationLines[1].Offset, group.translationLines[1].Text); + INFO_LOG_FMT(SUBTITLES, "2 = {} {}", group.translationLines[2].Offset, group.translationLines[2].Text); + INFO_LOG_FMT(SUBTITLES, "3 = {} {}", group.translationLines[3].Offset, group.translationLines[3].Text); + + auto tl = group.GetTLForRelativeOffset(3025152); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "3025152 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "1222025153 = is zero {}", tl == 0); + } + tl = group.GetTLForRelativeOffset(4025152); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "4025152 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "4025152 = is zero {}", tl == 0); + } + tl = group.GetTLForRelativeOffset(7025152); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "7025152 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "7025152 = is zero {}", tl == 0); + } + tl = group.GetTLForRelativeOffset(9025152); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "9025152 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "9025152 = is zero {}", tl == 0); + } + tl = group.GetTLForRelativeOffset(9025153); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "9025153 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "9025153 = is zero {}", tl == 0); + } + tl = group.GetTLForRelativeOffset(1222025153); + if (tl != 0) + { + INFO_LOG_FMT(SUBTITLES, "1222025153 = {} {}", tl->Offset, tl->Text); + } + else + { + INFO_LOG_FMT(SUBTITLES, "1222025153 = is zero {}", tl == 0); + }*/ +} + +void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) +{ + //return; + //auto group = Translations["str/0000.thp"]; + ////INFO_LOG_FMT(SUBTITLES, "0 = {} {}", group.translationLines[0].Offset, group.translationLines[0].Text); + ////INFO_LOG_FMT(SUBTITLES, "1 = {} {}", group.translationLines[1].Offset, group.translationLines[1].Text); + ////INFO_LOG_FMT(SUBTITLES, "2 = {} {}", group.translationLines[2].Offset, group.translationLines[2].Text); + ////INFO_LOG_FMT(SUBTITLES, "3 = {} {}", group.translationLines[3].Offset, group.translationLines[3].Text); + + //auto tl = group.GetTLForRelativeOffset(3025152); + //INFO_LOG_FMT(SUBTITLES, "0 = {} {}", tl->Offset, tl->Text); + + //tl = group.GetTLForRelativeOffset(4025152); + //INFO_LOG_FMT(SUBTITLES, "1 = {} {}", tl->Offset, tl->Text); + + //tl = group.GetTLForRelativeOffset(7025152); + //INFO_LOG_FMT(SUBTITLES, "2 = {} {}", tl->Offset, tl->Text); + + //tl = group.GetTLForRelativeOffset(9025152); + //INFO_LOG_FMT(SUBTITLES, "5 = {} {}", tl->Offset, tl->Text); + + //tl = group.GetTLForRelativeOffset(9025153); + //INFO_LOG_FMT(SUBTITLES, "6 = {} {}", tl->Offset, tl->Text); + + //tl = group.GetTLForRelativeOffset(1222025153); + //INFO_LOG_FMT(SUBTITLES, "7 = is zero {}", tl==0); + + //return; + if (!_subtitlesInitialized) + return; + + const DiscIO::FileSystem* file_system = volume.GetFileSystem(partition); + if (!file_system) + return; + + const std::unique_ptr file_info = file_system->FindFileInfo(offset); + + if (!file_info) + return; + + std::string path = file_info->GetPath(); + + auto relativeOffset = offset - file_info->GetOffset(); + INFO_LOG_FMT(SUBTITLES, "File {} File Offset {} Offset {} Relative offset {}", path, + file_info->GetOffset(), + offset, relativeOffset); + + if (Translations.count(path) == 0) + return; + + auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); + INFO_LOG_FMT(SUBTITLES, "{} Lines count {}", tl!=0, Translations[path].translationLines.size()); + + + if (!tl) + return; + + + //auto msg = fmt::format("offset {}", tl->Offset); + //OSD::AddMessage(msg, 5000, OSD::Color::GREEN, + // BottomOSDStackName, true, + // 1); + + OSD::AddMessage(tl->Text, tl->Miliseconds, tl->Color, + tl->DisplayOnTop ? TopOSDStackName : BottomOSDStackName, !tl->AllowDuplicate, + tl->Scale); +} +} // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.h b/Source/Core/Subtitles/Subtitles.h new file mode 100644 index 000000000000..812e6defe072 --- /dev/null +++ b/Source/Core/Subtitles/Subtitles.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +namespace Subtitles +{ +const std::string BottomOSDStackName = "subtitles-bottom"; +const std::string TopOSDStackName = "subtitles-top"; +void Reload(); +void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset); +} // namespace Subtitles diff --git a/Source/Core/Subtitles/TranslationEntry.h b/Source/Core/Subtitles/TranslationEntry.h new file mode 100644 index 000000000000..66327affee75 --- /dev/null +++ b/Source/Core/Subtitles/TranslationEntry.h @@ -0,0 +1,75 @@ +#pragma once +#include +#include "Common/CommonTypes.h" +namespace Subtitles +{ +struct TranslationEntry +{ + std::string Filename; + std::string Text; + u32 Miliseconds; + u32 Color; + bool Enabled; + bool AllowDuplicate; + float Scale; + u32 Offset; + u32 OffsetEnd; + bool DisplayOnTop; + +public: + TranslationEntry() + : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), + Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) + { + } + TranslationEntry(std::string filename, std::string text, u32 miliseconds, u32 color, bool enabled, + bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) + : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), + AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), + DisplayOnTop(displayOnTop) + { + } + bool IsOffset() { return Offset > 0; } +}; + +struct TranslationEntryGroup +{ + std::vector translationLines; + + // Ensure lines are sorted for performance when querying + void Sort() + { + std::sort(translationLines.begin(), translationLines.end(), + [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); + } + TranslationEntry* GetTLForRelativeOffset(u32 offset) + { + if (translationLines.empty()) + { + return 0; + } + + if (!translationLines[translationLines.size() - 1].IsOffset()) + { + return &translationLines[translationLines.size() - 1]; + } + for (auto i = 0; i < translationLines.size(); i++) + { + if (offset >= translationLines[i].Offset) + { + if (translationLines[i].OffsetEnd == 0 || translationLines[i].OffsetEnd >= offset) + { + return &translationLines[i]; + } + else + { + return 0; + } + } + } + + return 0; + } + void Add(TranslationEntry tl) { translationLines.push_back(tl); } +}; +} // namespace Subtitles diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 9b31ad5bf9cc..7c4f5142d088 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -84,6 +84,7 @@ static void CreateLoadPath(std::string path) File::CreateFullPath(File::GetUserPath(D_RIIVOLUTION_IDX)); File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX)); File::CreateFullPath(File::GetUserPath(D_DYNAMICINPUT_IDX)); + File::CreateFullPath(File::GetUserPath(D_SUBTITLES_IDX)); } static void CreateResourcePackPath(std::string path) From cf6264ae5104b84dffdc3d2141ea551b7b5a198e Mon Sep 17 00:00:00 2001 From: PTwr Date: Thu, 5 Oct 2023 04:53:51 +0200 Subject: [PATCH 12/28] cleanup --- Source/Core/DolphinLib.vcxproj | 4 -- Source/Core/Subtitles/Subtitles.cpp | 103 +--------------------------- 2 files changed, 1 insertion(+), 106 deletions(-) diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 1abe928b9497..bb26de052aa4 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,10 +70,6 @@ - - - - diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index b5b712741c41..bae8a79a3c93 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -84,8 +84,6 @@ void DeserializeSubtitlesJson(std::string json) if (tl.Enabled) { - //TranslationEntryGroup group = Translations.find(tl.Filename)->second; - //group.Add(tl); Translations[tl.Filename].Add(tl); } } @@ -159,98 +157,10 @@ void Reload() { std::string game_id = SConfig::GetInstance().GetGameID(); LoadSubtitlesForGame(game_id); - - /* auto group = Translations["str/0000.thp"]; - - INFO_LOG_FMT(SUBTITLES, "0 = {} {}", group.translationLines[0].Offset, group.translationLines[0].Text); - INFO_LOG_FMT(SUBTITLES, "1 = {} {}", group.translationLines[1].Offset, group.translationLines[1].Text); - INFO_LOG_FMT(SUBTITLES, "2 = {} {}", group.translationLines[2].Offset, group.translationLines[2].Text); - INFO_LOG_FMT(SUBTITLES, "3 = {} {}", group.translationLines[3].Offset, group.translationLines[3].Text); - - auto tl = group.GetTLForRelativeOffset(3025152); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "3025152 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "1222025153 = is zero {}", tl == 0); - } - tl = group.GetTLForRelativeOffset(4025152); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "4025152 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "4025152 = is zero {}", tl == 0); - } - tl = group.GetTLForRelativeOffset(7025152); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "7025152 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "7025152 = is zero {}", tl == 0); - } - tl = group.GetTLForRelativeOffset(9025152); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "9025152 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "9025152 = is zero {}", tl == 0); - } - tl = group.GetTLForRelativeOffset(9025153); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "9025153 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "9025153 = is zero {}", tl == 0); - } - tl = group.GetTLForRelativeOffset(1222025153); - if (tl != 0) - { - INFO_LOG_FMT(SUBTITLES, "1222025153 = {} {}", tl->Offset, tl->Text); - } - else - { - INFO_LOG_FMT(SUBTITLES, "1222025153 = is zero {}", tl == 0); - }*/ } void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) { - //return; - //auto group = Translations["str/0000.thp"]; - ////INFO_LOG_FMT(SUBTITLES, "0 = {} {}", group.translationLines[0].Offset, group.translationLines[0].Text); - ////INFO_LOG_FMT(SUBTITLES, "1 = {} {}", group.translationLines[1].Offset, group.translationLines[1].Text); - ////INFO_LOG_FMT(SUBTITLES, "2 = {} {}", group.translationLines[2].Offset, group.translationLines[2].Text); - ////INFO_LOG_FMT(SUBTITLES, "3 = {} {}", group.translationLines[3].Offset, group.translationLines[3].Text); - - //auto tl = group.GetTLForRelativeOffset(3025152); - //INFO_LOG_FMT(SUBTITLES, "0 = {} {}", tl->Offset, tl->Text); - - //tl = group.GetTLForRelativeOffset(4025152); - //INFO_LOG_FMT(SUBTITLES, "1 = {} {}", tl->Offset, tl->Text); - - //tl = group.GetTLForRelativeOffset(7025152); - //INFO_LOG_FMT(SUBTITLES, "2 = {} {}", tl->Offset, tl->Text); - - //tl = group.GetTLForRelativeOffset(9025152); - //INFO_LOG_FMT(SUBTITLES, "5 = {} {}", tl->Offset, tl->Text); - - //tl = group.GetTLForRelativeOffset(9025153); - //INFO_LOG_FMT(SUBTITLES, "6 = {} {}", tl->Offset, tl->Text); - - //tl = group.GetTLForRelativeOffset(1222025153); - //INFO_LOG_FMT(SUBTITLES, "7 = is zero {}", tl==0); - - //return; if (!_subtitlesInitialized) return; @@ -266,26 +176,15 @@ void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partiti std::string path = file_info->GetPath(); auto relativeOffset = offset - file_info->GetOffset(); - INFO_LOG_FMT(SUBTITLES, "File {} File Offset {} Offset {} Relative offset {}", path, - file_info->GetOffset(), - offset, relativeOffset); if (Translations.count(path) == 0) return; - auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); - INFO_LOG_FMT(SUBTITLES, "{} Lines count {}", tl!=0, Translations[path].translationLines.size()); - + auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); if (!tl) return; - - //auto msg = fmt::format("offset {}", tl->Offset); - //OSD::AddMessage(msg, 5000, OSD::Color::GREEN, - // BottomOSDStackName, true, - // 1); - OSD::AddMessage(tl->Text, tl->Miliseconds, tl->Color, tl->DisplayOnTop ? TopOSDStackName : BottomOSDStackName, !tl->AllowDuplicate, tl->Scale); From 8fb42b0ee7d767f59156b434b92aa67127366eab Mon Sep 17 00:00:00 2001 From: PTwr Date: Fri, 6 Oct 2023 10:35:12 +0200 Subject: [PATCH 13/28] html colors, hex colors, and shitty decimal colors --- Source/Core/DolphinLib.vcxproj | 1 + Source/Core/Subtitles/Subtitles.cpp | 59 +++++++-- Source/Core/Subtitles/TranslationEntry.h | 2 +- Source/Core/Subtitles/WebColors.h | 151 +++++++++++++++++++++++ 4 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 Source/Core/Subtitles/WebColors.h diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index bb26de052aa4..b2a4eebad93f 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,6 +70,7 @@ + diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index bae8a79a3c93..b9341caf8991 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -1,5 +1,6 @@ #include "Subtitles.h" #include "TranslationEntry.h" +#include "WebColors.h" #include "picojson.h" #include @@ -71,11 +72,55 @@ void DeserializeSubtitlesJson(std::string json) continue; } + u32 color = OSD::Color::CYAN; + + if (Color.is()) + color = Color.get(); + else + { + auto str = Color.to_str(); + Common::ToLower(&str); + + if (str.starts_with("0x")) + //hex string + color = std::stoul(str, nullptr, 16); + else if (WebColors.count(str) == 1) + //html color name + color = WebColors[str]; + else + //color noted with 3 or 4 base10 numers (rgb/argb) + try //string parsing suucks + { + // try parse (a)rgb space delimited color + u32 a, r, g, b; + auto parts = SplitString(str, ' '); + if (parts.size() == 4) + { + a = std::stoul(parts[0], nullptr, 10); + r = std::stoul(parts[1], nullptr, 10); + g = std::stoul(parts[2], nullptr, 10); + b = std::stoul(parts[3], nullptr, 10); + color = a << 24 | r << 16 | g << 8 | b; + } + else if (parts.size() == 3) + { + a = 255; + r = std::stoul(parts[0], nullptr, 10); + g = std::stoul(parts[1], nullptr, 10); + b = std::stoul(parts[2], nullptr, 10); + color = a << 24 | r << 16 | g << 8 | b; + } + } + catch (std::exception x) + { + Error("Invalid color: " + str); + } + } + auto tl = TranslationEntry(FileName.to_str(), Translation.to_str(), Miliseconds.is() ? Miliseconds.get() : OSD::Duration::SHORT, - Color.is() ? Color.get() : OSD::Color::CYAN, - Enabled.is() ? Enabled.get() : true, + color, Enabled.is() ? Enabled.get() : true, AllowDuplicate.is() ? AllowDuplicate.get() : false, Scale.is() ? Scale.get() : 1, Offset.is() ? Offset.get() : 0, @@ -125,8 +170,8 @@ void IniitalizeOSDMessageStacks() auto bottomstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, BottomOSDStackName); OSD::AddMessageStack(bottomstack); - auto topstack = - OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, TopOSDStackName); + auto topstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, + TopOSDStackName); OSD::AddMessageStack(topstack); _messageStacksInitialized = true; } @@ -178,10 +223,10 @@ void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partiti auto relativeOffset = offset - file_info->GetOffset(); if (Translations.count(path) == 0) - return; + return; + + auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); - auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); - if (!tl) return; diff --git a/Source/Core/Subtitles/TranslationEntry.h b/Source/Core/Subtitles/TranslationEntry.h index 66327affee75..47f6600b41c9 100644 --- a/Source/Core/Subtitles/TranslationEntry.h +++ b/Source/Core/Subtitles/TranslationEntry.h @@ -1,6 +1,6 @@ #pragma once #include -#include "Common/CommonTypes.h" +#include namespace Subtitles { struct TranslationEntry diff --git a/Source/Core/Subtitles/WebColors.h b/Source/Core/Subtitles/WebColors.h new file mode 100644 index 000000000000..ff81b4b8aca6 --- /dev/null +++ b/Source/Core/Subtitles/WebColors.h @@ -0,0 +1,151 @@ +#pragma once +#include +#include +#include + +namespace Subtitles +{ +// web colors in hex form with Alpha = 255 +std::map WebColors = { + {"mediumvioletred", 0xFFC71585}, + {"deeppink", 0xFFFF1493}, + {"palevioletred", 0xFFDB7093}, + {"hotpink", 0xFFFF69B4}, + {"lightpink", 0xFFFFB6C1}, + {"pink", 0xFFFFC0CB}, + {"darkred", 0xFF8B0000}, + {"red", 0xFFFF0000}, + {"firebrick", 0xFFB22222}, + {"crimson", 0xFFDC143C}, + {"indianred", 0xFFCD5C5C}, + {"lightcoral", 0xFFF08080}, + {"salmon", 0xFFFA8072}, + {"darksalmon", 0xFFE9967A}, + {"lightsalmon", 0xFFFFA07A}, + {"orangered", 0xFFFF4500}, + {"tomato", 0xFFFF6347}, + {"darkorange", 0xFFFF8C00}, + {"coral", 0xFFFF7F50}, + {"orange", 0xFFFFA500}, + {"darkkhaki", 0xFFBDB76B}, + {"gold", 0xFFFFD700}, + {"khaki", 0xFFF0E68C}, + {"peachpuff", 0xFFFFDAB9}, + {"yellow", 0xFFFFFF00}, + {"palegoldenrod", 0xFFEEE8AA}, + {"moccasin", 0xFFFFE4B5}, + {"papayawhip", 0xFFFFEFD5}, + {"lightgoldenrodyellow", 0xFFFAFAD2}, + {"lemonchiffon", 0xFFFFFACD}, + {"lightyellow", 0xFFFFFFE0}, + {"maroon", 0xFF800000}, + {"brown", 0xFFA52A2A}, + {"saddlebrown", 0xFF8B4513}, + {"sienna", 0xFFA0522D}, + {"chocolate", 0xFFD2691E}, + {"darkgoldenrod", 0xFFB8860B}, + {"peru", 0xFFCD853F}, + {"rosybrown", 0xFFBC8F8F}, + {"goldenrod", 0xFFDAA520}, + {"sandybrown", 0xFFF4A460}, + {"tan", 0xFFD2B48C}, + {"burlywood", 0xFFDEB887}, + {"wheat", 0xFFF5DEB3}, + {"navajowhite", 0xFFFFDEAD}, + {"bisque", 0xFFFFE4C4}, + {"blanchedalmond", 0xFFFFEBCD}, + {"cornsilk", 0xFFFFF8DC}, + {"indigo", 0xFF4B0082}, + {"purple", 0xFF800080}, + {"darkmagenta", 0xFF8B008B}, + {"darkviolet", 0xFF9400D3}, + {"darkslateblue", 0xFF483D8B}, + {"blueviolet", 0xFF8A2BE2}, + {"darkorchid", 0xFF9932CC}, + {"fuchsia", 0xFFFF00FF}, + {"magenta", 0xFFFF00FF}, + {"slateblue", 0xFF6A5ACD}, + {"mediumslateblue", 0xFF7B68EE}, + {"mediumorchid", 0xFFBA55D3}, + {"mediumpurple", 0xFF9370DB}, + {"orchid", 0xFFDA70D6}, + {"violet", 0xFFEE82EE}, + {"plum", 0xFFDDA0DD}, + {"thistle", 0xFFD8BFD8}, + {"lavender", 0xFFE6E6FA}, + {"midnightblue", 0xFF191970}, + {"navy", 0xFF80}, + {"darkblue", 0xFF00008B}, + {"mediumblue", 0xFF0000CD}, + {"blue", 0xFF0000FF}, + {"royalblue", 0xFF41690}, + {"steelblue", 0xFF4682B4}, + {"dodgerblue", 0xFF1E90FF}, + {"deepskyblue", 0xFF00BFFF}, + {"cornflowerblue", 0xFF6495ED}, + {"skyblue", 0xFF87CEEB}, + {"lightskyblue", 0xFF87CEFA}, + {"lightsteelblue", 0xFFB0C4DE}, + {"lightblue", 0xFFADD8E6}, + {"powderblue", 0xFFB0E0E6}, + {"teal", 0xFF8080}, + {"darkcyan", 0xFF008B8B}, + {"lightseagreen", 0xFF20B2AA}, + {"cadetblue", 0xFF5F9EA0}, + {"darkturquoise", 0xFF00CED1}, + {"mediumturquoise", 0xFF48D1CC}, + {"turquoise", 0xFF40E0D0}, + {"aqua", 0xFF00FFFF}, + {"cyan", 0xFF00FFFF}, + {"aquamarine", 0xFF7FFFD4}, + {"paleturquoise", 0xFFAFEEEE}, + {"lightcyan", 0xFFE0FFFF}, + {"darkgreen", 0xFF6400}, + {"green", 0xFF8000}, + {"darkolivegreen", 0xFF556B2F}, + {"forestgreen", 0xFF228B22}, + {"seagreen", 0xFF2E8B57}, + {"olive", 0xFF808000}, + {"olivedrab", 0xFF6B8E23}, + {"mediumseagreen", 0xFF3CB371}, + {"limegreen", 0xFF32CD32}, + {"lime", 0xFF00FF00}, + {"springgreen", 0xFF00FF7F}, + {"mediumspringgreen", 0xFF00FA9A}, + {"darkseagreen", 0xFF8FBC8F}, + {"mediumaquamarine", 0xFF66CDAA}, + {"yellowgreen", 0xFF9ACD32}, + {"lawngreen", 0xFF7CFC00}, + {"chartreuse", 0xFF7FFF00}, + {"lightgreen", 0xFF90EE90}, + {"greenyellow", 0xFFADFF2F}, + {"palegreen", 0xFF98FB98}, + {"mistyrose", 0xFFFFE4E1}, + {"antiquewhite", 0xFFFAEBD7}, + {"linen", 0xFFFAF0E6}, + {"beige", 0xFFF5F5DC}, + {"whitesmoke", 0xFFF5F5F5}, + {"lavenderblush", 0xFFFFF0F5}, + {"oldlace", 0xFFFDF5E6}, + {"aliceblue", 0xFFF0F8FF}, + {"seashell", 0xFFFFF5EE}, + {"ghostwhite", 0xFFF8F8FF}, + {"honeydew", 0xFFF0FFF0}, + {"floralwhite", 0xFFFFFAF0}, + {"azure", 0xFFF0FFFF}, + {"mintcream", 0xFFF5FFFA}, + {"snow", 0xFFFFFAFA}, + {"ivory", 0xFFFFFFF0}, + {"white", 0xFFFFFFFF}, + {"black", 0xFF0}, + {"darkslategray", 0xFF2F4F4F}, + {"dimgray", 0xFF696969}, + {"slategray", 0xFF708090}, + {"gray", 0xFF808080}, + {"lightslategray", 0xFF778899}, + {"darkgray", 0xFFA9A9A9}, + {"silver", 0xFFC0C0C0}, + {"lightgray", 0xFFD3D3D3}, + {"gainsboro", 0xFFDCDCDC}, +}; +} // namespace Subtitles From 617ffa4b4cc534f49305dc07fb184256cab06f0c Mon Sep 17 00:00:00 2001 From: PTwr Date: Mon, 9 Oct 2023 16:53:56 +0100 Subject: [PATCH 14/28] fixed hex values --- Source/Core/Subtitles/WebColors.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Core/Subtitles/WebColors.h b/Source/Core/Subtitles/WebColors.h index ff81b4b8aca6..c82e0045769b 100644 --- a/Source/Core/Subtitles/WebColors.h +++ b/Source/Core/Subtitles/WebColors.h @@ -74,11 +74,11 @@ std::map WebColors = { {"thistle", 0xFFD8BFD8}, {"lavender", 0xFFE6E6FA}, {"midnightblue", 0xFF191970}, - {"navy", 0xFF80}, + {"navy", 0xFF000080}, {"darkblue", 0xFF00008B}, {"mediumblue", 0xFF0000CD}, {"blue", 0xFF0000FF}, - {"royalblue", 0xFF41690}, + {"royalblue", 0xFF041690}, {"steelblue", 0xFF4682B4}, {"dodgerblue", 0xFF1E90FF}, {"deepskyblue", 0xFF00BFFF}, @@ -88,7 +88,7 @@ std::map WebColors = { {"lightsteelblue", 0xFFB0C4DE}, {"lightblue", 0xFFADD8E6}, {"powderblue", 0xFFB0E0E6}, - {"teal", 0xFF8080}, + {"teal", 0xFF008080}, {"darkcyan", 0xFF008B8B}, {"lightseagreen", 0xFF20B2AA}, {"cadetblue", 0xFF5F9EA0}, @@ -100,8 +100,8 @@ std::map WebColors = { {"aquamarine", 0xFF7FFFD4}, {"paleturquoise", 0xFFAFEEEE}, {"lightcyan", 0xFFE0FFFF}, - {"darkgreen", 0xFF6400}, - {"green", 0xFF8000}, + {"darkgreen", 0xFF006400}, + {"green", 0xFF008000}, {"darkolivegreen", 0xFF556B2F}, {"forestgreen", 0xFF228B22}, {"seagreen", 0xFF2E8B57}, @@ -137,7 +137,7 @@ std::map WebColors = { {"snow", 0xFFFFFAFA}, {"ivory", 0xFFFFFFF0}, {"white", 0xFFFFFFFF}, - {"black", 0xFF0}, + {"black", 0xFF000000}, {"darkslategray", 0xFF2F4F4F}, {"dimgray", 0xFF696969}, {"slategray", 0xFF708090}, From ea77d09de6c4349d7600677b0df5e0e9744804ec Mon Sep 17 00:00:00 2001 From: PTwr Date: Tue, 10 Oct 2023 09:43:58 +0100 Subject: [PATCH 15/28] a little cleanup --- Source/Core/DolphinLib.vcxproj | 1 + Source/Core/Subtitles/Helpers.h | 69 +++++++++++++++++++++++ Source/Core/Subtitles/Subtitles.cpp | 66 +++------------------- Source/Core/Subtitles/TranslationEntry.h | 8 ++- Source/Core/VideoCommon/OnScreenDisplay.h | 4 +- 5 files changed, 86 insertions(+), 62 deletions(-) create mode 100644 Source/Core/Subtitles/Helpers.h diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index b2a4eebad93f..f18b1bf595fb 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,6 +70,7 @@ + diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h new file mode 100644 index 000000000000..51eb2a6a00c5 --- /dev/null +++ b/Source/Core/Subtitles/Helpers.h @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include +#include +#include +#include "WebColors.h" + +namespace Subtitles +{ + +void Info(std::string msg) +{ + OSD::AddMessage(msg, 2000, OSD::Color::GREEN); + INFO_LOG_FMT(SUBTITLES, "{}", msg); +} +void Error(std::string err) +{ + OSD::AddMessage(err, 2000, OSD::Color::RED); + ERROR_LOG_FMT(SUBTITLES, "{}", err); +} + +u32 TryParsecolor(picojson::value raw, u32 defaultColor) +{ + if (raw.is()) + return raw.get(); + else + { + auto str = raw.to_str(); + Common::ToLower(&str); + + if (str.starts_with("0x")) + // hex string + return std::stoul(str, nullptr, 16); + else if (WebColors.count(str) == 1) + // html color name + return WebColors[str]; + else + // color noted with 3 or 4 base10 numers (rgb/argb) + try // string parsing suucks + { + // try parse (a)rgb space delimited color + u32 a, r, g, b; + auto parts = SplitString(str, ' '); + if (parts.size() == 4) + { + a = std::stoul(parts[0], nullptr, 10); + r = std::stoul(parts[1], nullptr, 10); + g = std::stoul(parts[2], nullptr, 10); + b = std::stoul(parts[3], nullptr, 10); + return a << 24 | r << 16 | g << 8 | b; + } + else if (parts.size() == 3) + { + a = 255; + r = std::stoul(parts[0], nullptr, 10); + g = std::stoul(parts[1], nullptr, 10); + b = std::stoul(parts[2], nullptr, 10); + return a << 24 | r << 16 | g << 8 | b; + } + } + catch (std::exception x) + { + Error("Invalid color: " + str); + } + } + return defaultColor; +} +} // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index b9341caf8991..a053c63c9d40 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -1,13 +1,15 @@ #include "Subtitles.h" #include "TranslationEntry.h" #include "WebColors.h" -#include "picojson.h" +#include "Helpers.h" + +#include #include -#include "Core/ConfigManager.h" +#include -#include -#include +#include +#include #include #include @@ -21,17 +23,6 @@ bool _messageStacksInitialized = false; bool _subtitlesInitialized = false; std::map Translations; -void Info(std::string msg) -{ - OSD::AddMessage(msg, 2000, OSD::Color::GREEN); - INFO_LOG_FMT(SUBTITLES, "{}", msg); -} -void Error(std::string err) -{ - OSD::AddMessage(err, 2000, OSD::Color::RED); - ERROR_LOG_FMT(SUBTITLES, "{}", err); -} - void DeserializeSubtitlesJson(std::string json) { if (json == "") @@ -72,50 +63,7 @@ void DeserializeSubtitlesJson(std::string json) continue; } - u32 color = OSD::Color::CYAN; - - if (Color.is()) - color = Color.get(); - else - { - auto str = Color.to_str(); - Common::ToLower(&str); - - if (str.starts_with("0x")) - //hex string - color = std::stoul(str, nullptr, 16); - else if (WebColors.count(str) == 1) - //html color name - color = WebColors[str]; - else - //color noted with 3 or 4 base10 numers (rgb/argb) - try //string parsing suucks - { - // try parse (a)rgb space delimited color - u32 a, r, g, b; - auto parts = SplitString(str, ' '); - if (parts.size() == 4) - { - a = std::stoul(parts[0], nullptr, 10); - r = std::stoul(parts[1], nullptr, 10); - g = std::stoul(parts[2], nullptr, 10); - b = std::stoul(parts[3], nullptr, 10); - color = a << 24 | r << 16 | g << 8 | b; - } - else if (parts.size() == 3) - { - a = 255; - r = std::stoul(parts[0], nullptr, 10); - g = std::stoul(parts[1], nullptr, 10); - b = std::stoul(parts[2], nullptr, 10); - color = a << 24 | r << 16 | g << 8 | b; - } - } - catch (std::exception x) - { - Error("Invalid color: " + str); - } - } + u32 color = TryParsecolor(Color, OSD::Color::CYAN); auto tl = TranslationEntry(FileName.to_str(), Translation.to_str(), Miliseconds.is() ? Miliseconds.get() : diff --git a/Source/Core/Subtitles/TranslationEntry.h b/Source/Core/Subtitles/TranslationEntry.h index 47f6600b41c9..5adfcdf9456e 100644 --- a/Source/Core/Subtitles/TranslationEntry.h +++ b/Source/Core/Subtitles/TranslationEntry.h @@ -36,7 +36,7 @@ struct TranslationEntryGroup { std::vector translationLines; - // Ensure lines are sorted for performance when querying + // Ensure lines are sorted to simplify querying void Sort() { std::sort(translationLines.begin(), translationLines.end(), @@ -49,14 +49,20 @@ struct TranslationEntryGroup return 0; } + //if there is entry with offset=0 or offset=null if (!translationLines[translationLines.size() - 1].IsOffset()) { + //use it by default return &translationLines[translationLines.size() - 1]; } + + //from latest to earliest for (auto i = 0; i < translationLines.size(); i++) { + //find first translation that covers current offset if (offset >= translationLines[i].Offset) { + //if range is open, or offset is in range if (translationLines[i].OffsetEnd == 0 || translationLines[i].OffsetEnd >= offset) { return &translationLines[i]; diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 4ca4e38ecec8..adb15b49bdaf 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -13,7 +13,6 @@ namespace OSD { -// TODO clean this mess struct Message { Message() = default; @@ -30,6 +29,7 @@ struct Message u32 color = 0; float scale = 1; }; + class OSDMessageStack { public: @@ -60,7 +60,7 @@ class OSDMessageStack { for (auto it = messages.begin(); it != messages.end(); ++it) { - if (message == it->second.text) + if (type == it->first && message == it->second.text) { return true; } From c6e5d48462bf9c44d0be31a9f9d4e0860a906867 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 00:08:08 +0100 Subject: [PATCH 16/28] Cleanup, style fixes --- Source/Core/DolphinLib.vcxproj | 3 +- Source/Core/Subtitles/Helpers.h | 14 +++- Source/Core/Subtitles/SubtitleEntry.h | 82 +++++++++++++++++++ Source/Core/Subtitles/Subtitles.cpp | 42 +++++----- Source/Core/Subtitles/Subtitles.h | 8 +- Source/Core/Subtitles/TranslationEntry.h | 81 ------------------ Source/Core/Subtitles/WebColors.h | 7 +- .../Core/VideoCommon/OnScreenDisplay.enum.h | 20 ----- Source/Core/VideoCommon/OnScreenDisplay.h | 24 +++++- 9 files changed, 149 insertions(+), 132 deletions(-) create mode 100644 Source/Core/Subtitles/SubtitleEntry.h delete mode 100644 Source/Core/Subtitles/TranslationEntry.h delete mode 100644 Source/Core/VideoCommon/OnScreenDisplay.enum.h diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index f18b1bf595fb..217feb8aac50 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -72,9 +72,8 @@ - + - diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index 51eb2a6a00c5..46f3e3e74176 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -1,10 +1,16 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #pragma once + #include -#include -#include -#include + #include -#include "WebColors.h" + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "Subtitles/WebColors.h" +#include "VideoCommon/OnScreenDisplay.h" namespace Subtitles { diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h new file mode 100644 index 000000000000..f16ff5952bca --- /dev/null +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -0,0 +1,82 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +namespace Subtitles +{ + +struct SubtitleEntry +{ + std::string Filename; + std::string Text; + u32 Miliseconds; + u32 Color; + bool Enabled; + bool AllowDuplicate; + float Scale; + u32 Offset; + u32 OffsetEnd; + bool DisplayOnTop; + +public: + SubtitleEntry() + : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), + Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) + { + } + SubtitleEntry(std::string filename, std::string text, u32 miliseconds, u32 color, bool enabled, + bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) + : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), + AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), + DisplayOnTop(displayOnTop) + { + } + bool IsOffset() { return Offset > 0; } +}; + +/// +/// Helper struct to deal with multiple subtitle lines per file +/// +struct SubtitleEntryGroup +{ + std::vector subtitleLines; + + // Ensure lines are sorted in reverse to simplify querying + void Sort() + { + std::sort(subtitleLines.begin(), subtitleLines.end(), + [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); + } + SubtitleEntry* GetTLForRelativeOffset(u32 offset) + { + if (subtitleLines.empty()) + return 0; + + // entry with offset=0 or offset=null is used by default + if (!subtitleLines[subtitleLines.size() - 1].IsOffset()) + return &subtitleLines[subtitleLines.size() - 1]; + + // from latest to earliest + for (auto i = 0; i < subtitleLines.size(); i++) + { + // find first translation that covers current offset + if (offset >= subtitleLines[i].Offset) + { + // if range is open, or offset is in range + if (subtitleLines[i].OffsetEnd == 0 || subtitleLines[i].OffsetEnd >= offset) + return &subtitleLines[i]; + else + return 0; + } + } + + return 0; + } + void Add(SubtitleEntry tl) { subtitleLines.push_back(tl); } +}; +} // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index a053c63c9d40..1ac725d26641 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -1,15 +1,9 @@ -#include "Subtitles.h" -#include "TranslationEntry.h" -#include "WebColors.h" -#include "Helpers.h" +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later -#include - -#include -#include +#pragma once -#include -#include +#include "Subtitles/Subtitles.h" #include #include @@ -17,18 +11,26 @@ #include #include +#include + +#include "Common/FileUtil.h" +#include "Core/ConfigManager.h" +#include "DiscIO/Filesystem.h" +#include "Subtitles/Helpers.h" +#include "Subtitles/SubtitleEntry.h" +#include "Subtitles/WebColors.h" +#include "VideoCommon/OnScreenDisplay.h" + namespace Subtitles { bool _messageStacksInitialized = false; bool _subtitlesInitialized = false; -std::map Translations; +std::map Translations; void DeserializeSubtitlesJson(std::string json) { if (json == "") - { return; - } picojson::value v; std::string err = picojson::parse(v, json); @@ -58,14 +60,13 @@ void DeserializeSubtitlesJson(std::string json) auto OffsetEnd = item.get("OffsetEnd"); auto DisplayOnTop = item.get("DisplayOnTop"); + // FileName and Translation are required fields if (!FileName.is() || !Translation.is()) - { continue; - } u32 color = TryParsecolor(Color, OSD::Color::CYAN); - auto tl = TranslationEntry(FileName.to_str(), Translation.to_str(), + auto tl = SubtitleEntry(FileName.to_str(), Translation.to_str(), Miliseconds.is() ? Miliseconds.get() : OSD::Duration::SHORT, color, Enabled.is() ? Enabled.get() : true, @@ -75,10 +76,9 @@ void DeserializeSubtitlesJson(std::string json) OffsetEnd.is() ? OffsetEnd.get() : 0, DisplayOnTop.is() ? DisplayOnTop.get() : false); + //fitler out disabled entries, tp lighten lookup load if (tl.Enabled) - { Translations[tl.Filename].Add(tl); - } } } @@ -118,9 +118,11 @@ void IniitalizeOSDMessageStacks() auto bottomstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, BottomOSDStackName); OSD::AddMessageStack(bottomstack); + auto topstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, TopOSDStackName); OSD::AddMessageStack(topstack); + _messageStacksInitialized = true; } @@ -137,9 +139,9 @@ void LoadSubtitlesForGame(std::string gameId) if (Translations.empty()) return; - // ensure stuff is sorted, can't trust crap people will do in text files :) + // ensure stuff is sorted, you never know what mess people will make in text files :) std::for_each(Translations.begin(), Translations.end(), - [](std::pair& t) { t.second.Sort(); }); + [](std::pair& t) { t.second.Sort(); }); IniitalizeOSDMessageStacks(); diff --git a/Source/Core/Subtitles/Subtitles.h b/Source/Core/Subtitles/Subtitles.h index 812e6defe072..9b0c1179a231 100644 --- a/Source/Core/Subtitles/Subtitles.h +++ b/Source/Core/Subtitles/Subtitles.h @@ -1,6 +1,12 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #pragma once + #include -#include + +#include "DiscIO/Filesystem.h" + namespace Subtitles { const std::string BottomOSDStackName = "subtitles-bottom"; diff --git a/Source/Core/Subtitles/TranslationEntry.h b/Source/Core/Subtitles/TranslationEntry.h deleted file mode 100644 index 5adfcdf9456e..000000000000 --- a/Source/Core/Subtitles/TranslationEntry.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include -#include -namespace Subtitles -{ -struct TranslationEntry -{ - std::string Filename; - std::string Text; - u32 Miliseconds; - u32 Color; - bool Enabled; - bool AllowDuplicate; - float Scale; - u32 Offset; - u32 OffsetEnd; - bool DisplayOnTop; - -public: - TranslationEntry() - : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), - Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) - { - } - TranslationEntry(std::string filename, std::string text, u32 miliseconds, u32 color, bool enabled, - bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) - : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), - AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), - DisplayOnTop(displayOnTop) - { - } - bool IsOffset() { return Offset > 0; } -}; - -struct TranslationEntryGroup -{ - std::vector translationLines; - - // Ensure lines are sorted to simplify querying - void Sort() - { - std::sort(translationLines.begin(), translationLines.end(), - [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); - } - TranslationEntry* GetTLForRelativeOffset(u32 offset) - { - if (translationLines.empty()) - { - return 0; - } - - //if there is entry with offset=0 or offset=null - if (!translationLines[translationLines.size() - 1].IsOffset()) - { - //use it by default - return &translationLines[translationLines.size() - 1]; - } - - //from latest to earliest - for (auto i = 0; i < translationLines.size(); i++) - { - //find first translation that covers current offset - if (offset >= translationLines[i].Offset) - { - //if range is open, or offset is in range - if (translationLines[i].OffsetEnd == 0 || translationLines[i].OffsetEnd >= offset) - { - return &translationLines[i]; - } - else - { - return 0; - } - } - } - - return 0; - } - void Add(TranslationEntry tl) { translationLines.push_back(tl); } -}; -} // namespace Subtitles diff --git a/Source/Core/Subtitles/WebColors.h b/Source/Core/Subtitles/WebColors.h index c82e0045769b..26bd67cceb96 100644 --- a/Source/Core/Subtitles/WebColors.h +++ b/Source/Core/Subtitles/WebColors.h @@ -1,8 +1,13 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #pragma once -#include + #include #include +#include "Common/CommonTypes.h" + namespace Subtitles { // web colors in hex form with Alpha = 255 diff --git a/Source/Core/VideoCommon/OnScreenDisplay.enum.h b/Source/Core/VideoCommon/OnScreenDisplay.enum.h deleted file mode 100644 index cae56c8d8fb0..000000000000 --- a/Source/Core/VideoCommon/OnScreenDisplay.enum.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -namespace OSD -{ -enum class MessageStackDirection -{ - Downward = 1, - Upward = 2, - Rightward = 4, - Leftward = 8, -}; -enum class MessageType -{ - NetPlayPing, - NetPlayBuffer, - - // This entry must be kept last so that persistent typed messages are - // displayed before other messages - Typeless, -}; -} diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index adb15b49bdaf..2feb37eee4ad 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -7,12 +7,30 @@ #include #include + #include "Common/CommonTypes.h" #include "Common/Timer.h" -#include "OnScreenDisplay.enum.h" namespace OSD { +enum class MessageStackDirection +{ + Downward = 1, + Upward = 2, + Rightward = 4, + Leftward = 8, +}; + +enum class MessageType +{ + NetPlayPing, + NetPlayBuffer, + + // This entry must be kept last so that persistent typed messages are + // displayed before other messages + Typeless, +}; + struct Message { Message() = default; @@ -39,10 +57,10 @@ class OSDMessageStack bool reversed; std::string name; std::multimap messages; - + OSDMessageStack() : OSDMessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} OSDMessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, - bool _reversed, std::string _name) + bool _reversed, std::string _name) { initialPosOffset = ImVec2(_x_offset, _y_offset); dir = _dir; From 4b7750f5f1950b27545849824efcef31a8892ba2 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 00:18:37 +0100 Subject: [PATCH 17/28] build fix --- Source/Core/VideoCommon/OnScreenDisplay.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 2feb37eee4ad..80390b0b69e8 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -5,6 +5,7 @@ #include #include +#include #include From 4387e7cd70a81634e1ad8522b132df98fe019fb6 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 00:37:46 +0100 Subject: [PATCH 18/28] reverted Visual Studio automatic changes --- Source/Core/DolphinLib.vcxproj | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 217feb8aac50..3a87426acc74 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -1,4 +1,4 @@ - + @@ -69,25 +69,20 @@ - - - - - - - - - - + - - + + + ]]> + @@ -95,6 +90,10 @@ - + \ No newline at end of file From 9d3529f0c08f55346c2519b859af899688062e0e Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 00:59:34 +0100 Subject: [PATCH 19/28] code cleanup --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 5 +-- Source/Core/Subtitles/Helpers.h | 34 ++++----------------- Source/Core/Subtitles/SubtitleEntry.h | 4 +++ Source/Core/VideoCommon/OnScreenDisplay.cpp | 2 -- Source/Core/VideoCommon/OnScreenDisplay.h | 11 +++---- 5 files changed, 15 insertions(+), 41 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 3cd1a2e94d4b..e7ac881753d2 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -53,7 +53,7 @@ static bool IsVideoFile(const std::string& filename) Common::ToLower(&extension); static const std::unordered_set extensions = { - ".thp", // 1Wii/Game Cube Video File + ".thp", // Wii/Game Cube Video File }; return extensions.find(extension) != extensions.end(); @@ -94,9 +94,6 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); - //if (path.contains("BGM") || path.contains("bgm")) - // return; - //TODO just use colors instead of this horrid hack if (IsSoundFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string); diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index 46f3e3e74176..f4dbe4338b48 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -29,46 +29,24 @@ void Error(std::string err) u32 TryParsecolor(picojson::value raw, u32 defaultColor) { if (raw.is()) + { return raw.get(); + } else { auto str = raw.to_str(); Common::ToLower(&str); if (str.starts_with("0x")) + { // hex string return std::stoul(str, nullptr, 16); + } else if (WebColors.count(str) == 1) + { // html color name return WebColors[str]; - else - // color noted with 3 or 4 base10 numers (rgb/argb) - try // string parsing suucks - { - // try parse (a)rgb space delimited color - u32 a, r, g, b; - auto parts = SplitString(str, ' '); - if (parts.size() == 4) - { - a = std::stoul(parts[0], nullptr, 10); - r = std::stoul(parts[1], nullptr, 10); - g = std::stoul(parts[2], nullptr, 10); - b = std::stoul(parts[3], nullptr, 10); - return a << 24 | r << 16 | g << 8 | b; - } - else if (parts.size() == 3) - { - a = 255; - r = std::stoul(parts[0], nullptr, 10); - g = std::stoul(parts[1], nullptr, 10); - b = std::stoul(parts[2], nullptr, 10); - return a << 24 | r << 16 | g << 8 | b; - } - } - catch (std::exception x) - { - Error("Invalid color: " + str); - } + } } return defaultColor; } diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h index f16ff5952bca..a7483d2b444c 100644 --- a/Source/Core/Subtitles/SubtitleEntry.h +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -69,9 +69,13 @@ struct SubtitleEntryGroup { // if range is open, or offset is in range if (subtitleLines[i].OffsetEnd == 0 || subtitleLines[i].OffsetEnd >= offset) + { return &subtitleLines[i]; + } else + { return 0; + } } } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index f758839133bf..11e7b0436eae 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -30,7 +30,6 @@ static std::atomic s_obscured_pixels_left = 0; static std::atomic s_obscured_pixels_top = 0; // default message stack -// static std::multimap s_messages; static OSDMessageStack defaultMessageStack = OSDMessageStack(); static std::map messageStacks; @@ -52,7 +51,6 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t const std::string window_name = fmt::format("osd_{}_{}", messageStack.name, index); // The size must be reset, otherwise the length of old messages could influence new ones. - //ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); // Gradually fade old messages away (except in their first frame) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 80390b0b69e8..f7df813bb3d9 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -60,14 +60,11 @@ class OSDMessageStack std::multimap messages; OSDMessageStack() : OSDMessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} - OSDMessageStack(float _x_offset, float _y_offset, MessageStackDirection _dir, bool _centered, - bool _reversed, std::string _name) + OSDMessageStack(float x_offset, float y_offset, MessageStackDirection dir, bool centered, + bool reversed, std::string name) + : dir(dir), centered(centered), reversed(reversed), name(name) { - initialPosOffset = ImVec2(_x_offset, _y_offset); - dir = _dir; - centered = _centered; - reversed = _reversed; - name = _name; + initialPosOffset = ImVec2(x_offset, y_offset); } bool isVertical() From 84edd57d04664b606d021857e51536d1ee19827b Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 01:55:10 +0100 Subject: [PATCH 20/28] code cleanup --- Source/Core/Subtitles/Helpers.cpp | 56 +++++++++++++++++++ Source/Core/Subtitles/Helpers.h | 42 ++------------- Source/Core/Subtitles/SubtitleEntry.cpp | 72 +++++++++++++++++++++++++ Source/Core/Subtitles/SubtitleEntry.h | 53 +++--------------- Source/Core/Subtitles/Subtitles.cpp | 8 ++- 5 files changed, 144 insertions(+), 87 deletions(-) create mode 100644 Source/Core/Subtitles/Helpers.cpp create mode 100644 Source/Core/Subtitles/SubtitleEntry.cpp diff --git a/Source/Core/Subtitles/Helpers.cpp b/Source/Core/Subtitles/Helpers.cpp new file mode 100644 index 000000000000..00d9a22bfdfc --- /dev/null +++ b/Source/Core/Subtitles/Helpers.cpp @@ -0,0 +1,56 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Subtitles/Helpers.h" + +#include + +#include + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "Subtitles/WebColors.h" +#include "VideoCommon/OnScreenDisplay.h" + +namespace Subtitles +{ + +void Info(std::string msg) +{ + OSD::AddMessage(msg, 2000, OSD::Color::GREEN); + INFO_LOG_FMT(SUBTITLES, "{}", msg); +} + +void Error(std::string err) +{ + OSD::AddMessage(err, 2000, OSD::Color::RED); + ERROR_LOG_FMT(SUBTITLES, "{}", err); +} + +u32 TryParsecolor(picojson::value raw, u32 defaultColor) +{ + if (raw.is()) + { + return raw.get(); + } + else + { + auto str = raw.to_str(); + Common::ToLower(&str); + + if (str.starts_with("0x")) + { + // hex string + return std::stoul(str, nullptr, 16); + } + else if (WebColors.count(str) == 1) + { + // html color name + return WebColors[str]; + } + } + return defaultColor; +} +} // namespace Subtitles diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index f4dbe4338b48..bd7196a0ca03 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -8,46 +8,10 @@ #include #include "Common/CommonTypes.h" -#include "Common/StringUtil.h" -#include "Subtitles/WebColors.h" -#include "VideoCommon/OnScreenDisplay.h" namespace Subtitles { - -void Info(std::string msg) -{ - OSD::AddMessage(msg, 2000, OSD::Color::GREEN); - INFO_LOG_FMT(SUBTITLES, "{}", msg); -} -void Error(std::string err) -{ - OSD::AddMessage(err, 2000, OSD::Color::RED); - ERROR_LOG_FMT(SUBTITLES, "{}", err); -} - -u32 TryParsecolor(picojson::value raw, u32 defaultColor) -{ - if (raw.is()) - { - return raw.get(); - } - else - { - auto str = raw.to_str(); - Common::ToLower(&str); - - if (str.starts_with("0x")) - { - // hex string - return std::stoul(str, nullptr, 16); - } - else if (WebColors.count(str) == 1) - { - // html color name - return WebColors[str]; - } - } - return defaultColor; -} +void Info(std::string msg); +void Error(std::string err); +u32 TryParsecolor(picojson::value raw, u32 defaultColor); } // namespace Subtitles diff --git a/Source/Core/Subtitles/SubtitleEntry.cpp b/Source/Core/Subtitles/SubtitleEntry.cpp new file mode 100644 index 000000000000..7545408964b9 --- /dev/null +++ b/Source/Core/Subtitles/SubtitleEntry.cpp @@ -0,0 +1,72 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "Subtitles/SubtitleEntry.h" + +#include +#include + +#include "Common/CommonTypes.h" + +namespace Subtitles +{ +// Ensure lines are sorted in reverse to simplify querying +void SubtitleEntryGroup::Sort() +{ + std::sort(subtitleLines.begin(), subtitleLines.end(), + [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); +} +SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) +{ + if (subtitleLines.empty()) + return 0; + + // entry with offset=0 or offset=null is used by default + if (!subtitleLines[subtitleLines.size() - 1].IsOffset()) + return &subtitleLines[subtitleLines.size() - 1]; + + // from latest to earliest + for (auto i = 0; i < subtitleLines.size(); i++) + { + // find first translation that covers current offset + if (offset >= subtitleLines[i].Offset) + { + // if range is open, or offset is in range + if (subtitleLines[i].OffsetEnd == 0 || subtitleLines[i].OffsetEnd >= offset) + { + return &subtitleLines[i]; + } + else + { + return 0; + } + } + } + + return 0; +} +void SubtitleEntryGroup::Add(SubtitleEntry tl) +{ + subtitleLines.push_back(tl); +} + +SubtitleEntry::SubtitleEntry() + : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), + Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) +{ +} +SubtitleEntry::SubtitleEntry(std::string filename, std::string text, u32 miliseconds, + u32 color, bool enabled, bool allowDuplicates, float scale, + u32 offset, u32 offsetEnd, bool displayOnTop) + : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), + AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), + DisplayOnTop(displayOnTop) +{ +} +bool SubtitleEntry::IsOffset() +{ + return Offset > 0; +} +} // namespace Subtitles diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h index a7483d2b444c..d3c5c61efe56 100644 --- a/Source/Core/Subtitles/SubtitleEntry.h +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -24,19 +24,10 @@ struct SubtitleEntry bool DisplayOnTop; public: - SubtitleEntry() - : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), - Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) - { - } + SubtitleEntry(); SubtitleEntry(std::string filename, std::string text, u32 miliseconds, u32 color, bool enabled, - bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) - : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), - AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), - DisplayOnTop(displayOnTop) - { - } - bool IsOffset() { return Offset > 0; } + bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop); + bool IsOffset(); }; /// @@ -47,40 +38,8 @@ struct SubtitleEntryGroup std::vector subtitleLines; // Ensure lines are sorted in reverse to simplify querying - void Sort() - { - std::sort(subtitleLines.begin(), subtitleLines.end(), - [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); - } - SubtitleEntry* GetTLForRelativeOffset(u32 offset) - { - if (subtitleLines.empty()) - return 0; - - // entry with offset=0 or offset=null is used by default - if (!subtitleLines[subtitleLines.size() - 1].IsOffset()) - return &subtitleLines[subtitleLines.size() - 1]; - - // from latest to earliest - for (auto i = 0; i < subtitleLines.size(); i++) - { - // find first translation that covers current offset - if (offset >= subtitleLines[i].Offset) - { - // if range is open, or offset is in range - if (subtitleLines[i].OffsetEnd == 0 || subtitleLines[i].OffsetEnd >= offset) - { - return &subtitleLines[i]; - } - else - { - return 0; - } - } - } - - return 0; - } - void Add(SubtitleEntry tl) { subtitleLines.push_back(tl); } + void Sort(); + SubtitleEntry* GetTLForRelativeOffset(u32 offset); + void Add(SubtitleEntry tl); }; } // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index 1ac725d26641..c1987b46010c 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -14,11 +14,11 @@ #include #include "Common/FileUtil.h" +#include "Common/Logging/LogManager.h" #include "Core/ConfigManager.h" #include "DiscIO/Filesystem.h" #include "Subtitles/Helpers.h" #include "Subtitles/SubtitleEntry.h" -#include "Subtitles/WebColors.h" #include "VideoCommon/OnScreenDisplay.h" namespace Subtitles @@ -132,6 +132,12 @@ void LoadSubtitlesForGame(std::string gameId) Translations.clear(); auto subtitleDir = File::GetUserPath(D_SUBTITLES_IDX) + gameId; + + if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::SUBTITLES, + Common::Log::LogLevel::LWARNING)) + { + Info(fmt::format("Loading subtitles for {} from {}", gameId, subtitleDir)); + } auto fileEnumerator = File::ScanDirectoryTree(subtitleDir, true); RecursivelyReadTranslationJsons(fileEnumerator, ".json"); From 5f9371e339a3995e8fbec5d631d45aa27b57ddd8 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 12:51:28 +0100 Subject: [PATCH 21/28] code cleanup and switch from pass by copy to pass by reference --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 2 +- Source/Core/Subtitles/Helpers.cpp | 17 ++++++----- Source/Core/Subtitles/Helpers.h | 6 ++-- Source/Core/Subtitles/SubtitleEntry.cpp | 4 +-- Source/Core/Subtitles/SubtitleEntry.h | 5 ++-- Source/Core/Subtitles/Subtitles.cpp | 38 +++++++++++++------------ Source/Core/Subtitles/Subtitles.h | 1 + 7 files changed, 40 insertions(+), 33 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index e7ac881753d2..5f1fc3e628a3 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -94,7 +94,7 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); - //TODO just use colors instead of this horrid hack + //TODO maybe just replace with map? if (IsSoundFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string); else if (IsVideoFile(path)) diff --git a/Source/Core/Subtitles/Helpers.cpp b/Source/Core/Subtitles/Helpers.cpp index 00d9a22bfdfc..189292cbe8f7 100644 --- a/Source/Core/Subtitles/Helpers.cpp +++ b/Source/Core/Subtitles/Helpers.cpp @@ -17,19 +17,19 @@ namespace Subtitles { -void Info(std::string msg) +inline void Info(std::string msg) { - OSD::AddMessage(msg, 2000, OSD::Color::GREEN); + OSD::AddMessage(msg, 5000, OSD::Color::GREEN); INFO_LOG_FMT(SUBTITLES, "{}", msg); } -void Error(std::string err) +inline void Error(std::string err) { - OSD::AddMessage(err, 2000, OSD::Color::RED); + OSD::AddMessage(err, 5000, OSD::Color::RED); ERROR_LOG_FMT(SUBTITLES, "{}", err); } -u32 TryParsecolor(picojson::value raw, u32 defaultColor) +u32 TryParsecolor(picojson::value& raw, u32 defaultColor) { if (raw.is()) { @@ -42,8 +42,11 @@ u32 TryParsecolor(picojson::value raw, u32 defaultColor) if (str.starts_with("0x")) { - // hex string - return std::stoul(str, nullptr, 16); + u32 parsedHex = 0; + if (TryParse(str, &parsedHex)) + { + return parsedHex; + } } else if (WebColors.count(str) == 1) { diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index bd7196a0ca03..28635aa7caf4 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -11,7 +11,7 @@ namespace Subtitles { -void Info(std::string msg); -void Error(std::string err); -u32 TryParsecolor(picojson::value raw, u32 defaultColor); +inline void Info(std::string msg); +inline void Error(std::string err); +u32 TryParsecolor(picojson::value& raw, u32 defaultColor); } // namespace Subtitles diff --git a/Source/Core/Subtitles/SubtitleEntry.cpp b/Source/Core/Subtitles/SubtitleEntry.cpp index 7545408964b9..36ff26e1123c 100644 --- a/Source/Core/Subtitles/SubtitleEntry.cpp +++ b/Source/Core/Subtitles/SubtitleEntry.cpp @@ -47,7 +47,7 @@ SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) return 0; } -void SubtitleEntryGroup::Add(SubtitleEntry tl) +void SubtitleEntryGroup::Add(SubtitleEntry& tl) { subtitleLines.push_back(tl); } @@ -57,7 +57,7 @@ SubtitleEntry::SubtitleEntry() Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) { } -SubtitleEntry::SubtitleEntry(std::string filename, std::string text, u32 miliseconds, +SubtitleEntry::SubtitleEntry(const std::string& filename, const std::string& text, u32 miliseconds, u32 color, bool enabled, bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h index d3c5c61efe56..45130b25b81f 100644 --- a/Source/Core/Subtitles/SubtitleEntry.h +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -25,7 +25,8 @@ struct SubtitleEntry public: SubtitleEntry(); - SubtitleEntry(std::string filename, std::string text, u32 miliseconds, u32 color, bool enabled, + SubtitleEntry(const std::string& filename, const std::string& text, u32 miliseconds, u32 color, + bool enabled, bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop); bool IsOffset(); }; @@ -40,6 +41,6 @@ struct SubtitleEntryGroup // Ensure lines are sorted in reverse to simplify querying void Sort(); SubtitleEntry* GetTLForRelativeOffset(u32 offset); - void Add(SubtitleEntry tl); + void Add(SubtitleEntry& tl); }; } // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index c1987b46010c..744c56271b9f 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -27,7 +27,7 @@ bool _messageStacksInitialized = false; bool _subtitlesInitialized = false; std::map Translations; -void DeserializeSubtitlesJson(std::string json) +void DeserializeSubtitlesJson(std::string& json) { if (json == "") return; @@ -36,7 +36,7 @@ void DeserializeSubtitlesJson(std::string json) std::string err = picojson::parse(v, json); if (!err.empty()) { - Error("Subtitle JSON Error: " + err); + Error(fmt::format("Subtitle JSON Error: {}", err)); return; } @@ -66,23 +66,25 @@ void DeserializeSubtitlesJson(std::string json) u32 color = TryParsecolor(Color, OSD::Color::CYAN); - auto tl = SubtitleEntry(FileName.to_str(), Translation.to_str(), - Miliseconds.is() ? Miliseconds.get() : - OSD::Duration::SHORT, - color, Enabled.is() ? Enabled.get() : true, - AllowDuplicate.is() ? AllowDuplicate.get() : false, - Scale.is() ? Scale.get() : 1, - Offset.is() ? Offset.get() : 0, - OffsetEnd.is() ? OffsetEnd.get() : 0, - DisplayOnTop.is() ? DisplayOnTop.get() : false); - - //fitler out disabled entries, tp lighten lookup load + const std::string filename = FileName.to_str(); + const std::string translation = Translation.to_str(); + auto tl = + SubtitleEntry(filename, translation, + Miliseconds.is() ? Miliseconds.get() : OSD::Duration::SHORT, + color, Enabled.is() ? Enabled.get() : true, + AllowDuplicate.is() ? AllowDuplicate.get() : false, + Scale.is() ? Scale.get() : 1, + Offset.is() ? Offset.get() : 0, + OffsetEnd.is() ? OffsetEnd.get() : 0, + DisplayOnTop.is() ? DisplayOnTop.get() : false); + + // fitler out disabled entries, tp lighten lookup load if (tl.Enabled) Translations[tl.Filename].Add(tl); } } -void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, std::string filter) +void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, const std::string& filter) { for (const auto& child : folder.children) { @@ -126,21 +128,21 @@ void IniitalizeOSDMessageStacks() _messageStacksInitialized = true; } -void LoadSubtitlesForGame(std::string gameId) +void LoadSubtitlesForGame(std::string& gameId) { _subtitlesInitialized = false; Translations.clear(); auto subtitleDir = File::GetUserPath(D_SUBTITLES_IDX) + gameId; - + if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::SUBTITLES, - Common::Log::LogLevel::LWARNING)) + Common::Log::LogLevel::LWARNING)) { Info(fmt::format("Loading subtitles for {} from {}", gameId, subtitleDir)); } auto fileEnumerator = File::ScanDirectoryTree(subtitleDir, true); - RecursivelyReadTranslationJsons(fileEnumerator, ".json"); + RecursivelyReadTranslationJsons(fileEnumerator, SubtitleFileExtension); if (Translations.empty()) return; diff --git a/Source/Core/Subtitles/Subtitles.h b/Source/Core/Subtitles/Subtitles.h index 9b0c1179a231..2e5e4fd5d49c 100644 --- a/Source/Core/Subtitles/Subtitles.h +++ b/Source/Core/Subtitles/Subtitles.h @@ -9,6 +9,7 @@ namespace Subtitles { +const std::string SubtitleFileExtension = ".json"; const std::string BottomOSDStackName = "subtitles-bottom"; const std::string TopOSDStackName = "subtitles-top"; void Reload(); From d1a7096beac54defddfba72cd900fd883230f5ac Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 13:55:40 +0100 Subject: [PATCH 22/28] inline/copyconstructor/pointer fixes --- Source/Core/Core/HW/DVD/DVDThread.cpp | 3 +- Source/Core/Core/HW/DVD/FileMonitor.cpp | 2 +- Source/Core/Subtitles/Helpers.cpp | 6 +-- Source/Core/Subtitles/Helpers.h | 6 +-- Source/Core/Subtitles/SubtitleEntry.cpp | 6 +-- Source/Core/Subtitles/SubtitleEntry.h | 2 +- Source/Core/Subtitles/Subtitles.cpp | 55 +++++++++++---------- Source/Core/VideoCommon/OnScreenDisplay.cpp | 15 +++--- Source/Core/VideoCommon/OnScreenDisplay.h | 5 +- 9 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 96f4cf7fa880..c3a07617cc83 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -33,7 +33,8 @@ #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" -#include + +#include "Subtitles/Subtitles.h" namespace DVD { diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 5f1fc3e628a3..937f389d3a5a 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -94,7 +94,7 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); - //TODO maybe just replace with map? + // Current logger system does not support colors, so we abuse log levels instead if (IsSoundFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string); else if (IsVideoFile(path)) diff --git a/Source/Core/Subtitles/Helpers.cpp b/Source/Core/Subtitles/Helpers.cpp index 189292cbe8f7..5d1c08358296 100644 --- a/Source/Core/Subtitles/Helpers.cpp +++ b/Source/Core/Subtitles/Helpers.cpp @@ -17,19 +17,19 @@ namespace Subtitles { -inline void Info(std::string msg) +void Info(std::string msg) { OSD::AddMessage(msg, 5000, OSD::Color::GREEN); INFO_LOG_FMT(SUBTITLES, "{}", msg); } -inline void Error(std::string err) +void Error(std::string err) { OSD::AddMessage(err, 5000, OSD::Color::RED); ERROR_LOG_FMT(SUBTITLES, "{}", err); } -u32 TryParsecolor(picojson::value& raw, u32 defaultColor) +u32 TryParsecolor(const picojson::value& raw, u32 defaultColor) { if (raw.is()) { diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index 28635aa7caf4..19497d8fceff 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -11,7 +11,7 @@ namespace Subtitles { -inline void Info(std::string msg); -inline void Error(std::string err); -u32 TryParsecolor(picojson::value& raw, u32 defaultColor); +void Info(std::string msg); +void Error(std::string err); +u32 TryParsecolor(const picojson::value& raw, u32 defaultColor); } // namespace Subtitles diff --git a/Source/Core/Subtitles/SubtitleEntry.cpp b/Source/Core/Subtitles/SubtitleEntry.cpp index 36ff26e1123c..ba31ad6e5ec3 100644 --- a/Source/Core/Subtitles/SubtitleEntry.cpp +++ b/Source/Core/Subtitles/SubtitleEntry.cpp @@ -21,7 +21,7 @@ void SubtitleEntryGroup::Sort() SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) { if (subtitleLines.empty()) - return 0; + return nullptr; // entry with offset=0 or offset=null is used by default if (!subtitleLines[subtitleLines.size() - 1].IsOffset()) @@ -40,7 +40,7 @@ SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) } else { - return 0; + return nullptr; } } } @@ -57,7 +57,7 @@ SubtitleEntry::SubtitleEntry() Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) { } -SubtitleEntry::SubtitleEntry(const std::string& filename, const std::string& text, u32 miliseconds, +SubtitleEntry::SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, u32 color, bool enabled, bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop) : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h index 45130b25b81f..09aa8f7f587c 100644 --- a/Source/Core/Subtitles/SubtitleEntry.h +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -25,7 +25,7 @@ struct SubtitleEntry public: SubtitleEntry(); - SubtitleEntry(const std::string& filename, const std::string& text, u32 miliseconds, u32 color, + SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, u32 color, bool enabled, bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop); bool IsOffset(); diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index 744c56271b9f..1288d8c0dc7d 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -49,38 +49,40 @@ void DeserializeSubtitlesJson(std::string& json) auto arr = v.get(); for (auto item : arr) { - auto FileName = item.get("FileName"); - auto Translation = item.get("Translation"); - auto Miliseconds = item.get("Miliseconds"); - auto Color = item.get("Color"); - auto Enabled = item.get("Enabled"); - auto AllowDuplicate = item.get("AllowDuplicate"); - auto Scale = item.get("Scale"); - auto Offset = item.get("Offset"); - auto OffsetEnd = item.get("OffsetEnd"); - auto DisplayOnTop = item.get("DisplayOnTop"); + const auto FileName = item.get("FileName"); + const auto Translation = item.get("Translation"); + const auto Miliseconds = item.get("Miliseconds"); + const auto Color = item.get("Color"); + const auto Enabled = item.get("Enabled"); + const auto AllowDuplicate = item.get("AllowDuplicate"); + const auto Scale = item.get("Scale"); + const auto Offset = item.get("Offset"); + const auto OffsetEnd = item.get("OffsetEnd"); + const auto DisplayOnTop = item.get("DisplayOnTop"); + + bool enabled = Enabled.is() ? Enabled.get() : true; + if (!enabled) + return; // FileName and Translation are required fields if (!FileName.is() || !Translation.is()) continue; - u32 color = TryParsecolor(Color, OSD::Color::CYAN); + const u32 color = TryParsecolor(Color, OSD::Color::CYAN); - const std::string filename = FileName.to_str(); - const std::string translation = Translation.to_str(); - auto tl = - SubtitleEntry(filename, translation, - Miliseconds.is() ? Miliseconds.get() : OSD::Duration::SHORT, - color, Enabled.is() ? Enabled.get() : true, - AllowDuplicate.is() ? AllowDuplicate.get() : false, - Scale.is() ? Scale.get() : 1, - Offset.is() ? Offset.get() : 0, - OffsetEnd.is() ? OffsetEnd.get() : 0, - DisplayOnTop.is() ? DisplayOnTop.get() : false); + std::string filename = FileName.to_str(); + std::string translation = Translation.to_str(); + + auto tl = SubtitleEntry( + filename, translation, + Miliseconds.is() ? Miliseconds.get() : OSD::Duration::SHORT, color, enabled, + AllowDuplicate.is() ? AllowDuplicate.get() : false, + Scale.is() ? Scale.get() : 1, + Offset.is() ? Offset.get() : 0, + OffsetEnd.is() ? OffsetEnd.get() : 0, + DisplayOnTop.is() ? DisplayOnTop.get() : false); // fitler out disabled entries, tp lighten lookup load - if (tl.Enabled) - Translations[tl.Filename].Add(tl); } } @@ -128,7 +130,7 @@ void IniitalizeOSDMessageStacks() _messageStacksInitialized = true; } -void LoadSubtitlesForGame(std::string& gameId) +void LoadSubtitlesForGame(const std::string& gameId) { _subtitlesInitialized = false; Translations.clear(); @@ -158,8 +160,7 @@ void LoadSubtitlesForGame(std::string& gameId) void Reload() { - std::string game_id = SConfig::GetInstance().GetGameID(); - LoadSubtitlesForGame(game_id); + LoadSubtitlesForGame(SConfig::GetInstance().GetGameID()); } void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 11e7b0436eae..63f7d70c79ec 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -44,11 +44,11 @@ static ImVec4 ARGBToImVec4(const u32 argb) } static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int time_left, - OSDMessageStack& messageStack) + OSDMessageStack& message_Stack) { // We have to provide a window name, and these shouldn't be duplicated. // So instead, we generate a name based on the number of messages drawn. - const std::string window_name = fmt::format("osd_{}_{}", messageStack.name, index); + const std::string window_name = fmt::format("osd_{}_{}", message_Stack.name, index); // The size must be reset, otherwise the length of old messages could influence new ones. ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); @@ -78,9 +78,9 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t float x_pos = position.x; float y_pos = position.y; - if (messageStack.centered) + if (message_Stack.centered) { - if (messageStack.isVertical()) + if (message_Stack.isVertical()) { float x_center = ImGui::GetIO().DisplaySize.x / 2.0; x_pos = x_center - window_width / 2; @@ -92,11 +92,11 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t } } - if (messageStack.dir == MessageStackDirection::Leftward) + if (message_Stack.dir == MessageStackDirection::Leftward) { x_pos -= window_width; } - if (messageStack.dir == MessageStackDirection::Upward) + if (message_Stack.dir == MessageStackDirection::Upward) { y_pos -= window_height; } @@ -140,9 +140,8 @@ void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, AddTypedMessage(MessageType::Typeless, message, ms, argb, messageStack, preventDuplicate, scale); } -void AddMessageStack(OSDMessageStack info) +void AddMessageStack(OSDMessageStack& info) { - // TODO handle dups messageStacks[info.name] = info; } void DrawMessages(OSDMessageStack& messageStack) diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index f7df813bb3d9..9a2530b98175 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -49,9 +49,8 @@ struct Message float scale = 1; }; -class OSDMessageStack +struct OSDMessageStack { -public: ImVec2 initialPosOffset; MessageStackDirection dir; bool centered; @@ -100,7 +99,7 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration -void AddMessageStack(OSDMessageStack info); +void AddMessageStack(OSDMessageStack& info); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, From 29eb73abf2482bd051ea3b96358a305335a3b332 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 14:11:09 +0100 Subject: [PATCH 23/28] variable naming cleanup --- Source/Core/Subtitles/Subtitles.cpp | 14 ++++----- Source/Core/VideoCommon/OnScreenDisplay.cpp | 33 +++++++++++---------- Source/Core/VideoCommon/OnScreenDisplay.h | 10 +++---- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index 1288d8c0dc7d..e8dddaa30fe3 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -23,8 +23,8 @@ namespace Subtitles { -bool _messageStacksInitialized = false; -bool _subtitlesInitialized = false; +bool g_messageStacksInitialized = false; +bool g_subtitlesInitialized = false; std::map Translations; void DeserializeSubtitlesJson(std::string& json) @@ -116,7 +116,7 @@ void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, const std::st void IniitalizeOSDMessageStacks() { - if (_messageStacksInitialized) + if (g_messageStacksInitialized) return; auto bottomstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, @@ -127,12 +127,12 @@ void IniitalizeOSDMessageStacks() TopOSDStackName); OSD::AddMessageStack(topstack); - _messageStacksInitialized = true; + g_messageStacksInitialized = true; } void LoadSubtitlesForGame(const std::string& gameId) { - _subtitlesInitialized = false; + g_subtitlesInitialized = false; Translations.clear(); auto subtitleDir = File::GetUserPath(D_SUBTITLES_IDX) + gameId; @@ -155,7 +155,7 @@ void LoadSubtitlesForGame(const std::string& gameId) IniitalizeOSDMessageStacks(); - _subtitlesInitialized = true; + g_subtitlesInitialized = true; } void Reload() @@ -165,7 +165,7 @@ void Reload() void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partition, u64 offset) { - if (!_subtitlesInitialized) + if (!g_subtitlesInitialized) return; const DiscIO::FileSystem* file_system = volume.GetFileSystem(partition); diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 63f7d70c79ec..0d4fd902ea7d 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -30,7 +30,7 @@ static std::atomic s_obscured_pixels_left = 0; static std::atomic s_obscured_pixels_top = 0; // default message stack -static OSDMessageStack defaultMessageStack = OSDMessageStack(); +static OSDMessageStack s_defaultMessageStack = OSDMessageStack(); static std::map messageStacks; static std::mutex s_messages_mutex; @@ -80,14 +80,14 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t if (message_Stack.centered) { - if (message_Stack.isVertical()) + if (message_Stack.IsVertical()) { - float x_center = ImGui::GetIO().DisplaySize.x / 2.0; + const float x_center = ImGui::GetIO().DisplaySize.x / 2.0; x_pos = x_center - window_width / 2; } else { - float y_center = ImGui::GetIO().DisplaySize.y / 2.0; + const float y_center = ImGui::GetIO().DisplaySize.y / 2.0; y_pos = y_center - window_height / 2; } } @@ -101,7 +101,7 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t y_pos -= window_height; } - auto windowPos = ImVec2(x_pos, y_pos); + const auto windowPos = ImVec2(x_pos, y_pos); ImGui::SetWindowPos(window_name.c_str(), windowPos); } @@ -114,17 +114,17 @@ static ImVec2 DrawMessage(int index, Message& msg, const ImVec2& position, int t } void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, - std::string messageStack, bool preventDuplicate, float scale) + std::string message_stack, bool prevent_duplicate, float scale) { std::lock_guard lock{s_messages_mutex}; - OSDMessageStack* stack = &defaultMessageStack; - if (messageStacks.contains(messageStack)) + OSDMessageStack* stack = &s_defaultMessageStack; + if (messageStacks.contains(message_stack)) { - stack = &messageStacks[messageStack]; + stack = &messageStacks[message_stack]; } - if (preventDuplicate && stack->hasMessage(message, type)) + if (prevent_duplicate && stack->HasMessage(message, type)) { return; } @@ -135,9 +135,10 @@ void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, stack->messages.emplace(type, Message(std::move(message), ms, argb, scale)); } -void AddMessage(std::string message, u32 ms, u32 argb, std::string messageStack, bool preventDuplicate, float scale) +void AddMessage(std::string message, u32 ms, u32 argb, std::string message_stack, bool prevent_duplicate, float scale) { - AddTypedMessage(MessageType::Typeless, message, ms, argb, messageStack, preventDuplicate, scale); + AddTypedMessage(MessageType::Typeless, message, ms, argb, message_stack, prevent_duplicate, + scale); } void AddMessageStack(OSDMessageStack& info) @@ -190,10 +191,10 @@ void DrawMessages(OSDMessageStack& messageStack) if (draw_messages) { - auto messageSize = + const auto messageSize = DrawMessage(index++, msg, ImVec2(current_x, current_y), time_left, messageStack); - if (messageStack.isVertical()) + if (messageStack.IsVertical()) { current_y += messageStack.dir == OSD::MessageStackDirection::Upward ? -messageSize.y : messageSize.y; @@ -208,7 +209,7 @@ void DrawMessages(OSDMessageStack& messageStack) } void DrawMessages() { - DrawMessages(defaultMessageStack); + DrawMessages(s_defaultMessageStack); for (auto& [name, stack] : messageStacks) { DrawMessages(stack); @@ -218,7 +219,7 @@ void DrawMessages() void ClearMessages() { std::lock_guard lock{s_messages_mutex}; - defaultMessageStack.messages.clear(); + s_defaultMessageStack.messages.clear(); for (auto& [name, stack] : messageStacks) { stack.messages.clear(); diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 9a2530b98175..dce17d017cbc 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -66,12 +66,12 @@ struct OSDMessageStack initialPosOffset = ImVec2(x_offset, y_offset); } - bool isVertical() + bool IsVertical() { return dir == MessageStackDirection::Downward || dir == MessageStackDirection::Upward; } - bool hasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) + bool HasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) { for (auto it = messages.begin(); it != messages.end(); ++it) { @@ -103,10 +103,10 @@ void AddMessageStack(OSDMessageStack& info); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, - std::string messageStack = "", bool preventDuplicate = false, float scale = 1); + std::string message_stack = "", bool prevent_duplicate = false, float scale = 1); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, - u32 argb = Color::YELLOW, std::string messageStack = "", - bool preventDuplicate = false, float scale = 1); + u32 argb = Color::YELLOW, std::string message_stack = "", + bool prevent_duplicate = false, float scale = 1); // Draw the current messages on the screen. Only call once per frame. void DrawMessages(); From 2399d599a2d19fd6aba8caa1f7ee7a0843fa22be Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 11 Oct 2023 16:25:48 +0100 Subject: [PATCH 24/28] Subtitles at timestamp --- Source/Core/Subtitles/SubtitleEntry.cpp | 82 +++++++++++++++++++++---- Source/Core/Subtitles/SubtitleEntry.h | 20 ++++-- Source/Core/Subtitles/Subtitles.cpp | 15 +++-- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/Source/Core/Subtitles/SubtitleEntry.cpp b/Source/Core/Subtitles/SubtitleEntry.cpp index ba31ad6e5ec3..42fb4ac5bb69 100644 --- a/Source/Core/Subtitles/SubtitleEntry.cpp +++ b/Source/Core/Subtitles/SubtitleEntry.cpp @@ -6,27 +6,62 @@ #include "Subtitles/SubtitleEntry.h" #include +#include #include #include "Common/CommonTypes.h" +#include "Subtitles/Helpers.h" namespace Subtitles { // Ensure lines are sorted in reverse to simplify querying -void SubtitleEntryGroup::Sort() +void SubtitleEntryGroup::Preprocess() { - std::sort(subtitleLines.begin(), subtitleLines.end(), - [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); + for (auto i = 0; i < subtitleLines.size(); i++) + { + hasOffsets |= subtitleLines[i].Offset > 0; + hasTimestamps |= subtitleLines[i].Timestamp > 0; + } + + // Info(fmt::format("HasOffsets: {} HasTimestamps {} File: {}", hasOffsets, hasTimestamps)); + + if (hasOffsets) + { + std::sort(subtitleLines.begin(), subtitleLines.end(), + [](const auto& lhs, const auto& rhs) { return lhs.Offset > rhs.Offset; }); + } + // Offsets override Timestamps + else if (hasTimestamps) + { + std::sort(subtitleLines.begin(), subtitleLines.end(), + [](const auto& lhs, const auto& rhs) { return lhs.Timestamp > rhs.Timestamp; }); + } } -SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) +SubtitleEntry* SubtitleEntryGroup::GetSubtitle(u32 offset) { if (subtitleLines.empty()) return nullptr; - // entry with offset=0 or offset=null is used by default - if (!subtitleLines[subtitleLines.size() - 1].IsOffset()) - return &subtitleLines[subtitleLines.size() - 1]; + if (hasOffsets) + { + return GetSubtitleForRelativeOffset(offset); + } + if (hasTimestamps) + { + if (offset == 0) + { + // restart timer if file is being read from start + timer.Start(); + } + // TODO do sync emulated time with real time, or just ingore this issue? + auto timestamp = timer.ElapsedMs(); + return GetSubtitleForRelativeTimestamp(timestamp); + } + return &subtitleLines[0]; +} +SubtitleEntry* SubtitleEntryGroup::GetSubtitleForRelativeOffset(u32 offset) +{ // from latest to earliest for (auto i = 0; i < subtitleLines.size(); i++) { @@ -47,6 +82,29 @@ SubtitleEntry* SubtitleEntryGroup::GetTLForRelativeOffset(u32 offset) return 0; } +SubtitleEntry* SubtitleEntryGroup::GetSubtitleForRelativeTimestamp(u64 timestamp) +{ + // from latest to earliest + for (auto i = 0; i < subtitleLines.size(); i++) + { + // find first translation that covers current offset + if (timestamp >= subtitleLines[i].Timestamp) + { + // use display time as treshold + auto endstamp = subtitleLines[i].Timestamp + subtitleLines[i].Miliseconds; + if (endstamp >= timestamp) + { + return &subtitleLines[i]; + } + else + { + return nullptr; + } + } + } + + return 0; +} void SubtitleEntryGroup::Add(SubtitleEntry& tl) { subtitleLines.push_back(tl); @@ -54,15 +112,15 @@ void SubtitleEntryGroup::Add(SubtitleEntry& tl) SubtitleEntry::SubtitleEntry() : Filename(""), Text(""), Miliseconds(0), Color(0), Enabled(false), AllowDuplicate(false), - Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false) + Scale(1), Offset(0), OffsetEnd(0), DisplayOnTop(false), Timestamp(0) { } -SubtitleEntry::SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, - u32 color, bool enabled, bool allowDuplicates, float scale, - u32 offset, u32 offsetEnd, bool displayOnTop) +SubtitleEntry::SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, u32 color, + bool enabled, bool allowDuplicates, float scale, u32 offset, + u32 offsetEnd, bool displayOnTop, u64 timestamp) : Filename(filename), Text(text), Miliseconds(miliseconds), Color(color), Enabled(enabled), AllowDuplicate(allowDuplicates), Scale(scale), Offset(offset), OffsetEnd(offsetEnd), - DisplayOnTop(displayOnTop) + DisplayOnTop(displayOnTop), Timestamp(timestamp) { } bool SubtitleEntry::IsOffset() diff --git a/Source/Core/Subtitles/SubtitleEntry.h b/Source/Core/Subtitles/SubtitleEntry.h index 09aa8f7f587c..4ec25b8b7c6f 100644 --- a/Source/Core/Subtitles/SubtitleEntry.h +++ b/Source/Core/Subtitles/SubtitleEntry.h @@ -6,6 +6,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/Timer.h" namespace Subtitles { @@ -22,12 +23,13 @@ struct SubtitleEntry u32 Offset; u32 OffsetEnd; bool DisplayOnTop; + u64 Timestamp; public: SubtitleEntry(); - SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, u32 color, - bool enabled, - bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop); + SubtitleEntry(std::string& filename, std::string& text, u32 miliseconds, u32 color, bool enabled, + bool allowDuplicates, float scale, u32 offset, u32 offsetEnd, bool displayOnTop, + u64 timestamp); bool IsOffset(); }; @@ -36,11 +38,19 @@ struct SubtitleEntry /// struct SubtitleEntryGroup { + Common::Timer timer; + std::vector subtitleLines; + bool hasOffsets = false; + bool hasTimestamps = false; // Ensure lines are sorted in reverse to simplify querying - void Sort(); - SubtitleEntry* GetTLForRelativeOffset(u32 offset); + void Preprocess(); void Add(SubtitleEntry& tl); + SubtitleEntry* GetSubtitle(u32 offset); + +private: + SubtitleEntry* GetSubtitleForRelativeOffset(u32 offset); + SubtitleEntry* GetSubtitleForRelativeTimestamp(u64 timestamp); }; } // namespace Subtitles diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index e8dddaa30fe3..2a6ff4957da7 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -59,10 +59,12 @@ void DeserializeSubtitlesJson(std::string& json) const auto Offset = item.get("Offset"); const auto OffsetEnd = item.get("OffsetEnd"); const auto DisplayOnTop = item.get("DisplayOnTop"); + const auto Timestamp = item.get("Timestamp"); + // fitler out disabled entries, to lighten lookup load bool enabled = Enabled.is() ? Enabled.get() : true; if (!enabled) - return; + continue; // FileName and Translation are required fields if (!FileName.is() || !Translation.is()) @@ -80,9 +82,10 @@ void DeserializeSubtitlesJson(std::string& json) Scale.is() ? Scale.get() : 1, Offset.is() ? Offset.get() : 0, OffsetEnd.is() ? OffsetEnd.get() : 0, - DisplayOnTop.is() ? DisplayOnTop.get() : false); + DisplayOnTop.is() ? DisplayOnTop.get() : false, + Timestamp.is() ? Timestamp.get() : 0); - // fitler out disabled entries, tp lighten lookup load + Translations[tl.Filename].Add(tl); } } @@ -146,16 +149,18 @@ void LoadSubtitlesForGame(const std::string& gameId) auto fileEnumerator = File::ScanDirectoryTree(subtitleDir, true); RecursivelyReadTranslationJsons(fileEnumerator, SubtitleFileExtension); + Info(fmt::format("Translations.empty() = {}", Translations.empty())); if (Translations.empty()) return; // ensure stuff is sorted, you never know what mess people will make in text files :) std::for_each(Translations.begin(), Translations.end(), - [](std::pair& t) { t.second.Sort(); }); + [](std::pair& t) { t.second.Preprocess(); }); IniitalizeOSDMessageStacks(); g_subtitlesInitialized = true; + Info(fmt::format("Subtitles loaded for {}", gameId)); } void Reload() @@ -184,7 +189,7 @@ void OnFileAccess(const DiscIO::Volume& volume, const DiscIO::Partition& partiti if (Translations.count(path) == 0) return; - auto tl = Translations[path].GetTLForRelativeOffset((u32)relativeOffset); + auto tl = Translations[path].GetSubtitle((u32)relativeOffset); if (!tl) return; From 60efd6b46ac6d3e9fa137688474dee0616aa41c9 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 18 Oct 2023 19:56:02 +0100 Subject: [PATCH 25/28] improved logger --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 20 +++----------------- Source/Core/Subtitles/Helpers.cpp | 10 ++++++++++ Source/Core/Subtitles/Helpers.h | 1 + Source/Core/Subtitles/SubtitleEntry.cpp | 9 +++++---- Source/Core/Subtitles/Subtitles.cpp | 9 ++------- 5 files changed, 21 insertions(+), 28 deletions(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 937f389d3a5a..885b4ef20f6f 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -21,7 +21,7 @@ namespace FileMonitor { // Filtered files -static bool IsSoundFile(const std::string& filename) +static bool IsSoundOrVideoFile(const std::string& filename) { std::string extension; SplitPath(filename, nullptr, nullptr, &extension); @@ -41,19 +41,8 @@ static bool IsSoundFile(const std::string& filename) ".song", // Tales of Symphonia ".ssm", // Custom Robo, Kirby Air Ride, etc. ".str", // Harry Potter & the Sorcerer's Stone - }; - - return extensions.find(extension) != extensions.end(); -} -// Filtered files -static bool IsVideoFile(const std::string& filename) -{ - std::string extension; - SplitPath(filename, nullptr, nullptr, &extension); - Common::ToLower(&extension); - static const std::unordered_set extensions = { - ".thp", // Wii/Game Cube Video File + ".thp", // Wii/Game Cube Video File }; return extensions.find(extension) != extensions.end(); @@ -94,11 +83,8 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part const std::string path = file_info->GetPath(); const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); - // Current logger system does not support colors, so we abuse log levels instead - if (IsSoundFile(path)) + if (IsSoundOrVideoFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string); - else if (IsVideoFile(path)) - NOTICE_LOG_FMT(FILEMON, "{}", log_string); else WARN_LOG_FMT(FILEMON, "{}", log_string); diff --git a/Source/Core/Subtitles/Helpers.cpp b/Source/Core/Subtitles/Helpers.cpp index 5d1c08358296..08bc52defedc 100644 --- a/Source/Core/Subtitles/Helpers.cpp +++ b/Source/Core/Subtitles/Helpers.cpp @@ -10,6 +10,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/Logging/LogManager.h" #include "Common/StringUtil.h" #include "Subtitles/WebColors.h" #include "VideoCommon/OnScreenDisplay.h" @@ -17,6 +18,15 @@ namespace Subtitles { +void OSDInfo(std::string msg) +{ + if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::SUBTITLES, + Common::Log::LogLevel::LWARNING)) + { + OSD::AddMessage(msg, 5000, OSD::Color::GREEN); + } +} + void Info(std::string msg) { OSD::AddMessage(msg, 5000, OSD::Color::GREEN); diff --git a/Source/Core/Subtitles/Helpers.h b/Source/Core/Subtitles/Helpers.h index 19497d8fceff..c48983001871 100644 --- a/Source/Core/Subtitles/Helpers.h +++ b/Source/Core/Subtitles/Helpers.h @@ -11,6 +11,7 @@ namespace Subtitles { +void OSDInfo(std::string msg); void Info(std::string msg); void Error(std::string err); u32 TryParsecolor(const picojson::value& raw, u32 defaultColor); diff --git a/Source/Core/Subtitles/SubtitleEntry.cpp b/Source/Core/Subtitles/SubtitleEntry.cpp index 42fb4ac5bb69..eae6c00459e5 100644 --- a/Source/Core/Subtitles/SubtitleEntry.cpp +++ b/Source/Core/Subtitles/SubtitleEntry.cpp @@ -23,8 +23,6 @@ void SubtitleEntryGroup::Preprocess() hasTimestamps |= subtitleLines[i].Timestamp > 0; } - // Info(fmt::format("HasOffsets: {} HasTimestamps {} File: {}", hasOffsets, hasTimestamps)); - if (hasOffsets) { std::sort(subtitleLines.begin(), subtitleLines.end(), @@ -80,10 +78,13 @@ SubtitleEntry* SubtitleEntryGroup::GetSubtitleForRelativeOffset(u32 offset) } } - return 0; + return nullptr; } SubtitleEntry* SubtitleEntryGroup::GetSubtitleForRelativeTimestamp(u64 timestamp) { + //if Subttile log is enabled, display timestamp for easier subtitle time aligning + OSDInfo(fmt::format("Timestamp: {}", timestamp)); + // from latest to earliest for (auto i = 0; i < subtitleLines.size(); i++) { @@ -103,7 +104,7 @@ SubtitleEntry* SubtitleEntryGroup::GetSubtitleForRelativeTimestamp(u64 timestamp } } - return 0; + return nullptr; } void SubtitleEntryGroup::Add(SubtitleEntry& tl) { diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index 2a6ff4957da7..1c8cb6d1d8ce 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -106,7 +106,7 @@ void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, const std::st if (extension == filter) { - Info("Reading translations from: " + filepath); + OSDInfo(fmt::format("Reading translations from: {}", filepath)); std::string json; File::ReadFileToString(filepath, json); @@ -140,16 +140,11 @@ void LoadSubtitlesForGame(const std::string& gameId) auto subtitleDir = File::GetUserPath(D_SUBTITLES_IDX) + gameId; - if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::SUBTITLES, - Common::Log::LogLevel::LWARNING)) - { - Info(fmt::format("Loading subtitles for {} from {}", gameId, subtitleDir)); - } + OSDInfo(fmt::format("Loading subtitles for {} from {}", gameId, subtitleDir)); auto fileEnumerator = File::ScanDirectoryTree(subtitleDir, true); RecursivelyReadTranslationJsons(fileEnumerator, SubtitleFileExtension); - Info(fmt::format("Translations.empty() = {}", Translations.empty())); if (Translations.empty()) return; From 0d9f99f1df76d9a318edb5ca38443088219e9163 Mon Sep 17 00:00:00 2001 From: PTwr Date: Wed, 18 Oct 2023 20:34:50 +0100 Subject: [PATCH 26/28] include filepath in json error message --- Source/Core/Subtitles/Subtitles.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index 1c8cb6d1d8ce..90087186fce7 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -27,8 +27,13 @@ bool g_messageStacksInitialized = false; bool g_subtitlesInitialized = false; std::map Translations; -void DeserializeSubtitlesJson(std::string& json) +void DeserializeSubtitlesJson(std::string& filepath) { + OSDInfo(fmt::format("Reading translations from: {}", filepath)); + + std::string json; + File::ReadFileToString(filepath, json); + if (json == "") return; @@ -36,13 +41,13 @@ void DeserializeSubtitlesJson(std::string& json) std::string err = picojson::parse(v, json); if (!err.empty()) { - Error(fmt::format("Subtitle JSON Error: {}", err)); + Error(fmt::format("Subtitle JSON Error: {} in {}", err, filepath)); return; } if (!v.is()) { - Error("Subtitle JSON Error: Not an array"); + Error(fmt::format("Subtitle JSON Error: Not an array in {}", filepath)); return; } @@ -106,12 +111,7 @@ void RecursivelyReadTranslationJsons(const File::FSTEntry& folder, const std::st if (extension == filter) { - OSDInfo(fmt::format("Reading translations from: {}", filepath)); - - std::string json; - File::ReadFileToString(filepath, json); - - DeserializeSubtitlesJson(json); + DeserializeSubtitlesJson(filepath); } } } From 20aa5bf8274c046914dc6d15d81725804f9f2049 Mon Sep 17 00:00:00 2001 From: PTwr Date: Thu, 19 Oct 2023 06:29:42 +0100 Subject: [PATCH 27/28] Copy constructor fixes --- Source/Core/Subtitles/Subtitles.cpp | 8 +-- Source/Core/VideoCommon/OnScreenDisplay.cpp | 69 +++++++++++++++++-- Source/Core/VideoCommon/OnScreenDisplay.h | 74 +++------------------ 3 files changed, 73 insertions(+), 78 deletions(-) diff --git a/Source/Core/Subtitles/Subtitles.cpp b/Source/Core/Subtitles/Subtitles.cpp index f9589782fe45..66e1b1cf72a5 100644 --- a/Source/Core/Subtitles/Subtitles.cpp +++ b/Source/Core/Subtitles/Subtitles.cpp @@ -122,13 +122,9 @@ void IniitalizeOSDMessageStacks() if (g_messageStacksInitialized) return; - auto bottomstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, - BottomOSDStackName); - OSD::AddMessageStack(bottomstack); + OSD::AddMessageStack(0, 0, OSD::MessageStackDirection::Upward, true, true, BottomOSDStackName); - auto topstack = OSD::OSDMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, - TopOSDStackName); - OSD::AddMessageStack(topstack); + OSD::AddMessageStack(0, 0, OSD::MessageStackDirection::Downward, true, false, TopOSDStackName); g_messageStacksInitialized = true; } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index 02d047e89857..d0ed4377148b 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -33,7 +33,63 @@ constexpr float MESSAGE_DROP_TIME = 5000.f; // Ms to drop OSD messages that has static std::atomic s_obscured_pixels_left = 0; static std::atomic s_obscured_pixels_top = 0; -static std::multimap s_messages; +struct Message +{ + Message() = default; + Message(std::string text_, u32 duration_, u32 color_, std::unique_ptr icon_ = nullptr, + float scale_ = 1) + : text(std::move(text_)), duration(duration_), color(color_), icon(std::move(icon_)), + scale(scale_) + { + timer.Start(); + } + s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } + std::string text; + Common::Timer timer; + u32 duration = 0; + bool ever_drawn = false; + bool should_discard = false; + u32 color = 0; + std::unique_ptr icon; + std::unique_ptr texture; + float scale = 1; +}; + +struct OSDMessageStack +{ + ImVec2 initialPosOffset; + MessageStackDirection dir; + bool centered; + bool reversed; + std::string name; + std::multimap messages; + + OSDMessageStack() : OSDMessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} + OSDMessageStack(float x_offset, float y_offset, MessageStackDirection dir, bool centered, + bool reversed, std::string name) + : dir(dir), centered(centered), reversed(reversed), name(name) + { + initialPosOffset = ImVec2(x_offset, y_offset); + } + + bool IsVertical() + { + return dir == MessageStackDirection::Downward || dir == MessageStackDirection::Upward; + } + + bool HasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) + { + for (auto it = messages.begin(); it != messages.end(); ++it) + { + if (type == it->first && message == it->second.text) + { + return true; + } + } + return false; + } +}; + // default message stack static OSDMessageStack s_defaultMessageStack = OSDMessageStack(); static std::map messageStacks; @@ -165,11 +221,9 @@ void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, st // A message may hold a reference to a texture that can only be destroyed on the video thread, so // only mark the old typed message (if any) for removal. It will be discarded on the next call to // DrawMessages(). - auto range = s_messages.equal_range(type); + auto range = stack->messages.equal_range(type); for (auto it = range.first; it != range.second; ++it) it->second.should_discard = true; - - stack->messages.erase(type); } stack->messages.emplace(type, Message(std::move(message), ms, argb, std::move(icon), scale)); } @@ -181,9 +235,10 @@ void AddMessage(std::string message, u32 ms, u32 argb, std::unique_ptr ico scale); } -void AddMessageStack(OSDMessageStack& info) +void AddMessageStack(float x_offset, float y_offset, MessageStackDirection dir, bool centered, + bool reversed, std::string name) { - messageStacks.emplace(info.name, info); + messageStacks.emplace(name, OSDMessageStack(x_offset, y_offset, dir, centered, reversed, name)); } void DrawMessages(OSDMessageStack& messageStack) { @@ -217,7 +272,7 @@ void DrawMessages(OSDMessageStack& messageStack) Message& msg = it->second; if (msg.should_discard) { - it = s_messages.erase(it); + it = messageStack.messages.erase(it); continue; } diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 185ff0795bf1..a442162f4f05 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -36,70 +36,6 @@ enum class MessageType Typeless, }; -struct Icon -{ - std::vector rgba_data; - u32 width = 0; - u32 height = 0; -}; // struct Icon - -struct Message -{ - Message() = default; - Message(std::string text_, u32 duration_, u32 color_, std::unique_ptr icon_ = nullptr, float scale_ = 1) - : text(std::move(text_)), duration(duration_), color(color_), icon(std::move(icon_)), scale(scale_) - { - timer.Start(); - } - s64 TimeRemaining() const { return duration - timer.ElapsedMs(); } - std::string text; - Common::Timer timer; - u32 duration = 0; - bool ever_drawn = false; - bool should_discard = false; - u32 color = 0; - std::unique_ptr icon; - std::unique_ptr texture; - float scale = 1; -}; - -struct OSDMessageStack -{ - OSDMessageStack(OSDMessageStack& t) {} - - ImVec2 initialPosOffset; - MessageStackDirection dir; - bool centered; - bool reversed; - std::string name; - std::multimap messages; - - OSDMessageStack() : OSDMessageStack(0, 0, MessageStackDirection::Downward, false, false, "") {} - OSDMessageStack(float x_offset, float y_offset, MessageStackDirection dir, bool centered, - bool reversed, std::string name) - : dir(dir), centered(centered), reversed(reversed), name(name) - { - initialPosOffset = ImVec2(x_offset, y_offset); - } - - bool IsVertical() - { - return dir == MessageStackDirection::Downward || dir == MessageStackDirection::Upward; - } - - bool HasMessage(std::string message, MessageType type = OSD::MessageType::Typeless) - { - for (auto it = messages.begin(); it != messages.end(); ++it) - { - if (type == it->first && message == it->second.text) - { - return true; - } - } - return false; - } -}; - namespace Color { constexpr u32 CYAN = 0xFF00FFFF; @@ -115,7 +51,15 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration -void AddMessageStack(OSDMessageStack& info); +struct Icon +{ + std::vector rgba_data; + u32 width = 0; + u32 height = 0; +}; // struct Icon + +void AddMessageStack(float x_offset, float y_offset, MessageStackDirection dir, bool centered, + bool reversed, std::string name); // On-screen message display (colored yellow by default) void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, From d86a68e4bf6eb133e44151bfc99d8a277c75bb2f Mon Sep 17 00:00:00 2001 From: PTwr Date: Tue, 7 Nov 2023 09:59:32 +0100 Subject: [PATCH 28/28] relative offsets in filemonitor logs --- Source/Core/Core/HW/DVD/FileMonitor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 885b4ef20f6f..93036874ac5b 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -74,14 +74,16 @@ void FileLogger::Log(const DiscIO::Volume& volume, const DiscIO::Partition& part return; const u64 file_offset = file_info->GetOffset(); + const u64 relativeOffset = offset - file_info->GetOffset(); + // TODO add last_log time to keep logging streamed asset offsets without spamming logs? Or another LogType so user can enable nonstop logging? // Do nothing if we found the same file again if (m_previous_partition == partition && m_previous_file_offset == file_offset) return; const std::string size_string = Common::ThousandSeparate(file_info->GetSize() / 1000, 7); const std::string path = file_info->GetPath(); - const std::string log_string = fmt::format("{} kB {} offset {}", size_string, path, offset); + const std::string log_string = fmt::format("{} kB {} offset {} fileOffset {} relativeOffset {}", size_string, path, offset, file_offset, relativeOffset); if (IsSoundOrVideoFile(path)) INFO_LOG_FMT(FILEMON, "{}", log_string);