mirror of
https://github.com/wiiu-env/wiiload_plugin.git
synced 2024-11-25 12:06:52 +01:00
Use libwupsbackend for wiiloading plugins
This commit is contained in:
parent
4c9dd43072
commit
0d33e1a9b6
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,3 +5,7 @@ sysapp.cbp
|
||||
sysapp.cscope_file_list
|
||||
*.wps
|
||||
*.elf
|
||||
cmake-build-debug/
|
||||
.idea/
|
||||
*.rpx
|
||||
*.txt
|
||||
|
5
Makefile
5
Makefile
@ -23,7 +23,6 @@ TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := src \
|
||||
src/fs \
|
||||
src/plugin \
|
||||
src/utils
|
||||
DATA := data
|
||||
INCLUDES := src
|
||||
@ -36,12 +35,12 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
||||
|
||||
CXXFLAGS := $(CFLAGS)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(WUPSSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lwups -lwut
|
||||
LIBS := -lwups -lwut -lwupsbackend
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
|
50
makefile.mk
50
makefile.mk
@ -1,50 +0,0 @@
|
||||
# Compiling the projects with libutils logging code?
|
||||
DO_LOGGING := 1
|
||||
|
||||
# Target filename
|
||||
TARGET := $(notdir $(CURDIR)).mod
|
||||
|
||||
# Source directories
|
||||
SOURCES := src src/utils src/fs src/plugin
|
||||
|
||||
# Data directories
|
||||
DATA :=
|
||||
|
||||
# Include directories
|
||||
INCLUDES := src
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation and linking
|
||||
#---------------------------------------------------------------------------------
|
||||
# Extra C AND C++ compiler flags
|
||||
COMMON_CFLAGS :=
|
||||
# Extra C compiler flags
|
||||
CFLAGS :=
|
||||
# Extra C++ compiler flags
|
||||
CXXFLAGS :=
|
||||
# Extra linking flags for all linking steps
|
||||
LDFLAGS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(WUPSDIR) $(WUT_ROOT) $(PORTLIBS)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lwups -lwut
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# Will be added to the final lib paths
|
||||
# example:
|
||||
# -L$C:/library1/lib
|
||||
#---------------------------------------------------------------------------------
|
||||
EXTERNAL_LIBPATHS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# Will be added to the final include paths
|
||||
# -IC:/library1/include
|
||||
#---------------------------------------------------------------------------------
|
||||
EXTERNAL_INCLUDE :=
|
@ -1,117 +0,0 @@
|
||||
/* based on module.c
|
||||
* by Alex Chadwick
|
||||
*
|
||||
* Copyright (C) 2014, Alex Chadwick
|
||||
* Modified 2018, Maschell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _PLUGIN_INFORMATION_H_
|
||||
#define _PLUGIN_INFORMATION_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "utils/logger.h"
|
||||
#include "utils/ipcclient.h"
|
||||
|
||||
class PluginInformation {
|
||||
public:
|
||||
PluginInformation(plugin_information_handle handle, const char * path, const char * name, const char * author) {
|
||||
this->handle = handle;
|
||||
this->path = path;
|
||||
this->name = name;
|
||||
this->author = author;
|
||||
}
|
||||
|
||||
~PluginInformation() {
|
||||
IPC_Delete_Plugin_Information(this->handle);
|
||||
}
|
||||
|
||||
std::string getName() {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
std::string getAuthor() {
|
||||
return this->author;
|
||||
}
|
||||
|
||||
std::string getVersion() {
|
||||
return this->version;
|
||||
}
|
||||
|
||||
std::string getLicense() {
|
||||
return this->license;
|
||||
}
|
||||
|
||||
std::string getBuildTimestamp() {
|
||||
return this->buildtimestamp;
|
||||
}
|
||||
|
||||
std::string getDescription() {
|
||||
return this->description;
|
||||
}
|
||||
|
||||
std::string getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
size_t getSize() {
|
||||
return this->size;
|
||||
}
|
||||
|
||||
plugin_information_handle getHandle() {
|
||||
return this->handle;
|
||||
}
|
||||
private:
|
||||
void setVersion(const char * version) {
|
||||
this->version = version;
|
||||
}
|
||||
|
||||
void setLicense(const char * license) {
|
||||
this->license = license;
|
||||
}
|
||||
|
||||
void setBuildTimestamp(const char * buildtimestamp) {
|
||||
this->buildtimestamp = buildtimestamp;
|
||||
}
|
||||
|
||||
void setDescription(const char * description) {
|
||||
this->description = description;
|
||||
}
|
||||
|
||||
void setSize(size_t size) {
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
plugin_information_handle handle;
|
||||
|
||||
std::string path;
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::string version;
|
||||
std::string license;
|
||||
std::string buildtimestamp;
|
||||
std::string description;
|
||||
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,121 +0,0 @@
|
||||
#include <string>
|
||||
#include "PluginInformationUtils.h"
|
||||
#include "utils/ipcclient.h"
|
||||
|
||||
std::vector<PluginInformation *> PluginInformationUtils::getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize) {
|
||||
std::vector<PluginInformation *> result;
|
||||
|
||||
if(handleListSize > 0) {
|
||||
DEBUG_FUNCTION_LINE("Getting details for handles\n");
|
||||
plugin_information * informationList = NULL;
|
||||
uint32_t informationListSize = 0;
|
||||
uint32_t res = IPC_Get_Plugin_Information_Details(handleList, handleListSize, &informationList, &informationListSize);
|
||||
if(res == 0) {
|
||||
for(uint32_t i = 0; i<informationListSize; i++) {
|
||||
DEBUG_FUNCTION_LINE("Adding %08X %s\n", informationList[i].handle, informationList[i].path);
|
||||
result.push_back(new PluginInformation(informationList[i].handle,informationList[i].path,informationList[i].name,informationList[i].author));
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("IPC_Get_Plugin_Information_Details failed\n");
|
||||
}
|
||||
if(informationList != NULL) {
|
||||
free(informationList);
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("List is empty.\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PluginInformationUtils::clearPluginInformation(std::vector<PluginInformation *> pluginInformation) {
|
||||
for(size_t i = 0; i < pluginInformation.size(); i++) {
|
||||
PluginInformation * curPluginInformation = pluginInformation[i];
|
||||
if(curPluginInformation != NULL) {
|
||||
delete curPluginInformation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PluginInformation *> PluginInformationUtils::getPluginsByPath(std::string path) {
|
||||
std::vector<PluginInformation *> result;
|
||||
plugin_information_handle * handleList = NULL;
|
||||
uint32_t handleListSize = 0;
|
||||
|
||||
uint32_t res = IPC_Get_Plugin_Information(path.c_str(), &handleList, &handleListSize);
|
||||
if(res == 0) {
|
||||
DEBUG_FUNCTION_LINE("SUCCESS reading plugins from %s. handleListSize %d, handlelist %08X \n",path, handleListSize, handleList);
|
||||
result = getPluginInformationByStruct(handleList, handleListSize);
|
||||
}
|
||||
|
||||
if(handleList != NULL) {
|
||||
free(handleList);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginInformation * PluginInformationUtils::loadPluginInformation(std::string path) {
|
||||
std::vector<PluginInformation *> result;
|
||||
plugin_information_handle handle = NULL;
|
||||
|
||||
uint32_t res = IPC_Get_Plugin_Information_For_Filepath(path.c_str(), &handle);
|
||||
if(res == 0 && handle != NULL) {
|
||||
DEBUG_FUNCTION_LINE("SUCCESS reading plugins from %s. handle %08X \n",path.c_str(), &handle);
|
||||
result = getPluginInformationByStruct(&handle, 1);
|
||||
}
|
||||
|
||||
if(result.size() > 0){
|
||||
return result.at(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool PluginInformationUtils::loadAndLinkPluginsOnRestart(std::vector<PluginInformation *> pluginInformation) {
|
||||
uint32_t handleListSize = pluginInformation.size();
|
||||
|
||||
DEBUG_FUNCTION_LINE("Convert PluginInformation* to plugin_information_handle *\n");
|
||||
|
||||
plugin_information_handle * handleList = (plugin_information_handle *) malloc(handleListSize * sizeof(plugin_information_handle));
|
||||
if(handleList == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Allocation was okay %08X\n", handleList);
|
||||
|
||||
|
||||
uint32_t cur = 0;
|
||||
for (std::vector<PluginInformation *>::iterator it = pluginInformation.begin() ; it != pluginInformation.end(); ++it) {
|
||||
PluginInformation * curPlugin = *it;
|
||||
handleList[cur] = curPlugin->getHandle();
|
||||
DEBUG_FUNCTION_LINE("Adding to List %08X\n", handleList[cur]);
|
||||
cur++;
|
||||
}
|
||||
bool result = false;
|
||||
int32_t res = IPC_Link_Plugin_Information_On_Restart(handleList, handleListSize);
|
||||
|
||||
if(res >= 0) {
|
||||
DEBUG_FUNCTION_LINE("result was %d\n", res);
|
||||
result = true;
|
||||
}
|
||||
|
||||
free(handleList);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<PluginInformation *> PluginInformationUtils::getPluginsLoadedInMemory() {
|
||||
std::vector<PluginInformation *> result;
|
||||
plugin_information_handle * handleList = NULL;
|
||||
uint32_t handleListSize = 0;
|
||||
|
||||
uint32_t res = IPC_Get_Plugin_Information_Loaded(&handleList, &handleListSize);
|
||||
if(res == 0) {
|
||||
result = getPluginInformationByStruct(handleList, handleListSize);
|
||||
}
|
||||
|
||||
if(handleList != NULL) {
|
||||
free(handleList);
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/* based on module.c
|
||||
* by Alex Chadwick
|
||||
*
|
||||
* Copyright (C) 2014, Alex Chadwick
|
||||
* Modified 2018,2019 Maschell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _PLUGIN_INFORMATION_UTILS_H_
|
||||
#define _PLUGIN_INFORMATION_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "PluginInformation.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/ipcclient.h"
|
||||
|
||||
class PluginInformationUtils {
|
||||
public:
|
||||
|
||||
|
||||
static PluginInformation * loadPluginInformation(std::string path);
|
||||
|
||||
|
||||
/**
|
||||
\brief Parses the meta data of all plugins in the given directory.
|
||||
|
||||
\param path the path of the directory which should be scanned.
|
||||
|
||||
\return a list of PluginInformation objects, one for each valid plugin.
|
||||
**/
|
||||
static std::vector<PluginInformation *> getPluginsByPath(std::string path);
|
||||
|
||||
|
||||
/**
|
||||
\brief Gets plugin information from the global struct.
|
||||
|
||||
\return a list of MetaInformation objects for all plugins currently loaded and linked (relocated). Will only contain
|
||||
plugin which are still on the sd card.
|
||||
**/
|
||||
static std::vector<PluginInformation *> getPluginsLoadedInMemory();
|
||||
|
||||
|
||||
static void clearPluginInformation(std::vector<PluginInformation *> pluginInformation) ;
|
||||
|
||||
|
||||
static bool loadAndLinkPluginsOnRestart(std::vector<PluginInformation *> pluginInformation);
|
||||
|
||||
static std::vector<PluginInformation *> getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,60 +0,0 @@
|
||||
#include <string>
|
||||
#include "PluginLoader.h"
|
||||
#include "utils/ipcclient.h"
|
||||
|
||||
PluginLoader * PluginLoader::createInstance(uint32_t startAddress, uint32_t endAddress) {
|
||||
plugin_loader_handle handle = IPC_Open_Plugin_Loader(startAddress, endAddress);
|
||||
|
||||
if(handle != 0) {
|
||||
return new PluginLoader(handle, startAddress,endAddress);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PluginLoader::destroyInstance(PluginLoader * loader) {
|
||||
if(loader != NULL) {
|
||||
delete loader;
|
||||
}
|
||||
}
|
||||
|
||||
PluginLoader::PluginLoader(plugin_information_handle handle,uint32_t startAddress, uint32_t endAddress) {
|
||||
this->handle = handle;
|
||||
this->startAddress = startAddress;
|
||||
this->endAddress = endAddress;
|
||||
}
|
||||
|
||||
PluginLoader::~PluginLoader() {
|
||||
IPC_Close_Plugin_Loader(this->handle);
|
||||
}
|
||||
|
||||
bool PluginLoader::loadAndLinkPlugins(std::vector<PluginInformation *> pluginInformation) {
|
||||
uint32_t handleListSize = pluginInformation.size();
|
||||
|
||||
DEBUG_FUNCTION_LINE("Convert PluginInformation* to plugin_information_handle *\n");
|
||||
|
||||
plugin_information_handle * handleList = (plugin_information_handle *) malloc(handleListSize * sizeof(plugin_information_handle));
|
||||
if(handleList == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Allocation was okay %08X\n", handleList);
|
||||
|
||||
|
||||
uint32_t cur = 0;
|
||||
for (std::vector<PluginInformation *>::iterator it = pluginInformation.begin() ; it != pluginInformation.end(); ++it) {
|
||||
PluginInformation * curPlugin = *it;
|
||||
handleList[cur] = curPlugin->getHandle();
|
||||
DEBUG_FUNCTION_LINE("Adding to List %08X\n", handleList[cur]);
|
||||
cur++;
|
||||
}
|
||||
bool result = false;
|
||||
int32_t res = IPC_Link_Plugin_Information(this->handle, handleList, handleListSize);
|
||||
|
||||
if(res >= 0) {
|
||||
DEBUG_FUNCTION_LINE("result was %d\n", res);
|
||||
result = true;
|
||||
}
|
||||
|
||||
free(handleList);
|
||||
return result;
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
#ifndef _PLUGIN_LOADER_H_
|
||||
#define _PLUGIN_LOADER_H_
|
||||
|
||||
#include <vector>
|
||||
#include "PluginInformation.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <utils/utils.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
class PluginLoader {
|
||||
|
||||
public:
|
||||
static PluginLoader * createInstance(uint32_t startAddress, uint32_t endAddress);
|
||||
|
||||
static void destroyInstance(PluginLoader * loader);
|
||||
|
||||
~PluginLoader();
|
||||
|
||||
/**
|
||||
\brief Takes a list of plugins that should be linked (relocated) loaded into the memory.
|
||||
The function that should be replaced will be replaced in the order of the given plugin list.
|
||||
So two plugin will override the same function, the plugin first in this list will override the function first.
|
||||
Also the hooks of the plugins will be called in the order their plugin where passed to this method.
|
||||
|
||||
\param A list of plugin that should be linked (relocated) an loaded into memory
|
||||
|
||||
\return Returns true if all plugins were linked successfully. Returns false if at least one plugin failed while linking.
|
||||
**/
|
||||
bool loadAndLinkPlugins(std::vector<PluginInformation *> pluginInformation);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
\brief Load
|
||||
|
||||
\param pluginInformation a PluginInformation object of the plugin that should be linked (relocated) and loaded.
|
||||
**/
|
||||
bool loadAndLinkPlugin(PluginInformation * pluginInformation);
|
||||
|
||||
|
||||
/*
|
||||
size_t getTotalSpace() {
|
||||
return ((uint32_t) this->endAddress - (uint32_t) this->startAddress);
|
||||
}
|
||||
|
||||
size_t getAvailableSpace() {
|
||||
return ((uint32_t) this->endAddress - (uint32_t) this->currentStoreAddress);
|
||||
}
|
||||
|
||||
size_t getUsedSpace() {
|
||||
return getTotalSpace() - getAvailableSpace();
|
||||
}
|
||||
|
||||
void resetPluginLoader() {
|
||||
this->currentStoreAddress = ROUNDUP((uint32_t)startAddress, 0x10000);
|
||||
}*/
|
||||
private:
|
||||
PluginLoader(plugin_loader_handle handle, uint32_t startAddress, uint32_t endAddress);
|
||||
|
||||
static std::vector<PluginInformation *> getPluginInformationByStruct(plugin_information_handle * handleList, uint32_t handleListSize);
|
||||
|
||||
plugin_loader_handle handle = 0;
|
||||
|
||||
uint32_t startAddress = 0;
|
||||
uint32_t endAddress = 0;
|
||||
uint32_t currentStoreAddress = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,69 +0,0 @@
|
||||
/****************************************************************************
|
||||
* Copyright (C) 2015 Dimok
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#ifndef _CMUTEX_H_
|
||||
#define _CMUTEX_H_
|
||||
|
||||
#include <malloc.h>
|
||||
#include <coreinit/mutex.h>
|
||||
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex() {
|
||||
pMutex = (OSMutex*) malloc(sizeof(OSMutex));
|
||||
if(!pMutex)
|
||||
return;
|
||||
|
||||
OSInitMutex(pMutex);
|
||||
}
|
||||
virtual ~CMutex() {
|
||||
if(pMutex)
|
||||
free(pMutex);
|
||||
}
|
||||
|
||||
void lock(void) {
|
||||
if(pMutex)
|
||||
OSLockMutex(pMutex);
|
||||
}
|
||||
void unlock(void) {
|
||||
if(pMutex)
|
||||
OSUnlockMutex(pMutex);
|
||||
}
|
||||
BOOL tryLock(void) {
|
||||
if(!pMutex)
|
||||
return false;
|
||||
|
||||
return (OSTryLockMutex(pMutex) != 0);
|
||||
}
|
||||
private:
|
||||
OSMutex *pMutex;
|
||||
};
|
||||
|
||||
class CMutexLock
|
||||
{
|
||||
public:
|
||||
CMutexLock() {
|
||||
mutex.lock();
|
||||
}
|
||||
virtual ~CMutexLock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
private:
|
||||
CMutex mutex;
|
||||
};
|
||||
|
||||
#endif // _CMUTEX_H_
|
@ -3,23 +3,17 @@
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <libgen.h>
|
||||
#include <sysapp/launch.h>
|
||||
|
||||
#include <coreinit/messagequeue.h>
|
||||
#include <coreinit/ios.h>
|
||||
|
||||
#include "TcpReceiver.h"
|
||||
#include "fs/CFile.hpp"
|
||||
#include "fs/FSUtils.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/StringTools.h"
|
||||
#include "utils/net.h"
|
||||
#include "utils/utils.h"
|
||||
#include "plugin/PluginInformationUtils.h"
|
||||
|
||||
#define WUPS_TEMP_PLUGIN_PATH "fs:/vol/external01/wiiu/plugins/temp/"
|
||||
#define WUPS_TEMP_PLUGIN_FILE "fs:/vol/external01/wiiu/plugins/temp/temp.mod"
|
||||
#include <wups_backend/PluginUtils.h>
|
||||
#include <coreinit/debug.h>
|
||||
|
||||
#define RPX_TEMP_PATH "fs:/vol/external01/wiiu/apps/"
|
||||
#define RPX_TEMP_FILE "fs:/vol/external01/wiiu/apps/temp.rpx"
|
||||
@ -241,15 +235,54 @@ int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) {
|
||||
fileSize = fileSizeUnc;
|
||||
}
|
||||
|
||||
if(inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE){
|
||||
if(inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE && inflatedData[0x9] != 0xDE && inflatedData[0xA] != 0xAD){
|
||||
DEBUG_FUNCTION_LINE("Try to load a rpx\n");
|
||||
FSUtils::CreateSubfolder(RPX_TEMP_PATH);
|
||||
res = FSUtils::saveBufferToFile(RPX_TEMP_FILE,inflatedData, fileSize);
|
||||
free(inflatedData);
|
||||
loadedRPX = true;
|
||||
}else if(inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE && inflatedData[0x9] == 0xDE && inflatedData[0xA] == 0xAD){
|
||||
|
||||
auto newContainer = PluginUtils::getPluginForBuffer((char*)inflatedData, fileSize);
|
||||
if(newContainer){
|
||||
auto oldPlugins = PluginUtils::getLoadedPlugins(8);
|
||||
std::vector<PluginContainer> finalList;
|
||||
|
||||
finalList.push_back(newContainer.value());
|
||||
for (auto &plugin : oldPlugins) {
|
||||
if (plugin.metaInformation.getName().compare(newContainer->metaInformation.getName()) == 0 &&
|
||||
plugin.metaInformation.getAuthor().compare(newContainer->metaInformation.getAuthor()) == 0
|
||||
) {
|
||||
DEBUG_FUNCTION_LINE("Skipping duplicate");
|
||||
PluginUtils::destroyPluginContainer(plugin);
|
||||
continue;
|
||||
}else{
|
||||
FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH);
|
||||
res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,inflatedData, fileSize);
|
||||
finalList.push_back(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &plugin : finalList) {
|
||||
DEBUG_FUNCTION_LINE("name: %s\n", plugin.getMetaInformation().getName().c_str());
|
||||
DEBUG_FUNCTION_LINE("author: %s\n", plugin.getMetaInformation().getAuthor().c_str());
|
||||
DEBUG_FUNCTION_LINE("handle: %08X\n", plugin.getPluginData().getHandle());
|
||||
DEBUG_FUNCTION_LINE("====\n");
|
||||
}
|
||||
|
||||
if (PluginUtils::LoadAndLinkOnRestart(finalList) != 0) {
|
||||
DEBUG_FUNCTION_LINE("Failed to load& link\n");
|
||||
PluginUtils::destroyPluginContainer(finalList);
|
||||
}else{
|
||||
PluginUtils::destroyPluginContainer(finalList);
|
||||
SYSRelaunchTitle(NULL,NULL);
|
||||
}
|
||||
|
||||
free(inflatedData);
|
||||
free(loadAddress);
|
||||
|
||||
return fileSize;
|
||||
}else{
|
||||
DEBUG_FUNCTION_LINE("Failed to parse plugin\n");
|
||||
}
|
||||
free(inflatedData);
|
||||
}
|
||||
|
||||
@ -260,9 +293,8 @@ int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) {
|
||||
res = FSUtils::saveBufferToFile(RPX_TEMP_FILE,loadAddress, fileSize);
|
||||
free(loadAddress);
|
||||
loadedRPX = true;
|
||||
}else{
|
||||
FSUtils::CreateSubfolder(WUPS_TEMP_PLUGIN_PATH);
|
||||
res = FSUtils::saveBufferToFile(WUPS_TEMP_PLUGIN_FILE,loadAddress, fileSize);
|
||||
}else if(loadAddress[0x7] == 0xCA && loadAddress[0x8] == 0xFE && loadAddress[0x9] == 0xDE){
|
||||
OSFatal("Not implemented yet");
|
||||
free(loadAddress);
|
||||
}
|
||||
}
|
||||
@ -298,38 +330,6 @@ int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
|
||||
PluginInformation * newFile = PluginInformationUtils::loadPluginInformation("sd:/wiiu/plugins/temp/temp.mod");
|
||||
if(newFile == NULL){
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<PluginInformation *> alreadyLoaded = PluginInformationUtils::getPluginsLoadedInMemory();
|
||||
|
||||
std::vector<PluginInformation *> newList;
|
||||
|
||||
newList.push_back(newFile);
|
||||
|
||||
for (std::vector<PluginInformation *>::iterator it = alreadyLoaded.begin() ; it != alreadyLoaded.end(); ++it) {
|
||||
PluginInformation * curPlugin = *it;
|
||||
if(curPlugin->getPath().compare(newFile->getPath()) != 0){
|
||||
if(curPlugin->getName().compare(newFile->getName()) == 0 &&
|
||||
curPlugin->getAuthor().compare(newFile->getAuthor()) == 0
|
||||
){
|
||||
DEBUG_FUNCTION_LINE("Name and Author of the new plugin are identical to an old one. Loading the new one! %s %s\n",newFile->getName().c_str(),newFile->getAuthor().c_str());
|
||||
continue;
|
||||
}
|
||||
newList.push_back(curPlugin);
|
||||
}else{
|
||||
DEBUG_FUNCTION_LINE("%s was overridden\n",newFile->getPath().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
PluginInformationUtils::loadAndLinkPluginsOnRestart(newList);
|
||||
|
||||
alreadyLoaded.push_back(newFile);
|
||||
PluginInformationUtils::clearPluginInformation(alreadyLoaded);
|
||||
|
||||
SYSRelaunchTitle(NULL,NULL);
|
||||
|
||||
return fileSize;
|
||||
|
@ -1,294 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include "utils/logger.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include "ipcclient.h"
|
||||
|
||||
int (*ipc_ioctl)(ipcmessage *message) = (int (*)(ipcmessage*)) *(uint32_t*)0x80800000;
|
||||
|
||||
#define ALIGN(align) __attribute__((aligned(align)))
|
||||
|
||||
int32_t doIOCTL(int32_t command, uint32_t *in_buf, uint32_t in_length, uint32_t *io_buf, uint32_t io_length) {
|
||||
ALIGN(0x20) ipcmessage message;
|
||||
|
||||
memset(&message,0,sizeof(message));
|
||||
|
||||
message.command = command;
|
||||
|
||||
message.ioctl.buffer_in = in_buf;
|
||||
message.ioctl.length_in = in_length;
|
||||
message.ioctl.buffer_io = io_buf;
|
||||
message.ioctl.length_io = io_length;
|
||||
|
||||
DEBUG_FUNCTION_LINE("command: %d in_buf %08X size: %d io_buf %08X size: %d \n",command, in_buf,in_length,io_buf,io_length);
|
||||
|
||||
//DCFlushRange(&message, sizeof(ipcmessage));
|
||||
//ICInvalidatRange(&message, sizeof(ipcmessage));
|
||||
|
||||
return ((int (*)(ipcmessage *))((uint32_t*)*((uint32_t*)0x80800000)) )(&message);
|
||||
}
|
||||
|
||||
plugin_loader_handle IPC_Open_Plugin_Loader(uint32_t startAddress, uint32_t endAddress) {
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(8,0x20));
|
||||
if(!io_buf) {
|
||||
return (plugin_loader_handle) NULL;
|
||||
}
|
||||
|
||||
io_buf[0] = startAddress;
|
||||
io_buf[1] = endAddress;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_OPEN_PLUGIN_LOADER, io_buf, 8, io_buf, 4);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return (plugin_loader_handle) NULL;
|
||||
}
|
||||
|
||||
plugin_information_handle result = (plugin_loader_handle) io_buf[0];
|
||||
free(io_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IPC_Close_Plugin_Loader(plugin_loader_handle handle) {
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(4,0x20));
|
||||
if(!io_buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
io_buf[0] = handle;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_CLOSE_PLUGIN_LOADER, io_buf, 4, NULL, 0);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(io_buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t IPC_Get_Plugin_Information(const char * path, plugin_information_handle ** handleList, uint32_t * handleListSize) {
|
||||
uint32_t buffersize = ROUNDUP((128 * sizeof(plugin_information_handle)) + 4,0x20);
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
io_buf[0] = (uint32_t) path;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_FOR_PATH, io_buf, 4, io_buf, buffersize);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return ret;
|
||||
}
|
||||
uint32_t length = io_buf[0];
|
||||
if(handleListSize != NULL) {
|
||||
*handleListSize = length;
|
||||
}
|
||||
|
||||
uint32_t result = -1;
|
||||
|
||||
if(handleList != NULL) {
|
||||
// we create a new buffer so the caller can free it properly
|
||||
uint32_t outbuffersize = ROUNDUP((length * sizeof(plugin_information_handle)),0x20);
|
||||
*handleList = (uint32_t*)memalign(0x20, outbuffersize);
|
||||
if(*handleList != NULL) {
|
||||
result = 0;
|
||||
memcpy(*handleList, &(io_buf[1]), length * sizeof(plugin_information_handle));
|
||||
}
|
||||
}
|
||||
|
||||
free(io_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t IPC_Get_Plugin_Information_Loaded(plugin_information_handle ** handleList, uint32_t * handleListSize) {
|
||||
uint32_t buffersize = ROUNDUP((128 * sizeof(plugin_information_handle)),0x20);
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_LOADED, io_buf, 0, io_buf, buffersize);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return ret;
|
||||
}
|
||||
// DEBUG_FUNCTION_LINE("IPC_Get_Plugin_Information_Loaded was fine\n");
|
||||
|
||||
uint32_t length = io_buf[0];
|
||||
if(handleListSize != NULL) {
|
||||
// DEBUG_FUNCTION_LINE("length set to %d\n", length);
|
||||
*handleListSize = length;
|
||||
}
|
||||
uint32_t result = -1;
|
||||
|
||||
if(handleList != NULL && length > 0) {
|
||||
// we create a new buffer so the caller can free it properly
|
||||
uint32_t outbuffersize = ROUNDUP((length * sizeof(plugin_information_handle)),0x20);
|
||||
*handleList = (uint32_t*)memalign(0x20, outbuffersize);
|
||||
if(*handleList != NULL) {
|
||||
result = 0;
|
||||
memcpy(*handleList, &(io_buf[1]), length * sizeof(plugin_information_handle));
|
||||
}
|
||||
}
|
||||
|
||||
free(io_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t IPC_Get_Plugin_Information_Details(plugin_information_handle * handles, uint32_t handlesize, plugin_information ** informationList, uint32_t * informationListSize) {
|
||||
uint32_t buffersize = ROUNDUP((handlesize * sizeof(plugin_information)),0x20);
|
||||
if(buffersize < 8){
|
||||
buffersize = 8;
|
||||
}
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf) {
|
||||
if(io_buf != NULL) {
|
||||
free(io_buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
io_buf[0] = (uint32_t) handles;
|
||||
io_buf[1] = handlesize;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_GET_INFORMATION_DETAILS, io_buf, 8, io_buf, buffersize);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t result = -1;
|
||||
|
||||
if(informationListSize != NULL) {
|
||||
*informationListSize = handlesize;
|
||||
}
|
||||
|
||||
if(informationList != NULL) {
|
||||
// we create a new buffer so the caller can free it properly
|
||||
uint32_t outbuffersize = ROUNDUP((handlesize * sizeof(plugin_information)),0x20);
|
||||
*informationList = (plugin_information*)memalign(0x20, outbuffersize);
|
||||
if(*informationList != NULL) {
|
||||
result = 0;
|
||||
memcpy(*informationList, &(io_buf[0]), handlesize * sizeof(plugin_information));
|
||||
}
|
||||
}
|
||||
|
||||
free(io_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t IPC_Delete_Plugin_Information(plugin_information_handle handle) {
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, ROUNDUP(4,0x20));
|
||||
if(!io_buf) {
|
||||
if(io_buf != NULL) {
|
||||
free(io_buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
io_buf[0] = (uint32_t) handle;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_DELETE_INFORMATION, io_buf, 4, NULL, 0);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
free(io_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t IPC_Link_Plugin_Information(plugin_loader_handle handle, plugin_information_handle * handleList, uint32_t listSize) {
|
||||
uint32_t buffersize = ROUNDUP((listSize * sizeof(plugin_information_handle)),0x20);
|
||||
uint32_t io_buffersize = ROUNDUP(12,0x20);
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buffersize);
|
||||
uint32_t * buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf || !buf) {
|
||||
if(buf != NULL) {
|
||||
free(buf);
|
||||
}
|
||||
if(io_buf != NULL) {
|
||||
free(io_buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, handleList, listSize * sizeof(plugin_information_handle*));
|
||||
|
||||
io_buf[0] = handle;
|
||||
io_buf[1] = (uint32_t) buf;
|
||||
io_buf[2] = listSize;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION, io_buf, 12, io_buf, io_buffersize);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
int32_t result = (int32_t) io_buf[0];
|
||||
|
||||
free(io_buf);
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t IPC_Link_Plugin_Information_On_Restart(plugin_information_handle * handleList, uint32_t listSize) {
|
||||
uint32_t buffersize = ROUNDUP((listSize * sizeof(plugin_information_handle)),0x20);
|
||||
uint32_t io_buffersize = ROUNDUP(8,0x20);
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, io_buffersize);
|
||||
uint32_t * buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf || !buf) {
|
||||
if(buf != NULL) {
|
||||
free(buf);
|
||||
}
|
||||
if(io_buf != NULL) {
|
||||
free(io_buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, handleList, listSize * sizeof(plugin_information_handle*));
|
||||
|
||||
io_buf[0] = (uint32_t) buf;
|
||||
io_buf[1] = listSize;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION_ON_RESTART, io_buf, 8, io_buf, io_buffersize);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
int32_t result = (int32_t) io_buf[0];
|
||||
|
||||
free(io_buf);
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t IPC_Get_Plugin_Information_For_Filepath(const char * path, plugin_information_handle * handle) {
|
||||
uint32_t buffersize = ROUNDUP((sizeof(plugin_information_handle)),0x20);
|
||||
uint32_t *io_buf = (uint32_t*)memalign(0x20, buffersize);
|
||||
if(!io_buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
io_buf[0] = (uint32_t) path;
|
||||
|
||||
int32_t ret = doIOCTL(IOCTL_PLUGIN_INFORMATION_GET_INFORMATION_FOR_FILEPATH, io_buf, 4, io_buf, 4);
|
||||
if(ret < 0) {
|
||||
free(io_buf);
|
||||
return ret;
|
||||
}
|
||||
*handle = io_buf[0];
|
||||
|
||||
free(io_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
#ifndef __IPC_UTILS_H_
|
||||
#define __IPC_UTILS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define IPC_ERROR_INVALID_NONE 0
|
||||
#define IPC_ERROR_INVALID_SIZE 0xFFFFFFFF
|
||||
#define IPC_ERROR_INVALID_ARG 0xFFFFFFFE
|
||||
#define IPC_ERROR_FAILED_ALLOC 0xFFFFFFFD
|
||||
|
||||
#define IOCTL_OPEN_PLUGIN_LOADER 0x01
|
||||
#define IOCTL_CLOSE_PLUGIN_LOADER 0x02
|
||||
#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_FOR_PATH 0x03
|
||||
#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_LOADED 0x04
|
||||
#define IOCTL_PLUGIN_LOADER_GET_INFORMATION_DETAILS 0x05
|
||||
#define IOCTL_PLUGIN_LOADER_DELETE_INFORMATION 0x06
|
||||
#define IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION 0x07
|
||||
#define IOCTL_PLUGIN_LOADER_LINK_VIA_INFORMATION_ON_RESTART 0x08
|
||||
#define IOCTL_PLUGIN_INFORMATION_GET_INFORMATION_FOR_FILEPATH 0x09
|
||||
|
||||
/* IPC message */
|
||||
typedef struct ipcmessage {
|
||||
uint32_t command;
|
||||
union {
|
||||
struct {
|
||||
uint32_t *buffer_in;
|
||||
uint32_t length_in;
|
||||
uint32_t *buffer_io;
|
||||
uint32_t length_io;
|
||||
} ioctl;
|
||||
};
|
||||
} __attribute__((packed)) ipcmessage;
|
||||
|
||||
typedef uint32_t plugin_information_handle;
|
||||
typedef uint32_t plugin_loader_handle;
|
||||
|
||||
/* plugin_information message */
|
||||
typedef struct plugin_information {
|
||||
plugin_information_handle handle;
|
||||
char path[256];
|
||||
char name[256];
|
||||
char author[256];
|
||||
} plugin_information;
|
||||
|
||||
extern int (*ipc_ioctl)(ipcmessage *message);
|
||||
|
||||
plugin_loader_handle IPC_Open_Plugin_Loader(uint32_t startAddress, uint32_t endAddress);
|
||||
|
||||
bool IPC_Close_Plugin_Loader(plugin_loader_handle handle);
|
||||
|
||||
|
||||
int32_t IPC_Get_Plugin_Information(const char * path, plugin_information_handle ** handleList, uint32_t * handleListSize);
|
||||
int32_t IPC_Get_Plugin_Information_For_Filepath(const char * path, plugin_information_handle * handle);
|
||||
int32_t IPC_Get_Plugin_Information_Loaded(plugin_information_handle ** handleList, uint32_t * handleListSize);
|
||||
int32_t IPC_Get_Plugin_Information_Details(plugin_information_handle * handles, uint32_t handlesize, plugin_information ** informationList, uint32_t * informationListSize);
|
||||
|
||||
int32_t IPC_Delete_Plugin_Information(plugin_information_handle handle);
|
||||
|
||||
int32_t IPC_Link_Plugin_Information(plugin_loader_handle handle, plugin_information_handle * handleList, uint32_t listSize);
|
||||
int32_t IPC_Link_Plugin_Information_On_Restart(plugin_information_handle * handleList, uint32_t listSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user