mirror of
https://github.com/wiiu-env/WiiUPluginLoaderBackend.git
synced 2026-04-26 00:27:19 -05:00
Merge 8b190a28de into 4d32c1f5ff
This commit is contained in:
commit
4e9bc97eab
17
Dockerfile
17
Dockerfile
|
|
@ -1,11 +1,12 @@
|
||||||
FROM ghcr.io/wiiu-env/devkitppc:20260126
|
FROM ghcr.io/wiiu-env/devkitppc:20260225
|
||||||
|
|
||||||
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20260126 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20260225 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20260126 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20260225 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20260208 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20260131 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libwupsbackend:20240425 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libwupsbackend:20260131 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libnotifications:20260131 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20250125-cb22627 /artifacts $DEVKITPRO
|
COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20260112 /artifacts $DEVKITPRO
|
||||||
|
COPY --from=ghcr.io/wiiu-env/libiopshell:20260318 /artifacts $DEVKITPRO
|
||||||
|
|
||||||
WORKDIR project
|
WORKDIR project
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -59,7 +59,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
||||||
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBS := -lwums -lwups -lwut -lfunctionpatcher -lmappedmemory -lz -lnotifications -lbuttoncombo
|
LIBS := -lwums -lwups -lwut -lfunctionpatcher -lmappedmemory -lz -lnotifications -lbuttoncombo -liopshell
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level
|
# list of directories containing libraries, this must be the top level
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "plugin/PluginMetaInformationFactory.h"
|
#include "plugin/PluginMetaInformationFactory.h"
|
||||||
#include "plugin/RelocationData.h"
|
#include "plugin/RelocationData.h"
|
||||||
#include "plugin/SectionInfo.h"
|
#include "plugin/SectionInfo.h"
|
||||||
|
#include "plugin/TrackingPluginHeapMemoryAllocator.h"
|
||||||
#include "utils/ElfUtils.h"
|
#include "utils/ElfUtils.h"
|
||||||
#include "utils/StringTools.h"
|
#include "utils/StringTools.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
|
|
@ -42,10 +43,10 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
|
||||||
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
DisplayErrorNotificationMessage(errMsg, 15.0f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
plugins.emplace_back(std::move(*metaInfo), std::move(*linkInfo), pluginDataWrapper.getPluginData());
|
plugins.emplace_back(std::move(*metaInfo), std::move(*linkInfo), pluginDataWrapper.getPluginData(), pluginDataWrapper.getHeapTrackingOptions());
|
||||||
} else {
|
} else {
|
||||||
DEBUG_FUNCTION_LINE_INFO("LOAD (INACTIVE) %s", metaInfo->getName().c_str());
|
DEBUG_FUNCTION_LINE_INFO("LOAD (INACTIVE) %s", metaInfo->getName().c_str());
|
||||||
plugins.emplace_back(std::move(*metaInfo), PluginLinkInformation::CreateStub(), pluginDataWrapper.getPluginData());
|
plugins.emplace_back(std::move(*metaInfo), PluginLinkInformation::CreateStub(), pluginDataWrapper.getPluginData(), std::nullopt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
|
auto errMsg = string_format("Failed to load plugin: %s", pluginDataWrapper.getPluginData()->getSource().c_str());
|
||||||
|
|
@ -67,23 +68,18 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
|
||||||
|
|
||||||
bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData,
|
bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData,
|
||||||
std::span<relocation_trampoline_entry_t> trampData,
|
std::span<relocation_trampoline_entry_t> trampData,
|
||||||
std::map<std::string, OSDynLoad_Module> &usedRPls) {
|
std::map<std::string, OSDynLoad_Module> &usedRPls,
|
||||||
|
const IPluginHeapMemoryAllocator &memory_allocator) {
|
||||||
for (auto const &cur : relocData) {
|
for (auto const &cur : relocData) {
|
||||||
uint32_t functionAddress = 0;
|
uint32_t functionAddress = 0;
|
||||||
auto &functionName = cur.getName();
|
auto &functionName = cur.getName();
|
||||||
|
|
||||||
if (functionName == "MEMAllocFromDefaultHeap") {
|
if (functionName == "MEMAllocFromDefaultHeap") {
|
||||||
OSDynLoad_Module rplHandle;
|
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetAllocFunctionAddress());
|
||||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
|
||||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemory", (void **) &functionAddress);
|
|
||||||
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
||||||
OSDynLoad_Module rplHandle;
|
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetAllocExFunctionAddress());
|
||||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
|
||||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", (void **) &functionAddress);
|
|
||||||
} else if (functionName == "MEMFreeToDefaultHeap") {
|
} else if (functionName == "MEMFreeToDefaultHeap") {
|
||||||
OSDynLoad_Module rplHandle;
|
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetFreeFunctionAddress());
|
||||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
|
||||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", (void **) &functionAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionAddress == 0) {
|
if (functionAddress == 0) {
|
||||||
|
|
@ -160,7 +156,7 @@ bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str());
|
DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str());
|
||||||
if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(),
|
if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(),
|
||||||
trampData,
|
trampData,
|
||||||
usedRPls)) {
|
usedRPls, pluginContainer.getMemoryAllocator())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
class IPluginHeapMemoryAllocator;
|
||||||
class RelocationData;
|
class RelocationData;
|
||||||
class PluginLoadWrapper;
|
class PluginLoadWrapper;
|
||||||
class PluginContainer;
|
class PluginContainer;
|
||||||
|
|
@ -25,7 +26,8 @@ public:
|
||||||
|
|
||||||
static bool doRelocation(const std::vector<RelocationData> &relocData,
|
static bool doRelocation(const std::vector<RelocationData> &relocData,
|
||||||
std::span<relocation_trampoline_entry_t> trampData,
|
std::span<relocation_trampoline_entry_t> trampData,
|
||||||
std::map<std::string, OSDynLoad_Module> &usedRPls);
|
std::map<std::string, OSDynLoad_Module> &usedRPls,
|
||||||
|
const IPluginHeapMemoryAllocator &);
|
||||||
|
|
||||||
static bool DoFunctionPatches(std::vector<PluginContainer> &plugins);
|
static bool DoFunctionPatches(std::vector<PluginContainer> &plugins);
|
||||||
|
|
||||||
|
|
|
||||||
692
source/ShellCommands.cpp
Normal file
692
source/ShellCommands.cpp
Normal file
|
|
@ -0,0 +1,692 @@
|
||||||
|
#include "ShellCommands.h"
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "plugin/ButtonComboManager.h"
|
||||||
|
#include "plugin/FunctionData.h"
|
||||||
|
#include "plugin/HookData.h"
|
||||||
|
#include "plugin/PluginContainer.h"
|
||||||
|
#include "plugin/PluginData.h"
|
||||||
|
#include "plugin/SectionInfo.h"
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
#include <iopshell/api.h>
|
||||||
|
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <numeric>
|
||||||
|
#include <optional>
|
||||||
|
#include <zconf.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string toString(WUPSButtonCombo_ComboType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_INVALID:
|
||||||
|
return "Invalid";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_HOLD:
|
||||||
|
return "Hold";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_HOLD_OBSERVER:
|
||||||
|
return "Hold (Observer)";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_PRESS_DOWN:
|
||||||
|
return "Press Down";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_PRESS_DOWN_OBSERVER:
|
||||||
|
return "Press Down (Observer)";
|
||||||
|
}
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(WUPSButtonCombo_ComboStatus type) {
|
||||||
|
switch (type) {
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_STATUS_INVALID_STATUS:
|
||||||
|
return "Invalid";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_STATUS_VALID:
|
||||||
|
return "Valid";
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_STATUS_CONFLICT:
|
||||||
|
return "Conflict";
|
||||||
|
}
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string toString(const function_replacement_library_type_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case LIBRARY_AVM:
|
||||||
|
return "AVM";
|
||||||
|
case LIBRARY_CAMERA:
|
||||||
|
return "CAMERA";
|
||||||
|
case LIBRARY_COREINIT:
|
||||||
|
return "COREINIT";
|
||||||
|
case LIBRARY_DC:
|
||||||
|
return "DC";
|
||||||
|
case LIBRARY_DMAE:
|
||||||
|
return "DMAE";
|
||||||
|
case LIBRARY_DRMAPP:
|
||||||
|
return "DRMAPP";
|
||||||
|
case LIBRARY_ERREULA:
|
||||||
|
return "ERREULA";
|
||||||
|
case LIBRARY_GX2:
|
||||||
|
return "GX2";
|
||||||
|
case LIBRARY_H264:
|
||||||
|
return "H264";
|
||||||
|
case LIBRARY_LZMA920:
|
||||||
|
return "LZMA920";
|
||||||
|
case LIBRARY_MIC:
|
||||||
|
return "MIC";
|
||||||
|
case LIBRARY_NFC:
|
||||||
|
return "NFC";
|
||||||
|
case LIBRARY_NIO_PROF:
|
||||||
|
return "NIO_PROF";
|
||||||
|
case LIBRARY_NLIBCURL:
|
||||||
|
return "NLIBCURL";
|
||||||
|
case LIBRARY_NLIBNSS:
|
||||||
|
return "NLIBNSS";
|
||||||
|
case LIBRARY_NLIBNSS2:
|
||||||
|
return "NLIBNSS2";
|
||||||
|
case LIBRARY_NN_AC:
|
||||||
|
return "NN_AC";
|
||||||
|
case LIBRARY_NN_ACP:
|
||||||
|
return "NN_ACP";
|
||||||
|
case LIBRARY_NN_ACT:
|
||||||
|
return "NN_ACT";
|
||||||
|
case LIBRARY_NN_AOC:
|
||||||
|
return "NN_AOC";
|
||||||
|
case LIBRARY_NN_BOSS:
|
||||||
|
return "NN_BOSS";
|
||||||
|
case LIBRARY_NN_CCR:
|
||||||
|
return "NN_CCR";
|
||||||
|
case LIBRARY_NN_CMPT:
|
||||||
|
return "NN_CMPT";
|
||||||
|
case LIBRARY_NN_DLP:
|
||||||
|
return "NN_DLP";
|
||||||
|
case LIBRARY_NN_EC:
|
||||||
|
return "NN_EC";
|
||||||
|
case LIBRARY_NN_FP:
|
||||||
|
return "NN_FP";
|
||||||
|
case LIBRARY_NN_HAI:
|
||||||
|
return "NN_HAI";
|
||||||
|
case LIBRARY_NN_HPAD:
|
||||||
|
return "NN_HPAD";
|
||||||
|
case LIBRARY_NN_IDBE:
|
||||||
|
return "NN_IDBE";
|
||||||
|
case LIBRARY_NN_NDM:
|
||||||
|
return "NN_NDM";
|
||||||
|
case LIBRARY_NN_NETS2:
|
||||||
|
return "NN_NETS2";
|
||||||
|
case LIBRARY_NN_NFP:
|
||||||
|
return "NN_NFP";
|
||||||
|
case LIBRARY_NN_NIM:
|
||||||
|
return "NN_NIM";
|
||||||
|
case LIBRARY_NN_OLV:
|
||||||
|
return "NN_OLV";
|
||||||
|
case LIBRARY_NN_PDM:
|
||||||
|
return "NN_PDM";
|
||||||
|
case LIBRARY_NN_SAVE:
|
||||||
|
return "NN_SAVE";
|
||||||
|
case LIBRARY_NN_SL:
|
||||||
|
return "NN_SL";
|
||||||
|
case LIBRARY_NN_SPM:
|
||||||
|
return "NN_SPM";
|
||||||
|
case LIBRARY_NN_TEMP:
|
||||||
|
return "NN_TEMP";
|
||||||
|
case LIBRARY_NN_UDS:
|
||||||
|
return "NN_UDS";
|
||||||
|
case LIBRARY_NN_VCTL:
|
||||||
|
return "NN_VCTL";
|
||||||
|
case LIBRARY_NSYSCCR:
|
||||||
|
return "NSYSCCR";
|
||||||
|
case LIBRARY_NSYSHID:
|
||||||
|
return "NSYSHID";
|
||||||
|
case LIBRARY_NSYSKBD:
|
||||||
|
return "NSYSKBD";
|
||||||
|
case LIBRARY_NSYSNET:
|
||||||
|
return "NSYSNET";
|
||||||
|
case LIBRARY_NSYSUHS:
|
||||||
|
return "NSYSUHS";
|
||||||
|
case LIBRARY_NSYSUVD:
|
||||||
|
return "NSYSUVD";
|
||||||
|
case LIBRARY_NTAG:
|
||||||
|
return "NTAG";
|
||||||
|
case LIBRARY_PADSCORE:
|
||||||
|
return "PADSCORE";
|
||||||
|
case LIBRARY_PROC_UI:
|
||||||
|
return "PROC_UI";
|
||||||
|
case LIBRARY_SND_CORE:
|
||||||
|
return "SND_CORE";
|
||||||
|
case LIBRARY_SND_USER:
|
||||||
|
return "SND_USER";
|
||||||
|
case LIBRARY_SNDCORE2:
|
||||||
|
return "SNDCORE2";
|
||||||
|
case LIBRARY_SNDUSER2:
|
||||||
|
return "SNDUSER2";
|
||||||
|
case LIBRARY_SWKBD:
|
||||||
|
return "SWKBD";
|
||||||
|
case LIBRARY_SYSAPP:
|
||||||
|
return "SYSAPP";
|
||||||
|
case LIBRARY_TCL:
|
||||||
|
return "TCL";
|
||||||
|
case LIBRARY_TVE:
|
||||||
|
return "TVE";
|
||||||
|
case LIBRARY_UAC:
|
||||||
|
return "UAC";
|
||||||
|
case LIBRARY_UAC_RPL:
|
||||||
|
return "UAC_RPL";
|
||||||
|
case LIBRARY_USB_MIC:
|
||||||
|
return "USB_MIC";
|
||||||
|
case LIBRARY_UVC:
|
||||||
|
return "UVC";
|
||||||
|
case LIBRARY_UVD:
|
||||||
|
return "UVD";
|
||||||
|
case LIBRARY_VPAD:
|
||||||
|
return "VPAD";
|
||||||
|
case LIBRARY_VPADBASE:
|
||||||
|
return "VPADBASE";
|
||||||
|
case LIBRARY_ZLIB125:
|
||||||
|
return "ZLIB125";
|
||||||
|
case LIBRARY_OTHER:
|
||||||
|
return "OTHER";
|
||||||
|
}
|
||||||
|
return "OTHER";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getButtonChar(const WUPSButtonCombo_Buttons value) {
|
||||||
|
std::string combo;
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_A) {
|
||||||
|
return "A";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_B) {
|
||||||
|
return "B";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_X) {
|
||||||
|
return "X";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_Y) {
|
||||||
|
return "Y";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_L) {
|
||||||
|
return "L";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_R) {
|
||||||
|
return "R";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_ZL) {
|
||||||
|
return "ZL";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_ZR) {
|
||||||
|
return "ZR";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_UP) {
|
||||||
|
return "UP";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_DOWN) {
|
||||||
|
return "DOWN";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_LEFT) {
|
||||||
|
return "LEFT";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_RIGHT) {
|
||||||
|
return "RIGHT";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_STICK_L) {
|
||||||
|
return "STICK-L";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_STICK_R) {
|
||||||
|
return "STICK-R";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_PLUS) {
|
||||||
|
return "PLUS";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_MINUS) {
|
||||||
|
return "MINUS";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_TV) {
|
||||||
|
return "TV";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_RESERVED_BIT) {
|
||||||
|
return "RESERVED-BIT";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_1) {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_2) {
|
||||||
|
return "2";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_C) {
|
||||||
|
return "C";
|
||||||
|
}
|
||||||
|
if (value & WUPS_BUTTON_COMBO_BUTTON_Z) {
|
||||||
|
return "Z";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getComboAsString(const uint32_t value) {
|
||||||
|
char comboString[60] = {};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < 32; i++) {
|
||||||
|
uint32_t bitMask = 1 << i;
|
||||||
|
if (value & bitMask) {
|
||||||
|
auto val = getButtonChar(static_cast<WUPSButtonCombo_Buttons>(bitMask));
|
||||||
|
if (val[0] != '\0') {
|
||||||
|
strcat(comboString, val);
|
||||||
|
strcat(comboString, "+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string res(comboString);
|
||||||
|
if (res.ends_with("+")) {
|
||||||
|
res.pop_back();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getMaskAsString(WUPSButtonCombo_ControllerTypes controller_mask) {
|
||||||
|
if (controller_mask == WUPS_BUTTON_COMBO_CONTROLLER_NONE) {
|
||||||
|
return "NONE";
|
||||||
|
}
|
||||||
|
if (controller_mask == WUPS_BUTTON_COMBO_CONTROLLER_ALL) {
|
||||||
|
return "ALL";
|
||||||
|
}
|
||||||
|
// Optional: Check strictly for full groups if you prefer that over listing individual bits
|
||||||
|
if (controller_mask == WUPS_BUTTON_COMBO_CONTROLLER_VPAD) {
|
||||||
|
return "VPAD (All)";
|
||||||
|
}
|
||||||
|
if (controller_mask == WUPS_BUTTON_COMBO_CONTROLLER_WPAD) {
|
||||||
|
return "WPAD (All)";
|
||||||
|
}
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
|
||||||
|
// VPADs
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_VPAD_0) parts.emplace_back("VPAD_0");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_VPAD_1) parts.emplace_back("VPAD_1");
|
||||||
|
|
||||||
|
// WPADs
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_0) parts.emplace_back("WPAD_0");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_1) parts.emplace_back("WPAD_1");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_2) parts.emplace_back("WPAD_2");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_3) parts.emplace_back("WPAD_3");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_4) parts.emplace_back("WPAD_4");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_5) parts.emplace_back("WPAD_5");
|
||||||
|
if (controller_mask & WUPS_BUTTON_COMBO_CONTROLLER_WPAD_6) parts.emplace_back("WPAD_6");
|
||||||
|
|
||||||
|
|
||||||
|
if (parts.empty()) {
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (size_t i = 0; i < parts.size(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
result += " | ";
|
||||||
|
}
|
||||||
|
result += parts[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHoldCombo(WUPSButtonCombo_ComboType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_HOLD:
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_HOLD_OBSERVER:
|
||||||
|
return true;
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_INVALID:
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_PRESS_DOWN:
|
||||||
|
case WUPS_BUTTON_COMBO_COMBO_TYPE_PRESS_DOWN_OBSERVER:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
namespace ShellCommands {
|
||||||
|
namespace {
|
||||||
|
class ConsoleTable {
|
||||||
|
public:
|
||||||
|
enum Alignment { LEFT,
|
||||||
|
RIGHT };
|
||||||
|
|
||||||
|
void AddColumn(const std::string &title, const Alignment align = LEFT) {
|
||||||
|
mCols.push_back({title, align, title.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRow(const std::vector<std::string> &rowData) {
|
||||||
|
if (rowData.size() != mCols.size()) return;
|
||||||
|
mRows.push_back(rowData);
|
||||||
|
updateWidths(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddFooter(const std::vector<std::string> &footerData) {
|
||||||
|
if (footerData.size() != mCols.size()) return;
|
||||||
|
mFooter = footerData;
|
||||||
|
updateWidths(footerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Print() {
|
||||||
|
OSReport("\n");
|
||||||
|
PrintSeparator();
|
||||||
|
|
||||||
|
OSReport("|");
|
||||||
|
for (const auto &[title, align, width] : mCols) printCell(title, width, align);
|
||||||
|
OSReport("\n");
|
||||||
|
|
||||||
|
PrintSeparator();
|
||||||
|
|
||||||
|
for (const auto &row : mRows) {
|
||||||
|
OSReport("|");
|
||||||
|
for (size_t i = 0; i < row.size(); ++i) printCell(row[i], mCols[i].width, mCols[i].align);
|
||||||
|
OSReport("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFooter.empty()) {
|
||||||
|
PrintSeparator();
|
||||||
|
OSReport("|");
|
||||||
|
for (size_t i = 0; i < mFooter.size(); ++i) printCell(mFooter[i], mCols[i].width, mCols[i].align);
|
||||||
|
OSReport("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalWidth = 1;
|
||||||
|
for (const auto &col : mCols) {
|
||||||
|
totalWidth += col.width + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string border(totalWidth, '-');
|
||||||
|
OSReport("%s\n", border.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateWidths(const std::vector<std::string> &data) {
|
||||||
|
for (size_t i = 0; i < mCols.size(); ++i) {
|
||||||
|
if (data[i].length() > mCols[i].width) {
|
||||||
|
mCols[i].width = data[i].length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintSeparator() const {
|
||||||
|
OSReport("|");
|
||||||
|
for (const auto &col : mCols) {
|
||||||
|
// Separator is width + 2 spaces padding
|
||||||
|
std::string line(col.width + 2, '-');
|
||||||
|
OSReport("%s|", line.c_str());
|
||||||
|
}
|
||||||
|
OSReport("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printCell(const std::string &text, const size_t width, const Alignment align) {
|
||||||
|
if (align == LEFT) OSReport(" %-*s |", static_cast<int>(width), text.c_str());
|
||||||
|
else
|
||||||
|
OSReport(" %*s |", static_cast<int>(width), text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Column {
|
||||||
|
std::string title;
|
||||||
|
Alignment align;
|
||||||
|
size_t width;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Column> mCols;
|
||||||
|
std::vector<std::vector<std::string>> mRows;
|
||||||
|
std::vector<std::string> mFooter;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string BytesToHumanReadable(uint32_t bytes) {
|
||||||
|
char buffer[32];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%.2f KiB", bytes / 1024.0f);
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHeapUsage() {
|
||||||
|
ConsoleTable table;
|
||||||
|
|
||||||
|
table.AddColumn("Plugin name", ConsoleTable::LEFT);
|
||||||
|
table.AddColumn("Current usage", ConsoleTable::RIGHT);
|
||||||
|
table.AddColumn("Peak usage", ConsoleTable::RIGHT);
|
||||||
|
table.AddColumn("Current allocations", ConsoleTable::RIGHT);
|
||||||
|
table.AddColumn("Total allocations", ConsoleTable::RIGHT);
|
||||||
|
table.AddColumn("Total frees", ConsoleTable::RIGHT);
|
||||||
|
|
||||||
|
uint32_t totalCurrentBytes = 0;
|
||||||
|
uint32_t totalPeakBytes = 0;
|
||||||
|
uint32_t totalChunks = 0;
|
||||||
|
|
||||||
|
for (const auto &plugin : gLoadedPlugins) {
|
||||||
|
if (!plugin.isLinkedAndLoaded() || !plugin.isUsingTrackingPluginHeapMemoryAllocator()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto tracking = plugin.getTrackingMemoryAllocator();
|
||||||
|
if (!tracking) { continue; }
|
||||||
|
|
||||||
|
const auto stats = tracking->GetHeapMemoryUsageSnapshot();
|
||||||
|
if (!stats) { continue; }
|
||||||
|
|
||||||
|
auto activeChunks = stats->allocationMap.size();
|
||||||
|
|
||||||
|
table.AddRow({stats->pluginName,
|
||||||
|
BytesToHumanReadable(stats->currentAllocated),
|
||||||
|
BytesToHumanReadable(stats->peakAllocated),
|
||||||
|
std::to_string(activeChunks),
|
||||||
|
std::to_string(stats->allocCount),
|
||||||
|
std::to_string(stats->freeCount)});
|
||||||
|
|
||||||
|
// Accumulate Totals
|
||||||
|
totalCurrentBytes += stats->currentAllocated;
|
||||||
|
totalPeakBytes += stats->peakAllocated;
|
||||||
|
totalChunks += activeChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the Footer Row
|
||||||
|
table.AddFooter({"TOTAL (tracked plugins)",
|
||||||
|
BytesToHumanReadable(totalCurrentBytes),
|
||||||
|
BytesToHumanReadable(totalPeakBytes),
|
||||||
|
std::to_string(totalChunks),
|
||||||
|
"-",
|
||||||
|
"-"});
|
||||||
|
|
||||||
|
table.Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListPlugins(int argc, char **argv) {
|
||||||
|
bool showAll = false;
|
||||||
|
if (argc > 1 && (std::string_view(argv[1]) == "-a" || std::string_view(argv[1]) == "--all")) {
|
||||||
|
showAll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleTable table;
|
||||||
|
|
||||||
|
table.AddColumn("Index", ConsoleTable::LEFT);
|
||||||
|
table.AddColumn("Name", ConsoleTable::LEFT);
|
||||||
|
table.AddColumn("Author", ConsoleTable::LEFT);
|
||||||
|
table.AddColumn("Version", ConsoleTable::LEFT);
|
||||||
|
if (showAll) {
|
||||||
|
table.AddColumn("Active", ConsoleTable::LEFT);
|
||||||
|
}
|
||||||
|
table.AddColumn("API", ConsoleTable::LEFT);
|
||||||
|
table.AddColumn("Memory footprint", ConsoleTable::RIGHT);
|
||||||
|
table.AddColumn("Heap usage", ConsoleTable::RIGHT);
|
||||||
|
uint32_t totalSizeOther = 0;
|
||||||
|
uint32_t id = 0;
|
||||||
|
for (const auto &plugin : gLoadedPlugins) {
|
||||||
|
if (!showAll && !plugin.isLinkedAndLoaded()) {
|
||||||
|
totalSizeOther += plugin.getMemoryFootprint();
|
||||||
|
id++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &meta = plugin.getMetaInformation();
|
||||||
|
|
||||||
|
std::string heapUsage = "unknown";
|
||||||
|
if (const auto tracking = plugin.getTrackingMemoryAllocator()) {
|
||||||
|
uint32_t heapUsageSize = 0;
|
||||||
|
if (const auto stats = tracking->GetHeapMemoryUsageSnapshot()) {
|
||||||
|
heapUsageSize = stats->currentAllocated;
|
||||||
|
}
|
||||||
|
heapUsage = BytesToHumanReadable(heapUsageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> rowData;
|
||||||
|
rowData.emplace_back(std::to_string(id));
|
||||||
|
rowData.emplace_back(meta.getName());
|
||||||
|
rowData.emplace_back(meta.getAuthor());
|
||||||
|
rowData.emplace_back(meta.getVersion());
|
||||||
|
if (showAll) {
|
||||||
|
rowData.emplace_back(plugin.isLinkedAndLoaded() ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
rowData.emplace_back(meta.getWUPSVersion().toString());
|
||||||
|
rowData.emplace_back("~ " + BytesToHumanReadable(plugin.getMemoryFootprint()));
|
||||||
|
rowData.emplace_back(heapUsage);
|
||||||
|
|
||||||
|
table.AddRow(rowData);
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalSizeOther > 0 && !showAll) {
|
||||||
|
std::vector<std::string> rowData;
|
||||||
|
rowData.emplace_back("Inactive Plugins");
|
||||||
|
rowData.emplace_back("-");
|
||||||
|
rowData.emplace_back("-");
|
||||||
|
|
||||||
|
rowData.emplace_back("-");
|
||||||
|
rowData.emplace_back("~ " + BytesToHumanReadable(totalSizeOther));
|
||||||
|
rowData.emplace_back("-");
|
||||||
|
|
||||||
|
table.AddFooter(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginDetails(std::optional<uint32_t> idOpt) {
|
||||||
|
uint32_t id = 0;
|
||||||
|
for (const auto &plugin : gLoadedPlugins) {
|
||||||
|
if (idOpt) {
|
||||||
|
if (*idOpt != id) {
|
||||||
|
id++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto &meta = plugin.getMetaInformation();
|
||||||
|
OSReport("\n");
|
||||||
|
OSReport("Index: %d\n", id);
|
||||||
|
OSReport("Name: %s\n", meta.getName().c_str());
|
||||||
|
OSReport("Description: %s\n", meta.getDescription().c_str());
|
||||||
|
OSReport("Author: %s\n", meta.getAuthor().c_str());
|
||||||
|
OSReport("Version: %s\n", meta.getVersion().c_str());
|
||||||
|
OSReport("Build date: %s\n", meta.getBuildTimestamp().c_str());
|
||||||
|
if (!meta.getStorageId().empty()) {
|
||||||
|
OSReport("Storage Id: %s\n", meta.getStorageId().c_str());
|
||||||
|
}
|
||||||
|
OSReport("Active: %s\n", plugin.isLinkedAndLoaded() ? "Yes" : "No");
|
||||||
|
OSReport("API: %s\n", meta.getWUPSVersion().toString().c_str());
|
||||||
|
|
||||||
|
const auto memoryFootprint = plugin.getMemoryFootprint();
|
||||||
|
const auto pluginSize = plugin.getPluginDataCopy()->getBuffer().size_bytes();
|
||||||
|
size_t textSize = 0;
|
||||||
|
size_t dataSize = 0;
|
||||||
|
|
||||||
|
OSReport("Memory footprint: ~%s\n", BytesToHumanReadable(memoryFootprint).c_str());
|
||||||
|
OSReport("\t- .wps: ~%s\n", BytesToHumanReadable(pluginSize).c_str());
|
||||||
|
if (plugin.isLinkedAndLoaded()) {
|
||||||
|
textSize = plugin.getPluginLinkInformation().getTextMemory().size();
|
||||||
|
dataSize = plugin.getPluginLinkInformation().getDataMemory().size();
|
||||||
|
OSReport("\t- CODE: ~%s\n", BytesToHumanReadable(textSize).c_str());
|
||||||
|
OSReport("\t- DATA: ~%s\n", BytesToHumanReadable(dataSize).c_str());
|
||||||
|
}
|
||||||
|
const auto otherSize = memoryFootprint - pluginSize - textSize - dataSize;
|
||||||
|
OSReport("\t- Other: ~%s\n", BytesToHumanReadable(otherSize).c_str());
|
||||||
|
OSReport("\n");
|
||||||
|
if (plugin.isLinkedAndLoaded()) {
|
||||||
|
const auto §ionInfoList = plugin.getPluginLinkInformation().getSectionInfoList();
|
||||||
|
OSReport("Sections: %d\n", sectionInfoList.size());
|
||||||
|
for (const auto §ionInfo : sectionInfoList) {
|
||||||
|
OSReport("\t- 0x%08X - 0x%08X %-15s %-11s\n", sectionInfo.second.getAddress(), sectionInfo.second.getAddress() + sectionInfo.second.getSize(), sectionInfo.first.c_str(), BytesToHumanReadable(sectionInfo.second.getSize()).c_str());
|
||||||
|
}
|
||||||
|
OSReport("\n");
|
||||||
|
|
||||||
|
const auto &hookList = plugin.getPluginLinkInformation().getHookDataList();
|
||||||
|
OSReport("WUPS Hooks: %d\n", hookList.size());
|
||||||
|
for (const auto &hook : hookList) {
|
||||||
|
OSReport("\t- %p - %s\n", hook.getFunctionPointer(), hookNameToString(hook.getType()).c_str());
|
||||||
|
}
|
||||||
|
OSReport("\n");
|
||||||
|
const auto &buttonCombos = plugin.GetButtonComboData();
|
||||||
|
OSReport("Button combos: %d\n", buttonCombos.size());
|
||||||
|
for (const auto &combo : buttonCombos) {
|
||||||
|
OSReport("\t- \"%s\" \n", combo.label.c_str());
|
||||||
|
OSReport("\t\tStatus: %s\n", toString(combo.status).c_str());
|
||||||
|
OSReport("\t\tCallback: %p (%s)\n", combo.callbackOptions.callback, combo.callbackOptions.callback != nullptr ? getModuleAndSymbolName(reinterpret_cast<uint32_t>(combo.callbackOptions.callback)).c_str() : "");
|
||||||
|
OSReport("\t\tContext: %p\n", combo.callbackOptions.context);
|
||||||
|
OSReport("\t\tType: %s\n", toString(combo.buttonComboOptions.type).c_str());
|
||||||
|
if (isHoldCombo(combo.buttonComboOptions.type)) {
|
||||||
|
OSReport("\t\tHold duration: %d\n", combo.buttonComboOptions.optionalHoldForXMs);
|
||||||
|
}
|
||||||
|
OSReport("\t\tCombo: %s\n", getComboAsString(combo.buttonComboOptions.basicCombo.combo).c_str());
|
||||||
|
OSReport("\t\tController Mask: %s\n", getMaskAsString(combo.buttonComboOptions.basicCombo.controllerMask).c_str());
|
||||||
|
}
|
||||||
|
OSReport("\n");
|
||||||
|
const auto &functionPatches = plugin.getPluginLinkInformation().getFunctionDataList();
|
||||||
|
OSReport("Function patches: %d\n", functionPatches.size());
|
||||||
|
for (const auto &function : functionPatches) {
|
||||||
|
if (function.getPhysicalAddress() != nullptr) {
|
||||||
|
OSReport("\t- Hook: %p - PA: %p VA: %p\n", function.getReplaceAddress(), function.getPhysicalAddress(), function.getVirtualAddress());
|
||||||
|
} else {
|
||||||
|
OSReport("\t- Hook: %p - %-9s - %s\n", function.getReplaceAddress(), toString(function.getLibrary()).c_str(), function.getName().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OSReport("\n");
|
||||||
|
OSReport("Heap usage:\n");
|
||||||
|
if (const auto tracking = plugin.getTrackingMemoryAllocator(); tracking != nullptr) {
|
||||||
|
if (const auto stats = tracking->GetHeapMemoryUsageSnapshot(); stats) {
|
||||||
|
OSReport("\t- Currently allocated: %s\n", BytesToHumanReadable(stats->currentAllocated).c_str());
|
||||||
|
OSReport("\t- Peak allocated: %s\n", BytesToHumanReadable(stats->peakAllocated).c_str());
|
||||||
|
OSReport("\t- Current allocations: %d\n", stats->allocationMap.size());
|
||||||
|
OSReport("\t- Total allocations: %d\n", stats->allocCount);
|
||||||
|
OSReport("\t- Total frees: %d\n", stats->freeCount);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OSReport("\t Not tracked.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OSReport("\n=================\n");
|
||||||
|
|
||||||
|
id++;
|
||||||
|
if (idOpt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<IOPShellModule::CommandGroup> sPluginsGroup;
|
||||||
|
void InitPluginsCommandGroup() {
|
||||||
|
// Must be in memory while commands are active
|
||||||
|
sPluginsGroup = std::make_unique<IOPShellModule::CommandGroup>("plugins", "Manage aroma plugins");
|
||||||
|
if (const auto res = sPluginsGroup->RegisterGroup(); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to register \"aroma plugins\" command: %s", IOPShellModule::GetErrorString(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto res = sPluginsGroup->AddCommand("heap_usage", PrintHeapUsage, "Show current heap usage for tracked plugins"); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to create \"aroma plugins heap_usage\" command: %s", IOPShellModule::GetErrorString(res));
|
||||||
|
}
|
||||||
|
if (const auto res = sPluginsGroup->AddRawCommand("list", ListPlugins, "Lists active plugins", "Usage: \"list -a\" to list all plugins"); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to create \"aroma plugins list\" command: %s", IOPShellModule::GetErrorString(res));
|
||||||
|
}
|
||||||
|
if (auto res = sPluginsGroup->AddCommand("details", PluginDetails, "Shows details for plugins"); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to create \"aroma plugins details\" command: %s", IOPShellModule::GetErrorString(res));
|
||||||
|
} else {
|
||||||
|
if (res = sPluginsGroup->AddAlias("details", "show"); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to create \"aroma plugins details\" alias \"aroma plugins show\": %s", IOPShellModule::GetErrorString(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto res = sPluginsGroup->RegisterGroup(); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to register \"aroma plugins\" command: %s", IOPShellModule::GetErrorString(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init() {
|
||||||
|
InitPluginsCommandGroup();
|
||||||
|
}
|
||||||
|
} // namespace ShellCommands
|
||||||
5
source/ShellCommands.h
Normal file
5
source/ShellCommands.h
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ShellCommands {
|
||||||
|
void Init();
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "utils/buttoncombo/ButtonComboUtils.h"
|
#include "utils/buttoncombo/ButtonComboUtils.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
#include "utils/storage/StorageUtils.h"
|
#include "utils/storage/StorageUtils.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
#include <wups/button_combo/api.h>
|
#include <wups/button_combo/api.h>
|
||||||
#include <wups/button_combo_internal.h>
|
#include <wups/button_combo_internal.h>
|
||||||
|
|
@ -15,45 +16,13 @@
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
static const char **hook_names = (const char *[]){
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_MALLOC",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WUT_MALLOC",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_NEWLIB",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WUT_NEWLIB",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_STDCPP",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WUT_STDCPP",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_DEVOPTAB",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WUT_DEVOPTAB",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_SOCKETS",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WUT_SOCKETS",
|
|
||||||
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WRAPPER",
|
|
||||||
"WUPS_LOADER_HOOK_FINI_WRAPPER",
|
|
||||||
|
|
||||||
"WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED",
|
|
||||||
"WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED",
|
|
||||||
|
|
||||||
"WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED",
|
|
||||||
|
|
||||||
"WUPS_LOADER_HOOK_INIT_PLUGIN",
|
|
||||||
"WUPS_LOADER_HOOK_DEINIT_PLUGIN",
|
|
||||||
"WUPS_LOADER_HOOK_APPLICATION_STARTS",
|
|
||||||
"WUPS_LOADER_HOOK_RELEASE_FOREGROUND",
|
|
||||||
"WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND",
|
|
||||||
"WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT",
|
|
||||||
"WUPS_LOADER_HOOK_APPLICATION_ENDS",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_STORAGE",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_CONFIG",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_BUTTON_COMBO",
|
|
||||||
"WUPS_LOADER_HOOK_INIT_WUT_THREAD",
|
|
||||||
};
|
|
||||||
|
|
||||||
void CallHook(const std::vector<PluginContainer> &plugins, const wups_loader_hook_type_t hook_type) {
|
void CallHook(const std::vector<PluginContainer> &plugins, const wups_loader_hook_type_t hook_type) {
|
||||||
CallHook(plugins, hook_type, [](const auto &) { return true; });
|
CallHook(plugins, hook_type, [](const auto &) { return true; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallHook(const std::vector<PluginContainer> &plugins, const wups_loader_hook_type_t hook_type, const std::function<bool(const PluginContainer &)> &pred) {
|
void CallHook(const std::vector<PluginContainer> &plugins, const wups_loader_hook_type_t hook_type, const std::function<bool(const PluginContainer &)> &pred) {
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hook_names[hook_type], hook_type);
|
DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s [%d]", hookNameToString(hook.getType()).c_str(), hook_type);
|
||||||
for (const auto &plugin : plugins) {
|
for (const auto &plugin : plugins) {
|
||||||
if (pred(plugin)) {
|
if (pred(plugin)) {
|
||||||
CallHook(plugin, hook_type);
|
CallHook(plugin, hook_type);
|
||||||
|
|
@ -67,7 +36,7 @@ void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_
|
||||||
}
|
}
|
||||||
for (const auto &hook : plugin.getPluginLinkInformation().getHookDataList()) {
|
for (const auto &hook : plugin.getPluginLinkInformation().getHookDataList()) {
|
||||||
if (hook.getType() == hook_type) {
|
if (hook.getType() == hook_type) {
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s for plugin %s [%d]", hook_names[hook.getType()], plugin.getMetaInformation().getName().c_str(), hook_type);
|
DEBUG_FUNCTION_LINE_VERBOSE("Calling hook of type %s for plugin %s [%d]", hookNameToString(hook.getType()).c_str(), plugin.getMetaInformation().getName().c_str(), hook_type);
|
||||||
void *func_ptr = hook.getFunctionPointer();
|
void *func_ptr = hook.getFunctionPointer();
|
||||||
if (func_ptr != nullptr) {
|
if (func_ptr != nullptr) {
|
||||||
switch (hook_type) {
|
switch (hook_type) {
|
||||||
|
|
@ -172,7 +141,7 @@ void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
DEBUG_FUNCTION_LINE_ERR("######################################");
|
DEBUG_FUNCTION_LINE_ERR("######################################");
|
||||||
DEBUG_FUNCTION_LINE_ERR("Hook is not implemented %s [%d]", hook_names[hook_type], hook_type);
|
DEBUG_FUNCTION_LINE_ERR("Hook is not implemented %s [%d]", hookNameToString(hook_type).c_str(), hook_type);
|
||||||
DEBUG_FUNCTION_LINE_ERR("######################################");
|
DEBUG_FUNCTION_LINE_ERR("######################################");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "PluginManagement.h"
|
#include "PluginManagement.h"
|
||||||
|
#include "ShellCommands.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "hooks.h"
|
#include "hooks.h"
|
||||||
#include "patcher/hooks_patcher_static.h"
|
#include "patcher/hooks_patcher_static.h"
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
|
|
||||||
#include <buttoncombo/api.h>
|
#include <buttoncombo/api.h>
|
||||||
#include <function_patcher/function_patching.h>
|
#include <function_patcher/function_patching.h>
|
||||||
|
#include <iopshell/api.h>
|
||||||
#include <notifications/notification_defines.h>
|
#include <notifications/notification_defines.h>
|
||||||
#include <notifications/notifications.h>
|
#include <notifications/notifications.h>
|
||||||
|
|
||||||
|
|
@ -39,6 +41,9 @@ WUMS_DEPENDS_ON(homebrew_memorymapping);
|
||||||
WUMS_DEPENDS_ON(homebrew_notifications);
|
WUMS_DEPENDS_ON(homebrew_notifications);
|
||||||
WUMS_DEPENDS_ON(homebrew_buttoncombo);
|
WUMS_DEPENDS_ON(homebrew_buttoncombo);
|
||||||
|
|
||||||
|
// This should be a soft dependency
|
||||||
|
//WUMS_DEPENDS_ON(homebrew_iopshell);
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
WUMS_INITIALIZE() {
|
WUMS_INITIALIZE() {
|
||||||
|
|
@ -59,6 +64,12 @@ WUMS_INITIALIZE() {
|
||||||
gNotificationModuleLoaded = true;
|
gNotificationModuleLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto res = IOPShellModule::Init(); res != IOPSHELL_MODULE_ERROR_SUCCESS) {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("Failed to init IOPShellModule: %s (%d)", IOPShellModule::GetErrorString(res), res);
|
||||||
|
} else {
|
||||||
|
ShellCommands::Init();
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("Patching functions");
|
DEBUG_FUNCTION_LINE("Patching functions");
|
||||||
for (uint32_t i = 0; i < method_hooks_static_size; i++) {
|
for (uint32_t i = 0; i < method_hooks_static_size; i++) {
|
||||||
if (FunctionPatcher_AddFunctionPatch(&method_hooks_static[i], nullptr, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
|
if (FunctionPatcher_AddFunctionPatch(&method_hooks_static[i], nullptr, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
|
||||||
|
|
@ -240,26 +251,31 @@ WUMS_APPLICATION_STARTS() {
|
||||||
// Check if we want to link a plugin that's currently unloaded
|
// Check if we want to link a plugin that's currently unloaded
|
||||||
// E.g. if you disable a plugin from the config menu and then wiiload it, the disabled plugin copy should be unloaded
|
// E.g. if you disable a plugin from the config menu and then wiiload it, the disabled plugin copy should be unloaded
|
||||||
for (const auto &pluginLoadWrapper : gLoadOnNextLaunch) {
|
for (const auto &pluginLoadWrapper : gLoadOnNextLaunch) {
|
||||||
|
// If a plugin is not enabled...
|
||||||
if (!pluginLoadWrapper.isLoadAndLink()) {
|
if (!pluginLoadWrapper.isLoadAndLink()) {
|
||||||
const auto unloadedMetaInfoIt = pluginMetaInformationCache.find(pluginLoadWrapper.getPluginData()->getHandle());
|
const auto unloadedMetaInfoIt = pluginMetaInformationCache.find(pluginLoadWrapper.getPluginData()->getHandle());
|
||||||
if (unloadedMetaInfoIt == pluginMetaInformationCache.end()) {
|
if (unloadedMetaInfoIt == pluginMetaInformationCache.end()) {
|
||||||
DEBUG_FUNCTION_LINE_WARN("Failed to find meta information for plugin data handle %08X", pluginLoadWrapper.getPluginData()->getHandle());
|
DEBUG_FUNCTION_LINE_WARN("Failed to find meta information for plugin data handle %08X", pluginLoadWrapper.getPluginData()->getHandle());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (const auto it = std::ranges::find_if(gLoadOnNextLaunch, [&pluginLoadWrapper, &pluginMetaInformationCache, &unloadedMetaInfoIt](const PluginLoadWrapper &plugin) {
|
// Check if there is another plugin which will be loaded and has the same author/name
|
||||||
const bool differentPluginData = plugin.getPluginData()->getHandle() != pluginLoadWrapper.getPluginData()->getHandle();
|
const auto isPluginWithSameMetaLoaded = [&pluginLoadWrapper, &pluginMetaInformationCache, &unloadedMetaInfoIt](const PluginLoadWrapper &plugin) {
|
||||||
const bool otherWillBeLinked = plugin.isLoadAndLink();
|
const bool differentPluginData = plugin.getPluginData()->getHandle() != pluginLoadWrapper.getPluginData()->getHandle();
|
||||||
bool sameAuthorAndName = false;
|
const bool otherWillBeLinked = plugin.isLoadAndLink();
|
||||||
if (const auto otherMetaInfoIt = pluginMetaInformationCache.find(plugin.getPluginData()->getHandle()); otherMetaInfoIt != pluginMetaInformationCache.end()) {
|
bool sameAuthorAndName = false;
|
||||||
const auto &otherMetaInfo = otherMetaInfoIt->second;
|
if (const auto otherMetaInfoIt = pluginMetaInformationCache.find(plugin.getPluginData()->getHandle()); otherMetaInfoIt != pluginMetaInformationCache.end()) {
|
||||||
const auto &unloadedMetaInfo = unloadedMetaInfoIt->second;
|
const auto &otherMetaInfo = otherMetaInfoIt->second;
|
||||||
sameAuthorAndName = otherMetaInfo == unloadedMetaInfo;
|
const auto &unloadedMetaInfo = unloadedMetaInfoIt->second;
|
||||||
}
|
sameAuthorAndName = otherMetaInfo == unloadedMetaInfo;
|
||||||
return differentPluginData && otherWillBeLinked && sameAuthorAndName;
|
}
|
||||||
});
|
return differentPluginData && otherWillBeLinked && sameAuthorAndName;
|
||||||
|
};
|
||||||
|
if (const auto it = std::ranges::find_if(gLoadOnNextLaunch, isPluginWithSameMetaLoaded);
|
||||||
it != gLoadOnNextLaunch.end()) {
|
it != gLoadOnNextLaunch.end()) {
|
||||||
|
// If yes, we want to drop the currently unloaded one, so lets NOT add it to the list
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// otherwise add it to the list to keep it added.
|
||||||
}
|
}
|
||||||
filteredLoadOnNextLaunch.push_back(pluginLoadWrapper);
|
filteredLoadOnNextLaunch.push_back(pluginLoadWrapper);
|
||||||
}
|
}
|
||||||
|
|
@ -268,8 +284,11 @@ WUMS_APPLICATION_STARTS() {
|
||||||
// Check which plugins are already loaded and which needs to be
|
// Check which plugins are already loaded and which needs to be
|
||||||
for (const auto &pluginLoadWrapper : filteredLoadOnNextLaunch) {
|
for (const auto &pluginLoadWrapper : filteredLoadOnNextLaunch) {
|
||||||
const auto &pluginNeedsNoReloadFn = [&pluginLoadWrapper](const PluginContainer &container) {
|
const auto &pluginNeedsNoReloadFn = [&pluginLoadWrapper](const PluginContainer &container) {
|
||||||
|
bool willBeLinked = pluginLoadWrapper.isLoadAndLink();
|
||||||
return (container.getPluginDataCopy()->getHandle() == pluginLoadWrapper.getPluginData()->getHandle()) &&
|
return (container.getPluginDataCopy()->getHandle() == pluginLoadWrapper.getPluginData()->getHandle()) &&
|
||||||
(container.isLinkedAndLoaded() == pluginLoadWrapper.isLoadAndLink());
|
(container.isLinkedAndLoaded() == pluginLoadWrapper.isLoadAndLink()) &&
|
||||||
|
// Only check if tracking is enabled if the plugin will actually be loaded.
|
||||||
|
(!willBeLinked || (container.isUsingTrackingPluginHeapMemoryAllocator() == pluginLoadWrapper.isHeapTrackingEnabled()));
|
||||||
};
|
};
|
||||||
// Check if the plugin data is already loaded
|
// Check if the plugin data is already loaded
|
||||||
if (const auto it = std::ranges::find_if(gLoadedPlugins, pluginNeedsNoReloadFn);
|
if (const auto it = std::ranges::find_if(gLoadedPlugins, pluginNeedsNoReloadFn);
|
||||||
|
|
@ -372,6 +391,21 @@ void CleanupPlugins(std::vector<PluginContainer> &pluginsToDeinit) {
|
||||||
}
|
}
|
||||||
plugin.DeinitButtonComboData();
|
plugin.DeinitButtonComboData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for leaked memory
|
||||||
|
for (auto &plugin : pluginsToDeinit) {
|
||||||
|
if (const auto tracking = plugin.getTrackingMemoryAllocator()) {
|
||||||
|
if (const auto stats = tracking->GetHeapMemoryUsageSnapshot()) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Plugin \"%s\" leaked %d allocations", plugin.getMetaInformation().getName().c_str(), stats->allocationMap.size());
|
||||||
|
for (const auto &alloc : stats->allocationMap) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Leaked %d bytes @ %p", alloc.second.size, alloc.first);
|
||||||
|
if (!alloc.second.stackTrace.empty()) {
|
||||||
|
PrintCapturedStackTrace(alloc.second.stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins) {
|
void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins) {
|
||||||
auto *curThread = OSGetCurrentThread();
|
auto *curThread = OSGetCurrentThread();
|
||||||
|
|
|
||||||
|
|
@ -598,4 +598,28 @@ WUPSButtonCombo_Error ButtonComboManager::ExecuteForWrapper(const WUPSButtonComb
|
||||||
|
|
||||||
uint32_t ButtonComboManager::getHandle() const {
|
uint32_t ButtonComboManager::getHandle() const {
|
||||||
return *mHandle;
|
return *mHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ButtonComboInfo> ButtonComboManager::GetButtonCombos() const {
|
||||||
|
std::vector<ButtonComboInfo> result;
|
||||||
|
for (auto &wrapper : mComboWrappers) {
|
||||||
|
ButtonComboInfo res;
|
||||||
|
char text[256] = {};
|
||||||
|
WUPSButtonCombo_MetaOptionsOut meta = {text, sizeof(text)};
|
||||||
|
if (wrapper.GetButtonComboCallback(res.callbackOptions) != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (wrapper.GetButtonComboInfoEx(res.buttonComboOptions) != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (wrapper.GetButtonComboMeta(meta) != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (wrapper.GetButtonComboStatus(res.status) != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res.label = text;
|
||||||
|
result.push_back(res);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -10,12 +10,18 @@
|
||||||
|
|
||||||
class ButtonComboWrapper;
|
class ButtonComboWrapper;
|
||||||
|
|
||||||
|
struct ButtonComboInfo {
|
||||||
|
std::string label;
|
||||||
|
WUPSButtonCombo_CallbackOptions callbackOptions;
|
||||||
|
WUPSButtonCombo_ButtonComboInfoEx buttonComboOptions;
|
||||||
|
WUPSButtonCombo_ComboStatus status;
|
||||||
|
};
|
||||||
|
|
||||||
class ButtonComboManager {
|
class ButtonComboManager {
|
||||||
public:
|
public:
|
||||||
explicit ButtonComboManager();
|
explicit ButtonComboManager();
|
||||||
~ButtonComboManager();
|
~ButtonComboManager();
|
||||||
|
|
||||||
|
|
||||||
ButtonComboManager(const ButtonComboManager &) = delete;
|
ButtonComboManager(const ButtonComboManager &) = delete;
|
||||||
|
|
||||||
ButtonComboManager(ButtonComboManager &&src) noexcept;
|
ButtonComboManager(ButtonComboManager &&src) noexcept;
|
||||||
|
|
@ -67,6 +73,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] uint32_t getHandle() const;
|
[[nodiscard]] uint32_t getHandle() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<ButtonComboInfo> GetButtonCombos() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::forward_list<ButtonComboWrapper> mComboWrappers;
|
std::forward_list<ButtonComboWrapper> mComboWrappers;
|
||||||
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
||||||
|
|
|
||||||
44
source/plugin/DefaultPluginHeapMemoryAllocator.cpp
Normal file
44
source/plugin/DefaultPluginHeapMemoryAllocator.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include "DefaultPluginHeapMemoryAllocator.h"
|
||||||
|
|
||||||
|
#include "utils/logger.h"
|
||||||
|
|
||||||
|
#include <coreinit/dynload.h>
|
||||||
|
|
||||||
|
DefaultPluginHeapMemoryAllocator DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator;
|
||||||
|
|
||||||
|
DefaultPluginHeapMemoryAllocator::DefaultPluginHeapMemoryAllocator() {
|
||||||
|
OSDynLoad_Module rplHandle;
|
||||||
|
if (OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle) == OS_DYNLOAD_OK) {
|
||||||
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemory", reinterpret_cast<void **>(&mMemoryMappingModuleAllocPtr));
|
||||||
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", reinterpret_cast<void **>(&mMemoryMappingModuleAllocExPtr));
|
||||||
|
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", reinterpret_cast<void **>(&mMemoryMappingModuleFreePtr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapFn *DefaultPluginHeapMemoryAllocator::GetAllocFunctionAddress() const {
|
||||||
|
if (mMemoryMappingModuleAllocPtr == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleAllocPtr was NULL!");
|
||||||
|
return &MEMAllocFromDefaultHeap;
|
||||||
|
}
|
||||||
|
return mMemoryMappingModuleAllocPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapExFn *DefaultPluginHeapMemoryAllocator::GetAllocExFunctionAddress() const {
|
||||||
|
if (mMemoryMappingModuleAllocExPtr == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleAllocExPtr was NULL!");
|
||||||
|
return &MEMAllocFromDefaultHeapEx;
|
||||||
|
}
|
||||||
|
return mMemoryMappingModuleAllocExPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMFreeToDefaultHeapFn *DefaultPluginHeapMemoryAllocator::GetFreeFunctionAddress() const {
|
||||||
|
if (mMemoryMappingModuleFreePtr == nullptr) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleFreePtr was NULL!");
|
||||||
|
return &MEMFreeToDefaultHeap;
|
||||||
|
}
|
||||||
|
return mMemoryMappingModuleFreePtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PluginMemorySnapshot> DefaultPluginHeapMemoryAllocator::GetHeapMemoryUsageSnapshot() const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
20
source/plugin/DefaultPluginHeapMemoryAllocator.h
Normal file
20
source/plugin/DefaultPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
#include "IPluginHeapMemoryAllocator.h"
|
||||||
|
|
||||||
|
class DefaultPluginHeapMemoryAllocator : public IPluginHeapMemoryAllocator {
|
||||||
|
public:
|
||||||
|
~DefaultPluginHeapMemoryAllocator() override = default;
|
||||||
|
MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const override;
|
||||||
|
MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const override;
|
||||||
|
MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const override;
|
||||||
|
std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const override;
|
||||||
|
|
||||||
|
static DefaultPluginHeapMemoryAllocator gDefaultPluginHeapMemoryAllocator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DefaultPluginHeapMemoryAllocator();
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapFn *mMemoryMappingModuleAllocPtr = nullptr;
|
||||||
|
MEMAllocFromDefaultHeapExFn *mMemoryMappingModuleAllocExPtr = nullptr;
|
||||||
|
MEMFreeToDefaultHeapFn *mMemoryMappingModuleFreePtr = nullptr;
|
||||||
|
};
|
||||||
|
|
@ -92,3 +92,10 @@ bool FunctionData::RemovePatch() {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t FunctionData::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mName.capacity();
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ public:
|
||||||
bool RemovePatch();
|
bool RemovePatch();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
void *mPAddress = nullptr;
|
void *mPAddress = nullptr;
|
||||||
void *mVAddress = nullptr;
|
void *mVAddress = nullptr;
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
|
@ -62,4 +64,6 @@ private:
|
||||||
void *mReplaceCall = nullptr;
|
void *mReplaceCall = nullptr;
|
||||||
|
|
||||||
PatchedFunctionHandle mHandle = 0;
|
PatchedFunctionHandle mHandle = 0;
|
||||||
|
|
||||||
|
friend class PluginLinkInformation;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,10 @@ FunctionSymbolData::~FunctionSymbolData() = default;
|
||||||
|
|
||||||
[[nodiscard]] uint32_t FunctionSymbolData::getSize() const {
|
[[nodiscard]] uint32_t FunctionSymbolData::getSize() const {
|
||||||
return mSize;
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FunctionSymbolData::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
totalSize += mName.capacity();
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,11 @@ public:
|
||||||
[[nodiscard]] uint32_t getSize() const;
|
[[nodiscard]] uint32_t getSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
void *mAddress;
|
void *mAddress;
|
||||||
uint32_t mSize;
|
uint32_t mSize;
|
||||||
|
|
||||||
|
friend class PluginLinkInformation;
|
||||||
};
|
};
|
||||||
46
source/plugin/IPluginHeapMemoryAllocator.h
Normal file
46
source/plugin/IPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <coreinit/memdefaultheap.h>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct AllocationInfo {
|
||||||
|
uint32_t size;
|
||||||
|
std::vector<uint32_t> stackTrace;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PluginMemorySnapshot {
|
||||||
|
uint32_t currentAllocated = 0;
|
||||||
|
uint32_t peakAllocated = 0;
|
||||||
|
uint32_t allocCount = 0;
|
||||||
|
uint32_t freeCount = 0;
|
||||||
|
std::string pluginName = {};
|
||||||
|
|
||||||
|
std::map<void *, AllocationInfo> allocationMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPluginHeapMemoryAllocator {
|
||||||
|
public:
|
||||||
|
virtual ~IPluginHeapMemoryAllocator() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The returned address needs to be valid for the whole time
|
||||||
|
*/
|
||||||
|
virtual MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The returned address needs to be valid for the whole time
|
||||||
|
*/
|
||||||
|
virtual MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The returned address needs to be valid for the whole time
|
||||||
|
*/
|
||||||
|
virtual MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const = 0;
|
||||||
|
|
||||||
|
virtual std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const = 0;
|
||||||
|
};
|
||||||
|
|
@ -35,6 +35,14 @@ WUPSConfigAPIStatus PluginConfigData::CallMenuClosedCallback() const {
|
||||||
return WUPSCONFIG_API_RESULT_SUCCESS;
|
return WUPSCONFIG_API_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t PluginConfigData::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mName.capacity() * sizeof(char);
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<PluginConfigData> PluginConfigData::create(const WUPSConfigAPIOptions options,
|
std::optional<PluginConfigData> PluginConfigData::create(const WUPSConfigAPIOptions options,
|
||||||
const WUPSConfigAPI_MenuOpenedCallback openedCallback,
|
const WUPSConfigAPI_MenuOpenedCallback openedCallback,
|
||||||
const WUPSConfigAPI_MenuClosedCallback closedCallback) {
|
const WUPSConfigAPI_MenuClosedCallback closedCallback) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ public:
|
||||||
static std::optional<PluginConfigData> create(WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback);
|
static std::optional<PluginConfigData> create(WUPSConfigAPIOptions options, WUPSConfigAPI_MenuOpenedCallback openedCallback, WUPSConfigAPI_MenuClosedCallback closedCallback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
WUPSConfigAPI_MenuOpenedCallback mOpenedCallback;
|
WUPSConfigAPI_MenuOpenedCallback mOpenedCallback;
|
||||||
WUPSConfigAPI_MenuClosedCallback mClosedCallback;
|
WUPSConfigAPI_MenuClosedCallback mClosedCallback;
|
||||||
|
friend class PluginContainer;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include "PluginContainer.h"
|
#include "PluginContainer.h"
|
||||||
|
|
||||||
|
#include "plugin/ButtonComboManager.h"
|
||||||
#include "plugin/FunctionData.h"
|
#include "plugin/FunctionData.h"
|
||||||
#include "plugin/HookData.h"
|
#include "plugin/HookData.h"
|
||||||
#include "plugin/PluginConfigData.h"
|
#include "plugin/PluginConfigData.h"
|
||||||
|
|
@ -6,21 +8,34 @@
|
||||||
#include "plugin/PluginLinkInformation.h"
|
#include "plugin/PluginLinkInformation.h"
|
||||||
#include "plugin/RelocationData.h"
|
#include "plugin/RelocationData.h"
|
||||||
#include "plugin/SectionInfo.h"
|
#include "plugin/SectionInfo.h"
|
||||||
|
|
||||||
|
#include "plugin/ButtonComboManager.h"
|
||||||
#include "utils/buttoncombo/ButtonComboUtils.h"
|
#include "utils/buttoncombo/ButtonComboUtils.h"
|
||||||
|
#include "utils/logger.h"
|
||||||
#include "utils/storage/StorageUtils.h"
|
#include "utils/storage/StorageUtils.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
PluginContainer::PluginContainer(PluginMetaInformation metaInformation, PluginLinkInformation pluginLinkInformation, std::shared_ptr<PluginData> pluginData)
|
PluginContainer::PluginContainer(PluginMetaInformation metaInformation, PluginLinkInformation pluginLinkInformation, std::shared_ptr<PluginData> pluginData, std::optional<PluginMetaInformation::HeapTrackingOptions> heapTrackingOptions)
|
||||||
: mMetaInformation(std::move(metaInformation)),
|
: mMetaInformation(std::move(metaInformation)),
|
||||||
mPluginLinkInformation(std::move(pluginLinkInformation)),
|
mPluginLinkInformation(std::move(pluginLinkInformation)),
|
||||||
mPluginData(std::move(pluginData)) {
|
mPluginData(std::move(pluginData)) {
|
||||||
// Abuse this as a stable handle that references itself and survives std::move
|
// Abuse this as a stable handle that references itself and survives std::move
|
||||||
*mHandle = reinterpret_cast<uint32_t>(mHandle.get());
|
*mHandle = reinterpret_cast<uint32_t>(mHandle.get());
|
||||||
|
|
||||||
|
auto trackingOptions = mMetaInformation.getHeapTrackingOptions();
|
||||||
|
if (heapTrackingOptions) {
|
||||||
|
trackingOptions = *heapTrackingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const bool res = useTrackingPluginHeapMemoryAllocator(trackingOptions); !res) {
|
||||||
|
DEBUG_FUNCTION_LINE_WARN("Failed to set heap tracking options for \"%s\"", mMetaInformation.getName().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginContainer::PluginContainer(PluginContainer &&src) noexcept : mMetaInformation(std::move(src.mMetaInformation)),
|
PluginContainer::PluginContainer(PluginContainer &&src) noexcept : mMetaInformation(std::move(src.mMetaInformation)),
|
||||||
mPluginLinkInformation(std::move(src.mPluginLinkInformation)),
|
mPluginLinkInformation(std::move(src.mPluginLinkInformation)),
|
||||||
|
mTrackingHeapAllocatorOpt(std::move(src.mTrackingHeapAllocatorOpt)),
|
||||||
mPluginData(std::move(src.mPluginData)),
|
mPluginData(std::move(src.mPluginData)),
|
||||||
mHandle(std::move(src.mHandle)),
|
mHandle(std::move(src.mHandle)),
|
||||||
mPluginConfigData(std::move(src.mPluginConfigData)),
|
mPluginConfigData(std::move(src.mPluginConfigData)),
|
||||||
|
|
@ -39,6 +54,7 @@ PluginContainer &PluginContainer::operator=(PluginContainer &&src) noexcept {
|
||||||
this->mPluginData = std::move(src.mPluginData);
|
this->mPluginData = std::move(src.mPluginData);
|
||||||
this->mPluginConfigData = std::move(src.mPluginConfigData);
|
this->mPluginConfigData = std::move(src.mPluginConfigData);
|
||||||
this->mHandle = std::move(src.mHandle);
|
this->mHandle = std::move(src.mHandle);
|
||||||
|
this->mTrackingHeapAllocatorOpt = std::move(src.mTrackingHeapAllocatorOpt);
|
||||||
this->mStorageRootItem = src.mStorageRootItem;
|
this->mStorageRootItem = src.mStorageRootItem;
|
||||||
this->mInitDone = src.mInitDone;
|
this->mInitDone = src.mInitDone;
|
||||||
this->mButtonComboManagerHandle = src.mButtonComboManagerHandle;
|
this->mButtonComboManagerHandle = src.mButtonComboManagerHandle;
|
||||||
|
|
@ -126,6 +142,7 @@ void PluginContainer::InitButtonComboData() {
|
||||||
}
|
}
|
||||||
mButtonComboManagerHandle = ButtonComboUtils::API::Internal::CreateButtonComboData();
|
mButtonComboManagerHandle = ButtonComboUtils::API::Internal::CreateButtonComboData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginContainer::DeinitButtonComboData() {
|
void PluginContainer::DeinitButtonComboData() {
|
||||||
if (getMetaInformation().getWUPSVersion() < WUPSVersion(0, 8, 2)) {
|
if (getMetaInformation().getWUPSVersion() < WUPSVersion(0, 8, 2)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -133,6 +150,81 @@ void PluginContainer::DeinitButtonComboData() {
|
||||||
ButtonComboUtils::API::Internal::RemoveButtonComboData(mButtonComboManagerHandle);
|
ButtonComboUtils::API::Internal::RemoveButtonComboData(mButtonComboManagerHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<ButtonComboInfo> PluginContainer::GetButtonComboData() const {
|
||||||
|
if (getMetaInformation().getWUPSVersion() < WUPSVersion(0, 8, 2)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return ButtonComboUtils::API::Internal::GetButtonComboData(mButtonComboManagerHandle);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t PluginContainer::getButtonComboManagerHandle() const {
|
uint32_t PluginContainer::getButtonComboManagerHandle() const {
|
||||||
return mButtonComboManagerHandle;
|
return mButtonComboManagerHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginContainer::useTrackingPluginHeapMemoryAllocator(PluginMetaInformation::HeapTrackingOptions options) {
|
||||||
|
if (options != PluginMetaInformation::TRACK_HEAP_OPTIONS_NONE) {
|
||||||
|
if (!this->mTrackingHeapAllocatorOpt) {
|
||||||
|
uint32_t stackTraceDepth = 0;
|
||||||
|
if (options == PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES) {
|
||||||
|
stackTraceDepth = 8;
|
||||||
|
}
|
||||||
|
this->mTrackingHeapAllocatorOpt = TrackingPluginHeapMemoryAllocator::Create(this->mMetaInformation.getName(), stackTraceDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->mTrackingHeapAllocatorOpt.has_value();
|
||||||
|
}
|
||||||
|
this->mTrackingHeapAllocatorOpt.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginContainer::isUsingTrackingPluginHeapMemoryAllocator() const {
|
||||||
|
return mTrackingHeapAllocatorOpt.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const IPluginHeapMemoryAllocator &PluginContainer::getMemoryAllocator() const {
|
||||||
|
if (mTrackingHeapAllocatorOpt) {
|
||||||
|
return *mTrackingHeapAllocatorOpt;
|
||||||
|
}
|
||||||
|
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TrackingPluginHeapMemoryAllocator *PluginContainer::getTrackingMemoryAllocator() const {
|
||||||
|
if (mTrackingHeapAllocatorOpt) {
|
||||||
|
return &(mTrackingHeapAllocatorOpt.value());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PluginContainer::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
if (mHandle) {
|
||||||
|
totalSize += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPluginData) {
|
||||||
|
totalSize += mPluginData->getMemoryFootprint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPluginConfigData.has_value()) {
|
||||||
|
size_t configFootprint = mPluginConfigData->getMemoryFootprint();
|
||||||
|
if (configFootprint > sizeof(PluginConfigData)) {
|
||||||
|
totalSize += (configFootprint - sizeof(PluginConfigData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
size_t metaFootprint = mMetaInformation.getMemoryFootprint();
|
||||||
|
if (metaFootprint > sizeof(PluginMetaInformation)) {
|
||||||
|
totalSize += (metaFootprint - sizeof(PluginMetaInformation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
size_t linkFootprint = mPluginLinkInformation.getMemoryFootprint();
|
||||||
|
if (linkFootprint > sizeof(PluginLinkInformation)) {
|
||||||
|
totalSize += (linkFootprint - sizeof(PluginLinkInformation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -20,17 +20,20 @@
|
||||||
#include "PluginConfigData.h"
|
#include "PluginConfigData.h"
|
||||||
#include "PluginLinkInformation.h"
|
#include "PluginLinkInformation.h"
|
||||||
#include "PluginMetaInformation.h"
|
#include "PluginMetaInformation.h"
|
||||||
|
#include "TrackingPluginHeapMemoryAllocator.h"
|
||||||
|
|
||||||
#include <wups/storage.h>
|
#include <wups/storage.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
|
struct ButtonComboInfo;
|
||||||
class PluginData;
|
class PluginData;
|
||||||
|
|
||||||
class PluginContainer {
|
class PluginContainer {
|
||||||
public:
|
public:
|
||||||
PluginContainer(PluginMetaInformation metaInformation, PluginLinkInformation pluginLinkInformation, std::shared_ptr<PluginData> pluginData);
|
PluginContainer(PluginMetaInformation metaInformation, PluginLinkInformation pluginLinkInformation, std::shared_ptr<PluginData> pluginData, std::optional<PluginMetaInformation::HeapTrackingOptions> heapTrackingOptions);
|
||||||
|
|
||||||
PluginContainer(const PluginContainer &) = delete;
|
PluginContainer(const PluginContainer &) = delete;
|
||||||
|
|
||||||
|
|
@ -66,12 +69,27 @@ public:
|
||||||
|
|
||||||
void InitButtonComboData();
|
void InitButtonComboData();
|
||||||
void DeinitButtonComboData();
|
void DeinitButtonComboData();
|
||||||
|
std::vector<ButtonComboInfo> GetButtonComboData() const;
|
||||||
|
|
||||||
[[nodiscard]] uint32_t getButtonComboManagerHandle() const;
|
[[nodiscard]] uint32_t getButtonComboManagerHandle() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isUsingTrackingPluginHeapMemoryAllocator() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const IPluginHeapMemoryAllocator &getMemoryAllocator() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const TrackingPluginHeapMemoryAllocator *getTrackingMemoryAllocator() const;
|
||||||
|
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @return Returns true if setting the value was successful
|
||||||
|
*/
|
||||||
|
bool useTrackingPluginHeapMemoryAllocator(PluginMetaInformation::HeapTrackingOptions options);
|
||||||
|
|
||||||
PluginMetaInformation mMetaInformation;
|
PluginMetaInformation mMetaInformation;
|
||||||
PluginLinkInformation mPluginLinkInformation;
|
PluginLinkInformation mPluginLinkInformation;
|
||||||
|
std::optional<TrackingPluginHeapMemoryAllocator> mTrackingHeapAllocatorOpt;
|
||||||
std::shared_ptr<PluginData> mPluginData;
|
std::shared_ptr<PluginData> mPluginData;
|
||||||
|
|
||||||
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
||||||
|
|
|
||||||
|
|
@ -33,3 +33,17 @@ std::span<const uint8_t> PluginData::getBuffer() const {
|
||||||
const std::string &PluginData::getSource() const {
|
const std::string &PluginData::getSource() const {
|
||||||
return mSource;
|
return mSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t PluginData::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mBuffer.capacity() * sizeof(uint8_t);
|
||||||
|
|
||||||
|
totalSize += mSource.capacity() * sizeof(char);
|
||||||
|
|
||||||
|
if (mHandle) {
|
||||||
|
totalSize += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] const std::string &getSource() const;
|
[[nodiscard]] const std::string &getSource() const;
|
||||||
|
|
||||||
|
size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<uint8_t> mBuffer;
|
std::vector<uint8_t> mBuffer;
|
||||||
std::string mSource;
|
std::string mSource;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,11 @@ std::vector<PluginLoadWrapper> PluginDataFactory::loadDir(const std::string_view
|
||||||
if (inactivePluginsFilenames.contains(fileName)) {
|
if (inactivePluginsFilenames.contains(fileName)) {
|
||||||
shouldBeLoadedAndLinked = false;
|
shouldBeLoadedAndLinked = false;
|
||||||
}
|
}
|
||||||
result.emplace_back(std::move(pluginData), shouldBeLoadedAndLinked);
|
bool enableHeapTracking = false;
|
||||||
|
#ifdef DEBUG
|
||||||
|
enableHeapTracking = true;
|
||||||
|
#endif
|
||||||
|
result.emplace_back(std::move(pluginData), shouldBeLoadedAndLinked, enableHeapTracking);
|
||||||
} else {
|
} else {
|
||||||
auto errMsg = string_format("Failed to load plugin: %s", full_file_path.c_str());
|
auto errMsg = string_format("Failed to load plugin: %s", full_file_path.c_str());
|
||||||
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
DEBUG_FUNCTION_LINE_ERR("%s", errMsg.c_str());
|
||||||
|
|
|
||||||
|
|
@ -120,3 +120,35 @@ std::span<relocation_trampoline_entry_t> PluginLinkInformation::getTrampData() c
|
||||||
const auto &entry = mAllocatedTextAndTrampMemoryAddress[1]; // 1 is tramp data
|
const auto &entry = mAllocatedTextAndTrampMemoryAddress[1]; // 1 is tramp data
|
||||||
return std::span(static_cast<relocation_trampoline_entry_t *>(entry.data()), entry.size() / sizeof(relocation_trampoline_entry_t));
|
return std::span(static_cast<relocation_trampoline_entry_t *>(entry.data()), entry.size() / sizeof(relocation_trampoline_entry_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t PluginLinkInformation::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mHookDataList.capacity() * sizeof(HookData);
|
||||||
|
|
||||||
|
totalSize += mFunctionDataList.capacity() * sizeof(FunctionData);
|
||||||
|
for (const auto &func : mFunctionDataList) {
|
||||||
|
totalSize += (func.getMemoryFootprint() - sizeof(FunctionData));
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize += mRelocationDataList.capacity() * sizeof(RelocationData);
|
||||||
|
for (const auto &reloc : mRelocationDataList) {
|
||||||
|
totalSize += (reloc.getMemoryFootprint() - sizeof(RelocationData));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &symbol : mSymbolDataList) {
|
||||||
|
totalSize += sizeof(FunctionSymbolData);
|
||||||
|
totalSize += (symbol.getMemoryFootprint() - sizeof(FunctionSymbolData));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &[key, section] : mSectionInfoList) {
|
||||||
|
totalSize += sizeof(std::string) + sizeof(SectionInfo);
|
||||||
|
totalSize += key.capacity();
|
||||||
|
totalSize += (section.getMemoryFootprint() - sizeof(SectionInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize += (mAllocatedTextAndTrampMemoryAddress.getMemoryFootprint() - sizeof(HeapMemoryFixedSizePool));
|
||||||
|
totalSize += (mAllocatedDataMemoryAddress.getMemoryFootprint() - sizeof(HeapMemoryFixedSizePool));
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,10 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool hasValidData() const;
|
[[nodiscard]] bool hasValidData() const;
|
||||||
|
|
||||||
[[nodiscard]] int numberOfSegments() const;
|
|
||||||
|
|
||||||
[[nodiscard]] std::span<relocation_trampoline_entry_t> getTrampData() const;
|
[[nodiscard]] std::span<relocation_trampoline_entry_t> getTrampData() const;
|
||||||
|
|
||||||
|
size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginLinkInformation() = default;
|
PluginLinkInformation() = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "PluginMetaInformation.h"
|
||||||
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class PluginData;
|
class PluginData;
|
||||||
|
|
||||||
class PluginLoadWrapper {
|
class PluginLoadWrapper {
|
||||||
public:
|
public:
|
||||||
PluginLoadWrapper(std::shared_ptr<PluginData> pluginData, const bool linkAndLoad) : mPluginData(std::move(pluginData)), mIsLoadAndLink(linkAndLoad) {
|
PluginLoadWrapper(std::shared_ptr<PluginData> pluginData, const bool linkAndLoad, const bool heapTrackingEnabled = false)
|
||||||
|
: mPluginData(std::move(pluginData)), mIsLoadAndLink(linkAndLoad), mIsHeapTrackingEnabled(heapTrackingEnabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const std::shared_ptr<PluginData> &getPluginData() const {
|
[[nodiscard]] const std::shared_ptr<PluginData> &getPluginData() const {
|
||||||
|
|
@ -17,7 +21,19 @@ public:
|
||||||
return mIsLoadAndLink;
|
return mIsLoadAndLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isHeapTrackingEnabled() const {
|
||||||
|
return mIsHeapTrackingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<PluginMetaInformation::HeapTrackingOptions> getHeapTrackingOptions() const {
|
||||||
|
if (mIsHeapTrackingEnabled) {
|
||||||
|
return PluginMetaInformation::HeapTrackingOptions::TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<PluginData> mPluginData;
|
std::shared_ptr<PluginData> mPluginData;
|
||||||
bool mIsLoadAndLink = false;
|
bool mIsLoadAndLink = false;
|
||||||
|
bool mIsHeapTrackingEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
@ -38,6 +38,10 @@
|
||||||
return mSize;
|
return mSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PluginMetaInformation::HeapTrackingOptions PluginMetaInformation::getHeapTrackingOptions() const {
|
||||||
|
return mHeapTrackingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
PluginMetaInformation::PluginMetaInformation() = default;
|
PluginMetaInformation::PluginMetaInformation() = default;
|
||||||
|
|
||||||
void PluginMetaInformation::setName(std::string name) {
|
void PluginMetaInformation::setName(std::string name) {
|
||||||
|
|
@ -78,4 +82,22 @@ void PluginMetaInformation::setSize(const size_t size) {
|
||||||
|
|
||||||
void PluginMetaInformation::setStorageId(std::string storageId) {
|
void PluginMetaInformation::setStorageId(std::string storageId) {
|
||||||
mStorageId = std::move(storageId);
|
mStorageId = std::move(storageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginMetaInformation::setHeapTrackingOptions(HeapTrackingOptions value) {
|
||||||
|
mHeapTrackingOptions = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PluginMetaInformation::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mName.capacity();
|
||||||
|
totalSize += mAuthor.capacity();
|
||||||
|
totalSize += mVersion.capacity();
|
||||||
|
totalSize += mLicense.capacity();
|
||||||
|
totalSize += mBuildTimestamp.capacity();
|
||||||
|
totalSize += mDescription.capacity();
|
||||||
|
totalSize += mStorageId.capacity();
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +26,14 @@
|
||||||
class WUPSVersion;
|
class WUPSVersion;
|
||||||
|
|
||||||
class PluginMetaInformation {
|
class PluginMetaInformation {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum HeapTrackingOptions {
|
||||||
|
TRACK_HEAP_OPTIONS_NONE = 0,
|
||||||
|
TRACK_HEAP_OPTIONS_TRACK_SIZE = 1,
|
||||||
|
TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES = 2,
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] const std::string &getName() const;
|
[[nodiscard]] const std::string &getName() const;
|
||||||
|
|
||||||
[[nodiscard]] const std::string &getAuthor() const;
|
[[nodiscard]] const std::string &getAuthor() const;
|
||||||
|
|
@ -45,6 +52,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] size_t getSize() const;
|
[[nodiscard]] size_t getSize() const;
|
||||||
|
|
||||||
|
[[nodiscard]] HeapTrackingOptions getHeapTrackingOptions() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginMetaInformation();
|
PluginMetaInformation();
|
||||||
|
|
||||||
|
|
@ -68,6 +77,10 @@ private:
|
||||||
|
|
||||||
void setStorageId(std::string storageId);
|
void setStorageId(std::string storageId);
|
||||||
|
|
||||||
|
void setHeapTrackingOptions(HeapTrackingOptions value);
|
||||||
|
|
||||||
|
size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
std::string mAuthor;
|
std::string mAuthor;
|
||||||
std::string mVersion;
|
std::string mVersion;
|
||||||
|
|
@ -75,8 +88,9 @@ private:
|
||||||
std::string mBuildTimestamp;
|
std::string mBuildTimestamp;
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
std::string mStorageId;
|
std::string mStorageId;
|
||||||
size_t mSize = {};
|
size_t mSize = {};
|
||||||
WUPSVersion mWUPSVersion = WUPSVersion(0, 0, 0);
|
WUPSVersion mWUPSVersion = WUPSVersion(0, 0, 0);
|
||||||
|
HeapTrackingOptions mHeapTrackingOptions = TRACK_HEAP_OPTIONS_NONE;
|
||||||
|
|
||||||
friend class PluginMetaInformationFactory;
|
friend class PluginMetaInformationFactory;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,12 @@ std::optional<PluginMetaInformation> PluginMetaInformationFactory::loadPlugin(st
|
||||||
pluginInfo.setDescription(value);
|
pluginInfo.setDescription(value);
|
||||||
} else if (key == "storage_id") {
|
} else if (key == "storage_id") {
|
||||||
pluginInfo.setStorageId(value);
|
pluginInfo.setStorageId(value);
|
||||||
|
} else if (key == "debug") {
|
||||||
|
if (value == "track_heap") {
|
||||||
|
pluginInfo.setHeapTrackingOptions(PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE);
|
||||||
|
} else if (value == "track_heap_with_stack_trace") {
|
||||||
|
pluginInfo.setHeapTrackingOptions(PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES);
|
||||||
|
}
|
||||||
} else if (key == "wups") {
|
} else if (key == "wups") {
|
||||||
if (value == "0.7.1") {
|
if (value == "0.7.1") {
|
||||||
pluginInfo.setWUPSVersion(0, 7, 1);
|
pluginInfo.setWUPSVersion(0, 7, 1);
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,12 @@ RelocationData::~RelocationData() = default;
|
||||||
|
|
||||||
[[nodiscard]] const ImportRPLInformation &RelocationData::getImportRPLInformation() const {
|
[[nodiscard]] const ImportRPLInformation &RelocationData::getImportRPLInformation() const {
|
||||||
return *mRPLInfo;
|
return *mRPLInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RelocationData::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
totalSize += mName.capacity();
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -46,10 +46,13 @@ public:
|
||||||
[[nodiscard]] const ImportRPLInformation &getImportRPLInformation() const;
|
[[nodiscard]] const ImportRPLInformation &getImportRPLInformation() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
char mType;
|
char mType;
|
||||||
size_t mOffset;
|
size_t mOffset;
|
||||||
int32_t mAddend;
|
int32_t mAddend;
|
||||||
void *mDestination;
|
void *mDestination;
|
||||||
std::string mName;
|
std::string mName;
|
||||||
std::shared_ptr<ImportRPLInformation> mRPLInfo;
|
std::shared_ptr<ImportRPLInformation> mRPLInfo;
|
||||||
|
friend class PluginLinkInformation;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,10 @@ SectionInfo::SectionInfo(std::string name,
|
||||||
|
|
||||||
[[nodiscard]] uint32_t SectionInfo::isInSection(uint32_t addr) const {
|
[[nodiscard]] uint32_t SectionInfo::isInSection(uint32_t addr) const {
|
||||||
return addr >= mAddress && addr < mAddress + mSectionSize;
|
return addr >= mAddress && addr < mAddress + mSectionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SectionInfo::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
totalSize += mName.capacity();
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,11 @@ public:
|
||||||
[[nodiscard]] uint32_t isInSection(uint32_t addr) const;
|
[[nodiscard]] uint32_t isInSection(uint32_t addr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
uint32_t mAddress = {};
|
uint32_t mAddress = {};
|
||||||
uint32_t mSectionSize = {};
|
uint32_t mSectionSize = {};
|
||||||
|
|
||||||
|
friend class PluginLinkInformation;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
177
source/plugin/TrackingPluginHeapMemoryAllocator.cpp
Normal file
177
source/plugin/TrackingPluginHeapMemoryAllocator.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
#include "TrackingPluginHeapMemoryAllocator.h"
|
||||||
|
|
||||||
|
#include "utils/logger.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct PluginMemoryStatsInternal {
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
bool inUse = false;
|
||||||
|
uint32_t stackTraceDepth = 0;
|
||||||
|
PluginMemorySnapshot data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate 64 different thunk functions so we can track 64 plugins at a time which should be more than enough
|
||||||
|
constexpr int MAX_SLOTS = 64;
|
||||||
|
template<int N>
|
||||||
|
struct MemoryThunk {
|
||||||
|
inline static PluginMemoryStatsInternal stats;
|
||||||
|
|
||||||
|
static void *Alloc(uint32_t size) {
|
||||||
|
const auto ptr = (*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocFunctionAddress())(size);
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
std::lock_guard lock(stats.mutex);
|
||||||
|
if (stats.inUse) {
|
||||||
|
stats.data.allocCount++;
|
||||||
|
stats.data.currentAllocated += size;
|
||||||
|
if (stats.data.currentAllocated > stats.data.peakAllocated) {
|
||||||
|
stats.data.peakAllocated = stats.data.currentAllocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.data.allocationMap[ptr] = {size, stats.stackTraceDepth > 0 ? CaptureStackTrace(stats.stackTraceDepth) : std::vector<uint32_t>()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *AllocEx(uint32_t size, int align) {
|
||||||
|
const auto ptr = (*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocExFunctionAddress())(size, align);
|
||||||
|
if (ptr) {
|
||||||
|
std::lock_guard lock(stats.mutex);
|
||||||
|
if (stats.inUse) {
|
||||||
|
stats.data.allocCount++;
|
||||||
|
stats.data.currentAllocated += size;
|
||||||
|
if (stats.data.currentAllocated > stats.data.peakAllocated) {
|
||||||
|
stats.data.peakAllocated = stats.data.currentAllocated;
|
||||||
|
}
|
||||||
|
stats.data.allocationMap[ptr] = {size, stats.stackTraceDepth > 0 ? CaptureStackTrace(stats.stackTraceDepth) : std::vector<uint32_t>()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Free(void *ptr) {
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
|
std::lock_guard lock(stats.mutex);
|
||||||
|
if (stats.inUse) {
|
||||||
|
if (const auto it = stats.data.allocationMap.find(ptr); it != stats.data.allocationMap.end()) {
|
||||||
|
uint32_t size = it->second.size;
|
||||||
|
if (stats.data.currentAllocated >= size) {
|
||||||
|
stats.data.currentAllocated -= size;
|
||||||
|
} else {
|
||||||
|
stats.data.currentAllocated = 0;
|
||||||
|
}
|
||||||
|
stats.data.allocationMap.erase(it);
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE_ERR("free() of for unknown ptr detected (double free?). \"%s\": %p", stats.data.pluginName.c_str(), ptr);
|
||||||
|
auto stackTrace = CaptureStackTrace(16);
|
||||||
|
PrintCapturedStackTrace(stackTrace);
|
||||||
|
}
|
||||||
|
stats.data.freeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetFreeFunctionAddress())(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int... Is>
|
||||||
|
constexpr std::array<MEMAllocFromDefaultHeapFn, sizeof...(Is)> CreateAllocTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::Alloc...}; }
|
||||||
|
template<int... Is>
|
||||||
|
constexpr std::array<MEMAllocFromDefaultHeapExFn, sizeof...(Is)> CreateAllocExTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::AllocEx...}; }
|
||||||
|
template<int... Is>
|
||||||
|
constexpr std::array<MEMFreeToDefaultHeapFn, sizeof...(Is)> CreateFreeTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::Free...}; }
|
||||||
|
|
||||||
|
template<int... Is>
|
||||||
|
constexpr std::array<PluginMemoryStatsInternal *, sizeof...(Is)> CreateStatsTable(std::integer_sequence<int, Is...>) {
|
||||||
|
return {&MemoryThunk<Is>::stats...};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sAllocThunks = CreateAllocTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||||
|
auto sAllocExThunks = CreateAllocExTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||||
|
auto sFreeThunks = CreateFreeTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||||
|
auto sStatsTable = CreateStatsTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator::TrackingPluginHeapMemoryAllocator(const int32_t index) : sIndex(index) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TrackingPluginHeapMemoryAllocator> TrackingPluginHeapMemoryAllocator::Create(const std::string_view pluginName, uint32_t stackTraceDepth) {
|
||||||
|
int32_t index = 0;
|
||||||
|
for (auto *stat : sStatsTable) {
|
||||||
|
std::lock_guard lock(stat->mutex);
|
||||||
|
if (!stat->inUse) {
|
||||||
|
stat->inUse = true;
|
||||||
|
stat->stackTraceDepth = stackTraceDepth;
|
||||||
|
stat->data = {};
|
||||||
|
stat->data.pluginName = pluginName;
|
||||||
|
return TrackingPluginHeapMemoryAllocator{index};
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator::~TrackingPluginHeapMemoryAllocator() {
|
||||||
|
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *stats = sStatsTable[sIndex];
|
||||||
|
std::lock_guard lock(stats->mutex);
|
||||||
|
stats->inUse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator::TrackingPluginHeapMemoryAllocator(TrackingPluginHeapMemoryAllocator &&src) noexcept {
|
||||||
|
this->sIndex = src.sIndex;
|
||||||
|
src.sIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator &TrackingPluginHeapMemoryAllocator::operator=(TrackingPluginHeapMemoryAllocator &&src) noexcept {
|
||||||
|
if (this != &src) {
|
||||||
|
// Release current slot if we have one
|
||||||
|
if (this->sIndex >= 0 && this->sIndex < MAX_SLOTS) {
|
||||||
|
auto *stats = sStatsTable[this->sIndex];
|
||||||
|
std::lock_guard lock(stats->mutex);
|
||||||
|
stats->inUse = false;
|
||||||
|
stats->data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sIndex = src.sIndex;
|
||||||
|
src.sIndex = -1;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapFn *TrackingPluginHeapMemoryAllocator::GetAllocFunctionAddress() const {
|
||||||
|
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||||
|
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocFunctionAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sAllocThunks[sIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapExFn *TrackingPluginHeapMemoryAllocator::GetAllocExFunctionAddress() const {
|
||||||
|
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||||
|
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocExFunctionAddress();
|
||||||
|
}
|
||||||
|
return &sAllocExThunks[sIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
MEMFreeToDefaultHeapFn *TrackingPluginHeapMemoryAllocator::GetFreeFunctionAddress() const {
|
||||||
|
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||||
|
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetFreeFunctionAddress();
|
||||||
|
}
|
||||||
|
return &sFreeThunks[sIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PluginMemorySnapshot> TrackingPluginHeapMemoryAllocator::GetHeapMemoryUsageSnapshot() const {
|
||||||
|
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto *stats = sStatsTable[sIndex];
|
||||||
|
std::lock_guard lock(stats->mutex);
|
||||||
|
return stats->data;
|
||||||
|
}
|
||||||
29
source/plugin/TrackingPluginHeapMemoryAllocator.h
Normal file
29
source/plugin/TrackingPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
#include "DefaultPluginHeapMemoryAllocator.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class TrackingPluginHeapMemoryAllocator : public IPluginHeapMemoryAllocator {
|
||||||
|
public:
|
||||||
|
static std::optional<TrackingPluginHeapMemoryAllocator> Create(std::string_view pluginName, uint32_t stackTraceDepth);
|
||||||
|
|
||||||
|
~TrackingPluginHeapMemoryAllocator() override;
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator(const TrackingPluginHeapMemoryAllocator &) = delete;
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator(TrackingPluginHeapMemoryAllocator &&src) noexcept;
|
||||||
|
|
||||||
|
TrackingPluginHeapMemoryAllocator &operator=(TrackingPluginHeapMemoryAllocator &&src) noexcept;
|
||||||
|
|
||||||
|
MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const override;
|
||||||
|
MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const override;
|
||||||
|
MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const override;
|
||||||
|
std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int sIndex = -1;
|
||||||
|
TrackingPluginHeapMemoryAllocator(int index);
|
||||||
|
};
|
||||||
|
|
@ -64,6 +64,19 @@ HeapMemoryFixedSizePool::operator bool() const {
|
||||||
HeapMemoryFixedSizePool::MemorySegmentInfo HeapMemoryFixedSizePool::operator[](const int idx) const {
|
HeapMemoryFixedSizePool::MemorySegmentInfo HeapMemoryFixedSizePool::operator[](const int idx) const {
|
||||||
if (idx < 0 || idx >= static_cast<int>(mSegmentInfos.size())) {
|
if (idx < 0 || idx >= static_cast<int>(mSegmentInfos.size())) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Out of bounce access (tried to access index %d; size is %d", idx, mSegmentInfos.size());
|
DEBUG_FUNCTION_LINE_ERR("Out of bounce access (tried to access index %d; size is %d", idx, mSegmentInfos.size());
|
||||||
|
return {nullptr, 0};
|
||||||
}
|
}
|
||||||
return mSegmentInfos[idx];
|
return mSegmentInfos[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HeapMemoryFixedSizePool::getMemoryFootprint() const {
|
||||||
|
size_t totalSize = sizeof(*this);
|
||||||
|
|
||||||
|
if (mData) {
|
||||||
|
totalSize += mTotalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize += mSegmentInfos.capacity() * sizeof(MemorySegmentInfo);
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +39,11 @@ public:
|
||||||
MemorySegmentInfo operator[](int idx) const;
|
MemorySegmentInfo operator[](int idx) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] size_t getMemoryFootprint() const;
|
||||||
|
|
||||||
std::unique_ptr<uint8_t[]> mData{};
|
std::unique_ptr<uint8_t[]> mData{};
|
||||||
std::size_t mTotalSize{};
|
std::size_t mTotalSize{};
|
||||||
std::vector<MemorySegmentInfo> mSegmentInfos;
|
std::vector<MemorySegmentInfo> mSegmentInfos;
|
||||||
|
|
||||||
|
friend class PluginLinkInformation;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ namespace ButtonComboUtils::API {
|
||||||
DEBUG_FUNCTION_LINE_WARN("Tried to remove ButtonComboManager by invalid handle: %08X", buttonComboManagerHandle);
|
DEBUG_FUNCTION_LINE_WARN("Tried to remove ButtonComboManager by invalid handle: %08X", buttonComboManagerHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ButtonComboInfo> GetButtonComboData(uint32_t buttonComboManagerHandle) {
|
||||||
|
std::lock_guard lock(sButtonComboMutex);
|
||||||
|
for (auto &manager : sButtonComboManager) {
|
||||||
|
if (manager.getHandle() == buttonComboManagerHandle) {
|
||||||
|
return manager.GetButtonCombos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,15 @@
|
||||||
#include <wups/button_combo/defines.h>
|
#include <wups/button_combo/defines.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct ButtonComboInfo;
|
||||||
|
|
||||||
namespace ButtonComboUtils::API {
|
namespace ButtonComboUtils::API {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
uint32_t CreateButtonComboData();
|
uint32_t CreateButtonComboData();
|
||||||
void RemoveButtonComboData(uint32_t buttonComboManagerHandle);
|
void RemoveButtonComboData(uint32_t buttonComboManagerHandle);
|
||||||
|
std::vector<ButtonComboInfo> GetButtonComboData(uint32_t buttonComboManagerHandle);
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|
||||||
WUPSButtonCombo_Error AddButtonCombo(void *identifier,
|
WUPSButtonCombo_Error AddButtonCombo(void *identifier,
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@
|
||||||
|
|
||||||
#include "config/WUPSConfig.h"
|
#include "config/WUPSConfig.h"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
ConfigDisplayItem::ConfigDisplayItem(GeneralConfigInformation &info,
|
ConfigDisplayItem::ConfigDisplayItem(GeneralConfigInformation &info,
|
||||||
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config,
|
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config,
|
||||||
const bool isActive) : mConfig(std::move(config)),
|
const bool isActive,
|
||||||
mInfo(std::move(info)),
|
const bool isHeapTracking) : mConfig(std::move(config)),
|
||||||
mIsActivePlugin(isActive),
|
mInfo(std::move(info)),
|
||||||
mInitialIsActivePlugin(isActive) {
|
mIsActivePlugin(isActive),
|
||||||
|
mInitialIsActivePlugin(isActive),
|
||||||
|
mIsHeapTrackingEnabled(isHeapTracking),
|
||||||
|
mInitialIsHeapTrackingEnabled(isHeapTracking) {
|
||||||
assert(mConfig);
|
assert(mConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,4 +35,16 @@ void ConfigDisplayItem::toggleIsActivePlugin() {
|
||||||
|
|
||||||
void ConfigDisplayItem::resetIsActivePlugin() {
|
void ConfigDisplayItem::resetIsActivePlugin() {
|
||||||
mIsActivePlugin = mInitialIsActivePlugin;
|
mIsActivePlugin = mInitialIsActivePlugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigDisplayItem::isHeapTrackingEnabled() const {
|
||||||
|
return mIsHeapTrackingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDisplayItem::toggleIsHeapTrackingEnabled() {
|
||||||
|
mIsHeapTrackingEnabled = !mIsHeapTrackingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigDisplayItem::resetIsHeapTrackingEnabled() {
|
||||||
|
mIsHeapTrackingEnabled = mInitialIsHeapTrackingEnabled;
|
||||||
}
|
}
|
||||||
|
|
@ -15,21 +15,25 @@ struct GeneralConfigInformation {
|
||||||
|
|
||||||
class ConfigDisplayItem {
|
class ConfigDisplayItem {
|
||||||
public:
|
public:
|
||||||
ConfigDisplayItem(GeneralConfigInformation &info, std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config, bool isActive);
|
ConfigDisplayItem(GeneralConfigInformation &info, std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> config, bool isActive, bool isHeapTracking = false);
|
||||||
|
|
||||||
[[nodiscard]] const GeneralConfigInformation &getConfigInformation() const;
|
[[nodiscard]] const GeneralConfigInformation &getConfigInformation() const;
|
||||||
|
|
||||||
[[nodiscard]] const WUPSConfigAPIBackend::WUPSConfig &getConfig() const;
|
[[nodiscard]] const WUPSConfigAPIBackend::WUPSConfig &getConfig() const;
|
||||||
|
|
||||||
[[nodiscard]] bool isActivePlugin() const;
|
[[nodiscard]] bool isActivePlugin() const;
|
||||||
|
|
||||||
void toggleIsActivePlugin();
|
void toggleIsActivePlugin();
|
||||||
|
|
||||||
void resetIsActivePlugin();
|
void resetIsActivePlugin();
|
||||||
|
|
||||||
|
[[nodiscard]] bool isHeapTrackingEnabled() const;
|
||||||
|
void toggleIsHeapTrackingEnabled();
|
||||||
|
void resetIsHeapTrackingEnabled();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> mConfig;
|
std::unique_ptr<WUPSConfigAPIBackend::WUPSConfig> mConfig;
|
||||||
GeneralConfigInformation mInfo;
|
GeneralConfigInformation mInfo;
|
||||||
bool mIsActivePlugin;
|
bool mIsActivePlugin;
|
||||||
bool mInitialIsActivePlugin;
|
bool mInitialIsActivePlugin;
|
||||||
|
bool mIsHeapTrackingEnabled;
|
||||||
|
bool mInitialIsHeapTrackingEnabled;
|
||||||
};
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ConfigRenderer.h"
|
#include "ConfigRenderer.h"
|
||||||
|
#include "ConfigRendererStates.h"
|
||||||
|
|
||||||
#include "CategoryRenderer.h"
|
#include "CategoryRenderer.h"
|
||||||
#include "ConfigDisplayItem.h"
|
#include "ConfigDisplayItem.h"
|
||||||
|
|
@ -11,15 +12,11 @@
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
ConfigRenderer::ConfigRenderer(std::vector<ConfigDisplayItem> &&vec) : mConfigs(std::move(vec)) {
|
ConfigRenderer::ConfigRenderer(std::vector<ConfigDisplayItem> &&vec) : mConfigs(std::move(vec)) {
|
||||||
std::ranges::copy(mConfigs,
|
SetListState(std::make_unique<DefaultListState>());
|
||||||
std::back_inserter(mAllConfigs));
|
|
||||||
std::ranges::copy_if(mConfigs,
|
|
||||||
std::back_inserter(mActiveConfigs),
|
|
||||||
[&](const auto &value) {
|
|
||||||
return value.isActivePlugin();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigRenderer::~ConfigRenderer() = default;
|
ConfigRenderer::~ConfigRenderer() = default;
|
||||||
|
|
@ -44,6 +41,10 @@ ConfigSubState ConfigRenderer::Update(Input &input, const WUPSConfigSimplePadDat
|
||||||
if (complexInputData.vpad.vpadError == VPAD_READ_SUCCESS && complexInputData.vpad.data.hold != 0) {
|
if (complexInputData.vpad.vpadError == VPAD_READ_SUCCESS && complexInputData.vpad.data.hold != 0) {
|
||||||
mLastInputWasOnWiimote = false;
|
mLastInputWasOnWiimote = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset transient return state
|
||||||
|
mNextSubState = SUB_STATE_RUNNING;
|
||||||
|
|
||||||
switch (mState) {
|
switch (mState) {
|
||||||
case STATE_MAIN:
|
case STATE_MAIN:
|
||||||
return UpdateStateMain(input);
|
return UpdateStateMain(input);
|
||||||
|
|
@ -51,9 +52,9 @@ ConfigSubState ConfigRenderer::Update(Input &input, const WUPSConfigSimplePadDat
|
||||||
if (mCategoryRenderer) {
|
if (mCategoryRenderer) {
|
||||||
auto subResult = mCategoryRenderer->Update(input, simpleInputData, complexInputData);
|
auto subResult = mCategoryRenderer->Update(input, simpleInputData, complexInputData);
|
||||||
if (subResult != SUB_STATE_RUNNING) {
|
if (subResult != SUB_STATE_RUNNING) {
|
||||||
mNeedRedraw = true;
|
mNeedRedraw = true;
|
||||||
mActivePluginsDirty = false;
|
mPluginListDirty = false;
|
||||||
mState = STATE_MAIN;
|
mState = STATE_MAIN;
|
||||||
return SUB_STATE_RUNNING;
|
return SUB_STATE_RUNNING;
|
||||||
}
|
}
|
||||||
return SUB_STATE_RUNNING;
|
return SUB_STATE_RUNNING;
|
||||||
|
|
@ -99,90 +100,50 @@ void ConfigRenderer::ResetNeedsRedraw() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::RequestRedraw() {
|
||||||
|
mNeedRedraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
|
ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
|
||||||
auto &configs = GetConfigList();
|
if (!mListState) return SUB_STATE_ERROR;
|
||||||
|
|
||||||
|
auto &configs = GetDisplayedConfigList();
|
||||||
const auto prevSelectedItem = mCursorPos;
|
const auto prevSelectedItem = mCursorPos;
|
||||||
|
auto totalElementSize = (int32_t) configs.size();
|
||||||
|
|
||||||
const auto &savePendingConfigFn = [&configs, this]() {
|
// Delegate specific inputs to the State
|
||||||
for (const auto &element : configs) {
|
bool inputHandled = mListState->HandleInput(*this, input);
|
||||||
CallOnCloseCallback(element.get().getConfigInformation(), element.get().getConfig());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto totalElementSize = (int32_t) configs.size();
|
if (mNextSubState != SUB_STATE_RUNNING) {
|
||||||
|
return mNextSubState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputHandled) {
|
||||||
|
return SUB_STATE_RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Navigation (Common to all states)
|
||||||
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
|
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
|
||||||
mCursorPos++;
|
mCursorPos++;
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_LEFT) {
|
} else if (input.data.buttons_d & Input::eButtons::BUTTON_LEFT) {
|
||||||
// Paging up
|
|
||||||
mCursorPos -= MAX_BUTTONS_ON_SCREEN - 1;
|
mCursorPos -= MAX_BUTTONS_ON_SCREEN - 1;
|
||||||
// Don't jump past the top
|
if (mCursorPos < 0) mCursorPos = 0;
|
||||||
if (mCursorPos < 0)
|
|
||||||
mCursorPos = 0;
|
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_RIGHT) {
|
} else if (input.data.buttons_d & Input::eButtons::BUTTON_RIGHT) {
|
||||||
// Paging down
|
|
||||||
mCursorPos += MAX_BUTTONS_ON_SCREEN - 1;
|
mCursorPos += MAX_BUTTONS_ON_SCREEN - 1;
|
||||||
// Don't jump past the bottom
|
if (mCursorPos >= totalElementSize) mCursorPos = totalElementSize - 1;
|
||||||
if (mCursorPos >= totalElementSize)
|
|
||||||
mCursorPos = totalElementSize - 1;
|
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
|
} else if (input.data.buttons_d & Input::eButtons::BUTTON_UP) {
|
||||||
mCursorPos--;
|
mCursorPos--;
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_PLUS) {
|
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
mNeedRedraw = true;
|
|
||||||
mCategoryRenderer.reset();
|
|
||||||
savePendingConfigFn();
|
|
||||||
return SUB_STATE_RETURN_WITH_PLUGIN_RELOAD;
|
|
||||||
}
|
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_X) {
|
|
||||||
if (!mSetActivePluginsMode && !mAllConfigs.empty()) {
|
|
||||||
mSetActivePluginsMode = true;
|
|
||||||
mNeedRedraw = true;
|
|
||||||
return SUB_STATE_RUNNING;
|
|
||||||
}
|
|
||||||
} else if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
|
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
mActivePluginsDirty = true;
|
|
||||||
mNeedRedraw = true;
|
|
||||||
configs[mCursorPos].get().toggleIsActivePlugin();
|
|
||||||
return SUB_STATE_RUNNING;
|
|
||||||
} else if (!configs.empty()) {
|
|
||||||
if (mCursorPos != mCurrentOpen) {
|
|
||||||
mCategoryRenderer.reset();
|
|
||||||
mCategoryRenderer = make_unique_nothrow<CategoryRenderer>(&(configs[mCursorPos].get().getConfigInformation()), &(configs[mCursorPos].get().getConfig()), true);
|
|
||||||
}
|
|
||||||
mNeedRedraw = true;
|
|
||||||
mCurrentOpen = mCursorPos;
|
|
||||||
mState = STATE_SUB;
|
|
||||||
return SUB_STATE_RUNNING;
|
|
||||||
}
|
|
||||||
} else if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
|
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
for (auto &cur : mConfigs) {
|
|
||||||
cur.resetIsActivePlugin();
|
|
||||||
}
|
|
||||||
mActivePluginsDirty = false;
|
|
||||||
mNeedRedraw = true;
|
|
||||||
mSetActivePluginsMode = false;
|
|
||||||
return SUB_STATE_RUNNING;
|
|
||||||
} else {
|
|
||||||
mNeedRedraw = true;
|
|
||||||
mCategoryRenderer.reset();
|
|
||||||
savePendingConfigFn();
|
|
||||||
return SUB_STATE_RETURN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCursorPos < 0) {
|
if (totalElementSize > 0) {
|
||||||
mCursorPos = totalElementSize - 1;
|
if (mCursorPos < 0) mCursorPos = totalElementSize - 1;
|
||||||
} else if (mCursorPos >= totalElementSize) {
|
else if (mCursorPos >= totalElementSize)
|
||||||
mCursorPos = 0;
|
mCursorPos = 0;
|
||||||
}
|
} else {
|
||||||
if (mCursorPos < 0) {
|
|
||||||
mCursorPos = 0;
|
mCursorPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the render offset when reaching the boundaries
|
// Adjust render offset
|
||||||
if (mCursorPos < mRenderOffset) {
|
if (mCursorPos < mRenderOffset) {
|
||||||
mRenderOffset = mCursorPos;
|
mRenderOffset = mCursorPos;
|
||||||
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
|
} else if (mCursorPos >= mRenderOffset + MAX_BUTTONS_ON_SCREEN - 1) {
|
||||||
|
|
@ -197,71 +158,70 @@ ConfigSubState ConfigRenderer::UpdateStateMain(const Input &input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigRenderer::RenderStateMain() const {
|
void ConfigRenderer::RenderStateMain() const {
|
||||||
auto &configs = GetConfigList();
|
auto &configs = GetDisplayedConfigList();
|
||||||
|
|
||||||
DrawUtils::beginDraw();
|
DrawUtils::beginDraw();
|
||||||
DrawUtils::clear(COLOR_BACKGROUND);
|
DrawUtils::clear(COLOR_BACKGROUND);
|
||||||
|
|
||||||
auto totalElementSize = (int32_t) configs.size();
|
auto totalElementSize = (int32_t) configs.size();
|
||||||
|
int start = std::max(0, mRenderOffset);
|
||||||
|
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
|
||||||
|
|
||||||
// Calculate the range of items to display
|
if (configs.empty()) {
|
||||||
int start = std::max(0, mRenderOffset);
|
|
||||||
int end = std::min(start + MAX_BUTTONS_ON_SCREEN, totalElementSize);
|
|
||||||
|
|
||||||
if (mActiveConfigs.empty() && !mSetActivePluginsMode) {
|
|
||||||
DrawUtils::setFontSize(24);
|
DrawUtils::setFontSize(24);
|
||||||
std::string noConfigText = "No active plugins";
|
std::string noConfigText = "No plugins available";
|
||||||
uint32_t szNoConfig = DrawUtils::getTextWidth(noConfigText.data());
|
|
||||||
|
|
||||||
if (!mAllConfigs.empty()) {
|
if (!mListState->IsMainView()) {
|
||||||
|
noConfigText = "No active plugins";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t szNoConfig = DrawUtils::getTextWidth(noConfigText.data());
|
||||||
|
|
||||||
|
if (mListState->IsMainView()) {
|
||||||
|
DrawUtils::print((SCREEN_WIDTH / 2) - (szNoConfig / 2), (SCREEN_HEIGHT / 2), noConfigText.data());
|
||||||
|
} else {
|
||||||
const auto activateHint = string_format("Press %s to activate inactive plugins", mLastInputWasOnWiimote ? "\uE048" : "\uE002");
|
const auto activateHint = string_format("Press %s to activate inactive plugins", mLastInputWasOnWiimote ? "\uE048" : "\uE002");
|
||||||
const auto szHint = DrawUtils::getTextWidth(activateHint.c_str());
|
const auto szHint = DrawUtils::getTextWidth(activateHint.c_str());
|
||||||
|
|
||||||
DrawUtils::print((SCREEN_WIDTH / 2) - (szNoConfig / 2), (SCREEN_HEIGHT / 2) - 16, noConfigText.data());
|
DrawUtils::print((SCREEN_WIDTH / 2) - (szNoConfig / 2), (SCREEN_HEIGHT / 2) - 16, noConfigText.data());
|
||||||
DrawUtils::print((SCREEN_WIDTH / 2) - (szHint / 2), (SCREEN_HEIGHT / 2) + 16, activateHint.data());
|
DrawUtils::print((SCREEN_WIDTH / 2) - (szHint / 2), (SCREEN_HEIGHT / 2) + 16, activateHint.data());
|
||||||
} else {
|
|
||||||
DrawUtils::print((SCREEN_WIDTH / 2) - (szNoConfig / 2), (SCREEN_HEIGHT / 2), noConfigText.data());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint32_t yOffset = 8 + 24 + 8 + 4;
|
uint32_t yOffset = 8 + 24 + 8 + 4;
|
||||||
for (int32_t i = start; i < end; i++) {
|
for (int32_t i = start; i < end; i++) {
|
||||||
DrawConfigEntry(yOffset, configs[i].get().getConfigInformation(), i == mCursorPos, configs[i].get().isActivePlugin());
|
DrawConfigEntry(yOffset, configs[i].get(), i == mCursorPos);
|
||||||
yOffset += 42 + 8;
|
yOffset += 42 + 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::setFontColor(COLOR_TEXT);
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
// draw top bar
|
// Top Bar
|
||||||
DrawUtils::setFontSize(24);
|
DrawUtils::setFontSize(24);
|
||||||
if (mSetActivePluginsMode) {
|
DrawUtils::print(16, 6 + 24, mListState->GetTitle().c_str());
|
||||||
DrawUtils::print(16, 6 + 24, "Please select the plugins that should be active");
|
|
||||||
} else {
|
|
||||||
DrawUtils::print(16, 6 + 24, "Wii U Plugin System Config Menu");
|
|
||||||
|
|
||||||
auto countInactivePlugins = mAllConfigs.size() - mActiveConfigs.size();
|
if (mListState->IsMainView()) {
|
||||||
|
auto countInactivePlugins = mConfigs.size() - mFilteredConfigs.size();
|
||||||
if (countInactivePlugins > 0) {
|
if (countInactivePlugins > 0) {
|
||||||
DrawUtils::setFontSize(14);
|
DrawUtils::setFontSize(14);
|
||||||
const std::string plugin_unloaded = string_format("Found %d inactive plugins", countInactivePlugins);
|
const std::string plugin_unloaded = string_format("Found %d inactive plugins", countInactivePlugins);
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16 - DrawUtils::getTextWidth(MODULE_VERSION_FULL) - 32, 8 + 24, plugin_unloaded.c_str(), true);
|
DrawUtils::print(SCREEN_WIDTH - 16 - DrawUtils::getTextWidth(MODULE_VERSION_FULL) - 32, 8 + 24, plugin_unloaded.c_str(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::setFontSize(18);
|
DrawUtils::setFontSize(18);
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, MODULE_VERSION_FULL, true);
|
DrawUtils::print(SCREEN_WIDTH - 16, 8 + 24, MODULE_VERSION_FULL, true);
|
||||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
|
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
|
||||||
|
|
||||||
// draw bottom bar
|
// Bottom Bar
|
||||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
|
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_BLACK);
|
||||||
DrawUtils::setFontSize(18);
|
DrawUtils::setFontSize(18);
|
||||||
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\uE07D/\uE07E Navigate ");
|
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, "\uE000 Toggle | \uE045 Apply", true);
|
|
||||||
} else if (totalElementSize > 0) {
|
|
||||||
const auto text = string_format("\ue000 Select | %s Manage plugins", mLastInputWasOnWiimote ? "\uE048" : "\uE002");
|
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, text.c_str(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw scroll indicator
|
if (totalElementSize > 0) {
|
||||||
|
DrawUtils::print(16, SCREEN_HEIGHT - 10, "\uE07D/\uE07E Navigate ");
|
||||||
|
}
|
||||||
|
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, mListState->GetBottomBar(mLastInputWasOnWiimote).c_str(), true);
|
||||||
|
|
||||||
|
// Scroll Indicator
|
||||||
DrawUtils::setFontSize(24);
|
DrawUtils::setFontSize(24);
|
||||||
if (end < totalElementSize) {
|
if (end < totalElementSize) {
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
|
DrawUtils::print(SCREEN_WIDTH / 2 + 12, SCREEN_HEIGHT - 32, "\ufe3e", true);
|
||||||
|
|
@ -270,18 +230,15 @@ void ConfigRenderer::RenderStateMain() const {
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
|
DrawUtils::print(SCREEN_WIDTH / 2 + 12, 32 + 20, "\ufe3d", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw home button
|
// Home Button
|
||||||
DrawUtils::setFontSize(18);
|
DrawUtils::setFontSize(18);
|
||||||
const char *exitHint = "\ue044 Exit";
|
const char *exitHint = mListState->IsMainView() ? "\ue001 Abort" : "\ue044 Exit";
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
exitHint = "\ue001 Abort";
|
|
||||||
}
|
|
||||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
|
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHint) / 2, SCREEN_HEIGHT - 10, exitHint, true);
|
||||||
|
|
||||||
DrawUtils::endDraw();
|
DrawUtils::endDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigRenderer::DrawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted, bool isActive) const {
|
void ConfigRenderer::DrawConfigEntry(uint32_t yOffset, const ConfigDisplayItem &item, bool isHighlighted) const {
|
||||||
DrawUtils::setFontColor(COLOR_TEXT);
|
DrawUtils::setFontColor(COLOR_TEXT);
|
||||||
|
|
||||||
if (isHighlighted) {
|
if (isHighlighted) {
|
||||||
|
|
@ -291,18 +248,15 @@ void ConfigRenderer::DrawConfigEntry(uint32_t yOffset, const GeneralConfigInform
|
||||||
}
|
}
|
||||||
|
|
||||||
int textXOffset = 16 * 2;
|
int textXOffset = 16 * 2;
|
||||||
if (mSetActivePluginsMode) {
|
|
||||||
DrawUtils::setFontSize(24);
|
// Delegate Icon drawing to state, returns true if icon was drawn
|
||||||
if (isActive) {
|
if (mListState->RenderItemIcon(item, textXOffset, yOffset + 8 + 24)) {
|
||||||
DrawUtils::print(textXOffset, yOffset + 8 + 24, "\u25C9");
|
|
||||||
} else {
|
|
||||||
DrawUtils::print(textXOffset, yOffset + 8 + 24, "\u25CE");
|
|
||||||
}
|
|
||||||
textXOffset += 32;
|
textXOffset += 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawUtils::setFontSize(24);
|
DrawUtils::setFontSize(24);
|
||||||
|
|
||||||
|
const auto &configInformation = item.getConfigInformation();
|
||||||
DrawUtils::print(textXOffset, yOffset + 8 + 24, configInformation.name.c_str());
|
DrawUtils::print(textXOffset, yOffset + 8 + 24, configInformation.name.c_str());
|
||||||
uint32_t sz = DrawUtils::getTextWidth(configInformation.name.c_str());
|
uint32_t sz = DrawUtils::getTextWidth(configInformation.name.c_str());
|
||||||
DrawUtils::setFontSize(12);
|
DrawUtils::setFontSize(12);
|
||||||
|
|
@ -310,6 +264,82 @@ void ConfigRenderer::DrawConfigEntry(uint32_t yOffset, const GeneralConfigInform
|
||||||
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, configInformation.version.c_str(), true);
|
DrawUtils::print(SCREEN_WIDTH - 16 * 2, yOffset + 8 + 24, configInformation.version.c_str(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::SetListState(std::unique_ptr<ConfigListState> state) {
|
||||||
|
mListState = std::move(state);
|
||||||
|
mNeedRedraw = true;
|
||||||
|
mCursorPos = 0;
|
||||||
|
mRenderOffset = 0;
|
||||||
|
// Fallback to "show all"
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> pred = [](const auto &) { return true; };
|
||||||
|
if (mListState) {
|
||||||
|
pred = mListState->GetConfigFilter();
|
||||||
|
}
|
||||||
|
// Copy references into filteredConfigView
|
||||||
|
mFilteredConfigs.clear();
|
||||||
|
std::ranges::copy_if(mConfigs, std::back_inserter(mFilteredConfigs),
|
||||||
|
std::move(pred));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<ConfigDisplayItem> &ConfigRenderer::GetConfigItems() {
|
||||||
|
return mConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::reference_wrapper<ConfigDisplayItem>> &ConfigRenderer::GetFilteredConfigItems() {
|
||||||
|
return mFilteredConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ConfigRenderer::GetCursorPos() const {
|
||||||
|
return mCursorPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::EnterSelectedCategory() {
|
||||||
|
auto &items = GetDisplayedConfigList();
|
||||||
|
if (mCursorPos < 0 || static_cast<size_t>(mCursorPos) >= items.size()) return;
|
||||||
|
|
||||||
|
if (mCursorPos != mCurrentOpen) {
|
||||||
|
mCategoryRenderer.reset();
|
||||||
|
mCategoryRenderer = make_unique_nothrow<CategoryRenderer>(&(items[mCursorPos].get().getConfigInformation()), &(items[mCursorPos].get().getConfig()), true);
|
||||||
|
}
|
||||||
|
mNeedRedraw = true;
|
||||||
|
mCurrentOpen = mCursorPos;
|
||||||
|
mState = STATE_SUB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::SavePendingConfigs() {
|
||||||
|
for (const auto &element : mConfigs) {
|
||||||
|
CallOnCloseCallback(element.getConfigInformation(), element.getConfig());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::Exit() {
|
||||||
|
mNeedRedraw = true;
|
||||||
|
mCategoryRenderer.reset();
|
||||||
|
SavePendingConfigs();
|
||||||
|
mNextSubState = SUB_STATE_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::ExitWithReload() {
|
||||||
|
mNeedRedraw = true;
|
||||||
|
mCategoryRenderer.reset();
|
||||||
|
SavePendingConfigs();
|
||||||
|
mNextSubState = SUB_STATE_RETURN_WITH_PLUGIN_RELOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigRenderer::SetPluginsListDirty(bool dirty) {
|
||||||
|
mPluginListDirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigRenderer::GetPluginsListIfChanged(std::vector<PluginLoadWrapper> &result) {
|
||||||
|
if (mPluginListDirty) {
|
||||||
|
result.clear();
|
||||||
|
for (const auto &cur : mConfigs) {
|
||||||
|
result.emplace_back(cur.getConfigInformation().pluginData, cur.isActivePlugin(), cur.isHeapTrackingEnabled());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories) {
|
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories) {
|
||||||
for (const auto &cat : categories) {
|
for (const auto &cat : categories) {
|
||||||
if (!cat->getCategories().empty()) {
|
if (!cat->getCategories().empty()) {
|
||||||
|
|
@ -321,18 +351,6 @@ void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigRenderer::GetActivePluginsIfChanged(std::vector<PluginLoadWrapper> &result) {
|
|
||||||
if (mActivePluginsDirty) {
|
|
||||||
std::vector<std::string> inactive_plugins;
|
|
||||||
result.clear();
|
|
||||||
for (const auto &cur : mConfigs) {
|
|
||||||
result.emplace_back(cur.getConfigInformation().pluginData, cur.isActivePlugin());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const WUPSConfigAPIBackend::WUPSConfig &config) {
|
void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, const WUPSConfigAPIBackend::WUPSConfig &config) {
|
||||||
CallOnCloseCallback(info, config.getCategories());
|
CallOnCloseCallback(info, config.getCategories());
|
||||||
for (const auto &item : config.getItems()) {
|
for (const auto &item : config.getItems()) {
|
||||||
|
|
@ -340,9 +358,6 @@ void ConfigRenderer::CallOnCloseCallback(const GeneralConfigInformation &info, c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::reference_wrapper<ConfigDisplayItem>> &ConfigRenderer::GetConfigList() const {
|
const std::vector<std::reference_wrapper<ConfigDisplayItem>> &ConfigRenderer::GetDisplayedConfigList() const {
|
||||||
if (mSetActivePluginsMode) {
|
return mFilteredConfigs;
|
||||||
return mAllConfigs;
|
}
|
||||||
}
|
|
||||||
return mActiveConfigs;
|
|
||||||
}
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
|
|
||||||
#include <wups/config.h>
|
#include <wups/config.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace WUPSConfigAPIBackend {
|
namespace WUPSConfigAPIBackend {
|
||||||
class WUPSConfig;
|
class WUPSConfig;
|
||||||
class WUPSConfigCategory;
|
class WUPSConfigCategory;
|
||||||
|
|
@ -18,6 +18,8 @@ class PluginLoadWrapper;
|
||||||
class Input;
|
class Input;
|
||||||
class CategoryRenderer;
|
class CategoryRenderer;
|
||||||
class ConfigDisplayItem;
|
class ConfigDisplayItem;
|
||||||
|
class ConfigListState;
|
||||||
|
|
||||||
class ConfigRenderer {
|
class ConfigRenderer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -30,22 +32,31 @@ public:
|
||||||
void Render() const;
|
void Render() const;
|
||||||
|
|
||||||
[[nodiscard]] bool NeedsRedraw() const;
|
[[nodiscard]] bool NeedsRedraw() const;
|
||||||
|
|
||||||
void ResetNeedsRedraw();
|
void ResetNeedsRedraw();
|
||||||
|
void RequestRedraw();
|
||||||
|
|
||||||
bool GetActivePluginsIfChanged(std::vector<PluginLoadWrapper> &result);
|
bool GetPluginsListIfChanged(std::vector<PluginLoadWrapper> &result);
|
||||||
|
|
||||||
|
void SetListState(std::unique_ptr<ConfigListState> state);
|
||||||
|
const std::vector<ConfigDisplayItem> &GetConfigItems();
|
||||||
|
std::vector<std::reference_wrapper<ConfigDisplayItem>> &GetFilteredConfigItems(); // Mutable access
|
||||||
|
int32_t GetCursorPos() const;
|
||||||
|
void EnterSelectedCategory();
|
||||||
|
void Exit();
|
||||||
|
void ExitWithReload();
|
||||||
|
void SetPluginsListDirty(bool dirty);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConfigSubState UpdateStateMain(const Input &input);
|
ConfigSubState UpdateStateMain(const Input &input);
|
||||||
|
|
||||||
void RenderStateMain() const;
|
void RenderStateMain() const;
|
||||||
|
void DrawConfigEntry(uint32_t yOffset, const ConfigDisplayItem &item, bool isHighlighted) const;
|
||||||
void DrawConfigEntry(uint32_t yOffset, const GeneralConfigInformation &configInformation, bool isHighlighted, bool isActive) const;
|
|
||||||
|
|
||||||
void CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories);
|
void CallOnCloseCallback(const GeneralConfigInformation &info, const std::vector<std::unique_ptr<WUPSConfigAPIBackend::WUPSConfigCategory>> &categories);
|
||||||
void CallOnCloseCallback(const GeneralConfigInformation &info, const WUPSConfigAPIBackend::WUPSConfig &config);
|
void CallOnCloseCallback(const GeneralConfigInformation &info, const WUPSConfigAPIBackend::WUPSConfig &config);
|
||||||
|
void SavePendingConfigs();
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<std::reference_wrapper<ConfigDisplayItem>> &GetConfigList() const;
|
|
||||||
|
[[nodiscard]] const std::vector<std::reference_wrapper<ConfigDisplayItem>> &GetDisplayedConfigList() const;
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
STATE_MAIN = 0,
|
STATE_MAIN = 0,
|
||||||
|
|
@ -53,18 +64,20 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ConfigDisplayItem> mConfigs;
|
std::vector<ConfigDisplayItem> mConfigs;
|
||||||
std::vector<std::reference_wrapper<ConfigDisplayItem>> mAllConfigs;
|
mutable std::vector<std::reference_wrapper<ConfigDisplayItem>> mFilteredConfigs;
|
||||||
std::vector<std::reference_wrapper<ConfigDisplayItem>> mActiveConfigs;
|
|
||||||
|
std::unique_ptr<ConfigListState> mListState;
|
||||||
std::unique_ptr<CategoryRenderer> mCategoryRenderer;
|
std::unique_ptr<CategoryRenderer> mCategoryRenderer;
|
||||||
|
|
||||||
State mState = STATE_MAIN;
|
State mState = STATE_MAIN;
|
||||||
|
// Used to signal the main loop to return a specific state
|
||||||
|
ConfigSubState mNextSubState = SUB_STATE_RUNNING;
|
||||||
|
|
||||||
int32_t mCursorPos = 0;
|
int32_t mCursorPos = 0;
|
||||||
int32_t mRenderOffset = 0;
|
int32_t mRenderOffset = 0;
|
||||||
int32_t mCurrentOpen = -1;
|
int32_t mCurrentOpen = -1;
|
||||||
|
|
||||||
bool mNeedRedraw = true;
|
bool mNeedRedraw = true;
|
||||||
bool mSetActivePluginsMode = false;
|
bool mPluginListDirty = false;
|
||||||
bool mActivePluginsDirty = false;
|
|
||||||
bool mLastInputWasOnWiimote = false;
|
bool mLastInputWasOnWiimote = false;
|
||||||
};
|
};
|
||||||
177
source/utils/config/ConfigRendererStates.cpp
Normal file
177
source/utils/config/ConfigRendererStates.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
#include "ConfigRendererStates.h"
|
||||||
|
#include "ConfigDisplayItem.h"
|
||||||
|
#include "ConfigRenderer.h"
|
||||||
|
#include "utils/DrawUtils.h"
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "utils/input/Input.h"
|
||||||
|
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> DefaultListState::GetConfigFilter() const {
|
||||||
|
return [](const auto &item) { return item.isActivePlugin(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultListState::IsMainView() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultListState::HandleInput(ConfigRenderer &renderer, const Input &input) {
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_DOWN) {
|
||||||
|
// Hidden Combo: L + R + Down
|
||||||
|
constexpr auto COMBO_HOLD = Input::eButtons::BUTTON_L | Input::eButtons::BUTTON_R;
|
||||||
|
if ((input.data.buttons_h & COMBO_HOLD) == COMBO_HOLD) {
|
||||||
|
// Switch to Heap Tracking Mode
|
||||||
|
if (!renderer.GetConfigItems().empty()) {
|
||||||
|
renderer.SetListState(std::make_unique<HeapTrackingListState>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // Allow scrolling
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
|
||||||
|
if (!renderer.GetConfigItems().empty()) {
|
||||||
|
renderer.EnterSelectedCategory();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_X) {
|
||||||
|
if (!renderer.GetConfigItems().empty()) {
|
||||||
|
renderer.SetListState(std::make_unique<ActivePluginsListState>());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
|
||||||
|
renderer.Exit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DefaultListState::GetTitle() const {
|
||||||
|
return "Wii U Plugin System Config Menu";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DefaultListState::GetBottomBar(bool isWiimote) const {
|
||||||
|
return string_format("\ue000 Select | %s Manage plugins", isWiimote ? "\uE048" : "\uE002");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DefaultListState::RenderItemIcon(const ConfigDisplayItem & /*item*/, int /*x*/, int /*y*/) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ActivePluginsListState::HandleInput(ConfigRenderer &renderer, const Input &input) {
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
|
||||||
|
auto &items = renderer.GetFilteredConfigItems();
|
||||||
|
int pos = renderer.GetCursorPos();
|
||||||
|
if (pos >= 0 && static_cast<size_t>(pos) < items.size()) {
|
||||||
|
items[pos].get().toggleIsActivePlugin();
|
||||||
|
renderer.SetPluginsListDirty(true);
|
||||||
|
renderer.RequestRedraw();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_PLUS) {
|
||||||
|
// Apply and Reload
|
||||||
|
renderer.ExitWithReload();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
|
||||||
|
// Abort / Reset
|
||||||
|
for (auto &item : renderer.GetFilteredConfigItems()) {
|
||||||
|
item.get().resetIsActivePlugin();
|
||||||
|
}
|
||||||
|
renderer.SetPluginsListDirty(false);
|
||||||
|
renderer.SetListState(std::make_unique<DefaultListState>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ActivePluginsListState::GetTitle() const {
|
||||||
|
return "Please select the plugins that should be active";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ActivePluginsListState::GetBottomBar(bool /*isWiimote*/) const {
|
||||||
|
return "\uE000 Toggle | \uE001 Abort | \uE045 Apply";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActivePluginsListState::RenderItemIcon(const ConfigDisplayItem &item, int x, int y) const {
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
if (item.isActivePlugin()) {
|
||||||
|
DrawUtils::print(x, y, "\u25C9");
|
||||||
|
} else {
|
||||||
|
DrawUtils::print(x, y, "\u25CE");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> ActivePluginsListState::GetConfigFilter() const {
|
||||||
|
return [](const auto &) { return true; };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActivePluginsListState::IsMainView() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeapTrackingListState::HandleInput(ConfigRenderer &renderer, const Input &input) {
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_A) {
|
||||||
|
auto &items = renderer.GetFilteredConfigItems();
|
||||||
|
int pos = renderer.GetCursorPos();
|
||||||
|
if (pos >= 0 && static_cast<size_t>(pos) < items.size()) {
|
||||||
|
items[pos].get().toggleIsHeapTrackingEnabled();
|
||||||
|
renderer.SetPluginsListDirty(true);
|
||||||
|
renderer.RequestRedraw();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & Input::eButtons::BUTTON_PLUS) {
|
||||||
|
// Apply and Reload
|
||||||
|
renderer.ExitWithReload();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.data.buttons_d & (Input::eButtons::BUTTON_B | Input::eButtons::BUTTON_HOME)) {
|
||||||
|
// Abort
|
||||||
|
for (auto &item : renderer.GetFilteredConfigItems()) {
|
||||||
|
item.get().resetIsHeapTrackingEnabled();
|
||||||
|
}
|
||||||
|
renderer.SetPluginsListDirty(false);
|
||||||
|
renderer.SetListState(std::make_unique<DefaultListState>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HeapTrackingListState::GetTitle() const {
|
||||||
|
return "Select plugins to enable Heap Tracking";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HeapTrackingListState::GetBottomBar(bool /*isWiimote*/) const {
|
||||||
|
return "\uE000 Toggle | \uE001 Abort | \uE045 Apply";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeapTrackingListState::RenderItemIcon(const ConfigDisplayItem &item, const int x, const int y) const {
|
||||||
|
DrawUtils::setFontSize(24);
|
||||||
|
if (item.isHeapTrackingEnabled()) {
|
||||||
|
DrawUtils::print(x, y, "\u25C9");
|
||||||
|
} else {
|
||||||
|
DrawUtils::print(x, y, "\u25CE");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> HeapTrackingListState::GetConfigFilter() const {
|
||||||
|
return [](const auto &item) { return item.isActivePlugin(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeapTrackingListState::IsMainView() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
53
source/utils/config/ConfigRendererStates.h
Normal file
53
source/utils/config/ConfigRendererStates.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class ConfigRenderer;
|
||||||
|
class Input;
|
||||||
|
class ConfigDisplayItem;
|
||||||
|
|
||||||
|
class ConfigListState {
|
||||||
|
public:
|
||||||
|
virtual ~ConfigListState() = default;
|
||||||
|
|
||||||
|
virtual bool HandleInput(ConfigRenderer &renderer, const Input &input) = 0;
|
||||||
|
|
||||||
|
virtual std::string GetTitle() const = 0;
|
||||||
|
virtual std::string GetBottomBar(bool isWiimote) const = 0;
|
||||||
|
virtual bool RenderItemIcon(const ConfigDisplayItem &item, int x, int y) const = 0;
|
||||||
|
|
||||||
|
virtual std::function<bool(const ConfigDisplayItem &)> GetConfigFilter() const = 0;
|
||||||
|
virtual bool IsMainView() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultListState : public ConfigListState {
|
||||||
|
public:
|
||||||
|
bool HandleInput(ConfigRenderer &renderer, const Input &input) override;
|
||||||
|
std::string GetTitle() const override;
|
||||||
|
std::string GetBottomBar(bool isWiimote) const override;
|
||||||
|
bool RenderItemIcon(const ConfigDisplayItem &item, int x, int y) const override;
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> GetConfigFilter() const override;
|
||||||
|
bool IsMainView() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ActivePluginsListState : public ConfigListState {
|
||||||
|
public:
|
||||||
|
bool HandleInput(ConfigRenderer &renderer, const Input &input) override;
|
||||||
|
std::string GetTitle() const override;
|
||||||
|
std::string GetBottomBar(bool isWiimote) const override;
|
||||||
|
bool RenderItemIcon(const ConfigDisplayItem &item, int x, int y) const override;
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> GetConfigFilter() const override;
|
||||||
|
bool IsMainView() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HeapTrackingListState : public ConfigListState {
|
||||||
|
public:
|
||||||
|
bool HandleInput(ConfigRenderer &renderer, const Input &input) override;
|
||||||
|
std::string GetTitle() const override;
|
||||||
|
std::string GetBottomBar(bool isWiimote) const override;
|
||||||
|
bool RenderItemIcon(const ConfigDisplayItem &item, int x, int y) const override;
|
||||||
|
std::function<bool(const ConfigDisplayItem &)> GetConfigFilter() const override;
|
||||||
|
bool IsMainView() const override;
|
||||||
|
};
|
||||||
|
|
@ -133,7 +133,7 @@ void ConfigUtils::displayMenu() {
|
||||||
config = make_unique_nothrow<WUPSConfigAPIBackend::WUPSConfig>(info.name);
|
config = make_unique_nothrow<WUPSConfigAPIBackend::WUPSConfig>(info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
configs.emplace_back(info, std::move(config), plugin.isLinkedAndLoaded());
|
configs.emplace_back(info, std::move(config), plugin.isLinkedAndLoaded(), plugin.isUsingTrackingPluginHeapMemoryAllocator());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort Configs by name
|
// Sort Configs by name
|
||||||
|
|
@ -226,7 +226,7 @@ void ConfigUtils::displayMenu() {
|
||||||
|
|
||||||
std::vector<PluginLoadWrapper> newActivePluginsList;
|
std::vector<PluginLoadWrapper> newActivePluginsList;
|
||||||
|
|
||||||
if (subStateReturnValue == SUB_STATE_RETURN_WITH_PLUGIN_RELOAD && renderer.GetActivePluginsIfChanged(newActivePluginsList)) {
|
if (subStateReturnValue == SUB_STATE_RETURN_WITH_PLUGIN_RELOAD && renderer.GetPluginsListIfChanged(newActivePluginsList)) {
|
||||||
startTime = OSGetTime();
|
startTime = OSGetTime();
|
||||||
renderBasicScreen("Applying changes, app will now restart...");
|
renderBasicScreen("Applying changes, app will now restart...");
|
||||||
|
|
||||||
|
|
@ -243,6 +243,7 @@ void ConfigUtils::displayMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gLoadOnNextLaunch = newActivePluginsList;
|
gLoadOnNextLaunch = newActivePluginsList;
|
||||||
WUPSBackendSettings::SetInactivePluginFilenames(newInactivePluginsList);
|
WUPSBackendSettings::SetInactivePluginFilenames(newInactivePluginsList);
|
||||||
if (!WUPSBackendSettings::SaveSettings()) {
|
if (!WUPSBackendSettings::SaveSettings()) {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,17 @@ extern "C" PluginBackendApiErrorType WUPSLoadAndLinkByDataHandle(const wups_back
|
||||||
|
|
||||||
for (const auto &pluginData : gLoadedData) {
|
for (const auto &pluginData : gLoadedData) {
|
||||||
if (pluginData->getHandle() == handle) {
|
if (pluginData->getHandle() == handle) {
|
||||||
gLoadOnNextLaunch.emplace_back(pluginData, true);
|
bool heapTrackingActive = false;
|
||||||
|
#ifdef DEBUG
|
||||||
|
heapTrackingActive = true;
|
||||||
|
#endif
|
||||||
|
for (const auto &plugin : gLoadedPlugins) {
|
||||||
|
if (plugin.getPluginDataCopy()->getHandle() == handle) {
|
||||||
|
heapTrackingActive = plugin.isUsingTrackingPluginHeapMemoryAllocator();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gLoadOnNextLaunch.emplace_back(pluginData, true, heapTrackingActive);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +62,7 @@ extern "C" PluginBackendApiErrorType WUPSLoadAndLinkByDataHandle(const wups_back
|
||||||
// Keep inactive plugins loaded. Duplicates will be eliminated by comparing name/author
|
// Keep inactive plugins loaded. Duplicates will be eliminated by comparing name/author
|
||||||
for (const auto &plugin : gLoadedPlugins) {
|
for (const auto &plugin : gLoadedPlugins) {
|
||||||
if (!plugin.isLinkedAndLoaded()) {
|
if (!plugin.isLinkedAndLoaded()) {
|
||||||
gLoadOnNextLaunch.emplace_back(plugin.getPluginDataCopy(), false);
|
gLoadOnNextLaunch.emplace_back(plugin.getPluginDataCopy(), false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <coreinit/debug.h>
|
||||||
#include <coreinit/ios.h>
|
#include <coreinit/ios.h>
|
||||||
|
|
||||||
|
#include <wups/hooks.h>
|
||||||
#include <wups/storage.h>
|
#include <wups/storage.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -198,4 +200,145 @@ std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
std::vector<uint32_t>
|
||||||
|
CaptureStackTrace(uint32_t maxDepth) {
|
||||||
|
std::vector<uint32_t> trace;
|
||||||
|
trace.reserve(maxDepth);
|
||||||
|
|
||||||
|
uint32_t *stackPointer;
|
||||||
|
// Grab the current Stack Pointer (r1)
|
||||||
|
asm volatile("mr %0, 1"
|
||||||
|
: "=r"(stackPointer));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < maxDepth + 1; ++i) {
|
||||||
|
// Basic alignment check
|
||||||
|
if (!stackPointer || (reinterpret_cast<uintptr_t>(stackPointer) & 0x3)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t backChain = stackPointer[0];
|
||||||
|
uint32_t returnAddr = stackPointer[1];
|
||||||
|
|
||||||
|
if (returnAddr == 0) break;
|
||||||
|
if (i != 0) {
|
||||||
|
trace.push_back(returnAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backChain == 0) break;
|
||||||
|
stackPointer = reinterpret_cast<uint32_t *>(backChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SC17_FindClosestSymbol ((uint32_t(*)(uint32_t addr, int32_t * outDistance, char *symbolNameBuffer, uint32_t symbolNameBufferLength, char *moduleNameBuffer, uint32_t moduleNameBufferLength))(0x101C400 + 0x1f934))
|
||||||
|
|
||||||
|
std::string getModuleAndSymbolName(uint32_t addr) {
|
||||||
|
int distance = 0;
|
||||||
|
char moduleName[50] = {};
|
||||||
|
char symbolName[256] = {};
|
||||||
|
|
||||||
|
if (SC17_FindClosestSymbol(addr, &distance, symbolName, sizeof(symbolName) - 1, moduleName, sizeof(moduleName) - 1) != 0) {
|
||||||
|
return string_format("0x%08X", addr);
|
||||||
|
} else {
|
||||||
|
moduleName[sizeof(moduleName) - 1] = '\0';
|
||||||
|
symbolName[sizeof(symbolName) - 1] = '\0';
|
||||||
|
return string_format("%s|%s+0x%X",
|
||||||
|
moduleName,
|
||||||
|
symbolName,
|
||||||
|
distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintCapturedStackTrace(const std::span<const uint32_t> trace) {
|
||||||
|
if (trace.empty()) {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("┌────────────────────── CAPTURED TRACE ──────────────────────┐");
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("│ <Empty Trace>");
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("└────────────────────────────────────────────────────────────┘");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("┌────────────────────── CAPTURED TRACE ──────────────────────┐");
|
||||||
|
for (size_t i = 0; i < trace.size(); ++i) {
|
||||||
|
uint32_t addr = trace[i];
|
||||||
|
int distance = 0;
|
||||||
|
char moduleName[50] = {};
|
||||||
|
char symbolName[256] = {};
|
||||||
|
|
||||||
|
if (SC17_FindClosestSymbol(addr, &distance, symbolName, sizeof(symbolName) - 1, moduleName, sizeof(moduleName) - 1) != 0) {
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("│ [%02d] 0x%08X", i, addr);
|
||||||
|
} else {
|
||||||
|
moduleName[sizeof(moduleName) - 1] = '\0';
|
||||||
|
symbolName[sizeof(symbolName) - 1] = '\0';
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("│ [%02d] %s : %s + 0x%X (0x%08X)",
|
||||||
|
i,
|
||||||
|
moduleName,
|
||||||
|
symbolName,
|
||||||
|
distance,
|
||||||
|
addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE_INFO("└────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hookNameToString(const wups_loader_hook_type_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_MALLOC:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_MALLOC";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WUT_MALLOC:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WUT_MALLOC";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_NEWLIB:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_NEWLIB";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WUT_NEWLIB:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WUT_NEWLIB";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_STDCPP:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_STDCPP";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WUT_STDCPP:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WUT_STDCPP";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_DEVOPTAB:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_DEVOPTAB";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WUT_DEVOPTAB:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WUT_DEVOPTAB";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_SOCKETS:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_SOCKETS";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WUT_SOCKETS:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WUT_SOCKETS";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WRAPPER:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WRAPPER";
|
||||||
|
case WUPS_LOADER_HOOK_FINI_WRAPPER:
|
||||||
|
return "WUPS_LOADER_HOOK_FINI_WRAPPER";
|
||||||
|
case WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED:
|
||||||
|
return "WUPS_LOADER_HOOK_GET_CONFIG_DEPRECATED";
|
||||||
|
case WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED:
|
||||||
|
return "WUPS_LOADER_HOOK_CONFIG_CLOSED_DEPRECATED";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_STORAGE_DEPRECATED";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_PLUGIN:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_PLUGIN";
|
||||||
|
case WUPS_LOADER_HOOK_DEINIT_PLUGIN:
|
||||||
|
return "WUPS_LOADER_HOOK_DEINIT_PLUGIN";
|
||||||
|
case WUPS_LOADER_HOOK_APPLICATION_STARTS:
|
||||||
|
return "WUPS_LOADER_HOOK_APPLICATION_STARTS";
|
||||||
|
case WUPS_LOADER_HOOK_RELEASE_FOREGROUND:
|
||||||
|
return "WUPS_LOADER_HOOK_RELEASE_FOREGROUND";
|
||||||
|
case WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND:
|
||||||
|
return "WUPS_LOADER_HOOK_ACQUIRED_FOREGROUND";
|
||||||
|
case WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT:
|
||||||
|
return "WUPS_LOADER_HOOK_APPLICATION_REQUESTS_EXIT";
|
||||||
|
case WUPS_LOADER_HOOK_APPLICATION_ENDS:
|
||||||
|
return "WUPS_LOADER_HOOK_APPLICATION_ENDS";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_STORAGE:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_STORAGE";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_CONFIG:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_CONFIG";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_BUTTON_COMBO:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_BUTTON_COMBO";
|
||||||
|
case WUPS_LOADER_HOOK_INIT_WUT_THREAD:
|
||||||
|
return "WUPS_LOADER_HOOK_INIT_WUT_THREAD";
|
||||||
|
}
|
||||||
|
return "<UNKNOWN>";
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <coreinit/dynload.h>
|
#include <coreinit/dynload.h>
|
||||||
|
|
||||||
|
#include <wups/hooks.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -161,4 +163,12 @@ UtilsIOError ParseJsonFromFile(const std::string &filePath, nlohmann::json &outJ
|
||||||
|
|
||||||
std::vector<std::string> getPluginFilePaths(std::string_view basePath);
|
std::vector<std::string> getPluginFilePaths(std::string_view basePath);
|
||||||
|
|
||||||
std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePath);
|
std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePath);
|
||||||
|
|
||||||
|
std::vector<uint32_t> CaptureStackTrace(uint32_t maxDepth);
|
||||||
|
|
||||||
|
std::string getModuleAndSymbolName(uint32_t addr);
|
||||||
|
|
||||||
|
void PrintCapturedStackTrace(std::span<const uint32_t> trace);
|
||||||
|
|
||||||
|
std::string hookNameToString(wups_loader_hook_type_t type);
|
||||||
Loading…
Reference in New Issue
Block a user