diff --git a/src/Consts.cpp b/src/Consts.cpp index ce84ad4..99d69c6 100644 --- a/src/Consts.cpp +++ b/src/Consts.cpp @@ -51,6 +51,8 @@ const char* EV_WINDOW_RESIZED = "EV_WINDOW_RESIZED"; const char* EV_MUMBLE_IDENTITY_UPDATED = "EV_MUMBLE_IDENTITY_UPDATED"; const char* EV_OPTIONS_CALLED = "EV_OPTIONS_CALLED"; const char* EV_EULA_ACCEPTED = "EV_EULA_ACCEPTED"; +const char* EV_ADDON_LOADED = "EV_ADDON_LOADED"; +const char* EV_ADDON_UNLOADED = "EV_ADDON_UNLOADED"; /* DataLink */ const char* DL_MUMBLE_LINK = "DL_MUMBLE_LINK"; diff --git a/src/Consts.h b/src/Consts.h index 7e8d3a9..13ff749 100644 --- a/src/Consts.h +++ b/src/Consts.h @@ -52,6 +52,8 @@ extern const char* EV_WINDOW_RESIZED; extern const char* EV_MUMBLE_IDENTITY_UPDATED; extern const char* EV_OPTIONS_CALLED; extern const char* EV_EULA_ACCEPTED; +extern const char* EV_ADDON_LOADED; +extern const char* EV_ADDON_UNLOADED; /* DataLink */ extern const char* DL_MUMBLE_LINK; diff --git a/src/Events/EventHandler.cpp b/src/Events/EventHandler.cpp index 6f3c8b7..1c46e18 100644 --- a/src/Events/EventHandler.cpp +++ b/src/Events/EventHandler.cpp @@ -2,7 +2,9 @@ #include <thread> #include <algorithm> +#include <chrono> +#include "core.h" #include "Consts.h" #include "Shared.h" @@ -13,48 +15,55 @@ namespace Events void Raise(const char* aIdentifier, void* aEventData) { + //auto start_time = std::chrono::high_resolution_clock::now(); + std::string str = aIdentifier; - Events::Mutex.lock(); + const std::lock_guard<std::mutex> lock(Mutex); { for (EVENT_CONSUME callback : Registry[str]) { - std::thread([callback, aEventData]() { callback(aEventData); }).detach(); + callback(aEventData); } } - Events::Mutex.unlock(); + + //auto end_time = std::chrono::high_resolution_clock::now(); + //auto time = end_time - start_time; + //LogDebug(CH_EVENTS, u8"Executed event (%s) in %uµs.", aIdentifier, time / std::chrono::microseconds(1)); + } + void RaiseNotification(const char* aIdentifier) + { + Raise(aIdentifier, nullptr); } void Subscribe(const char* aIdentifier, EVENT_CONSUME aConsumeEventCallback) { std::string str = aIdentifier; - Events::Mutex.lock(); + const std::lock_guard<std::mutex> lock(Mutex); { Registry[str].push_back(aConsumeEventCallback); } - Events::Mutex.unlock(); } void Unsubscribe(const char* aIdentifier, EVENT_CONSUME aConsumeEventCallback) { std::string str = aIdentifier; - Events::Mutex.lock(); + const std::lock_guard<std::mutex> lock(Mutex); { if (Registry.find(str) != Registry.end()) { Registry[str].erase(std::remove(Registry[str].begin(), Registry[str].end(), aConsumeEventCallback), Registry[str].end()); } } - Events::Mutex.unlock(); } int Verify(void* aStartAddress, void* aEndAddress) { int refCounter = 0; - Events::Mutex.lock(); + const std::lock_guard<std::mutex> lock(Mutex); { for (auto& [identifier, consumers] : Registry) { @@ -68,7 +77,6 @@ namespace Events } } } - Events::Mutex.unlock(); return refCounter; } diff --git a/src/Events/EventHandler.h b/src/Events/EventHandler.h index b5e0a57..170c85e 100644 --- a/src/Events/EventHandler.h +++ b/src/Events/EventHandler.h @@ -15,6 +15,8 @@ namespace Events /* Raises an event of provided name, passing a pointer to an eventArgs struct. */ void Raise(const char* aIdentifier, void* aEventData); + /* Raises an event without payload, a notification. */ + void RaiseNotification(const char* aIdentifier); /* Subscribes the provided ConsumeEventCallback function, to the provided event name. */ void Subscribe(const char* aIdentifier, EVENT_CONSUME aConsumeEventCallback); /* Unsubscribes the provided ConsumeEventCallback function from the provided event name. */ diff --git a/src/Events/FuncDefs.h b/src/Events/FuncDefs.h index 6dcaf9d..04d556c 100644 --- a/src/Events/FuncDefs.h +++ b/src/Events/FuncDefs.h @@ -3,6 +3,7 @@ typedef void (*EVENT_CONSUME)(void* aEventArgs); typedef void (*EVENTS_RAISE)(const char* aIdentifier, void* aEventData); +typedef void (*EVENTS_RAISENOTIFICATION)(const char* aIdentifier); typedef void (*EVENTS_SUBSCRIBE)(const char* aIdentifier, EVENT_CONSUME aConsumeEventCallback); #endif \ No newline at end of file diff --git a/src/GUI/GUI.cpp b/src/GUI/GUI.cpp index 7ed6662..ac9d462 100644 --- a/src/GUI/GUI.cpp +++ b/src/GUI/GUI.cpp @@ -331,31 +331,31 @@ namespace GUI if (arcIni.is_open()) { std::string line; - std::string style = "appearance_imgui_style180="; - std::string colours = "appearance_imgui_colours180="; + std::string styleKey = "appearance_imgui_style180="; + std::string coloursKey = "appearance_imgui_colours180="; + std::string fontSizeKey = "font_size="; + + ImGuiStyle* style = &ImGui::GetStyle(); while (std::getline(arcIni, line)) { - if (line.find(style, 0) != line.npos) + if (line.find(styleKey, 0) != line.npos) { - line = line.substr(style.length()); - - ImGuiStyle* style = &ImGui::GetStyle(); - + line = line.substr(styleKey.length()); std::string decode = Base64::Decode(&line[0], line.length()); - memcpy(style, &decode[0], decode.length()); } - else if (line.find(colours, 0) != line.npos) + else if (line.find(coloursKey, 0) != line.npos) { - line = line.substr(colours.length()); - - ImGuiStyle* style = &ImGui::GetStyle(); - + line = line.substr(coloursKey.length()); std::string decode = Base64::Decode(&line[0], line.length()); - memcpy(&style->Colors[0], &decode[0], decode.length()); } + else if (line.find(fontSizeKey, 0) != line.npos) + { + line = line.substr(fontSizeKey.length()); + FontSize = std::stof(line); + } } arcIni.close(); } @@ -454,99 +454,155 @@ namespace GUI } } } - void OnEULAAccepted(void* aEventArgs) - { - Mutex.lock(); - auto it = std::find_if(Windows.begin(), Windows.end(), [](const IWindow* wnd) { - return wnd->Name == "EULAModal"; - }); - - if (it != Windows.end()) - { - delete (*it); - Windows.erase(it); - } - Mutex.unlock(); - Events::Unsubscribe(EV_EULA_ACCEPTED, OnEULAAccepted); - } void Setup() { ImGuiIO& io = ImGui::GetIO(); - /* add user font, or fallback to default */ - if (std::filesystem::exists(Path::F_FONT)) + if (!Settings::Settings.is_null()) { - FontSize = 16.0f; - if (!Settings::Settings.is_null()) + if (!Settings::Settings[OPT_ACCEPTEULA].is_null()) { - if (!Settings::Settings[OPT_FONTSIZE].is_null()) - { - Settings::Settings[OPT_FONTSIZE].get_to(FontSize); - } - else - { - Settings::Settings[OPT_FONTSIZE] = FontSize; - Settings::Save(); - } + Settings::Settings[OPT_ACCEPTEULA].get_to(HasAcceptedEULA); + } + else + { + HasAcceptedEULA = false; } - std::string strFont = Path::F_FONT.string(); - io.Fonts->AddFontFromFileTTF(strFont.c_str(), FontSize); - } - else - { - io.Fonts->AddFontDefault(); - } + if (!Settings::Settings[OPT_NOTIFYCHANGELOG].is_null()) + { + Settings::Settings[OPT_NOTIFYCHANGELOG].get_to(NotifyChangelog); + } + else + { + NotifyChangelog = false; + } - /* load gw2 fonts */ + if (!Settings::Settings[OPT_FONTSIZE].is_null()) + { + Settings::Settings[OPT_FONTSIZE].get_to(FontSize); + } + else + { + FontSize = 16.0f; + } - LPVOID resM{}; DWORD szM{}; - HRSRC hResM = FindResourceA(NexusHandle, MAKEINTRESOURCE(RES_FONT_MENOMONIA), RT_FONT); - if (hResM) - { - HGLOBAL hLResM = LoadResource(NexusHandle, hResM); + if (!Settings::Settings[OPT_LASTUISCALE].is_null() && Renderer::Scaling == 0) + { + Settings::Settings[OPT_LASTUISCALE].get_to(LastScaling); + Settings::Settings[OPT_LASTUISCALE].get_to(Renderer::Scaling); + } + else + { + LastScaling = SC_NORMAL; + Renderer::Scaling = SC_NORMAL; + Settings::Settings[OPT_LASTUISCALE] = SC_NORMAL; + } - if (hLResM) + if (!Settings::Settings[OPT_QAVERTICAL].is_null()) { - LPVOID pLResM = LockResource(hLResM); + Settings::Settings[OPT_QAVERTICAL].get_to(QuickAccess::VerticalLayout); + } + else + { + QuickAccess::VerticalLayout = false; + Settings::Settings[OPT_QAVERTICAL] = false; + } - if (pLResM) - { - DWORD dwResSzM = SizeofResource(NexusHandle, hResM); + if (!Settings::Settings[OPT_QALOCATION].is_null()) + { + Settings::Settings[OPT_QALOCATION].get_to(QuickAccess::Location); + } + else + { + QuickAccess::Location = EQAPosition::Extend; + Settings::Settings[OPT_QALOCATION] = 0; + } - if (0 != dwResSzM) - { - resM = pLResM; - szM = dwResSzM; - } - } + if (!Settings::Settings[OPT_QAOFFSETX].is_null() && !Settings::Settings[OPT_QAOFFSETY].is_null()) + { + Settings::Settings[OPT_QAOFFSETX].get_to(QuickAccess::Offset.x); + Settings::Settings[OPT_QAOFFSETY].get_to(QuickAccess::Offset.y); + } + else + { + QuickAccess::Offset = ImVec2(0.0f, 0.0f); + Settings::Settings[OPT_QAOFFSETX] = 0.0f; + Settings::Settings[OPT_QAOFFSETY] = 0.0f; } - } - LPVOID resT{}; DWORD szT{}; - HRSRC hResT = FindResourceA(NexusHandle, MAKEINTRESOURCE(RES_FONT_TREBUCHET), RT_FONT); - if (hResT) - { - HGLOBAL hLResT = LoadResource(NexusHandle, hResT); + if (!Settings::Settings[OPT_CLOSEMENU].is_null()) + { + Settings::Settings[OPT_CLOSEMENU].get_to(CloseMenuAfterSelecting); + } + else + { + CloseMenuAfterSelecting = true; + Settings::Settings[OPT_CLOSEMENU] = true; + } - if (hLResT) + if (!Settings::Settings[OPT_CLOSEESCAPE].is_null()) + { + Settings::Settings[OPT_CLOSEESCAPE].get_to(CloseOnEscape); + } + else { - LPVOID pLResT = LockResource(hLResT); + CloseOnEscape = true; + Settings::Settings[OPT_CLOSEESCAPE] = true; + } - if (pLResT) - { - DWORD dwResSzT = SizeofResource(NexusHandle, hResT); + if (!Settings::Settings[OPT_LINKARCSTYLE].is_null()) + { + Settings::Settings[OPT_LINKARCSTYLE].get_to(LinkArcDPSStyle); + } + else + { + LinkArcDPSStyle = true; + Settings::Settings[OPT_LINKARCSTYLE] = true; + } - if (0 != dwResSzT) - { - resT = pLResT; - szT = dwResSzT; - } - } + ImGuiStyle* style = &ImGui::GetStyle(); + if (!Settings::Settings[OPT_IMGUISTYLE].is_null()) + { + std::string style64 = Settings::Settings[OPT_IMGUISTYLE].get<std::string>(); + std::string decode = Base64::Decode(&style64[0], style64.length()); + memcpy(style, &decode[0], decode.length()); + } + + if (!Settings::Settings[OPT_IMGUICOLORS].is_null()) + { + std::string colors64 = Settings::Settings[OPT_IMGUICOLORS].get<std::string>(); + std::string decode = Base64::Decode(&colors64[0], colors64.length()); + memcpy(&style->Colors[0], &decode[0], decode.length()); } } + ImportArcDPSStyle(); + + /* add user font, or fallback to default */ + if (!LinkArcDPSStyle && std::filesystem::exists(Path::F_FONT)) + { + std::string strFont = Path::F_FONT.string(); + io.Fonts->AddFontFromFileTTF(strFont.c_str(), FontSize); + } + else if (LinkArcDPSStyle && std::filesystem::exists(Path::D_GW2_ADDONS / "arcdps" / "arcdps_font.ttf")) + { + std::string strFont = (Path::D_GW2_ADDONS / "arcdps" / "arcdps_font.ttf").string(); + io.Fonts->AddFontFromFileTTF(strFont.c_str(), FontSize); + } + else + { + io.Fonts->AddFontDefault(); + } + + /* load gw2 fonts */ + LPVOID resM{}; DWORD szM{}; + GetResource(NexusHandle, MAKEINTRESOURCE(RES_FONT_MENOMONIA), RT_FONT, &resM, &szM); + + LPVOID resT{}; DWORD szT{}; + GetResource(NexusHandle, MAKEINTRESOURCE(RES_FONT_TREBUCHET), RT_FONT, &resT, &szT); + GUI::Mutex.lock(); { /* small UI*/ @@ -661,101 +717,6 @@ namespace GUI /* add shortcut */ QuickAccess::AddShortcut(QA_MENU, ICON_NEXUS, ICON_NEXUS_HOVER, KB_MENU, "Nexus Menu"); - if (!Settings::Settings.is_null()) - { - if (!Settings::Settings[OPT_ACCEPTEULA].is_null()) - { - Settings::Settings[OPT_ACCEPTEULA].get_to(HasAcceptedEULA); - } - else - { - HasAcceptedEULA = false; - } - - if (!Settings::Settings[OPT_NOTIFYCHANGELOG].is_null()) - { - Settings::Settings[OPT_NOTIFYCHANGELOG].get_to(NotifyChangelog); - } - else - { - NotifyChangelog = false; - } - - if (!Settings::Settings[OPT_LASTUISCALE].is_null() && Renderer::Scaling == 0) - { - Settings::Settings[OPT_LASTUISCALE].get_to(LastScaling); - Settings::Settings[OPT_LASTUISCALE].get_to(Renderer::Scaling); - } - else - { - LastScaling = SC_NORMAL; - Renderer::Scaling = SC_NORMAL; - Settings::Settings[OPT_LASTUISCALE] = SC_NORMAL; - } - - if (!Settings::Settings[OPT_QAVERTICAL].is_null()) - { - Settings::Settings[OPT_QAVERTICAL].get_to(QuickAccess::VerticalLayout); - } - else - { - QuickAccess::VerticalLayout = false; - Settings::Settings[OPT_QAVERTICAL] = false; - } - - if (!Settings::Settings[OPT_QALOCATION].is_null()) - { - Settings::Settings[OPT_QALOCATION].get_to(QuickAccess::Location); - } - else - { - QuickAccess::Location = EQAPosition::Extend; - Settings::Settings[OPT_QALOCATION] = 0; - } - - if (!Settings::Settings[OPT_QAOFFSETX].is_null() && !Settings::Settings[OPT_QAOFFSETY].is_null()) - { - Settings::Settings[OPT_QAOFFSETX].get_to(QuickAccess::Offset.x); - Settings::Settings[OPT_QAOFFSETY].get_to(QuickAccess::Offset.y); - } - else - { - QuickAccess::Offset = ImVec2(0.0f, 0.0f); - Settings::Settings[OPT_QAOFFSETX] = 0.0f; - Settings::Settings[OPT_QAOFFSETY] = 0.0f; - } - - if (!Settings::Settings[OPT_CLOSEMENU].is_null()) - { - Settings::Settings[OPT_CLOSEMENU].get_to(CloseMenuAfterSelecting); - } - else - { - CloseMenuAfterSelecting = true; - Settings::Settings[OPT_CLOSEMENU] = true; - } - - if (!Settings::Settings[OPT_CLOSEESCAPE].is_null()) - { - Settings::Settings[OPT_CLOSEESCAPE].get_to(CloseOnEscape); - } - else - { - CloseOnEscape = true; - Settings::Settings[OPT_CLOSEESCAPE] = true; - } - - if (!Settings::Settings[OPT_LINKARCSTYLE].is_null()) - { - Settings::Settings[OPT_LINKARCSTYLE].get_to(LinkArcDPSStyle); - } - else - { - LinkArcDPSStyle = true; - Settings::Settings[OPT_LINKARCSTYLE] = true; - } - } - if (IsUpdateAvailable && NotifyChangelog) { QuickAccess::NotifyShortcut(QA_MENU); @@ -765,11 +726,8 @@ namespace GUI { EULAModal* eulaModal = new EULAModal(); AddWindow(eulaModal); - Events::Subscribe(EV_EULA_ACCEPTED, OnEULAAccepted); } - ImportArcDPSStyle(); - IsSetup = true; } diff --git a/src/GUI/Widgets/Addons/AddonItem.cpp b/src/GUI/Widgets/Addons/AddonItem.cpp index 8857cb7..a30a129 100644 --- a/src/GUI/Widgets/Addons/AddonItem.cpp +++ b/src/GUI/Widgets/Addons/AddonItem.cpp @@ -121,7 +121,7 @@ namespace GUI } } } - if (!((aAddon->Definitions->Unload == nullptr && aAddon->State == EAddonState::Loaded) || aAddon->Definitions->HasFlag(EAddonFlags::DisableHotloading))) + if (aAddon->State == EAddonState::Loaded || aAddon->State == EAddonState::LoadedLOCKED) { amtBtns++; if (ImGui::GW2Button(("Uninstall##" + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) @@ -135,6 +135,10 @@ namespace GUI } } } + if (aAddon->State == EAddonState::LoadedLOCKED) + { + ImGui::TooltipGeneric("This addon is currently locked and requires a restart to be removed."); + } } if (aAddon->Definitions->Provider == EUpdateProvider::GitHub && aAddon->Definitions->UpdateLink) { @@ -154,7 +158,7 @@ namespace GUI } } - void AddonItem(LibraryAddon* aAddon) + void AddonItem(LibraryAddon* aAddon, bool aInstalled) { float btnHeight = 22.0f * Renderer::Scaling; float itemWidthScaled = itemWidth * Renderer::Scaling; @@ -200,19 +204,26 @@ namespace GUI ImGui::BeginChild("##AddonItemActions", ImVec2(actionsWidth, itemHeightScaled - 12.0f - 12.0f)); // just check if loaded, if it was not hot-reloadable it would be EAddonState::LoadedLOCKED - if (ImGui::GW2Button(aAddon->IsInstalling ? ("Installing...##" + sig).c_str() : ("Install##" + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) + if (!aInstalled) { - if (!aAddon->IsInstalling) + + if (ImGui::GW2Button(aAddon->IsInstalling ? ("Installing...##" + sig).c_str() : ("Install##" + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) { - std::thread([aAddon]() - { - Loader::InstallAddon(aAddon); - aAddon->IsInstalling = false; - Loader::NotifyChanges(); - }) - .detach(); + if (!aAddon->IsInstalling) + { + std::thread([aAddon]() + { + Loader::InstallAddon(aAddon); + aAddon->IsInstalling = false; + }) + .detach(); + } } } + else + { + ImGui::Text("Already installed."); + } if (aAddon->Provider == EUpdateProvider::GitHub && !aAddon->DownloadURL.empty()) { if (ImGui::GW2Button(("GitHub##" + sig).c_str(), ImVec2(btnWidth * ImGui::GetFontSize(), btnHeight))) diff --git a/src/GUI/Widgets/Addons/AddonItem.h b/src/GUI/Widgets/Addons/AddonItem.h index 8094842..e567314 100644 --- a/src/GUI/Widgets/Addons/AddonItem.h +++ b/src/GUI/Widgets/Addons/AddonItem.h @@ -7,7 +7,7 @@ namespace GUI { void AddonItem(Addon* aAddon); - void AddonItem(LibraryAddon* aAddon); + void AddonItem(LibraryAddon* aAddon, bool aInstalled = false); } #endif \ No newline at end of file diff --git a/src/GUI/Widgets/Addons/AddonsWindow.cpp b/src/GUI/Widgets/Addons/AddonsWindow.cpp index bcfc2a0..5155c0f 100644 --- a/src/GUI/Widgets/Addons/AddonsWindow.cpp +++ b/src/GUI/Widgets/Addons/AddonsWindow.cpp @@ -23,6 +23,8 @@ namespace GUI float contentWidth = 540.0f; float contentHeight = 410.0f; + bool showInstalled = false; + AddonsWindow::AddonsWindow(std::string aName) { Name = aName; @@ -158,6 +160,7 @@ namespace GUI { for (auto& [path, addon] : Loader::Addons) { + if (path.filename() == "arcdps_integration64.dll") { continue; } AddonItem(addon); } } @@ -171,6 +174,34 @@ namespace GUI std::string strAddons = Path::D_GW2_ADDONS.string(); ShellExecuteA(NULL, "explore", strAddons.c_str(), NULL, NULL, SW_SHOW); } + ImGui::SameLine(); + if (ImGui::GW2Button("Check for Updates", ImVec2(ImGui::CalcTextSize("Check for Updates").x + 16.0f, btnHeight))) + { + const std::lock_guard<std::mutex> lock(Loader::Mutex); + { + for (auto& [path, addon] : Loader::Addons) + { + if (nullptr == addon->Definitions) { continue; } + + std::filesystem::path tmpPath = path.string(); + signed int tmpSig = addon->Definitions->Signature; + std::string tmpName = addon->Definitions->Name; + AddonVersion tmpVers = addon->Definitions->Version; + EUpdateProvider tmpProv = addon->Definitions->Provider; + std::string tmpLink = addon->Definitions->UpdateLink != nullptr ? addon->Definitions->UpdateLink : ""; + + std::thread([tmpPath, tmpSig, tmpName, tmpVers, tmpProv, tmpLink]() + { + if (Loader::UpdateAddon(tmpPath, tmpSig, tmpName, tmpVers, tmpProv, tmpLink)) + { + Loader::QueueAddon(ELoaderAction::Reload, tmpPath); + } + }) + .detach(); + } + } + } + ImGui::TooltipGeneric("Checks each addon and updates it, if available.\nSome addons require a restart to apply the update and won't take effect immediately."); } else if (TabIndex == 1) { @@ -194,9 +225,9 @@ namespace GUI } } } - if (!exists) + if (false == exists || true == showInstalled) { - AddonItem(libAddon); + AddonItem(libAddon, exists); downloadable++; } } @@ -213,6 +244,8 @@ namespace GUI ImGui::EndChild(); } + + ImGui::Checkbox("Show already installed", &showInstalled); } ImGui::EndChild(); diff --git a/src/GUI/Widgets/EULA/EULAModal.cpp b/src/GUI/Widgets/EULA/EULAModal.cpp index 2614743..368dfe4 100644 --- a/src/GUI/Widgets/EULA/EULAModal.cpp +++ b/src/GUI/Widgets/EULA/EULAModal.cpp @@ -80,7 +80,15 @@ namespace GUI if (close) { ImGui::CloseCurrentPopup(); - Events::Raise(EV_EULA_ACCEPTED, nullptr); + auto it = std::find_if(GUI::Windows.begin(), GUI::Windows.end(), [](const IWindow* wnd) { + return wnd->Name == "EULAModal"; + }); + + if (it != GUI::Windows.end()) + { + delete (*it); + GUI::Windows.erase(it); + } } ImGui::EndPopup(); diff --git a/src/GUI/Widgets/Menu/Menu.cpp b/src/GUI/Widgets/Menu/Menu.cpp index b128eb0..80a2bba 100644 --- a/src/GUI/Widgets/Menu/Menu.cpp +++ b/src/GUI/Widgets/Menu/Menu.cpp @@ -35,6 +35,7 @@ namespace GUI float bgSz = 220 * Renderer::Scaling; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1)); if (ImGui::Begin("Menu", &Visible, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar)) { @@ -68,6 +69,7 @@ namespace GUI } ImGui::End(); + ImGui::PopStyleColor(); ImGui::PopStyleVar(); } diff --git a/src/GUI/Widgets/Options/OptionsWindow.cpp b/src/GUI/Widgets/Options/OptionsWindow.cpp index b3135d1..7d73a73 100644 --- a/src/GUI/Widgets/Options/OptionsWindow.cpp +++ b/src/GUI/Widgets/Options/OptionsWindow.cpp @@ -171,12 +171,39 @@ namespace GUI ImportArcDPSStyle(); Settings::Save(); } + ImGui::TooltipGeneric("This will read out the ImGui style settings from ArcDPS and apply them to Nexus.\nChanging ArcDPS style at runtime is not reflected by Nexus as ArcDPS does only save on shutdown.\nRestart required."); if (!GUI::LinkArcDPSStyle) { ImGuiStyle& style = ImGui::GetStyle(); - ImGuiStyle ref_saved_style = style; - ImGuiStyle* ref = &ref_saved_style; + + if (ImGui::SmallButton("Save Style")) + { + std::string encode = Base64::Encode((unsigned char*)&style, sizeof(ImGuiStyle)); + Settings::Settings[OPT_IMGUISTYLE] = encode; + Settings::Save(); + + encode = Base64::Encode((unsigned char*)&style.Colors[0], sizeof(ImVec4) * ImGuiCol_COUNT); + Settings::Settings[OPT_IMGUICOLORS] = encode; + Settings::Save(); + } + ImGui::SameLine(); + if (ImGui::SmallButton("Revert Changes")) + { + if (!Settings::Settings[OPT_IMGUISTYLE].is_null()) + { + std::string style64 = Settings::Settings[OPT_IMGUISTYLE].get<std::string>(); + std::string decode = Base64::Decode(&style64[0], style64.length()); + memcpy(&style, &decode[0], decode.length()); + } + + if (!Settings::Settings[OPT_IMGUICOLORS].is_null()) + { + std::string colors64 = Settings::Settings[OPT_IMGUICOLORS].get<std::string>(); + std::string decode = Base64::Decode(&colors64[0], colors64.length()); + memcpy(&style.Colors[0], &decode[0], decode.length()); + } + } if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) { @@ -237,14 +264,6 @@ namespace GUI continue; ImGui::PushID(i); ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf); - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) - { - // Tips: in a real user application, you may want to merge and use an icon font into the main font, - // so instead of "Save"/"Revert" you'd use icons! - // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient! - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) { ref->Colors[i] = style.Colors[i]; } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) { style.Colors[i] = ref->Colors[i]; } - } ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); ImGui::TextUnformatted(name); ImGui::PopID(); diff --git a/src/Hooks.cpp b/src/Hooks.cpp index e2715a5..f87eb7d 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -63,59 +63,62 @@ namespace Hooks } HRESULT __stdcall DXGIPresent(IDXGISwapChain* pChain, UINT SyncInterval, UINT Flags) { - if (Renderer::SwapChain != pChain) + if (!(State::Nexus == ENexusState::SHUTTING_DOWN || State::Nexus == ENexusState::SHUTDOWN)) { - Renderer::SwapChain = pChain; - - if (Renderer::Device) + if (Renderer::SwapChain != pChain) { - Renderer::DeviceContext->Release(); - Renderer::DeviceContext = 0; - Renderer::Device->Release(); - Renderer::Device = 0; - } + Renderer::SwapChain = pChain; - Renderer::SwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&Renderer::Device); - Renderer::Device->GetImmediateContext(&Renderer::DeviceContext); + if (Renderer::Device) + { + Renderer::DeviceContext->Release(); + Renderer::DeviceContext = 0; + Renderer::Device->Release(); + Renderer::Device = 0; + } - DXGI_SWAP_CHAIN_DESC swapChainDesc; - Renderer::SwapChain->GetDesc(&swapChainDesc); + Renderer::SwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&Renderer::Device); + Renderer::Device->GetImmediateContext(&Renderer::DeviceContext); - Renderer::WindowHandle = swapChainDesc.OutputWindow; - Hooks::GW2::WndProc = (WNDPROC)SetWindowLongPtr(Renderer::WindowHandle, GWLP_WNDPROC, (LONG_PTR)WndProc); + DXGI_SWAP_CHAIN_DESC swapChainDesc; + Renderer::SwapChain->GetDesc(&swapChainDesc); - Loader::Initialize(); + Renderer::WindowHandle = swapChainDesc.OutputWindow; + Hooks::GW2::WndProc = (WNDPROC)SetWindowLongPtr(Renderer::WindowHandle, GWLP_WNDPROC, (LONG_PTR)WndProc); - State::Nexus = ENexusState::READY; - State::Directx = EDxState::READY; /* acquired swapchain */ - } + Loader::Initialize(); - if (State::Nexus == ENexusState::READY && !State::IsImGuiInitialized) - { - GUI::Initialize(); - } + State::Nexus = ENexusState::READY; + State::Directx = EDxState::READY; /* acquired swapchain */ + } - Loader::ProcessQueue(); + if (State::Nexus == ENexusState::READY && !State::IsImGuiInitialized) + { + GUI::Initialize(); + } - TextureLoader::ProcessQueue(); + Loader::ProcessQueue(); - //void UpdateNexusLink() - { - NexusLink->Width = Renderer::Width; - NexusLink->Height = Renderer::Height; - NexusLink->Scaling = Renderer::Scaling; + TextureLoader::ProcessQueue(); - NexusLink->IsMoving = IsMoving; - NexusLink->IsCameraMoving = IsCameraMoving; - NexusLink->IsGameplay = IsGameplay; + //void UpdateNexusLink() + { + NexusLink->Width = Renderer::Width; + NexusLink->Height = Renderer::Height; + NexusLink->Scaling = Renderer::Scaling; - NexusLink->Font = Font; - NexusLink->FontBig = FontBig; - NexusLink->FontUI = FontUI; - } + NexusLink->IsMoving = IsMoving; + NexusLink->IsCameraMoving = IsCameraMoving; + NexusLink->IsGameplay = IsGameplay; - GUI::Render(); + NexusLink->Font = Font; + NexusLink->FontBig = FontBig; + NexusLink->FontUI = FontUI; + } + GUI::Render(); + } + return Hooks::DXGI::Present(pChain, SyncInterval, Flags); } HRESULT __stdcall DXGIResizeBuffers(IDXGISwapChain* pChain, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) @@ -130,7 +133,7 @@ namespace Hooks NexusLink->Width = Width; NexusLink->Height = Height; - Events::Raise(EV_WINDOW_RESIZED, nullptr); + Events::RaiseNotification(EV_WINDOW_RESIZED); return Hooks::DXGI::ResizeBuffers(pChain, BufferCount, Width, Height, NewFormat, SwapChainFlags); } diff --git a/src/Loader/AddonAPI.h b/src/Loader/AddonAPI.h index ec4fbd6..bdd87cb 100644 --- a/src/Loader/AddonAPI.h +++ b/src/Loader/AddonAPI.h @@ -99,6 +99,7 @@ struct AddonAPI2 : AddonAPI /* Events */ EVENTS_RAISE RaiseEvent; + EVENTS_RAISENOTIFICATION RaiseEventNotification; EVENTS_SUBSCRIBE SubscribeEvent; EVENTS_SUBSCRIBE UnsubscribeEvent; diff --git a/src/Loader/Loader.cpp b/src/Loader/Loader.cpp index 519e6a0..f088dde 100644 --- a/src/Loader/Loader.cpp +++ b/src/Loader/Loader.cpp @@ -7,6 +7,8 @@ #include <malloc.h> #include <vector> +#include "resource.h" + #include "core.h" #include "State.h" #include "Shared.h" @@ -61,6 +63,7 @@ namespace Loader std::string extDll = ".dll"; std::string extUpdate = ".update"; std::string extOld = ".old"; + std::string extUninstall = ".uninstall"; void Initialize() { @@ -78,7 +81,7 @@ namespace Loader FSNotifierID = SHChangeNotifyRegister( Renderer::WindowHandle, SHCNRF_InterruptLevel | SHCNRF_NewDelivery, - SHCNE_UPDATEITEM, + SHCNE_UPDATEITEM | SHCNE_UPDATEDIR, WM_ADDONDIRUPDATE, 1, &changeentry @@ -155,7 +158,7 @@ namespace Loader if (notificationLock != 0) { - if (event == SHCNE_UPDATEITEM) + if (event == SHCNE_UPDATEITEM || event == SHCNE_UPDATEDIR) { char path[MAX_PATH]; if (SHGetPathFromIDList(ppidl[0], path)) @@ -301,6 +304,19 @@ namespace Loader { QueueAddon(ELoaderAction::Load, path); } + + if (path.extension() == extUninstall) + { + try + { + std::filesystem::remove(path); + } + catch (std::filesystem::filesystem_error fErr) + { + LogDebug(CH_LOADER, "%s", fErr.what()); + return; + } + } } } @@ -459,9 +475,53 @@ namespace Loader LogInfo(CH_LOADER, "Loaded addon: %s (Signature %d) [%p - %p] (API Version %d was requested.)", strPath, addon->Definitions->Signature, addon->Module, ((PBYTE)addon->Module) + moduleInfo.SizeOfImage, addon->Definitions->APIVersion); } addon->Definitions->Load(api); + Events::Raise(EV_ADDON_LOADED, &addon->Definitions->Signature); + Events::Raise(EV_MUMBLE_IDENTITY_UPDATED, MumbleIdentity); bool locked = addon->Definitions->Unload == nullptr || addon->Definitions->HasFlag(EAddonFlags::DisableHotloading); addon->State = locked ? EAddonState::LoadedLOCKED : EAddonState::Loaded; + + if (addon->Definitions->Signature == 0xFFF694D1) + { + typedef int (*addextension2)(HINSTANCE); + addextension2 exp_addextension2; + + if (true == FindFunction(addon->Module, &exp_addextension2, "addextension2")) + { + LPVOID res{}; DWORD sz{}; + GetResource(NexusHandle, MAKEINTRESOURCE(RES_ARCDPS_INTEGRATION), "DLL", &res, &sz); + + try + { + if (std::filesystem::exists(Path::F_ARCDPSINTEGRATION)) + { + std::filesystem::remove(Path::F_ARCDPSINTEGRATION); + } + + std::ofstream file(Path::F_ARCDPSINTEGRATION, std::ios::binary); + file.write((const char*)res, sz); + file.close(); + + HMODULE arcInt64 = LoadLibraryA(Path::F_ARCDPSINTEGRATION.string().c_str()); + int result = exp_addextension2(arcInt64); + + LogInfo(CH_LOADER, "Deployed ArcDPS Integration. Result: %d", result); + if (result == 0) + { + QueueAddon(ELoaderAction::Load, Path::F_ARCDPSINTEGRATION); + } + } + catch (std::filesystem::filesystem_error fErr) + { + LogDebug(CH_LOADER, "%s", fErr.what()); + return; + } + } + else + { + LogWarning(CH_LOADER, "Addon with signature \"0xFFF694D1\" found but \"addextension2\" is not exported. ArcDPS combat events won't be relayed."); + } + } } void UnloadAddon(const std::filesystem::path& aPath, bool aIsShutdown) { @@ -491,6 +551,7 @@ namespace Loader if (addon->State == EAddonState::Loaded) { addon->Definitions->Unload(); + Events::Raise(EV_ADDON_UNLOADED, &addon->Definitions->Signature); } else if (addon->State == EAddonState::LoadedLOCKED && aIsShutdown) { @@ -498,6 +559,7 @@ namespace Loader { /* If it's a shutdown and Unload is defined, let the addon run its shutdown routine to save settings etc, but do not freelib */ addon->Definitions->Unload(); + Events::Raise(EV_ADDON_UNLOADED, &addon->Definitions->Signature); } } } @@ -582,7 +644,7 @@ namespace Loader { try { - std::filesystem::rename(aPath, aPath.string() + extOld); + std::filesystem::rename(aPath, aPath.string() + extUninstall); Addons.erase(aPath); // remove from addons list anyway LogWarning(CH_LOADER, "Addon is stilled loaded, it will be uninstalled the next time the game is restarted: %s", aPath.string().c_str()); } @@ -663,7 +725,15 @@ namespace Loader Addon* addon = (*it).second; /* cleanup old files */ - if (std::filesystem::exists(pathOld)) { std::filesystem::remove(pathOld); } + try + { + if (std::filesystem::exists(pathOld)) { std::filesystem::remove(pathOld); } + } + catch (std::filesystem::filesystem_error fErr) + { + LogDebug(CH_LOADER, "%s", fErr.what()); + return true; // report as update here, as it was probably moved here during runtime but the dll is locked + } if (std::filesystem::exists(pathUpdate)) { if (addon->MD5 != MD5FromFile(pathUpdate)) @@ -671,7 +741,15 @@ namespace Loader return true; } - std::filesystem::remove(pathUpdate); + try + { + std::filesystem::remove(pathUpdate); + } + catch (std::filesystem::filesystem_error fErr) + { + LogDebug(CH_LOADER, "%s", fErr.what()); + return false; + } } bool wasUpdated = false; @@ -1088,6 +1166,7 @@ namespace Loader } LogInfo(CH_LOADER, "Successfully installed %s.", aAddon->Name.c_str()); + NotifyChanges(); } AddonAPI* GetAddonAPI(int aVersion) @@ -1173,6 +1252,7 @@ namespace Loader ((AddonAPI2*)api)->Log = LogMessageAddon; ((AddonAPI2*)api)->RaiseEvent = Events::Raise; + ((AddonAPI2*)api)->RaiseEventNotification = Events::RaiseNotification; ((AddonAPI2*)api)->SubscribeEvent = Events::Subscribe; ((AddonAPI2*)api)->UnsubscribeEvent = Events::Unsubscribe; diff --git a/src/Nexus.rc b/src/Nexus.rc index 6f6d24c..520f6c1 100644 --- a/src/Nexus.rc +++ b/src/Nexus.rc @@ -139,6 +139,8 @@ RES_TEX_BTNCLOSE_HOVER PNG "Resources\\BtnClose_Hover.png" RES_TEX_TITLEBAREND PNG "Resources\\TitleBarEnd.png" RES_TEX_TITLEBAREND_HOVER PNG "Resources\\TitleBarEnd_Hover.png" +RES_ARCDPS_INTEGRATION DLL "Resources\\bin64\\arcdps_integration64.dll" + #endif // English (United Kingdom) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/src/Nexus.vcxproj b/src/Nexus.vcxproj index d10cd2a..533a24c 100644 --- a/src/Nexus.vcxproj +++ b/src/Nexus.vcxproj @@ -318,6 +318,7 @@ </ItemGroup> <ItemGroup> <None Include="exports.def" /> + <None Include="Resources\bin64\arcdps_integration64.dll" /> </ItemGroup> <ItemGroup> <Font Include="Resources\Fonts\Menomonia.ttf" /> diff --git a/src/Nexus.vcxproj.filters b/src/Nexus.vcxproj.filters index f02220f..942dd30 100644 --- a/src/Nexus.vcxproj.filters +++ b/src/Nexus.vcxproj.filters @@ -121,6 +121,9 @@ <Filter Include="Resource Files\Images\QuickAccess"> <UniqueIdentifier>{c46e3486-9b1a-4bf8-bc57-adcaf1023e47}</UniqueIdentifier> </Filter> + <Filter Include="Resource Files\bin64"> + <UniqueIdentifier>{86b26924-5f68-4789-a455-7a9aadb24c45}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="Main.cpp"> @@ -1035,6 +1038,9 @@ <None Include="exports.def"> <Filter>Resource Files</Filter> </None> + <None Include="Resources\bin64\arcdps_integration64.dll"> + <Filter>Resource Files\bin64</Filter> + </None> </ItemGroup> <ItemGroup> <Font Include="Resources\Fonts\Menomonia.ttf"> diff --git a/src/Paths.cpp b/src/Paths.cpp index a40469a..715ec61 100644 --- a/src/Paths.cpp +++ b/src/Paths.cpp @@ -20,6 +20,7 @@ namespace Path std::filesystem::path F_OLD_DLL{}; std::filesystem::path F_SYSTEM_DLL{}; std::filesystem::path F_CHAINLOAD_DLL{}; + std::filesystem::path F_ARCDPSINTEGRATION{}; std::filesystem::path F_LOG{}; std::filesystem::path F_KEYBINDS{}; @@ -63,6 +64,7 @@ namespace Path F_UPDATE_DLL = F_HOST_DLL.string() + ".update"; /* get update dll path */ PathSystemAppend(F_SYSTEM_DLL, "d3d11.dll"); /* get system dll path */ F_CHAINLOAD_DLL = D_GW2 / "d3d11_chainload.dll"; /* get chainload dll path */ + F_ARCDPSINTEGRATION = D_GW2_ADDONS_NEXUS / "arcdps_integration64.dll"; /* get arcdps integration dll path */ /* push to paths */ ExistingPaths.push_back(D_GW2.string()); diff --git a/src/Paths.h b/src/Paths.h index 41e19e4..da9e3f7 100644 --- a/src/Paths.h +++ b/src/Paths.h @@ -23,6 +23,7 @@ namespace Path extern std::filesystem::path F_OLD_DLL; extern std::filesystem::path F_SYSTEM_DLL; extern std::filesystem::path F_CHAINLOAD_DLL; + extern std::filesystem::path F_ARCDPSINTEGRATION; extern std::filesystem::path F_LOG; extern std::filesystem::path F_KEYBINDS; diff --git a/src/Resources/bin64/arcdps_integration64.dll b/src/Resources/bin64/arcdps_integration64.dll new file mode 100644 index 0000000..c5268cf Binary files /dev/null and b/src/Resources/bin64/arcdps_integration64.dll differ diff --git a/src/Settings/Settings.cpp b/src/Settings/Settings.cpp index 346b09d..964ec2d 100644 --- a/src/Settings/Settings.cpp +++ b/src/Settings/Settings.cpp @@ -19,6 +19,8 @@ const char* OPT_QALOCATION = "QALocation"; const char* OPT_QAOFFSETX = "QAOffsetX"; const char* OPT_QAOFFSETY = "QAOffsetY"; const char* OPT_LINKARCSTYLE = "LinkWithArcDPSStyle"; +const char* OPT_IMGUISTYLE = "ImGuiStyle"; +const char* OPT_IMGUICOLORS = "ImGuiColors"; namespace Settings { diff --git a/src/Settings/Settings.h b/src/Settings/Settings.h index 61682e9..5090071 100644 --- a/src/Settings/Settings.h +++ b/src/Settings/Settings.h @@ -18,6 +18,8 @@ extern const char* OPT_QALOCATION; extern const char* OPT_QAOFFSETX; extern const char* OPT_QAOFFSETY; extern const char* OPT_LINKARCSTYLE; +extern const char* OPT_IMGUISTYLE; +extern const char* OPT_IMGUICOLORS; namespace Settings { diff --git a/src/core.cpp b/src/core.cpp index bde46c8..427fcfd 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -347,4 +347,36 @@ bool CreateDirectoryRecursive(std::string const& dirName, std::error_code& err) return false; } return true; -} \ No newline at end of file +} + +bool GetResource(HMODULE aModule, LPCSTR aResourceName, LPCSTR aResourceType, LPVOID* aOutLockedResource, DWORD* aOutResourceSize) +{ + HRSRC hRes = FindResourceA(aModule, aResourceName, aResourceType); + if (!hRes) + { + return false; + } + + HGLOBAL hLRes = LoadResource(aModule, hRes); + if (!hLRes) + { + return false; + } + + LPVOID pLRes = LockResource(hLRes); + if (!pLRes) + { + return false; + } + + DWORD dwResSz = SizeofResource(aModule, hRes); + if (!dwResSz) + { + return false; + } + + *aOutLockedResource = pLRes; + *aOutResourceSize = dwResSz; + + return true; +} diff --git a/src/core.h b/src/core.h index 105882f..2472182 100644 --- a/src/core.h +++ b/src/core.h @@ -41,3 +41,5 @@ namespace Base64 long long Timestamp(); bool CreateDirectoryRecursive(std::string const& dirName, std::error_code& err); + +bool GetResource(HMODULE aModule, LPCSTR aResourceName, LPCSTR aResourceType, LPVOID* aOutLockedResource, DWORD* aOutResourceSize); diff --git a/src/resource.h b/src/resource.h index 016b5da..90e5a6c 100644 --- a/src/resource.h +++ b/src/resource.h @@ -48,6 +48,8 @@ #define RES_TEX_TITLEBAREND 510 #define RES_TEX_TITLEBAREND_HOVER 511 +#define RES_ARCDPS_INTEGRATION 601 + // Next default values for new objects // #ifdef APSTUDIO_INVOKED