From ddd92b10a3e2a6ef9daccb3c31ec004a62adb287 Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:49:38 +0100 Subject: [PATCH] Fix non-ascii usernames, switch GUI to wide strings --- source/app/cfw.cpp | 2 +- source/app/common.h | 25 +-- source/app/dumping.cpp | 238 ++++++++++++++--------------- source/app/interfaces/fat32.cpp | 54 +++---- source/app/interfaces/fat32.h | 2 +- source/app/interfaces/stub.cpp | 22 +-- source/app/interfaces/stub.h | 2 +- source/app/interfaces/transfer.cpp | 2 +- source/app/interfaces/transfer.h | 4 +- source/app/main.cpp | 2 +- source/app/menu.cpp | 204 ++++++++++++------------- source/app/menu.h | 10 +- source/app/progress.cpp | 64 ++++---- source/app/progress.h | 8 +- source/app/titlelist.cpp | 24 +-- source/app/titlelist.h | 2 +- source/app/titles.cpp | 84 +++++----- source/app/titles.h | 4 +- source/app/users.cpp | 5 +- 19 files changed, 379 insertions(+), 379 deletions(-) diff --git a/source/app/cfw.cpp b/source/app/cfw.cpp index 3357fc0..e2e553d 100644 --- a/source/app/cfw.cpp +++ b/source/app/cfw.cpp @@ -64,7 +64,7 @@ CFWVersion testCFW() { return currCFWVersion; } else if (ret == MOCHA_RESULT_UNSUPPORTED_API_VERSION) { - uint8_t forceTiramisu = showDialogPrompt("Using an outdated Tiramisu version\nwithout FS client support!\n\nPlease update Tiramisu with this guide:\nhttps://wiiu.hacks.guide/#/tiramisu/sd-preparation\n\nForcing internal CFW will temporarily stop Tiramisu!", "Exit Dumpling To Update (Recommended)", "Force Internal CFW And Continue"); + uint8_t forceTiramisu = showDialogPrompt(L"Using an outdated Tiramisu version\nwithout FS client support!\n\nPlease update Tiramisu with this guide:\nhttps://wiiu.hacks.guide/#/tiramisu/sd-preparation\n\nForcing internal CFW will temporarily stop Tiramisu!", L"Exit Dumpling To Update (Recommended)", L"Force Internal CFW And Continue"); if (forceTiramisu == 1) { if (stopTiramisuServer()) { WHBLogFreetypeClear(); diff --git a/source/app/common.h b/source/app/common.h index 1c12253..08782c5 100644 --- a/source/app/common.h +++ b/source/app/common.h @@ -102,34 +102,39 @@ typename std::enable_if::enable, E>::type operator& // String trim functions [[maybe_unused]] -static inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char8_t ch) { +static inline void ltrim(std::wstring &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](wchar_t ch) { return !std::isspace(ch); })); } [[maybe_unused]] -static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](char8_t ch) { +static inline void rtrim(std::wstring &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](wchar_t ch) { return !std::isspace(ch); }).base(), s.end()); } [[maybe_unused]] -static inline void trim(std::string &s) { +static inline void trim(std::wstring &s) { ltrim(s); rtrim(s); } [[maybe_unused]] -static inline void replaceAll(std::string& str, const std::string& oldSubstr, const std::string& newSubstr) { +static inline void replaceAll(std::wstring& str, const std::wstring& oldSubstr, const std::wstring& newSubstr) { size_t pos = 0; - while ((pos = str.find(oldSubstr, pos)) != std::string::npos) { + while ((pos = str.find(oldSubstr, pos)) != std::wstring::npos) { str.replace(pos, oldSubstr.length(), newSubstr); pos += newSubstr.length(); } } +[[maybe_unused]] +static inline std::wstring toWstring(const std::string& stringToConvert) { + return std::wstring_convert>().from_bytes(stringToConvert); +} + // Enums and Structs enum class DUMP_METHOD { @@ -182,7 +187,7 @@ struct userAccount { bool defaultAccount = false; bool networkAccount; bool passwordCached; - std::string miiName; + std::wstring miiName; std::string persistentIdString; nn::act::SlotNo slot; nn::act::PersistentId persistentId; @@ -233,8 +238,8 @@ struct filePart { struct titleEntry { uint32_t titleLowId; - std::string shortTitle; - std::string productCode; + std::wstring shortTitle; + std::wstring productCode; std::string folderName; std::optional base; diff --git a/source/app/dumping.cpp b/source/app/dumping.cpp index 642d770..4832594 100644 --- a/source/app/dumping.cpp +++ b/source/app/dumping.cpp @@ -34,12 +34,12 @@ enum class WALK_EVENT { bool callback_scanFile(uint64_t& totalBytes, const char* filename, const std::string& srcPath) { struct stat fileStat{}; if (lstat(srcPath.c_str(), &fileStat) == -1) { - std::string errorMessage; - errorMessage += "Failed to retrieve info from source file!\n"; - if (errno == EIO) errorMessage += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; - errorMessage += "\nDetails:\n"; - errorMessage += "Error "+std::to_string(errno)+" when getting stats for:\n"; - errorMessage += srcPath; + std::wstring errorMessage; + errorMessage += L"Failed to retrieve info from source file!\n"; + if (errno == EIO) errorMessage += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; + errorMessage += L"\nDetails:\n"; + errorMessage += L"Error "+std::to_wstring(errno)+L" when getting stats for:\n"; + errorMessage += toWstring(srcPath); setErrorPrompt(errorMessage); return false; } @@ -57,24 +57,24 @@ bool callback_copyFile(TransferInterface* interface, bool& cancelledDumping, con // Check if file is an actual file first struct stat fileStat{}; if (lstat(srcPath.c_str(), &fileStat) == -1) { - std::string errorMessage; - errorMessage += "Failed to retrieve info from source file!\n"; - if (errno == EIO) errorMessage += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; - errorMessage += "\nDetails:\n"; - errorMessage += "Error "+std::to_string(errno)+" when getting stats for:\n"; - errorMessage += srcPath; + std::wstring errorMessage; + errorMessage += L"Failed to retrieve info from source file!\n"; + if (errno == EIO) errorMessage += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; + errorMessage += L"\nDetails:\n"; + errorMessage += L"Error "+std::to_wstring(errno)+L" when getting stats for:\n"; + errorMessage += toWstring(srcPath); setErrorPrompt(errorMessage); return false; } FILE* readHandle = fopen(srcPath.c_str(), "rb"); if (readHandle == nullptr) { - std::string errorMessage; - errorMessage += "Couldn't open the file to copy from!\n"; - if (errno == EIO) errorMessage += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny errors!\n"; - errorMessage += "\nDetails:\n"; - errorMessage += "Error "+std::to_string(errno)+" after opening file!\n"; - errorMessage += srcPath; + std::wstring errorMessage; + errorMessage += L"Couldn't open the file to copy from!\n"; + if (errno == EIO) errorMessage += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny errors!\n"; + errorMessage += L"\nDetails:\n"; + errorMessage += L"Error "+std::to_wstring(errno)+L" after opening file!\n"; + errorMessage += toWstring(srcPath); setErrorPrompt(errorMessage); return false; } @@ -85,8 +85,7 @@ bool callback_copyFile(TransferInterface* interface, bool& cancelledDumping, con uint8_t* copyBuffer = (uint8_t*)aligned_alloc(BUFFER_SIZE_ALIGNMENT, BUFFER_SIZE); if (copyBuffer == nullptr) { fclose(readHandle); - std::string errorMessage = "Failed to allocate memory for chunk buffer!"; - setErrorPrompt(errorMessage); + setErrorPrompt(L"Failed to allocate memory for chunk buffer!"); return false; } @@ -96,11 +95,11 @@ bool callback_copyFile(TransferInterface* interface, bool& cancelledDumping, con free(copyBuffer); copyBuffer = nullptr; fclose(readHandle); - std::string errorMessage; - errorMessage += "Failed to read all data from this file!\n"; - if (errno == EIO) errorMessage += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny errors!\n"; - errorMessage += "Error "+std::to_string(errno)+" when reading data from:\n"; - errorMessage += srcPath; + std::wstring errorMessage; + errorMessage += L"Failed to read all data from this file!\n"; + if (errno == EIO) errorMessage += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny errors!\n"; + errorMessage += L"Error "+std::to_wstring(errno)+L" when reading data from:\n"; + errorMessage += toWstring(srcPath); setErrorPrompt(errorMessage); return false; } @@ -119,7 +118,7 @@ bool callback_copyFile(TransferInterface* interface, bool& cancelledDumping, con // Check whether the inputs break updateInputs(); if (pressedBack()) { - uint8_t selectedChoice = showDialogPrompt("Are you sure that you want to cancel the dumping process?", "Yes", "No"); + uint8_t selectedChoice = showDialogPrompt(L"Are you sure that you want to cancel the dumping process?", L"Yes", L"No"); if (selectedChoice != 0) continue; WHBLogFreetypeClear(); WHBLogPrint("Quitting dumping process..."); @@ -148,8 +147,7 @@ bool callback_copyBuffer(TransferInterface* interface, bool& cancelledDumping, c while (true) { uint8_t* copyBuffer = (uint8_t*)aligned_alloc(BUFFER_SIZE_ALIGNMENT, BUFFER_SIZE); if (copyBuffer == nullptr) { - std::string errorMessage = "Failed to allocate memory for chunk buffer!"; - setErrorPrompt(errorMessage); + setErrorPrompt(L"Failed to allocate memory for chunk buffer!"); return false; } @@ -168,7 +166,7 @@ bool callback_copyBuffer(TransferInterface* interface, bool& cancelledDumping, c // Check whether the inputs break updateInputs(); if (pressedBack()) { - uint8_t selectedChoice = showDialogPrompt("Are you sure that you want to cancel the dumping process?", "Yes", "No"); + uint8_t selectedChoice = showDialogPrompt(L"Are you sure that you want to cancel the dumping process?", L"Yes", L"No"); if (selectedChoice != 0) continue; WHBLogFreetypeClear(); WHBLogPrint("Quitting dumping process..."); @@ -225,15 +223,14 @@ bool walkDirectoryRecursive(const std::string& srcPath, const std::string& destP // Open folder DIR* dirHandle; - if ((dirHandle = opendir((srcPath+"/").c_str())) == nullptr) { - std::string errorMessage; - errorMessage += "Failed to open folder to read content from!\n"; - if (errno == EIO) errorMessage += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; - errorMessage += "\nDetails:\n"; - errorMessage += "Error "+std::to_string(errno)+" while opening folder:\n"; - errorMessage += srcPath; + if (dirHandle = opendir((srcPath+"/").c_str()); dirHandle == nullptr) { + std::wstring errorMessage; + errorMessage += L"Failed to open folder to read content from!\n"; + if (errno == EIO) errorMessage += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; + errorMessage += L"\nDetails:\n"; + errorMessage += L"Error "+std::to_wstring(errno)+L" while opening folder:\n"; + errorMessage += toWstring(srcPath); setErrorPrompt(errorMessage); - setErrorPrompt("Failed to open the directory to read files from"); return false; } @@ -247,7 +244,7 @@ bool walkDirectoryRecursive(const std::string& srcPath, const std::string& destP // Use lstat since readdir returns DT_REG for symlinks struct stat fileStat{}; if (lstat(entrySrcPath.c_str(), &fileStat) != 0) { - setErrorPrompt("Unknown Error "+std::to_string(errno)+":\nCouldn't check type of file/folder!\n"+entrySrcPath); + setErrorPrompt(L"Unknown Error "+std::to_wstring(errno)+L":\nCouldn't check type of file/folder!\n"+toWstring(entrySrcPath)); return false; } if (S_ISLNK(fileStat.st_mode)) { @@ -272,21 +269,21 @@ bool walkDirectoryRecursive(const std::string& srcPath, const std::string& destP } } else if (errno == EIO) { - std::string errorMessage = - "The Wii U had an issue while reading the disc(?)!\n" - "Make sure that its clean!\nDumping is very sensitive to tiny issues!\n" - "Unfortunately, disc requires reinsertion to retry!\nTry restarting Dumpling and trying again.\n\nDetails:\n" - "Error "+std::to_string(errno)+" while iterating folder contents:\n" - +srcPath; + std::wstring errorMessage = + L"The Wii U had an issue while reading the disc(?)!\n" + L"Make sure that its clean!\nDumping is very sensitive to tiny issues!\n" + L"Unfortunately, disc requires reinsertion to retry!\nTry restarting Dumpling and trying again.\n\nDetails:\n" + L"Error "+std::to_wstring(errno)+L" while iterating folder contents:\n" + +toWstring(srcPath); setErrorPrompt(errorMessage); return false; } else if (errno != 0) { - std::string errorMessage = - "Unknown error while reading disc contents!\n" - "You might want to restart your console and try again.\n\nDetails:\n" - "Error "+std::to_string(errno)+" while iterating folder contents:\n" - +srcPath; + std::wstring errorMessage = + L"Unknown error while reading disc contents!\n" + L"You might want to restart your console and try again.\n\nDetails:\n" + L"Error "+std::to_wstring(errno)+L" while iterating folder contents:\n" + +toWstring(srcPath); setErrorPrompt(errorMessage); return false; } @@ -377,11 +374,11 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c if (deletedCount % 10 == 0) { WHBLogFreetypeStartScreen(); WHBLogPrint("Checking for existing files!"); - WHBLogFreetypePrint(""); - WHBLogFreetypePrint("This might take a few minutes if there's a lot of files..."); - WHBLogFreetypePrint("Please don't turn your Wii U off!"); - WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("Found and deleted %llu files...", deletedCount); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrint(L"This might take a few minutes if there's a lot of files..."); + WHBLogFreetypePrint(L"Please don't turn your Wii U off!"); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"Found and deleted %llu files...", deletedCount); WHBLogFreetypeDrawScreen(); } deletedCount++; @@ -389,7 +386,7 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c showDeleteProgress(); for (auto& entry : queue) { if (!deleteTitleEntry(*entry, config, showDeleteProgress)) { - showErrorPrompt("Exit to Main Menu"); + showErrorPrompt(L"Exit to Main Menu"); return false; } } @@ -402,14 +399,14 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c for (size_t i=0; ishortTitle.c_str(), i+1, queue.size()); - WHBLogFreetypePrint(""); - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE001 Button = Cancel scanning and just do dumping"); + WHBLogFreetypePrint(L"Scanning the dump size!"); + WHBLogFreetypePrint(L"This might take a few minutes if you selected a lot of titles..."); + WHBLogFreetypePrint(L"Your Wii U isn't frozen in that case!"); + WHBLogFreetypePrint(L""); + WHBLogPrintf("Scanning %S... (title %lu / %lu)", queue[i]->shortTitle.c_str(), i+1, queue.size()); + WHBLogFreetypePrint(L""); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE001 Button = Cancel scanning and just do dumping"); WHBLogFreetypeDraw(); titleEntry& queueEntry = *queue[i]; bool cancelledDumping = false; @@ -431,8 +428,8 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c break; } else if (!scanSuccess) { - showErrorPrompt("Back To Main Menu"); - showDialogPrompt("Failed while trying to scan the dump for its size!", "Exit to Main Menu"); + showErrorPrompt(L"Back To Main Menu"); + showDialogPrompt(L"Failed while trying to scan the dump for its size!", L"Exit to Main Menu"); return false; } } @@ -441,17 +438,17 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c // Check if there's enough space on the storage location and otherwise give a warning uint64_t sizeAvailable = !USE_WUT_DEVOPTAB() ? Fat32Transfer::getDriveSpace(config.dumpTarget) : std::numeric_limits::max(); if (sizeAvailable < totalDumpSize) { - std::string freeSpaceWarning; - freeSpaceWarning += "You only have " + formatByteSize(sizeAvailable) + ", while you require " + formatByteSize(totalDumpSize) + " of free space!\n"; - if (sizeAvailable == 0) freeSpaceWarning += "Make sure that your storage device is still plugged in!"; - else freeSpaceWarning += "Make enough space, or dump one game at a time."; - showDialogPrompt(freeSpaceWarning.c_str(), "Exit to Main Menu"); + std::wstring freeSpaceWarning; + freeSpaceWarning += L"You only have " + formatByteSize(sizeAvailable) + L", while you require " + formatByteSize(totalDumpSize) + L" of free space!\n"; + if (sizeAvailable == 0) freeSpaceWarning += L"Make sure that your storage device is still plugged in!"; + else freeSpaceWarning += L"Make enough space, or dump one game at a time."; + showDialogPrompt(freeSpaceWarning.c_str(), L"Exit to Main Menu"); return false; } else { WHBLogFreetypeClear(); - WHBLogPrintf("Dump is %s while selected location has %s available!", formatByteSize(totalDumpSize).c_str(), formatByteSize(sizeAvailable).c_str()); - WHBLogFreetypePrint("Dumping will start in 10 seconds..."); + WHBLogFreetypePrintf(L"Dump is %S while selected location has %S available!", formatByteSize(totalDumpSize).c_str(), formatByteSize(sizeAvailable).c_str()); + WHBLogFreetypePrint(L"Dumping will start in 10 seconds..."); WHBLogFreetypeDraw(); sleep_for(10s); } @@ -463,19 +460,19 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c // Dump files startQueue(totalDumpSize); for (size_t i=0; i 1) { - if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::GAME)) status += " (game "; - else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::UPDATE)) status += " (update "; - else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::DLC)) status += " (DLC "; - else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::SAVES)) status += " (save "; - else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::SYSTEM_APP)) status += " (app "; - else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::CUSTOM)) status += " (custom "; - else status += " (title "+std::to_string(i+1)+"/"+std::to_string(queue.size())+")"; + if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::GAME)) status += L" (game "; + else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::UPDATE)) status += L" (update "; + else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::DLC)) status += L" (DLC "; + else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::SAVES)) status += L" (save "; + else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::SYSTEM_APP)) status += L" (app "; + else if (HAS_FLAG(config.filterTypes, DUMP_TYPE_FLAGS::CUSTOM)) status += L" (custom "; + else status += L" (title "+std::to_wstring(i+1)+L"/"+std::to_wstring(queue.size())+L")"; } - status += "..."; + status += L"..."; setDumpingStatus(status); bool cancelledDumping = false; @@ -501,27 +498,27 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c if (!dumpSuccess) { interface.reset(); if (!cancelledDumping) { - showErrorPrompt("Back To Main Menu"); + showErrorPrompt(L"Back To Main Menu"); } - uint8_t doDeleteFiles = showDialogPrompt("Delete the incomplete files from the currently transferring title?\nThis will make space to retry or dump something else.\nDepending on the size it might take a few minutes.\nAlready completed titles will not be deleted...", "Yes", "No"); + uint8_t doDeleteFiles = showDialogPrompt(L"Delete the incomplete files from the currently transferring title?\nThis will make space to retry or dump something else.\nDepending on the size it might take a few minutes.\nAlready completed titles will not be deleted...", L"Yes", L"No"); if (doDeleteFiles == 0) { uint64_t deletedCount = 0; auto showDeleteProgress = [&deletedCount]() { if (deletedCount % 10 == 0) { WHBLogFreetypeStartScreen(); - WHBLogFreetypePrint("Deleting incomplete dumping files!"); - WHBLogFreetypePrint(""); - WHBLogFreetypePrint("This might take a few minutes if there's a lot of files..."); - WHBLogFreetypePrint("Please don't turn your Wii U off!"); - WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("Deleted %llu files so far...", deletedCount); + WHBLogFreetypePrint(L"Deleting incomplete dumping files!"); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrint(L"This might take a few minutes if there's a lot of files..."); + WHBLogFreetypePrint(L"Please don't turn your Wii U off!"); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"Deleted %llu files so far...", deletedCount); WHBLogFreetypeDrawScreen(); } deletedCount++; }; showDeleteProgress(); if (!deleteTitleEntry(queueEntry, config, showDeleteProgress)) { - showErrorPrompt("Back To Main Menu"); + showErrorPrompt(L"Back To Main Menu"); } } return false; @@ -532,11 +529,11 @@ bool dumpQueue(std::vector>& queue, dumpingConfig& c void dumpMLC() { std::vector> queue = { - std::make_shared(titleEntry{.shortTitle = "MLC Dump", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01"), .outputPath = "/MLC Dump"}}) + std::make_shared(titleEntry{.shortTitle = L"MLC Dump", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01"), .outputPath = "/MLC Dump"}}) }; dumpingConfig mlcConfig = {.dumpTypes = DUMP_TYPE_FLAGS::CUSTOM}; - uint8_t selectedChoice = showDialogPrompt("Dumping the MLC can take 6-20 hours depending on its contents!\nMake sure that you have enough space available.", "Proceed", "Cancel"); + uint8_t selectedChoice = showDialogPrompt(L"Dumping the MLC can take 6-20 hours depending on its contents!\nMake sure that you have enough space available.", L"Proceed", L"Cancel"); if (selectedChoice == 1) return; // Show the option screen @@ -545,7 +542,7 @@ void dumpMLC() { } mlcConfig.scanTitleSize = false; - if (dumpQueue(queue, mlcConfig)) showDialogPrompt("Successfully dumped entire MLC!", "OK"); + if (dumpQueue(queue, mlcConfig)) showDialogPrompt(L"Successfully dumped entire MLC!", L"OK"); } bool dumpDisc() { @@ -565,8 +562,8 @@ bool dumpDisc() { WHBLogPrint("Reinsert the disc if you started Dumpling"); WHBLogPrint("with a disc already inserted!"); WHBLogPrint(""); - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE001 Button = Back to Main Menu"); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE001 Button = Back to Main Menu"); WHBLogFreetypeDrawScreen(); sleep_for(100ms); @@ -589,12 +586,12 @@ bool dumpDisc() { WHBLogFreetypeDraw(); if (!mountDisc()) { - showDialogPrompt("Error while mounting disc!", "OK"); + showDialogPrompt(L"Error while mounting disc!", L"OK"); return false; } if (!loadTitles(false)) { - showDialogPrompt("Error while scanning disc titles!", "OK"); + showDialogPrompt(L"Error while scanning disc titles!", L"OK"); return false; } @@ -608,15 +605,15 @@ bool dumpDisc() { } WHBLogFreetypeClear(); - WHBLogPrint("Currently inserted disc is:"); - WHBLogPrint(queue.begin()->get()->shortTitle.c_str()); - WHBLogFreetypePrint(""); - WHBLogFreetypePrint("Continuing to next step in 5 seconds..."); + WHBLogFreetypePrint(L"Currently inserted disc is:"); + WHBLogFreetypePrint(queue.begin()->get()->shortTitle.c_str()); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrint(L"Continuing to next step in 5 seconds..."); WHBLogFreetypeDraw(); sleep_for(5s); // Dump queue - uint8_t dumpOnlyBase = showDialogPrompt("Do you want to dump the update and DLC files?\nIt might be faster when you want to:\n - Dump all updates/DLC at once, without disc swapping!\n - Through methods like Cemu's Online Features, DumpsterU etc.", "Yes", "No"); + uint8_t dumpOnlyBase = showDialogPrompt(L"Do you want to dump the update and DLC files?\nIt might be faster when you want to:\n - Dump all updates/DLC at once, without disc swapping!\n - Through methods like Cemu's Online Features, DumpsterU etc.", L"Yes", L"No"); dumpingConfig config = { .filterTypes = DUMP_TYPE_FLAGS::GAME, .dumpTypes = (dumpOnlyBase == 0) ? (DUMP_TYPE_FLAGS::GAME | DUMP_TYPE_FLAGS::UPDATE | DUMP_TYPE_FLAGS::DLC | DUMP_TYPE_FLAGS::SAVES) : (DUMP_TYPE_FLAGS::GAME | DUMP_TYPE_FLAGS::SAVES) @@ -627,12 +624,12 @@ bool dumpDisc() { if (dumpQueue(queue, config)) { auto endTime = std::chrono::system_clock::now(); auto elapsedDuration = std::chrono::duration_cast(endTime - startTime); - std::string finishedMsg = "Successfully dumped disc in\n"+formatElapsedTime(elapsedDuration)+"!"; - showDialogPrompt(finishedMsg.c_str(), "OK"); + std::wstring finishedMsg = L"Successfully dumped disc in\n"+formatElapsedTime(elapsedDuration)+L"!"; + showDialogPrompt(finishedMsg.c_str(), L"OK"); return true; } else { - showDialogPrompt("Failed to dump disc files...", "OK"); + showDialogPrompt(L"Failed to dump disc files...", L"OK"); return false; } } @@ -651,11 +648,16 @@ void dumpOnlineFiles() { std::string accountNameStr = normalizeFolderName(userIt->miiName); if (accountNameStr.empty()) accountNameStr = "Non-Ascii Username ["+accountIdStr+"]"; - titleEntry ecAccountInfoEntry{.shortTitle = "eShop Key File", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/usr/save/system/nim/ec"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/usr/save/system/nim/ec"}}; - titleEntry miiEntry{.shortTitle = "Mii Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10056000/content"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10056000/content"}}; - titleEntry ccertsEntry{.shortTitle = "ccerts Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10054000/content/ccerts"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10054000/content/ccerts"}}; - titleEntry scertsEntry{.shortTitle = "scerts Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10054000/content/scerts"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10054000/content/scerts"}}; - titleEntry accountsEntry{.shortTitle = "Selected Account", .customFolder = folderPart{.inputPath = convertToPosixPath((std::string("/vol/storage_mlc01/usr/save/system/act/") + accountIdStr).c_str()), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/usr/save/system/act/" + (onlineConfig.dumpAsDefaultUser ? "80000001" : accountIdStr)}}; + titleEntry ecAccountInfoEntry{.shortTitle = L"eShop Key File", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/usr/save/system/nim/ec"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/usr/save/system/nim/ec"}}; + titleEntry miiEntry{.shortTitle = L"Mii Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10056000/content"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10056000/content"}}; + titleEntry ccertsEntry{.shortTitle = L"ccerts Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10054000/content/ccerts"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10054000/content/ccerts"}}; + titleEntry scertsEntry{.shortTitle = L"scerts Files", .customFolder = folderPart{.inputPath = convertToPosixPath("/vol/storage_mlc01/sys/title/0005001b/10054000/content/scerts"), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/sys/title/0005001b/10054000/content/scerts"}}; + titleEntry accountsEntry{.shortTitle = L"Selected Account", .customFolder = folderPart{.inputPath = convertToPosixPath((std::string("/vol/storage_mlc01/usr/save/system/act/") + accountIdStr).c_str()), .outputPath = "/Online Files/" + accountNameStr + "/mlc01/usr/save/system/act/" + (onlineConfig.dumpAsDefaultUser ? "80000001" : accountIdStr)}}; + queue.emplace_back(std::make_shared(ecAccountInfoEntry)); + queue.emplace_back(std::make_shared(miiEntry)); + queue.emplace_back(std::make_shared(ccertsEntry)); + queue.emplace_back(std::make_shared(scertsEntry)); + queue.emplace_back(std::make_shared(accountsEntry)); // Dump otp.bin and seeprom.bin WiiUConsoleOTP otpReturn; @@ -664,20 +666,14 @@ void dumpOnlineFiles() { Mocha_ReadOTP(&otpReturn); Mocha_SEEPROMRead(seepromBuffer.data(), 0, seepromBuffer.size()); - titleEntry seepromFile{.shortTitle = "seeprom.bin File", .customFile = filePart{.srcBuffer = seepromBuffer.data(), .srcBufferSize = seepromBuffer.size(), .outputFolder = "/Online Files", .outputFile = "seeprom.bin"}}; - titleEntry otpFile{.shortTitle = "otp.bin File", .customFile = filePart{.srcBuffer = (uint8_t*)&otpReturn, .srcBufferSize = sizeof(otpReturn), .outputFolder = "/Online Files", .outputFile = "otp.bin"}}; - - // Add custom title entries to queue - queue.emplace_back(std::make_shared(ecAccountInfoEntry)); - queue.emplace_back(std::make_shared(miiEntry)); - queue.emplace_back(std::make_shared(ccertsEntry)); - queue.emplace_back(std::make_shared(scertsEntry)); - queue.emplace_back(std::make_shared(accountsEntry)); + titleEntry seepromFile{.shortTitle = L"seeprom.bin File", .customFile = filePart{.srcBuffer = seepromBuffer.data(), .srcBufferSize = seepromBuffer.size(), .outputFolder = "/Online Files", .outputFile = "seeprom.bin"}}; + titleEntry otpFile{.shortTitle = L"otp.bin File", .customFile = filePart{.srcBuffer = (uint8_t*)&otpReturn, .srcBufferSize = sizeof(otpReturn), .outputFolder = "/Online Files", .outputFile = "otp.bin"}}; queue.emplace_back(std::make_shared(seepromFile)); queue.emplace_back(std::make_shared(otpFile)); - if (dumpQueue(queue, onlineConfig)) showDialogPrompt("Successfully dumped all of the online files!", "OK"); - else showDialogPrompt("Failed to dump the online files...", "OK"); + + if (dumpQueue(queue, onlineConfig)) showDialogPrompt(L"Successfully dumped all of the online files!", L"OK"); + else showDialogPrompt(L"Failed to dump the online files...", L"OK"); } void cleanDumpingProcess() { diff --git a/source/app/interfaces/fat32.cpp b/source/app/interfaces/fat32.cpp index 276901f..fdd18b5 100644 --- a/source/app/interfaces/fat32.cpp +++ b/source/app/interfaces/fat32.cpp @@ -41,9 +41,9 @@ std::vector> Fat32Transfer::getDrives() { else if (i == DEV_USB01_REF) volumeName = "USB 1"; else if (i == DEV_USB02_REF) volumeName = "USB 2"; if (volumeLabel[0]) volumeName += std::string(" - ") + volumeLabel; - else volumeName += " - Not Named"; + else volumeName += " - Unnamed"; - targets.emplace_back(std::make_pair((driveIdPath+Fat32Transfer::targetDirectoryName).c_str(), volumeName)); + targets.emplace_back((driveIdPath+Fat32Transfer::targetDirectoryName).c_str(), volumeName); } return targets; } @@ -101,7 +101,7 @@ bool Fat32Transfer::deletePath(const std::string &path, const std::function FRESULT { DIR dir; if (FRESULT res = f_opendir(&dir, dirPath.c_str()); res != FR_OK) { - std::string deleteError = "Failed while deleting folder entry in recursive loop:\n"; - deleteError += dirPath.string()+"\n"; + std::wstring deleteError = L"Failed while deleting folder entry in recursive loop:\n"; + deleteError += dirPath.wstring()+L"\n"; setErrorPrompt(deleteError); return res; } @@ -137,8 +137,8 @@ bool Fat32Transfer::deletePath(const std::string &path, const std::functionfs = (FATFSPtr*)malloc(sizeof(FATFS)); if (FRESULT res = f_mount(this->fs, this->fatTarget.c_str(), 1); res != FR_OK) { this->runThreadLoop = false; - this->error = "Drive "+this->fatTarget+" couldn't be mounted! Error #"+std::to_string(res)+"\n"; + this->error = L"Drive "+toWstring(this->fatTarget)+L" couldn't be mounted! Error #"+std::to_wstring(res)+L"\n"; } if (this->runThreadLoop) { @@ -218,19 +218,19 @@ void Fat32Transfer::transferThreadLoop(dumpingConfig config) { [this](CommandSwitchDir& arg) { DEBUG_OSReport("Switch Directory: path=%s", arg.dirPath.c_str()); if (FRESULT res = f_chdir((this->fatTarget+arg.dirPath).c_str()); res != FR_OK) { - this->error += "Failed to switch to the given directory!\n"; - this->error += "Error "+std::to_string(res)+" after changing directory:\n"; - this->error += this->fatTarget+arg.dirPath; + this->error += L"Failed to switch to the given directory!\n"; + this->error += L"Error "+std::to_wstring(res)+L" after changing directory:\n"; + this->error += toWstring(this->fatTarget+arg.dirPath); this->runThreadLoop = false; } }, [this](CommandMakeDir& arg) { DEBUG_OSReport("Make Directory: path=%s", arg.dirPath.c_str()); if (FRESULT res = f_mkdir((this->fatTarget+arg.dirPath).c_str()); res != FR_OK && res != FR_EXIST/* && res != FR_NO_PATH*/) { - this->error += "Couldn't make the directory!\n"; - this->error += "For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; - this->error += "Error "+std::to_string(res)+" after creating directory:\n"; - this->error += this->fatTarget+arg.dirPath; + this->error += L"Couldn't make the directory!\n"; + this->error += L"For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; + this->error += L"Error "+std::to_wstring(res)+L" after creating directory:\n"; + this->error += toWstring(this->fatTarget+arg.dirPath); this->runThreadLoop = false; } }, @@ -240,10 +240,10 @@ void Fat32Transfer::transferThreadLoop(dumpingConfig config) { if (FRESULT res = (FRESULT) this->openFile(this->fatTarget + arg.filePath, arg.fileSize); res != FR_OK) { free(arg.chunkBuffer); this->closeFile(this->fatTarget + arg.filePath); - this->error += "Couldn't open the file to copy to!\n"; - this->error += "For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; - this->error += "Error "+std::to_string(res)+" after creating SD card file:\n"; - this->error += this->fatTarget+arg.filePath; + this->error += L"Couldn't open the file to copy to!\n"; + this->error += L"For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; + this->error += L"Error "+std::to_wstring(res)+L" after creating SD card file:\n"; + this->error += toWstring(this->fatTarget+arg.filePath); this->runThreadLoop = false; return; } @@ -252,11 +252,11 @@ void Fat32Transfer::transferThreadLoop(dumpingConfig config) { if (FRESULT res = f_write(this->currFileHandle, arg.chunkBuffer, arg.chunkSize, &bytesWritten); res != FR_OK || bytesWritten != arg.chunkSize) { free(arg.chunkBuffer); this->closeFile(this->fatTarget + arg.filePath); - this->error += "Failed to write data to dumping device!\n"; - if (res == FR_DENIED) this->error += "There's no space available on the USB/SD card!\n"; - this->error += "\nDetails:\n"; - this->error += "Error "+std::to_string(res)+" when writing data to:\n"; - this->error += this->fatTarget+arg.filePath; + this->error += L"Failed to write data to dumping device!\n"; + if (res == FR_DENIED) this->error += L"There's no space available on the USB/SD card!\n"; + this->error += L"\nDetails:\n"; + this->error += L"Error "+std::to_wstring(res)+L" when writing data to:\n"; + this->error += toWstring(this->fatTarget+arg.filePath); this->runThreadLoop = false; return; } @@ -273,7 +273,7 @@ void Fat32Transfer::transferThreadLoop(dumpingConfig config) { } OSReport("Cleaning up the thread...\n"); - OSReport(this->error.c_str()); + //OSReport(this->error.c_str()); // Shutdown fatfs thread if (this->currFileHandle != nullptr) { diff --git a/source/app/interfaces/fat32.h b/source/app/interfaces/fat32.h index d8e27ef..e3689ff 100644 --- a/source/app/interfaces/fat32.h +++ b/source/app/interfaces/fat32.h @@ -16,7 +16,7 @@ class Fat32Transfer : public TransferInterface { static constexpr const char* targetDirectoryName = "Dumpling"; std::string fatTarget; - std::string error; + std::wstring error; struct DIRPtr; std::unique_ptr currDir; diff --git a/source/app/interfaces/stub.cpp b/source/app/interfaces/stub.cpp index 8e6292c..d501a68 100644 --- a/source/app/interfaces/stub.cpp +++ b/source/app/interfaces/stub.cpp @@ -53,10 +53,10 @@ void StubTransfer::transferThreadLoop(dumpingConfig config) { if (destFileHandle == nullptr) { this->closeFileHandle(arg.filePath); free(arg.chunkBuffer); - this->error += "Couldn't open the file to copy to!\n"; - this->error += "For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; - this->error += "Error "+std::to_string(errno)+" after creating SD card file:\n"; - this->error += arg.filePath; + this->error += L"Couldn't open the file to copy to!\n"; + this->error += L"For SD cards: Make sure it isn't locked\nby the write-switch on the side!\n\nDetails:\n"; + this->error += L"Error "+std::to_wstring(errno)+L" after creating SD card file:\n"; + this->error += toWstring(arg.filePath); this->runThreadLoop = false; return; } @@ -64,13 +64,13 @@ void StubTransfer::transferThreadLoop(dumpingConfig config) { if (size_t bytesWritten = fwrite(arg.chunkBuffer, sizeof(uint8_t), arg.chunkSize, destFileHandle); bytesWritten != arg.chunkSize) { this->closeFileHandle(arg.filePath); free(arg.chunkBuffer); - this->error += "Failed to write data to dumping device!\n"; - if (errno == ENOSPC) this->error += "There's no space available on the USB/SD card!\n"; - if (errno == EIO) this->error += "For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; - this->error += "\nDetails:\n"; - this->error += "Error "+std::to_string(errno)+" when writing data from:\n"; - this->error += "to:\n"; - this->error += arg.filePath; + this->error += L"Failed to write data to dumping device!\n"; + if (errno == ENOSPC) this->error += L"There's no space available on the USB/SD card!\n"; + if (errno == EIO) this->error += L"For discs: Make sure that its clean!\nDumping is very sensitive to tiny issues!\n"; + this->error += L"\nDetails:\n"; + this->error += L"Error "+std::to_wstring(errno)+L" when writing data from:\n"; + this->error += L"to:\n"; + this->error += toWstring(arg.filePath); this->runThreadLoop = false; return; } diff --git a/source/app/interfaces/stub.h b/source/app/interfaces/stub.h index 5a6cb63..2341750 100644 --- a/source/app/interfaces/stub.h +++ b/source/app/interfaces/stub.h @@ -16,6 +16,6 @@ class StubTransfer : public TransferInterface { const std::string sdPath = "fs:/vol/external01"; - std::string error; + std::wstring error; std::unordered_map fileHandles; }; \ No newline at end of file diff --git a/source/app/interfaces/transfer.cpp b/source/app/interfaces/transfer.cpp index 4bada9d..e79f45f 100644 --- a/source/app/interfaces/transfer.cpp +++ b/source/app/interfaces/transfer.cpp @@ -57,6 +57,6 @@ bool TransferInterface::hasStopped() { return this->threadStopped; } -std::optional TransferInterface::getStopError() { +std::optional TransferInterface::getStopError() { return !this->threadStopped ? std::nullopt : this->threadStoppedError; } \ No newline at end of file diff --git a/source/app/interfaces/transfer.h b/source/app/interfaces/transfer.h index 28ffcdb..a78bbe7 100644 --- a/source/app/interfaces/transfer.h +++ b/source/app/interfaces/transfer.h @@ -36,7 +36,7 @@ class TransferInterface { const uint32_t maxQueueSize = 64; bool hasStopped(); - std::optional getStopError(); + std::optional getStopError(); protected: virtual void transferThreadLoop(dumpingConfig config) = 0; @@ -45,7 +45,7 @@ class TransferInterface { bool runThreadLoop = true; std::atomic threadStopped = false; - std::optional threadStoppedError; + std::optional threadStoppedError; std::thread transferThread; diff --git a/source/app/main.cpp b/source/app/main.cpp index 0335589..f9a050a 100644 --- a/source/app/main.cpp +++ b/source/app/main.cpp @@ -28,7 +28,7 @@ int main() { // Start Dumpling showLoadingScreen(); if (testCFW() != FAILED && ((getCFWVersion() == MOCHA_FSCLIENT || getCFWVersion() == CEMU) || executeExploit()) && initCFW() && mountSystemDrives() && loadUsers() && loadTitles(true)) { - WHBLogFreetypePrint(""); + WHBLogFreetypePrint(L""); WHBLogPrint("Finished loading!"); WHBLogFreetypeDraw(); sleep_for(3s); diff --git a/source/app/menu.cpp b/source/app/menu.cpp index 5d776bc..6230745 100644 --- a/source/app/menu.cpp +++ b/source/app/menu.cpp @@ -15,14 +15,14 @@ void showLoadingScreen() { WHBLogFreetypeSetBackgroundColor(0x0b5d5e00); WHBLogFreetypeSetFontColor(0xFFFFFFFF); - WHBLogFreetypeSetFontSize(22, 22); + WHBLogFreetypeSetFontSize(22); WHBLogPrint("Dumpling V2.6.1"); WHBLogPrint("-- Made by Crementif and Emiyl --"); WHBLogPrint(""); WHBLogFreetypeDraw(); } -#define OPTION(n) (selectedOption == (n) ? '>' : ' ') +#define OPTION(n) (selectedOption == (n) ? L'>' : L' ') // Can get recursively called void showMainMenu() { @@ -31,23 +31,23 @@ void showMainMenu() { while(!startSelectedOption) { // Print menu text WHBLogFreetypeStartScreen(); - WHBLogFreetypePrint("Dumpling V2.6.1"); - WHBLogFreetypePrint("==============================="); - WHBLogFreetypePrintf("%c Dump a game disc", OPTION(0)); - WHBLogFreetypePrintf("%c Dump digital games", OPTION(1)); - WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("%c Dump files to use Cemu online", OPTION(2)); - WHBLogFreetypePrintf("%c Dump Wii U applications (e.g. Friend List, eShop etc.)", OPTION(3)); - // WHBLogFreetypePrintf("%c Dump Amiibo Files", selectedOption==4 ? '>' : ' '); - WHBLogFreetypePrintf(""); - WHBLogFreetypePrintf("%c Dump only Base files of a game", OPTION(4)); - WHBLogFreetypePrintf("%c Dump only Update files of a game", OPTION(5)); - WHBLogFreetypePrintf("%c Dump only DLC files of a game", OPTION(6)); - WHBLogFreetypePrintf("%c Dump only Save files of a game", OPTION(7)); - WHBLogFreetypePrintf("%c Dump whole MLC (everything stored on internal storage)", OPTION(8)); - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE000 Button = Select Option \uE001 Button = Exit Dumpling"); - WHBLogFreetypeScreenPrintBottom(""); + WHBLogFreetypePrint(L"Dumpling V2.6.1"); + WHBLogFreetypePrint(L"==============================="); + WHBLogFreetypePrintf(L"%C Dump a game disc", OPTION(0)); + WHBLogFreetypePrintf(L"%C Dump digital games", OPTION(1)); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"%C Dump files to use Cemu online", OPTION(2)); + WHBLogFreetypePrintf(L"%C Dump Wii U applications (e.g. Friend List, eShop etc.)", OPTION(3)); + // WHBLogFreetypePrintf("%C Dump Amiibo Files", OPTION(4)); + WHBLogFreetypePrintf(L""); + WHBLogFreetypePrintf(L"%C Dump only Base files of a game", OPTION(4)); + WHBLogFreetypePrintf(L"%C Dump only Update files of a game", OPTION(5)); + WHBLogFreetypePrintf(L"%C Dump only DLC files of a game", OPTION(6)); + WHBLogFreetypePrintf(L"%C Dump only Save files of a game", OPTION(7)); + WHBLogFreetypePrintf(L"%C Dump whole MLC (everything stored on internal storage)", OPTION(8)); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE000 Button = Select Option \uE001 Button = Exit Dumpling"); + WHBLogFreetypeScreenPrintBottom(L""); WHBLogFreetypeDrawScreen(); // Loop until there's new input @@ -69,7 +69,7 @@ void showMainMenu() { break; } if (pressedBack()) { - uint8_t exitSelectedOption = showDialogPrompt(getCFWVersion() == MOCHA_FSCLIENT ? "Do you really want to exit Dumpling?" : "Do you really want to exit Dumpling?\nYour console will shutdown to prevent compatibility issues!", "Yes", "No"); + uint8_t exitSelectedOption = showDialogPrompt(getCFWVersion() == MOCHA_FSCLIENT ? L"Do you really want to exit Dumpling?" : L"Do you really want to exit Dumpling?\nYour console will shutdown to prevent compatibility issues!", L"Yes", L"No"); if (exitSelectedOption == 0) { WHBLogFreetypeClear(); return; @@ -86,25 +86,25 @@ void showMainMenu() { dumpDisc(); break; case 1: - showTitleList("Select all the games you want to dump!", {.filterTypes = DUMP_TYPE_FLAGS::GAME, .dumpTypes = (DUMP_TYPE_FLAGS::GAME | DUMP_TYPE_FLAGS::UPDATE | DUMP_TYPE_FLAGS::DLC | DUMP_TYPE_FLAGS::SAVES), .queue = true}); + showTitleList(L"Select all the games you want to dump!", {.filterTypes = DUMP_TYPE_FLAGS::GAME, .dumpTypes = (DUMP_TYPE_FLAGS::GAME | DUMP_TYPE_FLAGS::UPDATE | DUMP_TYPE_FLAGS::DLC | DUMP_TYPE_FLAGS::SAVES), .queue = true}); break; case 2: dumpOnlineFiles(); break; case 3: - showTitleList("Select all the system applications you want to dump!", {.filterTypes = DUMP_TYPE_FLAGS::SYSTEM_APP, .dumpTypes = DUMP_TYPE_FLAGS::GAME, .queue = true}); + showTitleList(L"Select all the system applications you want to dump!", {.filterTypes = DUMP_TYPE_FLAGS::SYSTEM_APP, .dumpTypes = DUMP_TYPE_FLAGS::GAME, .queue = true}); break; case 4: - showTitleList("Select all the games that you want to dump the base game from!", {.filterTypes = DUMP_TYPE_FLAGS::GAME, .dumpTypes = DUMP_TYPE_FLAGS::GAME, .queue = true}); + showTitleList(L"Select all the games that you want to dump the base game from!", {.filterTypes = DUMP_TYPE_FLAGS::GAME, .dumpTypes = DUMP_TYPE_FLAGS::GAME, .queue = true}); break; case 5: - showTitleList("Select all the games that you want to dump the update from!", {.filterTypes = DUMP_TYPE_FLAGS::UPDATE, .dumpTypes = DUMP_TYPE_FLAGS::UPDATE, .queue = true}); + showTitleList(L"Select all the games that you want to dump the update from!", {.filterTypes = DUMP_TYPE_FLAGS::UPDATE, .dumpTypes = DUMP_TYPE_FLAGS::UPDATE, .queue = true}); break; case 6: - showTitleList("Select all the games that you want to dump the DLC from!", {.filterTypes = DUMP_TYPE_FLAGS::DLC, .dumpTypes = DUMP_TYPE_FLAGS::DLC, .queue = true}); + showTitleList(L"Select all the games that you want to dump the DLC from!", {.filterTypes = DUMP_TYPE_FLAGS::DLC, .dumpTypes = DUMP_TYPE_FLAGS::DLC, .queue = true}); break; case 7: - showTitleList("Select all the games that you want to dump the save from!", {.filterTypes = DUMP_TYPE_FLAGS::SAVES, .dumpTypes = DUMP_TYPE_FLAGS::SAVES, .queue = true}); + showTitleList(L"Select all the games that you want to dump the save from!", {.filterTypes = DUMP_TYPE_FLAGS::SAVES, .dumpTypes = DUMP_TYPE_FLAGS::SAVES, .queue = true}); break; case 8: dumpMLC(); @@ -142,85 +142,85 @@ bool showOptionMenu(dumpingConfig& config, bool showAccountOption) { if ((drives.size()-1) < selectedDrive) selectedDrive = drives.size()-1; WHBLogFreetypeStartScreen(); - WHBLogFreetypePrint("Change any options for this dump:"); - WHBLogFreetypePrint("==============================="); - WHBLogFreetypePrintf("%c Dump Destination: %s", OPTION(0), drives.empty() ? "No Drives Detected" : drives[selectedDrive].second.c_str()); - WHBLogFreetypePrintf("%c Do Initial Scan For Required Empty Space: %s", OPTION(1), config.scanTitleSize ? "Yes" : "No"); - if (showAccountOption && dumpingOnlineFiles) WHBLogFreetypePrintf("%c Online Account: %s", OPTION(2), allUsers[selectedAccount].miiName.c_str()); - if (showAccountOption && !dumpingOnlineFiles) WHBLogFreetypePrintf("%c Account To Get Saves From: %s", OPTION(2), allUsers[selectedAccount].miiName.c_str()); - if (showAccountOption && dumpingOnlineFiles) WHBLogFreetypePrintf("%c Merge Account To Default Cemu User: %s", OPTION(3), config.dumpAsDefaultUser ? "Yes" : "No"); - if (showAccountOption && !dumpingOnlineFiles) WHBLogFreetypePrintf("%c Merge Saves To Default Cemu User: %s", OPTION(3), config.dumpAsDefaultUser ? "Yes" : "No"); - if (USE_DEBUG_STUBS == 1) WHBLogFreetypePrintf("%c Change Fat32 Cache Size (DEBUG): %u", OPTION(4), config.debugCacheSize); - else WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("%c [Confirm]", OPTION(5)); - WHBLogFreetypePrintf("%c [Cancel]", OPTION(6)); + WHBLogFreetypePrint(L"Change any options for this dump:"); + WHBLogFreetypePrint(L"==============================="); + WHBLogFreetypePrintf(L"%C Dump Destination: %S", OPTION(0), drives.empty() ? L"No Drives Detected" : toWstring(drives[selectedDrive].second).c_str()); + WHBLogFreetypePrintf(L"%C Do Initial Scan For Required Empty Space: %S", OPTION(1), config.scanTitleSize ? L"Yes" : L"No"); + if (showAccountOption && dumpingOnlineFiles) WHBLogFreetypePrintf(L"%C Online Account: %S", OPTION(2), allUsers[selectedAccount].miiName.c_str()); + if (showAccountOption && !dumpingOnlineFiles) WHBLogFreetypePrintf(L"%C Account To Get Saves From: %S", OPTION(2), allUsers[selectedAccount].miiName.c_str()); + if (showAccountOption && dumpingOnlineFiles) WHBLogFreetypePrintf(L"%C Merge Account To Default Cemu User: %S", OPTION(3), config.dumpAsDefaultUser ? L"Yes" : L"No"); + if (showAccountOption && !dumpingOnlineFiles) WHBLogFreetypePrintf(L"%C Merge Saves To Default Cemu User: %S", OPTION(3), config.dumpAsDefaultUser ? L"Yes" : L"No"); + if (USE_DEBUG_STUBS == 1) WHBLogFreetypePrintf(L"%C Change Fat32 Cache Size (DEBUG): %u", OPTION(4), config.debugCacheSize); + else WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"%C [Confirm]", OPTION(5)); + WHBLogFreetypePrintf(L"%C [Cancel]", OPTION(6)); if (selectedOption <= 4) { - WHBLogFreetypePrint("==============================="); + WHBLogFreetypePrint(L"==============================="); } if (selectedOption == 0) { - WHBLogFreetypePrint("Select an fat32/exfat device to write the files too."); - WHBLogFreetypePrint("Press \uE000 to refresh list. If your device doesn't show up:"); - WHBLogFreetypePrint(" - Reinsert the SD card/USB drive and double-check the lock switch"); - WHBLogFreetypePrint(" - Make sure its formatted as fat32/exfat drive"); - WHBLogFreetypePrint(" - It doesn't have multiple (hidden) partitions"); - WHBLogFreetypePrint(" - Try a different SD card (recommended) or USB drive"); + WHBLogFreetypePrint(L"Select an fat32/exfat device to write the files too."); + WHBLogFreetypePrint(L"Press \uE000 to refresh list. If your device doesn't show up:"); + WHBLogFreetypePrint(L" - Reinsert the SD card/USB drive and double-check the lock switch"); + WHBLogFreetypePrint(L" - Make sure its formatted as fat32/exfat drive"); + WHBLogFreetypePrint(L" - It doesn't have multiple (hidden) partitions"); + WHBLogFreetypePrint(L" - Try a different SD card (recommended) or USB drive"); } else if (selectedOption == 1) { - WHBLogFreetypePrint("Doing an initial scan allows you to:"); - WHBLogFreetypePrint(" - Determine space required on SD/USB destination"); - WHBLogFreetypePrint(" - Show overall progress while dumping"); - WHBLogFreetypePrint(" - Show rough time estimate while dumping"); - WHBLogFreetypePrint("This takes a few minutes (depending on size) extra."); + WHBLogFreetypePrint(L"Doing an initial scan allows you to:"); + WHBLogFreetypePrint(L" - Determine space required on SD/USB destination"); + WHBLogFreetypePrint(L" - Show overall progress while dumping"); + WHBLogFreetypePrint(L" - Show rough time estimate while dumping"); + WHBLogFreetypePrint(L"This takes a few minutes (depending on size) extra."); } else if (selectedOption == 2 && dumpingOnlineFiles) { - WHBLogFreetypePrint("Select the account you want to dump the online files for."); + WHBLogFreetypePrint(L"Select the account you want to dump the online files for."); if (!allUsers[selectedAccount].networkAccount) { - WHBLogFreetypePrint("This account doesn't have a NNID connected!"); - WHBLogFreetypePrint(" - Click the Mii icon on the Wii U's homescreen"); - WHBLogFreetypePrint(" - Click on Link a Nintendo Network ID option"); - WHBLogFreetypePrint(" - Enable the Save Password option"); - WHBLogFreetypePrint(" - Return to Dumpling"); + WHBLogFreetypePrint(L"This account doesn't have a NNID connected!"); + WHBLogFreetypePrint(L" - Click the Mii icon on the Wii U's homescreen"); + WHBLogFreetypePrint(L" - Click on Link a Nintendo Network ID option"); + WHBLogFreetypePrint(L" - Enable the Save Password option"); + WHBLogFreetypePrint(L" - Return to Dumpling"); } else if (!allUsers[selectedAccount].passwordCached) { - WHBLogFreetypePrint("This account doesn't have it's password saved!"); - WHBLogFreetypePrint("This is required to use Cemu online!"); - WHBLogFreetypePrint(" - Click the Mii icon on the Wii U's homescreen"); - WHBLogFreetypePrint(" - Enable the Save Password option"); - WHBLogFreetypePrint(" - Return to Dumpling"); + WHBLogFreetypePrint(L"This account doesn't have it's password saved!"); + WHBLogFreetypePrint(L"This is required to use Cemu online!"); + WHBLogFreetypePrint(L" - Click the Mii icon on the Wii U's homescreen"); + WHBLogFreetypePrint(L" - Enable the Save Password option"); + WHBLogFreetypePrint(L" - Return to Dumpling"); } else { - WHBLogFreetypePrint("This account is ready for Cemu's online functionality:"); - WHBLogFreetypePrint(" - Selected account has NNID connected to it!"); - WHBLogFreetypePrint(" - Selected account has its password saved!"); + WHBLogFreetypePrint(L"This account is ready for Cemu's online functionality:"); + WHBLogFreetypePrint(L" - Selected account has NNID connected to it!"); + WHBLogFreetypePrint(L" - Selected account has its password saved!"); } } else if (selectedOption == 2 && !dumpingOnlineFiles) { - WHBLogFreetypePrint("Select the Wii U account you want to dump the saves from."); - WHBLogFreetypePrint("If you don't care about the save files, then just any "); + WHBLogFreetypePrint(L"Select the Wii U account you want to dump the saves from."); + WHBLogFreetypePrint(L"If you don't care about the save files, then just any "); } else if (selectedOption == 3 && dumpingOnlineFiles) { - WHBLogFreetypePrint("Enabling this changes the dumped account files to"); - WHBLogFreetypePrint("replace the existing Cemu account instead of being"); - WHBLogFreetypePrint("an additional account."); - WHBLogFreetypePrint("Your existing saves, assuming you use Cemu's default"); - WHBLogFreetypePrint("account, won't need to be transferred that way."); + WHBLogFreetypePrint(L"Enabling this changes the dumped account files to"); + WHBLogFreetypePrint(L"replace the existing Cemu account instead of being"); + WHBLogFreetypePrint(L"an additional account."); + WHBLogFreetypePrint(L"Your existing saves, assuming you use Cemu's default"); + WHBLogFreetypePrint(L"account, won't need to be transferred that way."); } else if (selectedOption == 3 && !dumpingOnlineFiles) { - WHBLogFreetypePrint("Enabling this changes the dumped saves to replace existing saves"); - WHBLogFreetypePrint("from the default Cemu account instead of being dumped"); - WHBLogFreetypePrint("for your original Wii U account. This is recommended since"); - WHBLogFreetypePrint("otherwise you have to dump/create a Cemu account that matches"); - WHBLogFreetypePrint("the ID of your Wii U account to use these save files."); + WHBLogFreetypePrint(L"Enabling this changes the dumped saves to replace existing saves"); + WHBLogFreetypePrint(L"from the default Cemu account instead of being dumped"); + WHBLogFreetypePrint(L"for your original Wii U account. This is recommended since"); + WHBLogFreetypePrint(L"otherwise you have to dump/create a Cemu account that matches"); + WHBLogFreetypePrint(L"the ID of your Wii U account to use these save files."); } else if (selectedOption == 4) { - WHBLogFreetypePrint("This allows you to change the amount of fat32 sectors are kept"); - WHBLogFreetypePrint("in memory before they are actually written. Used for trial and"); - WHBLogFreetypePrint("erroring a good value for all games, since some options might"); - WHBLogFreetypePrint("favor games with a few very large files but be detrimental"); - WHBLogFreetypePrint("for small files. Recommended Values: 32,64,128,256 etc."); + WHBLogFreetypePrint(L"This allows you to change the amount of fat32 sectors are kept"); + WHBLogFreetypePrint(L"in memory before they are actually written. Used for trial and"); + WHBLogFreetypePrint(L"erroring a good value for all games, since some options might"); + WHBLogFreetypePrint(L"favor games with a few very large files but be detrimental"); + WHBLogFreetypePrint(L"for small files. Recommended Values: 32,64,128,256 etc."); } - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE000 Button = Select Option \uE001 Button = Cancel"); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE000 Button = Select Option \uE001 Button = Cancel"); WHBLogFreetypeDrawScreen(); sleep_for(100ms); @@ -259,7 +259,7 @@ bool showOptionMenu(dumpingConfig& config, bool showAccountOption) { sleep_for(500ms); break; // Refreshes screen with drive list } - const bool validAccount = !dumpingOnlineFiles || (dumpingOnlineFiles && allUsers[selectedAccount].networkAccount && allUsers[selectedAccount].passwordCached); + const bool validAccount = !dumpingOnlineFiles || true || (dumpingOnlineFiles && allUsers[selectedAccount].networkAccount && allUsers[selectedAccount].passwordCached); if (!drives.empty() & validAccount && (pressedStart() || (pressedOk() && selectedOption == 5))) { if (dumpingOnlineFiles && config.dumpAsDefaultUser) { alreadyDumpedDefaultAccount = true; @@ -279,26 +279,26 @@ bool showOptionMenu(dumpingConfig& config, bool showAccountOption) { // Helper functions -uint8_t showDialogPrompt(const char* message, const char* button1, const char* button2) { +uint8_t showDialogPrompt(const wchar_t* message, const wchar_t* button1, const wchar_t* button2) { sleep_for(100ms); uint8_t selectedOption = 0; while(true) { WHBLogFreetypeStartScreen(); // Print each line - std::istringstream messageStream(message); - std::string line; + std::wistringstream messageStream(message); + std::wstring line; while(std::getline(messageStream, line)) { - WHBLogPrint(line.c_str()); + WHBLogFreetypePrint(line.c_str()); } - WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("%c [%s]", OPTION(0), button1); - if (button2 != nullptr) WHBLogFreetypePrintf("%c [%s]", OPTION(1), button2); - WHBLogFreetypePrint(""); - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE000 Button = Select Option"); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"%C [%S]", OPTION(0), button1); + if (button2 != nullptr) WHBLogFreetypePrintf(L"%C [%S]", OPTION(1), button2); + WHBLogFreetypePrint(L""); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE000 Button = Select Option"); WHBLogFreetypeDrawScreen(); // Input loop @@ -327,24 +327,24 @@ uint8_t showDialogPrompt(const char* message, const char* button1, const char* b } } -void showDialogPrompt(const char* message, const char* button) { +void showDialogPrompt(const wchar_t* message, const wchar_t* button) { showDialogPrompt(message, button, nullptr); } -const char* errorMessage = nullptr; -void setErrorPrompt(const char* message) { +const wchar_t* errorMessage = nullptr; +void setErrorPrompt(const wchar_t* message) { errorMessage = message; } -std::string messageCopy; -void setErrorPrompt(std::string message) { +std::wstring messageCopy; +void setErrorPrompt(std::wstring message) { messageCopy = std::move(message); setErrorPrompt(messageCopy.c_str()); } -void showErrorPrompt(const char* button) { - std::string promptMessage("An error occurred:\n"); +void showErrorPrompt(const wchar_t* button) { + std::wstring promptMessage(L"An error occurred:\n"); if (errorMessage) promptMessage += errorMessage; - else promptMessage += "No error was specified!"; + else promptMessage += L"No error was specified!"; showDialogPrompt(promptMessage.c_str(), button); } \ No newline at end of file diff --git a/source/app/menu.h b/source/app/menu.h index 632ddc2..8c1b3b2 100644 --- a/source/app/menu.h +++ b/source/app/menu.h @@ -3,9 +3,9 @@ void showLoadingScreen(); void showMainMenu(); -uint8_t showDialogPrompt(const char* message, const char* button1, const char* button2); -void showDialogPrompt(const char* message, const char* button); +uint8_t showDialogPrompt(const wchar_t* message, const wchar_t* button1, const wchar_t* button2); +void showDialogPrompt(const wchar_t* message, const wchar_t* button); bool showOptionMenu(dumpingConfig& config, bool showAccountOption); -void setErrorPrompt(const char* message); -void setErrorPrompt(std::string message); -void showErrorPrompt(const char* button); \ No newline at end of file +void setErrorPrompt(const wchar_t* message); +void setErrorPrompt(std::wstring message); +void showErrorPrompt(const wchar_t* button); \ No newline at end of file diff --git a/source/app/progress.cpp b/source/app/progress.cpp index 9bfad2c..53b47bb 100644 --- a/source/app/progress.cpp +++ b/source/app/progress.cpp @@ -11,7 +11,7 @@ OSTime startTime; -std::string dumpingMessage; +std::wstring dumpingMessage; const char* currFilename; uint64_t totalQueueBytes; @@ -58,37 +58,37 @@ void showCurrentProgress() { // Print general dumping message WHBLogFreetypeStartScreen(); - WHBLogFreetypePrint("Dumping In Progress:"); - WHBLogFreetypePrint(""); + WHBLogFreetypePrint(L"Dumping In Progress:"); + WHBLogFreetypePrint(L""); WHBLogFreetypePrint(dumpingMessage.c_str()); if (totalQueueBytes != 0) printEstimateTime(); - WHBLogFreetypePrint(""); - WHBLogFreetypePrint("Details:"); - WHBLogFreetypePrintf("File Name = %s", currFilename); - WHBLogFreetypePrintf("Current Speed = %.3fMB/s", (double)bytesCopiedSecond/1000000.0, formatByteSizes(copiedFileBytes, totalFileBytes).c_str()); - if (totalQueueBytes != 0) WHBLogFreetypePrintf("Overall Progress = %.1f%% done - %s", calculatePercentage(copiedQueueBytes, totalQueueBytes), formatByteSizes(copiedQueueBytes, totalQueueBytes).c_str()); - else WHBLogFreetypePrintf("Overall Progress = %s written, %d files copied", formatByteSize(copiedQueueBytes).c_str(), filesCopied); - WHBLogFreetypePrint(""); - WHBLogFreetypePrintf("File Progress = %.1f%% done - %s", calculatePercentage(copiedFileBytes, totalFileBytes), formatByteSizes(copiedFileBytes, totalFileBytes).c_str()); - - /*WHBLogFreetypePrintf("Total Fat32 Time Spent on %.0f files: %.0f ms", profile_getSegment("files"), profile_getSegment("total")); - WHBLogFreetypePrintf(" - follow_path: %.0f ms", profile_getSegment("follow_path")); - WHBLogFreetypePrintf(" - dir_find's time: %.0f ms", profile_getSegment("followfinds")); - WHBLogFreetypePrintf(" - dir_register: %.0f ms", profile_getSegment("dir_register")); - WHBLogFreetypePrintf(" - dir_find's time: %.0f ms", profile_getSegment("registerfinds")); - WHBLogFreetypePrintf(" - dir_alloc: %.0f ms", profile_getSegment("dir_alloc"));*/ - - WHBLogFreetypePrint(""); - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE001 Button = Cancel Dumping"); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrint(L"Details:"); + WHBLogFreetypePrintf(L"File Name = %S", toWstring(currFilename).c_str()); + WHBLogFreetypePrintf(L"Current Speed = %.3fMB/s", (double)bytesCopiedSecond/1000000.0); + if (totalQueueBytes != 0) WHBLogFreetypePrintf(L"Overall Progress = %.1f%% done - %S", calculatePercentage(copiedQueueBytes, totalQueueBytes), formatByteSizes(copiedQueueBytes, totalQueueBytes).c_str()); + else WHBLogFreetypePrintf(L"Overall Progress = %S written, %d files copied", formatByteSize(copiedQueueBytes).c_str(), filesCopied); + WHBLogFreetypePrint(L""); + WHBLogFreetypePrintf(L"File Progress = %.1f%% done - %S", calculatePercentage(copiedFileBytes, totalFileBytes), formatByteSizes(copiedFileBytes, totalFileBytes).c_str()); + +// WHBLogFreetypePrintf("Total Fat32 Time Spent on %.0f files: %.0f ms", profile_getSegment("files"), profile_getSegment("total")); +// WHBLogFreetypePrintf(" - follow_path: %.0f ms", profile_getSegment("follow_path")); +// WHBLogFreetypePrintf(" - dir_find's time: %.0f ms", profile_getSegment("followfinds")); +// WHBLogFreetypePrintf(" - dir_register: %.0f ms", profile_getSegment("dir_register")); +// WHBLogFreetypePrintf(" - dir_find's time: %.0f ms", profile_getSegment("registerfinds")); +// WHBLogFreetypePrintf(" - dir_alloc: %.0f ms", profile_getSegment("dir_alloc")); + + WHBLogFreetypePrint(L""); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE001 Button = Cancel Dumping"); WHBLogFreetypeDrawScreen(); } } // Set Progress Functions -void setDumpingStatus(const std::string& message) { +void setDumpingStatus(const std::wstring& message) { dumpingMessage = message; } @@ -141,8 +141,8 @@ void printEstimateTime() { WHBLogPrint(estimatedTimeStr.c_str()); } -constexpr const char *suffix[] = {"Bytes", "KB", "MB", "GB"}; -std::string formatByteSize(uint64_t bytes) { +constexpr const wchar_t *suffix[] = {L"Bytes", L"KB", L"MB", L"GB"}; +std::wstring formatByteSize(uint64_t bytes) { constexpr uint8_t length = sizeof(suffix)/sizeof(suffix[0]); uint32_t i = 0; @@ -153,12 +153,12 @@ std::string formatByteSize(uint64_t bytes) { } } - char output[50]; - sprintf(output, "%.01lf %s", dblBytes, suffix[i]); + wchar_t output[50]; + swprintf(output, std::size(output), L"%.01lf %S", dblBytes, suffix[i]); return output; } -std::string formatByteSizes(uint64_t firstBytes, uint64_t secondBytes) { +std::wstring formatByteSizes(uint64_t firstBytes, uint64_t secondBytes) { constexpr uint8_t length = sizeof(suffix)/sizeof(suffix[0]); uint32_t i = 0; @@ -171,13 +171,13 @@ std::string formatByteSizes(uint64_t firstBytes, uint64_t secondBytes) { } } - char output[50]; - sprintf(output, "%.01lf/%.01lf %s", dblOther, dblDominant, suffix[i]); + wchar_t output[50]; + swprintf(output, std::size(output), L"%.01lf/%.01lf %S", dblOther, dblDominant, suffix[i]); return output; } -std::string formatElapsedTime(std::chrono::seconds elapsedSeconds) { - std::stringstream fmtTime; +std::wstring formatElapsedTime(std::chrono::seconds elapsedSeconds) { + std::wstringstream fmtTime; auto hr = std::chrono::duration_cast(elapsedSeconds); if (hr.count() != 0) { elapsedSeconds -= hr; diff --git a/source/app/progress.h b/source/app/progress.h index 23f5955..d039841 100644 --- a/source/app/progress.h +++ b/source/app/progress.h @@ -4,12 +4,12 @@ void startQueue(uint64_t queueByteSize); void startSingleDump(); void showCurrentProgress(); -void setDumpingStatus(const std::string& message); +void setDumpingStatus(const std::wstring& message); void setFile(const char* filename, uint64_t total); void setFileProgress(uint64_t copied); double calculatePercentage(uint64_t copied, uint64_t total); void printEstimateTime(); -std::string formatByteSize(uint64_t bytes); -std::string formatByteSizes(uint64_t firstBytes, uint64_t secondBytes); -std::string formatElapsedTime(std::chrono::seconds elapsedSeconds); \ No newline at end of file +std::wstring formatByteSize(uint64_t bytes); +std::wstring formatByteSizes(uint64_t firstBytes, uint64_t secondBytes); +std::wstring formatElapsedTime(std::chrono::seconds elapsedSeconds); \ No newline at end of file diff --git a/source/app/titlelist.cpp b/source/app/titlelist.cpp index 3814a3d..95040c7 100644 --- a/source/app/titlelist.cpp +++ b/source/app/titlelist.cpp @@ -12,7 +12,7 @@ struct listEntry { std::shared_ptr listedEntry; }; -void showTitleList(const char* message, dumpingConfig config) { +void showTitleList(const wchar_t* message, dumpingConfig config) { // Filter and create the list of titles that are displayed std::vector printTitles = {}; for (auto& title : installedTitles) { @@ -50,22 +50,22 @@ void showTitleList(const char* message, dumpingConfig config) { // Print selection header WHBLogFreetypeStartScreen(); WHBLogFreetypePrint(message); - WHBLogFreetypePrint("==============================="); + WHBLogFreetypePrint(L"==============================="); // Print message when no title is found if (printTitles.empty()) { - WHBLogFreetypePrint("Nothing was found!"); - WHBLogFreetypePrint("Make sure that everything is installed."); + WHBLogFreetypePrint(L"Nothing was found!"); + WHBLogFreetypePrint(L"Make sure that everything is installed."); } // Print range of titles for (size_t i=listOffset; i' : ' ', config.queue ? (printTitles[i].queued ? "\u25A0": "\u25A1") : "", printTitles[i].listedEntry->shortTitle.c_str()); + WHBLogFreetypePrintf(L"%C %C %.30S", i == selectedEntry ? L'>' : L' ', config.queue ? (printTitles[i].queued ? L'\u25A0': L'\u25A1') : L'\u200B', printTitles[i].listedEntry->shortTitle.c_str()); } - WHBLogFreetypeScreenPrintBottom("==============================="); - WHBLogFreetypeScreenPrintBottom("\uE045 Button = Start Dumping"); - WHBLogFreetypeScreenPrintBottom("\uE000 Button = Select Title"); - WHBLogFreetypeScreenPrintBottom("\uE001 Button = Back to Main Menu"); + WHBLogFreetypeScreenPrintBottom(L"==============================="); + WHBLogFreetypeScreenPrintBottom(L"\uE045 Button = Start Dumping"); + WHBLogFreetypeScreenPrintBottom(L"\uE000 Button = Select Title"); + WHBLogFreetypeScreenPrintBottom(L"\uE001 Button = Back to Main Menu"); WHBLogFreetypeDrawScreen(); // Loop until there's new input @@ -109,7 +109,7 @@ void showTitleList(const char* message, dumpingConfig config) { } if (queuedTitles.empty()) { - showDialogPrompt("Select at least one title to dump!", "Go Back"); + showDialogPrompt(L"Select at least one title to dump!", L"Go Back"); showTitleList(message, config); return; } @@ -120,7 +120,7 @@ void showTitleList(const char* message, dumpingConfig config) { if (dumpQueue(queuedTitles, config)) { auto endTime = std::chrono::system_clock::now(); auto elapsedDuration = std::chrono::duration_cast(endTime - startTime); - std::string finishedMsg = "Dumping successfully finished in\n"+formatElapsedTime(elapsedDuration)+"!"; - showDialogPrompt(finishedMsg.c_str(), "Continue to Main Menu"); + std::wstring finishedMsg = L"Dumping successfully finished in\n"+formatElapsedTime(elapsedDuration)+L"!"; + showDialogPrompt(finishedMsg.c_str(), L"Continue to Main Menu"); } } \ No newline at end of file diff --git a/source/app/titlelist.h b/source/app/titlelist.h index 85d78bf..c948e95 100644 --- a/source/app/titlelist.h +++ b/source/app/titlelist.h @@ -1,3 +1,3 @@ #include "common.h" -void showTitleList(const char* message, dumpingConfig config); \ No newline at end of file +void showTitleList(const wchar_t* message, dumpingConfig config); \ No newline at end of file diff --git a/source/app/titles.cpp b/source/app/titles.cpp index 6c52415..1d51966 100644 --- a/source/app/titles.cpp +++ b/source/app/titles.cpp @@ -17,34 +17,34 @@ bool getTitleMetaXml(titleEntry& title, titlePart& part) { metaPath.append("/meta/meta.xml"); // Read meta.xml - std::ifstream xmlFile(metaPath); + std::wifstream xmlFile(metaPath); if (xmlFile.bad()) { WHBLogPrint("Couldn't open title xml file stream!"); WHBLogPrint(metaPath.c_str()); return false; } - std::string line; - std::string shortTitleJapan; + std::wstring line; + std::wstring shortTitleJapan; // Parse data from it using string matching bool foundShortTitle = !title.shortTitle.empty(); bool foundJapaneseShortTitle = !shortTitleJapan.empty(); bool foundProductCode = !title.productCode.empty(); while(getline(xmlFile, line)) { - if (!foundShortTitle && line.find("shortname_en") != std::string::npos) { - title.shortTitle = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + if (!foundShortTitle && line.find(L"shortname_en") != std::wstring::npos) { + title.shortTitle = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); trim(title.shortTitle); decodeXMLEscapeLine(title.shortTitle); if (!title.shortTitle.empty()) foundShortTitle = true; } - else if (!foundJapaneseShortTitle && line.find("shortname_ja") != std::string::npos) { - shortTitleJapan = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + else if (!foundJapaneseShortTitle && line.find(L"shortname_ja") != std::wstring::npos) { + shortTitleJapan = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); trim(shortTitleJapan); decodeXMLEscapeLine(shortTitleJapan); if (!shortTitleJapan.empty()) foundJapaneseShortTitle = true; } - else if (!foundProductCode && line.find("product_code") != std::string::npos) { - title.productCode = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + else if (!foundProductCode && line.find(L"product_code") != std::wstring::npos) { + title.productCode = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); foundProductCode = true; } // Stop when all fields are encountered @@ -65,9 +65,9 @@ bool getTitleMetaXml(titleEntry& title, titlePart& part) { WHBLogPrint("Error while parsing this meta.xml file:"); WHBLogPrint(metaPath.c_str()); WHBLogPrint("Problem was that only some information could be found:"); - WHBLogPrintf("shortname_en = %s", title.shortTitle.c_str()); - WHBLogPrintf("shortname_ja = %s", shortTitleJapan.c_str()); - WHBLogPrintf("product_code = %s", title.productCode.c_str()); + WHBLogFreetypePrintf(L"shortname_en = %S", title.shortTitle.c_str()); + WHBLogFreetypePrintf(L"shortname_ja = %S", shortTitleJapan.c_str()); + WHBLogFreetypePrintf(L"product_code = %S", title.productCode.c_str()); WHBLogFreetypeDraw(); return false; } @@ -78,34 +78,34 @@ bool getSaveMetaXml(titleEntry& title, savePart& part) { metaPath.append("/meta/meta.xml"); // Read meta.xml - std::ifstream xmlFile(metaPath); + std::wifstream xmlFile(metaPath); if (xmlFile.bad()) { WHBLogPrint("Couldn't open save xml file stream!"); WHBLogPrint(metaPath.c_str()); return false; } - std::string line; - std::string shortTitleJapan; + std::wstring line; + std::wstring shortTitleJapan; // Parse data from it using string matching bool foundShortTitle = !title.shortTitle.empty(); bool foundJapaneseShortTitle = !shortTitleJapan.empty(); bool foundProductCode = !title.productCode.empty(); while(getline(xmlFile, line)) { - if (!foundShortTitle && line.find("shortname_en") != std::string::npos) { - title.shortTitle = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + if (!foundShortTitle && line.find(L"shortname_en") != std::wstring::npos) { + title.shortTitle = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); trim(title.shortTitle); decodeXMLEscapeLine(title.shortTitle); if (!title.shortTitle.empty()) foundShortTitle = true; } - else if (!foundJapaneseShortTitle && line.find("shortname_ja") != std::string::npos) { - shortTitleJapan = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + else if (!foundJapaneseShortTitle && line.find(L"shortname_ja") != std::wstring::npos) { + shortTitleJapan = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); trim(shortTitleJapan); decodeXMLEscapeLine(shortTitleJapan); if (!shortTitleJapan.empty()) foundJapaneseShortTitle = true; } - else if (!foundProductCode && line.find("product_code") != std::string::npos) { - title.productCode = line.substr(line.find('>')+1, (line.find_last_of('<'))-(line.find_first_of('>')+1)); + else if (!foundProductCode && line.find(L"product_code") != std::wstring::npos) { + title.productCode = line.substr(line.find(L'>')+1, (line.find_last_of(L'<'))-(line.find_first_of(L'>')+1)); foundProductCode = true; } // Stop when all fields are encountered @@ -125,9 +125,9 @@ bool getSaveMetaXml(titleEntry& title, savePart& part) { WHBLogPrint("Error while parsing this meta.xml file:"); WHBLogPrint(metaPath.c_str()); WHBLogPrint("Problem was that only some information could be found:"); - WHBLogPrintf("shortname_en = %s", title.shortTitle.c_str()); - WHBLogPrintf("shortname_ja = %s", shortTitleJapan.c_str()); - WHBLogPrintf("product_code = %s", title.productCode.c_str()); + WHBLogFreetypePrintf(L"shortname_en = %S", title.shortTitle.c_str()); + WHBLogFreetypePrintf(L"shortname_ja = %S", shortTitleJapan.c_str()); + WHBLogFreetypePrintf(L"product_code = %S", title.productCode.c_str()); WHBLogFreetypeDraw(); return false; } @@ -182,7 +182,7 @@ bool getSaveList(const std::string& saveDirPath) { // Loop over high title id folders e.g. storage_mlc01:/usr/save/[iterated] DIR* highDirHandle; - if ((highDirHandle = opendir((saveDirPath+"/").c_str())) == nullptr) return false; + if (highDirHandle = opendir((saveDirPath+"/").c_str()); highDirHandle == nullptr) return false; struct dirent* highDirEntry; while ((highDirEntry = readdir(highDirHandle)) != nullptr) { @@ -195,7 +195,7 @@ bool getSaveList(const std::string& saveDirPath) { // Loop over low title id folders e.g. storage_mlc01:/usr/save/00050000/[iterated] DIR* lowDirHandle; - if ((lowDirHandle = opendir(highDirPath.c_str())) == nullptr) continue; + if (lowDirHandle = opendir(highDirPath.c_str()); lowDirHandle == nullptr) continue; struct dirent* lowDirEntry; while ((lowDirEntry = readdir(lowDirHandle)) != nullptr) { @@ -210,7 +210,7 @@ bool getSaveList(const std::string& saveDirPath) { // Loop over user saves folders e.g. storage_mlc01:/usr/save/00050000/101b0500/user/[iterated] DIR* userDirHandle; - if ((userDirHandle = opendir((lowDirPath + "user/").c_str())) == nullptr) continue; + if (userDirHandle = opendir((lowDirPath + "user/").c_str()); userDirHandle == nullptr) continue; struct dirent* userDirEntry; while ((userDirEntry = readdir(userDirHandle)) != nullptr) { @@ -221,7 +221,7 @@ bool getSaveList(const std::string& saveDirPath) { // Check whether the common or user save has any contents bool hasContents = false; DIR* contentDirHandle; - if ((contentDirHandle = opendir(userDirPath.c_str())) == nullptr) continue; + if (contentDirHandle = opendir(userDirPath.c_str()); contentDirHandle == nullptr) continue; struct dirent* contentDirEntry; while ((contentDirEntry = readdir(contentDirHandle)) != nullptr) { @@ -503,7 +503,7 @@ bool getSaveListThroughACP(bool skipDiscs) { } */ -std::optional> getTitleWithName(std::string& nameOfTitle) { +std::optional> getTitleWithName(std::wstring& nameOfTitle) { for (auto& title : installedTitles) { if (title->shortTitle == nameOfTitle) return title; } @@ -511,24 +511,24 @@ std::optional> getTitleWithName(std::string& nameOfT } // Helper functions -std::array nonValidFilenames{'\0', '\\', '/', ':', '*', '?', '\"', '<', '>', '|', '.', '%', '$', '#'}; - -void decodeXMLEscapeLine(std::string& xmlString) { - replaceAll(xmlString, """, "\""); - replaceAll(xmlString, "'", "'"); - replaceAll(xmlString, "<", "<"); - replaceAll(xmlString, ">", ">"); - replaceAll(xmlString, "&", "&"); +std::array nonValidFilenames{L'\0', L'\\', L'/', L':', L'*', L'?', L'\"', L'<', L'>', L'|', L'.', L'%', L'$', L'#'}; + +void decodeXMLEscapeLine(std::wstring& xmlString) { + replaceAll(xmlString, L""", L"\""); + replaceAll(xmlString, L"'", L"'"); + replaceAll(xmlString, L"<", L"<"); + replaceAll(xmlString, L">", L">"); + replaceAll(xmlString, L"&", L"&"); } -std::string normalizeFolderName(std::string& unsafeTitle) { +std::string normalizeFolderName(std::wstring unsafeString) { + trim(unsafeString); std::string retTitle; - for (char& chr : unsafeTitle) { - if (std::find(nonValidFilenames.begin(), nonValidFilenames.end(), chr) == nonValidFilenames.end()) { - retTitle += chr; + for (wchar_t chr : unsafeString) { + if (chr <= 0x7F && std::find(nonValidFilenames.begin(), nonValidFilenames.end(), chr) == nonValidFilenames.end()) { // ASCII characters have a maximum value of 0x7F + retTitle += static_cast(chr); } } - trim(retTitle); return retTitle; } diff --git a/source/app/titles.h b/source/app/titles.h index b5e15c4..200a44e 100644 --- a/source/app/titles.h +++ b/source/app/titles.h @@ -6,8 +6,8 @@ bool checkForDiscTitles(int32_t mcpHandle); bool loadTitles(bool skipDiscs); std::optional> getTitleWithName(std::string& nameOfTitle); -std::string normalizeFolderName(std::string& unsafeTitle); -void decodeXMLEscapeLine(std::string& xmlString); +std::string normalizeFolderName(std::wstring unsafeString); +void decodeXMLEscapeLine(std::wstring& xmlString); bool isBase(MCPAppType type); bool isUpdate(MCPAppType type); bool isDLC(MCPAppType type); diff --git a/source/app/users.cpp b/source/app/users.cpp index 81d0eb8..52bfa1b 100644 --- a/source/app/users.cpp +++ b/source/app/users.cpp @@ -20,9 +20,8 @@ bool loadUsers() { if (res.IsFailure()) continue; // Convert mii name to normal string - std::u16string newString(miiName); - std::wstring_convert,char16_t> convert; - newAccount.miiName = convert.to_bytes(newString); + std::wstring_convert> convert; + newAccount.miiName = convert.from_bytes(reinterpret_cast(miiName), reinterpret_cast(miiName + std::char_traits::length(miiName))); // Convert persistent id into string std::ostringstream stream;