Reimplement the data structure and file reading

This commit is contained in:
Maschell 2022-04-27 19:10:44 +02:00
parent 74560db545
commit 15ab8b4c3b
17 changed files with 464 additions and 437 deletions

86
src/FileInfos.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include "utils/logger.h"
#include <coreinit/mcp.h>
#include <mutex>
#include <string>
#include <wuhb_utils/utils.h>
#include "utils/utils.h"
class FileInfos {
public:
explicit FileInfos(const std::string &relativePath) : relativeFilepath(relativePath) {
this->lowerTitleID = hash_string(relativePath.c_str());
}
~FileInfos() {
std::lock_guard<std::mutex> lock(mountLock);
if (isMounted) {
UnmountBundle();
}
}
bool MountBundle(const std::string &romfsName) {
if (isMounted) {
if (mountPath != romfsName) {
DEBUG_FUNCTION_LINE_ERR("Can't mount as %s because it's already mounted with a different name (%s)", romfsName.c_str(), mountPath.c_str());
return false;
}
return true;
}
if (!isBundle) {
DEBUG_FUNCTION_LINE_VERBOSE("Mounting not possible, is not a bundle");
return false;
}
auto fullMountPath = std::string("/vol/external01/").append(this->relativeFilepath);
int32_t outRes = -1;
if (WUHBUtils_MountBundle(romfsName.c_str(), fullMountPath.c_str(), BundleSource_FileDescriptor_CafeOS, &outRes) != WUHB_UTILS_RESULT_SUCCESS || outRes < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to mount bundle: %s", romfsName.c_str());
return false;
}
DEBUG_FUNCTION_LINE_VERBOSE("Succesfully mounted %s", romfsName.c_str());
this->isMounted = true;
mountPath = romfsName;
return true;
}
bool UnmountBundle() {
if (!isBundle) {
DEBUG_FUNCTION_LINE_VERBOSE("Skip unmounting, is not a bundle");
return true;
}
if (!isMounted) {
DEBUG_FUNCTION_LINE_VERBOSE("Skip unmounting, is not mounted");
return true;
}
int32_t outRes = -1;
if (WUHBUtils_UnmountBundle(mountPath.c_str(), &outRes) != WUHB_UTILS_RESULT_SUCCESS || outRes < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to unmount bundle: %s", mountPath.c_str());
return false;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Successfully unmounted bundle %s", this->mountPath.c_str());
}
this->isMounted = false;
this->mountPath.clear();
return true;
}
std::string relativeFilepath;
std::string filename;
std::string longname;
std::string shortname;
std::string author;
uint32_t lowerTitleID;
MCPTitleListType titleInfo{};
int32_t fileCount = 0;
bool isBundle = false;
std::mutex accessLock;
private:
std::mutex mountLock;
std::string mountPath;
bool isMounted = false;
};

View File

@ -1,97 +0,0 @@
#include "FileWrapper.h"
#include "fileinfos.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <wuhb_utils/utils.h>
FileHandleWrapper gFileHandleWrapper[FILE_WRAPPER_SIZE] __attribute__((section(".data")));
std::mutex fileWrapperMutex;
int FileHandleWrapper_GetSlot() {
std::lock_guard<std::mutex> lock(fileWrapperMutex);
int res = -1;
for (int i = 0; i < FILE_WRAPPER_SIZE; i++) {
if (!gFileHandleWrapper[i].inUse) {
gFileHandleWrapper[i].inUse = true;
res = i;
break;
}
}
OSMemoryBarrier();
return res;
}
bool FileHandleWrapper_FreeSlot(uint32_t slot) {
if (slot >= FILE_WRAPPER_SIZE) {
return false;
}
std::lock_guard<std::mutex> lock(fileWrapperMutex);
gFileHandleWrapper[slot].handle = 0;
gFileHandleWrapper[slot].inUse = false;
OSMemoryBarrier();
return -1;
}
bool FileHandleWrapper_FreeAll() {
std::lock_guard<std::mutex> lock(fileWrapperMutex);
for (int i = 0; i < FILE_WRAPPER_SIZE; i++) {
FileHandleWrapper_FreeSlot(i);
}
return -1;
}
int OpenFileForID(int id, const char *filepath, uint32_t *handle) {
if (!mountRomfs(id)) {
return -1;
}
char romName[10];
snprintf(romName, 10, "%08X", id);
char *dyn_path = (char *) malloc(strlen(filepath) + 1);
char last = 0;
int j = 0;
for (int i = 0; filepath[i] != 0; i++) {
if (filepath[i] == '/') {
if (filepath[i] != last) {
dyn_path[j++] = filepath[i];
}
} else {
dyn_path[j++] = filepath[i];
}
last = filepath[i];
}
dyn_path[j] = 0;
auto completePath = string_format("%s:/%s", romName, dyn_path);
WUHBFileHandle fileHandle = 0;
if (WUHBUtils_FileOpen(completePath.c_str(), &fileHandle) == WUHB_UTILS_RESULT_SUCCESS) {
int handle_wrapper_slot = FileHandleWrapper_GetSlot();
if (handle_wrapper_slot < 0) {
DEBUG_FUNCTION_LINE_ERR("No free slot");
if (WUHBUtils_FileClose(fileHandle) != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close file %08X", fileHandle);
}
unmountRomfs(id);
return -2;
}
gFileHandleWrapper[handle_wrapper_slot].handle = fileHandle;
*handle = 0xFF000000 | (id << 12) | (handle_wrapper_slot & 0x00000FFF);
gFileInfos[id].openedFiles++;
return 0;
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to open file %s", filepath);
if (gFileInfos[id].openedFiles == 0) {
unmountRomfs(id);
}
}
return -1;
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
typedef struct FileHandleWrapper_t {
uint32_t handle;
bool inUse;
} FileHandleWrapper;
#define FILE_WRAPPER_SIZE 64
extern FileHandleWrapper gFileHandleWrapper[FILE_WRAPPER_SIZE];
int OpenFileForID(int id, const char *path, uint32_t *handle);
bool FileHandleWrapper_FreeAll();

View File

@ -71,7 +71,7 @@ void initSaveData() {
DECL_FUNCTION(int32_t, LoadConsoleAccount__Q2_2nn3actFUc13ACTLoadOptionPCcb, nn::act::SlotNo slot, nn::act::ACTLoadOption unk1, char const *unk2, bool unk3) {
int32_t result = real_LoadConsoleAccount__Q2_2nn3actFUc13ACTLoadOptionPCcb(slot, unk1, unk2, unk3);
if (result >= 0 && gInWiiUMenu) {
DEBUG_FUNCTION_LINE_VERBOSE("Changed account, we need to init the save data");
DEBUG_FUNCTION_LINE("Changed account, we need to init the save data");
// If the account has changed, we need to init save data for this account
// Calls our function replacement.
SAVEInit();

View File

@ -1,74 +0,0 @@
#include "fileinfos.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <cstdio>
#include <cstring>
#include <mutex>
#include <wuhb_utils/utils.h>
FileInfos gFileInfos[FILE_INFO_SIZE] __attribute__((section(".data")));
std::mutex fileinfoMutex;
int32_t getIDByLowerTitleID(uint32_t lowerTitleID) {
std::lock_guard<std::mutex> lock(fileinfoMutex);
int res = -1;
for (int i = 0; i < FILE_INFO_SIZE; i++) {
if (strlen(gFileInfos[i].path) > 0 && gFileInfos[i].lowerTitleID == lowerTitleID) {
res = i;
break;
}
}
OSMemoryBarrier();
return res;
}
void unmountRomfs(uint32_t id) {
if (id >= FILE_INFO_SIZE) {
return;
}
std::lock_guard<std::mutex> lock(fileinfoMutex);
if (gFileInfos[id].romfsMounted) {
char romName[10];
snprintf(romName, 10, "%08X", id);
int32_t outRes;
if (WUHBUtils_UnmountBundle(romName, &outRes) || outRes != 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to unmount \"%s\"", romName);
}
gFileInfos[id].romfsMounted = false;
}
OSMemoryBarrier();
}
void unmountAllRomfs() {
for (int i = 0; i < FILE_INFO_SIZE; i++) {
unmountRomfs(i);
}
}
bool mountRomfs(uint32_t id) {
if (id >= FILE_INFO_SIZE) {
DEBUG_FUNCTION_LINE_ERR("HANDLE WAS TOO BIG %d", id);
return false;
}
std::lock_guard<std::mutex> lock(fileinfoMutex);
bool result = false;
if (!gFileInfos[id].romfsMounted) {
char buffer[256];
snprintf(buffer, 256, "/vol/external01/%s", gFileInfos[id].path);
char romName[10];
snprintf(romName, 10, "%08X", id);
DEBUG_FUNCTION_LINE("Mount %s as %s", buffer, romName);
int32_t res = 0;
if (WUHBUtils_MountBundle(romName, buffer, BundleSource_FileDescriptor_CafeOS, &res) == WUHB_UTILS_RESULT_SUCCESS && res == 0) {
DEBUG_FUNCTION_LINE("Mounted successfully ");
gFileInfos[id].romfsMounted = true;
result = true;
} else {
DEBUG_FUNCTION_LINE_ERR("Mounting failed %d", res);
result = false;
}
}
OSMemoryBarrier();
return result;
}

View File

@ -1,31 +0,0 @@
#pragma once
#include <coreinit/mcp.h>
#include <cstdint>
#include <mutex>
extern std::mutex fileinfoMutex;
typedef struct WUT_PACKED FileInfos_ {
char path[256];
char filename[256];
char shortname[64];
char longname[64];
char author[64];
int32_t source;
uint32_t lowerTitleID;
bool romfsMounted;
int openedFiles;
MCPTitleListType titleInfo;
} FileInfos;
#define FILE_INFO_SIZE 300
extern FileInfos gFileInfos[FILE_INFO_SIZE];
int32_t getIDByLowerTitleID(uint32_t lowerTitleID);
void unmountRomfs(uint32_t id);
void unmountAllRomfs();
bool mountRomfs(uint32_t id);

View File

@ -62,7 +62,7 @@ BOOL DirList::LoadPath(const std::string &folder, const char *filter, uint32_t f
std::string folderpath(folder);
uint32_t length = folderpath.size();
//! clear path of double slashes
//! clear relativeFilepath of double slashes
StringTools::RemoveDoubleSlashs(folderpath);
//! remove last slash if exists

View File

@ -132,7 +132,7 @@ int32_t FSUtils::CreateSubfolder(const char *fullpath) {
int32_t FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) {
CFile file(path, CFile::WriteOnly);
if (!file.isOpen()) {
DEBUG_FUNCTION_LINE("Failed to open %s\n", path);
DEBUG_FUNCTION_LINE_ERR("Failed to open %s\n", path);
return 0;
}
int32_t written = file.write((const uint8_t *) buffer, size);
@ -147,13 +147,13 @@ bool FSUtils::copyFile(const std::string &in, const std::string &out) {
int source = open(in.c_str(), O_RDONLY, 0);
if (source < 0) {
DEBUG_FUNCTION_LINE("Failed to open source %s", in.c_str());
DEBUG_FUNCTION_LINE_ERR("Failed to open source %s", in.c_str());
return false;
}
int dest = open(out.c_str(), 0x602, 0644);
if (dest < 0) {
DEBUG_FUNCTION_LINE("Failed to open dest %s", out.c_str());
DEBUG_FUNCTION_LINE_ERR("Failed to open dest %s", out.c_str());
close(source);
return false;
}
@ -161,7 +161,7 @@ bool FSUtils::copyFile(const std::string &in, const std::string &out) {
auto bufferSize = 128 * 1024;
char *buf = (char *) malloc(bufferSize);
if (buf == nullptr) {
DEBUG_FUNCTION_LINE("Failed to alloc buffer");
DEBUG_FUNCTION_LINE_ERR("Failed to alloc buffer");
return false;
}

54
src/fs/FileReader.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "FileReader.h"
#include "../utils/logger.h"
#include <cstring>
int64_t FileReader::read(uint8_t *buffer, uint32_t size) {
if (isReadFromBuffer) {
if (input_buffer == nullptr) {
return -1;
}
uint32_t toRead = size;
if (toRead > input_size - input_pos) {
toRead = input_size - input_pos;
}
if (toRead == 0) {
return 0;
}
memcpy(buffer, &input_buffer[input_pos], toRead);
input_pos += toRead;
return toRead;
} else if (isReadFromFile) {
int res = ::read(file_fd, buffer, size);
return res;
}
return -2;
}
FileReader::FileReader(std::string &path) {
int fd;
if ((fd = open(path.c_str(), O_RDONLY)) >= 0) {
this->isReadFromFile = true;
this->isReadFromBuffer = false;
this->file_fd = fd;
} else {
DEBUG_FUNCTION_LINE("## INFO ## Failed to open file %s", path.c_str());
}
}
FileReader::~FileReader() {
if (isReadFromFile) {
::close(this->file_fd);
}
}
FileReader::FileReader(uint8_t *buffer, uint32_t size) {
this->input_buffer = buffer;
this->input_size = size;
this->input_pos = 0;
this->isReadFromBuffer = true;
this->isReadFromFile = false;
}
bool FileReader::isReady() {
return this->isReadFromFile || this->isReadFromBuffer;
}

37
src/fs/FileReader.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <fcntl.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
class FileReader {
public:
FileReader(uint8_t *buffer, uint32_t size);
explicit FileReader(std::string &path);
virtual ~FileReader();
virtual int64_t read(uint8_t *buffer, uint32_t size);
virtual bool isReady();
virtual uint32_t getHandle(){
return reinterpret_cast<uint32_t>(this);
}
protected:
FileReader() = default;
private:
bool isReadFromBuffer = false;
uint8_t *input_buffer = nullptr;
uint32_t input_size = 0;
uint32_t input_pos = 0;
bool isReadFromFile = false;
int file_fd = 0;
};

79
src/fs/FileReaderWUHB.cpp Normal file
View File

@ -0,0 +1,79 @@
#include "FileReaderWUHB.h"
#include "utils/StringTools.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <wuhb_utils/utils.h>
FileReaderWUHB::FileReaderWUHB(const std::shared_ptr<FileInfos>& info, const std::string &relativeFilepath, bool autoUnmount) {
if (!info) {
DEBUG_FUNCTION_LINE_ERR("Info was NULL");
return;
}
if (!info->isBundle) {
DEBUG_FUNCTION_LINE("Failed to init file reader for %s, is not a bundle.", info->relativeFilepath.c_str());
return;
}
this->autoUnmount = autoUnmount;
this->info = info;
std::lock_guard<std::mutex> lock(info->accessLock);
auto romfsName = string_format("%08X", info->lowerTitleID);
if (!info->MountBundle(romfsName)) {
return;
}
auto filepath = romfsName.append(":/").append(relativeFilepath);
WUHBUtilsStatus status;
if ((status = WUHBUtils_FileOpen(filepath.c_str(), &this->fileHandle)) != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to open file in bundle: %s error: %d", filepath.c_str(), status);
return;
}
this->info->fileCount++;
this->initDone = true;
OSMemoryBarrier();
}
FileReaderWUHB::~FileReaderWUHB() {
if (!this->initDone) {
return;
}
std::lock_guard<std::mutex> lock(info->accessLock);
if (this->fileHandle != 0) {
if (WUHBUtils_FileClose(this->fileHandle) != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("WUHBUtils_FileClose failed for %08X", this->fileHandle);
}
this->fileHandle = 0;
info->fileCount--;
}
if (autoUnmount && info->fileCount <= 0) {
if (!info->UnmountBundle()) {
DEBUG_FUNCTION_LINE_ERR("Failed to unmount");
}
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Filecount is %d, we don't want to unmount yet", info->fileCount);
}
OSMemoryBarrier();
}
int64_t FileReaderWUHB::read(uint8_t *buffer, uint32_t size) {
if (!this->initDone) {
DEBUG_FUNCTION_LINE_ERR("read file but init was not successful");
return -1;
}
int32_t outRes = -1;
if (WUHBUtils_FileRead(this->fileHandle, buffer, size, &outRes) == WUHB_UTILS_RESULT_SUCCESS) {
return outRes;
}
DEBUG_FUNCTION_LINE_ERR("WUHBUtils_FileRead failed");
return -1;
}
bool FileReaderWUHB::isReady() {
return this->initDone;
}

18
src/fs/FileReaderWUHB.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "../FileInfos.h"
#include "FileReader.h"
#include <wuhb_utils/utils.h>
class FileReaderWUHB : public FileReader {
bool initDone = false;
std::shared_ptr<FileInfos> info;
WUHBFileHandle fileHandle = 0;
bool autoUnmount = false;
public:
explicit FileReaderWUHB(const std::shared_ptr<FileInfos>& info, const std::string &relativeFilepath, bool autoUnmount);
~FileReaderWUHB() override;
int64_t read(uint8_t *buffer, uint32_t size) override;
bool isReady() override;
};

View File

@ -1,8 +1,9 @@
#include "FileWrapper.h"
#include "FileInfos.h"
#include "SaveRedirection.h"
#include "fileinfos.h"
#include "filelist.h"
#include "fs/FSUtils.h"
#include "fs/FileReader.h"
#include "fs/FileReaderWUHB.h"
#include "utils/StringTools.h"
#include "utils/ini.h"
#include <content_redirection/redirection.h>
@ -14,10 +15,12 @@
#include <coreinit/systeminfo.h>
#include <coreinit/title.h>
#include <cstring>
#include <forward_list>
#include <fs/DirList.h>
#include <malloc.h>
#include <mutex>
#include <nn/acp.h>
#include <optional>
#include <rpxloader/rpxloader.h>
#include <sdutils/sdutils.h>
#include <sysapp/launch.h>
@ -38,13 +41,19 @@ WUPS_PLUGIN_AUTHOR("Maschell");
WUPS_PLUGIN_LICENSE("GPL");
#define UPPER_TITLE_ID_HOMEBREW 0x0005000F
#define TITLE_ID_HOMEBREW_MASK (((uint64_t) UPPER_TITLE_ID_HOMEBREW) << 32)
ACPMetaXml gLaunchXML __attribute__((section(".data")));
MCPTitleListType current_launched_title_info __attribute__((section(".data")));
BOOL gHomebrewLaunched __attribute__((section(".data")));
std::mutex fileInfosMutex;
std::forward_list<std::shared_ptr<FileInfos>> fileInfos;
std::mutex fileReaderListMutex;
std::forward_list<std::unique_ptr<FileReader>> openFileReaders;
void readCustomTitlesFromSD();
WUPS_USE_WUT_DEVOPTAB();
@ -52,8 +61,6 @@ WUPS_USE_WUT_DEVOPTAB();
INITIALIZE_PLUGIN() {
memset((void *) &current_launched_title_info, 0, sizeof(current_launched_title_info));
memset((void *) &gLaunchXML, 0, sizeof(gLaunchXML));
memset((void *) &gFileInfos, 0, sizeof(gFileInfos));
memset((void *) &gFileHandleWrapper, 0, sizeof(gFileHandleWrapper));
gHomebrewLaunched = FALSE;
// Use libwuhbutils.
@ -82,6 +89,18 @@ bool sSDUtilsInitDone = false;
bool sSDIsMounted = false;
bool sTitleRebooting = false;
void Cleanup() {
{
const std::lock_guard<std::mutex> lock1(fileReaderListMutex);
openFileReaders.clear();
}
{
const std::lock_guard<std::mutex> lock(fileInfosMutex);
fileInfos.clear();
}
}
void SDAttachedHandler([[maybe_unused]] SDUtilsAttachStatus status) {
if (!sTitleRebooting) {
_SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr);
@ -90,35 +109,31 @@ void SDAttachedHandler([[maybe_unused]] SDUtilsAttachStatus status) {
}
ON_APPLICATION_START() {
Cleanup();
initLogging();
if (OSGetTitleID() == 0x0005001010040000L || // Wii U Menu JPN
OSGetTitleID() == 0x0005001010040100L || // Wii U Menu USA
OSGetTitleID() == 0x0005001010040200L) { // Wii U Menu EUR
gInWiiUMenu = true;
if (SDUtils_Init() >= 0) {
sSDUtilsInitDone = true;
sTitleRebooting = false;
SDUtils_AddAttachHandler(SDAttachedHandler);
}
if (SDUtils_IsSdCardMounted(&sSDIsMounted) >= 0 && sSDIsMounted) {
readCustomTitlesFromSD();
}
} else {
gInWiiUMenu = false;
}
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) != OSGetTitleID()) {
DEBUG_FUNCTION_LINE("gHomebrewLaunched to FALSE");
gHomebrewLaunched = FALSE;
}
}
ON_APPLICATION_ENDS() {
Cleanup();
SaveRedirectionCleanUp();
unmountAllRomfs();
memset((void *) &gFileInfos, 0, sizeof(gFileInfos));
FileHandleWrapper_FreeAll();
deinitLogging();
gInWiiUMenu = false;
if (sSDUtilsInitDone) {
@ -129,19 +144,29 @@ ON_APPLICATION_ENDS() {
sSDIsMounted = false;
}
std::optional<std::shared_ptr<FileInfos>> getIDByLowerTitleID(uint32_t titleid_lower) {
std::lock_guard<std::mutex> lock(fileInfosMutex);
for (auto &cur : fileInfos) {
if (cur->lowerTitleID == titleid_lower) {
return cur;
}
}
return {};
}
void fillXmlForTitleID(uint32_t titleid_upper, uint32_t titleid_lower, ACPMetaXml *out_buf) {
int32_t id = getIDByLowerTitleID(titleid_lower);
if (id < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to get id by titleid");
auto titleIdInfoOpt = getIDByLowerTitleID(titleid_lower);
if (!titleIdInfoOpt.has_value()) {
DEBUG_FUNCTION_LINE_ERR("Failed to get info by titleid");
return;
}
if (id >= FILE_INFO_SIZE) {
return;
}
out_buf->title_id = ((uint64_t) titleid_upper * 0x100000000) + titleid_lower;
strncpy(out_buf->longname_en, gFileInfos[id].longname, 64);
strncpy(out_buf->shortname_en, gFileInfos[id].shortname, 64);
strncpy(out_buf->publisher_en, gFileInfos[id].author, 64);
auto &titleInfo = titleIdInfoOpt.value();
out_buf->title_id = (((uint64_t) titleid_upper) << 32) + titleid_lower;
strncpy(out_buf->longname_en, titleInfo->longname.c_str(), sizeof(out_buf->longname_en) - 1);
strncpy(out_buf->shortname_en, titleInfo->shortname.c_str(), sizeof(out_buf->shortname_en) - 1);
strncpy(out_buf->publisher_en, titleInfo->author.c_str(), sizeof(out_buf->publisher_en) - 1);
out_buf->e_manual = 1;
out_buf->e_manual_version = 0;
out_buf->title_version = 1;
@ -157,34 +182,22 @@ void fillXmlForTitleID(uint32_t titleid_upper, uint32_t titleid_lower, ACPMetaXm
out_buf->reserved_flag0 = 0x00010001;
out_buf->reserved_flag6 = 0x00000003;
out_buf->pc_usk = 128;
strncpy(out_buf->product_code, "WUP-P-HBLD", strlen("WUP-P-HBLD") + 1);
strncpy(out_buf->content_platform, "WUP", strlen("WUP") + 1);
strncpy(out_buf->company_code, "0001", strlen("0001") + 1);
}
/* hash: compute hash value of string */
unsigned int hash(char *str) {
unsigned int h;
unsigned char *p;
h = 0;
for (p = (unsigned char *) str; *p != '\0'; p++) {
h = 37 * h + *p;
}
return h; // or, h % ARRAY_SIZE;
strncpy(out_buf->product_code, "WUP-P-HBLD", sizeof(out_buf->product_code) - 1);
strncpy(out_buf->content_platform, "WUP", sizeof(out_buf->content_platform) - 1);
strncpy(out_buf->company_code, "0001", sizeof(out_buf->company_code) - 1);
}
static int handler(void *user, const char *section, const char *name,
const char *value) {
auto *fInfo = (FileInfos *) user;
auto *fInfo = (std::shared_ptr<FileInfos> *) user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("menu", "longname")) {
strncpy(fInfo->longname, value, 64 - 1);
fInfo->operator->()->longname = value;
} else if (MATCH("menu", "shortname")) {
strncpy(fInfo->shortname, value, 64 - 1);
fInfo->operator->()->shortname = value;
} else if (MATCH("menu", "author")) {
strncpy(fInfo->author, value, 64 - 1);
fInfo->operator->()->author = value;
} else {
return 0; /* unknown section/name, error */
}
@ -194,20 +207,16 @@ static int handler(void *user, const char *section, const char *name,
bool CheckFileExistsHelper(const char *path);
void readCustomTitlesFromSD() {
std::lock_guard<std::mutex> lock(fileInfosMutex);
if (!fileInfos.empty()) {
DEBUG_FUNCTION_LINE_VERBOSE("Using cached value");
return;
}
// Reset current infos
unmountAllRomfs();
memset((void *) &gFileInfos, 0, sizeof(gFileInfos));
DirList dirList("fs:/vol/external01/wiiu/apps", ".rpx,.wuhb", DirList::Files | DirList::CheckSubfolders, 1);
dirList.SortList();
int j = 0;
for (int i = 0; i < dirList.GetFilecount(); i++) {
if (j >= FILE_INFO_SIZE) {
DEBUG_FUNCTION_LINE_ERR("TOO MANY TITLES");
break;
}
//! skip wiiload temp files
if (strcasecmp(dirList.GetFilename(i), "temp.rpx") == 0) {
continue;
@ -228,48 +237,61 @@ void readCustomTitlesFromSD() {
continue;
}
auto repl = "fs:/vol/external01/";
auto input = dirList.GetFilepath(i);
const char * relativeFilepath;
if (std::string_view(input).starts_with(repl)) {
strncpy(gFileInfos[j].path, &input[strlen(repl)], sizeof(gFileInfos[j].path));
relativeFilepath = &input[strlen(repl)];
} else {
DEBUG_FUNCTION_LINE_ERR("Skip %s, Path doesn't start with %s (This should never happen", input, repl);
continue;
}
gFileInfos[j].lowerTitleID = hash(gFileInfos[j].path);
auto fileInfo = make_shared_nothrow<FileInfos>(relativeFilepath);
if (!fileInfo) {
DEBUG_FUNCTION_LINE_ERR("No more memory");
break;
}
MCPTitleListType *cur_title_info = &(gFileInfos[j].titleInfo);
std::lock_guard<std::mutex> infoLock(fileInfo->accessLock);
snprintf(cur_title_info->path, sizeof(cur_title_info->path), "/custom/%08X%08X", UPPER_TITLE_ID_HOMEBREW, gFileInfos[j].lowerTitleID);
auto *cur_title_info = &(fileInfo->titleInfo);
strncpy(gFileInfos[j].filename, dirList.GetFilename(i), sizeof(gFileInfos[j].filename));
strncpy(gFileInfos[j].longname, dirList.GetFilename(i), sizeof(gFileInfos[j].longname));
strncpy(gFileInfos[j].shortname, dirList.GetFilename(i), sizeof(gFileInfos[j].shortname));
strncpy(gFileInfos[j].author, dirList.GetFilename(i), sizeof(gFileInfos[j].author));
gFileInfos[j].source = 0; //SD Card;
snprintf(cur_title_info->path, sizeof(cur_title_info->path), "/custom/%08X%08X", UPPER_TITLE_ID_HOMEBREW, fileInfo->lowerTitleID);
const char *indexedDevice = "mlc";
strncpy(cur_title_info->indexedDevice, indexedDevice, sizeof(cur_title_info->indexedDevice));
strncpy(cur_title_info->indexedDevice, indexedDevice, sizeof(cur_title_info->indexedDevice) - 1);
fileInfo->filename = dirList.GetFilename(i);
fileInfo->longname = dirList.GetFilename(i);
fileInfo->shortname = dirList.GetFilename(i);
fileInfo->author = dirList.GetFilename(i);
// System apps don't have a splash screen.
cur_title_info->appType = MCP_APP_TYPE_SYSTEM_APPS;
DEBUG_FUNCTION_LINE_VERBOSE("Check %s", fileInfo->filename.c_str());
// Check if the bootTvTex and bootDrcTex exists
if (std::string_view(gFileInfos[j].filename).ends_with(".wuhb")) {
if (std::string_view(fileInfo->filename).ends_with(".wuhb")) {
int result = 0;
#define TMP_BUNDLE_NAME "romfscheck"
if (WUHBUtils_MountBundle(TMP_BUNDLE_NAME, dirList.GetFilepath(i), BundleSource_FileDescriptor, &result) == WUHB_UTILS_RESULT_SUCCESS && result >= 0) {
fileInfo->isBundle = true;
uint8_t *buffer;
uint32_t bufferSize;
auto readRes = WUHBUtils_ReadWholeFile(TMP_BUNDLE_NAME ":/meta/meta.ini", &buffer, &bufferSize);
if (readRes == WUHB_UTILS_RESULT_SUCCESS) {
buffer[bufferSize - 1] = '\0';
if (ini_parse_string((const char *) buffer, handler, &gFileInfos[j]) < 0) {
if (ini_parse_string((const char *) buffer, handler, &fileInfo) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to parse meta.ini");
}
free(buffer);
@ -283,6 +305,7 @@ void readCustomTitlesFromSD() {
if (CheckFileExistsHelper(bootTvTexPath) && CheckFileExistsHelper(bootDrcTexPath)) {
// Show splash screens
cur_title_info->appType = MCP_APP_TYPE_GAME;
DEBUG_FUNCTION_LINE_VERBOSE("Title has splashscreen");
}
int32_t unmountRes;
@ -294,12 +317,12 @@ void readCustomTitlesFromSD() {
DEBUG_FUNCTION_LINE_ERR("Failed to unmount \"%s\"", TMP_BUNDLE_NAME);
}
} else {
DEBUG_FUNCTION_LINE("%s is not a .wuhb file: %d", dirList.GetFilepath(i), result);
DEBUG_FUNCTION_LINE_ERR("%s is not a valid .wuhb file: %d", dirList.GetFilepath(i), result);
continue;
}
}
cur_title_info->titleId = TITLE_ID_HOMEBREW_MASK | gFileInfos[j].lowerTitleID;
cur_title_info->titleId = TITLE_ID_HOMEBREW_MASK | fileInfo->lowerTitleID;
cur_title_info->titleVersion = 1;
cur_title_info->groupId = 0x400;
@ -307,7 +330,7 @@ void readCustomTitlesFromSD() {
cur_title_info->sdkVersion = __OSGetProcessSDKVersion();
cur_title_info->unk0x60 = 0;
j++;
fileInfos.push_front(fileInfo);
}
}
@ -328,38 +351,42 @@ bool CheckFileExistsHelper(const char *path) {
DECL_FUNCTION(int32_t, MCP_TitleList, uint32_t handle, uint32_t *outTitleCount, MCPTitleListType *titleList, uint32_t size) {
int32_t result = real_MCP_TitleList(handle, outTitleCount, titleList, size);
uint32_t titlecount = *outTitleCount;
std::lock_guard<std::mutex> lock(fileinfoMutex);
for (auto &gFileInfo : gFileInfos) {
if (gFileInfo.lowerTitleID == 0) {
break;
}
memcpy(&(titleList[titlecount]), &(gFileInfo.titleInfo), sizeof(gFileInfo.titleInfo));
titlecount++;
if (!gInWiiUMenu) {
DEBUG_FUNCTION_LINE_VERBOSE("Not in Wii U Menu");
return result;
}
*outTitleCount = titlecount;
uint32_t titleCount = *outTitleCount;
std::lock_guard<std::mutex> lock(fileInfosMutex);
readCustomTitlesFromSD();
for (auto &gFileInfo : fileInfos) {
memcpy(&(titleList[titleCount]), &(gFileInfo->titleInfo), sizeof(MCPTitleListType));
titleCount++;
}
*outTitleCount = titleCount;
return result;
}
DECL_FUNCTION(int32_t, ACPCheckTitleLaunchByTitleListTypeEx, MCPTitleListType *title, uint32_t u2) {
if ((title->titleId & TITLE_ID_HOMEBREW_MASK) == TITLE_ID_HOMEBREW_MASK) {
int32_t id = getIDByLowerTitleID(title->titleId & 0xFFFFFFFF);
if (id >= 0) {
std::lock_guard<std::mutex> lock(fileInfosMutex);
auto fileInfo = getIDByLowerTitleID(title->titleId & 0xFFFFFFFF);
if (fileInfo.has_value()) {
DEBUG_FUNCTION_LINE("Starting a homebrew title");
fillXmlForTitleID((title->titleId & 0xFFFFFFFF00000000) >> 32, (title->titleId & 0xFFFFFFFF), &gLaunchXML);
std::string bundleFilePath = std::string("/vol/external01/") + gFileInfos[id].path;
gHomebrewLaunched = TRUE;
RPXLoader_LoadFromSDOnNextLaunch(gFileInfos[id].path);
RPXLoader_LoadFromSDOnNextLaunch(fileInfo.value()->relativeFilepath.c_str());
return 0;
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to get the id for titleID %016llX", title->titleId);
DEBUG_FUNCTION_LINE_ERR("Failed to get info for titleID %016llX", title->titleId);
}
}
@ -367,42 +394,43 @@ DECL_FUNCTION(int32_t, ACPCheckTitleLaunchByTitleListTypeEx, MCPTitleListType *t
return result;
}
DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, int error) {
const char *start = "/vol/storage_mlc01/sys/title/0005000F";
const char *icon = ".tga";
const char *tga = ".tga";
const char *iconTex = "iconTex.tga";
const char *sound = ".btsnd";
std::string_view pathStr = path;
if (pathStr.ends_with(icon) || pathStr.ends_with(sound)) {
if (strncmp(path, start, strlen(start)) == 0) {
int res = FS_STATUS_NOT_FOUND;
if (pathStr.ends_with(iconTex)) {
// fallback to dummy icon if loaded homebrew is no .wuhb
*handle = 0x13371338;
res = FS_STATUS_OK;
}
uint32_t lowerTitleID;
if (pathStr.starts_with(start)) {
std::unique_ptr<FileReader> reader;
if (pathStr.ends_with(tga) || pathStr.ends_with(sound)) {
char *id = path + 1 + strlen(start);
id[8] = 0;
char *ending = id + 9;
sscanf(id, "%08X", &lowerTitleID);
int32_t idVal = getIDByLowerTitleID(lowerTitleID);
if (idVal >= 0) {
if (!std::string_view(gFileInfos[idVal].filename).ends_with(".wuhb")) {
return res;
char *relativePath = id + 9;
auto lowerTitleID = strtoul(id, 0, 16);
auto fileInfo = getIDByLowerTitleID(lowerTitleID);
if (fileInfo.has_value()) {
reader = make_unique_nothrow<FileReaderWUHB>(fileInfo.value(), relativePath, !gHomebrewLaunched);
if (reader && !reader->isReady()) {
reader.reset();
}
if (OpenFileForID(idVal, ending, handle) >= 0) {
}
}
// If the icon is requested and loading it from a bundle failed, we fall back to a default one.
if (reader == nullptr && pathStr.ends_with(iconTex)) {
reader = make_unique_nothrow<FileReader>((uint8_t *) iconTex_tga, iconTex_tga_size);
if (reader && !reader->isReady()) {
reader.reset();
}
}
if (reader) {
std::lock_guard<std::mutex> lock(fileReaderListMutex);
*handle = reader->getHandle();
openFileReaders.push_front(std::move(reader));
return FS_STATUS_OK;
}
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to find id for titleID %08X", lowerTitleID);
}
return res;
}
}
int result = real_FSOpenFile(client, block, path, mode, handle, error);
@ -410,48 +438,18 @@ DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path,
}
DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t flags) {
if (handle == 0x13371338) {
return FS_STATUS_OK;
} else if ((handle & 0xFF000000) == 0xFF000000) {
int32_t fd = (handle & 0x00000FFF);
int32_t romid = (handle & 0x00FFF000) >> 12;
std::lock_guard<std::mutex> lock(fileinfoMutex);
uint32_t rl_handle = gFileHandleWrapper[fd].handle;
if (WUHBUtils_FileClose(rl_handle) != WUHB_UTILS_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to close file %08X", rl_handle);
}
if (gFileInfos[romid].openedFiles--) {
DCFlushRange(&gFileInfos[romid].openedFiles, 4);
if (gFileInfos[romid].openedFiles <= 0) {
DEBUG_FUNCTION_LINE_VERBOSE("unmount romfs no more handles");
unmountRomfs(romid);
}
}
if (remove_locked_first_if(fileReaderListMutex, openFileReaders, [handle](auto &cur) { return cur->getHandle() == handle; })) {
return FS_STATUS_OK;
}
return real_FSCloseFile(client, block, handle, flags);
}
DECL_FUNCTION(FSStatus, FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, uint32_t flags) {
if (handle == 0x13371338) {
uint32_t cpySize = size * count;
if (iconTex_tga_size < cpySize) {
cpySize = iconTex_tga_size;
}
memcpy(buffer, iconTex_tga, cpySize);
return (FSStatus) (cpySize / size);
} else if ((handle & 0xFF000000) == 0xFF000000) {
uint32_t fd = (handle & 0x00000FFF);
[[maybe_unused]] uint32_t romid = (handle & 0x00FFF000) >> 12;
uint32_t rl_handle = gFileHandleWrapper[fd].handle;
int readSize = 0;
if (WUHBUtils_FileRead(rl_handle, buffer, (size * count), &readSize) == WUHB_UTILS_RESULT_SUCCESS) {
return (FSStatus) (readSize / size);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to read file");
OSFatal("Failed to read file");
const std::lock_guard<std::mutex> lock(fileReaderListMutex);
for (auto &reader : openFileReaders) {
if ((uint32_t) reader.get() == (uint32_t) handle) {
return (FSStatus) (reader->read(buffer, size * count) / size);
}
}
FSStatus result = real_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
@ -604,9 +602,9 @@ DECL_FUNCTION(uint32_t, MCPGetTitleInternal, uint32_t mcp_handle, void *input, u
if (input != nullptr) {
auto *inputPtrAsU32 = (uint32_t *) input;
if (inputPtrAsU32[0] == UPPER_TITLE_ID_HOMEBREW && out_cnt >= 1) {
for (auto &gFileInfo : gFileInfos) {
if (gFileInfo.lowerTitleID == inputPtrAsU32[1]) {
memcpy(&titles[0], &(gFileInfo.titleInfo), sizeof(MCPTitleListType));
for (auto &gFileInfo : fileInfos) {
if (gFileInfo->lowerTitleID == inputPtrAsU32[1]) {
memcpy(&titles[0], &(gFileInfo->titleInfo), sizeof(MCPTitleListType));
return 1;
}
}

View File

@ -69,7 +69,7 @@ const char *StringTools::FullpathToFilename(const char *path) {
void StringTools::RemoveDoubleSlashs(std::string &str) {
uint32_t length = str.size();
//! clear path of double slashes
//! clear relativeFilepath of double slashes
for (uint32_t i = 1; i < length; ++i) {
if (str[i - 1] == '/' && str[i] == '/') {
str.erase(i, 1);

View File

@ -1,39 +0,0 @@
#include "utils/logger.h"
#include <stddef.h>
#include <stdlib.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_WRITE("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_WRITE("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);
}
}
}
}

13
src/utils/utils.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <stdint.h>
/* hash: compute hash value of string */
unsigned int hash_string(const char *str) {
unsigned int h;
unsigned char *p;
h = 0;
for (p = (unsigned char *) str; *p != '\0'; p++) {
h = 37 * h + *p;
}
return h; // or, h % ARRAY_SIZE;
}

View File

@ -1,36 +1,32 @@
#ifndef __UTILS_H_
#define __UTILS_H_
#pragma once
#include <cstdint>
#include <forward_list>
#include <malloc.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t hash_string(const char *);
#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)
#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<class T, class... Args>
std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}
template<typename T, class Allocator, class Predicate>
bool remove_locked_first_if(std::mutex &mutex, std::forward_list<T, Allocator> &list, Predicate pred) {
std::lock_guard<std::mutex> lock(mutex);
auto oit = list.before_begin(), it = std::next(oit);
while (it != list.end()) {
if (pred(*it)) {
list.erase_after(oit);
return true;
}
oit = it++;
}
return false;
}
#endif
#endif // __UTILS_H_