diff --git a/.gitignore b/.gitignore index 7e81da7..ed730be 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ cmake-build-debug/ .idea/ *.rpx *.txt +*.zip diff --git a/Dockerfile b/Dockerfile index f75b679..064482d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ghcr.io/wiiu-env/devkitppc:20231112 -COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.8.0-dev-20231221-ca17105 /artifacts $DEVKITPRO +COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.8.0-dev-20240302-3b5cc2f /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libwupsbackend:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/librpxloader:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libnotifications:20230621 /artifacts $DEVKITPRO diff --git a/Makefile b/Makefile index 1fbce30..5af750a 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,6 @@ WUMS_ROOT := $(DEVKITPRO)/wums TARGET := wiiload BUILD := build SOURCES := src \ - src/fs \ src/utils DATA := data INCLUDES := src diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..79b40b9 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,71 @@ +#include "config.h" +#include "globals.h" +#include "utils/TcpReceiver.h" +#include "utils/logger.h" +#include "utils/utils.h" +#include +#include +#include + +static void gServerEnabledChanged(ConfigItemBoolean *item, bool newValue) { + if (std::string_view(WIILOAD_ENABLED_STRING) != item->identifier) { + DEBUG_FUNCTION_LINE_WARN("Unexpected identifier in bool callback: %s", item->identifier); + return; + } + DEBUG_FUNCTION_LINE_VERBOSE("New value in gWiiloadServerEnabled: %d", newValue); + gWiiloadServerEnabled = newValue; + + gTcpReceiverThread.reset(); + + if (gWiiloadServerEnabled) { + DEBUG_FUNCTION_LINE("Starting server!"); + gTcpReceiverThread = make_unique_nothrow(4299); + if (gTcpReceiverThread == nullptr) { + DEBUG_FUNCTION_LINE_ERR("Failed to create wiiload thread"); + } + } else { + DEBUG_FUNCTION_LINE("Wiiload server has been stopped!"); + } + // If the value has changed, we store it in the storage. + WUPSStorageError res; + if ((res = WUPSStorageAPI::Store(item->identifier, gWiiloadServerEnabled)) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to store gWiiloadServerEnabled: %s (%d)", WUPSStorageAPI_GetStatusStr(res), res); + } +} + +static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { + try { + WUPSConfigCategory root = WUPSConfigCategory(rootHandle); + + root.add(WUPSConfigItemBoolean::Create(WIILOAD_ENABLED_STRING, "Enable Wiiload", + DEFAULT_WIILOAD_ENABLED_VALUE, gWiiloadServerEnabled, + &gServerEnabledChanged)); + + } catch (std::exception &e) { + OSReport("Exception: %s\n", e.what()); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; + } + return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; +} + +static void ConfigMenuClosedCallback() { + // Save all changes + if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); + } +} + +void InitConfigAndStorage() { + WUPSConfigAPIOptionsV1 configOptions = {.name = "Wiiload Plugin"}; + if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to init config api"); + } + + if (WUPSStorageAPI::GetOrStoreDefault(WIILOAD_ENABLED_STRING, gWiiloadServerEnabled, DEFAULT_WIILOAD_ENABLED_VALUE) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\"", WIILOAD_ENABLED_STRING); + } + + if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to save storage"); + } +} \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..a6f65ae --- /dev/null +++ b/src/config.h @@ -0,0 +1,6 @@ +#pragma once + +#define WIILOAD_ENABLED_STRING "enabled" +#define DEFAULT_WIILOAD_ENABLED_VALUE true + +void InitConfigAndStorage(); \ No newline at end of file diff --git a/src/fs/CFile.cpp b/src/fs/CFile.cpp deleted file mode 100644 index 729f5fe..0000000 --- a/src/fs/CFile.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include -#include -#include -#include - -CFile::CFile() { - iFd = -1; - mem_file = nullptr; - filesize = 0; - pos = 0; -} - -CFile::CFile(const std::string &filepath, eOpenTypes mode) { - iFd = -1; - this->open(filepath, mode); -} - -CFile::CFile(const uint8_t *mem, int32_t size) { - iFd = -1; - this->open(mem, size); -} - -CFile::~CFile() { - this->close(); -} - -int32_t CFile::open(const std::string &filepath, eOpenTypes mode) { - this->close(); - - int32_t openMode = 0; - - // This depend on the devoptab implementation. - // see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference - - switch (mode) { - default: - case ReadOnly: // file must exist - openMode = O_RDONLY; - break; - case WriteOnly: // file will be created / zerod - openMode = O_TRUNC | O_CREAT | O_WRONLY; - break; - case ReadWrite: // file must exist - openMode = O_RDWR; - break; - case Append: // append to file, file will be created if missing. write only - openMode = O_CREAT | O_APPEND | O_WRONLY; - break; - } - - //! Using fopen works only on the first launch as expected - //! on the second launch it causes issues because we don't overwrite - //! the .data sections which is needed for a normal application to re-init - //! this will be added with launching as RPX - iFd = ::open(filepath.c_str(), openMode); - if (iFd < 0) - return iFd; - - - filesize = ::lseek(iFd, 0, SEEK_END); - ::lseek(iFd, 0, SEEK_SET); - - return 0; -} - -int32_t CFile::open(const uint8_t *mem, int32_t size) { - this->close(); - - mem_file = mem; - filesize = size; - - return 0; -} - -void CFile::close() { - if (iFd >= 0) - ::close(iFd); - - iFd = -1; - mem_file = NULL; - filesize = 0; - pos = 0; -} - -int32_t CFile::read(uint8_t *ptr, size_t size) { - if (iFd >= 0) { - int32_t ret = ::read(iFd, ptr, size); - if (ret > 0) - pos += ret; - return ret; - } - - int32_t readsize = size; - - if (readsize > (int64_t) (filesize - pos)) - readsize = filesize - pos; - - if (readsize <= 0) - return readsize; - - if (mem_file != NULL) { - memcpy(ptr, mem_file + pos, readsize); - pos += readsize; - return readsize; - } - - return -1; -} - -int64_t CFile::write(const uint8_t *ptr, size_t size) { - if (iFd >= 0) { - size_t done = 0; - while (done < size) { - int32_t ret = ::write(iFd, ptr, size - done); - if (ret <= 0) - return ret; - - ptr += ret; - done += ret; - pos += ret; - } - return done; - } - - return -1; -} - -int32_t CFile::seek(long int offset, int32_t origin) { - int32_t ret = 0; - int64_t newPos = pos; - - if (origin == SEEK_SET) { - newPos = offset; - } else if (origin == SEEK_CUR) { - newPos += offset; - } else if (origin == SEEK_END) { - newPos = filesize + offset; - } - - if (newPos < 0) { - pos = 0; - } else { - pos = newPos; - } - - if (iFd >= 0) - ret = ::lseek(iFd, pos, SEEK_SET); - - if (mem_file != NULL) { - if (pos > filesize) { - pos = filesize; - } - } - - return ret; -} - -int32_t CFile::fwrite(const char *format, ...) { - char tmp[512]; - tmp[0] = 0; - int32_t result = -1; - - va_list va; - va_start(va, format); - if ((vsprintf(tmp, format, va) >= 0)) { - result = this->write((uint8_t *) tmp, strlen(tmp)); - } - va_end(va); - - - return result; -} diff --git a/src/fs/CFile.hpp b/src/fs/CFile.hpp deleted file mode 100644 index 2a9cce3..0000000 --- a/src/fs/CFile.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -class CFile { -public: - enum eOpenTypes { - ReadOnly, - WriteOnly, - ReadWrite, - Append - }; - - CFile(); - - CFile(const std::string &filepath, eOpenTypes mode); - - CFile(const uint8_t *memory, int32_t memsize); - - virtual ~CFile(); - - int32_t open(const std::string &filepath, eOpenTypes mode); - - int32_t open(const uint8_t *memory, int32_t memsize); - - [[nodiscard]] BOOL isOpen() const { - if (iFd >= 0) - return true; - - if (mem_file) - return true; - - return false; - } - - void close(); - - int32_t read(uint8_t *ptr, size_t size); - - int64_t write(const uint8_t *ptr, size_t size); - - int32_t fwrite(const char *format, ...); - - int32_t seek(long int offset, int32_t origin); - - [[nodiscard]] uint64_t tell() const { - return pos; - }; - - [[nodiscard]] uint64_t size() const { - return filesize; - }; - - void rewind() { - this->seek(0, SEEK_SET); - }; - -protected: - int32_t iFd; - const uint8_t *mem_file; - uint64_t filesize; - uint64_t pos; -}; diff --git a/src/fs/DirList.cpp b/src/fs/DirList.cpp deleted file mode 100644 index a0e24c0..0000000 --- a/src/fs/DirList.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * DirList Class - * for WiiXplorer 2010 - ***************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -DirList::DirList() { - Flags = 0; - Filter = 0; - Depth = 0; -} - -DirList::DirList(const std::string &path, const char *filter, uint32_t flags, uint32_t maxDepth) { - this->LoadPath(path, filter, flags, maxDepth); - this->SortList(); -} - -DirList::~DirList() { - ClearList(); -} - -BOOL DirList::LoadPath(const std::string &folder, const char *filter, uint32_t flags, uint32_t maxDepth) { - if (folder.empty()) return false; - - Flags = flags; - Filter = filter; - Depth = maxDepth; - - std::string folderpath(folder); - uint32_t length = folderpath.size(); - - //! clear path of double slashes - StringTools::RemoveDoubleSlashs(folderpath); - - //! remove last slash if exists - if (length > 0 && folderpath[length - 1] == '/') - folderpath.erase(length - 1); - - //! add root slash if missing - if (folderpath.find('/') == std::string::npos) { - folderpath += '/'; - } - - return InternalLoadPath(folderpath); -} - -BOOL DirList::InternalLoadPath(std::string &folderpath) { - if (folderpath.size() < 3) - return false; - - struct dirent *dirent = nullptr; - DIR *dir = NULL; - - dir = opendir(folderpath.c_str()); - if (dir == NULL) - return false; - - while ((dirent = readdir(dir)) != 0) { - BOOL isDir = dirent->d_type & DT_DIR; - const char *filename = dirent->d_name; - - if (isDir) { - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) - continue; - - if ((Flags & CheckSubfolders) && (Depth > 0)) { - int32_t length = folderpath.size(); - if (length > 2 && folderpath[length - 1] != '/') { - folderpath += '/'; - } - folderpath += filename; - - Depth--; - InternalLoadPath(folderpath); - folderpath.erase(length); - Depth++; - } - - if (!(Flags & Dirs)) - continue; - } else if (!(Flags & Files)) { - continue; - } - - if (Filter) { - char *fileext = strrchr(filename, '.'); - if (!fileext) - continue; - - if (StringTools::strtokcmp(fileext, Filter, ",") == 0) - AddEntrie(folderpath, filename, isDir); - } else { - AddEntrie(folderpath, filename, isDir); - } - } - closedir(dir); - - return true; -} - -void DirList::AddEntrie(const std::string &filepath, const char *filename, BOOL isDir) { - if (!filename) - return; - - int32_t pos = FileInfo.size(); - - FileInfo.resize(pos + 1); - - FileInfo[pos].FilePath = (char *) malloc(filepath.size() + strlen(filename) + 2); - if (!FileInfo[pos].FilePath) { - FileInfo.resize(pos); - return; - } - - sprintf(FileInfo[pos].FilePath, "%s/%s", filepath.c_str(), filename); - FileInfo[pos].isDir = isDir; -} - -void DirList::ClearList() { - for (uint32_t i = 0; i < FileInfo.size(); ++i) { - if (FileInfo[i].FilePath) { - free(FileInfo[i].FilePath); - FileInfo[i].FilePath = nullptr; - } - } - - FileInfo.clear(); - std::vector().swap(FileInfo); -} - -const char *DirList::GetFilename(int32_t ind) const { - if (!valid(ind)) - return ""; - - return StringTools::FullpathToFilename(FileInfo[ind].FilePath); -} - -static BOOL SortCallback(const DirEntry &f1, const DirEntry &f2) { - if (f1.isDir && !(f2.isDir)) return true; - if (!(f1.isDir) && f2.isDir) return false; - - if (f1.FilePath && !f2.FilePath) return true; - if (!f1.FilePath) return false; - - if (strcasecmp(f1.FilePath, f2.FilePath) > 0) - return false; - - return true; -} - -void DirList::SortList() { - if (FileInfo.size() > 1) - std::sort(FileInfo.begin(), FileInfo.end(), SortCallback); -} - -void DirList::SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)) { - if (FileInfo.size() > 1) - std::sort(FileInfo.begin(), FileInfo.end(), SortFunc); -} - -uint64_t DirList::GetFilesize(int32_t index) const { - struct stat st {}; - const char *path = GetFilepath(index); - - if (!path || stat(path, &st) != 0) - return 0; - - return st.st_size; -} - -int32_t DirList::GetFileIndex(const char *filename) const { - if (!filename) - return -1; - - for (int32_t i = 0; i < FileInfo.size(); ++i) { - if (strcasecmp(GetFilename(i), filename) == 0) - return i; - } - - return -1; -} diff --git a/src/fs/DirList.h b/src/fs/DirList.h deleted file mode 100644 index 39f2ee8..0000000 --- a/src/fs/DirList.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * DirList Class - * for WiiXplorer 2010 - ***************************************************************************/ -#pragma once - -#include -#include -#include - -typedef struct { - char *FilePath; - BOOL isDir; -} DirEntry; - -class DirList { -public: - //!Constructor - DirList(); - - //!\param path Path from where to load the filelist of all files - //!\param filter A fileext that needs to be filtered - //!\param flags search/filter flags from the enum - explicit DirList(const std::string &path, const char *filter = nullptr, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); - - //!Destructor - virtual ~DirList(); - - //! Load all the files from a directory - BOOL LoadPath(const std::string &path, const char *filter = nullptr, uint32_t flags = Files | Dirs, uint32_t maxDepth = 0xffffffff); - - //! Get a filename of the list - //!\param list index - [[nodiscard]] const char *GetFilename(int32_t index) const; - - //! Get the a filepath of the list - //!\param list index - [[nodiscard]] const char *GetFilepath(int32_t index) const { - if (!valid(index)) return ""; - else - return FileInfo[index].FilePath; - } - - //! Get the a filesize of the list - //!\param list index - [[nodiscard]] uint64_t GetFilesize(int32_t index) const; - - //! Is index a dir or a file - //!\param list index - [[nodiscard]] BOOL IsDir(int32_t index) const { - if (!valid(index)) return false; - return FileInfo[index].isDir; - }; - - //! Get the filecount of the whole list - [[nodiscard]] int32_t GetFilecount() const { - return FileInfo.size(); - }; - - //! Sort list by filepath - void SortList(); - - //! Custom sort command for custom sort functions definitions - void SortList(BOOL (*SortFunc)(const DirEntry &a, const DirEntry &b)); - - //! Get the index of the specified filename - int32_t GetFileIndex(const char *filename) const; - - //! Enum for search/filter flags - enum { - Files = 0x01, - Dirs = 0x02, - CheckSubfolders = 0x08, - }; - -protected: - // Internal parser - BOOL InternalLoadPath(std::string &path); - - //!Add a list entrie - void AddEntrie(const std::string &filepath, const char *filename, BOOL isDir); - - //! Clear the list - void ClearList(); - - //! Check if valid pos is requested - [[nodiscard]] inline BOOL valid(uint32_t pos) const { - return (pos < FileInfo.size()); - }; - - uint32_t Flags{}; - uint32_t Depth{}; - const char *Filter{}; - std::vector FileInfo; -}; diff --git a/src/fs/FSUtils.cpp b/src/fs/FSUtils.cpp deleted file mode 100644 index b7037b5..0000000 --- a/src/fs/FSUtils.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) { - //! always initialze input - *inbuffer = nullptr; - if (size) - *size = 0; - - int32_t iFd = open(filepath, O_RDONLY); - if (iFd < 0) - return -1; - - uint32_t filesize = lseek(iFd, 0, SEEK_END); - lseek(iFd, 0, SEEK_SET); - - auto *buffer = (uint8_t *) malloc(filesize); - if (buffer == nullptr) { - close(iFd); - return -2; - } - - uint32_t blocksize = 0x4000; - uint32_t done = 0; - int32_t readBytes = 0; - - while (done < filesize) { - if (done + blocksize > filesize) { - blocksize = filesize - done; - } - readBytes = read(iFd, buffer + done, blocksize); - if (readBytes <= 0) - break; - done += readBytes; - } - - close(iFd); - - if (done != filesize) { - free(buffer); - buffer = nullptr; - return -3; - } - - *inbuffer = buffer; - - //! sign is optional input - if (size) { - *size = filesize; - } - - return filesize; -} - -int32_t FSUtils::CheckFile(const char *filepath) { - if (!filepath) - return 0; - - struct stat filestat {}; - - char dirnoslash[strlen(filepath) + 2]; - snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath); - - while (dirnoslash[strlen(dirnoslash) - 1] == '/') - dirnoslash[strlen(dirnoslash) - 1] = '\0'; - - char *notRoot = strrchr(dirnoslash, '/'); - if (!notRoot) { - strcat(dirnoslash, "/"); - } - - if (stat(dirnoslash, &filestat) == 0) - return 1; - - return 0; -} - -int32_t FSUtils::CreateSubfolder(const char *fullpath) { - if (!fullpath) - return 0; - - int32_t result = 0; - - char dirnoslash[strlen(fullpath) + 1]; - strcpy(dirnoslash, fullpath); - - int32_t pos = strlen(dirnoslash) - 1; - while (dirnoslash[pos] == '/') { - dirnoslash[pos] = '\0'; - pos--; - } - - if (CheckFile(dirnoslash)) { - return 1; - } else { - char parentpath[strlen(dirnoslash) + 2]; - strcpy(parentpath, dirnoslash); - char *ptr = strrchr(parentpath, '/'); - - if (!ptr) { - //!Device root directory (must be with '/') - strcat(parentpath, "/"); - struct stat filestat {}; - if (stat(parentpath, &filestat) == 0) - return 1; - - return 0; - } - - ptr++; - ptr[0] = '\0'; - - result = CreateSubfolder(parentpath); - } - - if (!result) - return 0; - - if (mkdir(dirnoslash, 0777) == -1) { - return 0; - } - - return 1; -} - -BOOL FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) { - CFile file(path, CFile::WriteOnly); - if (!file.isOpen()) { - DEBUG_FUNCTION_LINE_ERR("Failed to open %s\n", path); - return false; - } - if (file.write((const uint8_t *) buffer, size) != size) { - DEBUG_FUNCTION_LINE_ERR("Failed to write file %s\n", path); - file.close(); - return false; - } - file.close(); - return true; -} diff --git a/src/fs/FSUtils.h b/src/fs/FSUtils.h deleted file mode 100644 index 489a3d0..0000000 --- a/src/fs/FSUtils.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __FS_UTILS_H_ -#define __FS_UTILS_H_ - -#include - -class FSUtils { -public: - static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size); - - //! todo: C++ class - static int32_t CreateSubfolder(const char *fullpath); - - static int32_t CheckFile(const char *filepath); - - static BOOL saveBufferToFile(const char *path, void *buffer, uint32_t size); -}; - -#endif // __FS_UTILS_H_ diff --git a/src/globals.cpp b/src/globals.cpp index 2e616ac..e972937 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -1,5 +1,5 @@ #include "globals.h" -bool gLibRPXLoaderInitDone __attribute__((section(".data"))) = false; -bool gWiiloadServerEnabled __attribute__((section(".data"))) = true; -bool gNotificationModuleLoaded __attribute__((section(".data"))) = true; \ No newline at end of file +bool gLibRPXLoaderInitDone = false; +std::unique_ptr gTcpReceiverThread = nullptr; +bool gWiiloadServerEnabled = true; \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index a8921bb..a004dc4 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,5 +1,7 @@ -#include +#include "utils/TcpReceiver.h" +#include +#include extern bool gLibRPXLoaderInitDone; +extern std::unique_ptr gTcpReceiverThread; extern bool gWiiloadServerEnabled; -extern bool gNotificationModuleLoaded; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9f926a4..b2fefd8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ #include "main.h" +#include "config.h" #include "globals.h" #include "utils/TcpReceiver.h" #include "utils/logger.h" +#include "utils/utils.h" #include #include #include @@ -12,18 +14,11 @@ WUPS_PLUGIN_NAME("Wiiload"); WUPS_PLUGIN_DESCRIPTION("Wiiload Server"); WUPS_PLUGIN_VERSION(VERSION_FULL); WUPS_PLUGIN_AUTHOR("Maschell"); -WUPS_PLUGIN_LICENSE("GPL"); +WUPS_PLUGIN_LICENSE("GPL3"); WUPS_USE_WUT_DEVOPTAB(); -WUPS_USE_STORAGE("wiiload"); // Unqiue id for the storage api -#define WIILOAD_ENABLED_STRING "enabled" - -std::unique_ptr tcpReceiverThread = nullptr; - - -static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle); -static void ConfigMenuClosedCallback(); +WUPS_USE_STORAGE("wiiload"); // Unique id for the storage api INITIALIZE_PLUGIN() { RPXLoaderStatus error; @@ -32,77 +27,16 @@ INITIALIZE_PLUGIN() { } else { gLibRPXLoaderInitDone = true; } + gTcpReceiverThread.reset(); NotificationModuleStatus res; if ((res = NotificationModule_InitLibrary()) != NOTIFICATION_MODULE_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE_ERR("Failed to init NotificationModule: %s", NotificationModule_GetStatusStr(res)); - gNotificationModuleLoaded = false; } else { NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 10.0f); - gNotificationModuleLoaded = true; } - WUPSConfigAPIOptionsV1 configOptions = {.name = "Wiiload Plugin"}; - if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to init config api"); - } - - if (WUPSStorageAPI::GetOrStoreDefault(WIILOAD_ENABLED_STRING, gWiiloadServerEnabled, true) != WUPS_STORAGE_ERROR_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to get or create item \"%s\"", WIILOAD_ENABLED_STRING); - } - - if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to save storage"); - } -} - -void gServerEnabledChanged(ConfigItemBoolean *item, bool newValue) { - if (std::string_view(WIILOAD_ENABLED_STRING) != item->identifier) { - DEBUG_FUNCTION_LINE_WARN("Unexpected identifier in bool callback: %s", item->identifier); - return; - } - DEBUG_FUNCTION_LINE_VERBOSE("New value in gWiiloadServerEnabled: %d", newValue); - gWiiloadServerEnabled = newValue; - - tcpReceiverThread.reset(); - - if (gWiiloadServerEnabled) { - DEBUG_FUNCTION_LINE("Starting server!"); - tcpReceiverThread = std::make_unique(4299); - - if (tcpReceiverThread == nullptr) { - DEBUG_FUNCTION_LINE_ERR("Failed to create wiiload thread"); - } - } else { - DEBUG_FUNCTION_LINE("Wiiload server has been stopped!"); - } - // If the value has changed, we store it in the storage. - auto res = WUPSStorageAPI::Store(item->identifier, gWiiloadServerEnabled); - if (res != WUPS_STORAGE_ERROR_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to store gWiiloadServerEnabled: %s", WUPSStorageAPI_GetStatusStr(res)); - } -} - -static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { - try { - WUPSConfigCategory root = WUPSConfigCategory(rootHandle); - - root.add(WUPSConfigItemBoolean::Create(WIILOAD_ENABLED_STRING, "Enable Wiiload", - true, gWiiloadServerEnabled, - &gServerEnabledChanged)); - - } catch (std::exception &e) { - OSReport("Exception T_T : %s\n", e.what()); - return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; - } - return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; -} - -static void ConfigMenuClosedCallback() { - // Save all changes - if (WUPSStorageAPI::SaveStorage() != WUPS_STORAGE_ERROR_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); - } + InitConfigAndStorage(); } /* Entry point */ @@ -110,8 +44,8 @@ ON_APPLICATION_START() { initLogging(); if (gWiiloadServerEnabled) { DEBUG_FUNCTION_LINE("Start wiiload thread"); - tcpReceiverThread = std::make_unique(4299); - if (tcpReceiverThread == nullptr) { + gTcpReceiverThread = make_unique_nothrow(4299); + if (gTcpReceiverThread == nullptr) { DEBUG_FUNCTION_LINE_ERR("Failed to create wiiload thread"); } } else { @@ -120,6 +54,6 @@ ON_APPLICATION_START() { } ON_APPLICATION_ENDS() { - tcpReceiverThread.reset(); + gTcpReceiverThread.reset(); deinitLogging(); } \ No newline at end of file diff --git a/src/utils/FSUtils.cpp b/src/utils/FSUtils.cpp new file mode 100644 index 0000000..428c589 --- /dev/null +++ b/src/utils/FSUtils.cpp @@ -0,0 +1,110 @@ +#include "FSUtils.h" +#include "logger.h" +#include +#include +#include +#include + +bool FSUtils::CheckFile(const char *filepath) { + if (!filepath || strlen(filepath) == 0) { + return false; + } + + struct stat filestat {}; + + char dirnoslash[strlen(filepath) + 2]; + snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath); + + while (dirnoslash[strlen(dirnoslash) - 1] == '/') { + dirnoslash[strlen(dirnoslash) - 1] = '\0'; + } + + char *notRoot = strrchr(dirnoslash, '/'); + if (!notRoot) { + strcat(dirnoslash, "/"); + } + + if (stat(dirnoslash, &filestat) == 0) { + return true; + } + + return false; +} + +bool FSUtils::CreateSubfolder(const char *fullpath) { + if (!fullpath || strlen(fullpath) == 0) { + return false; + } + + bool result = false; + + char dirnoslash[strlen(fullpath) + 1]; + strcpy(dirnoslash, fullpath); + + auto pos = strlen(dirnoslash) - 1; + while (dirnoslash[pos] == '/') { + dirnoslash[pos] = '\0'; + pos--; + } + + if (CheckFile(dirnoslash)) { + return true; + } else { + char parentpath[strlen(dirnoslash) + 2]; + strcpy(parentpath, dirnoslash); + char *ptr = strrchr(parentpath, '/'); + + if (!ptr) { + //!Device root directory (must be with '/') + strcat(parentpath, "/"); + struct stat filestat {}; + if (stat(parentpath, &filestat) == 0) { + return true; + } + + return false; + } + + ptr++; + ptr[0] = '\0'; + + result = CreateSubfolder(parentpath); + } + + if (!result) { + return false; + } + + if (mkdir(dirnoslash, 0777) == -1) { + return false; + } + + return true; +} + +bool FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) { + int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY); + if (fd < 0) { + DEBUG_FUNCTION_LINE_ERR("Failed to open %s. %d", path, fd); + return -1; + } + auto sizeToWrite = size; + auto *ptr = buffer; + int32_t curResult; + int64_t totalSizeWritten = 0; + while (sizeToWrite > 0) { + curResult = write(fd, ptr, sizeToWrite); + if (curResult < 0) { + close(fd); + return false; + } + if (curResult == 0) { + break; + } + ptr = (void *) (((uint32_t) ptr) + curResult); + totalSizeWritten += curResult; + sizeToWrite -= curResult; + } + close(fd); + return totalSizeWritten == size; +} diff --git a/src/utils/FSUtils.h b/src/utils/FSUtils.h new file mode 100644 index 0000000..eb796c1 --- /dev/null +++ b/src/utils/FSUtils.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class FSUtils { +public: + static bool CreateSubfolder(const char *fullpath); + + static bool CheckFile(const char *filepath); + + static bool saveBufferToFile(const char *path, void *buffer, uint32_t size); +}; \ No newline at end of file diff --git a/src/utils/StringTools.cpp b/src/utils/StringTools.cpp deleted file mode 100644 index 8cd2de3..0000000 --- a/src/utils/StringTools.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * for WiiXplorer 2010 - ***************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -BOOL StringTools::EndsWith(const std::string &a, const std::string &b) { - if (b.size() > a.size()) return false; - return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin()); -} - -const char *StringTools::byte_to_binary(int32_t x) { - static char b[9]; - b[0] = '\0'; - - int32_t z; - for (z = 128; z > 0; z >>= 1) { - strcat(b, ((x & z) == z) ? "1" : "0"); - } - - return b; -} - -std::string StringTools::removeCharFromString(std::string &input, char toBeRemoved) { - std::string output = input; - size_t position; - while (1) { - position = output.find(toBeRemoved); - if (position == std::string::npos) - break; - output.erase(position, 1); - } - return output; -} - -const char *StringTools::fmt(const char *format, ...) { - static char strChar[512]; - strChar[0] = 0; - - va_list va; - va_start(va, format); - if ((vsprintf(strChar, format, va) >= 0)) { - va_end(va); - return (const char *) strChar; - } - va_end(va); - - return NULL; -} - -const wchar_t *StringTools::wfmt(const char *format, ...) { - static char tmp[512]; - static wchar_t strWChar[512]; - strWChar[0] = 0; - tmp[0] = 0; - - if (!format) - return (const wchar_t *) strWChar; - - if (strcmp(format, "") == 0) - return (const wchar_t *) strWChar; - - va_list va; - va_start(va, format); - if ((vsprintf(tmp, format, va) >= 0)) { - int bt; - int32_t strlength = strlen(tmp); - bt = mbstowcs(strWChar, tmp, (strlength < 512) ? strlength : 512); - - if (bt > 0) { - strWChar[bt] = 0; - return (const wchar_t *) strWChar; - } - } - va_end(va); - - return NULL; -} - -int32_t StringTools::strprintf(std::string &str, const char *format, ...) { - static char tmp[512]; - tmp[0] = 0; - int32_t result = 0; - - va_list va; - va_start(va, format); - if ((vsprintf(tmp, format, va) >= 0)) { - str = tmp; - result = str.size(); - } - va_end(va); - - return result; -} - -std::string StringTools::strfmt(const char *format, ...) { - std::string str; - static char tmp[512]; - tmp[0] = 0; - - va_list va; - va_start(va, format); - if ((vsprintf(tmp, format, va) >= 0)) { - str = tmp; - } - va_end(va); - - return str; -} - -BOOL StringTools::char2wchar_t(const char *strChar, wchar_t *dest) { - if (!strChar || !dest) - return false; - - int bt; - bt = mbstowcs(dest, strChar, strlen(strChar)); - if (bt > 0) { - dest[bt] = 0; - return true; - } - - return false; -} - -int32_t StringTools::strtokcmp(const char *string, const char *compare, const char *separator) { - if (!string || !compare) - return -1; - - char TokCopy[512]; - strncpy(TokCopy, compare, sizeof(TokCopy)); - TokCopy[511] = '\0'; - - char *strTok = strtok(TokCopy, separator); - - while (strTok != NULL) { - if (strcasecmp(string, strTok) == 0) { - return 0; - } - strTok = strtok(NULL, separator); - } - - return -1; -} - -int32_t StringTools::strextcmp(const char *string, const char *extension, char seperator) { - if (!string || !extension) - return -1; - - char *ptr = strrchr(string, seperator); - if (!ptr) - return -1; - - return strcasecmp(ptr + 1, extension); -} - - -std::vector StringTools::stringSplit(const std::string &inValue, const std::string &splitter) { - std::string value = inValue; - std::vector result; - while (true) { - uint32_t index = value.find(splitter); - if (index == std::string::npos) { - result.push_back(value); - break; - } - std::string first = value.substr(0, index); - result.push_back(first); - if (index + splitter.size() == value.length()) { - result.push_back(""); - break; - } - if (index + splitter.size() > value.length()) { - break; - } - value = value.substr(index + splitter.size(), value.length()); - } - return result; -} diff --git a/src/utils/StringTools.h b/src/utils/StringTools.h deleted file mode 100644 index fa53b7f..0000000 --- a/src/utils/StringTools.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 - * by Dimok - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any - * purpose, including commercial applications, and to alter it and - * redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you - * must not claim that you wrote the original software. If you use - * this software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and - * must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - * for WiiXplorer 2010 - ***************************************************************************/ -#ifndef __STRING_TOOLS_H -#define __STRING_TOOLS_H - -#include -#include -#include - -class StringTools { -public: - static BOOL EndsWith(const std::string &a, const std::string &b); - - static const char *byte_to_binary(int32_t x); - - static std::string removeCharFromString(std::string &input, char toBeRemoved); - - static const char *fmt(const char *format, ...); - - static const wchar_t *wfmt(const char *format, ...); - - static int32_t strprintf(std::string &str, const char *format, ...); - - static std::string strfmt(const char *format, ...); - - static BOOL char2wchar_t(const char *src, wchar_t *dest); - - static int32_t strtokcmp(const char *string, const char *compare, const char *separator); - - static int32_t strextcmp(const char *string, const char *extension, char seperator); - - static const char *FullpathToFilename(const char *path) { - if (!path) return path; - - const char *ptr = path; - const char *Filename = ptr; - - while (*ptr != '\0') { - if (ptr[0] == '/' && ptr[1] != '\0') - Filename = ptr + 1; - - ++ptr; - } - - return Filename; - } - - static void RemoveDoubleSlashs(std::string &str) { - uint32_t length = str.size(); - - //! clear path of double slashes - for (uint32_t i = 1; i < length; ++i) { - if (str[i - 1] == '/' && str[i] == '/') { - str.erase(i, 1); - i--; - length--; - } - } - } - - static std::vector stringSplit(const std::string &value, const std::string &splitter); -}; - -#endif /* __STRING_TOOLS_H */ diff --git a/src/utils/TcpReceiver.cpp b/src/utils/TcpReceiver.cpp index a1e5528..152da3c 100644 --- a/src/utils/TcpReceiver.cpp +++ b/src/utils/TcpReceiver.cpp @@ -1,5 +1,5 @@ #include "TcpReceiver.h" -#include "fs/FSUtils.h" +#include "FSUtils.h" #include "globals.h" #include "utils/net.h" #include "utils/utils.h" @@ -16,7 +16,7 @@ #include #include -#define RPX_TEMP_PATH "fs:/vol/external01/wiiu/apps/" +#define APPS_TEMP_PATH "fs:/vol/external01/wiiu/apps/" #define RPX_TEMP_FILE "fs:/vol/external01/wiiu/apps/temp.rpx" #define WUHB_TEMP_FILE "fs:/vol/external01/wiiu/apps/temp.wuhb" #define WUHB_TEMP_FILE_2 "fs:/vol/external01/wiiu/apps/temp2.wuhb" @@ -52,14 +52,15 @@ bool TcpReceiver::createSocket() { bindAddress.sin_port = serverPort; bindAddress.sin_addr.s_addr = INADDR_ANY; - int32_t ret; if ((ret = bind(serverSocket, (struct sockaddr *) &bindAddress, 16)) < 0) { + DEBUG_FUNCTION_LINE_WARN("Failed to bind socket: %d errno: %d", ret, errno); cleanupSocket(); return false; } if ((ret = listen(serverSocket, 1)) < 0) { + DEBUG_FUNCTION_LINE_WARN("Failed to bind socket: %d errno: %d", ret, errno); cleanupSocket(); return false; } @@ -86,18 +87,45 @@ void TcpReceiver::executeThread() { } continue; } - struct sockaddr_in clientAddr {}; - memset(&clientAddr, 0, sizeof(clientAddr)); - - len = 16; - int32_t clientSocket = accept(serverSocket, (struct sockaddr *) &clientAddr, &len); + struct sockaddr_in clientAddr = {}; + len = 16; + int32_t clientSocket = accept(serverSocket, (struct sockaddr *) &clientAddr, &len); if (clientSocket >= 0) { uint32_t ipAddress = clientAddr.sin_addr.s_addr; DEBUG_FUNCTION_LINE("Waiting for wiiload connection"); - int32_t result = loadToMemory(clientSocket, ipAddress); + auto result = loadToMemory(clientSocket, ipAddress); close(clientSocket); - if (result >= 0) { + switch (result) { + case SUCCESS: + break; + case FILE_UNCOMPRESS_ERROR: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to decrompress recieved data. Launching will be aborted."); + break; + case NOT_ENOUGH_MEMORY: + NotificationModule_AddErrorNotification("Wiiload plugin: Not enough memory. Launching will be aborted."); + break; + case RECV_ERROR: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to receive data. Launching will be aborted."); + break; + case UNSUPPORTED_FORMAT: + NotificationModule_AddErrorNotification("Wiiload plugin: Tried to load an unsupported file. Launching will be aborted."); + break; + case PLUGIN_PARSE_FAILED: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to load plugin. Maybe unsupported version? Launching will be aborted."); + break; + case PLUGIN_LOAD_LINK_FAILED: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to load plugins. Maybe unsupported version? Launching will be aborted."); + break; + case FILE_SAVE_BUFFER_ERROR: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to save file to the sd card. Launching will be aborted."); + break; + case LAUNCH_FAILED: + NotificationModule_AddErrorNotification("Wiiload plugin: Failed to launch homebrew. Launching will be aborted."); + break; + } + + if (result == SUCCESS) { break; } } else if (!exitRequested) { @@ -113,26 +141,203 @@ void TcpReceiver::executeThread() { DEBUG_FUNCTION_LINE("Stopping wiiload server."); } -int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) { +TcpReceiver::eLoadResults TcpReceiver::tryLoadWUHB(void *data, uint32_t fileSize, std::string &loadedPathOut) { + if (memcmp(data, "WUHB", 4) == 0) { + DEBUG_FUNCTION_LINE("Try to load a .wuhb"); + if (!FSUtils::CreateSubfolder(APPS_TEMP_PATH)) { + DEBUG_FUNCTION_LINE_WARN("Failed to create directory: %s", APPS_TEMP_PATH); + return FILE_SAVE_BUFFER_ERROR; + } + if (FSUtils::saveBufferToFile(WUHB_TEMP_FILE, data, fileSize)) { + loadedPathOut = WUHB_TEMP_FILE_EX; + } else if (FSUtils::saveBufferToFile(WUHB_TEMP_FILE_2, data, fileSize)) { + loadedPathOut = WUHB_TEMP_FILE_2_EX; + } else { + DEBUG_FUNCTION_LINE_WARN("Failed to save .wuhb file to the sd card. Launching will be aborted."); + return FILE_SAVE_BUFFER_ERROR; + } + return SUCCESS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Loaded data is not a WUHB"); + return UNSUPPORTED_FORMAT; +} + +TcpReceiver::eLoadResults TcpReceiver::tryLoadRPX(uint8_t *data, uint32_t fileSize, std::string &loadedPathOut) { + if (data[0x7] == 0xCA && data[0x8] == 0xFE && data[0x9] != 0x50 && data[0xA] != 0x4C) { + DEBUG_FUNCTION_LINE("Try to load a .rpx"); + if (!FSUtils::CreateSubfolder(APPS_TEMP_PATH)) { + DEBUG_FUNCTION_LINE_WARN("Failed to create directory: %s", APPS_TEMP_PATH); + return FILE_SAVE_BUFFER_ERROR; + } + if (FSUtils::saveBufferToFile(RPX_TEMP_FILE, data, fileSize)) { + loadedPathOut = RPX_TEMP_FILE_EX; + } else { + DEBUG_FUNCTION_LINE_WARN("Failed to save .rpx file to the sd card. Launching will be aborted."); + return FILE_SAVE_BUFFER_ERROR; + } + return SUCCESS; + } + DEBUG_FUNCTION_LINE_VERBOSE("Loaded data is not a RPX"); + return UNSUPPORTED_FORMAT; +} + +TcpReceiver::eLoadResults TcpReceiver::tryLoadWPS(uint8_t *data, uint32_t fileSize) { + if (data[0x7] == 0xCA && data[0x8] == 0xFE && data[0x9] == 0x50 && data[0xA] == 0x4C) { + auto newContainer = WUPSBackend::PluginUtils::getPluginForBuffer((char *) data, fileSize); + if (newContainer) { + auto plugins = WUPSBackend::PluginUtils::getLoadedPlugins(32); + + auto &metaInformation = newContainer.value()->getMetaInformation(); + + // remove plugins with the same name and author as our new plugin + plugins.erase(std::remove_if(plugins.begin(), plugins.end(), + [metaInformation](auto &plugin) { + return plugin->getMetaInformation()->getName() == metaInformation->getName() && + plugin->getMetaInformation()->getAuthor() == metaInformation->getAuthor(); + }), + plugins.end()); + + // add the new plugin + plugins.push_back(std::move(newContainer.value())); + +#ifdef VERBOSE_DEBUG + for (auto &plugin : plugins) { + DEBUG_FUNCTION_LINE_VERBOSE("name: %s", plugin->getMetaInformation()->getName().c_str()); + DEBUG_FUNCTION_LINE_VERBOSE("author: %s", plugin->getMetaInformation()->getAuthor().c_str()); + DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", plugin->getPluginData()->getHandle()); + DEBUG_FUNCTION_LINE_VERBOSE("===="); + } +#endif + + if (WUPSBackend::PluginUtils::LoadAndLinkOnRestart(plugins) != 0) { + DEBUG_FUNCTION_LINE_ERR("WUPSBackend::PluginUtils::LoadAndLinkOnRestart failed"); + return PLUGIN_LOAD_LINK_FAILED; + } + return SUCCESS; + } else { + DEBUG_FUNCTION_LINE_ERR("Failed to parse plugin for buffer: %08X size %d", data, fileSize); + return PLUGIN_PARSE_FAILED; + } + } + DEBUG_FUNCTION_LINE_VERBOSE("Loaded data is not a plugin"); + return UNSUPPORTED_FORMAT; +} + +TcpReceiver::eLoadResults TcpReceiver::loadBinary(void *data, uint32_t fileSize) { + std::string loadedPath; + eLoadResults error; + if ((error = tryLoadWUHB(data, fileSize, loadedPath)) != UNSUPPORTED_FORMAT || (error = tryLoadRPX((uint8_t *) data, fileSize, loadedPath)) != UNSUPPORTED_FORMAT) { + if (error == SUCCESS) { + RPXLoaderStatus launchRes; + if ((launchRes = RPXLoader_LaunchHomebrew(loadedPath.c_str())) != RPX_LOADER_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to start %s %s", loadedPath.c_str(), RPXLoader_GetStatusStr(launchRes)); + return LAUNCH_FAILED; + } + } + + return error; + } else if ((error = tryLoadWPS((uint8_t *) data, fileSize)) != UNSUPPORTED_FORMAT) { + if (error == SUCCESS) { + _SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr); + } + return error; + } + return UNSUPPORTED_FORMAT; +} + +TcpReceiver::eLoadResults TcpReceiver::uncompressIfNeeded(const uint8_t *haxx, uint32_t fileSize, uint32_t fileSizeUnc, std::unique_ptr &&in_data, std::unique_ptr &out_data, uint32_t &fileSizeOut) { + // Do we need to unzip this thing? + if (haxx[4] > 0 || haxx[5] > 4) { + std::unique_ptr inflatedData; + uint8_t *in_data_raw = in_data.get(); + // We need to unzip... + if (in_data_raw[0] == 'P' && in_data_raw[1] == 'K' && in_data_raw[2] == 0x03 && in_data_raw[3] == 0x04) { + // Section is compressed, inflate + inflatedData = make_unique_nothrow(fileSizeUnc); + if (!inflatedData) { + DEBUG_FUNCTION_LINE_ERR("malloc failed"); + return NOT_ENOUGH_MEMORY; + } + + int32_t ret; + z_stream s = {}; + + s.zalloc = Z_NULL; + s.zfree = Z_NULL; + s.opaque = Z_NULL; + + ret = inflateInit(&s); + if (ret != Z_OK) { + DEBUG_FUNCTION_LINE_ERR("inflateInit failed %i", ret); + return FILE_UNCOMPRESS_ERROR; + } + + s.avail_in = fileSize; + s.next_in = (Bytef *) inflatedData.get(); + + s.avail_out = fileSizeUnc; + s.next_out = (Bytef *) inflatedData.get(); + + ret = inflate(&s, Z_FINISH); + if (ret != Z_OK && ret != Z_STREAM_END) { + DEBUG_FUNCTION_LINE_ERR("inflate failed %i", ret); + return FILE_UNCOMPRESS_ERROR; + } + + inflateEnd(&s); + fileSizeOut = fileSizeUnc; + out_data = std::move(inflatedData); + return SUCCESS; + } else { + // Section is compressed, inflate + inflatedData = make_unique_nothrow(fileSizeUnc); + if (!inflatedData) { + DEBUG_FUNCTION_LINE_ERR("malloc failed"); + return NOT_ENOUGH_MEMORY; + } + + uLongf f = fileSizeUnc; + int32_t result = uncompress((Bytef *) inflatedData.get(), &f, (Bytef *) in_data_raw, fileSize); + if (result != Z_OK) { + DEBUG_FUNCTION_LINE_ERR("uncompress failed %i", result); + return FILE_UNCOMPRESS_ERROR; + } + + fileSizeUnc = f; + fileSizeOut = fileSizeUnc; + out_data = std::move(inflatedData); + return SUCCESS; + } + } + fileSizeOut = fileSize; + out_data = std::move(in_data); + return SUCCESS; +} + +TcpReceiver::eLoadResults TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) { DEBUG_FUNCTION_LINE("Loading file from ip %08X", ipAddress); - uint32_t fileSize = 0; - uint32_t fileSizeUnc = 0; - unsigned char haxx[8]; - memset(haxx, 0, sizeof(haxx)); + uint32_t fileSize = 0; + uint32_t fileSizeUnc = 0; + unsigned char haxx[8] = {}; //skip haxx - recvwait(clientSocket, haxx, sizeof(haxx)); - recvwait(clientSocket, (unsigned char *) &fileSize, sizeof(fileSize)); + if (recvwait(clientSocket, haxx, sizeof(haxx)) != 0) { + return RECV_ERROR; + } + if (recvwait(clientSocket, (unsigned char *) &fileSize, sizeof(fileSize)) != 0) { + return RECV_ERROR; + } if (haxx[4] > 0 || haxx[5] > 4) { - recvwait(clientSocket, (unsigned char *) &fileSizeUnc, sizeof(fileSizeUnc)); // Compressed protocol, read another 4 bytes + if (recvwait(clientSocket, (unsigned char *) &fileSizeUnc, sizeof(fileSizeUnc)) != 0) { // Compressed protocol, read another 4 bytes + return RECV_ERROR; + } } uint32_t bytesRead = 0; - auto *loadAddress = (unsigned char *) memalign(0x40, fileSize); - if (!loadAddress) { - OSSleepTicks(OSSecondsToTicks(1)); + auto receivedData = make_unique_nothrow(fileSize); + if (!receivedData) { return NOT_ENOUGH_MEMORY; } @@ -142,7 +347,7 @@ int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) { if (blockSize > (fileSize - bytesRead)) blockSize = fileSize - bytesRead; - int32_t ret = recv(clientSocket, loadAddress + bytesRead, blockSize, 0); + int32_t ret = recv(clientSocket, receivedData.get() + bytesRead, blockSize, 0); if (ret <= 0) { DEBUG_FUNCTION_LINE_ERR("Failed to receive file"); break; @@ -152,202 +357,16 @@ int32_t TcpReceiver::loadToMemory(int32_t clientSocket, uint32_t ipAddress) { } if (bytesRead != fileSize) { - free(loadAddress); DEBUG_FUNCTION_LINE_ERR("File loading not finished, %i of %i bytes received", bytesRead, fileSize); - return FILE_READ_ERROR; + return RECV_ERROR; } - bool res = false; - bool loadedRPX = false; - const char *file_path = nullptr; - // Do we need to unzip this thing? - if (haxx[4] > 0 || haxx[5] > 4) { - unsigned char *inflatedData; - - // We need to unzip... - if (loadAddress[0] == 'P' && loadAddress[1] == 'K' && loadAddress[2] == 0x03 && loadAddress[3] == 0x04) { - //! TODO: - //! mhmm this is incorrect, it has to parse the zip - - // Section is compressed, inflate - inflatedData = (unsigned char *) malloc(fileSizeUnc); - if (!inflatedData) { - DEBUG_FUNCTION_LINE_ERR("Failed to malloc data"); - free(loadAddress); - - return NOT_ENOUGH_MEMORY; - } - - int32_t ret = 0; - z_stream s; - memset(&s, 0, sizeof(s)); - - s.zalloc = Z_NULL; - s.zfree = Z_NULL; - s.opaque = Z_NULL; - - ret = inflateInit(&s); - if (ret != Z_OK) { - DEBUG_FUNCTION_LINE_ERR("inflateInit failed %i", ret); - free(loadAddress); - free(inflatedData); - - return FILE_READ_ERROR; - } - - s.avail_in = fileSize; - s.next_in = (Bytef *) (&loadAddress[0]); - - s.avail_out = fileSizeUnc; - s.next_out = (Bytef *) &inflatedData[0]; - - ret = inflate(&s, Z_FINISH); - if (ret != Z_OK && ret != Z_STREAM_END) { - DEBUG_FUNCTION_LINE_ERR("inflate failed %i", ret); - free(loadAddress); - free(inflatedData); - - return FILE_READ_ERROR; - } - - inflateEnd(&s); - fileSize = fileSizeUnc; - } else { - // Section is compressed, inflate - inflatedData = (unsigned char *) malloc(fileSizeUnc); - if (!inflatedData) { - DEBUG_FUNCTION_LINE_ERR("malloc failed"); - free(loadAddress); - - return NOT_ENOUGH_MEMORY; - } - - uLongf f = fileSizeUnc; - int32_t result = uncompress((Bytef *) &inflatedData[0], &f, (Bytef *) loadAddress, fileSize); - if (result != Z_OK) { - DEBUG_FUNCTION_LINE_ERR("uncompress failed %i", result); - return FILE_READ_ERROR; - } - - fileSizeUnc = f; - fileSize = fileSizeUnc; - } - - if (memcmp(inflatedData, "WUHB", 4) == 0) { - DEBUG_FUNCTION_LINE("Try to load a .wuhb"); - FSUtils::CreateSubfolder(RPX_TEMP_PATH); - res = FSUtils::saveBufferToFile(WUHB_TEMP_FILE, inflatedData, fileSize); - file_path = WUHB_TEMP_FILE_EX; - if (!res) { - // temp.wuhb might be mounted, let's try temp2.wuhb - res = FSUtils::saveBufferToFile(WUHB_TEMP_FILE_2, inflatedData, fileSize); - file_path = WUHB_TEMP_FILE_2_EX; - } - - if (!res) { - NotificationModule_AddErrorNotification("Wiiload plugin: Failed to save .wuhb file to the sd card. Launching will be aborted."); - } - - loadedRPX = true; - } else if (inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE && inflatedData[0x9] != 0x50 && inflatedData[0xA] != 0x4C) { - DEBUG_FUNCTION_LINE("Try to load a .rpx"); - FSUtils::CreateSubfolder(RPX_TEMP_PATH); - res = FSUtils::saveBufferToFile(RPX_TEMP_FILE, inflatedData, fileSize); - file_path = RPX_TEMP_FILE_EX; - loadedRPX = true; - - if (!res) { - NotificationModule_AddErrorNotification("Wiiload plugin: Failed to save .rpx file to the sd card. Launching will be aborted."); - } - } else if (inflatedData[0x7] == 0xCA && inflatedData[0x8] == 0xFE && inflatedData[0x9] == 0x50 && inflatedData[0xA] == 0x4C) { - auto newContainer = WUPSBackend::PluginUtils::getPluginForBuffer((char *) inflatedData, fileSize); - if (newContainer) { - auto plugins = WUPSBackend::PluginUtils::getLoadedPlugins(32); - - auto &metaInformation = newContainer.value()->getMetaInformation(); - - // remove plugins with the same name and author as our new plugin - plugins.erase(std::remove_if(plugins.begin(), plugins.end(), - [metaInformation](auto &plugin) { - return plugin->getMetaInformation()->getName() == metaInformation->getName() && - plugin->getMetaInformation()->getAuthor() == metaInformation->getAuthor(); - }), - plugins.end()); - - // add the new plugin - plugins.push_back(std::move(newContainer.value())); - -#ifdef VERBOSE_DEBUG - for (auto &plugin : plugins) { - DEBUG_FUNCTION_LINE_VERBOSE("name: %s", plugin->getMetaInformation()->getName().c_str()); - DEBUG_FUNCTION_LINE_VERBOSE("author: %s", plugin->getMetaInformation()->getAuthor().c_str()); - DEBUG_FUNCTION_LINE_VERBOSE("handle: %08X", plugin->getPluginData()->getHandle()); - DEBUG_FUNCTION_LINE_VERBOSE("===="); - } -#endif - - if (WUPSBackend::PluginUtils::LoadAndLinkOnRestart(plugins) != 0) { - DEBUG_FUNCTION_LINE_ERR("WUPSBackend::PluginUtils::LoadAndLinkOnRestart failed"); - NotificationModule_AddErrorNotification("Wiiload plugin: Failed to load plugin. Launching will be aborted."); - } - - free(loadAddress); - free(inflatedData); - - _SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr); - return fileSize; - } else { - if (NotificationModule_AddErrorNotification("Wiiload plugin: Failed to load or parse the plugin. Launching will be aborted.") != NOTIFICATION_MODULE_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to display error notification"); - } - DEBUG_FUNCTION_LINE_ERR("Failed to parse plugin"); - } - } - free(inflatedData); - } else { - if (memcmp(loadAddress, "WUHB", 4) == 0) { - DEBUG_FUNCTION_LINE("Try to load a .wuhb"); - FSUtils::CreateSubfolder(RPX_TEMP_PATH); - res = FSUtils::saveBufferToFile(WUHB_TEMP_FILE, loadAddress, fileSize); - file_path = WUHB_TEMP_FILE_EX; - loadedRPX = true; - } else if (loadAddress[0x7] == 0xCA && loadAddress[0x8] == 0xFE) { - DEBUG_FUNCTION_LINE("Try to load a rpx"); - FSUtils::CreateSubfolder(RPX_TEMP_PATH); - res = FSUtils::saveBufferToFile(RPX_TEMP_FILE, loadAddress, fileSize); - file_path = RPX_TEMP_FILE_EX; - loadedRPX = true; - } else if (loadAddress[0x7] == 0xCA && loadAddress[0x8] == 0xFE && loadAddress[0x9] == 0x50) { - OSFatal("Not implemented yet"); - } - if (NotificationModule_AddErrorNotification("Failed to write file to the sd card.") != NOTIFICATION_MODULE_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to display error notification"); - } + std::unique_ptr finalData; + eLoadResults err; + if ((err = uncompressIfNeeded(haxx, fileSize, fileSizeUnc, std::move(receivedData), finalData, fileSize)) != SUCCESS) { + return err; } - free(loadAddress); - - if (!res) { - DEBUG_FUNCTION_LINE_ERR("Failed to launch/save a homebrew to the sd card"); - return NOT_ENOUGH_MEMORY; - } - - if (loadedRPX) { - if (!gLibRPXLoaderInitDone) { - DEBUG_FUNCTION_LINE_ERR("RPXLoaderModule missing, failed to launch homebrew."); - return NOT_SUPPORTED; - } - RPXLoaderStatus launchRes; - if ((launchRes = RPXLoader_LaunchHomebrew(file_path)) != RPX_LOADER_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to start %s %s", file_path, RPXLoader_GetStatusStr(launchRes)); - return NOT_ENOUGH_MEMORY; - } - - return fileSize; - } - - SYSRelaunchTitle(0, nullptr); - - return fileSize; + return loadBinary(finalData.get(), fileSize); } diff --git a/src/utils/TcpReceiver.h b/src/utils/TcpReceiver.h index 18dfc9a..29e42ae 100644 --- a/src/utils/TcpReceiver.h +++ b/src/utils/TcpReceiver.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -8,13 +9,15 @@ class TcpReceiver : public CThread { public: enum eLoadResults { - SUCCESS = 0, - INVALID_INPUT = -1, - FILE_OPEN_FAILURE = -2, - FILE_READ_ERROR = -3, - NOT_ENOUGH_MEMORY = -4, - NOT_A_VALID_PLUGIN = -5, - NOT_SUPPORTED = -6, + SUCCESS = 0, + UNSUPPORTED_FORMAT = -1, + PLUGIN_PARSE_FAILED = -2, + PLUGIN_LOAD_LINK_FAILED = -3, + FILE_SAVE_BUFFER_ERROR = -4, + FILE_UNCOMPRESS_ERROR = -5, + NOT_ENOUGH_MEMORY = -6, + RECV_ERROR = -7, + LAUNCH_FAILED = -8, }; explicit TcpReceiver(int32_t port); @@ -27,7 +30,13 @@ private: bool createSocket(); void cleanupSocket(); - static int32_t loadToMemory(int32_t clientSocket, uint32_t ipAddress); + static eLoadResults loadToMemory(int32_t clientSocket, uint32_t ipAddress); + + static TcpReceiver::eLoadResults tryLoadWUHB(void *data, uint32_t fileSize, std::string &loadedPathOut); + static TcpReceiver::eLoadResults tryLoadRPX(uint8_t *data, uint32_t fileSize, std::string &loadedPathOut); + static TcpReceiver::eLoadResults tryLoadWPS(uint8_t *data, uint32_t fileSize); + static TcpReceiver::eLoadResults loadBinary(void *data, uint32_t fileSize); + static TcpReceiver::eLoadResults uncompressIfNeeded(const uint8_t *haxx, uint32_t fileSize, uint32_t fileSizeUnc, std::unique_ptr &&in_data, std::unique_ptr &out_data, uint32_t &fileSizeOut); bool exitRequested; int32_t serverPort; diff --git a/src/utils/utils.c b/src/utils/utils.c deleted file mode 100644 index 8c3dc5c..0000000 --- a/src/utils/utils.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "utils/logger.h" -#include -#include - -// https://gist.github.com/ccbrown/9722406 -void dumpHex(const void *data, size_t size) { - char ascii[17]; - size_t i, j; - ascii[16] = '\0'; - DEBUG_FUNCTION_LINE("0x%08X (0x0000): ", data); - for (i = 0; i < size; ++i) { - WHBLogWritef("%02X ", ((unsigned char *) data)[i]); - if (((unsigned char *) data)[i] >= ' ' && ((unsigned char *) data)[i] <= '~') { - ascii[i % 16] = ((unsigned char *) data)[i]; - } else { - ascii[i % 16] = '.'; - } - if ((i + 1) % 8 == 0 || i + 1 == size) { - WHBLogWritef(" "); - if ((i + 1) % 16 == 0) { - WHBLogPrintf("| %s ", ascii); - if (i + 1 < size) { - DEBUG_FUNCTION_LINE("0x%08X (0x%04X); ", data + i + 1, i + 1); - } - } else if (i + 1 == size) { - ascii[(i + 1) % 16] = '\0'; - if ((i + 1) % 16 <= 8) { - WHBLogWritef(" "); - } - for (j = (i + 1) % 16; j < 16; ++j) { - WHBLogWritef(" "); - } - WHBLogPrintf("| %s ", ascii); - } - } - } -} diff --git a/src/utils/utils.h b/src/utils/utils.h index 81cbf5e..e48e53a 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,38 +1,14 @@ #pragma once #include +#include -#ifdef __cplusplus -extern "C" { -#endif - -#define LIMIT(x, min, max) \ - ({ \ - typeof(x) _x = x; \ - typeof(min) _min = min; \ - typeof(max) _max = max; \ - (((_x) < (_min)) ? (_min) : ((_x) > (_max)) ? (_max) \ - : (_x)); \ - }) - -#define DegToRad(a) ((a) *0.01745329252f) -#define RadToDeg(a) ((a) *57.29577951f) - -#define ALIGN4(x) (((x) + 3) & ~3) -#define ALIGN32(x) (((x) + 31) & ~31) - -// those work only in powers of 2 -#define ROUNDDOWN(val, align) ((val) & ~(align - 1)) -#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align - 1)), align) - - -#define le16(i) ((((uint16_t) ((i) &0xFF)) << 8) | ((uint16_t) (((i) &0xFF00) >> 8))) -#define le32(i) ((((uint32_t) le16((i) &0xFFFF)) << 16) | ((uint32_t) le16(((i) &0xFFFF0000) >> 16))) -#define le64(i) ((((uint64_t) le32((i) &0xFFFFFFFFLL)) << 32) | ((uint64_t) le32(((i) &0xFFFFFFFF00000000LL) >> 32))) - -//Needs to have log_init() called beforehand. -void dumpHex(const void *data, size_t size); - -#ifdef __cplusplus +template +std::unique_ptr make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward(args)...))) { + return std::unique_ptr(new (std::nothrow) T(std::forward(args)...)); } -#endif \ No newline at end of file + +template +inline typename std::unique_ptr make_unique_nothrow(size_t num) noexcept { + return std::unique_ptr(new (std::nothrow) std::remove_extent_t[num]()); +} \ No newline at end of file