From 648c0d0fd7ee76474665f3a2f3fd81965546bd38 Mon Sep 17 00:00:00 2001 From: Maschell Date: Sat, 9 Oct 2021 00:58:55 +0200 Subject: [PATCH] First commit --- .github/workflows/ci.yml | 66 ++ .github/workflows/pr.yml | 24 + .gitignore | 6 + Dockerfile | 6 + Makefile | 152 ++++ README.md | 27 + source/ApplicationState.cpp | 861 ++++++++++++++++++ source/ApplicationState.h | 138 +++ source/WUD/DefaultNUSDataProcessor.cpp | 60 ++ source/WUD/DefaultNUSDataProcessor.h | 35 + source/WUD/DiscReader.cpp | 177 ++++ source/WUD/DiscReader.h | 42 + source/WUD/DiscReaderDiscDrive.cpp | 97 ++ source/WUD/DiscReaderDiscDrive.h | 34 + source/WUD/NUSDataProcessor.h | 16 + source/WUD/NUSDataProvider.h | 41 + source/WUD/NUSDataProviderWUD.cpp | 114 +++ source/WUD/NUSDataProviderWUD.h | 50 + source/WUD/NUSDecryption.cpp | 26 + source/WUD/NUSDecryption.h | 28 + source/WUD/NUSTitle.cpp | 83 ++ source/WUD/NUSTitle.h | 48 + source/WUD/Ticket.cpp | 33 + source/WUD/Ticket.h | 29 + .../WUD/content/WiiUContentsInformation.cpp | 37 + source/WUD/content/WiiUContentsInformation.h | 35 + source/WUD/content/WiiUDiscContentsHeader.cpp | 44 + source/WUD/content/WiiUDiscContentsHeader.h | 36 + .../content/partitions/WiiUDataPartition.cpp | 43 + .../content/partitions/WiiUDataPartition.h | 46 + .../content/partitions/WiiUGMPartition.cpp | 50 + .../WUD/content/partitions/WiiUGMPartition.h | 47 + .../WUD/content/partitions/WiiUPartition.cpp | 79 ++ source/WUD/content/partitions/WiiUPartition.h | 51 ++ .../WUD/content/partitions/WiiUPartitions.cpp | 181 ++++ .../WUD/content/partitions/WiiUPartitions.h | 42 + .../partitions/volumes/H3HashArray.cpp | 37 + .../content/partitions/volumes/H3HashArray.h | 31 + .../partitions/volumes/VolumeHeader.cpp | 94 ++ .../content/partitions/volumes/VolumeHeader.h | 50 + source/WUD/entities/FST/FST.cpp | 47 + source/WUD/entities/FST/FST.h | 39 + source/WUD/entities/FST/header/FSTHeader.cpp | 30 + source/WUD/entities/FST/header/FSTHeader.h | 35 + .../entities/FST/nodeentry/DirectoryEntry.cpp | 82 ++ .../entities/FST/nodeentry/DirectoryEntry.h | 46 + .../WUD/entities/FST/nodeentry/FileEntry.cpp | 56 ++ source/WUD/entities/FST/nodeentry/FileEntry.h | 41 + .../entities/FST/nodeentry/NodeEntries.cpp | 57 ++ .../WUD/entities/FST/nodeentry/NodeEntries.h | 40 + .../WUD/entities/FST/nodeentry/NodeEntry.cpp | 83 ++ source/WUD/entities/FST/nodeentry/NodeEntry.h | 65 ++ .../entities/FST/nodeentry/NodeEntryParam.h | 30 + .../WUD/entities/FST/nodeentry/RootEntry.cpp | 45 + source/WUD/entities/FST/nodeentry/RootEntry.h | 36 + .../FST/sectionentry/SectionEntries.cpp | 40 + .../FST/sectionentry/SectionEntries.h | 41 + .../FST/sectionentry/SectionEntry.cpp | 32 + .../entities/FST/sectionentry/SectionEntry.h | 37 + .../entities/FST/stringtable/StringEntry.cpp | 27 + .../entities/FST/stringtable/StringEntry.h | 33 + .../entities/FST/stringtable/StringTable.cpp | 71 ++ .../entities/FST/stringtable/StringTable.h | 47 + source/WUD/entities/TMD/Content.cpp | 28 + source/WUD/entities/TMD/Content.h | 32 + source/WUD/entities/TMD/TitleMetaData.cpp | 42 + source/WUD/entities/TMD/TitleMetaData.h | 35 + source/WUD/header/WiiUDiscHeader.cpp | 40 + source/WUD/header/WiiUDiscHeader.h | 37 + source/WUD/header/WiiUDiscID.cpp | 43 + source/WUD/header/WiiUDiscID.h | 32 + source/WUD/header/WiiUManufactorDiscID.cpp | 26 + source/WUD/header/WiiUManufactorDiscID.h | 30 + source/common/common.cpp | 3 + source/common/common.h | 5 + source/fs/CFile.cpp | 173 ++++ source/fs/CFile.hpp | 71 ++ source/fs/FSUtils.cpp | 174 ++++ source/fs/FSUtils.h | 17 + source/input/Input.h | 60 ++ source/input/VPADInput.h | 59 ++ source/main.cpp | 108 +++ source/utils/FSTUtils.cpp | 61 ++ source/utils/FSTUtils.h | 16 + source/utils/ScreenUtils.cpp | 48 + source/utils/ScreenUtils.h | 55 ++ source/utils/StringTools.cpp | 234 +++++ source/utils/StringTools.h | 95 ++ source/utils/TinySHA1.hpp | 196 ++++ source/utils/WiiUScreen.cpp | 94 ++ source/utils/WiiUScreen.h | 53 ++ source/utils/blocksize/AddressInBlocks.h | 36 + source/utils/blocksize/AddressInDiscBlocks.h | 41 + .../utils/blocksize/AddressInVolumeBlocks.h | 28 + source/utils/blocksize/BlockSize.h | 30 + source/utils/blocksize/DiscBlockSize.h | 29 + source/utils/blocksize/SectionAddress.h | 27 + source/utils/blocksize/SectionBlockSize.h | 29 + source/utils/blocksize/SizeInBlocks.h | 37 + source/utils/blocksize/SizeInVolumeBlocks.h | 14 + source/utils/blocksize/VolumeBlockSize.h | 29 + source/utils/logger.h | 23 + source/utils/rijndael.c | 394 ++++++++ source/utils/rijndael.h | 8 + source/utils/utils.cpp | 64 ++ source/utils/utils.h | 45 + 106 files changed, 6857 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 source/ApplicationState.cpp create mode 100644 source/ApplicationState.h create mode 100644 source/WUD/DefaultNUSDataProcessor.cpp create mode 100644 source/WUD/DefaultNUSDataProcessor.h create mode 100644 source/WUD/DiscReader.cpp create mode 100644 source/WUD/DiscReader.h create mode 100644 source/WUD/DiscReaderDiscDrive.cpp create mode 100644 source/WUD/DiscReaderDiscDrive.h create mode 100644 source/WUD/NUSDataProcessor.h create mode 100644 source/WUD/NUSDataProvider.h create mode 100644 source/WUD/NUSDataProviderWUD.cpp create mode 100644 source/WUD/NUSDataProviderWUD.h create mode 100644 source/WUD/NUSDecryption.cpp create mode 100644 source/WUD/NUSDecryption.h create mode 100644 source/WUD/NUSTitle.cpp create mode 100644 source/WUD/NUSTitle.h create mode 100644 source/WUD/Ticket.cpp create mode 100644 source/WUD/Ticket.h create mode 100644 source/WUD/content/WiiUContentsInformation.cpp create mode 100644 source/WUD/content/WiiUContentsInformation.h create mode 100644 source/WUD/content/WiiUDiscContentsHeader.cpp create mode 100644 source/WUD/content/WiiUDiscContentsHeader.h create mode 100644 source/WUD/content/partitions/WiiUDataPartition.cpp create mode 100644 source/WUD/content/partitions/WiiUDataPartition.h create mode 100644 source/WUD/content/partitions/WiiUGMPartition.cpp create mode 100644 source/WUD/content/partitions/WiiUGMPartition.h create mode 100644 source/WUD/content/partitions/WiiUPartition.cpp create mode 100644 source/WUD/content/partitions/WiiUPartition.h create mode 100644 source/WUD/content/partitions/WiiUPartitions.cpp create mode 100644 source/WUD/content/partitions/WiiUPartitions.h create mode 100644 source/WUD/content/partitions/volumes/H3HashArray.cpp create mode 100644 source/WUD/content/partitions/volumes/H3HashArray.h create mode 100644 source/WUD/content/partitions/volumes/VolumeHeader.cpp create mode 100644 source/WUD/content/partitions/volumes/VolumeHeader.h create mode 100644 source/WUD/entities/FST/FST.cpp create mode 100644 source/WUD/entities/FST/FST.h create mode 100644 source/WUD/entities/FST/header/FSTHeader.cpp create mode 100644 source/WUD/entities/FST/header/FSTHeader.h create mode 100644 source/WUD/entities/FST/nodeentry/DirectoryEntry.cpp create mode 100644 source/WUD/entities/FST/nodeentry/DirectoryEntry.h create mode 100644 source/WUD/entities/FST/nodeentry/FileEntry.cpp create mode 100644 source/WUD/entities/FST/nodeentry/FileEntry.h create mode 100644 source/WUD/entities/FST/nodeentry/NodeEntries.cpp create mode 100644 source/WUD/entities/FST/nodeentry/NodeEntries.h create mode 100644 source/WUD/entities/FST/nodeentry/NodeEntry.cpp create mode 100644 source/WUD/entities/FST/nodeentry/NodeEntry.h create mode 100644 source/WUD/entities/FST/nodeentry/NodeEntryParam.h create mode 100644 source/WUD/entities/FST/nodeentry/RootEntry.cpp create mode 100644 source/WUD/entities/FST/nodeentry/RootEntry.h create mode 100644 source/WUD/entities/FST/sectionentry/SectionEntries.cpp create mode 100644 source/WUD/entities/FST/sectionentry/SectionEntries.h create mode 100644 source/WUD/entities/FST/sectionentry/SectionEntry.cpp create mode 100644 source/WUD/entities/FST/sectionentry/SectionEntry.h create mode 100644 source/WUD/entities/FST/stringtable/StringEntry.cpp create mode 100644 source/WUD/entities/FST/stringtable/StringEntry.h create mode 100644 source/WUD/entities/FST/stringtable/StringTable.cpp create mode 100644 source/WUD/entities/FST/stringtable/StringTable.h create mode 100644 source/WUD/entities/TMD/Content.cpp create mode 100644 source/WUD/entities/TMD/Content.h create mode 100644 source/WUD/entities/TMD/TitleMetaData.cpp create mode 100644 source/WUD/entities/TMD/TitleMetaData.h create mode 100644 source/WUD/header/WiiUDiscHeader.cpp create mode 100644 source/WUD/header/WiiUDiscHeader.h create mode 100644 source/WUD/header/WiiUDiscID.cpp create mode 100644 source/WUD/header/WiiUDiscID.h create mode 100644 source/WUD/header/WiiUManufactorDiscID.cpp create mode 100644 source/WUD/header/WiiUManufactorDiscID.h create mode 100644 source/common/common.cpp create mode 100644 source/common/common.h create mode 100644 source/fs/CFile.cpp create mode 100644 source/fs/CFile.hpp create mode 100644 source/fs/FSUtils.cpp create mode 100644 source/fs/FSUtils.h create mode 100644 source/input/Input.h create mode 100644 source/input/VPADInput.h create mode 100644 source/main.cpp create mode 100644 source/utils/FSTUtils.cpp create mode 100644 source/utils/FSTUtils.h create mode 100644 source/utils/ScreenUtils.cpp create mode 100644 source/utils/ScreenUtils.h create mode 100644 source/utils/StringTools.cpp create mode 100644 source/utils/StringTools.h create mode 100644 source/utils/TinySHA1.hpp create mode 100644 source/utils/WiiUScreen.cpp create mode 100644 source/utils/WiiUScreen.h create mode 100644 source/utils/blocksize/AddressInBlocks.h create mode 100644 source/utils/blocksize/AddressInDiscBlocks.h create mode 100644 source/utils/blocksize/AddressInVolumeBlocks.h create mode 100644 source/utils/blocksize/BlockSize.h create mode 100644 source/utils/blocksize/DiscBlockSize.h create mode 100644 source/utils/blocksize/SectionAddress.h create mode 100644 source/utils/blocksize/SectionBlockSize.h create mode 100644 source/utils/blocksize/SizeInBlocks.h create mode 100644 source/utils/blocksize/SizeInVolumeBlocks.h create mode 100644 source/utils/blocksize/VolumeBlockSize.h create mode 100644 source/utils/logger.h create mode 100644 source/utils/rijndael.c create mode 100644 source/utils/rijndael.h create mode 100644 source/utils/utils.cpp create mode 100644 source/utils/utils.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cb98bc5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: CI-Release + +on: + push: + branches: + - main + +jobs: + + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Checkout submodules using a PAT + run: | + git config --file .gitmodules --get-regexp url | while read url; do + git config --file=.gitmodules $(echo "$url" | sed -E "s/git@github.com:|https:\/\/github.com\//https:\/\/${{ secrets.CI_PAT }}:${{ secrets.CI_PAT }}@github.com\//") + done + git submodule sync + git submodule update --init --recursive + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.rpx" + deploy-binary: + needs: build-binary + runs-on: ubuntu-18.04 + steps: + - name: Get environment variables + id: get_repository_name + run: | + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV + echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV + - uses: actions/download-artifact@master + with: + name: binary + path: wiiu + - name: zip artifact + run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip wiiu + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + draft: false + prerelease: true + body: | + Not a stable release: + ${{ github.event.head_commit.message }} + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_content_type: application/unknown \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..8e28a5f --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,24 @@ +name: CI-PR + +on: [pull_request] + +jobs: + build-binary: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Checkout submodules using a PAT + run: | + git config --file .gitmodules --get-regexp url | while read url; do + git config --file=.gitmodules $(echo "$url" | sed -E "s/git@github.com:|https:\/\/github.com\//https:\/\/${{ secrets.CI_PAT }}:${{ secrets.CI_PAT }}@github.com\//") + done + git submodule sync + git submodule update --init --recursive + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.rpx" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d99ab92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +CMakeLists.txt +.idea/ +build/ +cmake-build-debug/ +*.elf +*.rpx diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2be9978 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM wiiuenv/devkitppc:20210920 + +COPY --from=wiiuenv/libiosuhax:20211008 /artifacts $DEVKITPRO +COPY --from=wiiuenv/libntfs:20201210 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c1f787d --- /dev/null +++ b/Makefile @@ -0,0 +1,152 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/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 := wudump +BUILD := build +SOURCES := source \ + source/fs \ + source/input \ + source/common \ + source/utils \ + source/utils/blocksize \ + source/WUD \ + source/WUD/content \ + source/WUD/content/partitions \ + source/WUD/content/partitions/volumes \ + source/WUD/header \ + source/WUD/entities/FST \ + source/WUD/entities/FST/header \ + source/WUD/entities/FST/nodeentry \ + source/WUD/entities/FST/sectionentry \ + source/WUD/entities/FST/stringtable \ + source/WUD/entities/TMD + +DATA := data +INCLUDES := include source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=gnu++20 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) + +LIBS := -lwut -lntfs -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) +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 $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).rpx + +$(OUTPUT).rpx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc1d837 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Wudump + +RPX version of [wudump](https://github.com/FIX94/wudump). + +Supports dumping as [WUX](https://gbatemp.net/threads/wii-u-image-wud-compression-tool.397901/) and WUD, but only to NTFS formatted USB drives and without dumping the `game.key`. + +## Dependencies + +- [wut](https://github.com/decaf-emu/wut) +- [libiosuhax](https://github.com/wiiu-env/libiosuhax) +- [libfat](https://github.com/wiiu-env/libfat) +- [libntfs](https://github.com/wiiu-env/libntfs) + +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t wudump-builder + +# make +docker run -it --rm -v ${PWD}:/project wudump-builder make + +# make clean +docker run -it --rm -v ${PWD}:/project wudump-builder make clean +``` \ No newline at end of file diff --git a/source/ApplicationState.cpp b/source/ApplicationState.cpp new file mode 100644 index 0000000..d3adcfc --- /dev/null +++ b/source/ApplicationState.cpp @@ -0,0 +1,861 @@ +#include "ApplicationState.h" +#include "utils/WiiUScreen.h" +#include "utils/ScreenUtils.h" +#include "common/common.h" +#include "utils/utils.h" +#include "utils/StringTools.h" +#include "utils/rijndael.h" +#include "fs/FSUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern ntfs_md *ntfs_mounts; +extern int ntfs_mount_count; + + +unsigned int swap_uint32(unsigned int val) { + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); + return (val << 16) | (val >> 16); +} + +unsigned long long swap_uint64(unsigned long long val) { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); + return (val << 32) | (val >> 32); +} + +/* + * Hash function used to create a hash of each sector + * The hashes are then compared to find duplicate sectors + */ +void calculateHash256(unsigned char *data, unsigned int length, unsigned char *hashOut) { + // cheap and simple hash implementation + // you can replace this part with your favorite hash method + memset(hashOut, 0x00, 32); + for (unsigned int i = 0; i < length; i++) { + hashOut[i % 32] ^= data[i]; + hashOut[(i + 7) % 32] += data[i]; + } +} + +ApplicationState::ApplicationState() : log("fs:/vol/external01/wudump.log", CFile::WriteOnly) { + this->log.fwrite("Started wudump\n"); + this->state = STATE_WELCOME_SCREEN; +} + +void ApplicationState::printHeader() { + WiiUScreen::drawLine("Wudump"); + WiiUScreen::drawLine("=================="); + WiiUScreen::drawLine(""); +} + +void ApplicationState::render() { + WiiUScreen::clearScreen(); + + if (this->state == STATE_ERROR) { + WiiUScreen::drawLine(); + WiiUScreen::drawLinef("Error: %s", ErrorMessage().c_str()); + WiiUScreen::drawLinef("Description: %s", ErrorDescription().c_str()); + WiiUScreen::drawLine(); + WiiUScreen::drawLine("Press A to return to the Wii U Menu."); + } else if (this->state == STATE_WELCOME_SCREEN) { + WiiUScreen::drawLine("Welcome to Wudump"); + WiiUScreen::drawLine("Press A to dump the currently inserted Disc"); + WiiUScreen::drawLine(""); + if (this->selectedOption == 0) { + WiiUScreen::drawLine("> Dump as WUX Dump as WUD Dump as .app Exit"); + } else if (this->selectedOption == 1) { + WiiUScreen::drawLine(" Dump as WUX > Dump as WUD Dump as .app Exit"); + } else if (this->selectedOption == 2) { + WiiUScreen::drawLine(" Dump as WUX Dump as WUD > Dump as .app Exit"); + } else if (this->selectedOption == 3) { + WiiUScreen::drawLine(" Dump as WUX Dump as WUD Dump as .app > Exit"); + } + } else if (this->state == STATE_CHOOSE_TARGET) { + printHeader(); + WiiUScreen::drawLine("Please choose your target:"); + std::vector options; + int32_t targetCount = 0; + if (this->dumpFormat == DUMP_AS_APP) { + options.emplace_back("SD"); + targetCount++; + } + for (int i = 0; i < ntfs_mount_count; i++) { + options.emplace_back(ntfs_mounts[i].name); + targetCount++; + } + + std::string curLine = ""; + if (this->selectedOption == 0) { + curLine = "> Back\t"; + } else { + curLine = " Back\t"; + } + + if (targetCount == 0) { + WiiUScreen::drawLine("Please insert a NTFS formatted USB drive and restart wudump\n"); + } else { + for (int32_t i = 0; i < targetCount; i++) { + if (this->selectedOption - 1 == i) { + curLine += "> " + options[i]; + } else { + curLine += " " + options[i]; + } + curLine += "\t"; + } + } + WiiUScreen::drawLine(curLine.c_str()); + } else if (this->state == STATE_OPEN_ODD1) { + WiiUScreen::drawLine("Open /dev/odd01"); + } else if (this->state == STATE_READ_DISC_INFO) { + WiiUScreen::drawLine("Read disc information"); + } else if (this->state == STATE_READ_DISC_INFO_DONE) { + WiiUScreen::drawLinef("Dumping: %s", this->discId); + } else if (this->state == STATE_DUMP_TICKET) { + WiiUScreen::drawLinef("Dumping game.key"); + } else if (this->state == STATE_DUMP_DISC_START || this->state == STATE_DUMP_DISC || this->state == STATE_WAIT_USER_ERROR_CONFIRM) { + WiiUScreen::drawLinef("Dumping: %s", this->discId); + + float percent = this->currentSector / (WUD_FILE_SIZE / READ_SECTOR_SIZE * 1.0f) * 100.0f; + WiiUScreen::drawLinef("Progress: %0.2f MiB / %5.2f MiB (%2.1f %%)", this->currentSector * (READ_SECTOR_SIZE / 1024.0f / 1024.0f), WUD_FILE_SIZE / 1024.0f / 1024.0f, percent); + if (doWUX) { + WiiUScreen::drawLinef("Written %0.2f MiB. Compression ratio 1:%0.2f", this->hashMap.size() * (READ_SECTOR_SIZE / 1024.0f / 1024.0f), + 1.0f / (this->hashMap.size() / (float) this->currentSector)); + } + + if (this->readResult < 0 || this->oddFd < 0) { + WiiUScreen::drawLine(); + + if (this->oddFd < 0) { + WiiUScreen::drawLine("Failed to open disc, try again."); + } else { + WiiUScreen::drawLinef("Error: Failed to read sector - Error %d", this->readResult); + } + WiiUScreen::drawLine(); + WiiUScreen::drawLine("Press A to skip this sector (will be replaced by 0's)"); + WiiUScreen::drawLine("Press B to try again"); + } else { + OSTime curTime = OSGetTime(); + float remaining = (WUD_FILE_SIZE - (READ_SECTOR_SIZE * this->currentSector)) / 1024.0f / 1024.0f; + float curSpeed = READ_SECTOR_SIZE * ((this->readSectors / 1000.0f) / OSTicksToMilliseconds(curTime - startTime)); + int32_t remainingSec = remaining / curSpeed; + int32_t minutes = (remainingSec / 60) % 60; + int32_t seconds = remainingSec % 60; + int32_t hours = remainingSec / 3600; + + WiiUScreen::drawLinef("Speed: %.2f MiB/s ETA: %02dh %02dm %02ds", curSpeed, remaining, hours, minutes, seconds); + } + + WiiUScreen::drawLine(); + if (!this->skippedSectors.empty()) { + WiiUScreen::drawLinef("Skipped dumping %d sectors", this->skippedSectors.size()); + } + } else if (this->state == STATE_DUMP_DISC_DONE) { + if (!flushWriteCache()) { + setError(ERROR_WRITE_FAILED); + } + WiiUScreen::drawLinef("Dumping done! Press A to continue"); + } else if (this->state == STATE_DUMP_APP_FILES_DONE) { + WiiUScreen::drawLinef("Dumping done! Press A to continue"); + } + 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, 4); + if (entrySelected(input)) { + if (this->selectedOption == 0) { + this->retryCount = 10; + this->state = STATE_CHOOSE_TARGET; + this->dumpFormat = DUMP_AS_WUX; + } else if (this->selectedOption == 1) { + this->retryCount = 10; + this->state = STATE_CHOOSE_TARGET; + this->dumpFormat = DUMP_AS_WUD; + } else if (this->selectedOption == 2) { + this->retryCount = 10; + this->state = STATE_CHOOSE_TARGET; + this->dumpFormat = DUMP_AS_APP; + } else { + SYSLaunchMenu(); + } + this->selectedOption = 0; + return; + } + } else if (this->state == STATE_CHOOSE_TARGET) { + WiiUScreen::drawLine("Please choose your target"); + std::vector options; + uint32_t targetCount = 0; + + if (this->dumpFormat == DUMP_AS_APP) { + options.emplace_back("fs:/vol/external01/"); + targetCount++; + } + if (ntfs_mount_count > 0) { + + for (int i = 0; i < ntfs_mount_count; i++) { + options.emplace_back(std::string(ntfs_mounts[i].name) + ":/"); + targetCount++; + } + } + + proccessMenuNavigation(input, targetCount + 1); + if (entrySelected(input)) { + if (this->selectedOption == 0) { + this->state = STATE_WELCOME_SCREEN; + } else if (targetCount > 0) { + target = options[selectedOption - 1]; + this->state = STATE_OPEN_ODD1; + } + this->selectedOption = 0; + } + } else if (this->state == STATE_OPEN_ODD1) { + if (this->readSectors > 0) { + auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd)); + if (ret >= 0) { + // continue! + this->state = STATE_DUMP_DISC; + } else { + this->oddFd = -1; + this->state = STATE_WAIT_USER_ERROR_CONFIRM; + } + return; + } + DEBUG_FUNCTION_LINE("STATE_OPEN_ODD1"); + if (this->retryCount-- <= 0) { + this->setError(ERROR_OPEN_ODD1); + return; + } + auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &(this->oddFd)); + if (ret >= 0) { + if (this->sectorBuf == nullptr) { + this->sectorBuf = (void *) memalign(0x100, this->sectorBufSize); + if (this->sectorBuf == nullptr) { + this->setError(ERROR_MALLOC_FAILED); + return; + } + } + DEBUG_FUNCTION_LINE("Opened /dev/odd01 %d", this->oddFd); + this->state = STATE_READ_DISC_INFO; + } + } else if (this->state == STATE_READ_DISC_INFO) { + DEBUG_FUNCTION_LINE("STATE_READ_DISC_INFO"); + if (IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 0, this->oddFd) >= 0) { + this->discId[10] = '\0'; + memcpy(this->discId, sectorBuf, 10); + if (this->discId[0] == 0) { + setError(ERROR_NO_DISC_ID); + return; + } + this->state = STATE_READ_DISC_INFO_DONE; + return; + } + + this->setError(ERROR_READ_FIRST_SECTOR); + return; + } else if (this->state == STATE_READ_DISC_INFO_DONE) { + DEBUG_FUNCTION_LINE("STATE_READ_DISC_INFO_DONE"); + this->state = STATE_DUMP_TICKET; + } else if (this->state == STATE_DUMP_TICKET) { + DEBUG_FUNCTION_LINE("STATE_DUMP_TICKET"); + + auto res = IOSUHAX_FSA_RawRead(gFSAfd, this->sectorBuf, READ_SECTOR_SIZE, 1, 3, this->oddFd); + uint8_t discKey[16]; + bool hasDiscKey = false; + if (res >= 0) { + if (((uint32_t *) this->sectorBuf)[0] != 0xCCA6E67B) { + uint8_t iv[16]; + memset(iv, 0, 16); + + auto odm_handle = IOS_Open("/dev/odm", IOS_OPEN_READ); + if (odm_handle >= 0) { + uint32_t io_buffer[0x20 / 4]; + // disc encryption key, only works with patched IOSU + io_buffer[0] = 3; + if (IOS_Ioctl(odm_handle, 0x06, io_buffer, 0x14, io_buffer, 0x20) == 0) { + memcpy(discKey, io_buffer, 16); + hasDiscKey = true; + } + IOS_Close(odm_handle); + } + } + } + + if (hasDiscKey) { + if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", target.c_str(), discId))) { + setError(ERROR_WRITE_FAILED); + return; + } + if (!FSUtils::saveBufferToFile(StringTools::fmt("%swudump/%s/game.key", target.c_str(), discId), discKey, 16)) { + setError(ERROR_WRITE_FAILED); + return; + } + } + if (this->dumpFormat == DUMP_AS_WUX || this->dumpFormat == DUMP_AS_WUD) { + if (this->dumpFormat == DUMP_AS_WUX) { + this->doWUX = true; + } + this->state = STATE_DUMP_DISC_START; + } else { + this->state = STATE_DUMP_APP_FILES; + } + } else if (this->state == STATE_DUMP_APP_FILES) { + ApplicationState::dumpAppFiles(); + if (this->state != STATE_ERROR) { + this->state = STATE_DUMP_APP_FILES_DONE; + } + } else if (this->state == STATE_DUMP_APP_FILES_DONE) { + if (entrySelected(input)) { + this->state = STATE_WELCOME_SCREEN; + if (this->oddFd >= 0) { + IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd); + this->oddFd = -1; + } + this->currentSector = 0; + this->readSectors = 0; + } + } else if (this->state == STATE_DUMP_DISC_START) { + ApplicationState::clearWriteCache(); + DEBUG_FUNCTION_LINE("STATE_DUMP_DISC_START"); + if (!FSUtils::CreateSubfolder(StringTools::fmt("%swudump/%s", target.c_str(), discId))) { + setError(ERROR_WRITE_FAILED); + return; + } + this->fileHandle = new CFile(StringTools::fmt("%swudump/%s/game.%s", target.c_str(), discId, doWUX ? "wux" : "wud"), CFile::WriteOnly); + + this->totalSectorCount = WUD_FILE_SIZE / SECTOR_SIZE; + + if (!this->fileHandle->isOpen()) { + DEBUG_FUNCTION_LINE("Failed to open file"); + this->setError(ERROR_FILE_OPEN_FAILED); + return; + } + + if (doWUX) { + wuxHeader_t wuxHeader = {0}; + wuxHeader.magic0 = WUX_MAGIC_0; + wuxHeader.magic1 = WUX_MAGIC_1; + wuxHeader.sectorSize = swap_uint32(SECTOR_SIZE); + wuxHeader.uncompressedSize = swap_uint64(WUD_FILE_SIZE); + wuxHeader.flags = 0; + + DEBUG_FUNCTION_LINE("Write header"); + this->fileHandle->write((uint8_t *) &wuxHeader, sizeof(wuxHeader_t)); + this->sectorTableStart = this->fileHandle->tell(); + + this->sectorIndexTable = (void *) malloc(totalSectorCount * 4); + if (sectorIndexTable == nullptr) { + this->setError(ERROR_MALLOC_FAILED); + return; + } + memset(this->sectorIndexTable, 0, totalSectorCount * 4); + + DEBUG_FUNCTION_LINE("Write empty sectorIndexTable"); + this->fileHandle->write((uint8_t *) this->sectorIndexTable, totalSectorCount * 4); + + DEBUG_FUNCTION_LINE("Get sector table end"); + this->sectorTableEnd = this->fileHandle->tell(); + uint64_t tableEnd = this->sectorTableEnd; + + this->sectorTableEnd += SECTOR_SIZE - 1; + this->sectorTableEnd -= (this->sectorTableEnd % SECTOR_SIZE); + + uint64_t padding = this->sectorTableEnd - tableEnd; + auto *paddingData = (uint8_t *) malloc(padding); + memset(paddingData, 0, padding); + this->fileHandle->write(reinterpret_cast(paddingData), padding); + free(paddingData); + this->hashMap.clear(); + } + + this->writeBufferSize = READ_SECTOR_SIZE * WRITE_BUFFER_NUM_SECTORS; + this->writeBuffer = (void *) memalign(0x1000, this->writeBufferSize); + if (this->writeBuffer == nullptr) { + this->setError(ERROR_MALLOC_FAILED); + return; + } + this->writeBufferPos = 0; + + this->startTime = OSGetTime(); + + this->state = STATE_DUMP_DISC; + this->currentSector = 0; + this->retryCount = 10; + this->selectedOption = 0; + this->readSectors = 0; + } else if (this->state == STATE_DUMP_DISC) { + //DEBUG_FUNCTION_LINE("STATE_DUMP_DISC"); + int32_t numSectors = this->currentSector + READ_NUM_SECTORS > this->totalSectorCount ? this->totalSectorCount - this->currentSector : READ_NUM_SECTORS; + if ((this->readResult = IOSUHAX_FSA_RawRead(gFSAfd, sectorBuf, READ_SECTOR_SIZE, numSectors, this->currentSector, this->oddFd)) >= 0) { + if (!writeDataToFile(this->sectorBuf, numSectors)) { + this->setError(ERROR_WRITE_FAILED); + return; + } + //DEBUG_FUNCTION_LINE("Read done %lld %lld", this->currentSector, this->totalSectorCount); + this->retryCount = 10; + if (this->currentSector >= this->totalSectorCount) { + this->state = STATE_DUMP_DISC_DONE; + + if (this->fileHandle->isOpen()) { + if (!this->flushWriteCache()) { + this->setError(ERROR_WRITE_FAILED); + return; + } + if (doWUX) { + this->writeSectorIndexTable(); + } + this->fileHandle->close(); + } + } + } else { + this->state = STATE_WAIT_USER_ERROR_CONFIRM; + if (this->oddFd >= 0) { + IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd); + this->oddFd = -1; + } + return; + } + } else if (this->state == STATE_WAIT_USER_ERROR_CONFIRM) { + if (this->autoSkip) { + if (this->oddFd >= 0) { + IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd); + this->oddFd = -1; + } + } + if (this->autoSkip || (input->data.buttons_d & Input::BUTTON_A)) { + this->log.fwrite("Skipped sector %d : 0x%ll016X-0x%ll016X, filled with 0's\n", this->currentSector, this->currentSector * READ_SECTOR_SIZE, (this->currentSector + 1) * READ_SECTOR_SIZE); + this->state = STATE_OPEN_ODD1; + this->skippedSectors.push_back(this->currentSector); + // We can't use seek because we may have cached values. + + if (this->emptySector == nullptr) { + this->emptySector = memalign(0x100, READ_SECTOR_SIZE); + if (this->emptySector == nullptr) { + this->setError(ERROR_MALLOC_FAILED); + return; + } + } + + if (!this->writeCached(reinterpret_cast(emptySector), READ_SECTOR_SIZE)) { + this->setError(ERROR_WRITE_FAILED); + return; + } + + this->currentSector += 1; + this->readResult = 0; + } else if (input->data.buttons_d & Input::BUTTON_B) { + this->state = STATE_OPEN_ODD1; + this->readResult = 0; + } else if (input->data.buttons_d & Input::BUTTON_Y) { + this->autoSkip = true; + } + } else if (this->state == STATE_DUMP_DISC_DONE) { + if (entrySelected(input)) { + if (this->oddFd >= 0) { + IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd); + this->oddFd = -1; + } + this->state = STATE_WELCOME_SCREEN; + this->selectedOption = 0; + this->currentSector = 0; + this->readSectors = 0; + this->writtenSector = 0; + return; + } + } +} + +std::string ApplicationState::ErrorMessage() { + if (this->error == ERROR_NONE) { + return "NONE"; + } else if (this->error == ERROR_IOSUHAX_FAILED) { + return "ERROR_IOSUHAX_FAILED"; + } else if (this->error == ERROR_MALLOC_FAILED) { + return "ERROR_MALLOC_FAILED"; + } else if (this->error == ERROR_FILE_OPEN_FAILED) { + return "ERROR_FILE_OPEN_FAILED"; + } else if (this->error == ERROR_NO_DISC_ID) { + return "ERROR_NO_DISC_ID"; + } + DEBUG_FUNCTION_LINE("Error: %d", this->error); + return "UNKNOWN_ERROR"; +} + +std::string ApplicationState::ErrorDescription() { + if (this->error == ERROR_NONE) { + return "-"; + } else if (this->error == ERROR_IOSUHAX_FAILED) { + return "Failed to init IOSUHAX."; + } else if (this->error == ERROR_MALLOC_FAILED) { + return "Failed to allocate data."; + } else if (this->error == ERROR_FILE_OPEN_FAILED) { + return "Failed to create file"; + } else if (this->error == ERROR_NO_DISC_ID) { + return "Failed to get the disc id"; + } + DEBUG_FUNCTION_LINE("Error: %d", this->error); + return "UNKNOWN_ERROR"; +} + +void ApplicationState::setError(eErrorState err) { + this->state = STATE_ERROR; + this->error = err; + //OSEnableHomeButtonMenu(true); +} + +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; +} + +ApplicationState::~ApplicationState() { + this->log.close(); + if (this->fileHandle->isOpen()) { + if (!this->flushWriteCache()) { + + } + if (doWUX) { + this->writeSectorIndexTable(); + } + this->fileHandle->close(); + } + if (this->emptySector != nullptr) { + free(this->emptySector); + this->emptySector = nullptr; + } + if (this->writeBuffer != nullptr) { + free(this->writeBuffer); + this->writeBuffer = nullptr; + } + if (this->sectorIndexTable != nullptr) { + free(this->sectorIndexTable); + this->sectorIndexTable = nullptr; + } + if (this->sectorBuf != nullptr) { + free(this->sectorBuf); + this->sectorBuf = nullptr; + } + if (this->oddFd >= 0) { + IOSUHAX_FSA_RawClose(gFSAfd, this->oddFd); + this->oddFd = -1; + } +} + +bool ApplicationState::writeDataToFile(void *buffer, int numberOfSectors) { + if (!doWUX) { + if (!writeCached(reinterpret_cast(buffer), numberOfSectors * READ_SECTOR_SIZE)) { + return false; + } + this->currentSector += numberOfSectors; + this->readSectors += numberOfSectors; + } else { + char hashOut[32]; + for (int i = 0; i < numberOfSectors; i++) { + uint32_t addr = ((uint32_t) buffer) + (i * READ_SECTOR_SIZE); + calculateHash256(reinterpret_cast(addr), READ_SECTOR_SIZE, reinterpret_cast(hashOut)); + char tmp[34]; + auto *test = (uint32_t *) hashOut; + snprintf(tmp, 33, "%08X%08X%08X%08X", test[0], test[1], test[2], test[3]); + std::string hash(tmp); + + uint32_t *indexTable = (uint32_t *) this->sectorIndexTable; + + auto it = hashMap.find(hash); + if (it != hashMap.end()) { + indexTable[this->currentSector] = swap_uint32(this->hashMap[hash]); + } else { + indexTable[this->currentSector] = swap_uint32(this->writtenSector); + hashMap[hash] = this->writtenSector; + if (this->fileHandle->isOpen()) { + if (!writeCached(addr, READ_SECTOR_SIZE)) { + return false; + } + } + this->writtenSector++; + } + this->currentSector++; + this->readSectors++; + } + } + return true; +} + +bool ApplicationState::writeCached(uint32_t addr, uint32_t writeSize) { + // DEBUG_FUNCTION_LINE("Lest write %d bytes", writeSize); + + if (writeSize == this->writeBufferSize) { + if (!this->flushWriteCache()) { + return false; + } + int32_t res = this->fileHandle->write(reinterpret_cast(addr), writeSize); + return res >= 0; + } + + uint32_t toWrite = writeSize; + if (toWrite == 0) { + return true; + } + + uint32_t written = 0; + + do { + uint32_t curWrite = toWrite; + + if (this->writeBufferPos + curWrite > this->writeBufferSize) { + curWrite = this->writeBufferSize - this->writeBufferPos; + } + // DEBUG_FUNCTION_LINE("Copy from %08X into %08X, size %08X, %d",(addr + written),((uint32_t) this->writeBuffer) + this->writeBufferPos, curWrite, this->writeBufferPos/READ_SECTOR_SIZE); + OSBlockMove((void *) (((uint32_t) this->writeBuffer) + this->writeBufferPos), (void *) (addr + written), curWrite, 1); + this->writeBufferPos += curWrite; + + if (this->writeBufferPos == this->writeBufferSize) { + if (!flushWriteCache()) { + return false; + } + } + + toWrite -= curWrite; + written += curWrite; + } while (toWrite > 0); + return true; +} + +bool ApplicationState::flushWriteCache() { + if (this->writeBufferPos > 0) { + int32_t res = this->fileHandle->write(static_cast(this->writeBuffer), this->writeBufferPos); + if (res < 0) { + return false; + } + this->writeBufferPos = 0; + } + return true; +} + +void ApplicationState::clearWriteCache() { + this->writeBufferPos = 0; +} + +void ApplicationState::writeSectorIndexTable() { + if (this->fileHandle->isOpen() && doWUX) { + this->fileHandle->seek(this->sectorTableStart, SEEK_SET); + this->fileHandle->write((uint8_t *) this->sectorIndexTable, totalSectorCount * 4); + } +} + +void ApplicationState::printDumpState(const char *fmt, ...) { + WiiUScreen::clearScreen(); + ApplicationState::printHeader(); + 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); + ApplicationState::printFooter(); + WiiUScreen::flipBuffers(); +} + +void ApplicationState::dumpAppFiles() { + uint8_t opt[0x400]; + IOSUHAX_read_otp(opt, 0x400); + uint8_t cKey[0x10]; + memcpy(cKey, opt + 0xE0, 0x10); + + DEBUG_FUNCTION_LINE("Reading Partitions"); + + printDumpState("Reading Partitions..."); + + auto discReader = new DiscReaderDiscDrive(); + if (!discReader->IsReady()) { + DEBUG_FUNCTION_LINE("!IsReady"); + this->setError(ERROR_OPEN_ODD1); + delete discReader; + return; + } + DEBUG_FUNCTION_LINE("Read DiscHeader"); + auto *discHeader = new WiiUDiscHeader(discReader, 0); + bool forceExit = false; + for (auto &partition: discHeader->wiiUContentsInformation->partitions->partitions) { + auto gmPartition = dynamic_cast(partition); + if (gmPartition != nullptr) { + auto *nusTitle = NUSTitle::loadTitleFromGMPartition(gmPartition, discReader, cKey); + if (nusTitle == nullptr) { + DEBUG_FUNCTION_LINE("nusTitle was null"); + continue; + } + auto *dataProvider = nusTitle->dataProcessor->getDataProvider(); + + uint64_t partitionSize = 0; + uint64_t partitionSizeWritten = 0; + for (auto &content: nusTitle->tmd->contentList) { + partitionSize += ROUNDUP(content->encryptedFileSize, 16); + } + + auto partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f, + partitionSize / 1024.0f / 1024.0f); + printDumpState("%s", partitionDumpInfo.c_str()); + + char buffer[512]; + snprintf(buffer, 500, "%swudump/%s/%s", target.c_str(), this->discId, gmPartition->getVolumeId().c_str()); + FSUtils::CreateSubfolder(buffer); + + uint8_t *wBuffer = nullptr; + uint32_t wBufferLen = 0; + if (dataProvider->getRawTMD(&wBuffer, &wBufferLen)) { + std::string fileName = std::string(buffer).append("/").append(WUD_TMD_FILENAME); + printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_TMD_FILENAME); + FSUtils::saveBufferToFile(fileName.c_str(), wBuffer, wBufferLen); + free(wBuffer); + } + + if (dataProvider->getRawTicket(&wBuffer, &wBufferLen)) { + std::string fileName = std::string(buffer).append("/").append(WUD_TICKET_FILENAME); + printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_TICKET_FILENAME); + FSUtils::saveBufferToFile(fileName.c_str(), wBuffer, wBufferLen); + free(wBuffer); + } + if (dataProvider->getRawCert(&wBuffer, &wBufferLen)) { + std::string fileName = std::string(buffer).append("/").append(WUD_TICKET_FILENAME); + printDumpState("%s\nSaving %s", partitionDumpInfo.c_str(), WUD_CERT_FILENAME); + FSUtils::saveBufferToFile(fileName.c_str(), wBuffer, wBufferLen); + free(wBuffer); + } + + auto contentCount = nusTitle->tmd->contentList.size(); + auto contentI = 1; + + for (auto &content: nusTitle->tmd->contentList) { + char bufApp[32]; + snprintf(bufApp, 31, "%08X.app", content->ID); + std::string appFileName = std::string(buffer) + "/" + bufApp; + + partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f, + partitionSize / 1024.0f / 1024.0f); + auto contentDumpInfo = StringTools::strfmt("Saving %s (Content %02d/%02d)\n", bufApp, contentI, contentCount); + + printDumpState("%s\n%s", partitionDumpInfo.c_str(), contentDumpInfo.c_str()); + + uint32_t bufferSize = READ_NUM_SECTORS * READ_SECTOR_SIZE * 2; + + auto *readBuffer = (uint8_t *) malloc(bufferSize); + if (readBuffer == nullptr) { + DEBUG_FUNCTION_LINE("Failed to alloc buffer"); + continue; + } + + CFile file(appFileName, CFile::WriteOnly); + if (!file.isOpen()) { + free(readBuffer); + continue; + } + uint32_t readSoFar = 0; + uint64_t curOffset = 0; + uint32_t size = ROUNDUP(content->encryptedFileSize, 16); + OSTime startTimeApp = OSGetTime(); + do { + if (!WHBProcIsRunning()) { + forceExit = true; + break; + } + startTimeApp = OSGetTime(); + WiiUScreen::clearScreen(); + + uint32_t toRead = size - readSoFar; + if (toRead > bufferSize) { + toRead = bufferSize; + } + dataProvider->readRawContent(content, readBuffer, curOffset, toRead); + if (file.write((const uint8_t *) readBuffer, toRead) != (int32_t) toRead) { + DEBUG_FUNCTION_LINE("Failed to write"); + break; + } + + OSTime curTime = OSGetTime(); + auto curSpeed = (float) toRead / (float) OSTicksToMilliseconds(curTime - startTimeApp); + + readSoFar += toRead; + curOffset += toRead; + + partitionSizeWritten += toRead; + + partitionDumpInfo = StringTools::strfmt("Partition: %s\n\tProgress: %.2f MiB / %.2f MiB\n", partition->getVolumeId().c_str(), partitionSizeWritten / 1024.0f / 1024.0f, + partitionSize / 1024.0f / 1024.0f); + printDumpState("%s\n%s\tProgress: %.2f MiB / %.2f MiB (%0.2f%%)\n\tSpeed: %0.2f MiB/s", partitionDumpInfo.c_str(), contentDumpInfo.c_str(), readSoFar / 1024.0f / 1024.0f, + size / 1024.0f / 1024.0f, ((readSoFar * 1.0f) / size) * 100.0f, curSpeed / 1024.0f); + } while (readSoFar < size); + + file.close(); + + if (forceExit) { + break; + } + + uint8_t *h3Data = nullptr; + uint32_t h3Length = 0; + if (dataProvider->getContentH3Hash(content, &h3Data, &h3Length)) { + char bufh3[32]; + snprintf(bufh3, 31, "%08X.h3", content->ID); + std::string h3FileName = std::string(buffer) + "/" + bufh3; + printDumpState("%s\n%s", partitionDumpInfo.c_str(), contentDumpInfo.c_str()); + FSUtils::saveBufferToFile(h3FileName.c_str(), h3Data, h3Length); + } + contentI++; + } + + delete nusTitle; + + if (forceExit) { + exit(0); + break; + } + } + } + delete discHeader; + delete discReader; +} diff --git a/source/ApplicationState.h b/source/ApplicationState.h new file mode 100644 index 0000000..e4ecc3f --- /dev/null +++ b/source/ApplicationState.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "input/Input.h" +#include "fs/CFile.hpp" + +#define SECTOR_SIZE 0x8000 +#define READ_SECTOR_SIZE SECTOR_SIZE +#define READ_NUM_SECTORS 128 +#define WRITE_BUFFER_NUM_SECTORS 128 +#define WUD_FILE_SIZE 0x5D3A00000L + + +typedef struct { + unsigned int magic0; + unsigned int magic1; + unsigned int sectorSize; + unsigned long long uncompressedSize; + unsigned int flags; +} wuxHeader_t; + +#define WUX_MAGIC_0 0x57555830 +#define WUX_MAGIC_1 swap_uint32(0x1099d02e) + +class ApplicationState { +public: + + enum eDumpTargetFormat { + DUMP_AS_WUX, + DUMP_AS_WUD, + DUMP_AS_APP, + }; + + + enum eErrorState { + ERROR_NONE, + ERROR_IOSUHAX_FAILED, + ERROR_OPEN_ODD1, + ERROR_READ_FIRST_SECTOR, + ERROR_FILE_OPEN_FAILED, + ERROR_MALLOC_FAILED, + ERROR_NO_DISC_ID, + ERROR_SECTOR_SIZE, + ERROR_MAGIC_NUMBER_WRONG, + ERROR_WRITE_FAILED, + }; + + enum eGameState { + STATE_ERROR, + STATE_WELCOME_SCREEN, + STATE_CHOOSE_TARGET, + STATE_OPEN_ODD1, + STATE_DUMP_APP_FILES, + STATE_DUMP_APP_FILES_DONE, + STATE_READ_DISC_INFO, + STATE_READ_DISC_INFO_DONE, + STATE_DUMP_TICKET, + STATE_DUMP_DISC_START, + STATE_DUMP_DISC_DONE, + STATE_WAIT_USER_ERROR_CONFIRM, + STATE_DUMP_DISC, + }; + + ApplicationState(); + + ~ApplicationState(); + + void setError(eErrorState error); + + void render(); + + void update(Input *input); + + std::string ErrorMessage(); + + std::string ErrorDescription(); + + int selectedOption; + + static void printFooter(); + + void proccessMenuNavigation(Input *input, int maxOptionValue); + + static bool entrySelected(Input *input); + +private: + static void printHeader(); + + CFile log; + eGameState state; + eDumpTargetFormat dumpFormat; + eErrorState error = ERROR_NONE; + std::string target = "fs:/vol/external01/"; + int oddFd = -1; + int retryCount = 0; + void *sectorBuf = nullptr; + int sectorBufSize = READ_NUM_SECTORS * READ_SECTOR_SIZE; + char discId[11]; + uint64_t currentSector = 0; + std::vector skippedSectors; + int readResult = 0; + + [[nodiscard]] bool writeDataToFile(void* buffer, int numberOfSection); + + uint64_t totalSectorCount = 0; + + std::map hashMap; + CFile *fileHandle; + OSTime startTime; + void *writeBuffer = nullptr; + uint32_t writeBufferPos = 0; + uint32_t writeBufferSize = 0; + + [[nodiscard]] bool writeCached(uint32_t addr, uint32_t writeSize); + void clearWriteCache(); + + [[nodiscard]] bool flushWriteCache(); + + uint32_t readSectors = 0; + uint64_t sectorTableStart = 0; + void *sectorIndexTable = nullptr; + uint64_t sectorTableEnd = 0; + + void writeSectorIndexTable(); + + void *emptySector = nullptr; + bool doWUX = false; + uint32_t writtenSector = 0; + bool autoSkip = false; + + void dumpAppFiles(); + + static void printDumpState(const char *fmt, ...); +}; \ No newline at end of file diff --git a/source/WUD/DefaultNUSDataProcessor.cpp b/source/WUD/DefaultNUSDataProcessor.cpp new file mode 100644 index 0000000..8f61974 --- /dev/null +++ b/source/WUD/DefaultNUSDataProcessor.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "DefaultNUSDataProcessor.h" + +bool DefaultNUSDataProcessor::readPlainDecryptedContent(Content *pContent, uint8_t **data, uint32_t *length) { + if (pContent == nullptr || data == nullptr || length == nullptr) { + return false; + } + + if ((pContent->type & 0x0002) == 0x0002) { + DEBUG_FUNCTION_LINE("Hashed content not supported yet"); + return false; + } + + *length = ROUNDUP(pContent->encryptedFileSize, 16); + *data = (uint8_t *) malloc(*length); + if (*data == nullptr) { + return false; + } + + auto *inData = (uint8_t *) malloc(*length); + + if (!dataProvider->readRawContent(pContent, inData, 0, *length)) { + free(*data); + free(inData); + return false; + } + + uint8_t IV[16]; + memset(IV, 0, 16); + uint16_t content_index = pContent->index; + memcpy(IV, &content_index, 2); + + nusDecryption->decryptData(IV, inData, *data, *length); + free(inData); + return true; +} + +NUSDataProvider *DefaultNUSDataProcessor::getDataProvider() { + return dataProvider; +} + +DefaultNUSDataProcessor::DefaultNUSDataProcessor(NUSDataProvider *pDataProvider, NUSDecryption *pNUSDecryption) { + dataProvider = pDataProvider; + nusDecryption = pNUSDecryption; +} diff --git a/source/WUD/DefaultNUSDataProcessor.h b/source/WUD/DefaultNUSDataProcessor.h new file mode 100644 index 0000000..2b43888 --- /dev/null +++ b/source/WUD/DefaultNUSDataProcessor.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "NUSDataProcessor.h" + +class DefaultNUSDataProcessor : public NUSDataProcessor { +public: + DefaultNUSDataProcessor(NUSDataProvider *pDataProvider, NUSDecryption *pNUSDecryption); + + ~DefaultNUSDataProcessor() override = default; + + bool readPlainDecryptedContent(Content *pContent, uint8_t **data, uint32_t *length) override; + + NUSDataProvider* getDataProvider() override; + +private: + NUSDataProvider *dataProvider; + NUSDecryption *nusDecryption; +}; diff --git a/source/WUD/DiscReader.cpp b/source/WUD/DiscReader.cpp new file mode 100644 index 0000000..c4a3d6c --- /dev/null +++ b/source/WUD/DiscReader.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include "DiscReader.h" + +bool DiscReader::readDecryptedChunk(uint64_t readOffset, uint8_t *out_buffer, uint8_t *key, uint8_t *IV) const { + int CHUNK_SIZE = 0x10000; + + uint32_t sectorOffset = readOffset / READ_SECTOR_SIZE; + + auto *encryptedBuffer = (uint8_t *) malloc(CHUNK_SIZE); + + if (encryptedBuffer == nullptr) { + DEBUG_FUNCTION_LINE("Failed to alloc buffer"); + return false; + } + + bool result = false; + if (readEncryptedSector(encryptedBuffer, 2, sectorOffset)) { + aes_set_key((uint8_t *) key); + aes_decrypt((uint8_t *) IV, (uint8_t *) encryptedBuffer, out_buffer, CHUNK_SIZE); + // Update IV + memcpy(IV, &encryptedBuffer[CHUNK_SIZE - 16], 16); + result = true; + } + + free(encryptedBuffer); + return result; +} + +bool DiscReader::readDecrypted(uint8_t *out_buffer, uint64_t clusterOffset, uint64_t fileOffset, uint32_t size, uint8_t *key, uint8_t *IV, bool useFixedIV) const { + uint8_t backupIV[0x10]; + memset(backupIV, 0, 16); + uint8_t *usedIV = backupIV; + if (useFixedIV) { + usedIV = IV; + if (IV == nullptr) { + usedIV = backupIV; + } + } + + int BLOCK_SIZE = 0x10000; + + uint32_t usedSize = size; + uint64_t usedFileOffset = fileOffset; + auto *buffer = (uint8_t *) malloc(BLOCK_SIZE); + if (buffer == nullptr) { + return false; + } + + uint32_t maxCopySize; + uint32_t copySize; + + uint64_t readOffset; + + uint32_t totalread = 0; + + bool result = true; + + do { + uint64_t totalOffset = (clusterOffset + usedFileOffset); + uint64_t blockNumber = (totalOffset / BLOCK_SIZE); + uint64_t blockOffset = (totalOffset % BLOCK_SIZE); + + readOffset = (blockNumber * BLOCK_SIZE); + if (!useFixedIV) { + memset(usedIV, 0, 16); + uint64_t ivTemp = usedFileOffset >> 16; + memcpy(usedIV + 8, &ivTemp, 8); + } + + if (!readDecryptedChunk(readOffset, buffer, key, usedIV)) { + result = false; + break; + } + maxCopySize = BLOCK_SIZE - blockOffset; + copySize = (usedSize > maxCopySize) ? maxCopySize : usedSize; + + memcpy(out_buffer + totalread, buffer + blockOffset, copySize); + + totalread += copySize; + + // update counters + usedSize -= copySize; + usedFileOffset += copySize; + } while (totalread < size); + + free(buffer); + + return result; +} + +bool DiscReader::readEncryptedAligned(uint8_t *buf, uint64_t offset_in_sector, uint32_t size) { + auto full_block_count = size / SECTOR_SIZE; + if (full_block_count > 0) { + if (!readEncryptedSector(buf, full_block_count, offset_in_sector)) { + return false; + } + } + + auto remainingSize = size - (full_block_count * SECTOR_SIZE); + if (remainingSize > 0) { + auto newOffset = offset_in_sector + full_block_count; + if (!readEncryptedSector(sector_buf, 1, newOffset)) { + return false; + } + memcpy(buf + (full_block_count * SECTOR_SIZE), sector_buf, remainingSize); + } + + return true; +} + +bool DiscReader::readEncrypted(uint8_t *buf, uint64_t offset, uint32_t size) { + if (size == 0) { + return true; + } + uint32_t missingFromPrevSector = offset % SECTOR_SIZE; + auto curOffset = offset; + uint32_t offsetInBuf = 0; + uint32_t totalRead = 0; + if (missingFromPrevSector > 0) { + auto offset_in_sectors = offset / SECTOR_SIZE; + if (!readEncryptedSector(sector_buf, 1, offset_in_sectors)) { + return false; + } + uint32_t toCopy = SECTOR_SIZE - missingFromPrevSector; + if (toCopy > size) { + toCopy = size; + } + memcpy(buf, sector_buf + missingFromPrevSector, toCopy); + totalRead += toCopy; + curOffset += missingFromPrevSector; + offsetInBuf += missingFromPrevSector; + + } + + if (totalRead >= size) { + return true; + } + + if (curOffset % SECTOR_SIZE == 0) { + if (!readEncryptedAligned(buf + offsetInBuf, offset / SECTOR_SIZE, size)) { + return false; + } + } else { + OSFatal("Failed to read encrypted"); + } + + return true; +} + +DiscReader::DiscReader() { + this->sector_buf = (uint8_t *) malloc(READ_SECTOR_SIZE); +} + +DiscReader::~DiscReader() { + free(this->sector_buf); +} diff --git a/source/WUD/DiscReader.h b/source/WUD/DiscReader.h new file mode 100644 index 0000000..06c8fc9 --- /dev/null +++ b/source/WUD/DiscReader.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include + +class DiscReader { +public: + DiscReader(); + virtual ~DiscReader(); + + virtual bool IsReady() = 0; + + virtual bool readEncryptedSector(uint8_t *buffer, uint32_t block_cnt, uint64_t offset_in_sector) const = 0; + + bool readEncryptedAligned(uint8_t *buf, uint64_t offset_in_sector, uint32_t size); + + bool readDecryptedChunk(uint64_t readOffset, uint8_t *out_buffer, uint8_t *key, uint8_t *IV) const; + + bool readDecrypted(uint8_t *out_buffer, uint64_t clusterOffset, uint64_t fileOffset, uint32_t size, uint8_t *key, uint8_t *IV, bool useFixedIV) const; + + virtual bool readEncrypted(uint8_t *buf, uint64_t offset, uint32_t size); + + uint8_t discKey[16]{}; + bool hasDiscKey = false; +private: + uint8_t *sector_buf; +}; \ No newline at end of file diff --git a/source/WUD/DiscReaderDiscDrive.cpp b/source/WUD/DiscReaderDiscDrive.cpp new file mode 100644 index 0000000..811d262 --- /dev/null +++ b/source/WUD/DiscReaderDiscDrive.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "DiscReaderDiscDrive.h" + + +DiscReaderDiscDrive::DiscReaderDiscDrive() : DiscReader() { + auto *sector_buf = (uint8_t *) malloc(READ_SECTOR_SIZE); + if (sector_buf == nullptr) { + return; + } + + auto ret = IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &device_handle); + if (ret < 0) { + return; + } + + auto res = IOSUHAX_FSA_RawRead(gFSAfd, sector_buf, READ_SECTOR_SIZE, 1, 3, device_handle); + + if (res >= 0) { + if (((uint32_t *) sector_buf)[0] != WiiUDiscContentsHeader::MAGIC) { + uint8_t iv[16]; + memset(iv, 0, 16); + + auto discKeyRes = IOSUHAX_ODM_GetDiscKey(discKey); + if (discKeyRes >= 0) { + hasDiscKey = true; + auto sector_buf_decrypted = (uint8_t *) malloc(READ_SECTOR_SIZE); + if (sector_buf_decrypted != nullptr) { + aes_set_key((uint8_t *) discKey); + aes_decrypt((uint8_t *) iv, (uint8_t *) sector_buf, (uint8_t *) §or_buf_decrypted[0], READ_SECTOR_SIZE); + if (((uint32_t *) sector_buf_decrypted)[0] == WiiUDiscContentsHeader::MAGIC) { + DEBUG_FUNCTION_LINE("Key was correct"); + this->init_done = true; + } + free(sector_buf_decrypted); + } + } + } else { + this->init_done = true; + } + } + free(sector_buf); +} + +bool DiscReaderDiscDrive::readEncryptedSector(uint8_t *buffer, uint32_t block_cnt, uint64_t offset_in_sectors) const { + if (IOSUHAX_FSA_RawRead(gFSAfd, buffer, READ_SECTOR_SIZE, block_cnt, offset_in_sectors, device_handle) < 0) { + DEBUG_FUNCTION_LINE("Failed to read from Disc"); + return false; + } + return true; +} + +bool DiscReaderDiscDrive::IsReady() { + return init_done; +} + +DiscReaderDiscDrive::~DiscReaderDiscDrive() { + if (device_handle != -1) { + IOSUHAX_FSA_RawOpen(gFSAfd, "/dev/odd01", &device_handle); + } +} + +bool DiscReaderDiscDrive::readEncrypted(uint8_t *buf, uint64_t offset, uint32_t size) { + if (size == 0) { + return true; + } + if ((offset & 0x7FFF) != 0 || (size & 0x7FFF) != 0) { + return DiscReader::readEncrypted(buf, offset, size); + } + uint32_t block_cnt = size >> 15; + uint32_t offset_in_sectors = offset >> 15; + if (IOSUHAX_FSA_RawRead(gFSAfd, buf, 0x8000, block_cnt, offset_in_sectors, device_handle) < 0) { + DEBUG_FUNCTION_LINE("Failed to read from Disc"); + return false; + } + return true; +} diff --git a/source/WUD/DiscReaderDiscDrive.h b/source/WUD/DiscReaderDiscDrive.h new file mode 100644 index 0000000..4e17703 --- /dev/null +++ b/source/WUD/DiscReaderDiscDrive.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "DiscReader.h" + +class DiscReaderDiscDrive : public DiscReader{ +public: + DiscReaderDiscDrive(); + ~DiscReaderDiscDrive() override; + bool readEncryptedSector(uint8_t *buffer, uint32_t block_cnt, uint64_t offset_in_sector) const override; + bool IsReady() override; + + bool readEncrypted(uint8_t *buf, uint64_t offset, uint32_t size) override; + +private: + bool init_done = false; + int32_t device_handle = -1; +}; \ No newline at end of file diff --git a/source/WUD/NUSDataProcessor.h b/source/WUD/NUSDataProcessor.h new file mode 100644 index 0000000..7985de2 --- /dev/null +++ b/source/WUD/NUSDataProcessor.h @@ -0,0 +1,16 @@ +#pragma once + +#include "NUSDataProvider.h" +#include "NUSDecryption.h" + +class NUSDataProcessor { +protected: + NUSDataProcessor() = default; + +public: + virtual ~NUSDataProcessor() = default; + + virtual NUSDataProvider *getDataProvider() = 0; + + virtual bool readPlainDecryptedContent(Content *pContent, uint8_t **data, uint32_t *length) = 0; +}; \ No newline at end of file diff --git a/source/WUD/NUSDataProvider.h b/source/WUD/NUSDataProvider.h new file mode 100644 index 0000000..9d484bc --- /dev/null +++ b/source/WUD/NUSDataProvider.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include + +class NUSDataProvider { + +public: + virtual ~NUSDataProvider() = default; + + virtual bool readRawContent(Content *content, uint8_t *buffer, uint64_t offset, uint32_t size) = 0; + + virtual bool getContentH3Hash(Content *content, uint8_t **data, uint32_t *size) = 0; + + virtual bool getRawTMD(uint8_t **data, uint32_t *size) = 0; + + virtual bool getRawTicket(uint8_t **data, uint32_t *size) = 0; + + virtual bool getRawCert(uint8_t **data, uint32_t *size) = 0; + + virtual void setFST(FST *fst) { + + } +}; \ No newline at end of file diff --git a/source/WUD/NUSDataProviderWUD.cpp b/source/WUD/NUSDataProviderWUD.cpp new file mode 100644 index 0000000..0427e4f --- /dev/null +++ b/source/WUD/NUSDataProviderWUD.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "NUSDataProviderWUD.h" + +NUSDataProviderWUD::NUSDataProviderWUD(WiiUGMPartition *pGamePartition, DiscReader *pDiscReader) { + gamePartition = pGamePartition; + discReader = pDiscReader; +} + +NUSDataProviderWUD::~NUSDataProviderWUD() { + delete fst; +} + +bool NUSDataProviderWUD::readRawContent(Content *content, uint8_t *buffer, uint64_t offset, uint32_t size) { + if (buffer == nullptr) { + DEBUG_FUNCTION_LINE(); + return false; + } + auto offsetInWUD = getOffsetInWUD(content) + offset; + return discReader->readEncrypted(buffer, offsetInWUD, size); +} + +bool NUSDataProviderWUD::getContentH3Hash(Content *content, uint8_t **data, uint32_t *size) { + if (content == nullptr || data == nullptr || size == nullptr) { + DEBUG_FUNCTION_LINE(); + return false; + } + auto cur = gamePartition->getVolumes().begin()->second->h3HashArrayList[content->index]; + if (cur == nullptr || cur->size == 0) { + DEBUG_FUNCTION_LINE(); + return false; + } + *data = (uint8_t *) malloc(cur->size); + *size = cur->size; + memcpy(*data, cur->data, *size); + return true; +} + +void NUSDataProviderWUD::setFST(FST *pFST) { + // We need to set the correct blocksizes + auto blockSize = gamePartition->getVolumes().begin()->second->blockSize; + for (const auto &e: pFST->sectionEntries->getSections()) { + e->address = AddressInVolumeBlocks(blockSize, e->address.value); + e->size = SizeInVolumeBlocks(blockSize, e->size.value); + } + fst = pFST; +} + +bool NUSDataProviderWUD::getRawCert(uint8_t **data, uint32_t *size) { + if (data == nullptr || size == nullptr) { + return false; + } + *data = (uint8_t *) malloc(gamePartition->certLen); + if (*data == nullptr) { + return false; + } + *size = gamePartition->certLen; + memcpy(*data, gamePartition->rawCert, gamePartition->certLen); + return true; +} + +bool NUSDataProviderWUD::getRawTicket(uint8_t **data, uint32_t *size) { + if (data == nullptr || size == nullptr) { + return false; + } + *data = (uint8_t *) malloc(gamePartition->tikLen); + if (*data == nullptr) { + return false; + } + *size = gamePartition->tikLen; + memcpy(*data, gamePartition->rawTicket, gamePartition->tikLen); + return true; +} + +bool NUSDataProviderWUD::getRawTMD(uint8_t **data, uint32_t *size) { + if (data == nullptr || size == nullptr) { + DEBUG_FUNCTION_LINE("input was null"); + return false; + } + *data = (uint8_t *) malloc(gamePartition->TMDLen); + if (*data == nullptr) { + DEBUG_FUNCTION_LINE("Failed to alloc memory"); + return false; + } + *size = gamePartition->TMDLen; + memcpy(*data, gamePartition->rawTMD, gamePartition->TMDLen); + return true; +} + +uint64_t NUSDataProviderWUD::getOffsetInWUD(Content *content) const { + if (content->index == 0) { // Index 0 is the FST which is at the beginning of the partition; + auto *vh = gamePartition->getVolumes().begin()->second; + return gamePartition->getSectionOffsetOnDefaultPartition() + vh->FSTAddress.getAddressInBytes(); + } + auto *info = FSTUtils::getSectionEntryForIndex(fst, content->index); + if (info == nullptr) { + OSFatal("Failed to get section for Content"); + } + return gamePartition->getSectionOffsetOnDefaultPartition() + info->address.getAddressInBytes(); +} diff --git a/source/WUD/NUSDataProviderWUD.h b/source/WUD/NUSDataProviderWUD.h new file mode 100644 index 0000000..9a0b7cd --- /dev/null +++ b/source/WUD/NUSDataProviderWUD.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "NUSDataProvider.h" +#include "DiscReader.h" +#include +#include + +class NUSDataProviderWUD : public NUSDataProvider { + +public: + NUSDataProviderWUD(WiiUGMPartition *pGamePartition, DiscReader *pDiscReader); + + ~NUSDataProviderWUD() override; + + bool readRawContent(Content *content, uint8_t *buffer, uint64_t offset, uint32_t size) override; + + bool getContentH3Hash(Content *content, uint8_t **data, uint32_t *size) override; + + void setFST(FST *pFST) override; + + bool getRawCert(uint8_t **data, uint32_t *size) override; + + bool getRawTicket(uint8_t **data, uint32_t *size) override; + + bool getRawTMD(uint8_t **data, uint32_t *size) override; + + FST *fst{}; + WiiUGMPartition *gamePartition; + DiscReader *discReader; + +private: + uint64_t getOffsetInWUD(Content *content) const; +}; \ No newline at end of file diff --git a/source/WUD/NUSDecryption.cpp b/source/WUD/NUSDecryption.cpp new file mode 100644 index 0000000..a6aa7e6 --- /dev/null +++ b/source/WUD/NUSDecryption.cpp @@ -0,0 +1,26 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "NUSDecryption.h" + +void NUSDecryption::decryptData(uint8_t *IV, uint8_t *inData, uint8_t *outData, uint32_t size) const { + aes_set_key(ticket->ticketKeyDec); + aes_decrypt(IV, inData, outData, size); +} + +NUSDecryption::NUSDecryption(Ticket *pTicket) { + ticket = pTicket; +} diff --git a/source/WUD/NUSDecryption.h b/source/WUD/NUSDecryption.h new file mode 100644 index 0000000..9148bba --- /dev/null +++ b/source/WUD/NUSDecryption.h @@ -0,0 +1,28 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include "Ticket.h" + +class NUSDecryption { +public: + explicit NUSDecryption(Ticket *pTicket); + + void decryptData(uint8_t *IV, uint8_t *inData, uint8_t *outData, uint32_t size) const ; + + Ticket *ticket; +}; diff --git a/source/WUD/NUSTitle.cpp b/source/WUD/NUSTitle.cpp new file mode 100644 index 0000000..580d94d --- /dev/null +++ b/source/WUD/NUSTitle.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "NUSTitle.h" + +NUSTitle *NUSTitle::loadTitle(NUSDataProvider *dataProvider, uint8_t *commonKey) { + uint8_t *data = nullptr; + uint32_t dataLen = 0; + if (!dataProvider->getRawTMD(&data, &dataLen)) { + DEBUG_FUNCTION_LINE("Failed to read TMD"); + delete dataProvider; + return nullptr; + } + + auto *tmd = new TitleMetaData(data); + free(data); + + if (!dataProvider->getRawTicket(&data, &dataLen)) { + DEBUG_FUNCTION_LINE("Failed to read ticket"); + delete tmd; + delete dataProvider; + return nullptr; + } + + auto *ticket = new Ticket(data, commonKey); + free(data); + auto *decryption = new NUSDecryption(ticket); + auto *dpp = new DefaultNUSDataProcessor(dataProvider, decryption); + + // If we have more than one content, the index 0 is the FST. + Content *fstContent = tmd->getContentByIndex(0); + + if (!dpp->readPlainDecryptedContent(fstContent, &data, &dataLen)) { + DEBUG_FUNCTION_LINE("Failed to read decrypted content"); + delete dataProvider; + delete dpp; + delete decryption; + delete ticket; + delete tmd; + return nullptr; + } + FST *fst = new FST(data, dataLen, 0, VolumeBlockSize(1)); + + // The dataprovider may need the FST to calculate the offset of a content + // on the partition. + dataProvider->setFST(fst); + + return new NUSTitle(tmd, dpp, dataProvider, decryption, ticket, fst); +} + +NUSTitle::NUSTitle(TitleMetaData *pTMD, NUSDataProcessor *pProcessor, NUSDataProvider *pDataProvider, NUSDecryption *pDecryption, Ticket *pTicket, FST *pFST) { + tmd = pTMD; + dataProcessor = pProcessor; + ticket = pTicket; + fst = pFST; + decryption = pDecryption; + dataProvider = pDataProvider; +} + +NUSTitle::~NUSTitle() { + delete dataProvider; + delete dataProcessor; + delete decryption; + delete ticket; + delete tmd; +} + +NUSTitle *NUSTitle::loadTitleFromGMPartition(WiiUGMPartition *pPartition, DiscReaderDiscDrive *pDrive, uint8_t *commonKey) { + return loadTitle(new NUSDataProviderWUD(pPartition, pDrive), commonKey); +} diff --git a/source/WUD/NUSTitle.h b/source/WUD/NUSTitle.h new file mode 100644 index 0000000..5b304d5 --- /dev/null +++ b/source/WUD/NUSTitle.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include "Ticket.h" +#include "NUSDataProcessor.h" +#include "NUSDecryption.h" +#include "DefaultNUSDataProcessor.h" +#include "DiscReaderDiscDrive.h" +#include "NUSDataProviderWUD.h" + +class NUSTitle { + +public: + ~NUSTitle(); + + NUSDataProcessor *dataProcessor; + TitleMetaData *tmd; + Ticket *ticket; + FST *fst; + NUSDecryption *decryption; + NUSDataProvider *dataProvider; + + static NUSTitle *loadTitleFromGMPartition(WiiUGMPartition *pPartition, DiscReaderDiscDrive *pDrive, uint8_t commonKey[16]); + +private: + static NUSTitle *loadTitle(NUSDataProvider *dataProvider, uint8_t commonKey[16]); + + NUSTitle(TitleMetaData *pTMD, NUSDataProcessor *pProcessor, NUSDataProvider *pDataProvider, NUSDecryption *pDecryption, Ticket *pTicket, FST *pFST); + +}; \ No newline at end of file diff --git a/source/WUD/Ticket.cpp b/source/WUD/Ticket.cpp new file mode 100644 index 0000000..842a3d0 --- /dev/null +++ b/source/WUD/Ticket.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "Ticket.h" + +Ticket::Ticket(uint8_t *data, uint8_t *commonKey) { + uint8_t *tikKeyEnc = data + 0x1BF; + uint8_t *title_id = data + 0x1DC; + uint8_t IV[0x10]; + + int k; + for (k = 0; k < 8; k++) { + IV[k] = title_id[k]; + IV[k + 8] = 0x00; + } + + aes_set_key(commonKey); + aes_decrypt(IV, tikKeyEnc, ticketKeyDec, 16); + memcpy(ticketKeyEnc, tikKeyEnc, 16); +} diff --git a/source/WUD/Ticket.h b/source/WUD/Ticket.h new file mode 100644 index 0000000..d4dbd7f --- /dev/null +++ b/source/WUD/Ticket.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include + +class Ticket { +public: + Ticket(uint8_t *data, uint8_t commonKey[16]); + + uint8_t ticketKeyEnc[16]{}; + uint8_t ticketKeyDec[16]{}; +}; \ No newline at end of file diff --git a/source/WUD/content/WiiUContentsInformation.cpp b/source/WUD/content/WiiUContentsInformation.cpp new file mode 100644 index 0000000..663a91c --- /dev/null +++ b/source/WUD/content/WiiUContentsInformation.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "WiiUContentsInformation.h" + +uint32_t WiiUContentsInformation::LENGTH = 32768; +WiiUContentsInformation::WiiUContentsInformation(DiscReader *reader, uint32_t offset) { + uint32_t curOffset = offset; + discContentHeader = new WiiUDiscContentsHeader(reader, curOffset); + curOffset += WiiUDiscContentsHeader::LENGTH; + + partitions = new WiiUPartitions(reader, curOffset, discContentHeader->numberOfPartition, discContentHeader->blockSize); + curOffset += WiiUPartitions::LENGTH; + + if (curOffset - offset != LENGTH) { + OSFatal("Length mismatch"); + } +} + +WiiUContentsInformation::~WiiUContentsInformation() { + delete partitions; + delete discContentHeader; +} diff --git a/source/WUD/content/WiiUContentsInformation.h b/source/WUD/content/WiiUContentsInformation.h new file mode 100644 index 0000000..18b7360 --- /dev/null +++ b/source/WUD/content/WiiUContentsInformation.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include "../DiscReader.h" +#include "WiiUDiscContentsHeader.h" +#include "partitions/WiiUPartitions.h" + +class WiiUContentsInformation { + +public: + WiiUContentsInformation(DiscReader *reader, uint32_t offset); + + ~WiiUContentsInformation(); + + WiiUDiscContentsHeader *discContentHeader; + + WiiUPartitions *partitions; + + static uint32_t LENGTH; +}; diff --git a/source/WUD/content/WiiUDiscContentsHeader.cpp b/source/WUD/content/WiiUDiscContentsHeader.cpp new file mode 100644 index 0000000..f1b3b6f --- /dev/null +++ b/source/WUD/content/WiiUDiscContentsHeader.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include +#include "WiiUDiscContentsHeader.h" + +uint32_t WiiUDiscContentsHeader::LENGTH = 2048; +uint32_t WiiUDiscContentsHeader::MAGIC = 0xCCA6E67B; + +WiiUDiscContentsHeader::WiiUDiscContentsHeader(DiscReader *reader, uint32_t offset) { + auto *buffer = (uint8_t *) malloc(LENGTH); + if (!reader->hasDiscKey) { + if(!reader->readEncrypted(buffer, offset, LENGTH)){ + OSFatal("WiiUDiscContentsHeader: Failed to read encrypted"); + } + } else { + if(!reader->readDecrypted(buffer, offset, 0, LENGTH, reader->discKey, nullptr, true)){ + OSFatal("WiiUDiscContentsHeader: Failed to read decrypted"); + } + } + + if (((uint32_t *) buffer)[0] != MAGIC) { + OSFatal("WiiUDiscContentsHeader MAGIC mismatch."); + } + blockSize = DiscBlockSize(((uint32_t *) buffer)[1]); + memcpy(tocHash, &buffer[8], 20); + numberOfPartition = ((uint32_t *) buffer)[7]; + free(buffer); +} diff --git a/source/WUD/content/WiiUDiscContentsHeader.h b/source/WUD/content/WiiUDiscContentsHeader.h new file mode 100644 index 0000000..26ad6a3 --- /dev/null +++ b/source/WUD/content/WiiUDiscContentsHeader.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class WiiUDiscContentsHeader { + +public: + WiiUDiscContentsHeader(DiscReader *reader, uint32_t offset); + + ~WiiUDiscContentsHeader() = default; + + DiscBlockSize blockSize{}; + uint8_t tocHash[20]{}; + uint32_t numberOfPartition; + + static uint32_t LENGTH; + + static uint32_t MAGIC; +}; diff --git a/source/WUD/content/partitions/WiiUDataPartition.cpp b/source/WUD/content/partitions/WiiUDataPartition.cpp new file mode 100644 index 0000000..5d8d426 --- /dev/null +++ b/source/WUD/content/partitions/WiiUDataPartition.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "WiiUDataPartition.h" + +WiiUDataPartition::~WiiUDataPartition() { + delete basePartition; + delete fst; +} + +WiiUDataPartition::WiiUDataPartition(WiiUPartition *partition, FST *pFST) { + basePartition = partition; + fst = pFST; +} + +std::string WiiUDataPartition::getVolumeId() const &{ + return basePartition->getVolumeId(); +} + +std::map WiiUDataPartition::getVolumes() const &{ + return basePartition->getVolumes(); +} + +uint16_t WiiUDataPartition::getFileSystemDescriptor() const { + return basePartition->getFileSystemDescriptor(); +} + +uint64_t WiiUDataPartition::getSectionOffsetOnDefaultPartition() { + return basePartition->getSectionOffsetOnDefaultPartition(); +} diff --git a/source/WUD/content/partitions/WiiUDataPartition.h b/source/WUD/content/partitions/WiiUDataPartition.h new file mode 100644 index 0000000..ad8d823 --- /dev/null +++ b/source/WUD/content/partitions/WiiUDataPartition.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include "WiiUPartition.h" + +class WiiUDataPartition : public WiiUPartition { + +public: + WiiUDataPartition(WiiUPartition *partition, FST *pFST); + + ~WiiUDataPartition() override; + + [[nodiscard]] std::string getVolumeId() const & override; + + [[nodiscard]] std::map getVolumes() const & override; + + [[nodiscard]] uint16_t getFileSystemDescriptor() const override; + + + [[nodiscard]] uint64_t getSectionOffsetOnDefaultPartition() override; + + FST *fst{}; + +private: + WiiUPartition *basePartition; +}; + diff --git a/source/WUD/content/partitions/WiiUGMPartition.cpp b/source/WUD/content/partitions/WiiUGMPartition.cpp new file mode 100644 index 0000000..d2b17ec --- /dev/null +++ b/source/WUD/content/partitions/WiiUGMPartition.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "WiiUGMPartition.h" + +WiiUGMPartition::~WiiUGMPartition() { + free(rawCert); + free(rawTMD); + free(rawTicket); + delete basePartition; +} + +WiiUGMPartition::WiiUGMPartition(WiiUPartition *partition, uint8_t *pRawTIK, uint32_t pTikLen, uint8_t *pRawTMD, uint32_t pTMDLen, uint8_t *pRawCert, uint32_t pCertLen) { + basePartition = partition; + rawCert = pRawCert; + rawTMD = pRawTMD; + rawTicket = pRawTIK; + tikLen = pTikLen; + TMDLen = pTMDLen; + certLen = pCertLen; +} + +std::string WiiUGMPartition::getVolumeId() const &{ + return basePartition->getVolumeId(); +} + +std::map WiiUGMPartition::getVolumes() const &{ + return basePartition->getVolumes(); +} + +uint16_t WiiUGMPartition::getFileSystemDescriptor() const { + return basePartition->getFileSystemDescriptor(); +} + +uint64_t WiiUGMPartition::getSectionOffsetOnDefaultPartition() { + return basePartition->getSectionOffsetOnDefaultPartition(); +} diff --git a/source/WUD/content/partitions/WiiUGMPartition.h b/source/WUD/content/partitions/WiiUGMPartition.h new file mode 100644 index 0000000..3645985 --- /dev/null +++ b/source/WUD/content/partitions/WiiUGMPartition.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include "WiiUPartition.h" + +class WiiUGMPartition : public WiiUPartition { +public: + WiiUGMPartition(WiiUPartition *partition, uint8_t *pRawTIK, uint32_t pTikLen, uint8_t *pRawTMD, uint32_t pTMDLen, uint8_t *pRawCert, uint32_t pCertLen); + + ~WiiUGMPartition() override; + + [[nodiscard]] std::string getVolumeId() const & override; + + [[nodiscard]] std::map getVolumes() const & override; + + [[nodiscard]] uint16_t getFileSystemDescriptor() const override; + + [[nodiscard]] uint64_t getSectionOffsetOnDefaultPartition() override; + + uint8_t *rawTicket; + uint8_t *rawTMD; + uint8_t *rawCert; + uint32_t tikLen; + uint32_t TMDLen; + uint32_t certLen; + +private: + WiiUPartition *basePartition; +}; + diff --git a/source/WUD/content/partitions/WiiUPartition.cpp b/source/WUD/content/partitions/WiiUPartition.cpp new file mode 100644 index 0000000..83d87be --- /dev/null +++ b/source/WUD/content/partitions/WiiUPartition.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "WiiUPartition.h" + +uint32_t WiiUPartition::LENGTH = 128; + +WiiUPartition::WiiUPartition() = default; + +WiiUPartition::~WiiUPartition() { + for (auto const&[key, val]: volumes) { + delete val; + } + volumes.clear(); +} + +WiiUPartition::WiiUPartition(DiscReader *reader, uint32_t offset, const DiscBlockSize &blockSize) { + auto buffer = (uint8_t *) malloc(LENGTH); + if (buffer == nullptr) { + OSFatal("WiiUPartition: alloc buffer failed"); + } + if (!reader->hasDiscKey) { + if (!reader->readEncrypted(buffer, offset, LENGTH)) { + OSFatal("WiiUPartition: Failed to read encrypted"); + } + } else { + auto bufferBigger = (uint8_t *) malloc(LENGTH + 0x10); + if (bufferBigger == nullptr) { + OSFatal("WiiUPartition: alloc bufferBigger failed"); + } + if (!reader->readDecrypted(bufferBigger, offset - 0x10, 0, LENGTH + 0x10, reader->discKey, nullptr, true)) { + OSFatal("WiiUPartition: Failed to read encrypted"); + } + + memcpy(buffer, bufferBigger + 0x10, LENGTH); + + free(bufferBigger); + } + + char name[32]; + memset(name, 0, sizeof(name)); + memcpy(name, buffer, 31); + volumeId = name; + uint8_t num = buffer[31]; + + for (int i = 0; i < num; i++) { + auto address = *((uint32_t *) &buffer[32 + (i * 4)]); + AddressInDiscBlocks discLbaAddress = AddressInDiscBlocks(blockSize, address); + auto vh = new VolumeHeader(reader, discLbaAddress.getAddressInBytes()); + volumes[discLbaAddress] = vh; + } + + fileSystemDescriptor = ((uint16_t *) &buffer[64])[0]; + + free(buffer); +} + +uint64_t WiiUPartition::getSectionOffsetOnDefaultPartition() { + if (volumes.size() != 1) { + OSFatal("We have more or less than 1 volume header."); + } + return volumes.begin()->first.getAddressInBytes(); +} + + diff --git a/source/WUD/content/partitions/WiiUPartition.h b/source/WUD/content/partitions/WiiUPartition.h new file mode 100644 index 0000000..62a2b6a --- /dev/null +++ b/source/WUD/content/partitions/WiiUPartition.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include "volumes/VolumeHeader.h" + +class WiiUPartition { + +public: + WiiUPartition(); + explicit WiiUPartition(DiscReader *reader, uint32_t offset, const DiscBlockSize &blockSize); + + virtual uint64_t getSectionOffsetOnDefaultPartition(); + + virtual ~WiiUPartition(); + + [[nodiscard]] virtual std::string getVolumeId() const &{ + return volumeId; + } + + [[nodiscard]] virtual std::map getVolumes() const&{ + return volumes; + } + + [[nodiscard]] virtual uint16_t getFileSystemDescriptor() const{ + return fileSystemDescriptor; + } + +private: + std::string volumeId; + std::map volumes; + uint16_t fileSystemDescriptor{}; + static uint32_t LENGTH; +}; \ No newline at end of file diff --git a/source/WUD/content/partitions/WiiUPartitions.cpp b/source/WUD/content/partitions/WiiUPartitions.cpp new file mode 100644 index 0000000..0d5a9a8 --- /dev/null +++ b/source/WUD/content/partitions/WiiUPartitions.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include "WiiUPartitions.h" +#include "WiiUGMPartition.h" +#include "WiiUDataPartition.h" + +uint32_t WiiUPartitions::LENGTH = 30720; + +WiiUPartitions::WiiUPartitions(DiscReader *reader, uint32_t offset, uint32_t numberOfPartitions, const DiscBlockSize &blockSize) { + std::vector tmp; + tmp.reserve(numberOfPartitions); + for (uint32_t i = 0; i < numberOfPartitions; i++) { + tmp.push_back(new WiiUPartition(reader, offset + (i * 128), blockSize)); + } + WiiUPartition *SIPartition = nullptr; + for (auto &partition: tmp) { + if (partition->getVolumeId().starts_with("SI")) { + SIPartition = partition; + } + } + + if (SIPartition != nullptr) { + for (auto const&[key, val]: SIPartition->getVolumes()) { + auto volumeAddress = key; + auto volumeAddressInBytes = volumeAddress.getAddressInBytes(); + auto volumeHeader = val; + + auto fst = (uint8_t *) malloc(volumeHeader->FSTSize); + if (fst == nullptr) { + OSFatal("WiiUPartitions: Failed to alloc FST buffer"); + } + + if (!reader->hasDiscKey) { + if (!reader->readEncrypted(fst, volumeAddressInBytes + volumeHeader->FSTAddress.getAddressInBytes(), + volumeHeader->FSTSize)) { + OSFatal("WiiUPartitions: Failed to read encrypted"); + } + } else { + if (!reader->readDecrypted(fst, volumeAddressInBytes + volumeHeader->FSTAddress.getAddressInBytes(), 0, volumeHeader->FSTSize, + reader->discKey, nullptr, true)) { + OSFatal("WiiUPartitions: Failed to read decrypted"); + } + } + + FST *siFST = new FST(fst, volumeHeader->FSTSize, 0, volumeHeader->blockSize); + free(fst); + + for (auto &child: siFST->getRootEntry()->getDirChildren()) { + uint8_t *tikRaw = nullptr; + uint32_t tikRawLen = 0; + std::string tikFilePath = std::string(child->getFullPath() + '/' + WUD_TICKET_FILENAME); + if (!getFSTEntryAsByte(&tikRaw, &tikRawLen, tikFilePath, siFST, volumeAddress, reader)) { + OSFatal("tikRaw"); + } + + uint8_t *tmdRaw = nullptr; + uint32_t tmdRawLen = 0; + std::string tmdFilePath = std::string(child->getFullPath() + '/' + WUD_TMD_FILENAME); + if (!getFSTEntryAsByte(&tmdRaw, &tmdRawLen, tmdFilePath, siFST, volumeAddress, reader)) { + OSFatal("tmdRaw"); + } + + uint8_t *certRaw = nullptr; + uint32_t certRawLen = 0; + std::string certFilePath = std::string(child->getFullPath() + '/' + WUD_CERT_FILENAME); + if (!getFSTEntryAsByte(&certRaw, &certRawLen, certFilePath, siFST, volumeAddress, reader)) { + OSFatal("certRaw"); + } + + char partitionNameRaw[0x12]; + memset(partitionNameRaw, 0, 0x12); + snprintf(partitionNameRaw, 0x11, "%016llX", *((uint64_t *) &tikRaw[0x1DC])); + + std::string partitionName = std::string("GM") + partitionNameRaw; + + WiiUPartition *curPartition = nullptr; + for (auto &partition: tmp) { + if (partition->getVolumeId().starts_with(partitionName)) { + curPartition = partition; + } + } + + if (curPartition == nullptr) { + OSFatal("Failed to get partition"); + } + + auto *gmPartition = new WiiUGMPartition(curPartition, tikRaw, tikRawLen, tmdRaw, tmdRawLen, certRaw, certRawLen); + partitions.push_back(gmPartition); + } + delete siFST; + } + + } + + for (auto &partition: tmp) { + if (partition->getVolumeId().starts_with("GM")) { + continue; + } + if (partition->getVolumes().size() != 1) { + OSFatal("We can't handle more or less than one partion address yet."); + } + auto volumeAddress = partition->getVolumes().begin()->first; + auto vh = partition->getVolumes().begin()->second; + auto *rawFST = (uint8_t *) malloc(vh->FSTSize); + if (rawFST == nullptr) { + OSFatal("Failed to alloc rawFST"); + } + if (!reader->hasDiscKey) { + if (!reader->readEncrypted(rawFST, volumeAddress.getAddressInBytes() + vh->FSTAddress.getAddressInBytes(), vh->FSTSize)) { + OSFatal("WiiUPartition: Failed to read encrypted"); + } + } else { + if (!reader->readDecrypted(rawFST, volumeAddress.getAddressInBytes() + vh->FSTAddress.getAddressInBytes(), 0, vh->FSTSize, + reader->discKey, nullptr, true)) { + OSFatal("WiiUPartition: Failed to read encrypted"); + } + } + + FST *fst = new FST(rawFST, vh->FSTSize, 0, vh->blockSize); + free(rawFST); + partitions.push_back(new WiiUDataPartition(partition, fst)); + } +} + +WiiUPartitions::~WiiUPartitions() { + for (auto &partition: partitions) { + delete partition; + } +} + +bool WiiUPartitions::getFSTEntryAsByte(uint8_t **buffer_out, uint32_t *outSize, std::string &filePath, FST *fst, const AddressInDiscBlocks &volumeAddress, DiscReader *discReader) { + NodeEntry *entry = FSTUtils::getFSTEntryByFullPath(fst->nodeEntries->rootEntry, filePath); + + auto asFileEntry = dynamic_cast(entry); + if (asFileEntry == nullptr) { + return false; + } + + SectionEntry *info = asFileEntry->getSectionEntry(); + if (info == nullptr) { + OSFatal("WiiUPartitions::getFSTEntryAsByte, section info was null"); + } + + uint64_t sectionOffsetOnDisc = volumeAddress.getAddressInBytes() + info->address.getAddressInBytes(); + + auto *buffer = (uint8_t *) malloc(asFileEntry->getSize()); + if (buffer == nullptr) { + return false; + } + *buffer_out = buffer; + *outSize = asFileEntry->getSize(); + + if (!discReader->hasDiscKey) { + return discReader->readEncrypted(buffer, sectionOffsetOnDisc + asFileEntry->getOffset(), asFileEntry->getSize()); + } + + // Calculating the IV + uint8_t IV[16]; + memset(IV, 0, 16); + uint64_t ivTemp = asFileEntry->getOffset() >> 16; + memcpy(IV + 8, &ivTemp, 8); + + return discReader->readDecrypted(buffer, sectionOffsetOnDisc, asFileEntry->getOffset(), asFileEntry->getSize(), discReader->discKey, IV, false); +} + diff --git a/source/WUD/content/partitions/WiiUPartitions.h b/source/WUD/content/partitions/WiiUPartitions.h new file mode 100644 index 0000000..fd02902 --- /dev/null +++ b/source/WUD/content/partitions/WiiUPartitions.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include "WiiUPartition.h" + +#define WUD_TMD_FILENAME "title.tmd" +#define WUD_TICKET_FILENAME "title.tik" +#define WUD_CERT_FILENAME "title.cert" + +class WiiUPartitions { + +public: + static bool getFSTEntryAsByte(uint8_t **buffer_out, uint32_t *outSize, std::string &filePath, FST *fst, const AddressInDiscBlocks& volumeAddress, DiscReader* discReader); + + WiiUPartitions(DiscReader *reader, uint32_t offset, uint32_t numberOfPartitions, const DiscBlockSize& blockSize); + + ~WiiUPartitions(); + + std::vector partitions; + static uint32_t LENGTH; + +}; \ No newline at end of file diff --git a/source/WUD/content/partitions/volumes/H3HashArray.cpp b/source/WUD/content/partitions/volumes/H3HashArray.cpp new file mode 100644 index 0000000..a7e940d --- /dev/null +++ b/source/WUD/content/partitions/volumes/H3HashArray.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "H3HashArray.h" +#include +#include + +H3HashArray::H3HashArray(uint8_t *pData, uint32_t pSize) { + size = pSize; + data = nullptr; + if (pSize > 0) { + data = (uint8_t *) malloc(pSize); + if (data == nullptr) { + OSFatal("H3HashArray: Failed to alloc"); + } + memcpy(data, pData, pSize); + } +} + +H3HashArray::~H3HashArray() { + if (data) { + free(data); + } +} diff --git a/source/WUD/content/partitions/volumes/H3HashArray.h b/source/WUD/content/partitions/volumes/H3HashArray.h new file mode 100644 index 0000000..472c517 --- /dev/null +++ b/source/WUD/content/partitions/volumes/H3HashArray.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class H3HashArray { + +public: + H3HashArray(uint8_t *pData, uint32_t pSize); + + ~H3HashArray(); + + uint8_t *data = nullptr; + uint8_t size; +}; diff --git a/source/WUD/content/partitions/volumes/VolumeHeader.cpp b/source/WUD/content/partitions/volumes/VolumeHeader.cpp new file mode 100644 index 0000000..af4fdab --- /dev/null +++ b/source/WUD/content/partitions/volumes/VolumeHeader.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ + +#include "VolumeHeader.h" +#include +#include + +uint32_t VolumeHeader::MAGIC = 0xCC93A4F5; + +VolumeHeader::VolumeHeader(DiscReader *reader, uint64_t offset) { + auto buffer = (uint8_t *) malloc(64); + if (buffer == nullptr) { + OSFatal("VolumeHeader: failed to alloc buffer"); + } + + if (!reader->readEncrypted(buffer, offset, 64)) { + OSFatal("VolumeHeader: failed to read"); + } + + auto *bufferUint = (uint32_t *) buffer; + + if (bufferUint[0] != MAGIC) { + OSFatal("VolumeHeader MAGIC mismatch."); + } + + blockSize = VolumeBlockSize(bufferUint[1]); + volumeSize = SizeInVolumeBlocks(blockSize, bufferUint[2]); + h3HashArrayListSize = bufferUint[3]; + numberOfH3HashArray = bufferUint[4]; + FSTSize = bufferUint[5]; + FSTAddress = AddressInVolumeBlocks(blockSize, bufferUint[6]); + FSTHashMode = buffer[36]; + encryptType = buffer[37]; + majorVersion = buffer[38]; + minorVersion = buffer[39]; + expiringMajorVersion = buffer[40]; + + free(buffer); + + auto bufferH3 = (uint8_t *) malloc(ROUNDUP(h3HashArrayListSize,16)); + if (bufferH3 == nullptr) { + OSFatal("VolumeHeader: failed to alloc h3 buffer"); + } + + if (!reader->readEncrypted(bufferH3,offset + 64, ROUNDUP(h3HashArrayListSize,16))) { + OSFatal("VolumeHeader: failed to read h3"); + } + + h3HashArrayList = getH3HashArray(bufferH3, numberOfH3HashArray, h3HashArrayListSize); + + free(bufferH3); +} + +std::vector VolumeHeader::getH3HashArray(uint8_t *h3Data, uint32_t pNumberOfH3HashArray, uint32_t pH3HashArrayListSize) { + std::vector arrayList; + if(pNumberOfH3HashArray == 0){ + return arrayList; + } + + for (uint32_t i = 1; i < pNumberOfH3HashArray; i++) { + auto *offsetPtr = (uint32_t *) &h3Data[i * 4]; + uint32_t curOffset = offsetPtr[0]; + uint32_t curEnd = pH3HashArrayListSize; + if (i < pNumberOfH3HashArray - 1) { + // If it's not the last element, the end of our .h3 is the start of the next .h3 + curEnd = offsetPtr[1]; + } + + arrayList.push_back(new H3HashArray(h3Data + curOffset, curEnd - curOffset)); + } + + return arrayList; +} + +VolumeHeader::~VolumeHeader() { + for (auto &h3: h3HashArrayList) { + delete h3; + } +} + diff --git a/source/WUD/content/partitions/volumes/VolumeHeader.h b/source/WUD/content/partitions/volumes/VolumeHeader.h new file mode 100644 index 0000000..6b6656c --- /dev/null +++ b/source/WUD/content/partitions/volumes/VolumeHeader.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "H3HashArray.h" + +class VolumeHeader { + +public: + static std::vector getH3HashArray(uint8_t *h3Data, uint32_t numberOfH3HashArray, uint32_t h3HashArrayListSize); + + VolumeHeader(DiscReader *reader, uint64_t offset); + + ~VolumeHeader(); + + static uint32_t MAGIC; + VolumeBlockSize blockSize; + SizeInVolumeBlocks volumeSize; + uint32_t FSTSize; + AddressInVolumeBlocks FSTAddress; + uint8_t FSTHashMode; + uint8_t encryptType; + uint8_t majorVersion; + uint8_t minorVersion; + uint8_t expiringMajorVersion; + std::vector h3HashArrayList; + + uint32_t h3HashArrayListSize; + uint32_t numberOfH3HashArray; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/FST.cpp b/source/WUD/entities/FST/FST.cpp new file mode 100644 index 0000000..1d07054 --- /dev/null +++ b/source/WUD/entities/FST/FST.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "FST.h" + +FST::FST(uint8_t *data, uint32_t fstSize, uint32_t offset, const VolumeBlockSize &blockSize) { + uint32_t curOffset = offset; + + header = new FSTHeader(data + curOffset); + curOffset += header->LENGTH; + + sectionEntries = new SectionEntries(data + curOffset, header->numberOfSections, blockSize); + curOffset += sectionEntries->getSizeInBytes(); + + uint32_t lastEntryNumber = RootEntry::parseLastEntryNumber(data, curOffset); + + auto stringTableOffset = curOffset + (lastEntryNumber * 16); + + stringTable = StringTable::parseData(data, fstSize - stringTableOffset, stringTableOffset, lastEntryNumber); + + nodeEntries = NodeEntries::parseData(data, curOffset, sectionEntries, stringTable, header->blockSize); +} + +FST::~FST() { + delete nodeEntries; + delete stringTable; + delete sectionEntries; + delete header; +} + +RootEntry *FST::getRootEntry() const { + return nodeEntries->rootEntry; +} + diff --git a/source/WUD/entities/FST/FST.h b/source/WUD/entities/FST/FST.h new file mode 100644 index 0000000..ef25283 --- /dev/null +++ b/source/WUD/entities/FST/FST.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include + +class FST { + +public: + FST(uint8_t *data, uint32_t fstSize, uint32_t offset, const VolumeBlockSize& blockSize); + + ~FST(); + + FSTHeader *header; + SectionEntries *sectionEntries; + StringTable *stringTable; + NodeEntries *nodeEntries; + + [[nodiscard]] RootEntry * getRootEntry() const; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/header/FSTHeader.cpp b/source/WUD/entities/FST/header/FSTHeader.cpp new file mode 100644 index 0000000..d6e6075 --- /dev/null +++ b/source/WUD/entities/FST/header/FSTHeader.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include "FSTHeader.h" + +FSTHeader::FSTHeader(uint8_t *data) { + auto *dataAsUint = (uint32_t *) data; + if ((dataAsUint[0] & 0xFFFFFF00) != 0x46535400) { + OSFatal("FST Header magic was wrong"); + } + FSTVersion = data[3]; + blockSize = SectionBlockSize(dataAsUint[1]); + numberOfSections = dataAsUint[2]; + hashDisabled = data[12]; +} diff --git a/source/WUD/entities/FST/header/FSTHeader.h b/source/WUD/entities/FST/header/FSTHeader.h new file mode 100644 index 0000000..fc01711 --- /dev/null +++ b/source/WUD/entities/FST/header/FSTHeader.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class FSTHeader { + +public: + explicit FSTHeader(uint8_t *data) ; + + ~FSTHeader()= default; + + uint8_t FSTVersion; + SectionBlockSize blockSize; + uint8_t hashDisabled; + uint32_t numberOfSections; + uint32_t LENGTH = 32; + +}; diff --git a/source/WUD/entities/FST/nodeentry/DirectoryEntry.cpp b/source/WUD/entities/FST/nodeentry/DirectoryEntry.cpp new file mode 100644 index 0000000..a7c7a86 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/DirectoryEntry.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "DirectoryEntry.h" + +DirectoryEntry *DirectoryEntry::parseData(const uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable) { + auto *directoryEntry = new DirectoryEntry(); + directoryEntry->entryNumber = param.entryNumber; + directoryEntry->parent = param.parent; + directoryEntry->entryType = param.type; + directoryEntry->nameString = stringTable->getStringEntry(param.uint24); + if (directoryEntry->nameString == nullptr) { + OSFatal("Failed to find string for offset"); + } + + directoryEntry->parentEntryNumber = ((uint32_t *) &data[4])[0]; + directoryEntry->lastEntryNumber = ((uint32_t *) &data[8])[0]; + + directoryEntry->permission = param.permission; + + if (param.sectionNumber > sectionEntries->size()) { + OSFatal("section number does not match"); + } + directoryEntry->sectionEntry = sectionEntries->getSection(param.sectionNumber); + + return directoryEntry; +} + +DirectoryEntry::~DirectoryEntry() { + for (auto &child: children) { + delete child; + } +} + +std::vector DirectoryEntry::getDirChildren() const { + std::vector res; + for (auto &cur: children) { + if (cur->isDirectory()) { + res.push_back(dynamic_cast(cur)); + } + } + return res; +} + +std::vector DirectoryEntry::getFileChildren() const { + std::vector res; + for (auto &cur: children) { + if (cur->isFile()) { + res.push_back(dynamic_cast(cur)); + } + } + return res; +} + +std::vector DirectoryEntry::getChildren() const { + return children; +} + +void DirectoryEntry::printPathRecursive() { + DEBUG_FUNCTION_LINE("%s", getFullPath().c_str()); + for (auto &child: children) { + child->printPathRecursive(); + } +} + +void DirectoryEntry::addChild(NodeEntry *entry) { + children.push_back(entry); +} diff --git a/source/WUD/entities/FST/nodeentry/DirectoryEntry.h b/source/WUD/entities/FST/nodeentry/DirectoryEntry.h new file mode 100644 index 0000000..74495f4 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/DirectoryEntry.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include "NodeEntryParam.h" +#include "NodeEntry.h" +#include "FileEntry.h" + +class DirectoryEntry : public NodeEntry { + + +public: + ~DirectoryEntry() override; + + static DirectoryEntry *parseData(const uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable); + + uint32_t parentEntryNumber{}; + uint32_t lastEntryNumber{}; + std::vector children; + + void addChild(NodeEntry *entry); + + [[nodiscard]] std::vector getDirChildren() const; + + [[nodiscard]] std::vector getFileChildren() const; + + [[nodiscard]] std::vector getChildren() const; + + void printPathRecursive() override; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/nodeentry/FileEntry.cpp b/source/WUD/entities/FST/nodeentry/FileEntry.cpp new file mode 100644 index 0000000..a75eb42 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/FileEntry.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "FileEntry.h" +#include +#include + +#include + +FileEntry::FileEntry(SectionAddress address) : address(std::move(address)) { + +} + +NodeEntry *FileEntry::parseData(uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable, const SectionBlockSize &blockSize) { + auto *entry = new FileEntry(SectionAddress(blockSize, ((uint32_t *) &data[4])[0])); + + entry->entryNumber = param.entryNumber; + entry->parent = param.parent; + entry->entryType = param.type; + entry->nameString = stringTable->getStringEntry(param.uint24); + if (entry->nameString == nullptr) { + OSFatal("Failed to find string for offset"); + } + + entry->size = ((uint32_t *) &data[8])[0]; + + entry->permission = param.permission; + entry->sectionEntry = sectionEntries->getSection(param.sectionNumber); + + return entry; +} + +SectionEntry *FileEntry::getSectionEntry() { + return sectionEntry; +} + +uint64_t FileEntry::getOffset() const { + return address.getAddressInBytes(); +} + +uint32_t FileEntry::getSize() const { + return size; +} diff --git a/source/WUD/entities/FST/nodeentry/FileEntry.h b/source/WUD/entities/FST/nodeentry/FileEntry.h new file mode 100644 index 0000000..163b1b9 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/FileEntry.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +#include "NodeEntry.h" + +class FileEntry : public NodeEntry { +public: + explicit FileEntry(SectionAddress address); + + static NodeEntry *parseData(uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable, const SectionBlockSize &blockSize); + + ~FileEntry() override = default; + + SectionEntry *getSectionEntry(); + + [[nodiscard]] uint64_t getOffset() const; + + [[nodiscard]] uint32_t getSize() const; + +private: + SectionAddress address; + uint32_t size{}; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/nodeentry/NodeEntries.cpp b/source/WUD/entities/FST/nodeentry/NodeEntries.cpp new file mode 100644 index 0000000..29cf926 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/NodeEntries.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "NodeEntries.h" + +NodeEntries::NodeEntries(RootEntry *pEntry) { + rootEntry = pEntry; +} + +NodeEntries::~NodeEntries() { + delete rootEntry; +} + +NodeEntry *NodeEntries::DeserializeImpl(uint8_t *data, uint32_t offset, DirectoryEntry *parent, uint32_t entryNumber, SectionEntries *sectionEntries, StringTable *stringTable, + const SectionBlockSize &blockSize) { + NodeEntry *nodeEntry = NodeEntry::AutoDeserialize(data, offset, parent, entryNumber, sectionEntries, stringTable, blockSize); + auto asDirEntry = dynamic_cast(nodeEntry); + if (asDirEntry != nullptr) { + uint32_t curEntryNumber = asDirEntry->entryNumber + 1; + while (curEntryNumber < asDirEntry->lastEntryNumber) { + NodeEntry *entry = NodeEntries::DeserializeImpl(data, offset + (curEntryNumber - asDirEntry->entryNumber) * NodeEntry::LENGTH, + asDirEntry, curEntryNumber, sectionEntries, stringTable, blockSize); + asDirEntry->addChild(entry); + auto *childAsDir = dynamic_cast(entry); + if (childAsDir != nullptr) { + curEntryNumber = childAsDir->lastEntryNumber; + } else { + curEntryNumber++; + } + } + } + return nodeEntry; +} + +NodeEntries *NodeEntries::parseData(unsigned char *data, uint32_t offset, SectionEntries *sectionEntries, StringTable *stringTable, const SectionBlockSize &blockSize) { + NodeEntry *rootEntry = NodeEntries::DeserializeImpl(data, offset, (DirectoryEntry *) nullptr, 0, sectionEntries, stringTable, blockSize); + auto rootEntryCasted = dynamic_cast(rootEntry); + if (rootEntryCasted != nullptr) { + return new NodeEntries(rootEntryCasted); + } + OSFatal("Failed to parse Root"); + return nullptr; +} diff --git a/source/WUD/entities/FST/nodeentry/NodeEntries.h b/source/WUD/entities/FST/nodeentry/NodeEntries.h new file mode 100644 index 0000000..aff7041 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/NodeEntries.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include "DirectoryEntry.h" +#include "RootEntry.h" +#include "NodeEntry.h" +#include "NodeEntries.h" + +class NodeEntries { + +public: + explicit NodeEntries(RootEntry *pEntry); + + ~NodeEntries(); + + static NodeEntry * + DeserializeImpl(unsigned char *data, uint32_t offset, DirectoryEntry *parent, uint32_t entryNumber, SectionEntries *sectionEntries, StringTable *stringTable, const SectionBlockSize &blockSize); + + static NodeEntries *parseData(unsigned char *data, uint32_t offset, SectionEntries *sectionEntries, StringTable *stringTable, const SectionBlockSize &blockSize); + + RootEntry *rootEntry; +}; diff --git a/source/WUD/entities/FST/nodeentry/NodeEntry.cpp b/source/WUD/entities/FST/nodeentry/NodeEntry.cpp new file mode 100644 index 0000000..e0261b6 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/NodeEntry.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include +#include +#include "NodeEntry.h" +#include "DirectoryEntry.h" +#include "RootEntry.h" + +uint32_t NodeEntry::LENGTH = 16; + +NodeEntry *NodeEntry::AutoDeserialize(uint8_t *data, uint32_t offset, DirectoryEntry *pParent, uint32_t eEntryNumber, SectionEntries *sectionEntries, + StringTable *stringTable, const SectionBlockSize &blockSize) { + uint8_t *curEntryData = &data[offset]; + + NodeEntryParam param{}; + param.permission = ((uint16_t *) &curEntryData[12])[0]; + param.sectionNumber = ((uint16_t *) &curEntryData[14])[0]; + param.entryNumber = eEntryNumber; + param.parent = pParent; + param.type = curEntryData[0]; + param.uint24 = ((uint32_t *) curEntryData)[0] & 0x00FFFFFF; + + if ((param.type & ENTRY_TYPE_Directory) == ENTRY_TYPE_Directory && param.uint24 == 0) { // Root + return (NodeEntry *) RootEntry::parseData(curEntryData, param, sectionEntries, stringTable); + } else if ((param.type & ENTRY_TYPE_Directory) == ENTRY_TYPE_Directory) { + return (NodeEntry *) DirectoryEntry::parseData(curEntryData, param, sectionEntries, stringTable); + } else if ((param.type & ENTRY_TYPE_File) == ENTRY_TYPE_File) { + return (NodeEntry *) FileEntry::parseData(curEntryData, param, sectionEntries, stringTable, blockSize); + } + + OSFatal("FST Unknown Node Type"); + return nullptr; +} + +std::string NodeEntry::getName() const &{ + if (nameString != nullptr) { + return nameString->toString(); + } + return "ERROR"; +} + +std::string NodeEntry::getFullPathInternal() const &{ + if (parent != nullptr) { + return parent->getFullPathInternal().append("/").append(getName()); + } + return getName(); +} + +std::string NodeEntry::getFullPath() const &{ + return getFullPathInternal(); +} + +std::string NodeEntry::getPath() const &{ + if (parent != nullptr) { + return parent->getFullPath().append("/"); + } + return "/"; +} + +bool NodeEntry::isDirectory() const { + return (entryType & ENTRY_TYPE_Directory) == ENTRY_TYPE_Directory; +} + + +bool NodeEntry::isFile() const { + return (entryType & ENTRY_TYPE_File) == ENTRY_TYPE_File; +} \ No newline at end of file diff --git a/source/WUD/entities/FST/nodeentry/NodeEntry.h b/source/WUD/entities/FST/nodeentry/NodeEntry.h new file mode 100644 index 0000000..7531644 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/NodeEntry.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "NodeEntryParam.h" + +#define ENTRY_TYPE_File 0 +#define ENTRY_TYPE_Directory 1 +#define ENTRY_TYPE_Link 0x80 + +class DirectoryEntry; + +class NodeEntry { + +public: + uint16_t permission{}; + StringEntry *nameString{}; + SectionEntry *sectionEntry{}; + DirectoryEntry *parent{}; + uint8_t entryType{}; + uint32_t entryNumber{}; + + static NodeEntry *AutoDeserialize(uint8_t *data, uint32_t offset, DirectoryEntry *pParent, uint32_t eEntryNumber, SectionEntries *sectionEntries, + StringTable *stringTable, const SectionBlockSize& blockSize); + + virtual ~NodeEntry()= default; + + virtual void printPathRecursive() { + DEBUG_FUNCTION_LINE("%s", getFullPath().c_str()); + } + + [[nodiscard]] std::string getFullPath() const&; + + [[nodiscard]] std::string getPath() const&; + + [[nodiscard]] std::string getName() const&; + + [[nodiscard]] bool isDirectory() const; + + [[nodiscard]] bool isFile() const; + + static uint32_t LENGTH; +private: + [[nodiscard]] std::string getFullPathInternal() const &; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/nodeentry/NodeEntryParam.h b/source/WUD/entities/FST/nodeentry/NodeEntryParam.h new file mode 100644 index 0000000..cdf1a9a --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/NodeEntryParam.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +class DirectoryEntry; + +class NodeEntryParam { +public: + uint16_t sectionNumber; + uint32_t entryNumber; + DirectoryEntry* parent; + uint16_t permission; + uint8_t type; + uint32_t uint24; +}; diff --git a/source/WUD/entities/FST/nodeentry/RootEntry.cpp b/source/WUD/entities/FST/nodeentry/RootEntry.cpp new file mode 100644 index 0000000..c46ee25 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/RootEntry.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "RootEntry.h" + +RootEntry::RootEntry(DirectoryEntry *input) { + if ((input->entryType & ENTRY_TYPE_Directory) != ENTRY_TYPE_Directory || input->entryNumber != 0) { + OSFatal("Input is no root entry."); + } + + entryNumber = input->entryNumber; + parent = input->parent; + nameString = input->nameString; + if(nameString == nullptr){ + OSFatal("nameString was null"); + } + entryType = input->entryType; + + parentEntryNumber = input->parentEntryNumber; + lastEntryNumber = input->lastEntryNumber; + permission = input->permission; + sectionEntry = input->sectionEntry; +} + +uint32_t RootEntry::parseLastEntryNumber(uint8_t *data, uint32_t offset) { + return ((uint32_t *) &data[8 + offset])[0]; +} + +RootEntry *RootEntry::parseData(uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable) { + return new RootEntry(DirectoryEntry::parseData(data, param, sectionEntries, stringTable)); +} diff --git a/source/WUD/entities/FST/nodeentry/RootEntry.h b/source/WUD/entities/FST/nodeentry/RootEntry.h new file mode 100644 index 0000000..6977d98 --- /dev/null +++ b/source/WUD/entities/FST/nodeentry/RootEntry.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include "DirectoryEntry.h" + +class RootEntry : public DirectoryEntry { + + explicit RootEntry(DirectoryEntry *input); + +public: + + ~RootEntry() override = default; + + static RootEntry *parseData(uint8_t *data, NodeEntryParam param, SectionEntries *sectionEntries, StringTable *stringTable); + + static uint32_t parseLastEntryNumber(uint8_t *data, uint32_t offset); +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/sectionentry/SectionEntries.cpp b/source/WUD/entities/FST/sectionentry/SectionEntries.cpp new file mode 100644 index 0000000..3179fd1 --- /dev/null +++ b/source/WUD/entities/FST/sectionentry/SectionEntries.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "SectionEntries.h" + +SectionEntries::SectionEntries(uint8_t *data, uint32_t numberOfSections, const VolumeBlockSize &pBlockSize) { + for (uint32_t i = 0; i < numberOfSections; i++) { + list.push_back(new SectionEntry(data + (i * 32), i, pBlockSize)); + } +} + +SectionEntry *SectionEntries::getSection(uint16_t sectionNumber) const { + for (auto const &e: list) { + if (e->sectionNumber == sectionNumber) { + return e; + } + } + return nullptr; +} + +uint32_t SectionEntries::getSizeInBytes() const { + return list.size() * 32; +} + +uint32_t SectionEntries::size() const { + return list.size(); +} diff --git a/source/WUD/entities/FST/sectionentry/SectionEntries.h b/source/WUD/entities/FST/sectionentry/SectionEntries.h new file mode 100644 index 0000000..e4299b9 --- /dev/null +++ b/source/WUD/entities/FST/sectionentry/SectionEntries.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include "SectionEntry.h" + +class SectionEntries { + +public: + SectionEntries(uint8_t *data, uint32_t numberOfSections, const VolumeBlockSize &pBlockSize); + + [[nodiscard]] uint32_t getSizeInBytes() const; + + [[nodiscard]] uint32_t size() const; + + [[nodiscard]] SectionEntry *getSection(uint16_t sectionNumber) const; + + [[nodiscard]] std::vector getSections() const &{ + return list; + } + +private: + std::vector list; +}; \ No newline at end of file diff --git a/source/WUD/entities/FST/sectionentry/SectionEntry.cpp b/source/WUD/entities/FST/sectionentry/SectionEntry.cpp new file mode 100644 index 0000000..3e770dd --- /dev/null +++ b/source/WUD/entities/FST/sectionentry/SectionEntry.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "SectionEntry.h" + +SectionEntry::SectionEntry(uint8_t *data, uint32_t pSectionNumber, const VolumeBlockSize& pBlockSize) { + auto *dataAsUint = (uint32_t *) data; + address = AddressInVolumeBlocks(pBlockSize, dataAsUint[0]); + size = SizeInVolumeBlocks(pBlockSize, dataAsUint[1]); + + ownerID = ((uint64_t *) (&data[8]))[0]; + groupID = ((uint64_t *) (&data[16]))[0]; + hashMode = data[20]; + char buff[32]; + snprintf(buff, sizeof(buff), "Section: %d", pSectionNumber); + + name = std::string(buff); + sectionNumber = pSectionNumber; +} diff --git a/source/WUD/entities/FST/sectionentry/SectionEntry.h b/source/WUD/entities/FST/sectionentry/SectionEntry.h new file mode 100644 index 0000000..2a79063 --- /dev/null +++ b/source/WUD/entities/FST/sectionentry/SectionEntry.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include + +class SectionEntry { +public: + SectionEntry(uint8_t *data, uint32_t pSectionNumber, const VolumeBlockSize &pBlockSize); + + ~SectionEntry() = default; + + AddressInVolumeBlocks address; + SizeInVolumeBlocks size; + uint8_t hashMode; + uint64_t ownerID; + uint64_t groupID; + std::string name; + uint32_t sectionNumber; +}; diff --git a/source/WUD/entities/FST/stringtable/StringEntry.cpp b/source/WUD/entities/FST/stringtable/StringEntry.cpp new file mode 100644 index 0000000..cbce6e1 --- /dev/null +++ b/source/WUD/entities/FST/stringtable/StringEntry.cpp @@ -0,0 +1,27 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "StringEntry.h" +#include "StringTable.h" + +std::string StringEntry::toString() const{ + return stringTable->getByAddress(offset); +} + +StringEntry::StringEntry(StringTable *pTable, uint32_t pOffset) { + stringTable = pTable; + offset = pOffset; +} diff --git a/source/WUD/entities/FST/stringtable/StringEntry.h b/source/WUD/entities/FST/stringtable/StringEntry.h new file mode 100644 index 0000000..0569d7e --- /dev/null +++ b/source/WUD/entities/FST/stringtable/StringEntry.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class StringTable; + +class StringEntry { + +public: + StringEntry(StringTable *pTable, uint32_t pOffset); + + [[nodiscard]] std::string toString() const; + + StringTable *stringTable; + uint32_t offset; +}; diff --git a/source/WUD/entities/FST/stringtable/StringTable.cpp b/source/WUD/entities/FST/stringtable/StringTable.cpp new file mode 100644 index 0000000..a81f7dd --- /dev/null +++ b/source/WUD/entities/FST/stringtable/StringTable.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include +#include "StringTable.h" + +StringTable *StringTable::parseData(uint8_t *data, uint32_t dataLength, uint32_t offset, uint32_t stringCount) { + auto *stringTable = new StringTable(); + uint32_t curOffset = 0; + uint32_t i; + for (i = 0; curOffset < dataLength && i < stringCount; ++curOffset) { + if (data[offset + curOffset] == (uint8_t) 0) { + ++i; + } + } + + if (i < stringCount) { + OSFatal("stringtable is broken"); + } + + uint32_t curLength = 0; + for (i = 0; i < stringCount; ++i) { + curOffset = offset + curLength; + stringTable->stringMap[curLength] = new StringEntry(stringTable, curLength); + stringTable->strings[curLength] = (char *) &data[curOffset]; + + curLength += strlen((char *) &data[curOffset]) + 1; + } + + return stringTable; +} + +std::string StringTable::getByAddress(uint32_t address) { + return strings[address]; +} + +StringEntry *StringTable::getStringEntry(uint32_t address) { + return stringMap[address]; +} + +uint32_t StringTable::getSize() { + uint32_t capacity = 1; // root entry + for (auto &cur: strings) { + capacity += cur.second.length() + 1; + } + return capacity; +} + +StringEntry *StringTable::getEntry(std::string &str) { + for (auto &cur: strings) { + if (cur.second == str) { + return stringMap[cur.first]; + } + } + + return nullptr; +} diff --git a/source/WUD/entities/FST/stringtable/StringTable.h b/source/WUD/entities/FST/stringtable/StringTable.h new file mode 100644 index 0000000..f9f85ef --- /dev/null +++ b/source/WUD/entities/FST/stringtable/StringTable.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include "StringEntry.h" + +class StringTable { + +public: + ~StringTable() { + for (auto &cur: stringMap) { + delete cur.second; + } + stringMap.clear(); + strings.clear(); + } + + static StringTable *parseData(uint8_t *data, uint32_t dataLength, uint32_t offset, uint32_t stringCount); + + std::string getByAddress(uint32_t address); + + StringEntry *getStringEntry(uint32_t address); + + uint32_t getSize(); + + StringEntry *getEntry(std::string &str); + +private: + std::map stringMap; + std::map strings; +}; \ No newline at end of file diff --git a/source/WUD/entities/TMD/Content.cpp b/source/WUD/entities/TMD/Content.cpp new file mode 100644 index 0000000..c08c643 --- /dev/null +++ b/source/WUD/entities/TMD/Content.cpp @@ -0,0 +1,28 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include +#include "Content.h" + +uint32_t Content::LENGTH = 0x30; + +Content::Content(uint8_t *data) { + ID = ((uint32_t *) &data[0x00])[0]; + index = ((uint16_t *) &data[0x04])[0]; + type = ((uint16_t *) &data[0x06])[0]; + encryptedFileSize = ((uint64_t *) &data[0x08])[0]; + memcpy(hash, &data[0x10], 0x14); +} diff --git a/source/WUD/entities/TMD/Content.h b/source/WUD/entities/TMD/Content.h new file mode 100644 index 0000000..1d3c3fc --- /dev/null +++ b/source/WUD/entities/TMD/Content.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include + +class Content { +public: + static uint32_t LENGTH; + + explicit Content(uint8_t *data); + + uint16_t index; + uint32_t ID; + uint16_t type; + uint64_t encryptedFileSize; + uint8_t hash[0x14]{}; +}; diff --git a/source/WUD/entities/TMD/TitleMetaData.cpp b/source/WUD/entities/TMD/TitleMetaData.cpp new file mode 100644 index 0000000..1105770 --- /dev/null +++ b/source/WUD/entities/TMD/TitleMetaData.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "TitleMetaData.h" + +TitleMetaData::TitleMetaData(uint8_t *data) { + contentCount = ((uint16_t *) &data[0x1DE])[0]; + // Get Contents + for (uint16_t i = 0; i < contentCount; i++) { + auto curOffset = 0xB04 + (i * Content::LENGTH); + auto *c = new Content((uint8_t *) &data[curOffset]); + contentList.push_back(c); + } +} + +TitleMetaData::~TitleMetaData() { + for (auto &content: contentList) { + delete content; + } +} + +Content* TitleMetaData::getContentByIndex(uint16_t i) { + for (auto &content: contentList) { + if(content->index == i){ + return content; + } + } + return nullptr; +} diff --git a/source/WUD/entities/TMD/TitleMetaData.h b/source/WUD/entities/TMD/TitleMetaData.h new file mode 100644 index 0000000..7d8b542 --- /dev/null +++ b/source/WUD/entities/TMD/TitleMetaData.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include "Content.h" + +class TitleMetaData { +public: + explicit TitleMetaData(uint8_t *data); + + ~TitleMetaData(); + + std::vector contentList; + + Content* getContentByIndex(uint16_t index); + +private: + uint16_t contentCount; +}; diff --git a/source/WUD/header/WiiUDiscHeader.cpp b/source/WUD/header/WiiUDiscHeader.cpp new file mode 100644 index 0000000..6625ce9 --- /dev/null +++ b/source/WUD/header/WiiUDiscHeader.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "WiiUDiscHeader.h" +#include + +uint32_t WiiUDiscHeader::LENGTH = 131072L; + +WiiUDiscHeader::WiiUDiscHeader(DiscReader *reader, uint32_t offset) { + uint32_t curOffset = offset; + manufactorDiscID = new WiiUManufactorDiscID(reader, 0); + curOffset += WiiUManufactorDiscID::LENGTH; + discId = new WiiUDiscID(reader, curOffset); + curOffset += WiiUDiscID::LENGTH; + wiiUContentsInformation = new WiiUContentsInformation(reader, curOffset); + curOffset += WiiUContentsInformation::LENGTH; + + if (curOffset - offset != LENGTH) { + OSFatal("Length mismatch"); + } +} + +WiiUDiscHeader::~WiiUDiscHeader() { + delete manufactorDiscID; + delete discId; + delete wiiUContentsInformation; +} diff --git a/source/WUD/header/WiiUDiscHeader.h b/source/WUD/header/WiiUDiscHeader.h new file mode 100644 index 0000000..45b9429 --- /dev/null +++ b/source/WUD/header/WiiUDiscHeader.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include "WiiUManufactorDiscID.h" +#include "WiiUDiscID.h" + +class WiiUDiscHeader { + +public: + static uint32_t LENGTH; + + WiiUDiscHeader(DiscReader *reader, uint32_t offset); + + WiiUManufactorDiscID *manufactorDiscID = nullptr; + WiiUDiscID *discId = nullptr; + WiiUContentsInformation *wiiUContentsInformation = nullptr; + + ~WiiUDiscHeader(); +}; \ No newline at end of file diff --git a/source/WUD/header/WiiUDiscID.cpp b/source/WUD/header/WiiUDiscID.cpp new file mode 100644 index 0000000..ea585bf --- /dev/null +++ b/source/WUD/header/WiiUDiscID.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "WiiUDiscID.h" +#include + +uint32_t WiiUDiscID::LENGTH = 32768; +uint32_t WiiUDiscID::MAGIC = 0xCC549EB9; + +WiiUDiscID::WiiUDiscID(DiscReader *reader, uint32_t offset) { + auto data = (uint8_t *) malloc(LENGTH); + if (data == nullptr) { + OSFatal("Failed to alloc for WiiUDiscID"); + } + + if (!reader->readEncrypted(data, offset, LENGTH)) { + OSFatal("Failed to read data"); + } + + if (((uint32_t *) data)[0] != MAGIC) { + OSFatal("MAGIC FAIL"); + } + + majorVersion = data[5]; + minorVersion = data[6]; + + footprint = std::string((char *) &data[32]); + + free(data); +} diff --git a/source/WUD/header/WiiUDiscID.h b/source/WUD/header/WiiUDiscID.h new file mode 100644 index 0000000..3e7b54c --- /dev/null +++ b/source/WUD/header/WiiUDiscID.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class WiiUDiscID { + +public: + WiiUDiscID(DiscReader *reader, uint32_t offset); + + static uint32_t LENGTH; + static uint32_t MAGIC; + uint8_t majorVersion; + uint8_t minorVersion; + std::string footprint; +}; diff --git a/source/WUD/header/WiiUManufactorDiscID.cpp b/source/WUD/header/WiiUManufactorDiscID.cpp new file mode 100644 index 0000000..ecda868 --- /dev/null +++ b/source/WUD/header/WiiUManufactorDiscID.cpp @@ -0,0 +1,26 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#include "WiiUManufactorDiscID.h" +#include + +uint32_t WiiUManufactorDiscID::LENGTH = 65536; + +WiiUManufactorDiscID::WiiUManufactorDiscID(DiscReader *reader, uint32_t offset) { + if(!reader->readEncrypted(data, offset, LENGTH)){ + OSFatal("WiiUManufactorDiscID: read failed"); + } +} diff --git a/source/WUD/header/WiiUManufactorDiscID.h b/source/WUD/header/WiiUManufactorDiscID.h new file mode 100644 index 0000000..f729124 --- /dev/null +++ b/source/WUD/header/WiiUManufactorDiscID.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include + +class WiiUManufactorDiscID { + +public: + WiiUManufactorDiscID(DiscReader *reader, uint32_t offset); + + static uint32_t LENGTH; +private: + uint8_t data[65536]{}; +}; diff --git a/source/common/common.cpp b/source/common/common.cpp new file mode 100644 index 0000000..e20c3cc --- /dev/null +++ b/source/common/common.cpp @@ -0,0 +1,3 @@ +#include "common.h" + +int32_t gFSAfd = -1; \ No newline at end of file diff --git a/source/common/common.h b/source/common/common.h new file mode 100644 index 0000000..c400edb --- /dev/null +++ b/source/common/common.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern int32_t gFSAfd; \ No newline at end of file diff --git a/source/fs/CFile.cpp b/source/fs/CFile.cpp new file mode 100644 index 0000000..c562f11 --- /dev/null +++ b/source/fs/CFile.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#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; +} + + diff --git a/source/fs/CFile.hpp b/source/fs/CFile.hpp new file mode 100644 index 0000000..6c0421b --- /dev/null +++ b/source/fs/CFile.hpp @@ -0,0 +1,71 @@ +#ifndef CFILE_HPP_ +#define CFILE_HPP_ + +#include +#include +#include +#include +#include +#include + +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 diff --git a/source/fs/FSUtils.cpp b/source/fs/FSUtils.cpp new file mode 100644 index 0000000..59c4efa --- /dev/null +++ b/source/fs/FSUtils.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#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; +} + diff --git a/source/fs/FSUtils.h b/source/fs/FSUtils.h new file mode 100644 index 0000000..1e7160f --- /dev/null +++ b/source/fs/FSUtils.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +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); +}; \ No newline at end of file diff --git a/source/input/Input.h b/source/input/Input.h new file mode 100644 index 0000000..14a2192 --- /dev/null +++ b/source/input/Input.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +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{}; +}; diff --git a/source/input/VPADInput.h b/source/input/VPADInput.h new file mode 100644 index 0000000..6ac73cf --- /dev/null +++ b/source/input/VPADInput.h @@ -0,0 +1,59 @@ +#pragma once +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#include +#include "Input.h" + +class VPadInput : public Input { +public: + //!Constructor + VPadInput() { + memset(&vpad, 0, sizeof(vpad)); + } + + //!Destructor + ~VPadInput() override {} + + bool update(int32_t width, int32_t height) { + lastData = data; + + VPADReadError vpadError = VPAD_READ_NO_SAMPLES; + VPADRead(VPAD_CHAN_0, &vpad, 1, &vpadError); + + if (vpadError == VPAD_READ_SUCCESS) { + data.buttons_r = vpad.release; + data.buttons_h = vpad.hold; + data.buttons_d = vpad.trigger; + data.validPointer = !vpad.tpNormal.validity; + data.touched = vpad.tpNormal.touched; + + VPADGetTPCalibratedPoint(VPAD_CHAN_0, &tpCalib, &vpad.tpFiltered1); + + //! calculate the screen offsets + data.x = -(width >> 1) + (int32_t) (((float) tpCalib.x / 1280.0f) * (float) width); + data.y = -(height >> 1) + (int32_t) (float) height - (((float) tpCalib.y / 720.0f) * (float) height); + + return true; + } + return false; + } + +private: + VPADStatus vpad{}; + VPADTouchData tpCalib{}; +}; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..9f65c43 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,108 @@ +#include +#include +#include + +#include + +#include +#include + +#include "utils/logger.h" +#include "utils/WiiUScreen.h" +#include "input/VPADInput.h" +#include "ApplicationState.h" +#include "common/common.h" +#include "utils/utils.h" + +void initIOSUHax(); + +void deInitIOSUHax(); + +void main_loop(); + +bool sIosuhaxMount = false; + + +ntfs_md *ntfs_mounts = nullptr; +int ntfs_mount_count = 0; + +int main(int argc, char **argv) { + WHBLogUdpInit(); + DEBUG_FUNCTION_LINE("Hello from wudump!"); + WHBProcInit(); + WiiUScreen::Init(); + + initIOSUHax(); + + //DEBUG_FUNCTION_LINE("init fat"); + //fatInitDefault(); + + ntfs_mount_count = ntfsMountAll((ntfs_md **) &ntfs_mounts, NTFS_DEFAULT | NTFS_RECOVER); + + for (int i = 0; i < ntfs_mount_count; i++) { + DEBUG_FUNCTION_LINE("%s:", ntfs_mounts[i].name); + } + + main_loop(); + + if (ntfs_mounts != nullptr) { + int i = 0; + for (i = 0; i < ntfs_mount_count; i++) { + ntfsUnmount(ntfs_mounts[i].name, true); + } + free(ntfs_mounts); + } + + deInitIOSUHax(); + + WiiUScreen::DeInit(); + WHBProcShutdown(); + + return 0; +} + +void main_loop() { + DEBUG_FUNCTION_LINE("Creating state"); + ApplicationState state; + VPadInput input; + + DEBUG_FUNCTION_LINE(); + + if (gFSAfd < 0 || !sIosuhaxMount) { + DEBUG_FUNCTION_LINE(); + state.setError(ApplicationState::eErrorState::ERROR_IOSUHAX_FAILED); + } + + DEBUG_FUNCTION_LINE("Entering main loop"); + while (WHBProcIsRunning()) { + input.update(1280, 720); + state.update(&input); + state.render(); + } + exit(0); +} + +void initIOSUHax() { + sIosuhaxMount = false; + int res = IOSUHAX_Open(nullptr); + if (res < 0) { + DEBUG_FUNCTION_LINE("IOSUHAX_open failed"); + } else { + sIosuhaxMount = true; + gFSAfd = IOSUHAX_FSA_Open(); + if (gFSAfd < 0) { + DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open failed"); + } else { + } + DEBUG_FUNCTION_LINE("IOSUHAX done"); + } +} + +void deInitIOSUHax() { + if (sIosuhaxMount) { + if (gFSAfd >= 0) { + IOSUHAX_FSA_Close(gFSAfd); + } + IOSUHAX_Close(); + } +} \ No newline at end of file diff --git a/source/utils/FSTUtils.cpp b/source/utils/FSTUtils.cpp new file mode 100644 index 0000000..fbdaa38 --- /dev/null +++ b/source/utils/FSTUtils.cpp @@ -0,0 +1,61 @@ + +#include "FSTUtils.h" +#include "logger.h" +#include + +NodeEntry *FSTUtils::getFSTEntryByFullPath(DirectoryEntry *root, std::string &givenFullPath) { + std::string fullPath = givenFullPath; + if (strncmp(fullPath.c_str(), "/", 1) != 0) { + fullPath = "/" + fullPath; + } + + DirectoryEntry *pathOpt = root; + std::filesystem::path asPath = fullPath; + std::string dirPath = asPath.parent_path().string(); + if (dirPath != "/") { + pathOpt = getFileEntryDir(root, dirPath); + } + if (pathOpt == nullptr) { + return nullptr; + } + for (auto &child: pathOpt->getChildren()) { + if (child->getFullPath() == fullPath) { + return child; + } + } + + return nullptr; +} + +DirectoryEntry *FSTUtils::getFileEntryDir(DirectoryEntry *curEntry, std::string &string) { + // We add the "/" at the end so we don't get false results when using the "startWith" function. + if (!string.ends_with("/")) { + string += "/"; + } + for (auto &curChild: curEntry->getDirChildren()) { + std::string compareTo = curChild->getFullPath(); + if (!compareTo.ends_with("/")) { + compareTo += "/"; + } + if (string.starts_with(compareTo)) { + if (string == compareTo) { + return curChild; + } + return getFileEntryDir(curChild, string); + } + } + return nullptr; +} + +SectionEntry *FSTUtils::getSectionEntryForIndex(FST *pFst, uint16_t index) { + if (pFst == nullptr || pFst->sectionEntries == nullptr) { + return nullptr; + } + + for (const auto &entry: pFst->sectionEntries->getSections()) { + if (entry->sectionNumber == index) { + return entry; + } + } + return nullptr; +} diff --git a/source/utils/FSTUtils.h b/source/utils/FSTUtils.h new file mode 100644 index 0000000..ddf9e8c --- /dev/null +++ b/source/utils/FSTUtils.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include + +class FSTUtils{ +public: + static NodeEntry *getFSTEntryByFullPath(DirectoryEntry *root, std::string &givenFullPath); + + static DirectoryEntry *getFileEntryDir(DirectoryEntry *curEntry, std::string &string); + + static SectionEntry *getSectionEntryForIndex(FST *pFst, uint16_t index); +}; + diff --git a/source/utils/ScreenUtils.cpp b/source/utils/ScreenUtils.cpp new file mode 100644 index 0000000..3fe9090 --- /dev/null +++ b/source/utils/ScreenUtils.cpp @@ -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 . + ****************************************************************************/ +#include +#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); + } +} diff --git a/source/utils/ScreenUtils.h b/source/utils/ScreenUtils.h new file mode 100644 index 0000000..1428049 --- /dev/null +++ b/source/utils/ScreenUtils.h @@ -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 . + ****************************************************************************/ +#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; +}; \ No newline at end of file diff --git a/source/utils/StringTools.cpp b/source/utils/StringTools.cpp new file mode 100644 index 0000000..f313e3e --- /dev/null +++ b/source/utils/StringTools.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 StringTools::stringSplit(const std::string &inValue, const std::string &splitter) { + std::string value = inValue; + std::vector 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); }); +} \ No newline at end of file diff --git a/source/utils/StringTools.h b/source/utils/StringTools.h new file mode 100644 index 0000000..450d760 --- /dev/null +++ b/source/utils/StringTools.h @@ -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 +#include +#include +#include +#include + +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 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); +}; diff --git a/source/utils/TinySHA1.hpp b/source/utils/TinySHA1.hpp new file mode 100644 index 0000000..70af046 --- /dev/null +++ b/source/utils/TinySHA1.hpp @@ -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 + * + * 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 +#include +#include +#include +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(start); + const uint8_t* finish = static_cast(end); + while(begin != finish) { + processByte(*begin); + begin++; + } + return *this; + } + SHA1& processBytes(const void* const data, size_t len) { + const uint8_t* block = static_cast(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((bitCount>>24) & 0xFF)); + processByte( static_cast((bitCount>>16) & 0xFF)); + processByte( static_cast((bitCount>>8 ) & 0xFF)); + processByte( static_cast((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 diff --git a/source/utils/WiiUScreen.cpp b/source/utils/WiiUScreen.cpp new file mode 100644 index 0000000..b6e9260 --- /dev/null +++ b/source/utils/WiiUScreen.cpp @@ -0,0 +1,94 @@ +#include "WiiUScreen.h" +#include "ScreenUtils.h" +#include +#include +#include + +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; +} diff --git a/source/utils/WiiUScreen.h b/source/utils/WiiUScreen.h new file mode 100644 index 0000000..1b7dbe6 --- /dev/null +++ b/source/utils/WiiUScreen.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#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; +}; \ No newline at end of file diff --git a/source/utils/blocksize/AddressInBlocks.h b/source/utils/blocksize/AddressInBlocks.h new file mode 100644 index 0000000..18917bf --- /dev/null +++ b/source/utils/blocksize/AddressInBlocks.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "BlockSize.h" + +class AddressInBlocks { +public: + explicit AddressInBlocks(const BlockSize &blockSize, uint32_t pValue) : blockSize(blockSize) { + value = pValue; + } + + AddressInBlocks() = default; + + [[nodiscard]] virtual uint64_t getAddressInBytes() const { + return (uint64_t) value * blockSize.blockSize; + } + + BlockSize blockSize; + uint32_t value{}; +}; \ No newline at end of file diff --git a/source/utils/blocksize/AddressInDiscBlocks.h b/source/utils/blocksize/AddressInDiscBlocks.h new file mode 100644 index 0000000..b2b6d7d --- /dev/null +++ b/source/utils/blocksize/AddressInDiscBlocks.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include + +#include "AddressInBlocks.h" +#include "DiscBlockSize.h" +#include +#include + +class AddressInDiscBlocks : public AddressInBlocks { + +public: + AddressInDiscBlocks(const DiscBlockSize &blockSize, uint32_t value) : AddressInBlocks(blockSize, value) { + } +}; + + +namespace std { + template<> + struct less { + bool operator()(const AddressInDiscBlocks &lhs, const AddressInDiscBlocks &rhs) const { + return lhs.getAddressInBytes() < rhs.getAddressInBytes(); + } + }; +} diff --git a/source/utils/blocksize/AddressInVolumeBlocks.h b/source/utils/blocksize/AddressInVolumeBlocks.h new file mode 100644 index 0000000..368331c --- /dev/null +++ b/source/utils/blocksize/AddressInVolumeBlocks.h @@ -0,0 +1,28 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include "VolumeBlockSize.h" +#include "AddressInBlocks.h" + +class AddressInVolumeBlocks : public AddressInBlocks { + +public: + AddressInVolumeBlocks() = default; + AddressInVolumeBlocks(const VolumeBlockSize &pBlockSize, uint32_t pValue) : AddressInBlocks(pBlockSize, pValue) { + } +}; \ No newline at end of file diff --git a/source/utils/blocksize/BlockSize.h b/source/utils/blocksize/BlockSize.h new file mode 100644 index 0000000..7692079 --- /dev/null +++ b/source/utils/blocksize/BlockSize.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include + +class BlockSize { +public: + BlockSize() = default; + + explicit BlockSize(uint32_t blockSize) { + this->blockSize = blockSize; + } + + uint32_t blockSize {}; +}; \ No newline at end of file diff --git a/source/utils/blocksize/DiscBlockSize.h b/source/utils/blocksize/DiscBlockSize.h new file mode 100644 index 0000000..4e8ef77 --- /dev/null +++ b/source/utils/blocksize/DiscBlockSize.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include "utils/logger.h" +#include "BlockSize.h" + +class DiscBlockSize : public BlockSize { + +public: + DiscBlockSize() = default; + explicit DiscBlockSize(uint32_t blockSize) : BlockSize(blockSize) { + + } +}; \ No newline at end of file diff --git a/source/utils/blocksize/SectionAddress.h b/source/utils/blocksize/SectionAddress.h new file mode 100644 index 0000000..b76efda --- /dev/null +++ b/source/utils/blocksize/SectionAddress.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ + #pragma once + +#include "AddressInBlocks.h" +#include "SectionBlockSize.h" + +class SectionAddress : public AddressInBlocks { + +public: + SectionAddress(const SectionBlockSize &pBlockSize, uint32_t pValue) : AddressInBlocks(pBlockSize, pValue) { + } +}; diff --git a/source/utils/blocksize/SectionBlockSize.h b/source/utils/blocksize/SectionBlockSize.h new file mode 100644 index 0000000..a6807d1 --- /dev/null +++ b/source/utils/blocksize/SectionBlockSize.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "BlockSize.h" + +class SectionBlockSize : public BlockSize { + +public: + SectionBlockSize() = default; + + explicit SectionBlockSize(uint32_t pBlockSize) : BlockSize(pBlockSize) { + } +}; \ No newline at end of file diff --git a/source/utils/blocksize/SizeInBlocks.h b/source/utils/blocksize/SizeInBlocks.h new file mode 100644 index 0000000..fd3dc69 --- /dev/null +++ b/source/utils/blocksize/SizeInBlocks.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include +#include "BlockSize.h" + +class SizeInBlocks { +public: + SizeInBlocks() = default; + + explicit SizeInBlocks(const BlockSize &blockSize, uint32_t pValue) : blockSize(blockSize) { + value = pValue; + } + + virtual uint64_t getSizeInBytes() { + return (uint64_t) value * blockSize.blockSize; + } + + BlockSize blockSize; + uint64_t value{}; +}; \ No newline at end of file diff --git a/source/utils/blocksize/SizeInVolumeBlocks.h b/source/utils/blocksize/SizeInVolumeBlocks.h new file mode 100644 index 0000000..4a20023 --- /dev/null +++ b/source/utils/blocksize/SizeInVolumeBlocks.h @@ -0,0 +1,14 @@ +#pragma once + +#include "VolumeBlockSize.h" +#include "SizeInBlocks.h" + +class SizeInVolumeBlocks : public SizeInBlocks { + +public: + SizeInVolumeBlocks() = default; + + SizeInVolumeBlocks(const VolumeBlockSize &blockSize, uint32_t value) : SizeInBlocks(blockSize, value) { + + } +}; \ No newline at end of file diff --git a/source/utils/blocksize/VolumeBlockSize.h b/source/utils/blocksize/VolumeBlockSize.h new file mode 100644 index 0000000..cd3b9cf --- /dev/null +++ b/source/utils/blocksize/VolumeBlockSize.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2016-2021 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 . + ****************************************************************************/ +#pragma once + +#include +#include "BlockSize.h" + +class VolumeBlockSize : public BlockSize { + +public: + VolumeBlockSize() = default; + + explicit VolumeBlockSize(uint32_t blockSize) : BlockSize(blockSize) { + } +}; \ No newline at end of file diff --git a/source/utils/logger.h b/source/utils/logger.h new file mode 100644 index 0000000..750c908 --- /dev/null +++ b/source/utils/logger.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#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 \ No newline at end of file diff --git a/source/utils/rijndael.c b/source/utils/rijndael.c new file mode 100644 index 0000000..db48c10 --- /dev/null +++ b/source/utils/rijndael.c @@ -0,0 +1,394 @@ +/* Rijndael Block Cipher - rijndael.c + + Written by Mike Scott 21st April 1999 + mike@compapp.dcu.ie + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + +*/ + +#include +#include + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 byte */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static unsigned char InCo[4]={0xB,0xD,0x9,0xE}; /* Inverse Coefficients */ + +static unsigned char fbsub[256]; +static unsigned char rbsub[256]; +static unsigned char ptab[256],ltab[256]; +static unsigned int ftable[256]; +static unsigned int rtable[256]; +static unsigned int rco[30]; + +/* Parameter-dependent data */ + +int Nk,Nb,Nr; +unsigned char fi[24],ri[24]; +unsigned int fkey[120]; +unsigned int rkey[120]; + +static unsigned int pack(unsigned char *b) +{ /* pack bytes into a 32-bit Word */ + return ((unsigned int)b[3]<<24)|((unsigned int)b[2]<<16)|((unsigned int)b[1]<<8)|(unsigned int)b[0]; +} + +static void unpack(unsigned int a,unsigned char *b) +{ /* unpack bytes from a word */ + b[0]=(unsigned char)a; + b[1]=(unsigned char)(a>>8); + b[2]=(unsigned char)(a>>16); + b[3]=(unsigned char)(a>>24); +} + +static unsigned char xtime(unsigned char a) +{ + unsigned char b; + if (a&0x80) b=0x1B; + else b=0; + a<<=1; + a^=b; + return a; +} + +static unsigned char bmul(unsigned char x,unsigned char y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) return ptab[(ltab[x]+ltab[y])%255]; + else return 0; +} + +static unsigned int SubByte(unsigned int a) +{ + unsigned char b[4]; + unpack(a,b); + b[0]=fbsub[b[0]]; + b[1]=fbsub[b[1]]; + b[2]=fbsub[b[2]]; + b[3]=fbsub[b[3]]; + return pack(b); +} + +static unsigned char product(unsigned int x,unsigned int y) +{ /* dot product of two 4-byte arrays */ + unsigned char xb[4],yb[4]; + unpack(x,xb); + unpack(y,yb); + return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]); +} + +static unsigned int InvMixCol(unsigned int x) +{ /* matrix Multiplication */ + unsigned int y,m; + unsigned char b[4]; + + m=pack(InCo); + b[3]=product(m,x); + m=ROTL24(m); + b[2]=product(m,x); + m=ROTL24(m); + b[1]=product(m,x); + m=ROTL24(m); + b[0]=product(m,x); + y=pack(b); + return y; +} + +unsigned char ByteSub(unsigned char x) +{ + unsigned char y=ptab[255-ltab[x]]; /* multiplicative inverse */ + x=y; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; y^=0x63; + return y; +} + +void gentables(void) +{ /* generate tables */ + int i; + unsigned char y,b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0]=0; + ptab[0]=1; ltab[1]=0; + ptab[1]=3; ltab[3]=1; + for (i=2;i<256;i++) + { + ptab[i]=ptab[i-1]^xtime(ptab[i-1]); + ltab[ptab[i]]=i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0]=0x63; + rbsub[0x63]=0; + for (i=1;i<256;i++) + { + y=ByteSub((unsigned char)i); + fbsub[i]=y; rbsub[y]=i; + } + + for (i=0,y=1;i<30;i++) + { + rco[i]=y; + y=xtime(y); + } + + /* calculate forward and reverse tables */ + for (i=0;i<256;i++) + { + y=fbsub[i]; + b[3]=y^xtime(y); b[2]=y; + b[1]=y; b[0]=xtime(y); + ftable[i]=pack(b); + + y=rbsub[i]; + b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y); + b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y); + rtable[i]=pack(b); + } +} + +void gkey(int nb,int nk,unsigned char *key) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i,j,k,m,N; + int C1,C2,C3; + unsigned int CipherKey[8]; + + Nb=nb; Nk=nk; + + /* Nr is number of rounds */ + if (Nb>=Nk) Nr=6+Nb; + else Nr=6+Nk; + + C1=1; + if (Nb<8) { C2=2; C3=3; } + else { C2=3; C3=4; } + + /* pre-calculate forward and reverse increments */ + for (m=j=0;j>8)])^ + ROTL16(ftable[(unsigned char)(x[fi[m+1]]>>16)])^ + ROTL24(ftable[x[fi[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((unsigned int)fbsub[(unsigned char)(x[fi[m+1]]>>16)])^ + ROTL24((unsigned int)fbsub[x[fi[m+2]]>>24]); + } + for (i=j=0;i>8)])^ + ROTL16(rtable[(unsigned char)(x[ri[m+1]]>>16)])^ + ROTL24(rtable[x[ri[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((unsigned int)rbsub[(unsigned char)(x[ri[m+1]]>>16)])^ + ROTL24((unsigned int)rbsub[x[ri[m+2]]>>24]); + } + for (i=j=0;i +#include +#include +#include "logger.h" +#include "utils.h" +#include "TinySHA1.hpp" +#include "../fs/FSUtils.h" + +// 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_WRITE("0x%08X (0x0000): ", data); + for (i = 0; i < size; ++i) { + WHBLogWritef("%02X ", ((unsigned char *) data)[i]); + if (((unsigned char *) data)[i] >= ' ' && ((unsigned char *) data)[i] <= '~') { + ascii[i % 16] = ((unsigned char *) data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i + 1) % 8 == 0 || i + 1 == size) { + WHBLogWritef(" "); + if ((i + 1) % 16 == 0) { + WHBLogPrintf("| %s ", ascii); + if (i + 1 < size) { + DEBUG_FUNCTION_LINE_WRITE("0x%08X (0x%04X); ", ((uint32_t) 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(data), size); + free(data); + return result; +} diff --git a/source/utils/utils.h b/source/utils/utils.h new file mode 100644 index 0000000..d4223be --- /dev/null +++ b/source/utils/utils.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#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); +}; \ No newline at end of file