diff --git a/src/FileInfos.h b/src/FileInfos.h new file mode 100644 index 0000000..54062e9 --- /dev/null +++ b/src/FileInfos.h @@ -0,0 +1,86 @@ +#pragma once +#include "utils/logger.h" +#include +#include +#include +#include +#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 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; +}; \ No newline at end of file diff --git a/src/FileWrapper.cpp b/src/FileWrapper.cpp deleted file mode 100644 index d670ba5..0000000 --- a/src/FileWrapper.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "FileWrapper.h" -#include "fileinfos.h" -#include "utils/StringTools.h" -#include "utils/logger.h" -#include -#include -#include -#include -#include -#include - -FileHandleWrapper gFileHandleWrapper[FILE_WRAPPER_SIZE] __attribute__((section(".data"))); - -std::mutex fileWrapperMutex; - -int FileHandleWrapper_GetSlot() { - std::lock_guard 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 lock(fileWrapperMutex); - gFileHandleWrapper[slot].handle = 0; - gFileHandleWrapper[slot].inUse = false; - OSMemoryBarrier(); - return -1; -} - -bool FileHandleWrapper_FreeAll() { - std::lock_guard 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; -} \ No newline at end of file diff --git a/src/FileWrapper.h b/src/FileWrapper.h deleted file mode 100644 index 2fd6c4b..0000000 --- a/src/FileWrapper.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include - -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(); \ No newline at end of file diff --git a/src/SaveRedirection.cpp b/src/SaveRedirection.cpp index 483898e..e6efe0c 100644 --- a/src/SaveRedirection.cpp +++ b/src/SaveRedirection.cpp @@ -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(); diff --git a/src/fileinfos.cpp b/src/fileinfos.cpp deleted file mode 100644 index 943f727..0000000 --- a/src/fileinfos.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "fileinfos.h" -#include "utils/logger.h" -#include -#include -#include -#include -#include - -FileInfos gFileInfos[FILE_INFO_SIZE] __attribute__((section(".data"))); -std::mutex fileinfoMutex; - -int32_t getIDByLowerTitleID(uint32_t lowerTitleID) { - std::lock_guard 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 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 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; -} \ No newline at end of file diff --git a/src/fileinfos.h b/src/fileinfos.h deleted file mode 100644 index fc1170f..0000000 --- a/src/fileinfos.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -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); diff --git a/src/fs/DirList.cpp b/src/fs/DirList.cpp index 3198efb..decf637 100644 --- a/src/fs/DirList.cpp +++ b/src/fs/DirList.cpp @@ -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 diff --git a/src/fs/FSUtils.cpp b/src/fs/FSUtils.cpp index c9e3ff4..748525d 100644 --- a/src/fs/FSUtils.cpp +++ b/src/fs/FSUtils.cpp @@ -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; } diff --git a/src/fs/FileReader.cpp b/src/fs/FileReader.cpp new file mode 100644 index 0000000..1e264e2 --- /dev/null +++ b/src/fs/FileReader.cpp @@ -0,0 +1,54 @@ +#include "FileReader.h" +#include "../utils/logger.h" +#include + +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; +} \ No newline at end of file diff --git a/src/fs/FileReader.h b/src/fs/FileReader.h new file mode 100644 index 0000000..ace6a89 --- /dev/null +++ b/src/fs/FileReader.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include + +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(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; +}; diff --git a/src/fs/FileReaderWUHB.cpp b/src/fs/FileReaderWUHB.cpp new file mode 100644 index 0000000..07c4e71 --- /dev/null +++ b/src/fs/FileReaderWUHB.cpp @@ -0,0 +1,79 @@ +#include "FileReaderWUHB.h" +#include "utils/StringTools.h" +#include "utils/logger.h" +#include +#include + +FileReaderWUHB::FileReaderWUHB(const std::shared_ptr& 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 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 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; +} diff --git a/src/fs/FileReaderWUHB.h b/src/fs/FileReaderWUHB.h new file mode 100644 index 0000000..45fcf30 --- /dev/null +++ b/src/fs/FileReaderWUHB.h @@ -0,0 +1,18 @@ +#pragma once +#include "../FileInfos.h" +#include "FileReader.h" +#include + + +class FileReaderWUHB : public FileReader { + bool initDone = false; + std::shared_ptr info; + WUHBFileHandle fileHandle = 0; + bool autoUnmount = false; + +public: + explicit FileReaderWUHB(const std::shared_ptr& info, const std::string &relativeFilepath, bool autoUnmount); + ~FileReaderWUHB() override; + int64_t read(uint8_t *buffer, uint32_t size) override; + bool isReady() override; +}; diff --git a/src/main.cpp b/src/main.cpp index 9fd1a47..5168a71 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 @@ -14,10 +15,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -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> fileInfos; + +std::mutex fileReaderListMutex; +std::forward_list> openFileReaders; + void readCustomTitlesFromSD(); WUPS_USE_WUT_DEVOPTAB(); @@ -52,8 +61,6 @@ WUPS_USE_WUT_DEVOPTAB(); INITIALIZE_PLUGIN() { memset((void *) ¤t_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 lock1(fileReaderListMutex); + openFileReaders.clear(); + } + { + const std::lock_guard 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> getIDByLowerTitleID(uint32_t titleid_lower) { + std::lock_guard 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 *) 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 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(relativeFilepath); + if (!fileInfo) { + DEBUG_FUNCTION_LINE_ERR("No more memory"); + break; + } - MCPTitleListType *cur_title_info = &(gFileInfos[j].titleInfo); + std::lock_guard 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); } } @@ -327,39 +350,43 @@ 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; + int32_t result = real_MCP_TitleList(handle, outTitleCount, titleList, size); - std::lock_guard 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 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 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,41 +394,42 @@ 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; - 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; + if (pathStr.starts_with(start)) { + std::unique_ptr reader; + if (pathStr.ends_with(tga) || pathStr.ends_with(sound)) { + char *id = path + 1 + strlen(start); + id[8] = 0; + char *relativePath = id + 9; + auto lowerTitleID = strtoul(id, 0, 16); + auto fileInfo = getIDByLowerTitleID(lowerTitleID); + if (fileInfo.has_value()) { + reader = make_unique_nothrow(fileInfo.value(), relativePath, !gHomebrewLaunched); + if (reader && !reader->isReady()) { + reader.reset(); } - if (OpenFileForID(idVal, ending, handle) >= 0) { - return FS_STATUS_OK; - } - } else { - DEBUG_FUNCTION_LINE_ERR("Failed to find id for titleID %08X", lowerTitleID); } - return res; + } + // 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((uint8_t *) iconTex_tga, iconTex_tga_size); + if (reader && !reader->isReady()) { + reader.reset(); + } + } + if (reader) { + std::lock_guard lock(fileReaderListMutex); + *handle = reader->getHandle(); + openFileReaders.push_front(std::move(reader)); + return FS_STATUS_OK; } } @@ -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 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 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; } } diff --git a/src/utils/StringTools.cpp b/src/utils/StringTools.cpp index 88e1c8e..38cd47b 100644 --- a/src/utils/StringTools.cpp +++ b/src/utils/StringTools.cpp @@ -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); diff --git a/src/utils/utils.c b/src/utils/utils.c deleted file mode 100644 index 90e39fa..0000000 --- a/src/utils/utils.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "utils/logger.h" -#include -#include -#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_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); - } - } - } -} diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp new file mode 100644 index 0000000..a59c05a --- /dev/null +++ b/src/utils/utils.cpp @@ -0,0 +1,13 @@ +#include + +/* 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; +} diff --git a/src/utils/utils.h b/src/utils/utils.h index a65f134..d45d939 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,36 +1,32 @@ -#ifndef __UTILS_H_ -#define __UTILS_H_ +#pragma once +#include +#include #include -#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 +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)...)); +} + +template +std::shared_ptr make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward(args)...))) { + return std::shared_ptr(new (std::nothrow) T(std::forward(args)...)); +} + +template +bool remove_locked_first_if(std::mutex &mutex, std::forward_list &list, Predicate pred) { + std::lock_guard 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_