mirror of
https://github.com/wiiu-env/PayloadLoaderInstaller.git
synced 2024-11-13 05:35:06 +01:00
First commit
This commit is contained in:
commit
3dc8316f17
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
build/
|
||||
*.rpx
|
||||
*.elf
|
||||
CMakeLists.txt
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "payload"]
|
||||
path = payload
|
||||
url = https://github.com/wiiu-env/PayloadFromRPX
|
161
Makefile
Normal file
161
Makefile
Normal 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
1
payload
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c4c14496afc12b3f4f5ae6e9c335e379a212d1c2
|
411
source/ApplicationState.cpp
Normal file
411
source/ApplicationState.cpp
Normal 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
80
source/ApplicationState.h
Normal 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
5
source/Input.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Maschell on 15.10.2020.
|
||||
//
|
||||
|
||||
#include "Input.h"
|
60
source/Input.h
Normal file
60
source/Input.h
Normal 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
620
source/InstallerService.cpp
Normal 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
82
source/InstallerService.h
Normal 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
48
source/ScreenUtils.cpp
Normal 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
55
source/ScreenUtils.h
Normal 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
234
source/StringTools.cpp
Normal 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
95
source/StringTools.h
Normal 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
59
source/VPADInput.h
Normal 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
94
source/WiiUScreen.cpp
Normal 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
53
source/WiiUScreen.h
Normal 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
117
source/common/common.cpp
Normal 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
51
source/common/common.h
Normal 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
|
62
source/common/fst_structs.h
Normal file
62
source/common/fst_structs.h
Normal 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
173
source/fs/CFile.cpp
Normal 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
71
source/fs/CFile.hpp
Normal 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
174
source/fs/FSUtils.cpp
Normal 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
17
source/fs/FSUtils.h
Normal 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
96
source/main.cpp
Normal 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
196
source/utils/TinySHA1.hpp
Normal 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
23
source/utils/logger.h
Normal 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
|
74
source/utils/pugiconfig.hpp
Normal file
74
source/utils/pugiconfig.hpp
Normal 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
12865
source/utils/pugixml.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1477
source/utils/pugixml.hpp
Normal file
1477
source/utils/pugixml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
64
source/utils/utils.cpp
Normal file
64
source/utils/utils.cpp
Normal 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
45
source/utils/utils.h
Normal 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);
|
||||
};
|
Loading…
Reference in New Issue
Block a user