From 9845b0a889808116b28061675ef4e0f66c8e90d4 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Fri, 10 Oct 2025 10:18:43 +0100 Subject: [PATCH 01/70] chore: attach to console on windows if possible --- src/ModMan/main.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp index a6afc7bb..66780792 100644 --- a/src/ModMan/main.cpp +++ b/src/ModMan/main.cpp @@ -5,13 +5,16 @@ #include "Web/ViteAssets.h" #include "Web/WebViewLib.h" -#include #include #include #include #include #include +#ifdef _WIN32 +#include +#endif + using namespace std::string_literals; using namespace PLATFORM_NAMESPACE; @@ -105,10 +108,23 @@ namespace #ifdef _WIN32 int WINAPI WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) -{ #else int main() +#endif { +#ifdef _WIN32 + // Attach console if possible on Windows for stdout/stderr in console + if (AttachConsole(-1)) + { + FILE* fDummy; + (void)freopen_s(&fDummy, "CONOUT$", "w", stdout); + (void)freopen_s(&fDummy, "CONOUT$", "w", stderr); + (void)freopen_s(&fDummy, "CONIN$", "r", stdin); + std::cout.clear(); + std::clog.clear(); + std::cerr.clear(); + std::cin.clear(); + } #endif con::info("Starting ModMan " GIT_VERSION); From 219f0c1c85d8f98cdbc92d9f322d88a30868fad0 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Fri, 10 Oct 2025 10:57:07 +0100 Subject: [PATCH 02/70] chore: save shared modman info in context --- src/ModMan/Context/ModManContext.cpp | 7 ++++ src/ModMan/Context/ModManContext.h | 14 +++++++ src/ModMan/Web/Binds/Binds.cpp | 11 +++++ src/ModMan/Web/Binds/Binds.h | 8 ++++ src/ModMan/main.cpp | 61 +++++++++++----------------- src/ModManUi/src/App.vue | 25 +----------- src/ModManUi/src/native/index.ts | 6 +-- 7 files changed, 68 insertions(+), 64 deletions(-) create mode 100644 src/ModMan/Context/ModManContext.cpp create mode 100644 src/ModMan/Context/ModManContext.h create mode 100644 src/ModMan/Web/Binds/Binds.cpp create mode 100644 src/ModMan/Web/Binds/Binds.h diff --git a/src/ModMan/Context/ModManContext.cpp b/src/ModMan/Context/ModManContext.cpp new file mode 100644 index 00000000..ff4bbe9e --- /dev/null +++ b/src/ModMan/Context/ModManContext.cpp @@ -0,0 +1,7 @@ +#include "ModManContext.h" + +ModManContext& ModManContext::Get() +{ + static ModManContext context; + return context; +} diff --git a/src/ModMan/Context/ModManContext.h b/src/ModMan/Context/ModManContext.h new file mode 100644 index 00000000..05f72099 --- /dev/null +++ b/src/ModMan/Context/ModManContext.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Web/WebViewLib.h" + +#include + +class ModManContext +{ +public: + static ModManContext& Get(); + + std::unique_ptr m_main_webview; + std::unique_ptr m_dev_tools_webview; +}; diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp new file mode 100644 index 00000000..9e41b3a0 --- /dev/null +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -0,0 +1,11 @@ +#include "Binds.h" + +#include "Web/Binds/DialogBinds.h" + +namespace ui +{ + void RegisterAllBinds(webview::webview& wv) + { + RegisterDialogHandlerBinds(wv); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/Binds.h b/src/ModMan/Web/Binds/Binds.h new file mode 100644 index 00000000..3dd717ba --- /dev/null +++ b/src/ModMan/Web/Binds/Binds.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Web/WebViewLib.h" + +namespace ui +{ + void RegisterAllBinds(webview::webview& wv); +} diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp index 66780792..9321760e 100644 --- a/src/ModMan/main.cpp +++ b/src/ModMan/main.cpp @@ -1,5 +1,6 @@ -#include "GitVersion.h" -#include "Web/Binds/DialogBinds.h" +#include "Context/ModManContext.h" +#include "GitVersion.h" +#include "Web/Binds/Binds.h" #include "Web/Platform/AssetHandler.h" #include "Web/UiCommunication.h" #include "Web/ViteAssets.h" @@ -7,7 +8,6 @@ #include #include -#include #include #include @@ -21,15 +21,17 @@ using namespace PLATFORM_NAMESPACE; namespace { #ifdef _DEBUG - std::optional devToolWindow; - - void RunDevToolsWindow() + void SpawnDevToolsWindow() { con::debug("Creating dev tools window"); + auto& context = ModManContext::Get(); + try { - auto& newWindow = devToolWindow.emplace(false, nullptr); + context.m_dev_tools_webview = std::make_unique(false, nullptr); + auto& newWindow = *context.m_dev_tools_webview; + newWindow.set_title("Devtools"); newWindow.set_size(640, 480, WEBVIEW_HINT_NONE); newWindow.set_size(480, 320, WEBVIEW_HINT_MIN); @@ -42,59 +44,44 @@ namespace } #endif - int RunMainWindow() + int SpawnMainWindow() { con::debug("Creating main window"); + auto& context = ModManContext::Get(); try { - webview::webview w( + context.m_main_webview = std::make_unique( #ifdef _DEBUG true, #else false, #endif nullptr); - w.set_title("OpenAssetTools ModMan"); - w.set_size(1280, 640, WEBVIEW_HINT_NONE); - w.set_size(480, 320, WEBVIEW_HINT_MIN); + auto& newWindow = *context.m_main_webview; - ui::RegisterDialogHandlerBinds(w); + newWindow.set_title("OpenAssetTools ModMan"); + newWindow.set_size(1280, 640, WEBVIEW_HINT_NONE); + newWindow.set_size(480, 320, WEBVIEW_HINT_MIN); - // A binding that counts up or down and immediately returns the new value. - ui::Bind(w, - "greet", - [&w](std::string name) -> std::string - { - ui::Notify(w, "greeting", name); - return std::format("Hello from C++ {}!", name); - }); - -#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) - InstallAssetHandler(w); - constexpr auto urlPrefix = URL_PREFIX; -#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) - InstallAssetHandler(w); - constexpr auto urlPrefix = URL_PREFIX; -#else -#error Unsupported platform -#endif + InstallAssetHandler(newWindow); + ui::RegisterAllBinds(newWindow); #ifdef _DEBUG - w.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", urlPrefix)); + newWindow.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", URL_PREFIX)); if (VITE_DEV_SERVER) { - w.dispatch( + newWindow.dispatch( [] { - RunDevToolsWindow(); + SpawnDevToolsWindow(); }); } #else - w.navigate(std::format("{}index.html", urlPrefix)); + newWindow.navigate(std::format("{}index.html", URL_PREFIX)); #endif - w.run(); + newWindow.run(); } catch (const webview::exception& e) { @@ -129,7 +116,7 @@ int main() con::info("Starting ModMan " GIT_VERSION); - const auto result = RunMainWindow(); + const auto result = SpawnMainWindow(); return result; } diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 1ff66ed6..0db0d725 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,28 +1,13 @@ diff --git a/src/ModManUi/src/native/FastFileBinds.ts b/src/ModManUi/src/native/FastFileBinds.ts new file mode 100644 index 00000000..4f417902 --- /dev/null +++ b/src/ModManUi/src/native/FastFileBinds.ts @@ -0,0 +1,3 @@ +export interface FastFileBinds { + loadFastFile(path: string): Promise; +} diff --git a/src/ModManUi/src/native/index.ts b/src/ModManUi/src/native/index.ts index 9893cbbe..9ab6257a 100644 --- a/src/ModManUi/src/native/index.ts +++ b/src/ModManUi/src/native/index.ts @@ -1,7 +1,8 @@ import type { DialogBinds } from "./DialogBinds"; +import type { FastFileBinds } from "./FastFileBinds"; -export type NativeMethods = DialogBinds; +export type NativeMethods = DialogBinds & FastFileBinds; interface NativeEventMap { From 2037cf3258fb9ed03b390f1e80f12df4075ec857 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 14:15:04 +0100 Subject: [PATCH 06/70] chore: add ModMan args --- src/ModMan/ModManArgs.cpp | 105 ++++++++++++++++++++++++++++++++++++++ src/ModMan/ModManArgs.h | 19 +++++++ src/ModMan/main.cpp | 15 +++++- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/ModMan/ModManArgs.cpp create mode 100644 src/ModMan/ModManArgs.h diff --git a/src/ModMan/ModManArgs.cpp b/src/ModMan/ModManArgs.cpp new file mode 100644 index 00000000..b5b1f88c --- /dev/null +++ b/src/ModMan/ModManArgs.cpp @@ -0,0 +1,105 @@ +#include "ModManArgs.h" + +#include "GitVersion.h" +#include "Utils/Arguments/UsageInformation.h" +#include "Utils/Logging/Log.h" + +#include +#include + +namespace +{ + // clang-format off + const CommandLineOption* const OPTION_HELP = + CommandLineOption::Builder::Create() + .WithShortName("?") + .WithLongName("help") + .WithDescription("Displays usage information.") + .Build(); + + const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create() + .WithLongName("version") + .WithDescription("Prints the application version.") + .Build(); + + const CommandLineOption* const OPTION_VERBOSE = + CommandLineOption::Builder::Create() + .WithShortName("v") + .WithLongName("verbose") + .WithDescription("Outputs a lot more and more detailed messages.") + .Build(); + + const CommandLineOption* const OPTION_NO_COLOR = + CommandLineOption::Builder::Create() + .WithLongName("no-color") + .WithDescription("Disables colored terminal output.") + .Build(); + // clang-format on + + const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ + OPTION_HELP, + OPTION_VERSION, + OPTION_VERBOSE, + OPTION_NO_COLOR, + }; +} // namespace + +ModManArgs::ModManArgs() + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v) +{ +} + +void ModManArgs::PrintUsage() const +{ + UsageInformation usage(m_argument_parser.GetExecutableName()); + + for (const auto* commandLineOption : COMMAND_LINE_OPTIONS) + { + usage.AddCommandLineOption(commandLineOption); + } + + usage.Print(); +} + +void ModManArgs::PrintVersion() +{ + con::info("OpenAssetTools ModMan {}", GIT_VERSION); +} + +bool ModManArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue) +{ + shouldContinue = true; + if (!m_argument_parser.ParseArguments(argc, argv)) + { + PrintUsage(); + return false; + } + + // Check if the user requested help + if (m_argument_parser.IsOptionSpecified(OPTION_HELP)) + { + PrintUsage(); + shouldContinue = false; + return true; + } + + // Check if the user wants to see the version + if (m_argument_parser.IsOptionSpecified(OPTION_VERSION)) + { + PrintVersion(); + shouldContinue = false; + return true; + } + + // -v; --verbose + if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE)) + con::globalLogLevel = con::LogLevel::DEBUG; + else + con::globalLogLevel = con::LogLevel::INFO; + + // --no-color + con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR); + + return true; +} diff --git a/src/ModMan/ModManArgs.h b/src/ModMan/ModManArgs.h new file mode 100644 index 00000000..feb19628 --- /dev/null +++ b/src/ModMan/ModManArgs.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Utils/Arguments/ArgumentParser.h" + +class ModManArgs +{ +public: + ModManArgs(); + bool ParseArgs(int argc, const char** argv, bool& shouldContinue); + +private: + /** + * \brief Prints a command line usage help text for the ModMan tool to stdout. + */ + void PrintUsage() const; + static void PrintVersion(); + + ArgumentParser m_argument_parser; +}; diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp index 5c34be42..4b4f69ae 100644 --- a/src/ModMan/main.cpp +++ b/src/ModMan/main.cpp @@ -1,5 +1,6 @@ #include "Context/ModManContext.h" #include "GitVersion.h" +#include "ModManArgs.h" #include "Web/Binds/Binds.h" #include "Web/Platform/AssetHandler.h" #include "Web/UiCommunication.h" @@ -94,9 +95,13 @@ namespace } // namespace #ifdef _WIN32 +#define MODMAN_ARGC __argc +#define MODMAN_ARGV const_cast(__argv) int WINAPI WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) #else -int main() +#define MODMAN_ARGC argc +#define MODMAN_ARGV argv +int main(int argc, const char** argv) #endif { #ifdef _WIN32 @@ -114,6 +119,14 @@ int main() } #endif + ModManArgs args; + auto shouldContinue = true; + if (!args.ParseArgs(MODMAN_ARGC, MODMAN_ARGV, shouldContinue)) + return false; + + if (!shouldContinue) + return true; + con::info("Starting ModMan " GIT_VERSION); ModManContext::Get().Startup(); From 098be5355911f3572644b590004fe331052e9145 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 14:42:52 +0100 Subject: [PATCH 07/70] chore: add error handling for fastfile bind --- src/ModMan/Context/FastFileContext.cpp | 9 ++++++--- src/ModMan/Context/FastFileContext.h | 3 ++- src/ModMan/Web/Binds/FastFileBinds.cpp | 28 ++++++++++++++++++-------- src/ModManUi/src/App.vue | 10 +++++++-- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 1ea213e1..51d0814e 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -2,8 +2,11 @@ #include "ZoneLoading.h" -bool FastFileContext::LoadFastFile(const std::string& path) +std::optional FastFileContext::LoadFastFile(const std::string& path) { - m_loaded_zones.emplace_back(ZoneLoading::LoadZone(path)); - return true; + auto zone = ZoneLoading::LoadZone(path); + if (!zone) + return std::nullopt; + + return m_loaded_zones.emplace_back(std::move(zone)).get(); } diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index 9aed2d8c..54fa0114 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -2,12 +2,13 @@ #include "Zone/Zone.h" #include +#include #include class FastFileContext { public: - bool LoadFastFile(const std::string& path); + std::optional LoadFastFile(const std::string& path); std::vector> m_loaded_zones; }; diff --git a/src/ModMan/Web/Binds/FastFileBinds.cpp b/src/ModMan/Web/Binds/FastFileBinds.cpp index 3bfc74cb..a86ea0bf 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.cpp +++ b/src/ModMan/Web/Binds/FastFileBinds.cpp @@ -5,8 +5,26 @@ namespace { + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, path] + { + const auto maybeZone = ModManContext::Get().m_fast_file.LoadFastFile(path); -} + if (maybeZone) + { + ui::PromiseResolve(wv, id, true); + con::debug("Loaded zone \"{}\"", maybeZone.value()->m_name); + } + else + { + con::warn("Failed to load zone \"{}\"", path); + ui::PromiseReject(wv, id, false); + } + }); + } +} // namespace namespace ui { @@ -16,13 +34,7 @@ namespace ui "loadFastFile", [&wv](const std::string& id, std::string path) { - std::string idMove(id); - ModManContext::Get().m_db_thread.Dispatch( - [&wv, idMove, path] - { - ModManContext::Get().m_fast_file.LoadFastFile(path); - PromiseResolve(wv, idMove, true); - }); + LoadFastFile(wv, id, std::move(path)); }); } } // namespace ui diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index b360774f..3e15c414 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -9,9 +9,15 @@ async function onOpenFastfileClick() { lastPath.value = (await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] })) ?? ""; - loadingFastFile.value = true; - await webviewBinds.loadFastFile(lastPath.value); + loadingFastFile.value = true; + + webviewBinds.loadFastFile(lastPath.value) + .catch((e) => { + console.error("Failed to load fastfile", e); + }) + .finally(() => { loadingFastFile.value = false; + }); } From b27b7e77bdc99aab9d859390e62f3e213d4067fa Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 15:28:19 +0100 Subject: [PATCH 08/70] chore: pass ZoneLoading error as result --- src/Linker/Linker.cpp | 10 +- src/ModMan/Context/FastFileContext.cpp | 6 +- src/ModMan/Context/FastFileContext.h | 5 +- src/ModMan/Web/Binds/FastFileBinds.cpp | 6 +- src/ModManUi/src/App.vue | 4 +- src/Unlinker/Unlinker.cpp | 22 ++- src/Utils/Utils/Result.h | 227 ++++++++++++++++--------- src/ZoneLoading/ZoneLoading.cpp | 18 +- src/ZoneLoading/ZoneLoading.h | 4 +- 9 files changed, 181 insertions(+), 121 deletions(-) diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index 61050cf0..3da46b23 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -364,14 +364,16 @@ class LinkerImpl final : public Linker zoneDirectory = fs::current_path(); auto absoluteZoneDirectory = absolute(zoneDirectory).string(); - auto zone = ZoneLoading::LoadZone(zonePath); - if (!zone) + auto maybeZone = ZoneLoading::LoadZone(zonePath); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } - con::debug("Load zone \"{}\"", zone->m_name); + auto zone = std::move(*maybeZone); + + con::debug("Loaded zone \"{}\"", zone->m_name); m_loaded_zones.emplace_back(std::move(zone)); } diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 51d0814e..823d595c 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -2,11 +2,11 @@ #include "ZoneLoading.h" -std::optional FastFileContext::LoadFastFile(const std::string& path) +result::Expected FastFileContext::LoadFastFile(const std::string& path) { auto zone = ZoneLoading::LoadZone(path); if (!zone) - return std::nullopt; + return result::Unexpected(std::move(zone.error())); - return m_loaded_zones.emplace_back(std::move(zone)).get(); + return m_loaded_zones.emplace_back(std::move(*zone)).get(); } diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index 54fa0114..74b0b3af 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -1,14 +1,15 @@ #pragma once + +#include "Utils/Result.h" #include "Zone/Zone.h" #include -#include #include class FastFileContext { public: - std::optional LoadFastFile(const std::string& path); + result::Expected LoadFastFile(const std::string& path); std::vector> m_loaded_zones; }; diff --git a/src/ModMan/Web/Binds/FastFileBinds.cpp b/src/ModMan/Web/Binds/FastFileBinds.cpp index a86ea0bf..fab40843 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.cpp +++ b/src/ModMan/Web/Binds/FastFileBinds.cpp @@ -10,7 +10,7 @@ namespace ModManContext::Get().m_db_thread.Dispatch( [&wv, id, path] { - const auto maybeZone = ModManContext::Get().m_fast_file.LoadFastFile(path); + auto maybeZone = ModManContext::Get().m_fast_file.LoadFastFile(path); if (maybeZone) { @@ -19,8 +19,8 @@ namespace } else { - con::warn("Failed to load zone \"{}\"", path); - ui::PromiseReject(wv, id, false); + con::warn("Failed to load zone \"{}\": {}", path, maybeZone.error()); + ui::PromiseReject(wv, id, std::move(maybeZone).error()); } }); } diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 3e15c414..85054db9 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -12,8 +12,8 @@ async function onOpenFastfileClick() { loadingFastFile.value = true; webviewBinds.loadFastFile(lastPath.value) - .catch((e) => { - console.error("Failed to load fastfile", e); + .catch((e: string) => { + console.error("Failed to load fastfile:", e); }) .finally(() => { loadingFastFile.value = false; diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 790e75c0..1503d215 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -227,13 +227,15 @@ private: auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string(); auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory); - auto zone = ZoneLoading::LoadZone(zonePath); - if (zone == nullptr) + auto maybeZone = ZoneLoading::LoadZone(zonePath); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } + auto zone = std::move(*maybeZone); + con::debug("Loaded zone \"{}\"", zone->m_name); if (ShouldLoadObj()) @@ -287,16 +289,16 @@ private: auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory); - std::string zoneName; - auto zone = ZoneLoading::LoadZone(zonePath); - if (zone == nullptr) + auto maybeZone = ZoneLoading::LoadZone(zonePath); + if (!maybeZone) { - con::error("Failed to load zone \"{}\".", zonePath); + con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error()); return false; } - zoneName = zone->m_name; - con::debug("Loaded zone \"{}\"", zoneName); + auto zone = std::move(*maybeZone); + + con::debug("Loaded zone \"{}\"", zone->m_name); const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id); if (ShouldLoadObj()) @@ -308,6 +310,8 @@ private: if (ShouldLoadObj()) objLoader->UnloadContainersOfZone(*zone); + // Copy zone name for using it after freeing the zone + std::string zoneName = zone->m_name; zone.reset(); con::debug("Unloaded zone \"{}\"", zoneName); } diff --git a/src/Utils/Utils/Result.h b/src/Utils/Utils/Result.h index 2e932393..b5d870c0 100644 --- a/src/Utils/Utils/Result.h +++ b/src/Utils/Utils/Result.h @@ -6,112 +6,171 @@ using NoResult = std::monostate; // Can be replaced by std::expected with c++23 -template class Result +namespace result { -public: - Result(TResult result) - : m_data(std::variant(std::in_place_index<0>, std::move(result))) + template class Unexpected { - } + public: + Unexpected(TError result) + : m_data(std::move(result)) + { + } - static Result Ok(TResult result) + constexpr std::add_lvalue_reference_t value() & + { + return m_data; + } + + constexpr std::add_const_t> value() const& + { + return m_data; + } + + constexpr std::add_rvalue_reference_t value() && + { + return std::move(m_data); + } + + constexpr std::add_const_t> value() const&& + { + return std::move(m_data); + } + + constexpr std::add_lvalue_reference_t operator*() & + { + return m_data; + } + + constexpr std::add_const_t> operator*() const& + { + return m_data; + } + + constexpr std::add_rvalue_reference_t operator*() && + { + return std::move(m_data); + } + + constexpr std::add_const_t> operator*() const&& + { + return std::move(m_data); + } + + constexpr std::add_pointer_t operator->() + { + return m_data; + } + + constexpr std::add_const_t> operator->() const + { + return m_data; + } + + private: + TError m_data; + }; + + template class Expected { - return Result(std::in_place_index<0>, std::move(result)); - } + public: + Expected(TResult result) + : m_data(std::variant(std::in_place_index<0>, std::move(result))) + { + } - static Result Bad(TError error) - { - return Result(std::in_place_index<1>, std::move(error)); - } + Expected(Unexpected unexpected) + : m_data(std::variant(std::in_place_index<1>, std::move(*unexpected))) + { + } - constexpr operator bool() const noexcept - { - return m_data.index() == 0; - } + constexpr operator bool() const noexcept + { + return m_data.index() == 0; + } - constexpr bool has_value() const noexcept - { - return m_data.index() == 0; - } + constexpr bool has_value() const noexcept + { + return m_data.index() == 0; + } - constexpr std::add_lvalue_reference_t value() & - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_lvalue_reference_t value() & + { + return std::get<0>(m_data); + } - constexpr std::add_const_t> value() const& - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_const_t> value() const& + { + return std::get<0>(m_data); + } - constexpr std::add_rvalue_reference_t value() && - { - return std::move(std::get<0, TResult>(m_data)); - } + constexpr std::add_rvalue_reference_t value() && + { + return std::move(std::get<0>(m_data)); + } - constexpr std::add_const_t> value() const&& - { - return std::move(std::get<0, TResult>(m_data)); - } + constexpr std::add_const_t> value() const&& + { + return std::move(std::get<0>(m_data)); + } - constexpr std::add_lvalue_reference_t operator*() & - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_lvalue_reference_t operator*() & + { + return std::get<0>(m_data); + } - constexpr std::add_const_t> operator*() const& - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_const_t> operator*() const& + { + return std::get<0>(m_data); + } - constexpr std::add_rvalue_reference_t operator*() && - { - return std::move(std::get<0, TResult>(m_data)); - } + constexpr std::add_rvalue_reference_t operator*() && + { + return std::move(std::get<0>(m_data)); + } - constexpr std::add_const_t> operator*() const&& - { - return std::move(std::get<0, TResult>(m_data)); - } + constexpr std::add_const_t> operator*() const&& + { + return std::move(std::get<0>(m_data)); + } - constexpr std::add_pointer_t operator->() - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_pointer_t operator->() + { + return std::get<0>(m_data); + } - constexpr std::add_const_t> operator->() const - { - return std::get<0, TResult>(m_data); - } + constexpr std::add_const_t> operator->() const + { + return std::get<0>(m_data); + } - constexpr std::add_lvalue_reference_t error() & - { - return std::get<1, TError>(m_data); - } + constexpr std::add_lvalue_reference_t error() & + { + return std::get<1>(m_data); + } - constexpr std::add_const_t> error() const& - { - return std::get<1, TError>(m_data); - } + constexpr std::add_const_t> error() const& + { + return std::get<1>(m_data); + } - constexpr std::add_rvalue_reference_t error() && - { - return std::move(std::get<1, TError>(m_data)); - } + constexpr std::add_rvalue_reference_t error() && + { + return std::move(std::get<1>(m_data)); + } - constexpr std::add_const_t> error() const&& - { - return std::move(std::get<1, TError>(m_data)); - } + constexpr std::add_const_t> error() const&& + { + return std::move(std::get<1>(m_data)); + } -private: - explicit Result(std::variant data) - : m_data(std::move(data)) - { - } + private: + explicit Expected(std::variant data) + : m_data(std::move(data)) + { + } - std::variant m_data; -}; + std::variant m_data; + }; #define ENSURE_RESULT_VAR(var) \ if (!(var)) \ @@ -122,3 +181,5 @@ private: if (!result) \ return result; \ } + +} // namespace result diff --git a/src/ZoneLoading/ZoneLoading.cpp b/src/ZoneLoading/ZoneLoading.cpp index 3d15b80c..36e2a2ed 100644 --- a/src/ZoneLoading/ZoneLoading.cpp +++ b/src/ZoneLoading/ZoneLoading.cpp @@ -2,7 +2,6 @@ #include "Loading/IZoneLoaderFactory.h" #include "Loading/ZoneLoader.h" -#include "Utils/Logging/Log.h" #include "Utils/ObjFileStream.h" #include @@ -12,24 +11,18 @@ namespace fs = std::filesystem; -std::unique_ptr ZoneLoading::LoadZone(const std::string& path) +result::Expected, std::string> ZoneLoading::LoadZone(const std::string& path) { auto zoneName = fs::path(path).filename().replace_extension().string(); std::ifstream file(path, std::fstream::in | std::fstream::binary); if (!file.is_open()) - { - con::error("Could not open file '{}'.", path); - return nullptr; - } + return result::Unexpected(std::format("Could not open file '{}'.", path)); ZoneHeader header{}; file.read(reinterpret_cast(&header), sizeof(header)); if (file.gcount() != sizeof(header)) - { - con::error("Failed to read zone header from file '{}'.", path); - return nullptr; - } + return result::Unexpected(std::format("Failed to read zone header from file '{}'.", path)); std::unique_ptr zoneLoader; for (auto game = 0u; game < static_cast(GameId::COUNT); game++) @@ -42,10 +35,7 @@ std::unique_ptr ZoneLoading::LoadZone(const std::string& path) } if (!zoneLoader) - { - con::error("Could not create factory for zone '{}'.", zoneName); - return nullptr; - } + return result::Unexpected(std::format("Could not create factory for zone '{}'.", zoneName)); auto loadedZone = zoneLoader->LoadZone(file); diff --git a/src/ZoneLoading/ZoneLoading.h b/src/ZoneLoading/ZoneLoading.h index 749db050..330b3421 100644 --- a/src/ZoneLoading/ZoneLoading.h +++ b/src/ZoneLoading/ZoneLoading.h @@ -1,4 +1,6 @@ #pragma once + +#include "Utils/Result.h" #include "Zone/Zone.h" #include @@ -6,5 +8,5 @@ class ZoneLoading { public: - static std::unique_ptr LoadZone(const std::string& path); + static result::Expected, std::string> LoadZone(const std::string& path); }; From 7cefaee41cbcd3303775824f85d45de5164a03c8 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 15:30:19 +0100 Subject: [PATCH 09/70] fix: dialog binds not returning a promise --- src/ModManUi/src/native/DialogBinds.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ModManUi/src/native/DialogBinds.ts b/src/ModManUi/src/native/DialogBinds.ts index 8c7e66b7..97ddce0b 100644 --- a/src/ModManUi/src/native/DialogBinds.ts +++ b/src/ModManUi/src/native/DialogBinds.ts @@ -12,7 +12,7 @@ export interface SaveFileDialogDto { } export interface DialogBinds { - openFileDialog(options?: OpenFileDialogDto): string | null; - saveFileDialog(options?: SaveFileDialogDto): string | null; - folderSelectDialog(): string | null; + openFileDialog(options?: OpenFileDialogDto): Promise; + saveFileDialog(options?: SaveFileDialogDto): Promise; + folderSelectDialog(): Promise; } From 49f2000bad94324c1280cddafe3065e4d8c93adf Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 16:25:14 +0100 Subject: [PATCH 10/70] chore: track loaded zones in ui --- src/ModMan/Context/FastFileContext.cpp | 32 +++++++++++- src/ModMan/Context/FastFileContext.h | 3 ++ src/ModMan/Context/ModManContext.cpp | 1 + src/ModMan/Web/Binds/Binds.cpp | 2 +- src/ModMan/Web/Binds/FastFileBinds.cpp | 64 +++++++++++++++++++++++- src/ModMan/Web/Binds/FastFileBinds.h | 7 ++- src/ModManUi/src/App.vue | 31 +++++++++--- src/ModManUi/src/native/FastFileBinds.ts | 15 ++++++ src/ModManUi/src/native/index.ts | 7 +-- src/ModManUi/src/stores/ZoneStore.ts | 20 ++++++++ src/ModManUi/src/stores/counter.ts | 12 ----- src/ZoneCommon/Zone/Zone.cpp | 3 +- 12 files changed, 166 insertions(+), 31 deletions(-) create mode 100644 src/ModManUi/src/stores/ZoneStore.ts delete mode 100644 src/ModManUi/src/stores/counter.ts diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 823d595c..6e9319ee 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -1,12 +1,42 @@ #include "FastFileContext.h" +#include "Web/Binds/FastFileBinds.h" +#include "Web/UiCommunication.h" #include "ZoneLoading.h" +void FastFileContext::Destroy() +{ + // Unload all zones + m_loaded_zones.clear(); +} + result::Expected FastFileContext::LoadFastFile(const std::string& path) { auto zone = ZoneLoading::LoadZone(path); if (!zone) return result::Unexpected(std::move(zone.error())); - return m_loaded_zones.emplace_back(std::move(*zone)).get(); + auto* result = m_loaded_zones.emplace_back(std::move(*zone)).get(); + + ui::NotifyZoneLoaded(result->m_name, path); + + return result; +} + +result::Expected FastFileContext::UnloadZone(const std::string& zoneName) +{ + const auto existingZone = std::ranges::find_if(m_loaded_zones, + [&zoneName](const std::unique_ptr& zone) + { + return zone->m_name == zoneName; + }); + + if (existingZone != m_loaded_zones.end()) + { + m_loaded_zones.erase(existingZone); + ui::NotifyZoneUnloaded(zoneName); + return NoResult(); + } + + return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); } diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index 74b0b3af..a3f87a42 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -9,7 +9,10 @@ class FastFileContext { public: + void Destroy(); + result::Expected LoadFastFile(const std::string& path); + result::Expected UnloadZone(const std::string& zoneName); std::vector> m_loaded_zones; }; diff --git a/src/ModMan/Context/ModManContext.cpp b/src/ModMan/Context/ModManContext.cpp index cf01802f..221ebbf6 100644 --- a/src/ModMan/Context/ModManContext.cpp +++ b/src/ModMan/Context/ModManContext.cpp @@ -13,5 +13,6 @@ void ModManContext::Startup() void ModManContext::Destroy() { + m_fast_file.Destroy(); m_db_thread.Terminate(); } diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp index d75e5026..ab1668f5 100644 --- a/src/ModMan/Web/Binds/Binds.cpp +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -8,6 +8,6 @@ namespace ui void RegisterAllBinds(webview::webview& wv) { RegisterDialogHandlerBinds(wv); - RegisterFastFileBinds(wv); + RegisterZoneBinds(wv); } } // namespace ui diff --git a/src/ModMan/Web/Binds/FastFileBinds.cpp b/src/ModMan/Web/Binds/FastFileBinds.cpp index fab40843..b2cf0d50 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.cpp +++ b/src/ModMan/Web/Binds/FastFileBinds.cpp @@ -3,8 +3,27 @@ #include "Context/ModManContext.h" #include "Web/UiCommunication.h" +#include "Json/JsonExtension.h" + namespace { + class ZoneLoadedDto + { + public: + std::string zoneName; + std::string filePath; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + + class ZoneUnloadedDto + { + public: + std::string zoneName; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety { ModManContext::Get().m_db_thread.Dispatch( @@ -24,11 +43,47 @@ namespace } }); } + + void UnloadZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, zoneName] + { + auto result = ModManContext::Get().m_fast_file.UnloadZone(zoneName); + if (result) + { + con::debug("Unloaded zone \"{}\"", zoneName); + ui::PromiseResolve(wv, id, true); + } + else + { + con::warn("Failed unloading zone {}: {}", zoneName, result.error()); + ui::PromiseReject(wv, id, std::move(result).error()); + } + }); + } } // namespace namespace ui { - void RegisterFastFileBinds(webview::webview& wv) + void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath) + { + const ZoneLoadedDto dto{ + .zoneName = std::move(zoneName), + .filePath = std::move(fastFilePath), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto); + } + + void NotifyZoneUnloaded(std::string zoneName) + { + const ZoneUnloadedDto dto{ + .zoneName = std::move(zoneName), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneUnloaded", dto); + } + + void RegisterZoneBinds(webview::webview& wv) { BindAsync(wv, "loadFastFile", @@ -36,5 +91,12 @@ namespace ui { LoadFastFile(wv, id, std::move(path)); }); + + BindAsync(wv, + "unloadZone", + [&wv](const std::string& id, std::string zoneName) + { + UnloadZone(wv, id, std::move(zoneName)); + }); } } // namespace ui diff --git a/src/ModMan/Web/Binds/FastFileBinds.h b/src/ModMan/Web/Binds/FastFileBinds.h index dc45ad9a..cf11f10d 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.h +++ b/src/ModMan/Web/Binds/FastFileBinds.h @@ -4,5 +4,8 @@ namespace ui { - void RegisterFastFileBinds(webview::webview& wv); -} + void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath); + void NotifyZoneUnloaded(std::string zoneName); + + void RegisterZoneBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 85054db9..4a6a761c 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,7 +1,9 @@ @@ -28,9 +37,15 @@ async function onOpenFastfileClick() {

- The last path: {{ lastPath }} Loading: {{ loadingFastFile }}

+
+

Loaded zones:

+
+ {{ zone }} + +
+
diff --git a/src/ModManUi/src/native/FastFileBinds.ts b/src/ModManUi/src/native/FastFileBinds.ts index 4f417902..556d3210 100644 --- a/src/ModManUi/src/native/FastFileBinds.ts +++ b/src/ModManUi/src/native/FastFileBinds.ts @@ -1,3 +1,18 @@ +export interface ZoneLoadedDto { + zoneName: string; + filePath: string; +} + +export interface ZoneUnloadedDto { + zoneName: string; +} + export interface FastFileBinds { loadFastFile(path: string): Promise; + unloadZone(zoneName: string): Promise; +} + +export interface FastFileEventMap { + zoneLoaded: ZoneLoadedDto; + zoneUnloaded: ZoneUnloadedDto; } diff --git a/src/ModManUi/src/native/index.ts b/src/ModManUi/src/native/index.ts index 9ab6257a..d57dd38d 100644 --- a/src/ModManUi/src/native/index.ts +++ b/src/ModManUi/src/native/index.ts @@ -1,12 +1,9 @@ import type { DialogBinds } from "./DialogBinds"; -import type { FastFileBinds } from "./FastFileBinds"; - +import type { FastFileBinds, FastFileEventMap } from "./FastFileBinds"; export type NativeMethods = DialogBinds & FastFileBinds; -interface NativeEventMap { - -} +type NativeEventMap = FastFileEventMap; type WebViewExtensions = { webviewBinds: NativeMethods; diff --git a/src/ModManUi/src/stores/ZoneStore.ts b/src/ModManUi/src/stores/ZoneStore.ts new file mode 100644 index 00000000..3877ab87 --- /dev/null +++ b/src/ModManUi/src/stores/ZoneStore.ts @@ -0,0 +1,20 @@ +import { readonly, ref } from "vue"; +import { defineStore } from "pinia"; +import { webviewAddEventListener } from "@/native"; + +export const useZoneStore = defineStore("zone", () => { + const loadedZones = ref([]); + + webviewAddEventListener("zoneLoaded", (dto) => { + loadedZones.value.push(dto.zoneName); + }); + + webviewAddEventListener("zoneUnloaded", (dto) => { + const index = loadedZones.value.indexOf(dto.zoneName); + if (index >= 0) { + loadedZones.value.splice(index, 1); + } + }); + + return { loadedZones: readonly(loadedZones) }; +}); diff --git a/src/ModManUi/src/stores/counter.ts b/src/ModManUi/src/stores/counter.ts deleted file mode 100644 index 374b4d03..00000000 --- a/src/ModManUi/src/stores/counter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ref, computed } from "vue"; -import { defineStore } from "pinia"; - -export const useCounterStore = defineStore("counter", () => { - const count = ref(0); - const doubleCount = computed(() => count.value * 2); - function increment() { - count.value++; - } - - return { count, doubleCount, increment }; -}); diff --git a/src/ZoneCommon/Zone/Zone.cpp b/src/ZoneCommon/Zone/Zone.cpp index deae50d3..8c160d89 100644 --- a/src/ZoneCommon/Zone/Zone.cpp +++ b/src/ZoneCommon/Zone/Zone.cpp @@ -8,7 +8,8 @@ Zone::Zone(std::string name, const zone_priority_t priority, GameId gameId) m_language(GameLanguage::LANGUAGE_NONE), m_game_id(gameId), m_pools(ZoneAssetPools::CreateForGame(gameId, this, priority)), - m_memory(std::make_unique()) + m_memory(std::make_unique()), + m_registered(false) { } From c791034562b963fc69ccdb3dace6cfe37221aa60 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 16:55:32 +0100 Subject: [PATCH 11/70] chore: rename FastFileBinds to ZoneBinds --- src/ModMan/Context/FastFileContext.cpp | 2 +- src/ModMan/Web/Binds/Binds.cpp | 2 +- src/ModMan/Web/Binds/{FastFileBinds.cpp => ZoneBinds.cpp} | 2 +- src/ModMan/Web/Binds/{FastFileBinds.h => ZoneBinds.h} | 0 src/ModManUi/src/native/{FastFileBinds.ts => ZoneBinds.ts} | 4 ++-- src/ModManUi/src/native/index.ts | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) rename src/ModMan/Web/Binds/{FastFileBinds.cpp => ZoneBinds.cpp} (99%) rename src/ModMan/Web/Binds/{FastFileBinds.h => ZoneBinds.h} (100%) rename src/ModManUi/src/native/{FastFileBinds.ts => ZoneBinds.ts} (80%) diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 6e9319ee..3af0d92d 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -1,6 +1,6 @@ #include "FastFileContext.h" -#include "Web/Binds/FastFileBinds.h" +#include "Web/Binds/ZoneBinds.h" #include "Web/UiCommunication.h" #include "ZoneLoading.h" diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp index ab1668f5..94bd2ec5 100644 --- a/src/ModMan/Web/Binds/Binds.cpp +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -1,7 +1,7 @@ #include "Binds.h" -#include "FastFileBinds.h" #include "Web/Binds/DialogBinds.h" +#include "ZoneBinds.h" namespace ui { diff --git a/src/ModMan/Web/Binds/FastFileBinds.cpp b/src/ModMan/Web/Binds/ZoneBinds.cpp similarity index 99% rename from src/ModMan/Web/Binds/FastFileBinds.cpp rename to src/ModMan/Web/Binds/ZoneBinds.cpp index b2cf0d50..4291a1d1 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.cpp +++ b/src/ModMan/Web/Binds/ZoneBinds.cpp @@ -1,4 +1,4 @@ -#include "FastFileBinds.h" +#include "ZoneBinds.h" #include "Context/ModManContext.h" #include "Web/UiCommunication.h" diff --git a/src/ModMan/Web/Binds/FastFileBinds.h b/src/ModMan/Web/Binds/ZoneBinds.h similarity index 100% rename from src/ModMan/Web/Binds/FastFileBinds.h rename to src/ModMan/Web/Binds/ZoneBinds.h diff --git a/src/ModManUi/src/native/FastFileBinds.ts b/src/ModManUi/src/native/ZoneBinds.ts similarity index 80% rename from src/ModManUi/src/native/FastFileBinds.ts rename to src/ModManUi/src/native/ZoneBinds.ts index 556d3210..b95307bf 100644 --- a/src/ModManUi/src/native/FastFileBinds.ts +++ b/src/ModManUi/src/native/ZoneBinds.ts @@ -7,12 +7,12 @@ export interface ZoneUnloadedDto { zoneName: string; } -export interface FastFileBinds { +export interface ZoneBinds { loadFastFile(path: string): Promise; unloadZone(zoneName: string): Promise; } -export interface FastFileEventMap { +export interface ZoneEventMap { zoneLoaded: ZoneLoadedDto; zoneUnloaded: ZoneUnloadedDto; } diff --git a/src/ModManUi/src/native/index.ts b/src/ModManUi/src/native/index.ts index d57dd38d..c055a7c5 100644 --- a/src/ModManUi/src/native/index.ts +++ b/src/ModManUi/src/native/index.ts @@ -1,9 +1,9 @@ import type { DialogBinds } from "./DialogBinds"; -import type { FastFileBinds, FastFileEventMap } from "./FastFileBinds"; +import type { ZoneBinds, ZoneEventMap } from "./ZoneBinds"; -export type NativeMethods = DialogBinds & FastFileBinds; +export type NativeMethods = DialogBinds & ZoneBinds; -type NativeEventMap = FastFileEventMap; +type NativeEventMap = ZoneEventMap; type WebViewExtensions = { webviewBinds: NativeMethods; From f53195d4bd77a4f8be38c2f7e0ce3ff75bab9cbb Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 17:59:34 +0100 Subject: [PATCH 12/70] chore: update testing style --- src/ModManUi/src/App.vue | 29 ++++++++++++------ .../src/components/SpinningLoader.vue | 30 +++++++++++++++++++ src/ModManUi/src/main.scss | 10 +++++-- 3 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 src/ModManUi/src/components/SpinningLoader.vue diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 4a6a761c..0da45521 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -2,6 +2,7 @@ import { ref } from "vue"; import { webviewBinds } from "@/native"; import { useZoneStore } from "@/stores/ZoneStore"; +import SpinningLoader from "@/components/SpinningLoader.vue"; const zoneStore = useZoneStore(); const lastPath = ref(""); @@ -36,25 +37,35 @@ function onUnloadClicked(zoneName: string) { Nothing to see here yet, this is mainly for testing

- - Loading: {{ loadingFastFile }} +

Loaded zones:

-
- {{ zone }} - +
+
+ {{ zone }} + +
diff --git a/src/ModManUi/src/components/SpinningLoader.vue b/src/ModManUi/src/components/SpinningLoader.vue new file mode 100644 index 00000000..4161cd84 --- /dev/null +++ b/src/ModManUi/src/components/SpinningLoader.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/main.scss index 20d4d9c6..37c9c0b0 100644 --- a/src/ModManUi/src/main.scss +++ b/src/ModManUi/src/main.scss @@ -71,13 +71,17 @@ button { cursor: pointer; } -button:hover { +button:not(:disabled):hover { border-color: #396cd8; } -button:active { +button:not(:disabled):active { border-color: #396cd8; background-color: #e8e8e8; } +button:disabled { + opacity: 0.6; + cursor: not-allowed; +} input, button { @@ -103,7 +107,7 @@ button { color: #ffffff; background-color: #0f0f0f98; } - button:active { + button:not(:disabled):active { background-color: #0f0f0f69; } } \ No newline at end of file From d86b70e0063743a9b7107e4712f7456594405e7b Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sat, 11 Oct 2025 18:56:11 +0100 Subject: [PATCH 13/70] feat: unlink fastfiles via ModMan --- src/ModMan/Web/Binds/Binds.cpp | 2 + src/ModMan/Web/Binds/UnlinkingBinds.cpp | 93 +++++++++++++++++++++++ src/ModMan/Web/Binds/UnlinkingBinds.h | 8 ++ src/ModMan/Web/Binds/ZoneBinds.cpp | 7 +- src/ModManUi/src/App.vue | 69 ++++++++++++++--- src/ModManUi/src/native/UnlinkingBinds.ts | 3 + src/ModManUi/src/native/ZoneBinds.ts | 2 +- src/ModManUi/src/native/index.ts | 3 +- 8 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 src/ModMan/Web/Binds/UnlinkingBinds.cpp create mode 100644 src/ModMan/Web/Binds/UnlinkingBinds.h create mode 100644 src/ModManUi/src/native/UnlinkingBinds.ts diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp index 94bd2ec5..7ae5ab9c 100644 --- a/src/ModMan/Web/Binds/Binds.cpp +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -1,5 +1,6 @@ #include "Binds.h" +#include "UnlinkingBinds.h" #include "Web/Binds/DialogBinds.h" #include "ZoneBinds.h" @@ -8,6 +9,7 @@ namespace ui void RegisterAllBinds(webview::webview& wv) { RegisterDialogHandlerBinds(wv); + RegisterUnlinkingBinds(wv); RegisterZoneBinds(wv); } } // namespace ui diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.cpp b/src/ModMan/Web/Binds/UnlinkingBinds.cpp new file mode 100644 index 00000000..bc281dcf --- /dev/null +++ b/src/ModMan/Web/Binds/UnlinkingBinds.cpp @@ -0,0 +1,93 @@ +#include "UnlinkingBinds.h" + +#include "Context/ModManContext.h" +#include "IObjWriter.h" +#include "SearchPath/OutputPathFilesystem.h" +#include "SearchPath/SearchPaths.h" +#include "Utils/PathUtils.h" +#include "Web/UiCommunication.h" + +#include "Json/JsonExtension.h" +#include + +namespace fs = std::filesystem; + +namespace +{ + class ZoneLoadedDto + { + public: + std::string zoneName; + std::string filePath; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + + class ZoneUnloadedDto + { + public: + std::string zoneName; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + + result::Expected UnlinkZoneInDbThread(const std::string& zoneName) + { + const auto& context = ModManContext::Get().m_fast_file; + const auto existingZone = std::ranges::find_if(context.m_loaded_zones, + [&zoneName](const std::unique_ptr& zone) + { + return zone->m_name == zoneName; + }); + + if (existingZone == context.m_loaded_zones.end()) + return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); + + const auto& zone = *existingZone->get(); + + const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id); + + const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName; + const auto outputFolderPathStr = outputFolderPath.string(); + + OutputPathFilesystem outputFolderOutputPath(outputFolderPath); + SearchPaths searchPaths; + AssetDumpingContext dumpingContext(zone, outputFolderPathStr, outputFolderOutputPath, searchPaths); + objWriter->DumpZone(dumpingContext); + + return NoResult(); + } + + void UnlinkZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, zoneName] + { + auto result = UnlinkZoneInDbThread(zoneName); + + if (result) + { + con::debug("Unlinked zone \"{}\"", zoneName); + ui::PromiseResolve(wv, id, true); + } + else + { + con::warn("Failed to unlink zone \"{}\": {}", zoneName, result.error()); + ui::PromiseReject(wv, id, std::move(result).error()); + } + }); + } +} // namespace + +namespace ui +{ + void RegisterUnlinkingBinds(webview::webview& wv) + { + BindAsync(wv, + "unlinkZone", + [&wv](const std::string& id, std::string zoneName) + { + UnlinkZone(wv, id, std::move(zoneName)); + }); + } +} // namespace ui diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.h b/src/ModMan/Web/Binds/UnlinkingBinds.h new file mode 100644 index 00000000..4169a780 --- /dev/null +++ b/src/ModMan/Web/Binds/UnlinkingBinds.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Web/WebViewLib.h" + +namespace ui +{ + void RegisterUnlinkingBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModMan/Web/Binds/ZoneBinds.cpp b/src/ModMan/Web/Binds/ZoneBinds.cpp index 4291a1d1..8882a7d0 100644 --- a/src/ModMan/Web/Binds/ZoneBinds.cpp +++ b/src/ModMan/Web/Binds/ZoneBinds.cpp @@ -33,7 +33,12 @@ namespace if (maybeZone) { - ui::PromiseResolve(wv, id, true); + ui::PromiseResolve(wv, + id, + ZoneLoadedDto{ + .zoneName = maybeZone.value()->m_name, + .filePath = path, + }); con::debug("Loaded zone \"{}\"", maybeZone.value()->m_name); } else diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 0da45521..8c4a4dc1 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,21 +1,29 @@ - diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/main.scss index 37c9c0b0..bfd40850 100644 --- a/src/ModManUi/src/main.scss +++ b/src/ModManUi/src/main.scss @@ -14,13 +14,24 @@ -webkit-text-size-adjust: 100%; } +* { + box-sizing: border-box; +} + +body { + margin: 0; +} + .container { margin: 0; padding-top: 10vh; display: flex; + position: relative; flex-direction: column; - justify-content: center; + justify-content: start; text-align: center; + + height: 100vh; } .logo { diff --git a/src/ModManUi/src/native/ZoneBinds.ts b/src/ModManUi/src/native/ZoneBinds.ts index e0636061..d775479b 100644 --- a/src/ModManUi/src/native/ZoneBinds.ts +++ b/src/ModManUi/src/native/ZoneBinds.ts @@ -1,3 +1,8 @@ +export interface ZoneLoadProgressDto { + zoneName: string; + percentage: number; +} + export interface ZoneLoadedDto { zoneName: string; filePath: string; @@ -13,6 +18,7 @@ export interface ZoneBinds { } export interface ZoneEventMap { + zoneLoadProgress: ZoneLoadProgressDto; zoneLoaded: ZoneLoadedDto; zoneUnloaded: ZoneUnloadedDto; } diff --git a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp index 2eb5cdf0..5fb0586f 100644 --- a/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp +++ b/src/ZoneLoading/Zone/Stream/ZoneInputStream.cpp @@ -60,8 +60,6 @@ namespace m_block_shift(pointerBitCount - blockBitCount), m_offset_mask(std::numeric_limits::max() >> (sizeof(uintptr_t) * 8 - (pointerBitCount - blockBitCount))), m_last_fill_size(0), - m_has_progress_callback(progressCallback.has_value()), - m_progress_callback(std::move(progressCallback).value_or(nullptr)), m_progress_current_size(0uz), m_progress_total_size(0uz) { @@ -74,7 +72,12 @@ namespace m_insert_block = blocks[insertBlock]; - m_progress_total_size = CalculateTotalSize(); + if (progressCallback) + { + m_has_progress_callback = true; + m_progress_callback = *std::move(progressCallback); + m_progress_total_size = CalculateTotalSize(); + } } [[nodiscard]] unsigned GetPointerBitCount() const override @@ -452,7 +455,8 @@ namespace { m_block_offsets[block.m_index] += size; - if (m_has_progress_callback) + // We cannot know the full size of the temp block + if (m_has_progress_callback && block.m_type != XBlockType::BLOCK_TYPE_TEMP) { m_progress_current_size += size; m_progress_callback->OnProgress(m_progress_current_size, m_progress_total_size); @@ -473,7 +477,11 @@ namespace size_t result = 0uz; for (const auto& block : m_blocks) - result += block->m_buffer_size; + { + // We cannot know the full size of the temp block + if (block->m_type != XBlockType::BLOCK_TYPE_TEMP) + result += block->m_buffer_size; + } return result; } diff --git a/src/ZoneLoading/ZoneLoading.cpp b/src/ZoneLoading/ZoneLoading.cpp index e9b2913c..e7d4962b 100644 --- a/src/ZoneLoading/ZoneLoading.cpp +++ b/src/ZoneLoading/ZoneLoading.cpp @@ -28,10 +28,11 @@ result::Expected, std::string> ZoneLoading::LoadZone(const for (auto game = 0u; game < static_cast(GameId::COUNT); game++) { const auto* factory = IZoneLoaderFactory::GetZoneLoaderFactoryForGame(static_cast(game)); - zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback)); - - if (zoneLoader) + if (factory->InspectZoneHeader(header)) + { + zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback)); break; + } } if (!zoneLoader) From c6e9cbeddac375e88fb106c6b8a9eb9e1e6969b0 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 14 Oct 2025 23:20:56 +0100 Subject: [PATCH 17/70] feat: report on unlinking progress --- src/ModMan/Context/FastFileContext.cpp | 6 +-- src/ModMan/Web/Binds/UnlinkingBinds.cpp | 45 +++++++++++++++---- src/ModMan/Web/Binds/UnlinkingBinds.h | 2 + src/ModManUi/src/App.vue | 6 ++- src/ModManUi/src/native/UnlinkingBinds.ts | 9 ++++ src/ModManUi/src/native/index.ts | 4 +- src/ObjWriting/Dumping/AbstractAssetDumper.h | 39 +++++++++------- .../Dumping/AssetDumpingContext.cpp | 33 ++++++++++++-- src/ObjWriting/Dumping/AssetDumpingContext.h | 14 +++++- src/ObjWriting/Dumping/IAssetDumper.h | 3 +- .../Game/IW3/Image/ImageDumperIW3.cpp | 11 ++--- .../Game/IW3/Image/ImageDumperIW3.h | 3 +- .../Game/IW3/Localize/LocalizeDumperIW3.cpp | 13 ++++-- .../Game/IW3/Localize/LocalizeDumperIW3.h | 3 +- .../Game/IW3/Maps/MapEntsDumperIW3.cpp | 11 ++--- .../Game/IW3/Maps/MapEntsDumperIW3.h | 3 +- src/ObjWriting/Game/IW3/ObjWriterIW3.cpp | 15 ++++++- .../Game/IW3/RawFile/RawFileDumperIW3.cpp | 11 ++--- .../Game/IW3/RawFile/RawFileDumperIW3.h | 3 +- .../Game/IW3/Sound/LoadedSoundDumperIW3.cpp | 11 ++--- .../Game/IW3/Sound/LoadedSoundDumperIW3.h | 3 +- .../IW3/StringTable/StringTableDumperIW3.cpp | 11 ++--- .../IW3/StringTable/StringTableDumperIW3.h | 3 +- .../Game/IW4/Image/ImageDumperIW4.cpp | 11 ++--- .../Game/IW4/Image/ImageDumperIW4.h | 3 +- .../Leaderboard/LeaderboardJsonDumperIW4.cpp | 11 ++--- .../Leaderboard/LeaderboardJsonDumperIW4.h | 3 +- .../Game/IW4/LightDef/LightDefDumperIW4.cpp | 13 ++---- .../Game/IW4/LightDef/LightDefDumperIW4.h | 3 +- .../Game/IW4/Localize/LocalizeDumperIW4.cpp | 14 ++++-- .../Game/IW4/Localize/LocalizeDumperIW4.h | 3 +- .../Game/IW4/Maps/AddonMapEntsDumperIW4.cpp | 11 ++--- .../Game/IW4/Maps/AddonMapEntsDumperIW4.h | 3 +- .../Material/MaterialDecompilingDumperIW4.cpp | 9 +--- .../Material/MaterialDecompilingDumperIW4.h | 3 +- .../Game/IW4/Menu/MenuDumperIW4.cpp | 17 +++---- src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h | 3 +- .../Game/IW4/Menu/MenuListDumperIW4.cpp | 15 +++---- .../Game/IW4/Menu/MenuListDumperIW4.h | 9 ++-- src/ObjWriting/Game/IW4/ObjWriterIW4.cpp | 15 ++++++- .../IW4/PhysCollmap/PhysCollmapDumperIW4.cpp | 11 ++--- .../IW4/PhysCollmap/PhysCollmapDumperIW4.h | 3 +- .../PhysPresetInfoStringDumperIW4.cpp | 21 ++++----- .../PhysPresetInfoStringDumperIW4.h | 3 +- .../Game/IW4/RawFile/RawFileDumperIW4.cpp | 11 ++--- .../Game/IW4/RawFile/RawFileDumperIW4.h | 3 +- .../Game/IW4/Shader/PixelShaderDumperIW4.cpp | 11 ++--- .../Game/IW4/Shader/PixelShaderDumperIW4.h | 3 +- .../Game/IW4/Shader/VertexShaderDumperIW4.cpp | 11 ++--- .../Game/IW4/Shader/VertexShaderDumperIW4.h | 3 +- .../Game/IW4/Sound/LoadedSoundDumperIW4.cpp | 11 ++--- .../Game/IW4/Sound/LoadedSoundDumperIW4.h | 3 +- .../Game/IW4/Sound/SndCurveDumperIW4.cpp | 11 +---- .../Game/IW4/Sound/SndCurveDumperIW4.h | 3 +- .../IW4/StringTable/StringTableDumperIW4.cpp | 11 ++--- .../IW4/StringTable/StringTableDumperIW4.h | 3 +- .../StructuredDataDefDumperIW4.cpp | 11 ++--- .../StructuredDataDefDumperIW4.h | 3 +- .../Game/IW4/Techset/TechsetDumperIW4.cpp | 9 +--- .../Game/IW4/Techset/TechsetDumperIW4.h | 3 +- .../Game/IW4/Tracer/TracerDumperIW4.cpp | 21 ++++----- .../Game/IW4/Tracer/TracerDumperIW4.h | 3 +- .../Game/IW4/Vehicle/VehicleDumperIW4.cpp | 21 ++++----- .../Game/IW4/Vehicle/VehicleDumperIW4.h | 3 +- .../Game/IW4/Weapon/WeaponDumperIW4.cpp | 25 +++++------ .../Game/IW4/Weapon/WeaponDumperIW4.h | 3 +- .../Game/IW5/Image/ImageDumperIW5.cpp | 11 ++--- .../Game/IW5/Image/ImageDumperIW5.h | 3 +- .../Leaderboard/LeaderboardJsonDumperIW5.cpp | 11 ++--- .../Leaderboard/LeaderboardJsonDumperIW5.h | 3 +- .../Game/IW5/Localize/LocalizeDumperIW5.cpp | 13 ++++-- .../Game/IW5/Localize/LocalizeDumperIW5.h | 3 +- .../Game/IW5/Maps/AddonMapEntsDumperIW5.cpp | 11 ++--- .../Game/IW5/Maps/AddonMapEntsDumperIW5.h | 3 +- .../Game/IW5/Menu/MenuDumperIW5.cpp | 21 ++++----- src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h | 3 +- .../Game/IW5/Menu/MenuListDumperIW5.cpp | 11 ++--- .../Game/IW5/Menu/MenuListDumperIW5.h | 3 +- src/ObjWriting/Game/IW5/ObjWriterIW5.cpp | 16 ++++++- .../Game/IW5/RawFile/RawFileDumperIW5.cpp | 11 ++--- .../Game/IW5/RawFile/RawFileDumperIW5.h | 3 +- .../Game/IW5/Script/ScriptDumperIW5.cpp | 19 +++----- .../Game/IW5/Script/ScriptDumperIW5.h | 3 +- .../Game/IW5/Sound/LoadedSoundDumperIW5.cpp | 11 ++--- .../Game/IW5/Sound/LoadedSoundDumperIW5.h | 3 +- .../IW5/StringTable/StringTableDumperIW5.cpp | 11 ++--- .../IW5/StringTable/StringTableDumperIW5.h | 3 +- .../IW5/Weapon/AttachmentJsonDumperIW5.cpp | 11 ++--- .../Game/IW5/Weapon/AttachmentJsonDumperIW5.h | 3 +- .../Game/IW5/Weapon/WeaponDumperIW5.cpp | 25 +++++------ .../Game/IW5/Weapon/WeaponDumperIW5.h | 3 +- .../Game/T5/Image/ImageDumperT5.cpp | 11 ++--- src/ObjWriting/Game/T5/Image/ImageDumperT5.h | 3 +- .../Game/T5/Localize/LocalizeDumperT5.cpp | 13 ++++-- .../Game/T5/Localize/LocalizeDumperT5.h | 3 +- src/ObjWriting/Game/T5/ObjWriterT5.cpp | 15 ++++++- .../Game/T5/RawFile/RawFileDumperT5.cpp | 15 +++---- .../Game/T5/RawFile/RawFileDumperT5.h | 3 +- .../T5/StringTable/StringTableDumperT5.cpp | 11 ++--- .../Game/T5/StringTable/StringTableDumperT5.h | 3 +- .../Game/T6/FontIcon/FontIconCsvDumperT6.cpp | 11 ++--- .../Game/T6/FontIcon/FontIconCsvDumperT6.h | 3 +- .../Game/T6/FontIcon/FontIconJsonDumperT6.cpp | 11 ++--- .../Game/T6/FontIcon/FontIconJsonDumperT6.h | 3 +- .../Game/T6/Image/ImageDumperT6.cpp | 11 ++--- src/ObjWriting/Game/T6/Image/ImageDumperT6.h | 3 +- .../Leaderboard/LeaderboardJsonDumperT6.cpp | 11 ++--- .../T6/Leaderboard/LeaderboardJsonDumperT6.h | 3 +- .../Game/T6/Localize/LocalizeDumperT6.cpp | 13 ++++-- .../Game/T6/Localize/LocalizeDumperT6.h | 3 +- .../Game/T6/Maps/MapEntsDumperT6.cpp | 9 +--- src/ObjWriting/Game/T6/Maps/MapEntsDumperT6.h | 3 +- src/ObjWriting/Game/T6/ObjWriterT6.cpp | 27 +++++++++-- .../PhysConstraintsInfoStringDumperT6.cpp | 23 ++++------ .../PhysConstraintsInfoStringDumperT6.h | 3 +- .../PhysPresetInfoStringDumperT6.cpp | 21 ++++----- .../PhysPreset/PhysPresetInfoStringDumperT6.h | 3 +- src/ObjWriting/Game/T6/Qdb/QdbDumperT6.cpp | 11 ++--- src/ObjWriting/Game/T6/Qdb/QdbDumperT6.h | 3 +- .../Game/T6/RawFile/RawFileDumperT6.cpp | 15 +++---- .../Game/T6/RawFile/RawFileDumperT6.h | 3 +- .../Game/T6/Script/ScriptDumperT6.cpp | 11 ++--- .../Game/T6/Script/ScriptDumperT6.h | 3 +- src/ObjWriting/Game/T6/Slug/SlugDumperT6.cpp | 11 ++--- src/ObjWriting/Game/T6/Slug/SlugDumperT6.h | 3 +- .../Game/T6/Sound/SndBankDumperT6.cpp | 11 ++++- .../Game/T6/Sound/SndBankDumperT6.h | 3 +- .../T6/Sound/SndDriverGlobalsDumperT6.cpp | 13 ++++-- .../Game/T6/Sound/SndDriverGlobalsDumperT6.h | 3 +- .../T6/StringTable/StringTableDumperT6.cpp | 11 ++--- .../Game/T6/StringTable/StringTableDumperT6.h | 3 +- .../Game/T6/Techset/TechsetDumperT6.cpp | 9 +--- .../Game/T6/Techset/TechsetDumperT6.h | 3 +- .../Game/T6/Tracer/TracerDumperT6.cpp | 21 ++++----- .../Game/T6/Tracer/TracerDumperT6.h | 3 +- .../Game/T6/Vehicle/VehicleDumperT6.cpp | 21 ++++----- .../Game/T6/Vehicle/VehicleDumperT6.h | 3 +- .../Game/T6/Weapon/AttachmentDumperT6.cpp | 21 ++++----- .../Game/T6/Weapon/AttachmentDumperT6.h | 3 +- .../T6/Weapon/AttachmentUniqueDumperT6.cpp | 21 ++++----- .../Game/T6/Weapon/AttachmentUniqueDumperT6.h | 3 +- .../Game/T6/Weapon/CamoJsonDumperT6.cpp | 11 ++--- .../Game/T6/Weapon/CamoJsonDumperT6.h | 3 +- .../Game/T6/Weapon/WeaponDumperT6.cpp | 25 +++++------ .../Game/T6/Weapon/WeaponDumperT6.h | 3 +- .../Game/T6/ZBarrier/ZBarrierDumperT6.cpp | 21 ++++----- .../Game/T6/ZBarrier/ZBarrierDumperT6.h | 3 +- .../Material/MaterialJsonDumper.cpp.template | 13 ++---- .../Material/MaterialJsonDumper.h.template | 5 +-- .../XModel/XModelDumper.cpp.template | 39 +++++++--------- src/ObjWriting/XModel/XModelDumper.h.template | 3 +- src/Unlinker/Unlinker.cpp | 2 +- src/ZoneCommon/Pool/AssetPool.h | 36 +++++++++++++++ .../Material/MaterialJsonDumperIW3Test.cpp | 4 +- .../Material/MaterialJsonDumperIW4Test.cpp | 4 +- .../Material/MaterialJsonDumperIW5Test.cpp | 4 +- .../T5/Material/MaterialJsonDumperT5Test.cpp | 4 +- .../T6/FontIcon/FontIconJsonDumperT6Test.cpp | 4 +- .../T6/Material/MaterialJsonDumperT6Test.cpp | 4 +- 159 files changed, 686 insertions(+), 802 deletions(-) diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 5392cdae..2a5f8112 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -12,10 +12,10 @@ namespace { constexpr double MIN_PROGRESS_TO_REPORT = 0.005; - class EventProgressReporter : public ProgressCallback + class LoadingEventProgressReporter : public ProgressCallback { public: - explicit EventProgressReporter(std::string zoneName) + explicit LoadingEventProgressReporter(std::string zoneName) : m_zone_name(std::move(zoneName)), m_last_progress(0) { @@ -46,7 +46,7 @@ void FastFileContext::Destroy() result::Expected FastFileContext::LoadFastFile(const std::string& path) { - auto zone = ZoneLoading::LoadZone(path, std::make_unique(fs::path(path).filename().replace_extension().string())); + auto zone = ZoneLoading::LoadZone(path, std::make_unique(fs::path(path).filename().replace_extension().string())); if (!zone) return result::Unexpected(std::move(zone.error())); diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.cpp b/src/ModMan/Web/Binds/UnlinkingBinds.cpp index bc281dcf..82d9d9b2 100644 --- a/src/ModMan/Web/Binds/UnlinkingBinds.cpp +++ b/src/ModMan/Web/Binds/UnlinkingBinds.cpp @@ -14,22 +14,41 @@ namespace fs = std::filesystem; namespace { - class ZoneLoadedDto + class ZoneUnlinkProgressDto { public: std::string zoneName; - std::string filePath; + double percentage; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnlinkProgressDto, zoneName, percentage); - class ZoneUnloadedDto + constexpr double MIN_PROGRESS_TO_REPORT = 0.005; + + class UnlinkingEventProgressReporter : public ProgressCallback { public: - std::string zoneName; - }; + explicit UnlinkingEventProgressReporter(std::string zoneName) + : m_zone_name(std::move(zoneName)), + m_last_progress(0) + { + } - NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + void OnProgress(const size_t current, const size_t total) override + { + const double percentage = static_cast(current) / static_cast(total); + + if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) + { + m_last_progress = percentage; + ui::NotifyZoneUnlinkProgress(m_zone_name, percentage); + } + } + + private: + std::string m_zone_name; + double m_last_progress; + }; result::Expected UnlinkZoneInDbThread(const std::string& zoneName) { @@ -52,7 +71,8 @@ namespace OutputPathFilesystem outputFolderOutputPath(outputFolderPath); SearchPaths searchPaths; - AssetDumpingContext dumpingContext(zone, outputFolderPathStr, outputFolderOutputPath, searchPaths); + AssetDumpingContext dumpingContext( + zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); objWriter->DumpZone(dumpingContext); return NoResult(); @@ -81,6 +101,15 @@ namespace namespace ui { + void NotifyZoneUnlinkProgress(std::string zoneName, const double percentage) + { + const ZoneUnlinkProgressDto dto{ + .zoneName = std::move(zoneName), + .percentage = percentage, + }; + Notify(*ModManContext::Get().m_main_webview, "zoneUnlinkProgress", dto); + } + void RegisterUnlinkingBinds(webview::webview& wv) { BindAsync(wv, diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.h b/src/ModMan/Web/Binds/UnlinkingBinds.h index 4169a780..6d2ac0ed 100644 --- a/src/ModMan/Web/Binds/UnlinkingBinds.h +++ b/src/ModMan/Web/Binds/UnlinkingBinds.h @@ -4,5 +4,7 @@ namespace ui { + void NotifyZoneUnlinkProgress(std::string zoneName, double percentage); + void RegisterUnlinkingBinds(webview::webview& wv); } // namespace ui diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index c74edd92..4d5255fe 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -76,9 +76,13 @@ function onUnloadClicked(zoneName: string) { } webviewAddEventListener("zoneLoadProgress", (dto) => { - console.log(dto); lastPercentage.value = dto.percentage; }); + +webviewAddEventListener("zoneUnlinkProgress", (dto) => { + lastPercentage.value = dto.percentage; + console.log("unlink", dto); +}); @@ -127,10 +115,6 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => { column-gap: 0.5em; } -.loading { - margin-right: 0.2em; -} - .zone-list { display: flex; flex-direction: column; @@ -141,23 +125,21 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => { margin-left: 0.5em; } -.progressbar-wrapper { +@starting-style { + .progressbar { + opacity: 0; + } +} + +.progressbar { position: absolute; bottom: 0; left: 0; right: 0; - padding: 0.35rem 0.4rem; -} -.progressbar { - opacity: 0; height: 0.4rem; - border-radius: 2.5rem; - background-color: #b9772c; - transition: opacity 0.2s ease-in-out; - &.visible { - opacity: 1; - } + transition: opacity 0.2s ease-in-out; + opacity: 1; } diff --git a/src/ModManUi/src/PrimeVue.ts b/src/ModManUi/src/PrimeVue.ts new file mode 100644 index 00000000..9eb4aeb4 --- /dev/null +++ b/src/ModManUi/src/PrimeVue.ts @@ -0,0 +1,35 @@ +import type { App } from "vue"; +import PrimeVue from "primevue/config"; +import Aura from "@primeuix/themes/aura"; +import { definePreset } from "@primeuix/themes"; + +const ModManTheme = definePreset(Aura, { + semantic: { + primary: { + 50: "{orange.50}", + 100: "{orange.100}", + 200: "{orange.200}", + 300: "{orange.300}", + 400: "{orange.400}", + 500: "{orange.500}", + 600: "{orange.600}", + 700: "{orange.700}", + 800: "{orange.800}", + 900: "{orange.900}", + 950: "{orange.950}", + }, + }, +}); + +export function configurePrimeVue(app: App) { + app.use(PrimeVue, { + theme: { + preset: ModManTheme, + options: { + darkModeSelector: ".dark-mode", + }, + }, + }); + // Always make dark mode for now + document.documentElement.classList.add("dark-mode"); +} diff --git a/src/ModManUi/src/components/ZoneSelector.vue b/src/ModManUi/src/components/ZoneSelector.vue new file mode 100644 index 00000000..bc098574 --- /dev/null +++ b/src/ModManUi/src/components/ZoneSelector.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/main.scss index bfd40850..e119d56c 100644 --- a/src/ModManUi/src/main.scss +++ b/src/ModManUi/src/main.scss @@ -1,11 +1,20 @@ -:root { +@import "@fontsource/inter/latin-300.css"; +@import "@fontsource/inter/latin-300-italic.css"; +@import "@fontsource/inter/latin-400.css"; +@import "@fontsource/inter/latin-400-italic.css"; +@import "@fontsource/inter/latin-600.css"; +@import "@fontsource/inter/latin-600-italic.css"; +@import "@fontsource/inter/latin-700.css"; +@import "@fontsource/inter/latin-700-italic.css"; + +:root { font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; font-weight: 400; - color: #0f0f0f; - background-color: #f6f6f6; + color: var(--p-text-color); + background-color: var(--p-content-background); font-synthesis: none; text-rendering: optimizeLegibility; @@ -33,92 +42,3 @@ body { height: 100vh; } - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: 0.75s; -} - -.logo.tauri:hover { - filter: drop-shadow(0 0 2em #24c8db); -} - -.row { - display: flex; - justify-content: center; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - -h1 { - text-align: center; -} - -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { - cursor: pointer; -} - -button:not(:disabled):hover { - border-color: #396cd8; -} -button:not(:disabled):active { - border-color: #396cd8; - background-color: #e8e8e8; -} -button:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -input, -button { - outline: none; -} - -#greet-input { - margin-right: 5px; -} - -@media (prefers-color-scheme: dark) { - :root { - color: #f6f6f6; - background-color: #2f2f2f; - } - - a:hover { - color: #24c8db; - } - - input, - button { - color: #ffffff; - background-color: #0f0f0f98; - } - button:not(:disabled):active { - background-color: #0f0f0f69; - } -} \ No newline at end of file diff --git a/src/ModManUi/src/main.ts b/src/ModManUi/src/main.ts index d1a7b243..1f9883ef 100644 --- a/src/ModManUi/src/main.ts +++ b/src/ModManUi/src/main.ts @@ -3,6 +3,7 @@ import "./main.scss"; import { createApp } from "vue"; import { createPinia } from "pinia"; +import { configurePrimeVue } from "./PrimeVue.ts"; import App from "./App.vue"; @@ -10,4 +11,6 @@ const app = createApp(App); app.use(createPinia()); +configurePrimeVue(app); + app.mount("#app"); diff --git a/src/ModManUi/src/stores/ZoneStore.ts b/src/ModManUi/src/stores/ZoneStore.ts index 3877ab87..1b2fe86e 100644 --- a/src/ModManUi/src/stores/ZoneStore.ts +++ b/src/ModManUi/src/stores/ZoneStore.ts @@ -1,4 +1,4 @@ -import { readonly, ref } from "vue"; +import { ref } from "vue"; import { defineStore } from "pinia"; import { webviewAddEventListener } from "@/native"; @@ -16,5 +16,5 @@ export const useZoneStore = defineStore("zone", () => { } }); - return { loadedZones: readonly(loadedZones) }; + return { loadedZones }; }); From 23f5ad67e99d9b6a6b11084145ac196ab04fceac Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Thu, 16 Oct 2025 23:04:03 +0100 Subject: [PATCH 37/70] feat: combine loading bar and zone list --- src/ModMan/Context/FastFileContext.cpp | 45 ++++++---- src/ModMan/Context/FastFileContext.h | 15 +++- src/ModMan/Web/Binds/UnlinkingBinds.cpp | 14 ++-- src/ModMan/Web/Binds/ZoneBinds.cpp | 58 +++++++++++-- src/ModMan/Web/Binds/ZoneBinds.h | 3 +- src/ModManUi/src/App.vue | 88 ++------------------ src/ModManUi/src/components/ZoneSelector.vue | 72 ++++++++++++++-- src/ModManUi/src/native/UnlinkingBinds.ts | 3 + src/ModManUi/src/native/ZoneBinds.ts | 12 ++- src/ModManUi/src/stores/UnlinkingStore.ts | 31 +++++++ src/ModManUi/src/stores/ZoneStore.ts | 64 ++++++++++++-- 11 files changed, 276 insertions(+), 129 deletions(-) create mode 100644 src/ModManUi/src/stores/UnlinkingStore.ts diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 2a5f8112..1027b488 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -10,7 +10,7 @@ namespace fs = std::filesystem; namespace { - constexpr double MIN_PROGRESS_TO_REPORT = 0.005; + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; class LoadingEventProgressReporter : public ProgressCallback { @@ -23,7 +23,7 @@ namespace void OnProgress(const size_t current, const size_t total) override { - const double percentage = static_cast(current) / static_cast(total); + const double percentage = static_cast(current) / static_cast(total) * 100.0; if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) { @@ -38,38 +38,53 @@ namespace }; } // namespace +LoadedZone::LoadedZone(std::unique_ptr zone, std::string filePath) + : m_zone(std::move(zone)), + m_file_path(std::move(filePath)) +{ +} + void FastFileContext::Destroy() { // Unload all zones m_loaded_zones.clear(); } -result::Expected FastFileContext::LoadFastFile(const std::string& path) +result::Expected FastFileContext::LoadFastFile(const std::string& path) { auto zone = ZoneLoading::LoadZone(path, std::make_unique(fs::path(path).filename().replace_extension().string())); if (!zone) return result::Unexpected(std::move(zone.error())); - auto* result = m_loaded_zones.emplace_back(std::move(*zone)).get(); + auto loadedZone = std::make_unique(std::move(*zone), path); - ui::NotifyZoneLoaded(result->m_name, path); + LoadedZone* result; + { + std::lock_guard lock(m_zone_lock); + result = m_loaded_zones.emplace_back(std::move(loadedZone)).get(); + } + + ui::NotifyZoneLoaded(*result); return result; } result::Expected FastFileContext::UnloadZone(const std::string& zoneName) { - const auto existingZone = std::ranges::find_if(m_loaded_zones, - [&zoneName](const std::unique_ptr& zone) - { - return zone->m_name == zoneName; - }); - - if (existingZone != m_loaded_zones.end()) { - m_loaded_zones.erase(existingZone); - ui::NotifyZoneUnloaded(zoneName); - return NoResult(); + std::lock_guard lock(m_zone_lock); + const auto existingZone = std::ranges::find_if(m_loaded_zones, + [&zoneName](const std::unique_ptr& loadedZone) + { + return loadedZone->m_zone->m_name == zoneName; + }); + + if (existingZone != m_loaded_zones.end()) + { + m_loaded_zones.erase(existingZone); + ui::NotifyZoneUnloaded(zoneName); + return NoResult(); + } } return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index a3f87a42..e769e34f 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -4,15 +4,26 @@ #include "Zone/Zone.h" #include +#include #include +class LoadedZone +{ +public: + std::unique_ptr m_zone; + std::string m_file_path; + + LoadedZone(std::unique_ptr zone, std::string filePath); +}; + class FastFileContext { public: void Destroy(); - result::Expected LoadFastFile(const std::string& path); + result::Expected LoadFastFile(const std::string& path); result::Expected UnloadZone(const std::string& zoneName); - std::vector> m_loaded_zones; + std::vector> m_loaded_zones; + std::shared_mutex m_zone_lock; }; diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.cpp b/src/ModMan/Web/Binds/UnlinkingBinds.cpp index 82d9d9b2..ba3f7a58 100644 --- a/src/ModMan/Web/Binds/UnlinkingBinds.cpp +++ b/src/ModMan/Web/Binds/UnlinkingBinds.cpp @@ -23,7 +23,7 @@ namespace NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnlinkProgressDto, zoneName, percentage); - constexpr double MIN_PROGRESS_TO_REPORT = 0.005; + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; class UnlinkingEventProgressReporter : public ProgressCallback { @@ -36,7 +36,7 @@ namespace void OnProgress(const size_t current, const size_t total) override { - const double percentage = static_cast(current) / static_cast(total); + const double percentage = static_cast(current) / static_cast(total) * 100.0; if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) { @@ -54,17 +54,17 @@ namespace { const auto& context = ModManContext::Get().m_fast_file; const auto existingZone = std::ranges::find_if(context.m_loaded_zones, - [&zoneName](const std::unique_ptr& zone) + [&zoneName](const std::unique_ptr& loadedZone) { - return zone->m_name == zoneName; + return loadedZone->m_zone->m_name == zoneName; }); if (existingZone == context.m_loaded_zones.end()) return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); - const auto& zone = *existingZone->get(); + const auto& loadedZone = *existingZone->get(); - const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id); + const auto* objWriter = IObjWriter::GetObjWriterForGame(loadedZone.m_zone->m_game_id); const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName; const auto outputFolderPathStr = outputFolderPath.string(); @@ -72,7 +72,7 @@ namespace OutputPathFilesystem outputFolderOutputPath(outputFolderPath); SearchPaths searchPaths; AssetDumpingContext dumpingContext( - zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); + *loadedZone.m_zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); objWriter->DumpZone(dumpingContext); return NoResult(); diff --git a/src/ModMan/Web/Binds/ZoneBinds.cpp b/src/ModMan/Web/Binds/ZoneBinds.cpp index fd538376..01b2fd70 100644 --- a/src/ModMan/Web/Binds/ZoneBinds.cpp +++ b/src/ModMan/Web/Binds/ZoneBinds.cpp @@ -7,6 +7,15 @@ namespace { + class ZoneDto + { + public: + std::string name; + std::string filePath; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneDto, name, filePath); + class ZoneLoadProgressDto { public: @@ -19,11 +28,10 @@ namespace class ZoneLoadedDto { public: - std::string zoneName; - std::string filePath; + ZoneDto zone; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zone); class ZoneUnloadedDto { @@ -33,6 +41,33 @@ namespace NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + ZoneDto CreateZoneDto(const LoadedZone& loadedZone) + { + return ZoneDto{ + .name = loadedZone.m_zone->m_name, + .filePath = loadedZone.m_file_path, + }; + } + + std::vector GetLoadedZones() + { + auto& context = ModManContext::Get().m_fast_file; + + std::vector result; + + { + std::shared_lock lock(context.m_zone_lock); + result.reserve(context.m_loaded_zones.size()); + + for (const auto& loadedZone : context.m_loaded_zones) + { + result.emplace_back(CreateZoneDto(*loadedZone)); + } + } + + return result; + } + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety { ModManContext::Get().m_db_thread.Dispatch( @@ -45,10 +80,9 @@ namespace ui::PromiseResolve(wv, id, ZoneLoadedDto{ - .zoneName = maybeZone.value()->m_name, - .filePath = path, + .zone = CreateZoneDto(*maybeZone.value()), }); - con::debug("Loaded zone \"{}\"", maybeZone.value()->m_name); + con::debug("Loaded zone \"{}\"", maybeZone.value()->m_zone->m_name); } else { @@ -89,11 +123,10 @@ namespace ui Notify(*ModManContext::Get().m_main_webview, "zoneLoadProgress", dto); } - void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath) + void NotifyZoneLoaded(const LoadedZone& loadedZone) { const ZoneLoadedDto dto{ - .zoneName = std::move(zoneName), - .filePath = std::move(fastFilePath), + .zone = CreateZoneDto(loadedZone), }; Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto); } @@ -108,6 +141,13 @@ namespace ui void RegisterZoneBinds(webview::webview& wv) { + BindRetOnly>(wv, + "getZones", + [] + { + return GetLoadedZones(); + }); + BindAsync(wv, "loadFastFile", [&wv](const std::string& id, std::string path) diff --git a/src/ModMan/Web/Binds/ZoneBinds.h b/src/ModMan/Web/Binds/ZoneBinds.h index 142d5e5b..ee2efda8 100644 --- a/src/ModMan/Web/Binds/ZoneBinds.h +++ b/src/ModMan/Web/Binds/ZoneBinds.h @@ -1,11 +1,12 @@ #pragma once +#include "Context/FastFileContext.h" #include "Web/WebViewLib.h" namespace ui { void NotifyZoneLoadProgress(std::string zoneName, double percentage); - void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath); + void NotifyZoneLoaded(const LoadedZone& loadedZone); void NotifyZoneUnloaded(std::string zoneName); void RegisterZoneBinds(webview::webview& wv); diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index d79530fc..8e4f6e0f 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,80 +1,21 @@ @@ -113,6 +36,7 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => { display: flex; justify-content: center; column-gap: 0.5em; + margin-top: 1em; } .zone-list { diff --git a/src/ModManUi/src/components/ZoneSelector.vue b/src/ModManUi/src/components/ZoneSelector.vue index bc098574..2e4ab4b0 100644 --- a/src/ModManUi/src/components/ZoneSelector.vue +++ b/src/ModManUi/src/components/ZoneSelector.vue @@ -1,17 +1,46 @@ diff --git a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue new file mode 100644 index 00000000..02a85d85 --- /dev/null +++ b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue new file mode 100644 index 00000000..100f4a04 --- /dev/null +++ b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/src/ModManUi/src/components/ZoneSelector.vue b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorZoneList.vue similarity index 51% rename from src/ModManUi/src/components/ZoneSelector.vue rename to src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorZoneList.vue index 2e4ab4b0..4a10814d 100644 --- a/src/ModManUi/src/components/ZoneSelector.vue +++ b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorZoneList.vue @@ -2,7 +2,7 @@ import Button from "primevue/button"; import ProgressBar from "primevue/progressbar"; import Listbox from "primevue/listbox"; -import { computed, ref, watch } from "vue"; +import { computed } from "vue"; import { useZoneStore } from "@/stores/ZoneStore"; import { webviewBinds } from "@/native"; @@ -12,7 +12,18 @@ interface SelectableZone { } const zoneStore = useZoneStore(); -const selectedZone = ref(null); +const selectedZone = defineModel("selectedZone"); + +async function openFastFileSelect() { + return await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] }); +} + +async function onOpenFastFileClick() { + const fastFilePath = await openFastFileSelect(); + if (!fastFilePath) return; + + zoneStore.loadFastFile(fastFilePath); +} const availableZones = computed(() => { const result = [ @@ -40,64 +51,48 @@ const availableZones = computed(() => { function onUnloadClicked() { if (!selectedZone.value) return; - webviewBinds.unloadZone(selectedZone.value.zoneName).catch((e: string) => { + webviewBinds.unloadZone(selectedZone.value).catch((e: string) => { console.error("Failed to unload zone:", e); }); } - -watch( - () => zoneStore.loadedZones, - (newValue) => { - // Reset selection if unloaded - if (!selectedZone.value) return; - if (newValue.findIndex((loadedZone) => loadedZone.name === selectedZone.value?.zoneName) >= 0) - return; - selectedZone.value = null; - }, - { deep: true }, -); diff --git a/src/ModManUi/src/native/AssetBinds.ts b/src/ModManUi/src/native/AssetBinds.ts index 4e98dfc2..ce8a2f6c 100644 --- a/src/ModManUi/src/native/AssetBinds.ts +++ b/src/ModManUi/src/native/AssetBinds.ts @@ -17,7 +17,7 @@ export enum CommonAssetType { LIGHT_DEF = "LIGHT_DEF", UI_MAP = "UI_MAP", FONT = "FONT", - MENULIST = "MENULIST", + MENU_LIST = "MENU_LIST", MENU = "MENU", LOCALIZE_ENTRY = "LOCALIZE_ENTRY", WEAPON = "WEAPON", diff --git a/src/ModManUi/src/native/ZoneBinds.ts b/src/ModManUi/src/native/ZoneBinds.ts index f5babce3..1be14aad 100644 --- a/src/ModManUi/src/native/ZoneBinds.ts +++ b/src/ModManUi/src/native/ZoneBinds.ts @@ -1,6 +1,22 @@ +export enum GameId { + IW3 = "IW3", + IW4 = "IW4", + IW5 = "IW5", + T5 = "T5", + T6 = "T6", +} + +export enum GamePlatform { + PC = "PC", + XBOX = "XBOX", + PS3 = "PS3", +} + export interface ZoneDto { name: string; filePath: string; + game: GameId; + platform: GamePlatform; } export interface ZoneLoadProgressDto { diff --git a/src/ModManUi/src/utils/AssetTypeName.ts b/src/ModManUi/src/utils/AssetTypeName.ts new file mode 100644 index 00000000..d4c3e8d7 --- /dev/null +++ b/src/ModManUi/src/utils/AssetTypeName.ts @@ -0,0 +1,85 @@ +import { CommonAssetType } from "@/native/AssetBinds"; + +const LOOKUP_CAPITALIZED: Record = { + [CommonAssetType.PHYS_PRESET]: "Phys preset", + [CommonAssetType.XANIM]: "XAnim", + [CommonAssetType.XMODEL]: "XModel", + [CommonAssetType.MATERIAL]: "Material", + [CommonAssetType.TECHNIQUE_SET]: "Technique set", + [CommonAssetType.IMAGE]: "Image", + [CommonAssetType.SOUND]: "Sound", + [CommonAssetType.SOUND_CURVE]: "Sound curve", + [CommonAssetType.LOADED_SOUND]: "Loaded sound", + [CommonAssetType.CLIP_MAP]: "Clip map", + [CommonAssetType.COM_WORLD]: "Com world", + [CommonAssetType.GAME_WORLD_SP]: "Game world SP", + [CommonAssetType.GAME_WORLD_MP]: "Game world MP", + [CommonAssetType.MAP_ENTS]: "Map ents", + [CommonAssetType.GFX_WORLD]: "Gfx world", + [CommonAssetType.LIGHT_DEF]: "Light def", + [CommonAssetType.UI_MAP]: "UI map", + [CommonAssetType.FONT]: "Font", + [CommonAssetType.MENU_LIST]: "Menu list", + [CommonAssetType.MENU]: "Menu", + [CommonAssetType.LOCALIZE_ENTRY]: "Localize entry", + [CommonAssetType.WEAPON]: "Weapon", + [CommonAssetType.SOUND_DRIVER_GLOBALS]: "Sound driver globals", + [CommonAssetType.FX]: "FX", + [CommonAssetType.IMPACT_FX]: "Impact FX", + [CommonAssetType.AI_TYPE]: "AI type", + [CommonAssetType.MP_TYPE]: "MP type", + [CommonAssetType.CHARACTER]: "Character", + [CommonAssetType.XMODEL_ALIAS]: "XModel alias", + [CommonAssetType.RAW_FILE]: "Raw file", + [CommonAssetType.STRING_TABLE]: "String table", + [CommonAssetType.XMODEL_PIECES]: "XModel pieces", + [CommonAssetType.PHYS_COLL_MAP]: "Phys coll map", + [CommonAssetType.XMODEL_SURFS]: "XModel surfs", + [CommonAssetType.PIXEL_SHADER]: "Pixel shader", + [CommonAssetType.VERTEX_SHADER]: "Vertex shader", + [CommonAssetType.VERTEX_DECL]: "Vertex decl", + [CommonAssetType.FX_WORLD]: "FX world", + [CommonAssetType.LEADERBOARD]: "Leaderboard", + [CommonAssetType.STRUCTURED_DATA_DEF]: "Structured data def", + [CommonAssetType.TRACER]: "Tracer", + [CommonAssetType.VEHICLE]: "Vehicle", + [CommonAssetType.ADDON_MAP_ENTS]: "Addon map ents", + [CommonAssetType.GLASS_WORLD]: "Glass world", + [CommonAssetType.PATH_DATA]: "Path data", + [CommonAssetType.VEHICLE_TRACK]: "Vehicle track", + [CommonAssetType.ATTACHMENT]: "Attachment", + [CommonAssetType.SURFACE_FX]: "Surface FX", + [CommonAssetType.SCRIPT]: "Script", + [CommonAssetType.PHYS_CONSTRAINTS]: "Phys constraints", + [CommonAssetType.DESTRUCTIBLE_DEF]: "Destructible def", + [CommonAssetType.SOUND_PATCH]: "Sound patch", + [CommonAssetType.WEAPON_DEF]: "Weapon def", + [CommonAssetType.WEAPON_VARIANT]: "Weapon variant", + [CommonAssetType.MP_BODY]: "MP body", + [CommonAssetType.MP_HEAD]: "MP head", + [CommonAssetType.PACK_INDEX]: "Pack index", + [CommonAssetType.XGLOBALS]: "XGlobals", + [CommonAssetType.DDL]: "DDL", + [CommonAssetType.GLASSES]: "Glasses", + [CommonAssetType.EMBLEM_SET]: "Emblem set", + [CommonAssetType.FONT_ICON]: "Font icon", + [CommonAssetType.WEAPON_FULL]: "Weapon full", + [CommonAssetType.ATTACHMENT_UNIQUE]: "Attachment unique", + [CommonAssetType.WEAPON_CAMO]: "Weapon camo", + [CommonAssetType.KEY_VALUE_PAIRS]: "Key value pairs", + [CommonAssetType.MEMORY_BLOCK]: "Memory block", + [CommonAssetType.SKINNED_VERTS]: "Skinned verts", + [CommonAssetType.QDB]: "Qdb", + [CommonAssetType.SLUG]: "Slug", + [CommonAssetType.FOOTSTEP_TABLE]: "Footstep table", + [CommonAssetType.FOOTSTEP_FX_TABLE]: "Footstep FX table", + [CommonAssetType.ZBARRIER]: "ZBarrier", +}; + +export function getAssetTypeNameCapitalized(assetType: CommonAssetType): string { + return LOOKUP_CAPITALIZED[assetType]; +} + +export function getAssetTypeNameLower(assetType: CommonAssetType): string { + return getAssetTypeNameCapitalized(assetType).toLocaleLowerCase(); +} diff --git a/src/ZoneCommon/Zone/Zone.cpp b/src/ZoneCommon/Zone/Zone.cpp index 8c160d89..4f0fe2b5 100644 --- a/src/ZoneCommon/Zone/Zone.cpp +++ b/src/ZoneCommon/Zone/Zone.cpp @@ -2,11 +2,12 @@ #include "ZoneRegistry.h" -Zone::Zone(std::string name, const zone_priority_t priority, GameId gameId) +Zone::Zone(std::string name, const zone_priority_t priority, const GameId gameId, const GamePlatform platform) : m_name(std::move(name)), m_priority(priority), m_language(GameLanguage::LANGUAGE_NONE), m_game_id(gameId), + m_platform(platform), m_pools(ZoneAssetPools::CreateForGame(gameId, this, priority)), m_memory(std::make_unique()), m_registered(false) diff --git a/src/ZoneCommon/Zone/Zone.h b/src/ZoneCommon/Zone/Zone.h index 658bb412..a4f57018 100644 --- a/src/ZoneCommon/Zone/Zone.h +++ b/src/ZoneCommon/Zone/Zone.h @@ -15,7 +15,7 @@ class ZoneAssetPools; class Zone { public: - Zone(std::string name, zone_priority_t priority, GameId gameId); + Zone(std::string name, zone_priority_t priority, GameId gameId, GamePlatform platform); ~Zone(); Zone(const Zone& other) = delete; Zone(Zone&& other) noexcept = default; @@ -30,6 +30,7 @@ public: zone_priority_t m_priority; GameLanguage m_language; GameId m_game_id; + GamePlatform m_platform; ZoneScriptStrings m_script_strings; std::unique_ptr m_pools; diff --git a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp index ae0708d5..0a81643c 100644 --- a/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp +++ b/src/ZoneLoading/Game/IW3/ZoneLoaderFactoryIW3.cpp @@ -69,7 +69,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW3); + auto zone = std::make_unique(fileName, 0, GameId::IW3, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp index 3a23cbc9..78116b19 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp @@ -211,7 +211,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW4); + auto zone = std::make_unique(fileName, 0, GameId::IW4, inspectResult->m_generic_result.m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp index d3d3cbb7..557d5537 100644 --- a/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp +++ b/src/ZoneLoading/Game/IW5/ZoneLoaderFactoryIW5.cpp @@ -165,7 +165,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::IW5); + auto zone = std::make_unique(fileName, 0, GameId::IW5, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp index 861a0d4d..7d93b5d2 100644 --- a/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp +++ b/src/ZoneLoading/Game/T5/ZoneLoaderFactoryT5.cpp @@ -68,7 +68,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::T5); + auto zone = std::make_unique(fileName, 0, GameId::T5, inspectResult->m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GameLanguage::LANGUAGE_NONE; diff --git a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp index b4023dc0..f4a40755 100644 --- a/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp +++ b/src/ZoneLoading/Game/T6/ZoneLoaderFactoryT6.cpp @@ -284,7 +284,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH return nullptr; // Create new zone - auto zone = std::make_unique(fileName, 0, GameId::T6); + auto zone = std::make_unique(fileName, 0, GameId::T6, inspectResult->m_generic_result.m_platform); auto* zonePtr = zone.get(); zone->m_pools = std::make_unique(zonePtr, 0); zone->m_language = GetZoneLanguage(fileName); From 94cc12f4af08226a0d707520ac2741267c760ec7 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 21 Oct 2025 23:45:19 +0100 Subject: [PATCH 41/70] fix: add platform to test code --- .../Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp | 2 +- test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp | 2 +- test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp | 2 +- test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp | 2 +- test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp | 2 +- .../ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp | 2 +- .../Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp | 2 +- .../Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp | 2 +- .../ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp | 2 +- test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp | 2 +- .../Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp | 2 +- .../ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp | 2 +- .../Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp | 2 +- test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp | 2 +- .../Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp | 2 +- .../Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp | 2 +- test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp | 2 +- .../Game/IW3/Material/MaterialJsonDumperIW3Test.cpp | 2 +- .../Game/IW4/Material/MaterialJsonDumperIW4Test.cpp | 2 +- .../Game/IW5/Material/MaterialJsonDumperIW5Test.cpp | 2 +- .../Game/T5/Material/MaterialJsonDumperT5Test.cpp | 2 +- .../Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp | 2 +- .../Game/T6/Material/MaterialJsonDumperT6Test.cpp | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp index 4d5ab4fa..7e81cf1b 100644 --- a/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp +++ b/test/ObjCompilingTests/Game/T6/KeyValuePairs/KeyValuePairsCompilerT6Test.cpp @@ -20,7 +20,7 @@ namespace public: TestContext() : m_memory(), - m_zone("test", 0, GameId::T6), + m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_states(m_zone), m_creators(m_zone), diff --git a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp index fc423589..1f2d74df 100644 --- a/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp +++ b/test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp @@ -20,7 +20,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_states(m_zone), m_out_dir() { diff --git a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp index c9c6b82c..d92899bf 100644 --- a/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp @@ -20,7 +20,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), diff --git a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp index adfef729..c9b1ddc4 100644 --- a/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp +++ b/test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp @@ -19,7 +19,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_definition(), m_zone_definition_context(m_zone_definition), m_zone_states(m_zone), diff --git a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp index 33421760..a15cc0c4 100644 --- a/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp +++ b/test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp @@ -23,7 +23,7 @@ namespace { public: TestContext() - : m_zone("test", 0, GameId::T6), + : m_zone("test", 0, GameId::T6, GamePlatform::PC), m_zone_states(m_zone), m_out_dir() { diff --git a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp index 4c88b469..28806ca1 100644 --- a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp +++ b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp @@ -274,7 +274,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp index 4bfd7172..c98e09be 100644 --- a/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp +++ b/test/ObjLoadingTests/Game/IW3/StringTable/AssetLoaderStringTableIW3Test.cpp @@ -20,7 +20,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp index e6fdf9db..1585b4ea 100644 --- a/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/AssetLoaders/LoaderStringTableIW4Test.cpp @@ -20,7 +20,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp index 39fd6075..fcb765de 100644 --- a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp @@ -277,7 +277,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp index dcd495f1..ec4cc5bb 100644 --- a/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp +++ b/test/ObjLoadingTests/Game/IW4/Menu/LoaderMenuListIW4Test.cpp @@ -30,7 +30,7 @@ namespace test::game::iw4::menu::parsing::it public: MenuParsingItHelper() - : m_zone("MockZone", 0, GameId::IW4), + : m_zone("MockZone", 0, GameId::IW4, GamePlatform::PC), m_creator_collection(m_zone), m_ignored_asset_lookup(), m_context(m_zone, &m_creator_collection, &m_ignored_asset_lookup) diff --git a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp index 6aadc816..7b94170c 100644 --- a/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp +++ b/test/ObjLoadingTests/Game/IW5/AssetLoaders/LoaderStringTableIW5Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp index 8105f4ee..67a12e83 100644 --- a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp +++ b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp @@ -301,7 +301,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp index 7db27a83..86ce69ad 100644 --- a/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/AssetLoaders/LoaderStringTableT5Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp index 4847e225..ce0d8fa5 100644 --- a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp @@ -358,7 +358,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp index e26e8b74..ec8c0500 100644 --- a/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/AssetLoaders/LoaderStringTableT6Test.cpp @@ -19,7 +19,7 @@ namespace "test,data,lol\n" "lorem,ipsum"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp index 24311566..a75adfe2 100644 --- a/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/FontIcon/JsonLoaderFontIconT6Test.cpp @@ -79,7 +79,7 @@ namespace ] })FONT_ICON"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp index 95b3bfa1..24340f67 100644 --- a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -241,7 +241,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; AssetCreatorCollection creatorCollection(zone); diff --git a/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp index fda33fe2..0a1ad83c 100644 --- a/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp +++ b/test/ObjWritingTests/Game/IW3/Material/MaterialJsonDumperIW3Test.cpp @@ -551,7 +551,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW3); + Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; diff --git a/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp index adbbc517..f5397cd0 100644 --- a/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/MaterialJsonDumperIW4Test.cpp @@ -532,7 +532,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW4); + Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; diff --git a/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp index 9c003335..3196f28a 100644 --- a/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/MaterialJsonDumperIW5Test.cpp @@ -585,7 +585,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::IW5); + Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; diff --git a/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp index 0036b78e..d2664be6 100644 --- a/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp +++ b/test/ObjWritingTests/Game/T5/Material/MaterialJsonDumperT5Test.cpp @@ -614,7 +614,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T5); + Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; diff --git a/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp index 91b6b1f3..db107bca 100644 --- a/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/FontIcon/FontIconJsonDumperT6Test.cpp @@ -141,7 +141,7 @@ namespace ] })FONT_ICON"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; diff --git a/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp index bfefddb1..49b4a59b 100644 --- a/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/MaterialJsonDumperT6Test.cpp @@ -462,7 +462,7 @@ namespace ] })MATERIAL"); - Zone zone("MockZone", 0, GameId::T6); + Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC); MemoryManager memory; MockSearchPath mockObjPath; From 61ab196c5aeb947128c50cf1f628324eabe550c8 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Wed, 22 Oct 2025 17:49:31 +0100 Subject: [PATCH 42/70] chore: reduce size of asset MeterGroup --- .../unlinking/zone_selector/ZoneSelectorDetails.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue index 64646e4c..c1cf6a9b 100644 --- a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue +++ b/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelectorDetails.vue @@ -131,6 +131,10 @@ watch( .asset-meter { padding-top: 0.5em; + font-size: 0.8em; + line-height: 1.25; + --p-metergroup-gap: 1em; + --p-metergroup-label-list-horizontal-gap: 0.8em; } .count-skeleton { From 6b3fdbfb752eaeeed983f91d00a760d614dce936 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Wed, 22 Oct 2025 23:37:52 +0100 Subject: [PATCH 43/70] chore: add modman navigation --- src/ModManUi/package-lock.json | 24 +- src/ModManUi/package.json | 3 +- src/ModManUi/src/App.vue | 18 +- src/ModManUi/src/components/ModManHeader.vue | 60 ++++ .../src/components/icons/IconArrowLeft.vue | 9 + .../src/components/icons/IconGear.vue | 9 + src/ModManUi/src/main.ts | 4 +- src/ModManUi/src/router/Page.ts | 5 + src/ModManUi/src/router/Router.ts | 18 + src/ModManUi/src/style/_colors.scss | 311 ++++++++++++++++++ src/ModManUi/src/style/_variables.scss | 1 + src/ModManUi/src/{ => style}/main.scss | 15 +- src/ModManUi/src/style/utils.scss | 3 + .../inspect/ZoneInspector.vue} | 13 +- .../inspect/ZoneInspectorDetails.vue} | 0 .../inspect/ZoneInspectorZoneList.vue} | 0 src/ModManUi/tsconfig.app.json | 3 +- src/ModManUi/vite.config.ts | 1 + 18 files changed, 466 insertions(+), 31 deletions(-) create mode 100644 src/ModManUi/src/components/ModManHeader.vue create mode 100644 src/ModManUi/src/components/icons/IconArrowLeft.vue create mode 100644 src/ModManUi/src/components/icons/IconGear.vue create mode 100644 src/ModManUi/src/router/Page.ts create mode 100644 src/ModManUi/src/router/Router.ts create mode 100644 src/ModManUi/src/style/_colors.scss create mode 100644 src/ModManUi/src/style/_variables.scss rename src/ModManUi/src/{ => style}/main.scss (79%) create mode 100644 src/ModManUi/src/style/utils.scss rename src/ModManUi/src/{components/unlinking/zone_selector/ZoneSelector.vue => view/inspect/ZoneInspector.vue} (68%) rename src/ModManUi/src/{components/unlinking/zone_selector/ZoneSelectorDetails.vue => view/inspect/ZoneInspectorDetails.vue} (100%) rename src/ModManUi/src/{components/unlinking/zone_selector/ZoneSelectorZoneList.vue => view/inspect/ZoneInspectorZoneList.vue} (100%) diff --git a/src/ModManUi/package-lock.json b/src/ModManUi/package-lock.json index 40b2841d..14a58f0d 100644 --- a/src/ModManUi/package-lock.json +++ b/src/ModManUi/package-lock.json @@ -12,7 +12,8 @@ "@primeuix/themes": "^1.2.5", "pinia": "3.0.3", "primevue": "^4.4.1", - "vue": "3.5.22" + "vue": "3.5.22", + "vue-router": "4.6.3" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", @@ -6649,6 +6650,27 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, "node_modules/vue-tsc": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.1.2.tgz", diff --git a/src/ModManUi/package.json b/src/ModManUi/package.json index 0eeaac30..78ef8bad 100644 --- a/src/ModManUi/package.json +++ b/src/ModManUi/package.json @@ -17,7 +17,8 @@ "@primeuix/themes": "^1.2.5", "pinia": "3.0.3", "primevue": "^4.4.1", - "vue": "3.5.22" + "vue": "3.5.22", + "vue-router": "4.6.3" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 308f9eba..08fc7fde 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,20 +1,22 @@ diff --git a/src/ModManUi/src/components/ModManHeader.vue b/src/ModManUi/src/components/ModManHeader.vue new file mode 100644 index 00000000..eb0d48d2 --- /dev/null +++ b/src/ModManUi/src/components/ModManHeader.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/src/ModManUi/src/components/icons/IconArrowLeft.vue b/src/ModManUi/src/components/icons/IconArrowLeft.vue new file mode 100644 index 00000000..3e37cfb5 --- /dev/null +++ b/src/ModManUi/src/components/icons/IconArrowLeft.vue @@ -0,0 +1,9 @@ + diff --git a/src/ModManUi/src/components/icons/IconGear.vue b/src/ModManUi/src/components/icons/IconGear.vue new file mode 100644 index 00000000..6ba8ca16 --- /dev/null +++ b/src/ModManUi/src/components/icons/IconGear.vue @@ -0,0 +1,9 @@ + diff --git a/src/ModManUi/src/main.ts b/src/ModManUi/src/main.ts index 1f9883ef..dc4dcad6 100644 --- a/src/ModManUi/src/main.ts +++ b/src/ModManUi/src/main.ts @@ -1,15 +1,17 @@ import "../public/favicon.ico"; -import "./main.scss"; +import "@style/main.scss"; import { createApp } from "vue"; import { createPinia } from "pinia"; import { configurePrimeVue } from "./PrimeVue.ts"; import App from "./App.vue"; +import { createModManRouter } from "./router/Router.ts"; const app = createApp(App); app.use(createPinia()); +app.use(createModManRouter()); configurePrimeVue(app); diff --git a/src/ModManUi/src/router/Page.ts b/src/ModManUi/src/router/Page.ts new file mode 100644 index 00000000..1a8c8744 --- /dev/null +++ b/src/ModManUi/src/router/Page.ts @@ -0,0 +1,5 @@ +export const PAGE = { + HOME: "Home", + INSPECT_ZONE: "Inspect zone", + OPTIONS: "Options", +}; diff --git a/src/ModManUi/src/router/Router.ts b/src/ModManUi/src/router/Router.ts new file mode 100644 index 00000000..48a3e294 --- /dev/null +++ b/src/ModManUi/src/router/Router.ts @@ -0,0 +1,18 @@ +import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router"; +import { PAGE } from "./Page"; +import ZoneInspector from "@/view/inspect/ZoneInspector.vue"; + +const ROUTES: RouteRecordRaw[] = [ + { + path: "/", + name: PAGE.INSPECT_ZONE, + component: ZoneInspector, + }, +]; + +export function createModManRouter() { + return createRouter({ + history: createWebHashHistory(), + routes: ROUTES, + }); +} diff --git a/src/ModManUi/src/style/_colors.scss b/src/ModManUi/src/style/_colors.scss new file mode 100644 index 00000000..687d2dd2 --- /dev/null +++ b/src/ModManUi/src/style/_colors.scss @@ -0,0 +1,311 @@ +// Primitive colors +$EMERALD-50: var(--p-emerald-50); +$EMERALD-100: var(--p-emerald-100); +$EMERALD-200: var(--p-emerald-200); +$EMERALD-300: var(--p-emerald-300); +$EMERALD-400: var(--p-emerald-400); +$EMERALD-500: var(--p-emerald-500); +$EMERALD-600: var(--p-emerald-600); +$EMERALD-700: var(--p-emerald-700); +$EMERALD-800: var(--p-emerald-800); +$EMERALD-900: var(--p-emerald-900); +$EMERALD-950: var(--p-emerald-950); + +$GREEN-50: var(--p-green-50); +$GREEN-100: var(--p-green-100); +$GREEN-200: var(--p-green-200); +$GREEN-300: var(--p-green-300); +$GREEN-400: var(--p-green-400); +$GREEN-500: var(--p-green-500); +$GREEN-600: var(--p-green-600); +$GREEN-700: var(--p-green-700); +$GREEN-800: var(--p-green-800); +$GREEN-900: var(--p-green-900); +$GREEN-950: var(--p-green-950); + +$LIME-50: var(--p-lime-50); +$LIME-100: var(--p-lime-100); +$LIME-200: var(--p-lime-200); +$LIME-300: var(--p-lime-300); +$LIME-400: var(--p-lime-400); +$LIME-500: var(--p-lime-500); +$LIME-600: var(--p-lime-600); +$LIME-700: var(--p-lime-700); +$LIME-800: var(--p-lime-800); +$LIME-900: var(--p-lime-900); +$LIME-950: var(--p-lime-950); + +$RED-50: var(--p-red-50); +$RED-100: var(--p-red-100); +$RED-200: var(--p-red-200); +$RED-300: var(--p-red-300); +$RED-400: var(--p-red-400); +$RED-500: var(--p-red-500); +$RED-600: var(--p-red-600); +$RED-700: var(--p-red-700); +$RED-800: var(--p-red-800); +$RED-900: var(--p-red-900); +$RED-950: var(--p-red-950); + +$ORANGE-50: var(--p-orange-50); +$ORANGE-100: var(--p-orange-100); +$ORANGE-200: var(--p-orange-200); +$ORANGE-300: var(--p-orange-300); +$ORANGE-400: var(--p-orange-400); +$ORANGE-500: var(--p-orange-500); +$ORANGE-600: var(--p-orange-600); +$ORANGE-700: var(--p-orange-700); +$ORANGE-800: var(--p-orange-800); +$ORANGE-900: var(--p-orange-900); +$ORANGE-950: var(--p-orange-950); + +$AMBER-50: var(--p-amber-50); +$AMBER-100: var(--p-amber-100); +$AMBER-200: var(--p-amber-200); +$AMBER-300: var(--p-amber-300); +$AMBER-400: var(--p-amber-400); +$AMBER-500: var(--p-amber-500); +$AMBER-600: var(--p-amber-600); +$AMBER-700: var(--p-amber-700); +$AMBER-800: var(--p-amber-800); +$AMBER-900: var(--p-amber-900); +$AMBER-950: var(--p-amber-950); + +$YELLOW-50: var(--p-yellow-50); +$YELLOW-100: var(--p-yellow-100); +$YELLOW-200: var(--p-yellow-200); +$YELLOW-300: var(--p-yellow-300); +$YELLOW-400: var(--p-yellow-400); +$YELLOW-500: var(--p-yellow-500); +$YELLOW-600: var(--p-yellow-600); +$YELLOW-700: var(--p-yellow-700); +$YELLOW-800: var(--p-yellow-800); +$YELLOW-900: var(--p-yellow-900); +$YELLOW-950: var(--p-yellow-950); + +$TEAL-50: var(--p-teal-50); +$TEAL-100: var(--p-teal-100); +$TEAL-200: var(--p-teal-200); +$TEAL-300: var(--p-teal-300); +$TEAL-400: var(--p-teal-400); +$TEAL-500: var(--p-teal-500); +$TEAL-600: var(--p-teal-600); +$TEAL-700: var(--p-teal-700); +$TEAL-800: var(--p-teal-800); +$TEAL-900: var(--p-teal-900); +$TEAL-950: var(--p-teal-950); + +$CYAN-50: var(--p-cyan-50); +$CYAN-100: var(--p-cyan-100); +$CYAN-200: var(--p-cyan-200); +$CYAN-300: var(--p-cyan-300); +$CYAN-400: var(--p-cyan-400); +$CYAN-500: var(--p-cyan-500); +$CYAN-600: var(--p-cyan-600); +$CYAN-700: var(--p-cyan-700); +$CYAN-800: var(--p-cyan-800); +$CYAN-900: var(--p-cyan-900); +$CYAN-950: var(--p-cyan-950); + +$SKY-50: var(--p-sky-50); +$SKY-100: var(--p-sky-100); +$SKY-200: var(--p-sky-200); +$SKY-300: var(--p-sky-300); +$SKY-400: var(--p-sky-400); +$SKY-500: var(--p-sky-500); +$SKY-600: var(--p-sky-600); +$SKY-700: var(--p-sky-700); +$SKY-800: var(--p-sky-800); +$SKY-900: var(--p-sky-900); +$SKY-950: var(--p-sky-950); + +$BLUE-50: var(--p-blue-50); +$BLUE-100: var(--p-blue-100); +$BLUE-200: var(--p-blue-200); +$BLUE-300: var(--p-blue-300); +$BLUE-400: var(--p-blue-400); +$BLUE-500: var(--p-blue-500); +$BLUE-600: var(--p-blue-600); +$BLUE-700: var(--p-blue-700); +$BLUE-800: var(--p-blue-800); +$BLUE-900: var(--p-blue-900); +$BLUE-950: var(--p-blue-950); + +$INDIGO-50: var(--p-indigo-50); +$INDIGO-100: var(--p-indigo-100); +$INDIGO-200: var(--p-indigo-200); +$INDIGO-300: var(--p-indigo-300); +$INDIGO-400: var(--p-indigo-400); +$INDIGO-500: var(--p-indigo-500); +$INDIGO-600: var(--p-indigo-600); +$INDIGO-700: var(--p-indigo-700); +$INDIGO-800: var(--p-indigo-800); +$INDIGO-900: var(--p-indigo-900); +$INDIGO-950: var(--p-indigo-950); + +$VIOLET-50: var(--p-violet-50); +$VIOLET-100: var(--p-violet-100); +$VIOLET-200: var(--p-violet-200); +$VIOLET-300: var(--p-violet-300); +$VIOLET-400: var(--p-violet-400); +$VIOLET-500: var(--p-violet-500); +$VIOLET-600: var(--p-violet-600); +$VIOLET-700: var(--p-violet-700); +$VIOLET-800: var(--p-violet-800); +$VIOLET-900: var(--p-violet-900); +$VIOLET-950: var(--p-violet-950); + +$PURPLE-50: var(--p-purple-50); +$PURPLE-100: var(--p-purple-100); +$PURPLE-200: var(--p-purple-200); +$PURPLE-300: var(--p-purple-300); +$PURPLE-400: var(--p-purple-400); +$PURPLE-500: var(--p-purple-500); +$PURPLE-600: var(--p-purple-600); +$PURPLE-700: var(--p-purple-700); +$PURPLE-800: var(--p-purple-800); +$PURPLE-900: var(--p-purple-900); +$PURPLE-950: var(--p-purple-950); + +$FUCHSIA-50: var(--p-fuchsia-50); +$FUCHSIA-100: var(--p-fuchsia-100); +$FUCHSIA-200: var(--p-fuchsia-200); +$FUCHSIA-300: var(--p-fuchsia-300); +$FUCHSIA-400: var(--p-fuchsia-400); +$FUCHSIA-500: var(--p-fuchsia-500); +$FUCHSIA-600: var(--p-fuchsia-600); +$FUCHSIA-700: var(--p-fuchsia-700); +$FUCHSIA-800: var(--p-fuchsia-800); +$FUCHSIA-900: var(--p-fuchsia-900); +$FUCHSIA-950: var(--p-fuchsia-950); + +$PINK-50: var(--p-pink-50); +$PINK-100: var(--p-pink-100); +$PINK-200: var(--p-pink-200); +$PINK-300: var(--p-pink-300); +$PINK-400: var(--p-pink-400); +$PINK-500: var(--p-pink-500); +$PINK-600: var(--p-pink-600); +$PINK-700: var(--p-pink-700); +$PINK-800: var(--p-pink-800); +$PINK-900: var(--p-pink-900); +$PINK-950: var(--p-pink-950); + +$ROSE-50: var(--p-rose-50); +$ROSE-100: var(--p-rose-100); +$ROSE-200: var(--p-rose-200); +$ROSE-300: var(--p-rose-300); +$ROSE-400: var(--p-rose-400); +$ROSE-500: var(--p-rose-500); +$ROSE-600: var(--p-rose-600); +$ROSE-700: var(--p-rose-700); +$ROSE-800: var(--p-rose-800); +$ROSE-900: var(--p-rose-900); +$ROSE-950: var(--p-rose-950); + +$SLATE-50: var(--p-slate-50); +$SLATE-100: var(--p-slate-100); +$SLATE-200: var(--p-slate-200); +$SLATE-300: var(--p-slate-300); +$SLATE-400: var(--p-slate-400); +$SLATE-500: var(--p-slate-500); +$SLATE-600: var(--p-slate-600); +$SLATE-700: var(--p-slate-700); +$SLATE-800: var(--p-slate-800); +$SLATE-900: var(--p-slate-900); +$SLATE-950: var(--p-slate-950); + +$GRAY-50: var(--p-gray-50); +$GRAY-100: var(--p-gray-100); +$GRAY-200: var(--p-gray-200); +$GRAY-300: var(--p-gray-300); +$GRAY-400: var(--p-gray-400); +$GRAY-500: var(--p-gray-500); +$GRAY-600: var(--p-gray-600); +$GRAY-700: var(--p-gray-700); +$GRAY-800: var(--p-gray-800); +$GRAY-900: var(--p-gray-900); +$GRAY-950: var(--p-gray-950); + +$ZINC-50: var(--p-zinc-50); +$ZINC-100: var(--p-zinc-100); +$ZINC-200: var(--p-zinc-200); +$ZINC-300: var(--p-zinc-300); +$ZINC-400: var(--p-zinc-400); +$ZINC-500: var(--p-zinc-500); +$ZINC-600: var(--p-zinc-600); +$ZINC-700: var(--p-zinc-700); +$ZINC-800: var(--p-zinc-800); +$ZINC-900: var(--p-zinc-900); +$ZINC-950: var(--p-zinc-950); + +$NEUTRAL-50: var(--p-neutral-50); +$NEUTRAL-100: var(--p-neutral-100); +$NEUTRAL-200: var(--p-neutral-200); +$NEUTRAL-300: var(--p-neutral-300); +$NEUTRAL-400: var(--p-neutral-400); +$NEUTRAL-500: var(--p-neutral-500); +$NEUTRAL-600: var(--p-neutral-600); +$NEUTRAL-700: var(--p-neutral-700); +$NEUTRAL-800: var(--p-neutral-800); +$NEUTRAL-900: var(--p-neutral-900); +$NEUTRAL-950: var(--p-neutral-950); + +$STONE-50: var(--p-stone-50); +$STONE-100: var(--p-stone-100); +$STONE-200: var(--p-stone-200); +$STONE-300: var(--p-stone-300); +$STONE-400: var(--p-stone-400); +$STONE-500: var(--p-stone-500); +$STONE-600: var(--p-stone-600); +$STONE-700: var(--p-stone-700); +$STONE-800: var(--p-stone-800); +$STONE-900: var(--p-stone-900); +$STONE-950: var(--p-stone-950); + +// Semantic colors + +$PRIMARY-50: var(--p-primary-50); +$PRIMARY-100: var(--p-primary-100); +$PRIMARY-200: var(--p-primary-200); +$PRIMARY-300: var(--p-primary-300); +$PRIMARY-400: var(--p-primary-400); +$PRIMARY-500: var(--p-primary-500); +$PRIMARY-600: var(--p-primary-600); +$PRIMARY-700: var(--p-primary-700); +$PRIMARY-800: var(--p-primary-800); +$PRIMARY-900: var(--p-primary-900); +$PRIMARY-950: var(--p-primary-950); + +$SURFACE-50: var(--p-surface-50); +$SURFACE-100: var(--p-surface-100); +$SURFACE-200: var(--p-surface-200); +$SURFACE-300: var(--p-surface-300); +$SURFACE-400: var(--p-surface-400); +$SURFACE-500: var(--p-surface-500); +$SURFACE-600: var(--p-surface-600); +$SURFACE-700: var(--p-surface-700); +$SURFACE-800: var(--p-surface-800); +$SURFACE-900: var(--p-surface-900); +$SURFACE-950: var(--p-surface-950); + +$PRIMARY-COLOR: var(--p-primary-color); +$PRIMARY-CONTRAST-COLOR: var(--p-primary-contrast-color); +$PRIMARY-HOVER-COLOR: var(--p-primary-hover-color); +$PRIMARY-ACTIVE-COLOR: var(--p-primary-active-color); + +$CONTENT-BACKGROUND: var(--p-content-background); +$CONTENT-HOVER-BACKGROUND: var(--p-content-hover-background); +$CONTENT-BORDER-COLOR: var(--p-content-border-color); +$CONTENT-COLOR: var(--p-content-color); +$CONTENT-HOVER-COLOR: var(--p-content-hover-color); + +$HIGHLIGHT-BACKGROUND: var(--p-highlight-background); +$HIGHLIGHT-FOCUS-BACKGROUND: var(--p-highlight-focus-background); +$HIGHLIGHT-COLOR: var(--p-highlight-color); +$HIGHLIGHT-FOCUS-COLOR: var(--p-highlight-focus-color); + +$TEXT-COLOR: var(--p-text-color); +$TEXT-HOVER-COLOR: var(--p-text-hover-color); +$TEXT-MUTED-COLOR: var(--p-text-muted-color); +$TEXT-HOVER-MUTED-COLOR: var(--p-text-hover-muted-color); diff --git a/src/ModManUi/src/style/_variables.scss b/src/ModManUi/src/style/_variables.scss new file mode 100644 index 00000000..70c003a2 --- /dev/null +++ b/src/ModManUi/src/style/_variables.scss @@ -0,0 +1 @@ +$TRANSITION_DURATION: var(--p-transition-duration); diff --git a/src/ModManUi/src/main.scss b/src/ModManUi/src/style/main.scss similarity index 79% rename from src/ModManUi/src/main.scss rename to src/ModManUi/src/style/main.scss index a1906699..05162d79 100644 --- a/src/ModManUi/src/main.scss +++ b/src/ModManUi/src/style/main.scss @@ -1,4 +1,6 @@ -@import "@fontsource/inter/latin-300.css"; +@use "utils.scss"; + +@import "@fontsource/inter/latin-300.css"; @import "@fontsource/inter/latin-300-italic.css"; @import "@fontsource/inter/latin-400.css"; @import "@fontsource/inter/latin-400-italic.css"; @@ -30,14 +32,3 @@ body { margin: 0; } - -.container { - margin: 0; - padding-top: 10vh; - display: flex; - position: relative; - flex-direction: column; - justify-content: start; - - height: 100vh; -} diff --git a/src/ModManUi/src/style/utils.scss b/src/ModManUi/src/style/utils.scss new file mode 100644 index 00000000..7de368ca --- /dev/null +++ b/src/ModManUi/src/style/utils.scss @@ -0,0 +1,3 @@ +.icon { + height: 1lh; +} \ No newline at end of file diff --git a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue b/src/ModManUi/src/view/inspect/ZoneInspector.vue similarity index 68% rename from src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue rename to src/ModManUi/src/view/inspect/ZoneInspector.vue index 02a85d85..67c6ca45 100644 --- a/src/ModManUi/src/components/unlinking/zone_selector/ZoneSelector.vue +++ b/src/ModManUi/src/view/inspect/ZoneInspector.vue @@ -1,8 +1,8 @@ From 27ecb4cc57f1e268325bed9e319e047254726f37 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Mon, 27 Oct 2025 22:02:20 +0100 Subject: [PATCH 49/70] chore: only show specific back buttons in modman --- src/ModManUi/src/components/ModManHeader.vue | 23 ++++++++++++-------- src/ModManUi/src/router/RouteMeta.ts | 19 +++++++++++----- src/ModManUi/src/router/Router.ts | 1 + 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/ModManUi/src/components/ModManHeader.vue b/src/ModManUi/src/components/ModManHeader.vue index aaca28cd..e5419f5d 100644 --- a/src/ModManUi/src/components/ModManHeader.vue +++ b/src/ModManUi/src/components/ModManHeader.vue @@ -1,31 +1,36 @@