From 0b63fdf8a5120297744079523a3f09003873ac5a Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 15 Apr 2022 17:33:16 +0200 Subject: [PATCH] first commit --- .clang-format | 67 +++++++ .github/workflows/pr.yml | 25 +++ .github/workflows/push_image.yml | 33 ++++ .gitignore | 9 + Dockerfile | 9 + Dockerfile.buildlocal | 3 + Makefile | 157 +++++++++++++++++ README.md | 31 ++++ include/wuhb_utils/utils.h | 203 +++++++++++++++++++++ source/utils.cpp | 293 +++++++++++++++++++++++++++++++ 10 files changed, 830 insertions(+) create mode 100644 .clang-format create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/push_image.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Dockerfile.buildlocal create mode 100644 Makefile create mode 100644 README.md create mode 100644 include/wuhb_utils/utils.h create mode 100644 source/utils.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56cc685 --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..54f5094 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,25 @@ +name: CI-PR + +on: [pull_request] + +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include + build-lib: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build lib + run: | + docker build . -f Dockerfile.buildlocal -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: lib + path: "lib/*.a" \ No newline at end of file diff --git a/.github/workflows/push_image.yml b/.github/workflows/push_image.yml new file mode 100644 index 0000000..fa316c9 --- /dev/null +++ b/.github/workflows/push_image.yml @@ -0,0 +1,33 @@ +name: Publish Docker Image +on: + push: + branches: + - main +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include + build: + runs-on: ubuntu-latest + needs: clang-format + steps: + - uses: actions/checkout@master + - name: Get release version + id: get_release_tag + run: | + echo RELEASE_VERSION=$(echo $(date '+%Y%m%d')) >> $GITHUB_ENV + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//" | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV + echo REPOSITORY_OWNER=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $1}' | sed 's/[^a-zA-Z0-9]//g' | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: ${{ env.REPOSITORY_OWNER }}/${{ env.REPOSITORY_NAME }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + snapshot: true + cache: true + tags: "latest, ${{ env.RELEASE_VERSION }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f6ace4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/*.a +/build +*.bz2 +release/ +lib/ +CMakeLists.txt +.idea/ +cmake-build-debug/ +share/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..775c9f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM wiiuenv/devkitppc:20220303 + +WORKDIR tmp_build +COPY . . +RUN make clean && make && mkdir -p /artifacts/wums && cp -r lib /artifacts/wums && cp -r include /artifacts/wums +WORKDIR /artifacts + +FROM scratch +COPY --from=0 /artifacts /artifacts \ No newline at end of file diff --git a/Dockerfile.buildlocal b/Dockerfile.buildlocal new file mode 100644 index 0000000..1b9fe75 --- /dev/null +++ b/Dockerfile.buildlocal @@ -0,0 +1,3 @@ +FROM wiiuenv/devkitppc:20220303 + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..99e912f --- /dev/null +++ b/Makefile @@ -0,0 +1,157 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wut/share/wut_rules + +export VER_MAJOR := 1 +export VER_MINOR := 0 +export VER_PATCH := 0 + +VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) + +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := source \ + include \ + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS := -Wall -Werror -save-temps \ + -ffunction-sections -fdata-sections \ + $(MACHDEP) \ + $(BUILD_CFLAGS) + +CFLAGS += $(INCLUDE) -D__WIIU__ + +CXXFLAGS := $(CFLAGS) -std=gnu++17 + +ASFLAGS := $(MACHDEP) + +LDFLAGS = $(ARCH) -Wl,--gc-sections + + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) + +#--------------------------------------------------------------------------------- +# 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 TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +DEFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.def))) +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 := $(DEFFILES:.def=.o) $(SFILES:.s=.o) $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +.PHONY: all dist-bin dist-src dist install clean + +#--------------------------------------------------------------------------------- +all: lib/libwuhbutils.a + +dist-bin: all + @tar --exclude=*~ -cjf libwuhbutils-$(VERSION).tar.bz2 include lib + +dist-src: + @tar --exclude=*~ -cjf libwuhbutils-src-$(VERSION).tar.bz2 include source Makefile + +dist: dist-src dist-bin + +install: dist-bin + mkdir -p $(DESTDIR)$(DEVKITPRO)/wums + bzip2 -cd libwuhbutils-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(DEVKITPRO)/wums + +lib: + @[ -d $@ ] || mkdir -p $@ + +release: + @[ -d $@ ] || mkdir -p $@ + +lib/libwuhbutils.a :$(SOURCES) $(INCLUDES) | lib release + @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2 -s" \ + DEPSDIR=$(CURDIR)/release \ + --no-print-directory -C release \ + -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -rf release lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES_SRC) : $(HFILES) + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..37b2ce3 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +[![Publish Docker Image](https://github.com/wiiu-env/libwuhbutils/actions/workflows/push_image.yml/badge.svg)](https://github.com/wiiu-env/libwuhbutils/actions/workflows/push_image.yml) + +# libwuhbutils +Requires the [WUHBUtilsModule](https://github.com/wiiu-env/WUHBUtilsModule) to be running via [WUMSLoader](https://github.com/wiiu-env/WUMSLoader). +Requires [wut](https://github.com/devkitPro/wut) for building. +Install via `make install`. + +## Usage +Make sure to define +``` +WUMS_ROOT := $(DEVKITPRO)/wums +``` +and add `-lwuhbutils` to `LIBS` and `$(WUMS_ROOT)` to `LIBDIRS`. + +After that you can simply include ``, to get access to WUHBUtils function. + +To init the library call `WUHBUtils_Init()` and check for the `WUHB_UTILS_RESULT_SUCCESS` return code. + +## Use this lib in Dockerfiles. +A prebuilt version of this lib can found on dockerhub. To use it for your projects, add this to your Dockerfile. +``` +[...] +COPY --from=wiiuenv/libwuhbutils:[tag] /artifacts $DEVKITPRO +[...] +``` +Replace [tag] with a tag you want to use, a list of tags can be found [here](https://hub.docker.com/r/wiiuenv/libwuhbutils/tags). +It's highly recommended to pin the version to the **latest date** instead of using `latest`. + +## Format the code via docker + +`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source ./include -i` diff --git a/include/wuhb_utils/utils.h b/include/wuhb_utils/utils.h new file mode 100644 index 0000000..e84b547 --- /dev/null +++ b/include/wuhb_utils/utils.h @@ -0,0 +1,203 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum WUHBUtilsStatus { + WUHB_UTILS_RESULT_SUCCESS = 0, + WUHB_UTILS_RESULT_MODULE_NOT_FOUND = -1, + WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT = -2, + WUHB_UTILS_RESULT_UNSUPPORTED_VERSION = -3, + WUHB_UTILS_RESULT_INVALID_ARG = -10, + WUHB_UTILS_RESULT_NO_MEMORY = -11, + WUHB_UTILS_RESULT_MOUNT_NAME_TAKEN = -12, + WUHB_UTILS_RESULT_MOUNT_NOT_FOUND = -13, + WUHB_UTILS_RESULT_FILE_NOT_FOUND = -14, + WUHB_UTILS_RESULT_FILE_HANDLE_NOT_FOUND = -15, + WUHB_UTILS_RESULT_MOUNT_FAILED = -16, + WUHB_UTILS_RESULT_LIB_UNINITIALIZED = -20, + WUHB_UTILS_RESULT_UNKNOWN_ERROR = -1000, +}; + +typedef uint32_t WUHBUtilsVersion; +typedef uint32_t WUHBFileHandle; +#define WUHB_UTILS_MODULE_VERSION 0x00000001 + +typedef enum WUHBUtilsApiErrorType { + WUHB_UTILS_API_ERROR_NONE = 0, + WUHB_UTILS_API_ERROR_INVALID_ARG = -1, + WUHB_UTILS_API_ERROR_MOUNT_NAME_TAKEN = -2, + WUHB_UTILS_API_ERROR_MOUNT_NOT_FOUND = -3, + WUHB_UTILS_API_ERROR_FILE_NOT_FOUND = -4, + WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND = -5, + WUHB_UTILS_API_ERROR_NO_MEMORY = -6, + WUHB_UTILS_API_ERROR_MOUNT_FAILED = -7, +} WUHBUtilsApiErrorType; + +typedef struct { + uint64_t length; // Offset of the file's data. + uint64_t offset; // Length of the file's data. +} WUHBRPXInfo; + +typedef enum { + BundleSource_FileDescriptor, /* The POSIX file api be used, use paths like fs:/vol/external01/my.wuhb */ + BundleSource_FileDescriptor_CafeOS, /* The native CafeOS file api will be used, use paths like /vol/external01/my.wuhb */ +} BundleSource; + +/** + * This function has to be called before any other function of this lib (except WUHBUtils_GetVersion) can be used. + * + * @return WUHB_UTILS_RESULT_SUCCESS: The library has been initialized successfully. Other functions can now be used. + * WUHB_UTILS_RESULT_MODULE_NOT_FOUND: The module could not be found. Make sure the module is loaded. + * WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT: The module is missing an expected export. + * WUHB_UTILS_RESULT_UNSUPPORTED_VERSION: The version of the loaded module is not compatible with this version of the lib. +*/ +WUHBUtilsStatus WUHBUtils_Init(); + +/** + * Returns the API Version of the WUHBUtils Module. + * @return The WUHBUtilsVersion of the Module + */ +WUHBUtilsVersion WUHBUtils_GetVersion(); + +/** + * Mounts a given bundle to a given mount path. Use WUHBUtils_UnmountBundle to unmount it. + * + * Caution: the mounted path is only available via the WUHBUtils_FileXXX functions. + * + * @param name path the bundle should be mounted to (e.g. "bundle") + * @param bundle_path path to the bundle file (path may depend on the BundleSource) + * @param source type of source. See BundleSource for more information. + * @param outRes on success the result of the function will be stored here. + * @return WUHB_UTILS_RESULT_SUCCESS: MountBundle has been called successfully. The result has been written to outRes. + * *outRes is >= 0 on success. + * *outRes is < 0 on error. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_RESULT_INVALID_ARG: "name", "path" or "outRes" was NULL + * WUHB_UTILS_RESULT_MOUNT_NAME_TAKEN: The given name has been already taken. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_MountBundle(const char *name, const char *path, BundleSource source, int32_t *outRes); + +/** + * + * @param name path the bundle should be unmounted to (e.g. "bundle") + * @param outRes (optional) on success the result of the function will be stored here. + * @return WUHB_UTILS_RESULT_SUCCESS: UnmountBundle has been called successfully. The result has been written to outRes. + * *outRes is >= 0 on success. + * *outRes is < 0 on error. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_RESULT_INVALID_ARG: "name", was NULL + * WUHB_UTILS_API_ERROR_MOUNT_NOT_FOUND: "name" was not mounted. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_UnmountBundle(const char *name, int32_t *outRes); + +/** + * Opens a file inside a mounted bundle. + * (only read only is supported and is default) + * + * Make sure the bundle is mounted via WUHBUtils_MountBundle. + * + * If a given files does not exists, it's checks for a compressed version + * (at name + ".gz). If a compressed file was found, all file reads will be + * decompressed on the fly. + * + * @param name path to the file that should be opened. + * @param outHandle on success the result of the function will be stored here. + * @return WUHB_UTILS_RESULT_SUCCESS: file has been opened successfully. + * The file handle has been stored in *outHandle + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_RESULT_INVALID_ARG: "name", was NULL + * WUHB_UTILS_API_ERROR_FILE_NOT_FOUND: file at path "name" was not found. + * WUHB_UTILS_API_ERROR_NO_MEMORY: not enough memory. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_FileOpen(const char *name, WUHBFileHandle *outHandle); + +/** + * Reads from a given file. + * + * @param handle File handle to be read from. + * @param buffer buffer where data will be written to. + * Align to 0x40 for best performance + * @param size maximum bytes this function should read into buffer + * @return WUHB_UTILS_RESULT_SUCCESS file read has been called successfully. The result has been written to *outRes. + * On success, the number of bytes read is set to *outRes (zero indicates + * end of file), and the file position is advanced by this number. + * It is not an error if this number is smaller than the number of + * bytes requested. + * On error, *outRes is set to -1. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_RESULT_INVALID_ARG: "buffer" or "outRes" is NULL + * WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND: file handle is invalid. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_FileRead(WUHBFileHandle handle, uint8_t *buffer, uint32_t size, int32_t *outRes); + +/** + * Closes a given file + * + * example: if(WUHBUtils_FileClose(fileHandle) != WUHB_UTILS_RESULT_SUCCESS) { //error while closing the file } + * + * @param handle File to be closed + * @return WUHB_UTILS_RESULT_SUCCESS file handle has been closed successfully. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND: file handle is invalid. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_FileClose(WUHBFileHandle handle); + +/** + * Checks if a given file exists + * + * example: int32_t res; if(WUHBUtils_FileExists("bundle:/meta/meta.ini", &res) == WUHB_UTILS_RESULT_SUCCESS && res) { // file exists} + * + * @param name Paths to be checked + * @param outRes PTR to the int where the file check result will be stored. + * @return WUHB_UTILS_RESULT_SUCCESS file exists check has been called. The result has been written to *outRes. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_API_ERROR_INVALID_ARG: "name" or "outRes" is NULL. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. +*/ +WUHBUtilsStatus WUHBUtils_FileExists(const char *name, int32_t *outRes); + +/** + * Opens a file, reads it completely and returns the data as new buffer, + * Onn success the the caller has to call "free()" on the returned buffer after using it. + * + * @param path path to the file. + * @param buffer address where the buffer address will be stored + * @param size address where the size will be stored + * @return WUHB_UTILS_RESULT_SUCCESS file read has been done. The new buffer been written to *outBuf. + * The size of the buffer will be written to *outSize. + * The buffer returned in *outBuf has be cleaned up via "free()" + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_API_ERROR_INVALID_ARG: "outBuf" or "outSize" is NULL. + * WUHB_UTILS_RESULT_NO_MEMORY: Not enough memory. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_ReadWholeFile(const char *path, uint8_t **outBuf, uint32_t *outSize); + +/** + * Gets the offset and size of the /code/ *.rpx inside a WUHB. + * + * @param bundle_path path to the bundle file (path may depend on the BundleSource) + * @param source type of source. See BundleSource for more information. + * @param outFileInfo on success the result file info will be stored he + * @return WUHB_UTILS_RESULT_SUCCESS GetRPXInfo check has been done successfully. + * The result of the check has been written to outFileInfo. + * WUHB_UTILS_RESULT_LIB_UNINITIALIZED: "WUHBUtils_Init()" was not called before. + * WUHB_UTILS_API_ERROR_INVALID_ARG: "bundle_path" or "outFileInfo" is NULL. + * WUHB_UTILS_RESULT_MOUNT_FAILED: failed to mount bundle. + * WUHB_UTILS_RESULT_FILE_NOT_FOUND: No .rpx inside [bundle]/code/ found. + * WUHB_UTILS_RESULT_UNKNOWN_ERROR: Unknown error. + */ +WUHBUtilsStatus WUHBUtils_GetRPXInfo(const char *bundle_path, BundleSource source, WUHBRPXInfo *outFileInfo); + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..6eccf40 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include + +static OSDynLoad_Module sModuleHandle = nullptr; + +static WUHBUtilsVersion (*sWUUGetVersion)() = nullptr; +static WUHBUtilsApiErrorType (*sWUUMountBundle)(const char *, const char *, BundleSource, int32_t *) = nullptr; +static WUHBUtilsApiErrorType (*sWUUUnmountBundle)(const char *, int32_t *) = nullptr; +static WUHBUtilsApiErrorType (*sWUUFileOpen)(const char *, WUHBFileHandle *) = nullptr; +static WUHBUtilsApiErrorType (*sWUUFileRead)(WUHBFileHandle, uint8_t *, uint32_t, int32_t *) = nullptr; +static WUHBUtilsApiErrorType (*sWUUFileClose)(WUHBFileHandle) = nullptr; +static WUHBUtilsApiErrorType (*sWUUFileExists)(const char *, int32_t *) = nullptr; +static WUHBUtilsApiErrorType (*sWUUGetRPXInfo)(const char *, BundleSource, WUHBRPXInfo *) = nullptr; + +WUHBUtilsStatus WUHBUtils_Init() { + if (OSDynLoad_Acquire("homebrew_wuhb_utils", &sModuleHandle) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: OSDynLoad_Acquire failed.\n"); + return WUHB_UTILS_RESULT_MODULE_NOT_FOUND; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_GetVersion", (void **) &sWUUGetVersion) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_GetVersion failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + auto res = WUHBUtils_GetVersion(); + if (res != WUHB_UTILS_MODULE_VERSION) { + return WUHB_UTILS_RESULT_UNSUPPORTED_VERSION; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_MountBundle", (void **) &sWUUMountBundle) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_MountBundle failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_UnmountBundle", (void **) &sWUUUnmountBundle) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_UnmountBundle failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_FileOpen", (void **) &sWUUFileOpen) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_FileOpen failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_FileRead", (void **) &sWUUFileRead) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_FileRead failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_FileClose", (void **) &sWUUFileClose) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_FileClose failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_FileExists", (void **) &sWUUFileExists) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_FileExists failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + if (OSDynLoad_FindExport(sModuleHandle, FALSE, "WUU_GetRPXInfo", (void **) &sWUUGetRPXInfo) != OS_DYNLOAD_OK) { + OSReport("WUHBUtils_Init: WUU_GetRPXInfo failed.\n"); + return WUHB_UTILS_RESULT_MODULE_MISSING_EXPORT; + } + + return WUHB_UTILS_RESULT_SUCCESS; +} + +WUHBUtilsVersion GetVersion(); +WUHBUtilsVersion WUHBUtils_GetVersion() { + if (sWUUGetVersion == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + + return reinterpret_cast(sWUUGetVersion)(); +} + +WUHBUtilsApiErrorType MountBundle(const char *, const char *, BundleSource, int32_t *); +WUHBUtilsStatus WUHBUtils_MountBundle(const char *name, const char *path, BundleSource source, int32_t *outRes) { + if (sWUUMountBundle == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUMountBundle)(name, path, source, outRes); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + case WUHB_UTILS_API_ERROR_MOUNT_NAME_TAKEN: + return WUHB_UTILS_RESULT_MOUNT_NAME_TAKEN; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + +WUHBUtilsApiErrorType UnmountBundle(const char *, int32_t *); +WUHBUtilsStatus WUHBUtils_UnmountBundle(const char *name, int32_t *outRes) { + if (sWUUUnmountBundle == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUUnmountBundle)(name, outRes); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + case WUHB_UTILS_API_ERROR_MOUNT_NOT_FOUND: + return WUHB_UTILS_RESULT_MOUNT_NOT_FOUND; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + +WUHBUtilsApiErrorType FileOpen(const char *, WUHBFileHandle *); +WUHBUtilsStatus WUHBUtils_FileOpen(const char *name, WUHBFileHandle *outHandle) { + if (sWUUFileOpen == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUFileOpen)(name, outHandle); + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + case WUHB_UTILS_API_ERROR_FILE_NOT_FOUND: + return WUHB_UTILS_RESULT_FILE_NOT_FOUND; + case WUHB_UTILS_API_ERROR_NO_MEMORY: + return WUHB_UTILS_RESULT_NO_MEMORY; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + +WUHBUtilsApiErrorType FileRead(WUHBFileHandle, uint8_t *, uint32_t, int32_t *); +WUHBUtilsStatus WUHBUtils_FileRead(WUHBFileHandle handle, uint8_t *buffer, uint32_t size, int32_t *outRes) { + if (sWUUFileRead == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUFileRead)(handle, buffer, size, outRes); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + case WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND: + return WUHB_UTILS_RESULT_FILE_HANDLE_NOT_FOUND; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + +WUHBUtilsApiErrorType FileClose(WUHBFileHandle); +WUHBUtilsStatus WUHBUtils_FileClose(WUHBFileHandle handle) { + if (sWUUFileClose == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUFileClose)(handle); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND: + return WUHB_UTILS_RESULT_FILE_HANDLE_NOT_FOUND; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + +WUHBUtilsApiErrorType FileExists(const char *, int32_t *); +WUHBUtilsStatus WUHBUtils_FileExists(const char *name, int32_t *outRes) { + if (sWUUFileExists == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUFileExists)(name, outRes); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + + +WUHBUtilsApiErrorType GetRPXInfo(const char *, BundleSource, WUHBRPXInfo *); +WUHBUtilsStatus WUHBUtils_GetRPXInfo(const char *path, BundleSource source, WUHBRPXInfo *outFileInfo) { + if (sWUUGetRPXInfo == nullptr) { + return WUHB_UTILS_RESULT_LIB_UNINITIALIZED; + } + auto res = reinterpret_cast(sWUUGetRPXInfo)(path, source, outFileInfo); + + switch (res) { + case WUHB_UTILS_API_ERROR_NONE: + return WUHB_UTILS_RESULT_SUCCESS; + case WUHB_UTILS_API_ERROR_INVALID_ARG: + return WUHB_UTILS_RESULT_INVALID_ARG; + case WUHB_UTILS_API_ERROR_MOUNT_FAILED: + return WUHB_UTILS_RESULT_MOUNT_FAILED; + case WUHB_UTILS_API_ERROR_FILE_NOT_FOUND: + return WUHB_UTILS_RESULT_FILE_NOT_FOUND; + default: + return WUHB_UTILS_RESULT_UNKNOWN_ERROR; + } +} + + +WUHBUtilsStatus WUHBUtils_ReadWholeFile(const char *name, uint8_t **outBuf, uint32_t *outSize) { + if (!outBuf || !outSize) { + return WUHB_UTILS_RESULT_INVALID_ARG; + } + auto DEFAULT_READ_BUFFER_SIZE = 128 * 1024; + WUHBFileHandle handle; + WUHBUtilsStatus res; + if ((res = WUHBUtils_FileOpen(name, &handle)) != WUHB_UTILS_RESULT_SUCCESS) { + return res; + } + uint32_t buffer_size = DEFAULT_READ_BUFFER_SIZE; + auto *buffer = (uint8_t *) memalign(0x40, buffer_size); + if (!buffer) { + WUHBUtils_FileClose(handle); + return WUHB_UTILS_RESULT_NO_MEMORY; + } + + uint32_t readSize = DEFAULT_READ_BUFFER_SIZE; + auto readRes = -1; + uint32_t totalRead = 0; + while (true) { + if (totalRead + readSize > buffer_size) { + readSize = buffer_size - totalRead; + } + + if ((res = WUHBUtils_FileRead(handle, buffer + totalRead, readSize, &readRes)) != WUHB_UTILS_RESULT_SUCCESS) { + free(buffer); + WUHBUtils_FileClose(handle); + return res; + } + + if (readRes <= 0) { + break; + } + + totalRead += readRes; + + if (totalRead == buffer_size) { + auto newBufferSize = buffer_size * 2; + if (buffer_size >= 1024 * 1024) { + newBufferSize = buffer_size + 1024 * 1024; + } + auto *newBuffer = (uint8_t *) memalign(0x40, newBufferSize); + if (!newBuffer) { + newBufferSize = buffer_size + DEFAULT_READ_BUFFER_SIZE; + newBuffer = (uint8_t *) memalign(0x40, newBufferSize); + if (!newBuffer) { + free(buffer); + WUHBUtils_FileClose(handle); + return WUHB_UTILS_RESULT_NO_MEMORY; + } + } + memcpy(newBuffer, buffer, totalRead); + free(buffer); + buffer = newBuffer; + buffer_size = newBufferSize; + } + + //reset read size + readSize = DEFAULT_READ_BUFFER_SIZE; + } + + auto closeRes = WUHBUtils_FileClose(handle); + + if (closeRes != WUHB_UTILS_RESULT_SUCCESS) { + free(buffer); + return closeRes; + } + + auto *newBuffer = (uint8_t *) malloc(totalRead); + if (newBuffer) { + memcpy(newBuffer, buffer, totalRead); + free(buffer); + buffer = newBuffer; + } + + *outSize = totalRead; + *outBuf = buffer; + return WUHB_UTILS_RESULT_SUCCESS; +}