diff --git a/source/ApplicationState.cpp b/source/ApplicationState.cpp new file mode 100644 index 0000000..a0f2843 --- /dev/null +++ b/source/ApplicationState.cpp @@ -0,0 +1,312 @@ +#include "ApplicationState.h" +#include "WiiUScreen.h" +#include "ScreenUtils.h" +#include + +void ApplicationState::render() { + WiiUScreen::clearScreen(); + WiiUScreen::drawLine("Aroma Installer"); + WiiUScreen::drawLine("=================="); + WiiUScreen::drawLine(""); + + if (this->state == STATE_ERROR) { + WiiUScreen::drawLine("The installation failed:"); + WiiUScreen::drawLine(); + WiiUScreen::drawLinef("Error: %s", ErrorMessage().c_str()); + WiiUScreen::drawLinef("Description: %s", ErrorDescription().c_str()); + WiiUScreen::drawLine(); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("Press A to return to the Wii U Menu."); + } else if (this->state == STATE_WELCOME_SCREEN) { + WiiUScreen::drawLine("Welcome to the Aroma Installer!"); + WiiUScreen::drawLine("Do you want to check if an installation is possible?"); + WiiUScreen::drawLine(""); + if (this->selectedOption == 0) { + WiiUScreen::drawLine("> Check Exit"); + } else if (this->selectedOption == 1) { + WiiUScreen::drawLine(" Check > Exit"); + } + } else if (this->state == STATE_GET_APP_INFORMATION) { + WiiUScreen::drawLine("Getting app information"); + } else if (this->state == STATE_CHECK_PATCH_POSSIBLE) { + WiiUScreen::drawLine("Check if console can be patched."); + } else if (this->state == STATE_CHECK_PATCH_POSSIBLE_DONE) { + WiiUScreen::drawLinef("Compatible title: %s", appInfo->appName); + WiiUScreen::drawLine(); + if (this->fstPatchPossible) { + WiiUScreen::drawLine("[ X ] title.fst can be patched!"); + } else { + WiiUScreen::drawLine("[ ] title.fst can NOT be patched!"); + } + if (this->cosPatchPossible) { + WiiUScreen::drawLine("[ X ] cos.xml can be patched!"); + } else { + WiiUScreen::drawLine("[ ] cos.xml can NOT be patched!"); + } + if (this->systemXMLPatchPossible) { + WiiUScreen::drawLine("[ X ] system.xml can be patched!"); + } else { + WiiUScreen::drawLine("[ ] system.xml can NOT be patched!"); + } + + WiiUScreen::drawLine(); + + if (!this->fstPatchPossible || !this->cosPatchPossible) { + WiiUScreen::drawLine("A safe installation of Aroma can not be provided."); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("Press A to return to the Wii U Menu"); + } else { + WiiUScreen::drawLine("Do you want to install Aroma?"); + WiiUScreen::drawLine(""); + if (this->selectedOption == 0) { + WiiUScreen::drawLine("> Install Exit"); + } else if (this->selectedOption == 1) { + WiiUScreen::drawLine(" Install > Exit"); + } + } + } else if (this->state == STATE_INSTALL_CHOOSE_COLDBOOT) { + WiiUScreen::drawLine("Select your installation type:"); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("[Coldboot] Aroma will launch directly after booting the console."); + WiiUScreen::drawLine("[No Coldboot] Aroma will need to be launched manually."); + WiiUScreen::drawLine(""); + if (this->selectedOption == 0) { + WiiUScreen::drawLine("> Back Coldboot No Coldboot"); + } else if (this->selectedOption == 1) { + WiiUScreen::drawLine(" Back > Coldboot No Coldboot"); + } else if (this->selectedOption == 2) { + WiiUScreen::drawLine(" Back Coldboot > No Coldboot"); + } + } else if (this->state == STATE_INSTALL_CONFIRM_DIALOG) { + WiiUScreen::drawLine("Are you REALLY sure you want to install Aroma?"); + WiiUScreen::drawLine("Installing could permanently damage your console"); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("After the installation you can NOT longer use:"); + WiiUScreen::drawLinef("- %s", appInfo->appName); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("Selected installation type:"); + if (this->installColdboot) { + WiiUScreen::drawLine("- Coldboot"); + } else { + WiiUScreen::drawLine("- No Coldboot"); + } + + WiiUScreen::drawLine(); + WiiUScreen::drawLine(); + if (this->selectedOption == 0) { + WiiUScreen::drawLine("> Back Install"); + } else if (this->selectedOption == 1) { + WiiUScreen::drawLine(" Back > Install"); + } + } else if (this->state == STATE_INSTALL_STARTED) { + WiiUScreen::drawLine("Installing..."); + } else if (this->state == STATE_INSTALL_FST) { + WiiUScreen::drawLine("... patching title.fst"); + } else if (this->state == STATE_INSTALL_COS) { + WiiUScreen::drawLine("... patching cos.xml"); + } else if (this->state == STATE_INSTALL_SYSTEM_XML) { + WiiUScreen::drawLine("... patching system.xml"); + } else if (this->state == STATE_INSTALL_RPX) { + WiiUScreen::drawLine("... install safe.rpx"); + } + printFooter(); + WiiUScreen::flipBuffers(); +} + +void ApplicationState::update(Input *input) { + if (this->state == STATE_ERROR) { + if (entrySelected(input)) { + SYSLaunchMenu(); + } + } else if (this->state == STATE_WELCOME_SCREEN) { + proccessMenuNavigation(input, 2); + if (entrySelected(input)) { + if (this->selectedOption == 0) { + this->state = STATE_GET_APP_INFORMATION; + } else { + SYSLaunchMenu(); + } + this->selectedOption = 0; + return; + } + } else if (this->state == STATE_GET_APP_INFORMATION) { + getAppInformation(); + } else if (this->state == STATE_CHECK_PATCH_POSSIBLE) { + checkPatchPossible(); + } else if (this->state == STATE_CHECK_PATCH_POSSIBLE_DONE) { + if (this->fstPatchPossible && this->cosPatchPossible) { + proccessMenuNavigation(input, 2); + if (entrySelected(input)) { + if (this->selectedOption == 0) { + if (systemXMLPatchPossible) { + this->state = STATE_INSTALL_CHOOSE_COLDBOOT; + this->installColdboot = false; + } else { + this->state = STATE_INSTALL_CONFIRM_DIALOG; + } + } else { + SYSLaunchMenu(); + } + this->selectedOption = 0; + return; + } + } else { + if (entrySelected(input)) { + SYSLaunchMenu(); + } + } + } else if (this->state == STATE_INSTALL_CHOOSE_COLDBOOT) { + proccessMenuNavigation(input, 3); + if (entrySelected(input)) { + if (this->selectedOption == 0) { // Back + this->state = STATE_CHECK_PATCH_POSSIBLE_DONE; + } else { + if (selectedOption == 1) { // Install with coldboot + this->installColdboot = true; + } + this->state = STATE_INSTALL_CONFIRM_DIALOG; + } + this->selectedOption = 0; + return; + } + } else if (this->state == STATE_INSTALL_CONFIRM_DIALOG) { + proccessMenuNavigation(input, 2); + if (entrySelected(input)) { + if (this->selectedOption == 0) { + this->state = STATE_CHECK_PATCH_POSSIBLE_DONE; + } else { + this->state = STATE_INSTALL_STARTED; + OSEnableHomeButtonMenu(false); + } + this->selectedOption = 0; + return; + } + } else if (this->state == STATE_INSTALL_STARTED) { + this->state = STATE_INSTALL_FST; + } else if (this->state == STATE_INSTALL_FST) { + auto result = InstallerService::patchFST(this->appInfo->path, this->appInfo->fstHash); + if (result != InstallerService::SUCCESS) { + this->error = ERROR_INSTALLER_ERROR; + this->installerError = result; + this->state = STATE_ERROR; + } else { + this->state = STATE_INSTALL_COS; + } + } else if (this->state == STATE_INSTALL_COS) { + auto result = InstallerService::patchCOS(this->appInfo->path, this->appInfo->cosHash); + if (result != InstallerService::SUCCESS) { + this->error = ERROR_INSTALLER_ERROR; + this->installerError = result; + this->state = STATE_ERROR; + } else { + this->state = STATE_INSTALL_RPX; + } + } else if (this->state == STATE_INSTALL_RPX) { + if (entrySelected(input)) { + this->state = STATE_WELCOME_SCREEN; + } + } +} + +ApplicationState::ApplicationState() { + this->state = STATE_WELCOME_SCREEN; + this->selectedOption = 0; + DEBUG_FUNCTION_LINE("State has changed to \"STATE_WELCOME_SCREEN\""); +} + +void ApplicationState::checkPatchPossible() { + DEBUG_FUNCTION_LINE("Check patch possible"); + if (!this->appInfo) { + this->state = STATE_ERROR; + this->error = ERROR_NO_APP_INSTALLED; + DEBUG_FUNCTION_LINE("ERROR"); + return; + } + DEBUG_FUNCTION_LINE("CHECK FST"); + InstallerService::eResults result; + this->fstPatchPossible = ((result = InstallerService::checkFST(this->appInfo->path, this->appInfo->fstHash)) == InstallerService::SUCCESS); + if (result != InstallerService::SUCCESS) { + DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); + } + this->cosPatchPossible = ((result = InstallerService::checkCOS(this->appInfo->path, this->appInfo->cosHash)) == InstallerService::SUCCESS); + if (result != InstallerService::SUCCESS) { + DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); + } + this->systemXMLPatchPossible = ((result = InstallerService::checkSystemXML("storage_slc_installer:/config", this->appInfo->titleId)) == InstallerService::SUCCESS); + if (result != InstallerService::SUCCESS) { + DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); + } + this->state = STATE_CHECK_PATCH_POSSIBLE_DONE; + +} + +void ApplicationState::getAppInformation() { + DEBUG_FUNCTION_LINE("About to call getInstalledAppInformation"); + this->appInfo = InstallerService::getInstalledAppInformation(); + DEBUG_FUNCTION_LINE("back"); + if (!this->appInfo) { + DEBUG_FUNCTION_LINE("ERROR =("); + this->state = STATE_ERROR; + this->error = ERROR_NO_APP_INSTALLED; + } else { + DEBUG_FUNCTION_LINE("WORKED!"); + this->state = STATE_CHECK_PATCH_POSSIBLE; + } +} + +std::string ApplicationState::ErrorMessage() { + if (this->error == ERROR_NONE) { + return "NONE"; + } else if (this->error == ERROR_NO_APP_INSTALLED) { + return "ERROR_NO_APP_INSTALLED"; + } else if (this->error == ERROR_IOSUHAX_FAILED) { + return "ERROR_IOSUHAX_FAILED"; + } else if (this->error == ERROR_INSTALLER_ERROR) { + return InstallerService::ErrorMessage(this->installerError); + } + return "UNKNOWN_ERROR"; +} + +std::string ApplicationState::ErrorDescription() { + if (this->error == ERROR_NONE) { + return "-"; + } else if (this->error == ERROR_NO_APP_INSTALLED) { + return "No compatible application is installed. A safe installation is not possible."; + } else if (this->error == ERROR_INSTALLER_ERROR) { + return InstallerService::ErrorDescription(this->installerError); + } else if (this->error == ERROR_IOSUHAX_FAILED) { + return "Failed to init IOSUHAX."; + } + return "UNKNOWN_ERROR"; +} + +void ApplicationState::setError(eErrorState error) { + this->state = STATE_ERROR; + this->error = error; +} + +void ApplicationState::handleError() { + +} + +void ApplicationState::printFooter() { + ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_TV, 0, 27, "By Maschell"); + ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_DRC, 0, 17, "By Maschell"); +} + +void ApplicationState::proccessMenuNavigation(Input *input, int maxOptionValue) { + if (input->data.buttons_d & Input::BUTTON_LEFT) { + this->selectedOption--; + } else if (input->data.buttons_d & Input::BUTTON_RIGHT) { + this->selectedOption++; + } + if (this->selectedOption < 0) { + this->selectedOption = maxOptionValue; + } else if (this->selectedOption >= maxOptionValue) { + this->selectedOption = 0; + } +} + +bool ApplicationState::entrySelected(Input *input) { + return input->data.buttons_d & Input::BUTTON_A; +} diff --git a/source/ApplicationState.h b/source/ApplicationState.h new file mode 100644 index 0000000..1d38621 --- /dev/null +++ b/source/ApplicationState.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include "common/common.h" +#include "InstallerService.h" +#include "Input.h" + +class ApplicationState { +public: + + enum eErrorState { + ERROR_NONE, + ERROR_IOSUHAX_FAILED, + ERROR_NO_APP_INSTALLED, + ERROR_INSTALLER_ERROR + }; + + enum eGameState { + STATE_ERROR, + STATE_WELCOME_SCREEN, + STATE_GET_APP_INFORMATION, + STATE_CHECK_PATCH_POSSIBLE, + STATE_CHECK_PATCH_POSSIBLE_DONE, + STATE_INSTALL_CHOOSE_COLDBOOT, + STATE_INSTALL_CONFIRM_DIALOG, + STATE_INSTALL_STARTED, + STATE_INSTALL_FST, + STATE_INSTALL_SYSTEM_XML, + STATE_INSTALL_COS, + STATE_INSTALL_RPX + }; + + ApplicationState(); + + void setError(eErrorState error); + + void render(); + + void update(Input *input); + + void checkPatchPossible(); + + void getAppInformation(); + + std::optional appInfo; + + std::string ErrorMessage(); + + std::string ErrorDescription(); + + void handleError(); + + int selectedOption; + + static void printFooter(); + + void proccessMenuNavigation(Input *input, int maxOptionValue); + + bool entrySelected(Input *input); + + bool installColdboot = false; + InstallerService::eResults installerError = InstallerService::eResults::SUCCESS; + +private: + bool fstPatchPossible = false; + bool cosPatchPossible = false; + bool systemXMLPatchPossible = false; + + eGameState state; + eErrorState error = ERROR_NONE; +}; diff --git a/source/GameState.cpp b/source/GameState.cpp deleted file mode 100644 index 4c0957b..0000000 --- a/source/GameState.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "GameState.h" -#include "WiiUScreen.h" -#include "Input.h" - -void GameState::render() { - WiiUScreen::clearScreen(); - if (this->state == STATE_ERROR) { - WiiUScreen::drawLinef("Error: %s", ErrorMessage().c_str()); - } else if (this->state == STATE_GET_APP_INFORMATION) { - WiiUScreen::drawLine("Getting app information"); - } else if (this->state == STATE_CHECK_PATCH_POSSIBLE) { - WiiUScreen::drawLine("Check if console can be patched."); - } else if (this->state == STATE_CHECK_PATCH_POSSIBLE_DONE) { - if (this->fstPatchPossibe) { - WiiUScreen::drawLine("- title.fst can be patched!"); - } else { - WiiUScreen::drawLine("x title.fst can NOT be patched!"); - } - if (this->cosPatchPossibe) { - WiiUScreen::drawLine("- cos.xml can be patched!"); - } else { - WiiUScreen::drawLine("x cos.xml can NOT be patched!"); - } - if (this->systemXMLPatchPossibe) { - WiiUScreen::drawLine("- system.xml can be patched!"); - } else { - WiiUScreen::drawLine("x system.xml can NOT be patched!"); - } - } - WiiUScreen::flipBuffers(); -} - -Input GameState::getCurrentInput(){ - -} - -void GameState::update(Input input) { - if (this->state == STATE_ERROR) { - handleError(); - } else if (this->state == STATE_GET_APP_INFORMATION) { - getAppInformation(); - } else if (this->state == STATE_CHECK_PATCH_POSSIBLE) { - checkPatchPossible(); - } -} - -GameState::GameState() { - this->state = STATE_GET_APP_INFORMATION; - DEBUG_FUNCTION_LINE("State has changed to \"STATE_GET_APP_INFORMATION\""); -} - -void GameState::checkPatchPossible() { - DEBUG_FUNCTION_LINE("Check patch possible"); - if(!this->appInfo){ - this->state = STATE_ERROR; - this->error = ERROR_NO_APP_INSTALLED; - DEBUG_FUNCTION_LINE("ERROR"); - return; - } - DEBUG_FUNCTION_LINE("CHECK FST"); - InstallerService::eResults result; - this->fstPatchPossibe = ((result = InstallerService::checkFST(this->appInfo->path, this->appInfo->fstHash)) == InstallerService::SUCCESS); - if (result != InstallerService::SUCCESS) { - DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); - } - this->cosPatchPossibe = ((result = InstallerService::checkCOS(this->appInfo->path, this->appInfo->cosHash)) == InstallerService::SUCCESS); - if (result != InstallerService::SUCCESS) { - DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); - } - this->systemXMLPatchPossibe = ((result = InstallerService::checkSystemXML("storage_slc_installer:/config", this->appInfo->titleId)) == InstallerService::SUCCESS); - if (result != InstallerService::SUCCESS) { - DEBUG_FUNCTION_LINE("ERROR: %s", InstallerService::ErrorMessage(result).c_str()); - } - this->state = STATE_CHECK_PATCH_POSSIBLE_DONE; - -} - -void GameState::getAppInformation() { - DEBUG_FUNCTION_LINE("About to call getInstalledAppInformation"); - this->appInfo = InstallerService::getInstalledAppInformation(); - DEBUG_FUNCTION_LINE("back"); - if (!this->appInfo) { - DEBUG_FUNCTION_LINE("ERROR =("); - this->state = STATE_ERROR; - this->error = ERROR_NO_APP_INSTALLED; - } else { - DEBUG_FUNCTION_LINE("WORKED!"); - this->state = STATE_CHECK_PATCH_POSSIBLE; - } -} - -std::string GameState::ErrorMessage() { - if (this->error == ERROR_NONE) { - return "NONE"; - } else if (this->error == ERROR_NO_APP_INSTALLED) { - return "ERROR_NO_APP_INSTALLED"; - } - return "UNKNOWN_ERROR"; -} - -void GameState::handleError() { - -} diff --git a/source/GameState.h b/source/GameState.h deleted file mode 100644 index dc4b954..0000000 --- a/source/GameState.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include "common/common.h" -#include "InstallerService.h" - -class GameState { - - enum eGameState { - STATE_ERROR, - STATE_GET_APP_INFORMATION, - STATE_CHECK_PATCH_POSSIBLE, - STATE_CHECK_PATCH_POSSIBLE_DONE - }; - - enum eErrorState { - ERROR_NONE, - ERROR_NO_APP_INSTALLED - }; - -private: - bool fstPatchPossibe = false; - bool cosPatchPossibe = false; - bool systemXMLPatchPossibe = false; - - eGameState state; - eErrorState error = ERROR_NONE; -public: - GameState(); - - void render(); - - void update(); - - - void checkPatchPossible(); - - void getAppInformation(); - - std::optional appInfo; - - std::string ErrorMessage(); - - void handleError(); -}; diff --git a/source/Input.h b/source/Input.h index 3f64f17..ac91686 100644 --- a/source/Input.h +++ b/source/Input.h @@ -11,6 +11,39 @@ public: //!Destructor virtual ~Input() = default; + enum eButtons { + BUTTON_NONE = 0x0000, + VPAD_TOUCH = 0x80000000, + BUTTON_Z = 0x20000, + BUTTON_C = 0x10000, + BUTTON_A = 0x8000, + BUTTON_B = 0x4000, + BUTTON_X = 0x2000, + BUTTON_Y = 0x1000, + BUTTON_1 = BUTTON_Y, + BUTTON_2 = BUTTON_X, + BUTTON_LEFT = 0x0800, + BUTTON_RIGHT = 0x0400, + BUTTON_UP = 0x0200, + BUTTON_DOWN = 0x0100, + BUTTON_ZL = 0x0080, + BUTTON_ZR = 0x0040, + BUTTON_L = 0x0020, + BUTTON_R = 0x0010, + BUTTON_PLUS = 0x0008, + BUTTON_MINUS = 0x0004, + BUTTON_HOME = 0x0002, + BUTTON_SYNC = 0x0001, + STICK_R_LEFT = 0x04000000, + STICK_R_RIGHT = 0x02000000, + STICK_R_UP = 0x01000000, + STICK_R_DOWN = 0x00800000, + STICK_L_LEFT = 0x40000000, + STICK_L_RIGHT = 0x20000000, + STICK_L_UP = 0x10000000, + STICK_L_DOWN = 0x08000000 + }; + typedef struct { uint32_t buttons_h; uint32_t buttons_d; diff --git a/source/InstallerService.cpp b/source/InstallerService.cpp index 4205dd8..5d84bda 100644 --- a/source/InstallerService.cpp +++ b/source/InstallerService.cpp @@ -31,7 +31,7 @@ appInformation supportedApps[] = { InstallerService::eResults InstallerService::checkCOS(const std::string &path, char *hash) { - std::string cosFilePath = path + "/code/cos1.xml"; + std::string cosFilePath = path + "/code/cos.xml"; DEBUG_FUNCTION_LINE("Loading %s", cosFilePath.c_str()); pugi::xml_document doc; @@ -41,7 +41,7 @@ InstallerService::eResults InstallerService::checkCOS(const std::string &path, c return COS_XML_PARSING_FAILED; } - patchCOS(&doc); + patchCOSXMLData(&doc); std::stringstream ss; doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8); @@ -116,13 +116,18 @@ InstallerService::eResults InstallerService::checkFST(const std::string &path, c DEBUG_FUNCTION_LINE("Failed to load title.fst"); return FAILED_TO_LOAD_FILE; } - InstallerService::eResults res = patchFST(fstData, fstDataSize); + InstallerService::eResults res = patchFSTData(fstData, fstDataSize); if (res != SUCCESS) { + free(fstData); + fstData = nullptr; return res; } std::string newHash = Utils::calculateSHA1((const char *) fstData, fstDataSize); + free(fstData); + fstData = nullptr; + if (std::string(fstHash) == newHash) { DEBUG_FUNCTION_LINE("title.fst is compatible"); return SUCCESS; @@ -132,7 +137,7 @@ InstallerService::eResults InstallerService::checkFST(const std::string &path, c } } -bool InstallerService::patchCOS(pugi::xml_document *doc) { +bool InstallerService::patchCOSXMLData(pugi::xml_document *doc) { pugi::xml_node appEntry = doc->child("app"); appEntry.child("argstr").first_child().set_value("safe.rpx"); appEntry.child("avail_size").first_child().set_value("00000000"); @@ -190,7 +195,7 @@ std::optional InstallerService::getInstalledAppInformation() { return {}; } -InstallerService::eResults InstallerService::patchFST(uint8_t *fstData, uint32_t size) { +InstallerService::eResults InstallerService::patchFSTData(uint8_t *fstData, uint32_t size) { auto *fstHeader = (FSTHeader *) fstData; if (strncmp(FSTHEADER_MAGIC, fstHeader->magic, 3) != 0) { DEBUG_FUNCTION_LINE("FST magic is wrong %s", fstHeader->magic); @@ -232,47 +237,206 @@ InstallerService::eResults InstallerService::patchFST(uint8_t *fstData, uint32_t return InstallerService::SUCCESS; } -std::string InstallerService::ErrorMessage(InstallerService::eResults results) { - if (results == SUCCESS) { +std::string InstallerService::ErrorDescription(InstallerService::eResults error) { + if (error == SUCCESS) { return "Success"; - } else if (results == NO_COMPATIBLE_APP_INSTALLED) { + } else if (error == NO_COMPATIBLE_APP_INSTALLED) { + return "No compatible application was found on the console."; + } else if (error == FAILED_TO_COPY_FILES) { + return "Unable to copy files."; + } else if (error == FAILED_TO_CHECK_HASH_COPIED_FILES) { + return "The copy of a file has a different hash."; + } else if (error == SYSTEM_XML_INFORMATION_NOT_FOUND) { + return "Expected hashes for the target system.xml were not found."; + } else if (error == SYSTEM_XML_PARSING_FAILED) { + return "Failed to parse the system.xml"; + } else if (error == SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED) { + return "DO NOT REBOOT BEFORE FIXING THIS: Failed to restore the system.xml after an error."; + } else if (error == SYSTEM_XML_HASH_MISMATCH) { + return "The patched system.xml had an unexpected hash but was successfully restored"; + } else if (error == RPX_HASH_MISMATCH) { + return "The installed safe.rpx had an unexpected hash but was successfully restored"; + } else if (error == RPX_HASH_MISMATCH_RESTORE_FAILED) { + return "DO NOT REBOOT BEFORE FIXING THIS: Failed to restore the safe.rpx after an error."; + } else if (error == COS_XML_PARSING_FAILED) { + return "Failed to parse the cos.xml"; + } else if (error == COS_XML_HASH_MISMATCH) { + return "The patched cos.xml had an unexpected hash but was successfully restored"; + } else if (error == COS_XML_HASH_MISMATCH_RESTORE_FAILED) { + return "DO NOT REBOOT BEFORE FIXING THIS: Failed to restore the cos.xml after an error"; + } else if (error == MALLOC_FAILED) { + return "Failed to allocate memory"; + } else if (error == FST_HASH_MISMATCH) { + return "The patched title.fst had an unexpected hash but was successfully restored"; + } else if (error == FST_HASH_MISMATCH_RESTORE_FAILED) { + return "DO NOT REBOOT BEFORE FIXING THIS: Failed to restore the title.fst after an error"; + } else if (error == FST_HEADER_MISMATCH) { + return "Unexpected header in title.fst found. The file is probably broken."; + } else if (error == FST_NO_USABLE_SECTION_FOUND) { + return "Unable to patch title.fst to allow FailST"; + } else if (error == FAILED_TO_LOAD_FILE) { + return "Failed to load file."; + } else { + return "UNKNOWN ERROR"; + } +} + +std::string InstallerService::ErrorMessage(InstallerService::eResults error) { + if (error == SUCCESS) { + return "Success"; + } else if (error == NO_COMPATIBLE_APP_INSTALLED) { return "NO_COMPATIBLE_APP_INSTALLED"; - } else if (results == FAILED_TO_COPY_FILES) { + } else if (error == FAILED_TO_COPY_FILES) { return "FAILED_TO_COPY_FILES"; - } else if (results == FAILED_TO_CHECK_HASH_COPIED_FILES) { + } else if (error == FAILED_TO_CHECK_HASH_COPIED_FILES) { return "FAILED_TO_CHECK_HASH_COPIED_FILES"; - } else if (results == SYSTEM_XML_INFORMATION_NOT_FOUND) { + } else if (error == SYSTEM_XML_INFORMATION_NOT_FOUND) { return "SYSTEM_XML_INFORMATION_NOT_FOUND"; - } else if (results == SYSTEM_XML_PARSING_FAILED) { + } else if (error == SYSTEM_XML_PARSING_FAILED) { return "SYSTEM_XML_PARSING_FAILED"; - } else if (results == SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED) { + } else if (error == SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED) { return "SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED"; - } else if (results == SYSTEM_XML_HASH_MISMATCH) { + } else if (error == SYSTEM_XML_HASH_MISMATCH) { return "SYSTEM_XML_HASH_MISMATCH"; - } else if (results == RPX_HASH_MISMATCH) { + } else if (error == RPX_HASH_MISMATCH) { return "RPX_HASH_MISMATCH"; - } else if (results == RPX_HASH_MISMATCH_RESTORE_FAILED) { + } else if (error == RPX_HASH_MISMATCH_RESTORE_FAILED) { return "RPX_HASH_MISMATCH_RESTORE_FAILED"; - } else if (results == COS_XML_PARSING_FAILED) { + } else if (error == COS_XML_PARSING_FAILED) { return "COS_XML_PARSING_FAILED"; - } else if (results == COS_XML_HASH_MISMATCH) { + } else if (error == COS_XML_HASH_MISMATCH) { return "COS_XML_HASH_MISMATCH"; - } else if (results == COS_XML_HASH_MISMATCH_RESTORE_FAILED) { + } else if (error == COS_XML_HASH_MISMATCH_RESTORE_FAILED) { return "COS_XML_HASH_MISMATCH_RESTORE_FAILED"; - } else if (results == MALLOC_FAILED) { + } else if (error == MALLOC_FAILED) { return "MALLOC_FAILED"; - } else if (results == FST_HASH_MISMATCH) { + } else if (error == FST_HASH_MISMATCH) { return "FST_HASH_MISMATCH"; - } else if (results == FST_HASH_MISMATCH_RESTORE_FAILED) { + } else if (error == FST_HASH_MISMATCH_RESTORE_FAILED) { return "FST_HASH_MISMATCH_RESTORE_FAILED"; - } else if (results == FST_HEADER_MISMATCH) { + } else if (error == FST_HEADER_MISMATCH) { return "FST_HEADER_MISMATCH"; - } else if (results == FST_NO_USABLE_SECTION_FOUND) { + } else if (error == FST_NO_USABLE_SECTION_FOUND) { return "FST_NO_USABLE_SECTION_FOUND"; - } else if (results == FAILED_TO_LOAD_FILE) { + } else if (error == FAILED_TO_LOAD_FILE) { return "FAILED_TO_LOAD_FILE"; } else { return "UNKNOWN ERROR"; } } + +InstallerService::eResults InstallerService::patchFST(const std::string &path, const char *fstHash) { + std::string fstFilePath = path + "/code/title.fst"; + std::string fstBackupFilePath = path + "/code/backup.fst"; + std::string fstTargetFilePath = path + "/code/titla.fst"; + + if (!FSUtils::copyFile(fstFilePath, fstBackupFilePath)) { + DEBUG_FUNCTION_LINE("Failed to copy files"); + return FAILED_TO_COPY_FILES; + } + + std::string srcHash = Utils::hashFile(fstFilePath); + std::string dstHash = Utils::hashFile(fstBackupFilePath); + + if (srcHash != dstHash) { + ::remove(fstBackupFilePath.c_str()); + DEBUG_FUNCTION_LINE("Hashes do not match. %s %s", srcHash.c_str(), dstHash.c_str()); + return FAILED_TO_CHECK_HASH_COPIED_FILES; + } + + uint8_t *fstData = nullptr; + uint32_t fstDataSize = 0; + + DEBUG_FUNCTION_LINE("Trying to load FST from %s", fstFilePath.c_str()); + if (FSUtils::LoadFileToMem(fstFilePath.c_str(), &fstData, &fstDataSize) < 0) { + DEBUG_FUNCTION_LINE("Failed to load title.fst"); + return FAILED_TO_LOAD_FILE; + } + InstallerService::eResults res = patchFSTData(fstData, fstDataSize); + if (res != SUCCESS) { + free(fstData); + return res; + } + FSUtils::saveBufferToFile(fstTargetFilePath.c_str(), fstData, fstDataSize); + free(fstData); + fstData = nullptr; + + std::string newHash = Utils::hashFile(fstTargetFilePath); + + if (std::string(fstHash) == newHash) { + ::remove(fstBackupFilePath.c_str()); + DEBUG_FUNCTION_LINE("Successfully patched the title.fst"); + return SUCCESS; + } else { + DEBUG_FUNCTION_LINE("Hash mismatch! Expected %s but got %s while patching FST", fstHash, newHash.c_str()); + } + + FSUtils::copyFile(fstBackupFilePath, fstTargetFilePath); + + std::string srcHash2 = Utils::hashFile(fstTargetFilePath); + + if (srcHash != srcHash2) { + DEBUG_FUNCTION_LINE("Something went wrong. Failed to restore the title.fst. DO NOT RESTART THE SYSTEM until you manually restored the title.fst"); + return FST_HASH_MISMATCH_RESTORE_FAILED; + } + + ::remove(fstBackupFilePath.c_str()); + + return FST_HASH_MISMATCH; +} + +InstallerService::eResults InstallerService::patchCOS(const std::string &path, char *hash) { + std::string cosFilePath = path + "/code/cos.xml"; + std::string cosBackupFilePath = path + "/code/cback.xml"; + std::string cosTargetFilePath = path + "/code/cos.xml"; + + if (!FSUtils::copyFile(cosFilePath, cosBackupFilePath)) { + DEBUG_FUNCTION_LINE("Failed to copy files"); + return FAILED_TO_COPY_FILES; + } + + std::string srcHash = Utils::hashFile(cosFilePath); + std::string dstHash = Utils::hashFile(cosBackupFilePath); + + if (srcHash != dstHash) { + ::remove(cosBackupFilePath.c_str()); + DEBUG_FUNCTION_LINE("Hashes do not match. %s %s", srcHash.c_str(), dstHash.c_str()); + return FAILED_TO_CHECK_HASH_COPIED_FILES; + } + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(cosFilePath.c_str()); + + if (!result) { + ::remove(cosBackupFilePath.c_str()); + DEBUG_FUNCTION_LINE("failed to open %s : %s", cosFilePath.c_str(), result.description()); + return COS_XML_PARSING_FAILED; + } + + patchCOSXMLData(&doc); + + doc.save_file(cosTargetFilePath.c_str(), " ", pugi::format_default, pugi::encoding_utf8); + + std::string newHash = Utils::hashFile(cosTargetFilePath); + + if (std::string(hash) == newHash) { + ::remove(cosBackupFilePath.c_str()); + DEBUG_FUNCTION_LINE("Successfully patched the cos.xml"); + return SUCCESS; + } else { + DEBUG_FUNCTION_LINE("Hash mismatch! Expected %s but got %s while patching cos.xml", hash, newHash.c_str()); + } + + FSUtils::copyFile(cosBackupFilePath, cosTargetFilePath); + + std::string srcHash2 = Utils::hashFile(cosTargetFilePath); + + if (srcHash != srcHash2) { + DEBUG_FUNCTION_LINE("Something went wrong. Failed to restore the title.fst. DO NOT RESTART THE SYSTEM until you manually restored the cos.xml"); + return COS_XML_HASH_MISMATCH_RESTORE_FAILED; + } + ::remove(cosBackupFilePath.c_str()); + + return COS_XML_HASH_MISMATCH; +} diff --git a/source/InstallerService.h b/source/InstallerService.h index cfef261..9166452 100644 --- a/source/InstallerService.h +++ b/source/InstallerService.h @@ -28,6 +28,8 @@ public: FAILED_TO_LOAD_FILE = -18, }; + static eResults patchCOS(const std::string &path, char *hash); + static eResults checkCOS(const std::string &path, char *hash); static eResults checkSystemXML(const std::string &path, uint64_t titleId); @@ -36,10 +38,14 @@ public: static std::optional getInstalledAppInformation(); - static std::string ErrorMessage(eResults results); + static std::string ErrorMessage(eResults error); + static std::string ErrorDescription(eResults error); + + static eResults patchFST(const std::string &path, const char *hash); + private: - static eResults patchFST(uint8_t *data, uint32_t size); + static eResults patchFSTData(uint8_t *fstData, uint32_t size); - static bool patchCOS(pugi::xml_document *doc); + static bool patchCOSXMLData(pugi::xml_document *doc); }; \ No newline at end of file diff --git a/source/VPADInput.h b/source/VPADInput.h new file mode 100644 index 0000000..e7cd0ec --- /dev/null +++ b/source/VPADInput.h @@ -0,0 +1,59 @@ +#pragma once +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#include +#include "Input.h" + +class VPadInput : public Input { +public: + //!Constructor + VPadInput() { + memset(&vpad, 0, sizeof(vpad)); + } + + //!Destructor + ~VPadInput() override {} + + bool update(int32_t width, int32_t height) { + lastData = data; + + VPADReadError vpadError = VPAD_READ_NO_SAMPLES; + VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError); + + if (vpadError == VPAD_READ_SUCCESS) { + data.buttons_r = vpad.release; + data.buttons_h = vpad.hold; + data.buttons_d = vpad.trigger; + data.validPointer = !vpad.tpNormal.validity; + data.touched = vpad.tpNormal.touched; + + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &tpCalib, &vpad.tpFiltered1); + + //! calculate the screen offsets + data.x = -(width >> 1) + (int32_t) (((float) tpCalib.x / 1280.0f) * (float) width); + data.y = -(height >> 1) + (int32_t) (float) height - (((float) tpCalib.y / 720.0f) * (float) height); + + return true; + } + return false; + } + +private: + VPADStatus vpad{}; + VPADTouchData tpCalib{}; +}; \ No newline at end of file diff --git a/source/WiiUScreen.cpp b/source/WiiUScreen.cpp index 416bad4..7ce47cf 100644 --- a/source/WiiUScreen.cpp +++ b/source/WiiUScreen.cpp @@ -55,7 +55,7 @@ bool WiiUScreen::Init() { void WiiUScreen::DeInit() { if (sConsoleHasForeground) { OSScreenShutdown(); - WiiUScreen::ProcCallbackReleased(NULL); + WiiUScreen::ProcCallbackReleased(nullptr); } } @@ -76,11 +76,14 @@ void WiiUScreen::drawLinef(const char *fmt, ...) { va_end(va); } +void WiiUScreen::drawLine() { + WiiUScreen::drawLine(""); +} + void WiiUScreen::drawLine(const char *msg) { ScreenUtils::printTextOnScreen(CONSOLE_SCREEN_BOTH, 0, consoleCursorY++, msg); } - void WiiUScreen::flipBuffers() { ScreenUtils::flipBuffers(CONSOLE_SCREEN_BOTH); } diff --git a/source/WiiUScreen.h b/source/WiiUScreen.h index b5fa96b..69127ef 100644 --- a/source/WiiUScreen.h +++ b/source/WiiUScreen.h @@ -37,6 +37,9 @@ public: static void drawLinef(const char *fmt, ...); static void drawLine(const char *fmt); + + static void drawLine(); + static void flipBuffers(); static void clearScreen(); diff --git a/source/fs/FSUtils.cpp b/source/fs/FSUtils.cpp index 99030cb..8c8bac6 100644 --- a/source/fs/FSUtils.cpp +++ b/source/fs/FSUtils.cpp @@ -9,7 +9,7 @@ int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) { //! always initialze input - *inbuffer = NULL; + *inbuffer = nullptr; if (size) *size = 0; @@ -20,8 +20,8 @@ int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_ uint32_t filesize = lseek(iFd, 0, SEEK_END); lseek(iFd, 0, SEEK_SET); - uint8_t *buffer = (uint8_t *) malloc(filesize); - if (buffer == NULL) { + auto *buffer = (uint8_t *) malloc(filesize); + if (buffer == nullptr) { close(iFd); return -2; } @@ -44,7 +44,7 @@ int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_ if (done != filesize) { free(buffer); - buffer = NULL; + buffer = nullptr; return -3; } diff --git a/source/main.cpp b/source/main.cpp index 26dbacd..cb41645 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -8,12 +8,14 @@ #include #include #include +#include #include "WiiUScreen.h" #include "utils/logger.h" #include "InstallerService.h" #include "../build/safe_payload.h" -#include "GameState.h" +#include "ApplicationState.h" +#include "VPADInput.h" constexpr bool strings_equal(char const *a, char const *b) { return std::string_view(a) == b; @@ -25,14 +27,22 @@ void initIOSUHax(); void deInitIOSUHax(); +int sFSAFd = -1; +bool sIosuhaxMount = false; -int hello_thread() { +int main_loop() { DEBUG_FUNCTION_LINE("Creating state"); - GameState state; + ApplicationState state; + VPadInput input; + + if (sFSAFd < 0 || !sIosuhaxMount) { + state.setError(ApplicationState::eErrorState::ERROR_IOSUHAX_FAILED); + } DEBUG_FUNCTION_LINE("Entering main loop"); while (WHBProcIsRunning()) { - state.update(); + input.update(1280, 720); + state.update(&input); state.render(); } @@ -47,7 +57,7 @@ int main(int argc, char **argv) { initIOSUHax(); - hello_thread(); + main_loop(); deInitIOSUHax(); @@ -57,9 +67,6 @@ int main(int argc, char **argv) { return 0; } -int sFSAFd = -1; -bool sIosuhaxMount = false; - void initIOSUHax() { sIosuhaxMount = false; int res = IOSUHAX_Open(nullptr);