First commit

This commit is contained in:
Maschell 2020-10-14 13:54:25 +02:00
commit 3dc8316f17
32 changed files with 17572 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea/
cmake-build-debug/
build/
*.rpx
*.elf
CMakeLists.txt

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "payload"]
path = payload
url = https://github.com/wiiu-env/PayloadFromRPX

161
Makefile Normal file
View File

@ -0,0 +1,161 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wut/share/wut_rules
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#-------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source \
source/common \
source/utils \
source/fs
DATA := data
INCLUDES := include source
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
CFLAGS := -g -Wall -O2 -ffunction-sections -fstack-protector-all \
$(MACHDEP)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
CXXFLAGS := $(CFLAGS) -std=c++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
LIBS := -lwut -liosuhax
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#-------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#-------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#-------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#-------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#-------------------------------------------------------------------------------
export LD := $(CC)
#-------------------------------------------------------------------------------
else
#-------------------------------------------------------------------------------
export LD := $(CXX)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC) root.rpx.o
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean all
#-------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C payload
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#-------------------------------------------------------------------------------
clean:
@echo clean ...
make clean -C payload
@rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf
#-------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# main targets
#-------------------------------------------------------------------------------
safe_payload := ../payload/root.rpx
all : $(OUTPUT).rpx
$(safe_payload):
make -C ../payload
$(OUTPUT).rpx : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES) : safe_payload.h
$(OFILES_SRC) : $(HFILES_BIN)
#-------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#-------------------------------------------------------------------------------
safe_payload.h: $(safe_payload)
@bin2s -a 32 -H `(echo $(<F) | tr . _)`.h $< | $(AS) -o $(<F).o
@echo '#pragma once' > $@
@printf '#include "' >> $@
@(printf $(<F) | tr . _) >> $@
@echo '.h"' >> $@
@printf '#define RPX_HASH "' >> $@
@sha1sum $(<) | cut -f1 -d' ' | tr -d '\n' >> $@
@printf '"' >> $@
#-------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#-------------------------------------------------------------------------------
endif
#-------------------------------------------------------------------------------

1
payload Submodule

@ -0,0 +1 @@
Subproject commit c4c14496afc12b3f4f5ae6e9c335e379a212d1c2

411
source/ApplicationState.cpp Normal file
View File

@ -0,0 +1,411 @@
#include "ApplicationState.h"
#include "WiiUScreen.h"
#include "ScreenUtils.h"
#include "../build/safe_payload.h"
#include <sysapp/launch.h>
#include <iosuhax.h>
extern "C" void OSShutdown();
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:");
WiiUScreen::drawLinef("%s", appInfo->appName);
WiiUScreen::drawLine();
if (this->fstAlreadyPatched) {
WiiUScreen::drawLine("[ X ] title.fst is already patched!");
} else if (this->fstPatchPossible) {
WiiUScreen::drawLine("[ X ] title.fst can be patched!");
} else {
WiiUScreen::drawLine("[ ] title.fst can NOT be patched!");
}
if (this->cosAlreadyPatched) {
WiiUScreen::drawLine("[ X ] cos.xml is already patched!");
} else if (this->cosPatchPossible) {
WiiUScreen::drawLine("[ X ] cos.xml can be patched!");
} else {
WiiUScreen::drawLine("[ ] cos.xml can NOT be patched!");
}
if (this->rpxAlreadyPatched) {
WiiUScreen::drawLine("[ X ] safe.rpx is already patched!");
} else {
WiiUScreen::drawLine("[ X ] safe.rpx can to be patched!");
}
WiiUScreen::drawLine();
WiiUScreen::drawLinef("System is booting into: ");
if (this->coldbootTitle == nullptr) {
WiiUScreen::drawLinef("%ll016X (Unknown title)", this->coldbootTitleId);
} else {
WiiUScreen::drawLinef("%ll016X (%s)", this->coldbootTitle->tid, this->coldbootTitle->name);
}
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_NO_COLDBOOT_ALLOWED) {
WiiUScreen::drawLine("Note: To install Aroma as coldboot you need to run this installer");
WiiUScreen::drawLine("from an already running Aroma instance (and not the browser)");
WiiUScreen::drawLine("After the installation has finished, reboot the console, open the");
WiiUScreen::drawLine("Health & Safety app and run the Aroma installer.");
WiiUScreen::drawLine();
if (this->selectedOption == 0) {
WiiUScreen::drawLine("> Back Install without Coldboot");
} else if (this->selectedOption == 1) {
WiiUScreen::drawLine("> Back > Install without 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");
} else if (this->state == STATE_INSTALL_SUCCESS) {
WiiUScreen::drawLine("Aroma was successfully installed");
WiiUScreen::drawLine();
WiiUScreen::drawLine("Press A to shutdown the console");
}
printFooter();
WiiUScreen::flipBuffers();
}
void ApplicationState::update(Input *input) {
if (this->state == STATE_ERROR) {
OSEnableHomeButtonMenu(true);
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) {
if (!InstallerService::isColdBootAllowed()) {
this->installColdboot = false;
this->state = STATE_INSTALL_NO_COLDBOOT_ALLOWED;
return;
}
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_NO_COLDBOOT_ALLOWED) {
proccessMenuNavigation(input, 2);
if (entrySelected(input)) {
if (this->selectedOption == 0) {
this->state = STATE_CHECK_PATCH_POSSIBLE_DONE;
} else {
this->state = STATE_INSTALL_CONFIRM_DIALOG;
}
this->selectedOption = 0;
}
} 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) {
setError(ERROR_INSTALLER_ERROR);
this->installerError = result;
} 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) {
setError(ERROR_INSTALLER_ERROR);
this->installerError = result;
} else {
this->state = STATE_INSTALL_RPX;
}
} else if (this->state == STATE_INSTALL_RPX) {
auto result = InstallerService::copyRPX(this->appInfo->path, root_rpx, root_rpx_size, RPX_HASH);
if (result != InstallerService::SUCCESS) {
setError(ERROR_INSTALLER_ERROR);
this->installerError = result;
} else {
if (this->installColdboot) {
this->state = STATE_INSTALL_SYSTEM_XML;
} else {
this->state = STATE_INSTALL_SUCCESS;
}
}
} else if (this->state == STATE_INSTALL_SYSTEM_XML) {
auto result = InstallerService::patchSystemXML("storage_slc_installer:/config", this->appInfo->titleId);
if (result != InstallerService::SUCCESS) {
setError(ERROR_INSTALLER_ERROR);
this->installerError = result;
} else {
auto fsaFd = IOSUHAX_FSA_Open();
if (fsaFd >= 0) {
if (IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01") == 0) {
DEBUG_FUNCTION_LINE("Flushed mlc");
}
if (IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/system") == 0) {
DEBUG_FUNCTION_LINE("Flushed slc");
}
IOSUHAX_FSA_Close(fsaFd);
} else {
DEBUG_FUNCTION_LINE("Failed to open fsa");
}
this->state = STATE_INSTALL_SUCCESS;
}
} else if (this->state == STATE_INSTALL_SUCCESS) {
if (entrySelected(input)) {
OSShutdown();
}
}
}
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;
}
this->coldbootTitleId = InstallerService::getColdbootTitleId("storage_slc_installer:/config");
this->coldbootTitle = nullptr;
for (int i = 0; GameList[i].tid != 0; i++) {
if (GameList[i].tid == this->coldbootTitleId) {
this->coldbootTitle = &GameList[i];
break;
}
}
DEBUG_FUNCTION_LINE("CHECK FST");
InstallerService::eResults result;
this->fstAlreadyPatched = ((result = InstallerService::checkFSTAlreadyValid(this->appInfo->path, this->appInfo->fstHash)) == InstallerService::SUCCESS);
this->rpxAlreadyPatched = ((result = InstallerService::checkRPXAlreadyValid(this->appInfo->path, RPX_HASH)) == InstallerService::SUCCESS);
this->cosAlreadyPatched = ((result = InstallerService::checkCOSAlreadyValid(this->appInfo->path, this->appInfo->cosHash)) == InstallerService::SUCCESS);
this->tmdValid = ((result = InstallerService::checkTMDValid(this->appInfo->path, this->appInfo->tmdHash)) == InstallerService::SUCCESS);
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();
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 err) {
this->state = STATE_ERROR;
this->error = err;
OSEnableHomeButtonMenu(true);
}
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;
}

80
source/ApplicationState.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include <string>
#include <optional>
#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_NO_COLDBOOT_ALLOWED,
STATE_INSTALL_CONFIRM_DIALOG,
STATE_INSTALL_STARTED,
STATE_INSTALL_FST,
STATE_INSTALL_COS,
STATE_INSTALL_RPX,
STATE_INSTALL_SYSTEM_XML,
STATE_INSTALL_SUCCESS
};
ApplicationState();
void setError(eErrorState error);
void render();
void update(Input *input);
void checkPatchPossible();
void getAppInformation();
std::optional<appInformation> appInfo;
std::string ErrorMessage();
std::string ErrorDescription();
void handleError();
int selectedOption;
static void printFooter();
void proccessMenuNavigation(Input *input, int maxOptionValue);
static 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;
uint64_t coldbootTitleId;
_gList_t *coldbootTitle;
bool fstAlreadyPatched;
bool rpxAlreadyPatched;
bool cosAlreadyPatched;
bool tmdValid;
};

5
source/Input.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by Maschell on 15.10.2020.
//
#include "Input.h"

60
source/Input.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <cstring>
#include <cstdint>
class Input {
public:
//!Constructor
Input() = default;
//!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;
uint32_t buttons_r;
bool validPointer;
bool touched;
float pointerAngle;
int32_t x;
int32_t y;
} PadData;
PadData data{};
PadData lastData{};
};

620
source/InstallerService.cpp Normal file
View File

@ -0,0 +1,620 @@
#include "common/common.h"
#include "utils/logger.h"
#include "WiiUScreen.h"
#include "StringTools.h"
#include "fs/FSUtils.h"
#include "common/fst_structs.h"
#include "InstallerService.h"
#include "utils/utils.h"
#include "utils/pugixml.hpp"
#include <coreinit/mcp.h>
#include <string>
#include <memory>
#include <cstdlib>
#include <malloc.h>
#include <sstream>
InstallerService::eResults InstallerService::checkCOS(const std::string &path, char *hash) {
std::string cosFilePath = path + "/code/cos.xml";
DEBUG_FUNCTION_LINE("Loading %s", cosFilePath.c_str());
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(cosFilePath.c_str());
if (!result) {
DEBUG_FUNCTION_LINE("failed to open %s : %s", cosFilePath.c_str(), result.description());
return COS_XML_PARSING_FAILED;
}
patchCOSXMLData(&doc);
std::stringstream ss;
doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8);
std::string newHash = Utils::calculateSHA1(ss.str().c_str(), ss.str().size());
if (std::string(hash) == newHash) {
DEBUG_FUNCTION_LINE("Success! cos.xml is compatible");
return SUCCESS;
} else {
DEBUG_FUNCTION_LINE("Hash mismatch! cos.xml is NOT compatible. Expected hash: %s actual hash: %s",hash, newHash.c_str());
}
return COS_XML_HASH_MISMATCH;
}
InstallerService::eResults InstallerService::checkSystemXML(const std::string &path, uint64_t titleId) {
std::string inputFile = std::string(path + "/system.xml");
systemXMLInformation *data = nullptr;
for (int i = 0; systemXMLHashInformation[i].titleId != 0; i++) {
if (systemXMLHashInformation[i].titleId == titleId) {
data = &systemXMLHashInformation[i];
break;
}
}
if (data == nullptr) {
DEBUG_FUNCTION_LINE("system xml information were not found.");
return SYSTEM_XML_INFORMATION_NOT_FOUND;
}
DEBUG_FUNCTION_LINE("Setting coldboot to %016llX", data->titleId);
pugi::xml_document doc;
pugi::xml_parse_result resultSystem = doc.load_file(inputFile.c_str());
if (!resultSystem) {
DEBUG_FUNCTION_LINE("Error while parsing %s: %s", inputFile.c_str(), resultSystem.description());
return SYSTEM_XML_PARSING_FAILED;
}
char tmp[18];
snprintf(tmp, 17, "%016llX", data->titleId);
doc.child("system").child("default_title_id").first_child().set_value(tmp);
if (!doc.child("system").child("log").attribute("length")) {
doc.child("system").child("log").append_attribute("length") = "0";
}
if (!doc.child("system").child("standby").attribute("length")) {
doc.child("system").child("standby").append_attribute("length") = "0";
}
if (!doc.child("system").child("ramdisk").attribute("length")) {
doc.child("system").child("ramdisk").append_attribute("length") = "0";
}
std::stringstream ss;
doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8);
std::string newHash = Utils::calculateSHA1(ss.str().c_str(), ss.str().size());
if (std::string(data->hash) == newHash || std::string(data->hash2) == newHash) {
DEBUG_FUNCTION_LINE("Success! system.xml is compatible");
return SUCCESS;
} else {
DEBUG_FUNCTION_LINE("Hash mismatch! system.xml is NOT compatible. Expected hash: %s actual hash: %s", data->hash, newHash.c_str());
}
return SYSTEM_XML_HASH_MISMATCH;
}
InstallerService::eResults InstallerService::checkFST(const std::string &path, const char *fstHash) {
std::string fstFilePath = path + "/code/title.fst";
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);
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;
} else {
DEBUG_FUNCTION_LINE("title.fst is NOT compatible. expected hash: %s actual hash: %s", fstHash, newHash.c_str());
return FST_HASH_MISMATCH;
}
}
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");
appEntry.child("codegen_size").first_child().set_value("02000000");
appEntry.child("codegen_core").first_child().set_value("80000001");
appEntry.child("max_size").first_child().set_value("40000000");
appEntry.child("max_codesize").first_child().set_value("00800000");
for (pugi::xml_node permission: appEntry.child("permissions").children()) {
auto mask = permission.child("mask");
mask.first_child().set_value("FFFFFFFFFFFFFFFF");
}
return true;
}
std::optional<appInformation> InstallerService::getInstalledAppInformation() {
auto mcpHandle = (int32_t) MCP_Open();
auto titleCount = (uint32_t) MCP_TitleCount(mcpHandle);
auto *titleList = (MCPTitleListType *) memalign(32, sizeof(MCPTitleListType) * titleCount);
MCP_TitleListByAppType(mcpHandle, MCP_APP_TYPE_SYSTEM_APPS, &titleCount, titleList, sizeof(MCPTitleListType) * titleCount);
MCP_Close(mcpHandle);
DEBUG_FUNCTION_LINE("%d titles found on the WiiU", titleCount);
bool success = false;
for (uint32_t i = 0; i < titleCount; ++i) {
for (int j = 0; supportedApps[j].titleId != 0; j++) {
if (titleList[i].titleId == supportedApps[j].titleId) {
DEBUG_FUNCTION_LINE("%s is on the Wii U (%s) %d", supportedApps[j].appName, titleList[i].path, sizeof(supportedApps[j].path));
supportedApps[j].onTheWiiU = true;
strncpy(supportedApps[j].path, titleList[i].path, 255);
success = true;
break;
}
}
}
free(titleList);
if (success) {
success = false;
for (int j = 0; supportedApps[j].titleId != 0; j++) {
if (supportedApps[j].onTheWiiU) {
std::string path(supportedApps[j].path);
if (!StringTools::replace(path, "/vol/storage_mlc01", "storage_mlc_installer:")) {
DEBUG_FUNCTION_LINE("Title is not on MLC. This is not expected. %s", path.c_str());
return {};
}
strncpy(supportedApps[j].path, path.c_str(), 255);
return supportedApps[j];
}
}
}
return {};
}
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);
return InstallerService::FST_HEADER_MISMATCH;
}
auto numberOfSections = fstHeader->numberOfSections;
DEBUG_FUNCTION_LINE("Found %d sections", numberOfSections);
auto *sections = (FSTSectionEntry *) (fstData + sizeof(FSTHeader));
auto usableSectionIndex = -1;
for (uint32_t i = 0; i < numberOfSections; i++) {
if (sections[i].hashMode == 2) {
usableSectionIndex = i;
}
}
if (usableSectionIndex < 0) {
DEBUG_FUNCTION_LINE("Failed to find suitable section");
return InstallerService::FST_NO_USABLE_SECTION_FOUND;
} else {
DEBUG_FUNCTION_LINE("Section %d can be used as a base", usableSectionIndex);
}
auto *rootEntry = (FSTNodeEntry *) (fstData + sizeof(FSTHeader) + numberOfSections * sizeof(FSTSectionEntry));
auto numberOfNodeEntries = rootEntry->directory.lastEntryNumber;
char *stringTableOffset = (char *) ((uint32_t) rootEntry + (sizeof(FSTNodeEntry) * numberOfNodeEntries));
FSTNodeEntry *nodeEntries = rootEntry;
for (uint32_t i = 0; i < numberOfNodeEntries; i++) {
if (sections[nodeEntries[i].sectionNumber].hashMode != 2) {
auto nameOffset = (*((uint32_t *) nodeEntries[i].nameOffset) & 0xFFFFFF00) >> 8;
DEBUG_FUNCTION_LINE("Updating sectionNumber for %s (entry %d)", stringTableOffset + nameOffset, i);
nodeEntries[i].sectionNumber = usableSectionIndex;
}
}
return InstallerService::SUCCESS;
}
std::string InstallerService::ErrorDescription(InstallerService::eResults error) {
if (error == SUCCESS) {
return "Success";
} 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 (error == FAILED_TO_COPY_FILES) {
return "FAILED_TO_COPY_FILES";
} else if (error == FAILED_TO_CHECK_HASH_COPIED_FILES) {
return "FAILED_TO_CHECK_HASH_COPIED_FILES";
} else if (error == SYSTEM_XML_INFORMATION_NOT_FOUND) {
return "SYSTEM_XML_INFORMATION_NOT_FOUND";
} else if (error == SYSTEM_XML_PARSING_FAILED) {
return "SYSTEM_XML_PARSING_FAILED";
} else if (error == SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED) {
return "SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED";
} else if (error == SYSTEM_XML_HASH_MISMATCH) {
return "SYSTEM_XML_HASH_MISMATCH";
} else if (error == RPX_HASH_MISMATCH) {
return "RPX_HASH_MISMATCH";
} else if (error == RPX_HASH_MISMATCH_RESTORE_FAILED) {
return "RPX_HASH_MISMATCH_RESTORE_FAILED";
} else if (error == COS_XML_PARSING_FAILED) {
return "COS_XML_PARSING_FAILED";
} else if (error == COS_XML_HASH_MISMATCH) {
return "COS_XML_HASH_MISMATCH";
} else if (error == COS_XML_HASH_MISMATCH_RESTORE_FAILED) {
return "COS_XML_HASH_MISMATCH_RESTORE_FAILED";
} else if (error == MALLOC_FAILED) {
return "MALLOC_FAILED";
} else if (error == FST_HASH_MISMATCH) {
return "FST_HASH_MISMATCH";
} else if (error == FST_HASH_MISMATCH_RESTORE_FAILED) {
return "FST_HASH_MISMATCH_RESTORE_FAILED";
} else if (error == FST_HEADER_MISMATCH) {
return "FST_HEADER_MISMATCH";
} else if (error == FST_NO_USABLE_SECTION_FOUND) {
return "FST_NO_USABLE_SECTION_FOUND";
} 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/title.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;
}
InstallerService::eResults InstallerService::copyRPX(const std::string &path, const uint8_t *rpx_data, size_t size, const std::string &rpx_hash) {
std::string rpxSourceFilePath = path + "/code/safe.rpx";
std::string rpxBackupFilePath = path + "/code/sbac.rpx";
std::string rpxTargetFilePath = path + "/code/safe.rpx";
if (!FSUtils::copyFile(rpxSourceFilePath, rpxBackupFilePath)) {
DEBUG_CONSOLE_LOG("Failed to copy files");
return FAILED_TO_COPY_FILES;
}
std::string srcHash = Utils::hashFile(rpxSourceFilePath);
std::string dstHash = Utils::hashFile(rpxBackupFilePath);
if (srcHash != dstHash) {
::remove(rpxBackupFilePath.c_str());
DEBUG_CONSOLE_LOG("Hashes do not match. %s %s", srcHash.c_str(), dstHash.c_str());
return FAILED_TO_CHECK_HASH_COPIED_FILES;
}
if (FSUtils::saveBufferToFile(rpxTargetFilePath.c_str(), (void *) rpx_data, size) < 0) {
DEBUG_CONSOLE_LOG("Cannot save rpx to file %s", rpxTargetFilePath.c_str());
return SUCCESS;
}
std::string newHash = Utils::hashFile(rpxTargetFilePath);
if (StringTools::strCompareIC(rpx_hash, newHash)) {
::remove(rpxBackupFilePath.c_str());
DEBUG_CONSOLE_LOG("Successfully patched the safe.rpx");
return SUCCESS;
} else {
DEBUG_CONSOLE_LOG("Hash mismatch! Expected %s but got %s while patching safe.rpx", rpx_hash.c_str(), newHash.c_str());
}
FSUtils::copyFile(rpxBackupFilePath, rpxTargetFilePath);
std::string srcHash2 = Utils::hashFile(rpxTargetFilePath);
if (srcHash != srcHash2) {
DEBUG_CONSOLE_LOG("Something went wrong. Failed to restore the safe.rpx. DO NOT RESTART THE SYSTEM until you manually restored the safe.rpx");
return RPX_HASH_MISMATCH_RESTORE_FAILED;
}
::remove(rpxBackupFilePath.c_str());
return RPX_HASH_MISMATCH;
}
InstallerService::eResults InstallerService::patchSystemXML(const std::string &path, uint64_t titleId) {
std::string inputFile = std::string(path + "/system.xml");
std::string backupFile = std::string(path + "/sbackup.xml");
std::string finalFile = std::string(path + "/system.xml");
if (!FSUtils::copyFile(inputFile, backupFile)) {
DEBUG_CONSOLE_LOG("Failed to copy files");
return FAILED_TO_COPY_FILES;
}
std::string srcHash = Utils::hashFile(inputFile);
std::string dstHash = Utils::hashFile(backupFile);
if (srcHash != dstHash) {
::remove(backupFile.c_str());
DEBUG_CONSOLE_LOG("Hashes do not match. %s %s", srcHash.c_str(), dstHash.c_str());
return FAILED_TO_CHECK_HASH_COPIED_FILES;
}
systemXMLInformation *data = nullptr;
for (int i = 0; systemXMLHashInformation[i].titleId != 0; i++) {
if (systemXMLHashInformation[i].titleId == titleId) {
data = &systemXMLHashInformation[i];
break;
}
}
if (data == nullptr) {
::remove(backupFile.c_str());
DEBUG_FUNCTION_LINE("Data was NULL");
return SYSTEM_XML_INFORMATION_NOT_FOUND;
}
pugi::xml_document doc;
pugi::xml_parse_result resultSystem = doc.load_file(inputFile.c_str());
if (!resultSystem) {
::remove(backupFile.c_str());
DEBUG_FUNCTION_LINE("Error while parsing %s: %s", inputFile.c_str(), resultSystem.description());
return SYSTEM_XML_PARSING_FAILED;
}
char tmp[18];
snprintf(tmp, 17, "%016llX", data->titleId);
doc.child("system").child("default_title_id").first_child().set_value(tmp);
if (!doc.child("system").child("log").attribute("length")) {
doc.child("system").child("log").append_attribute("length") = "0";
}
if (!doc.child("system").child("standby").attribute("length")) {
doc.child("system").child("standby").append_attribute("length") = "0";
}
if (!doc.child("system").child("ramdisk").attribute("length")) {
doc.child("system").child("ramdisk").append_attribute("length") = "0";
}
doc.save_file(finalFile.c_str(), " ", pugi::format_default, pugi::encoding_utf8);
std::string newHash = Utils::hashFile(finalFile);
if (std::string(data->hash) == newHash || std::string(data->hash2) == newHash) {
::remove(backupFile.c_str());
DEBUG_FUNCTION_LINE("Successfully patched the system.xml");
return SUCCESS;
} else {
DEBUG_FUNCTION_LINE("Hash mismatch! Expected %s but got %s while setting default titleID to %s", data->hash, newHash.c_str(), data->titleId);
}
FSUtils::copyFile(backupFile, finalFile);
std::string srcHash2 = Utils::hashFile(finalFile);
if (srcHash != srcHash2) {
DEBUG_FUNCTION_LINE("Something went wrong. Failed to restore the system.xml. DO NOT RESTART THE SYSTEM until you manually restored the system.xml");
return SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED;
}
::remove(backupFile.c_str());
return SYSTEM_XML_HASH_MISMATCH;
}
uint64_t InstallerService::getColdbootTitleId(const std::string &path) {
std::string inputFile = std::string(path + "/system.xml");
pugi::xml_document doc;
pugi::xml_parse_result resultSystem = doc.load_file(inputFile.c_str());
if (!resultSystem) {
DEBUG_FUNCTION_LINE("Error while parsing %s: %s", inputFile.c_str(), resultSystem.description());
return SYSTEM_XML_PARSING_FAILED;
}
DEBUG_FUNCTION_LINE("%s", doc.child("system").child("default_title_id").first_child().value());
uint64_t result = strtoull(doc.child("system").child("default_title_id").first_child().value(), nullptr, 16);
return result;
}
InstallerService::eResults InstallerService::checkFSTAlreadyValid(const std::string &path, const std::string &hash) {
std::string filePath = path + "/code/title.fst";
return checkFileHash(filePath, hash);
}
InstallerService::eResults InstallerService::checkTMDValid(const std::string &path, const std::string &hash) {
std::string filePath = path + "/code/title.fst";
return checkFileHash(filePath, hash);
}
InstallerService::eResults InstallerService::checkCOSAlreadyValid(const std::string &path, const std::string &hash) {
std::string filePath = path + "/code/cos.xml";
return checkFileHash(filePath, hash);
}
InstallerService::eResults InstallerService::checkRPXAlreadyValid(const std::string &path, const std::string &hash) {
std::string filePath = path + "/code/safe.rpx";
return checkFileHash(filePath, hash);
}
InstallerService::eResults InstallerService::checkFileHash(const std::string &filePath, const std::string &hash) {
uint8_t *fileData = nullptr;
uint32_t fileDataSize = 0;
if (FSUtils::LoadFileToMem(filePath.c_str(), &fileData, &fileDataSize) < 0) {
return FAILED_TO_LOAD_FILE;
}
std::string newHash = Utils::calculateSHA1((const char *) fileData, fileDataSize);
free(fileData);
fileData = nullptr;
if (StringTools::strCompareIC(hash, newHash)) {
return SUCCESS;
} else {
DEBUG_FUNCTION_LINE("expected %s actual %s", hash.c_str(), newHash.c_str());
return FST_HASH_MISMATCH;
}
}

82
source/InstallerService.h Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include "utils/pugixml.hpp"
#include "common/common.h"
#include <optional>
#include <coreinit/memorymap.h>
class InstallerService {
public:
enum eResults {
SUCCESS = 0,
NO_COMPATIBLE_APP_INSTALLED = -1,
FAILED_TO_COPY_FILES = -2,
FAILED_TO_CHECK_HASH_COPIED_FILES = -3,
SYSTEM_XML_INFORMATION_NOT_FOUND = -4,
SYSTEM_XML_PARSING_FAILED = -5,
SYSTEM_XML_HASH_MISMATCH_RESTORE_FAILED = -6,
SYSTEM_XML_HASH_MISMATCH = -7,
RPX_HASH_MISMATCH = -8,
RPX_HASH_MISMATCH_RESTORE_FAILED = -9,
COS_XML_PARSING_FAILED = -10,
COS_XML_HASH_MISMATCH = -11,
COS_XML_HASH_MISMATCH_RESTORE_FAILED = -12,
MALLOC_FAILED = -13,
FST_HASH_MISMATCH = -14,
FST_HASH_MISMATCH_RESTORE_FAILED = -15,
FST_HEADER_MISMATCH = -16,
FST_NO_USABLE_SECTION_FOUND = -17,
FAILED_TO_LOAD_FILE = -18,
};
static bool isColdBootAllowed(){
if (OSIsAddressValid(0x00FFFFF8)) {
uint64_t bootedFrom = *((uint64_t *) 0x00FFFFF8);
if (
bootedFrom == 0x000500101004E000L || // H&S JAP
bootedFrom == 0x000500101004E100L || // H&S USA
bootedFrom == 0x000500101004E200L // H&S EUR
) {
return true;
}
}
return false;
}
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);
static eResults checkFST(const std::string &path, const char *fstHash);
static std::optional<appInformation> getInstalledAppInformation();
static std::string ErrorMessage(eResults error);
static std::string ErrorDescription(eResults error);
static eResults patchFST(const std::string &path, const char *hash);
static eResults copyRPX(const std::string &path, const uint8_t *rpx_data, size_t size, const std::string &rpx_hash);
static eResults patchSystemXML(const std::string &path, uint64_t id);
static uint64_t getColdbootTitleId(const std::string &path);
static eResults checkFSTAlreadyValid(const std::string &path, const std::string &hash);
static eResults checkTMDValid(const std::string &path, const std::string &hash);
static eResults checkCOSAlreadyValid(const std::string &path, const std::string &hash);
static eResults checkRPXAlreadyValid(const std::string &path, const std::string &hash);
private:
static eResults patchFSTData(uint8_t *fstData, uint32_t size);
static bool patchCOSXMLData(pugi::xml_document *doc);
static eResults checkFileHash(const std::string &filePath, const std::string &hash);
};

48
source/ScreenUtils.cpp Normal file
View File

@ -0,0 +1,48 @@
/****************************************************************************
* Copyright (C) 2018 Maschell
*
* 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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include <coreinit/screen.h>
#include "ScreenUtils.h"
void ScreenUtils::printTextOnScreen(ConsoleScreenID screen, int x, int y, const char *msg) {
if (!msg) {
return;
}
if (screen != CONSOLE_SCREEN_DRC) { // Draw TV if it's not DRC exclusive.
OSScreenPutFontEx(SCREEN_TV, x, y, msg);
}
if (screen != CONSOLE_SCREEN_TV) { // Draw DRC if it's not TV exclusive.
OSScreenPutFontEx(SCREEN_DRC, x, y, msg);
}
}
void ScreenUtils::OSScreenClear(ConsoleScreenID screen, uint32_t color) {
if (screen != CONSOLE_SCREEN_DRC) { // Clear TV if it's not DRC exclusive.
OSScreenClearBufferEx(SCREEN_TV, color);
}
if (screen != CONSOLE_SCREEN_TV) { // Clear DRC if it's not TV exclusive.
OSScreenClearBufferEx(SCREEN_DRC, color);
}
}
void ScreenUtils::flipBuffers(ConsoleScreenID screen) {
if (screen != CONSOLE_SCREEN_DRC) { // Flip TV buffer if it's not DRC exclusive.
OSScreenFlipBuffersEx(SCREEN_TV);
}
if (screen != CONSOLE_SCREEN_TV) { // Flip DRC buffer if it's not TV exclusive.
OSScreenFlipBuffersEx(SCREEN_DRC);
}
}

55
source/ScreenUtils.h Normal file
View File

@ -0,0 +1,55 @@
/****************************************************************************
* Copyright (C) 2018-2020 Maschell
*
* 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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#pragma once
//! Defines the ID of a display usable with OSScreen.
typedef enum ConsoleScreenID {
//! Represents the TV connected to the system.
CONSOLE_SCREEN_TV = 0,
//! Represents the screen in the DRC (gamepad).
CONSOLE_SCREEN_DRC = 1,
//! Represents both screens
CONSOLE_SCREEN_BOTH = 2,
} ConsoleScreenID;
class ScreenUtils {
public:
/**
Clears the screen for the given screens
\param screen defines on which screens should be printed
\param x defines the x position (character position) where the text should be printed
\param y defines on which line the text should be printed
\param msg C string that contains the text to be printed.
**/
static void printTextOnScreen(ConsoleScreenID screen, int x, int y, const char *msg);
/**
Clears the screen for the given screens
\param screen defines which screens should be cleared
**/
static void OSScreenClear(ConsoleScreenID screen, uint32_t i);
/**
Flips the buffer for the given screens
\param screen defines which screens should be flipped.
**/
static void flipBuffers(ConsoleScreenID screen);
private:
ScreenUtils() = default;
~ScreenUtils() = default;
};

234
source/StringTools.cpp Normal file
View File

@ -0,0 +1,234 @@
/***************************************************************************
* Copyright (C) 2010
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* for WiiXplorer 2010
***************************************************************************/
#include <vector>
#include <string>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <strings.h>
#include <wut_types.h>
#include <stdio.h>
#include "StringTools.h"
BOOL StringTools::EndsWith(const std::string &a, const std::string &b) {
if (b.size() > a.size())
return false;
return std::equal(a.begin() + a.size() - b.size(), a.end(), b.begin());
}
const char *StringTools::byte_to_binary(int32_t x) {
static char b[9];
b[0] = '\0';
int32_t z;
for (z = 128; z > 0; z >>= 1) {
strcat(b, ((x & z) == z) ? "1" : "0");
}
return b;
}
std::string StringTools::removeCharFromString(std::string &input, char toBeRemoved) {
std::string output = input;
size_t position;
while (1) {
position = output.find(toBeRemoved);
if (position == std::string::npos)
break;
output.erase(position, 1);
}
return output;
}
const char *StringTools::fmt(const char *format, ...) {
static char strChar[512];
strChar[0] = 0;
va_list va;
va_start(va, format);
if ((vsprintf(strChar, format, va) >= 0)) {
va_end(va);
return (const char *) strChar;
}
va_end(va);
return NULL;
}
const wchar_t *StringTools::wfmt(const char *format, ...) {
static char tmp[512];
static wchar_t strWChar[512];
strWChar[0] = 0;
tmp[0] = 0;
if (!format)
return (const wchar_t *) strWChar;
if (strcmp(format, "") == 0)
return (const wchar_t *) strWChar;
va_list va;
va_start(va, format);
if ((vsprintf(tmp, format, va) >= 0)) {
int32_t bt;
int32_t strlength = strlen(tmp);
bt = mbstowcs(strWChar, tmp, (strlength < 512) ? strlength : 512);
if (bt > 0) {
strWChar[bt] = 0;
return (const wchar_t *) strWChar;
}
}
va_end(va);
return NULL;
}
int32_t StringTools::strprintf(std::string &str, const char *format, ...) {
static char tmp[512];
tmp[0] = 0;
int32_t result = 0;
va_list va;
va_start(va, format);
if ((vsprintf(tmp, format, va) >= 0)) {
str = tmp;
result = str.size();
}
va_end(va);
return result;
}
std::string StringTools::strfmt(const char *format, ...) {
std::string str;
static char tmp[512];
tmp[0] = 0;
va_list va;
va_start(va, format);
if ((vsprintf(tmp, format, va) >= 0)) {
str = tmp;
}
va_end(va);
return str;
}
BOOL StringTools::char2wchar_t(const char *strChar, wchar_t *dest) {
if (!strChar || !dest)
return false;
int32_t bt;
bt = mbstowcs(dest, strChar, strlen(strChar));
if (bt > 0) {
dest[bt] = 0;
return true;
}
return false;
}
int32_t StringTools::strtokcmp(const char *string, const char *compare, const char *separator) {
if (!string || !compare)
return -1;
char TokCopy[512];
strncpy(TokCopy, compare, sizeof(TokCopy));
TokCopy[511] = '\0';
char *strTok = strtok(TokCopy, separator);
while (strTok != NULL) {
if (strcasecmp(string, strTok) == 0) {
return 0;
}
strTok = strtok(NULL, separator);
}
return -1;
}
int32_t StringTools::strextcmp(const char *string, const char *extension, char seperator) {
if (!string || !extension)
return -1;
char *ptr = strrchr(string, seperator);
if (!ptr)
return -1;
return strcasecmp(ptr + 1, extension);
}
std::vector<std::string> StringTools::stringSplit(const std::string &inValue, const std::string &splitter) {
std::string value = inValue;
std::vector<std::string> result;
while (true) {
uint32_t index = value.find(splitter);
if (index == std::string::npos) {
result.push_back(value);
break;
}
std::string first = value.substr(0, index);
result.push_back(first);
if (index + splitter.size() == value.length()) {
result.push_back("");
break;
}
if (index + splitter.size() > value.length()) {
break;
}
value = value.substr(index + splitter.size(), value.length());
}
return result;
}
bool StringTools::findStringIC(const std::string &strHaystack, const std::string &strNeedle) {
auto it = std::search(
strHaystack.begin(), strHaystack.end(),
strNeedle.begin(), strNeedle.end(),
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return (it != strHaystack.end());
}
bool StringTools::replace(std::string &str, const std::string &from, const std::string &to) {
size_t start_pos = str.find(from);
if (start_pos == std::string::npos) {
return false;
}
str.replace(start_pos, from.length(), to);
return true;
}
bool StringTools::strCompareIC(const std::string &str1, const std::string &str2) {
return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b) { return std::tolower(a) == std::tolower(b); });
}

95
source/StringTools.h Normal file
View File

@ -0,0 +1,95 @@
/***************************************************************************
* Copyright (C) 2010
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* for WiiXplorer 2010
***************************************************************************/
#pragma once
#include <vector>
#include <string>
#include <wut_types.h>
#include <algorithm>
#include <cctype>
class StringTools {
public:
static BOOL EndsWith(const std::string &a, const std::string &b);
static const char *byte_to_binary(int32_t x);
static std::string removeCharFromString(std::string &input, char toBeRemoved);
static const char *fmt(const char *format, ...);
static const wchar_t *wfmt(const char *format, ...);
static int32_t strprintf(std::string &str, const char *format, ...);
static std::string strfmt(const char *format, ...);
static BOOL char2wchar_t(const char *src, wchar_t *dest);
static int32_t strtokcmp(const char *string, const char *compare, const char *separator);
static int32_t strextcmp(const char *string, const char *extension, char seperator);
static const char *FullpathToFilename(const char *path) {
if (!path)
return path;
const char *ptr = path;
const char *Filename = ptr;
while (*ptr != '\0') {
if (ptr[0] == '/' && ptr[1] != '\0')
Filename = ptr + 1;
++ptr;
}
return Filename;
}
static void RemoveDoubleSlashs(std::string &str) {
uint32_t length = str.size();
//! clear path of double slashes
for (uint32_t i = 1; i < length; ++i) {
if (str[i - 1] == '/' && str[i] == '/') {
str.erase(i, 1);
i--;
length--;
}
}
}
static std::vector<std::string> stringSplit(const std::string &value, const std::string &splitter);
// https://stackoverflow.com/a/19839371
static bool findStringIC(const std::string &strHaystack, const std::string &strNeedle);
// https://stackoverflow.com/a/3418285
static bool replace(std::string& str, const std::string& from, const std::string& to);
static bool strCompareIC(const std::string &str1, const std::string &str2);
};

59
source/VPADInput.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#include <vpad/input.h>
#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{};
};

94
source/WiiUScreen.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "WiiUScreen.h"
#include "ScreenUtils.h"
#include <coreinit/memdefaultheap.h>
#include <cstdarg>
#include <cstdio>
void *WiiUScreen::sBufferTV = nullptr;
void *WiiUScreen::sBufferDRC = nullptr;
uint32_t WiiUScreen::sBufferSizeTV = 0;
uint32_t WiiUScreen::sBufferSizeDRC = 0;
bool WiiUScreen::sConsoleHasForeground = true;
uint32_t WiiUScreen::consoleColor = 0x041F60FF;
uint32_t WiiUScreen::consoleCursorY = 0;
uint32_t WiiUScreen::ProcCallbackAcquired(void *context) {
MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1);
if (sBufferSizeTV) {
sBufferTV = MEMAllocFromFrmHeapEx(heap, sBufferSizeTV, 4);
}
if (sBufferSizeDRC) {
sBufferDRC = MEMAllocFromFrmHeapEx(heap, sBufferSizeDRC, 4);
}
sConsoleHasForeground = true;
OSScreenSetBufferEx(SCREEN_TV, sBufferTV);
OSScreenSetBufferEx(SCREEN_DRC, sBufferDRC);
return 0;
}
uint32_t WiiUScreen::ProcCallbackReleased(void *context) {
MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM1);
MEMFreeByStateToFrmHeap(heap, CONSOLE_FRAME_HEAP_TAG);
sConsoleHasForeground = FALSE;
return 0;
}
bool WiiUScreen::Init() {
OSScreenInit();
sBufferSizeTV = OSScreenGetBufferSizeEx(SCREEN_TV);
sBufferSizeDRC = OSScreenGetBufferSizeEx(SCREEN_DRC);
WiiUScreen::ProcCallbackAcquired(nullptr);
OSScreenEnableEx(SCREEN_TV, 1);
OSScreenEnableEx(SCREEN_DRC, 1);
ScreenUtils::OSScreenClear(CONSOLE_SCREEN_BOTH, WiiUScreen::consoleColor);
ProcUIRegisterCallback(PROCUI_CALLBACK_ACQUIRE, WiiUScreen::ProcCallbackAcquired, nullptr, 100);
ProcUIRegisterCallback(PROCUI_CALLBACK_RELEASE, WiiUScreen::ProcCallbackReleased, nullptr, 100);
return true;
}
void WiiUScreen::DeInit() {
if (sConsoleHasForeground) {
OSScreenShutdown();
WiiUScreen::ProcCallbackReleased(nullptr);
}
}
void WiiUScreen::drawLinef(const char *fmt, ...) {
char *buf = (char *) MEMAllocFromDefaultHeapEx(PRINTF_BUFFER_LENGTH, 4);
va_list va;
if (!buf) {
return;
}
va_start(va, fmt);
vsnprintf(buf, PRINTF_BUFFER_LENGTH, fmt, va);
WiiUScreen::drawLine(buf);
MEMFreeToDefaultHeap(buf);
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);
}
void WiiUScreen::clearScreen() {
ScreenUtils::OSScreenClear(CONSOLE_SCREEN_BOTH, WiiUScreen::consoleColor);
consoleCursorY = 0;
}

53
source/WiiUScreen.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <whb/log.h>
#include <whb/log_console.h>
#include <coreinit/memheap.h>
#include <coreinit/cache.h>
#include <coreinit/memfrmheap.h>
#include <coreinit/memory.h>
#include <coreinit/screen.h>
#include <proc_ui/procui.h>
#include <cstring>
#include "utils/logger.h"
#define NUM_LINES (16)
#define LINE_LENGTH (128)
#define CONSOLE_FRAME_HEAP_TAG (0x000DECAF)
#define PRINTF_BUFFER_LENGTH 2048
#define DEBUG_CONSOLE_LOG(FMT, ARGS...)do { \
DEBUG_FUNCTION_LINE(FMT, ## ARGS); \
WiiUScreen::drawLinef(FMT, ## ARGS); \
} while (0)
class WiiUScreen {
public:
static uint32_t ProcCallbackAcquired(void *context);
static uint32_t ProcCallbackReleased(void *context);
static bool Init();
static void DeInit();
static void drawLinef(const char *fmt, ...);
static void drawLine(const char *fmt);
static void drawLine();
static void flipBuffers();
static void clearScreen();
private:
static void *sBufferTV, *sBufferDRC;
static uint32_t sBufferSizeTV, sBufferSizeDRC;
static bool sConsoleHasForeground;
static uint32_t consoleColor;
static uint32_t consoleCursorY;
};

117
source/common/common.cpp Normal file
View File

@ -0,0 +1,117 @@
#include "common.h"
systemXMLInformation systemXMLHashInformation[] = {
{WII_U_MENU_JAP, 0x0005001010040000L, "2645065A42D18D390C78543E3C4FE7E1D1957A63", "5E5C707E6DAF82393E93971BE98BE3B12204932A"},
{WII_U_MENU_USA, 0x0005001010040100L, "124562D41A02C7112DDD5F9A8F0EE5DF97E23471", "DC0F9941E99C629625419F444B5A5B177A67309F"},
{WII_U_MENU_EUR, 0x0005001010040200L, "F06041A4E5B3F899E748F1BAEB524DE058809F1D", "A0273C466DE15F33EC161BCD908B5BFE359FE6E0"},
{HEALTH_SAFETY_JPN, 0x000500101004E000L, "066D672824128713F0A7D156142A68B998080148", "2849DE91560F6667FE7415F89FC916BE3A27DE75"},
{HEALTH_SAFETY_USA, 0x000500101004E100L, "0EBCA1DFC0AB7A6A7FE8FB5EAF23179621B726A1", "83CF5B1CE0B64C51D15B1EFCAD659063790EB590"},
{HEALTH_SAFETY_EUR, 0x000500101004E200L, "DE46EC3E9B823ABA6CB0638D0C4CDEEF9C793BDD", "ED59630448EC6946F3E51618DA3681EC3A84D391"},
{MAX_SYSTEM_XML_DEFAULT_TITLE_ID, 0, {'\0'}, {'\0'}},
};
appInformation supportedApps[] = {
{0x000500101004E000L, "Health and Safety Information [JPN]", false, {'\0'}, "9D34DDD91604D781FDB0727AC75021833304964C", "F6EBF7BC8AE3AF3BB8A42E0CF3FDA051278AEB03", "2CF358E1F51932D305911A6836ED37DB0F94ABE4"},
{0x000500101004E100L, "Health and Safety Information [USA]", false, {'\0'}, "045734666A36C7EF0258A740855886EBDB20D59B", "F6EBF7BC8AE3AF3BB8A42E0CF3FDA051278AEB03", "C53C219FB8F777F9AB8F430D6BE4BC034D5638BA"},
{0x000500101004E200L, "Health and Safety Information [PAL]", false, {'\0'}, "130A76F8B36B36D43B88BBC74393D9AFD9CFD2A4", "F6EBF7BC8AE3AF3BB8A42E0CF3FDA051278AEB03", "4A29A60E5FBDAF410B7C22ECAEBDDBF29D1A874E"},
{0, nullptr, false, {'\0'}, {'\0'}, {'\0'}},
};
gList_t GameList[] = {
{0x0005001010040000L, "Wii U Menu [JPN]", MCP_REGION_JAPAN},
{0x0005001010040100L, "Wii U Menu [USA]", MCP_REGION_USA},
{0x0005001010040200L, "Wii U Menu [PAL]", MCP_REGION_EUROPE},
{0x000500101004E000L, "Health and Safety Information [JPN]", MCP_REGION_JAPAN},
{0x000500101004E100L, "Health and Safety Information [USA]", MCP_REGION_USA},
{0x000500101004E200L, "Health and Safety Information [PAL]", MCP_REGION_EUROPE},
{0x0005000010179A00L, "Kawashima: Motto Nou wo Kitaeru Otona no DS Training [JPN]", MCP_REGION_JAPAN},
{0x0005000010179B00L, "Brain Age: Train Your Brain in Minutes a Day! [USA]", MCP_REGION_USA},
{0x0005000010179C00L, "Dr. Kawashima's Brain Training [PAL]", MCP_REGION_EUROPE},
{0x0005000010179D00L, "Catch! Touch! Yoshi! [JPN]", MCP_REGION_JAPAN},
{0x0005000010179E00L, "Yoshi Touch & Go [USA]", MCP_REGION_USA},
{0x0005000010179F00L, "Yoshi Touch & Go [PAL]", MCP_REGION_EUROPE},
{0x0005000010195600L, "Mario Kart DS [JPN]", MCP_REGION_JAPAN},
{0x0005000010195700L, "Mario Kart DS [USA]", MCP_REGION_USA},
{0x0005000010195800L, "Mario Kart DS [PAL]", MCP_REGION_EUROPE},
{0x0005000010195900L, "New Super Mario Bros. [JPN]", MCP_REGION_JAPAN},
{0x0005000010195A00L, "New Super Mario Bros. [USA]", MCP_REGION_USA},
{0x0005000010195B00L, "New Super Mario Bros. [PAL]", MCP_REGION_EUROPE},
{0x0005000010198800L, "Yoshi's Island DS [JPN]", MCP_REGION_JAPAN},
{0x0005000010198900L, "Yoshi's Island DS [USA]", MCP_REGION_USA},
{0x0005000010198A00L, "Yoshi's Island DS [PAL]", MCP_REGION_EUROPE},
{0x0005000010198B00L, "Yawaraka Atama Juku [JPN]", MCP_REGION_JAPAN},
{0x0005000010198C00L, "Big Brain Academy [USA]", MCP_REGION_USA},
{0x0005000010198D00L, "Big Brain Academy [PAL]", MCP_REGION_EUROPE},
{0x00050000101A1E00L, "Sawaru: Made in Wario [JPN]", MCP_REGION_JAPAN},
{0x00050000101A1F00L, "WarioWare: Touched! [USA]", MCP_REGION_USA},
{0x00050000101A2000L, "WarioWare: Touched! [PAL]", MCP_REGION_EUROPE},
{0x00050000101A2100L, "Mario & Luigi RPG 2x2 [JPN]", MCP_REGION_JAPAN},
{0x00050000101A2200L, "Mario & Luigi: Partners in Time [USA]", MCP_REGION_USA},
{0x00050000101A2300L, "Mario & Luigi: Partners in Time [PAL]", MCP_REGION_EUROPE},
{0x00050000101A5200L, "Donkey Kong: Jungle Climber [JPN]", MCP_REGION_JAPAN},
{0x00050000101A5300L, "DK: Jungle Climber [USA]", MCP_REGION_USA},
{0x00050000101A5400L, "Donkey Kong: Jungle Climber [PAL]", MCP_REGION_EUROPE},
{0x00050000101A5500L, "Hoshi no Kirby: Sanjou! Dorocche Dan [JPN]", MCP_REGION_JAPAN},
{0x00050000101A5600L, "Kirby: Squeak Squad [USA]", MCP_REGION_USA},
{0x00050000101A5700L, "Kirby: Mouse Attack [PAL]", MCP_REGION_EUROPE},
{0x00050000101ABD00L, "Kaitou Wario the Seven [JPN]", MCP_REGION_JAPAN},
{0x00050000101ABE00L, "Wario: Master of Disguise [USA]", MCP_REGION_USA},
{0x00050000101ABF00L, "Wario: Master of Disguise [PAL]", MCP_REGION_EUROPE},
{0x00050000101AC000L, "Star Fox Command [JPN]", MCP_REGION_JAPAN},
{0x00050000101AC100L, "Star Fox Command [USA]", MCP_REGION_USA},
{0x00050000101AC200L, "Star Fox Command [PAL]", MCP_REGION_EUROPE},
{0x00050000101B8800L, "Touch! Kirby's Magic Paintbrush [JPN]", MCP_REGION_JAPAN},
{0x00050000101B8900L, "Kirby: Canvas Curse [USA]", MCP_REGION_USA},
{0x00050000101B8A00L, "Kirby: Power Paintbrush [PAL]", MCP_REGION_EUROPE},
{0x00050000101B8B00L, "Zelda no Densetsu: Daichi no Kiteki [JPN]", MCP_REGION_JAPAN},
{0x00050000101B8C00L, "The Legend of Zelda: Spirit Tracks [USA]", MCP_REGION_USA},
{0x00050000101B8D00L, "The Legend of Zelda: Spirit Tracks [PAL]", MCP_REGION_EUROPE},
{0x00050000101C3300L, "Super Mario 64 DS [JPN]", MCP_REGION_JAPAN},
{0x00050000101C3400L, "Super Mario 64 DS [USA]", MCP_REGION_USA},
{0x00050000101C3500L, "Super Mario 64 DS [PAL]", MCP_REGION_EUROPE},
{0x00050000101C3600L, "Zelda no Densetsu: Mugen no Sunadokei [JPN]", MCP_REGION_JAPAN},
{0x00050000101C3700L, "The Legend of Zelda: Phantom Hourglass [USA]", MCP_REGION_USA},
{0x00050000101C3800L, "The Legend of Zelda: Phantom Hourglass [PAL]", MCP_REGION_EUROPE},
{0x00050000101C8600L, "Atsumete! Kirby [JPN]", MCP_REGION_JAPAN},
{0x00050000101C8700L, "Kirby Mass Attack [USA]", MCP_REGION_USA},
{0x00050000101C8800L, "Kirby Mass Attack [PAL]", MCP_REGION_EUROPE},
{0x00050000101CC200L, "Pokemon Ranger [JPN]", MCP_REGION_JAPAN},
{0x00050000101CC300L, "Pokemon Ranger [USA]", MCP_REGION_USA},
{0x00050000101CC400L, "Pokemon Ranger [PAL]", MCP_REGION_EUROPE},
{0x00050000101D1F00L, "Oideyo Doubutsu no Mori [JPN]", MCP_REGION_JAPAN},
{0x00050000101D2000L, "Animal Crossing: Wild World [USA]", MCP_REGION_USA},
{0x00050000101D2100L, "Animal Crossing: Wild World [PAL]", MCP_REGION_EUROPE},
{0x00050000101E0C00L, "Pokemon Fushigi no Dungeon: Sora no Tankentai [JPN]", MCP_REGION_JAPAN},
{0x00050000101E0D00L, "Pokemon Mystery Dungeon: Explorers of Sky [USA]", MCP_REGION_USA},
{0x00050000101E0E00L, "Pokemon Mystery Dungeon: Explorers of Sky [PAL]", MCP_REGION_EUROPE},
{0x00050000101E0F00L, "Pokemon Ranger: Batonnage [JPN]", MCP_REGION_JAPAN},
{0x00050000101E1000L, "Pokemon Ranger: Shadows of Almia [USA]", MCP_REGION_USA},
{0x00050000101E1100L, "Pokemon Ranger: Shadows of Almia [PAL]", MCP_REGION_EUROPE},
{0x00050000101E6F00L, "Pokemon Ranger: Hikari no Kiseki [JPN]", MCP_REGION_JAPAN},
{0x00050000101E7000L, "Pokemon Ranger: Guardian Signs [USA]", MCP_REGION_USA},
{0x00050000101E7100L, "Pokemon Ranger: Guardian Signs [PAL]", MCP_REGION_EUROPE},
{0, "", MCP_REGION_JAPAN},
};

51
source/common/common.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <cstdint>
#include <coreinit/mcp.h>
#ifdef __cplusplus
extern "C" {
#endif
#define VERSION "v0.1"
enum SYSTEM_XML_DEFAULT_TITLE_ID {
WII_U_MENU_EUR,
WII_U_MENU_USA,
WII_U_MENU_JAP,
HEALTH_SAFETY_EUR,
HEALTH_SAFETY_USA,
HEALTH_SAFETY_JPN,
MAX_SYSTEM_XML_DEFAULT_TITLE_ID
};
typedef struct systemXMLInformation {
SYSTEM_XML_DEFAULT_TITLE_ID type;
uint64_t titleId;
char hash[41];
char hash2[41];
} systemXMLInformation;
typedef struct compatApps {
uint64_t titleId;
const char *appName;
bool onTheWiiU;
char path[255];
char fstHash[41];
char cosHash[41];
char tmdHash[41];
} appInformation;
typedef struct _gList_t {
uint64_t tid;
char name[64];
MCPRegion region;
} gList_t;
extern systemXMLInformation systemXMLHashInformation[];
extern appInformation supportedApps[];
extern _gList_t GameList[];
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,62 @@
#pragma once
#include <wut.h>
#define FSTHEADER_MAGIC "FST"
typedef struct WUT_PACKED FSTHeader {
char magic[3];
uint8_t version;
uint32_t blockSize;
uint32_t numberOfSections;
uint8_t hashDisabled;
WUT_PADDING_BYTES(32 - 13);
} FSTHeader;
WUT_CHECK_OFFSET(FSTHeader, 0x00, magic);
WUT_CHECK_OFFSET(FSTHeader, 0x03, version);
WUT_CHECK_OFFSET(FSTHeader, 0x04, blockSize);
WUT_CHECK_OFFSET(FSTHeader, 0x08, numberOfSections);
WUT_CHECK_OFFSET(FSTHeader, 0x0C, hashDisabled);
WUT_CHECK_SIZE(FSTHeader, 0x20);
typedef struct WUT_PACKED FSTSectionEntry {
uint32_t addressInVolumeBlocks;
uint32_t sizeInVolumeBlocks;
uint64_t ownerID;
uint32_t groupID;
uint8_t hashMode;
WUT_PADDING_BYTES(32 - 21);
} FSTSectionEntry;
WUT_CHECK_OFFSET(FSTSectionEntry, 0x00, addressInVolumeBlocks);
WUT_CHECK_OFFSET(FSTSectionEntry, 0x04, sizeInVolumeBlocks);
WUT_CHECK_OFFSET(FSTSectionEntry, 0x08, ownerID);
WUT_CHECK_OFFSET(FSTSectionEntry, 0x10, groupID);
WUT_CHECK_OFFSET(FSTSectionEntry, 0x14, hashMode);
WUT_CHECK_SIZE(FSTSectionEntry, 0x20);
typedef struct WUT_PACKED FSTNodeEntry {
uint8_t type;
char nameOffset[3]; // 24 bit int
union {
struct {
uint32_t parentEntryNumber;
uint32_t lastEntryNumber;
} directory;
struct {
uint32_t addressInBlocks;
uint32_t size;
} file;
};
uint16_t permission;
uint16_t sectionNumber;
} FSTNodeEntry;
WUT_CHECK_OFFSET(FSTNodeEntry, 0x00, type);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x01, nameOffset);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x04, directory.parentEntryNumber);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x08, directory.lastEntryNumber);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x04, file.addressInBlocks);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x08, file.size);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x0C, permission);
WUT_CHECK_OFFSET(FSTNodeEntry, 0x0E, sectionNumber);
WUT_CHECK_SIZE(FSTNodeEntry, 0x10);

173
source/fs/CFile.cpp Normal file
View File

@ -0,0 +1,173 @@
#include <stdarg.h>
#include <stdio.h>
#include <strings.h>
#include "CFile.hpp"
CFile::CFile() {
iFd = -1;
mem_file = NULL;
filesize = 0;
pos = 0;
}
CFile::CFile(const std::string &filepath, eOpenTypes mode) {
iFd = -1;
this->open(filepath, mode);
}
CFile::CFile(const uint8_t *mem, int32_t size) {
iFd = -1;
this->open(mem, size);
}
CFile::~CFile() {
this->close();
}
int32_t CFile::open(const std::string &filepath, eOpenTypes mode) {
this->close();
int32_t openMode = 0;
// This depend on the devoptab implementation.
// see https://github.com/devkitPro/wut/blob/master/libraries/wutdevoptab/devoptab_fs_open.c#L21 fpr reference
switch (mode) {
default:
case ReadOnly: // file must exist
openMode = O_RDONLY;
break;
case WriteOnly: // file will be created / zerod
openMode = O_TRUNC | O_CREAT | O_WRONLY;
break;
case ReadWrite: // file must exist
openMode = O_RDWR;
break;
case Append: // append to file, file will be created if missing. write only
openMode = O_CREAT | O_APPEND | O_WRONLY;
break;
}
//! Using fopen works only on the first launch as expected
//! on the second launch it causes issues because we don't overwrite
//! the .data sections which is needed for a normal application to re-init
//! this will be added with launching as RPX
iFd = ::open(filepath.c_str(), openMode);
if (iFd < 0)
return iFd;
filesize = ::lseek(iFd, 0, SEEK_END);
::lseek(iFd, 0, SEEK_SET);
return 0;
}
int32_t CFile::open(const uint8_t *mem, int32_t size) {
this->close();
mem_file = mem;
filesize = size;
return 0;
}
void CFile::close() {
if (iFd >= 0)
::close(iFd);
iFd = -1;
mem_file = NULL;
filesize = 0;
pos = 0;
}
int32_t CFile::read(uint8_t *ptr, size_t size) {
if (iFd >= 0) {
int32_t ret = ::read(iFd, ptr, size);
if (ret > 0)
pos += ret;
return ret;
}
int32_t readsize = size;
if (readsize > (int64_t) (filesize - pos))
readsize = filesize - pos;
if (readsize <= 0)
return readsize;
if (mem_file != NULL) {
memcpy(ptr, mem_file + pos, readsize);
pos += readsize;
return readsize;
}
return -1;
}
int32_t CFile::write(const uint8_t *ptr, size_t size) {
if (iFd >= 0) {
size_t done = 0;
while (done < size) {
int32_t ret = ::write(iFd, ptr, size - done);
if (ret <= 0)
return ret;
ptr += ret;
done += ret;
pos += ret;
}
return done;
}
return -1;
}
int32_t CFile::seek(long int offset, int32_t origin) {
int32_t ret = 0;
int64_t newPos = pos;
if (origin == SEEK_SET) {
newPos = offset;
} else if (origin == SEEK_CUR) {
newPos += offset;
} else if (origin == SEEK_END) {
newPos = filesize + offset;
}
if (newPos < 0) {
pos = 0;
} else {
pos = newPos;
}
if (iFd >= 0)
ret = ::lseek(iFd, pos, SEEK_SET);
if (mem_file != NULL) {
if (pos > filesize) {
pos = filesize;
}
}
return ret;
}
int32_t CFile::fwrite(const char *format, ...) {
char tmp[512];
tmp[0] = 0;
int32_t result = -1;
va_list va;
va_start(va, format);
if ((vsprintf(tmp, format, va) >= 0)) {
result = this->write((uint8_t *) tmp, strlen(tmp));
}
va_end(va);
return result;
}

71
source/fs/CFile.hpp Normal file
View File

@ -0,0 +1,71 @@
#ifndef CFILE_HPP_
#define CFILE_HPP_
#include <stdio.h>
#include <string>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <wut_types.h>
class CFile {
public:
enum eOpenTypes {
ReadOnly,
WriteOnly,
ReadWrite,
Append
};
CFile();
CFile(const std::string &filepath, eOpenTypes mode);
CFile(const uint8_t *memory, int32_t memsize);
virtual ~CFile();
int32_t open(const std::string &filepath, eOpenTypes mode);
int32_t open(const uint8_t *memory, int32_t memsize);
BOOL isOpen() const {
if (iFd >= 0)
return true;
if (mem_file)
return true;
return false;
}
void close();
int32_t read(uint8_t *ptr, size_t size);
int32_t write(const uint8_t *ptr, size_t size);
int32_t fwrite(const char *format, ...);
int32_t seek(long int offset, int32_t origin);
uint64_t tell() {
return pos;
};
uint64_t size() {
return filesize;
};
void rewind() {
this->seek(0, SEEK_SET);
};
protected:
int32_t iFd;
const uint8_t *mem_file;
uint64_t filesize;
uint64_t pos;
};
#endif

174
source/fs/FSUtils.cpp Normal file
View File

@ -0,0 +1,174 @@
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "FSUtils.h"
#include "CFile.hpp"
#include "utils/logger.h"
int32_t FSUtils::LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size) {
//! always initialze input
*inbuffer = nullptr;
if (size)
*size = 0;
int32_t iFd = open(filepath, O_RDONLY);
if (iFd < 0)
return -1;
uint32_t filesize = lseek(iFd, 0, SEEK_END);
lseek(iFd, 0, SEEK_SET);
auto *buffer = (uint8_t *) malloc(filesize);
if (buffer == nullptr) {
close(iFd);
return -2;
}
uint32_t blocksize = 0x4000;
uint32_t done = 0;
int32_t readBytes = 0;
while (done < filesize) {
if (done + blocksize > filesize) {
blocksize = filesize - done;
}
readBytes = read(iFd, buffer + done, blocksize);
if (readBytes <= 0)
break;
done += readBytes;
}
close(iFd);
if (done != filesize) {
free(buffer);
buffer = nullptr;
return -3;
}
*inbuffer = buffer;
//! sign is optional input
if (size) {
*size = filesize;
}
return filesize;
}
int32_t FSUtils::CheckFile(const char *filepath) {
if (!filepath)
return 0;
struct stat filestat;
char dirnoslash[strlen(filepath) + 2];
snprintf(dirnoslash, sizeof(dirnoslash), "%s", filepath);
while (dirnoslash[strlen(dirnoslash) - 1] == '/')
dirnoslash[strlen(dirnoslash) - 1] = '\0';
char *notRoot = strrchr(dirnoslash, '/');
if (!notRoot) {
strcat(dirnoslash, "/");
}
if (stat(dirnoslash, &filestat) == 0)
return 1;
return 0;
}
int32_t FSUtils::CreateSubfolder(const char *fullpath) {
if (!fullpath)
return 0;
int32_t result = 0;
char dirnoslash[strlen(fullpath) + 1];
strcpy(dirnoslash, fullpath);
int32_t pos = strlen(dirnoslash) - 1;
while (dirnoslash[pos] == '/') {
dirnoslash[pos] = '\0';
pos--;
}
if (CheckFile(dirnoslash)) {
return 1;
} else {
char parentpath[strlen(dirnoslash) + 2];
strcpy(parentpath, dirnoslash);
char *ptr = strrchr(parentpath, '/');
if (!ptr) {
//!Device root directory (must be with '/')
strcat(parentpath, "/");
struct stat filestat;
if (stat(parentpath, &filestat) == 0)
return 1;
return 0;
}
ptr++;
ptr[0] = '\0';
result = CreateSubfolder(parentpath);
}
if (!result)
return 0;
if (mkdir(dirnoslash, 0777) == -1) {
return 0;
}
return 1;
}
bool FSUtils::copyFile(const std::string &in, const std::string &out) {
// Using C++ buffers is **really** slow. Copying in 1023 byte chunks.
// Let's do it the old way.
size_t size;
int source = open(in.c_str(), O_RDONLY, 0);
int dest = open(out.c_str(), 0x602, 0644);
if (source < 0) {
return false;
}
if (dest < 0) {
close(source);
return false;
}
auto bufferSize = 1024 * 1024;
char *buf = (char *) malloc(bufferSize);
if (buf == NULL) {
return false;
}
while ((size = read(source, buf, bufferSize)) > 0) {
write(dest, buf, size);
}
free(buf);
close(source);
close(dest);
return true;
}
int32_t FSUtils::saveBufferToFile(const char *path, void *buffer, uint32_t size) {
CFile file(path, CFile::WriteOnly);
if (!file.isOpen()) {
return -1;
}
int32_t written = file.write((const uint8_t *) buffer, size);
file.close();
return written;
}

17
source/fs/FSUtils.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <wut_types.h>
#include <string>
class FSUtils {
public:
static int32_t LoadFileToMem(const char *filepath, uint8_t **inbuffer, uint32_t *size);
static int32_t CreateSubfolder(const char *fullpath);
static int32_t CheckFile(const char *filepath);
static bool copyFile(const std::string &in, const std::string &out);
static int32_t saveBufferToFile(const char *path, void *buffer, uint32_t size);
};

96
source/main.cpp Normal file
View File

@ -0,0 +1,96 @@
#include <coreinit/debug.h>
#include <whb/proc.h>
#include <whb/log.h>
#include <whb/log_udp.h>
#include <iosuhax.h>
#include <iosuhax_devoptab.h>
#include <string_view>
#include <vector>
#include "WiiUScreen.h"
#include "InstallerService.h"
#include "ApplicationState.h"
#include "VPADInput.h"
#include "../build/safe_payload.h"
constexpr bool strings_equal(char const *a, char const *b) {
return std::string_view(a) == b;
}
static_assert(strings_equal(RPX_HASH, "116cff322148216f38b1eaaf296d1cffc6a3a98f"), "Built with an untested root.rpx! Remove this check if you really know what you're doing.");
void initIOSUHax();
void deInitIOSUHax();
int sFSAFd = -1;
bool sIosuhaxMount = false;
int main_loop() {
DEBUG_FUNCTION_LINE("Creating state");
ApplicationState state;
VPadInput input;
if (sFSAFd < 0 || !sIosuhaxMount) {
state.setError(ApplicationState::eErrorState::ERROR_IOSUHAX_FAILED);
}
DEBUG_FUNCTION_LINE("Entering main loop");
while (WHBProcIsRunning()) {
input.update(1280, 720);
state.update(&input);
state.render();
}
return 0;
}
int main(int argc, char **argv) {
WHBLogUdpInit();
DEBUG_FUNCTION_LINE("Hello from Aroma Installer!");
WHBProcInit();
WiiUScreen::Init();
initIOSUHax();
main_loop();
deInitIOSUHax();
WiiUScreen::DeInit();
WHBProcShutdown();
return 0;
}
void initIOSUHax() {
sIosuhaxMount = false;
int res = IOSUHAX_Open(nullptr);
if (res < 0) {
DEBUG_FUNCTION_LINE("IOSUHAX_open failed");
OSFatal("IOSUHAX_open failed, please start this installer with an CFW");
} else {
sIosuhaxMount = true;
sFSAFd = IOSUHAX_FSA_Open();
if (sFSAFd < 0) {
DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open failed");
} else {
mount_fs("storage_slc_installer", sFSAFd, nullptr, "/vol/system");
mount_fs("storage_mlc_installer", sFSAFd, nullptr, "/vol/storage_mlc01");
}
DEBUG_FUNCTION_LINE("IOSUHAX done");
}
}
void deInitIOSUHax() {
if (sIosuhaxMount) {
unmount_fs("storage_slc_installer");
unmount_fs("storage_mlc_installer");
if (sFSAFd >= 0) {
IOSUHAX_FSA_Close(sFSAFd);
}
IOSUHAX_Close();
}
}

196
source/utils/TinySHA1.hpp Normal file
View File

@ -0,0 +1,196 @@
/*
*
* TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based
* on the implementation in boost::uuid::details.
*
* SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
*
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _TINY_SHA1_HPP_
#define _TINY_SHA1_HPP_
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>
namespace sha1
{
class SHA1
{
public:
typedef uint32_t digest32_t[5];
typedef uint8_t digest8_t[20];
inline static uint32_t LeftRotate(uint32_t value, size_t count) {
return (value << count) ^ (value >> (32-count));
}
SHA1(){ reset(); }
virtual ~SHA1() {}
SHA1(const SHA1& s) { *this = s; }
const SHA1& operator = (const SHA1& s) {
memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t));
memcpy(m_block, s.m_block, 64);
m_blockByteIndex = s.m_blockByteIndex;
m_byteCount = s.m_byteCount;
return *this;
}
SHA1& reset() {
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;
m_blockByteIndex = 0;
m_byteCount = 0;
return *this;
}
SHA1& processByte(uint8_t octet) {
this->m_block[this->m_blockByteIndex++] = octet;
++this->m_byteCount;
if(m_blockByteIndex == 64) {
this->m_blockByteIndex = 0;
processBlock();
}
return *this;
}
SHA1& processBlock(const void* const start, const void* const end) {
const uint8_t* begin = static_cast<const uint8_t*>(start);
const uint8_t* finish = static_cast<const uint8_t*>(end);
while(begin != finish) {
processByte(*begin);
begin++;
}
return *this;
}
SHA1& processBytes(const void* const data, size_t len) {
const uint8_t* block = static_cast<const uint8_t*>(data);
processBlock(block, block + len);
return *this;
}
const uint32_t* getDigest(digest32_t digest) {
size_t bitCount = this->m_byteCount * 8;
processByte(0x80);
if (this->m_blockByteIndex > 56) {
while (m_blockByteIndex != 0) {
processByte(0);
}
while (m_blockByteIndex < 56) {
processByte(0);
}
} else {
while (m_blockByteIndex < 56) {
processByte(0);
}
}
processByte(0);
processByte(0);
processByte(0);
processByte(0);
processByte( static_cast<unsigned char>((bitCount>>24) & 0xFF));
processByte( static_cast<unsigned char>((bitCount>>16) & 0xFF));
processByte( static_cast<unsigned char>((bitCount>>8 ) & 0xFF));
processByte( static_cast<unsigned char>((bitCount) & 0xFF));
memcpy(digest, m_digest, 5 * sizeof(uint32_t));
return digest;
}
const uint8_t* getDigestBytes(digest8_t digest) {
digest32_t d32;
getDigest(d32);
size_t di = 0;
digest[di++] = ((d32[0] >> 24) & 0xFF);
digest[di++] = ((d32[0] >> 16) & 0xFF);
digest[di++] = ((d32[0] >> 8) & 0xFF);
digest[di++] = ((d32[0]) & 0xFF);
digest[di++] = ((d32[1] >> 24) & 0xFF);
digest[di++] = ((d32[1] >> 16) & 0xFF);
digest[di++] = ((d32[1] >> 8) & 0xFF);
digest[di++] = ((d32[1]) & 0xFF);
digest[di++] = ((d32[2] >> 24) & 0xFF);
digest[di++] = ((d32[2] >> 16) & 0xFF);
digest[di++] = ((d32[2] >> 8) & 0xFF);
digest[di++] = ((d32[2]) & 0xFF);
digest[di++] = ((d32[3] >> 24) & 0xFF);
digest[di++] = ((d32[3] >> 16) & 0xFF);
digest[di++] = ((d32[3] >> 8) & 0xFF);
digest[di++] = ((d32[3]) & 0xFF);
digest[di++] = ((d32[4] >> 24) & 0xFF);
digest[di++] = ((d32[4] >> 16) & 0xFF);
digest[di++] = ((d32[4] >> 8) & 0xFF);
digest[di++] = ((d32[4]) & 0xFF);
return digest;
}
protected:
void processBlock() {
uint32_t w[80];
for (size_t i = 0; i < 16; i++) {
w[i] = (m_block[i*4 + 0] << 24);
w[i] |= (m_block[i*4 + 1] << 16);
w[i] |= (m_block[i*4 + 2] << 8);
w[i] |= (m_block[i*4 + 3]);
}
for (size_t i = 16; i < 80; i++) {
w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1);
}
uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];
for (std::size_t i=0; i<80; ++i) {
uint32_t f = 0;
uint32_t k = 0;
if (i<20) {
f = (b & c) | (~b & d);
k = 0x5A827999;
} else if (i<40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
} else if (i<60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
} else {
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = LeftRotate(b, 30);
b = a;
a = temp;
}
m_digest[0] += a;
m_digest[1] += b;
m_digest[2] += c;
m_digest[3] += d;
m_digest[4] += e;
}
private:
digest32_t m_digest;
uint8_t m_block[64];
size_t m_blockByteIndex;
size_t m_byteCount;
};
}
#endif

23
source/utils/logger.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <cstring>
#include <whb/log.h>
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,74 @@
/**
* pugixml parser - version 1.10
* --------------------------------------------------------
* Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
// #define PUGIXML_WCHAR_MODE
// Uncomment this to enable compact mode
// #define PUGIXML_COMPACT
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
#define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2019 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

12865
source/utils/pugixml.cpp Normal file

File diff suppressed because it is too large Load Diff

1477
source/utils/pugixml.hpp Normal file

File diff suppressed because it is too large Load Diff

64
source/utils/utils.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <string.h>
#include <stddef.h>
#include <whb/log.h>
#include <fs/FSUtils.h>
#include "logger.h"
#include "utils.h"
#include "TinySHA1.hpp"
// https://gist.github.com/ccbrown/9722406
void Utils::dumpHex(const void *data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
DEBUG_FUNCTION_LINE("0x%08X (0x0000): ", data);
for (i = 0; i < size; ++i) {
WHBLogWritef("%02X ", ((unsigned char *) data)[i]);
if (((unsigned char *) data)[i] >= ' ' && ((unsigned char *) data)[i] <= '~') {
ascii[i % 16] = ((unsigned char *) data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i + 1) % 8 == 0 || i + 1 == size) {
WHBLogWritef(" ");
if ((i + 1) % 16 == 0) {
WHBLogPrintf("| %s ", ascii);
if (i + 1 < size) {
DEBUG_FUNCTION_LINE("0x%08X (0x%04X); ", data + i + 1, i + 1);
}
} else if (i + 1 == size) {
ascii[(i + 1) % 16] = '\0';
if ((i + 1) % 16 <= 8) {
WHBLogWritef(" ");
}
for (j = (i + 1) % 16; j < 16; ++j) {
WHBLogWritef(" ");
}
WHBLogPrintf("| %s ", ascii);
}
}
}
}
std::string Utils::calculateSHA1(const char *buffer, size_t size) {
sha1::SHA1 s;
s.processBytes(buffer, size);
uint32_t digest[5];
s.getDigest(digest);
char tmp[48];
snprintf(tmp, 45, "%08X%08X%08X%08X%08X", digest[0], digest[1], digest[2], digest[3], digest[4]);
return tmp;
}
std::string Utils::hashFile(const std::string &path) {
uint8_t *data = NULL;
uint32_t size = 0;
FSUtils::LoadFileToMem(path.c_str(), &data, &size);
if (data == NULL) {
return calculateSHA1(NULL, 0);
}
std::string result = calculateSHA1(reinterpret_cast<const char *>(data), size);
free(data);
return result;
}

45
source/utils/utils.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <malloc.h>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
#define LIMIT(x, min, max) \
({ \
typeof( x ) _x = x; \
typeof( min ) _min = min; \
typeof( max ) _max = max; \
( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \
})
#define DegToRad(a) ( (a) * 0.01745329252f )
#define RadToDeg(a) ( (a) * 57.29577951f )
#define ALIGN4(x) (((x) + 3) & ~3)
#define ALIGN32(x) (((x) + 31) & ~31)
// those work only in powers of 2
#define ROUNDDOWN(val, align) ((val) & ~(align-1))
#define ROUNDUP(val, align) ROUNDDOWN(((val) + (align-1)), align)
#define le16(i) ((((uint16_t) ((i) & 0xFF)) << 8) | ((uint16_t) (((i) & 0xFF00) >> 8)))
#define le32(i) ((((uint32_t)le16((i) & 0xFFFF)) << 16) | ((uint32_t)le16(((i) & 0xFFFF0000) >> 16)))
#define le64(i) ((((uint64_t)le32((i) & 0xFFFFFFFFLL)) << 32) | ((uint64_t)le32(((i) & 0xFFFFFFFF00000000LL) >> 32)))
#ifdef __cplusplus
}
#endif
class Utils {
public:
static void dumpHex(const void *data, size_t size);
static std::string calculateSHA1(const char *buffer, size_t size);
static std::string hashFile(const std::string &path);
};