Rewrite logic, clean up and remove unused parts

This commit is contained in:
Maschell 2024-03-16 11:00:59 +01:00
parent 9d44e51a75
commit d4984dafbe
22 changed files with 476 additions and 1403 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ cmake-build-debug/
.idea/
*.rpx
*.txt
*.zip

View File

@ -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

View File

@ -23,7 +23,6 @@ WUMS_ROOT := $(DEVKITPRO)/wums
TARGET := wiiload
BUILD := build
SOURCES := src \
src/fs \
src/utils
DATA := data
INCLUDES := src

71
src/config.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "config.h"
#include "globals.h"
#include "utils/TcpReceiver.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <string>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/storage.h>
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<TcpReceiver>(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");
}
}

6
src/config.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define WIILOAD_ENABLED_STRING "enabled"
#define DEFAULT_WIILOAD_ENABLED_VALUE true
void InitConfigAndStorage();

View File

@ -1,172 +0,0 @@
#include <cstdarg>
#include <cstdio>
#include <fs/CFile.hpp>
#include <strings.h>
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;
}

View File

@ -1,68 +0,0 @@
#pragma once
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <string>
#include <unistd.h>
#include <wut_types.h>
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;
};

View File

@ -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 <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <strings.h>
#include <sys/dirent.h>
#include <sys/stat.h>
#include <fs/DirList.h>
#include <utils/StringTools.h>
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<DirEntry>().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;
}

View File

@ -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 <string>
#include <vector>
#include <wut_types.h>
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<DirEntry> FileInfo;
};

View File

@ -1,145 +0,0 @@
#include <fcntl.h>
#include <fs/CFile.hpp>
#include <fs/FSUtils.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <utils/logger.h>
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;
}

View File

@ -1,18 +0,0 @@
#ifndef __FS_UTILS_H_
#define __FS_UTILS_H_
#include <wut_types.h>
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_

View File

@ -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;
bool gLibRPXLoaderInitDone = false;
std::unique_ptr<TcpReceiver> gTcpReceiverThread = nullptr;
bool gWiiloadServerEnabled = true;

View File

@ -1,5 +1,7 @@
#include <stdint.h>
#include "utils/TcpReceiver.h"
#include <memory>
#include <cstdint>
extern bool gLibRPXLoaderInitDone;
extern std::unique_ptr<TcpReceiver> gTcpReceiverThread;
extern bool gWiiloadServerEnabled;
extern bool gNotificationModuleLoaded;

View File

@ -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 <coreinit/debug.h>
#include <notifications/notifications.h>
#include <rpxloader/rpxloader.h>
@ -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<TcpReceiver> 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<TcpReceiver>(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<TcpReceiver>(4299);
if (tcpReceiverThread == nullptr) {
gTcpReceiverThread = make_unique_nothrow<TcpReceiver>(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();
}

110
src/utils/FSUtils.cpp Normal file
View File

@ -0,0 +1,110 @@
#include "FSUtils.h"
#include "logger.h"
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
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;
}

12
src/utils/FSUtils.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <wut_types.h>
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);
};

View File

@ -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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <strings.h>
#include <utils/StringTools.h>
#include <vector>
#include <wchar.h>
#include <wut_types.h>
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<std::string> StringTools::stringSplit(const std::string &inValue, const std::string &splitter) {
std::string value = inValue;
std::vector<std::string> 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;
}

View File

@ -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 <string>
#include <vector>
#include <wut_types.h>
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<std::string> stringSplit(const std::string &value, const std::string &splitter);
};
#endif /* __STRING_TOOLS_H */

View File

@ -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 <wups_backend/PluginUtils.h>
#include <zlib.h>
#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<uint8_t> &&in_data, std::unique_ptr<uint8_t> &out_data, uint32_t &fileSizeOut) {
// Do we need to unzip this thing?
if (haxx[4] > 0 || haxx[5] > 4) {
std::unique_ptr<uint8_t> 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<uint8_t>(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<uint8_t>(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<uint8_t>(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<uint8_t> 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);
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
@ -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<uint8_t> &&in_data, std::unique_ptr<uint8_t> &out_data, uint32_t &fileSizeOut);
bool exitRequested;
int32_t serverPort;

View File

@ -1,37 +0,0 @@
#include "utils/logger.h"
#include <string.h>
#include <whb/log.h>
// 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);
}
}
}
}

View File

@ -1,38 +1,14 @@
#pragma once
#include <malloc.h>
#include <memory>
#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<class T, class... Args>
std::unique_ptr<T> make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}
template<typename T>
inline typename std::unique_ptr<T> make_unique_nothrow(size_t num) noexcept {
return std::unique_ptr<T>(new (std::nothrow) std::remove_extent_t<T>[num]());
}
#endif