mirror of
https://github.com/wiiu-env/WUHBUtilsModule.git
synced 2024-11-21 10:59:18 +01:00
first commit
This commit is contained in:
commit
55b2c3da32
67
.clang-format
Normal file
67
.clang-format
Normal file
@ -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
|
65
.github/workflows/ci.yml
vendored
Normal file
65
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: CI-Release
|
||||
|
||||
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 ./src
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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: "*.wms"
|
||||
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
|
||||
- name: zip artifact
|
||||
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
|
||||
- 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/zip
|
25
.github/workflows/pr.yml
vendored
Normal file
25
.github/workflows/pr.yml
vendored
Normal file
@ -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 ./src
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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: "*.wms"
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.cbp
|
||||
*.elf
|
||||
*.layout
|
||||
*.rpx
|
||||
build/
|
||||
*.save-failed
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.wms
|
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM wiiuenv/devkitppc:20220303
|
||||
|
||||
COPY --from=wiiuenv/wiiumodulesystem:20220204 /artifacts $DEVKITPRO
|
||||
COPY --from=wiiuenv/libromfs_wiiu:20220305 /artifacts $DEVKITPRO
|
||||
COPY --from=wiiuenv/libwuhbutils:20220415 /artifacts $DEVKITPRO
|
||||
|
||||
WORKDIR project
|
155
Makefile
Normal file
155
Makefile
Normal file
@ -0,0 +1,155 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
include $(DEVKITPRO)/wums/share/wums_rules
|
||||
|
||||
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||
WUT_ROOT := $(DEVKITPRO)/wut
|
||||
#-------------------------------------------------------------------------------
|
||||
# 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 := WUHBUtilsModule
|
||||
BUILD := build
|
||||
SOURCES := src \
|
||||
src/utils
|
||||
DATA := data
|
||||
INCLUDES := src
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -Wall -Wextra -Os -ffunction-sections\
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++20
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUMSSPECS)
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
CXXFLAGS += -DDEBUG -g
|
||||
CFLAGS += -DDEBUG -g
|
||||
endif
|
||||
|
||||
LIBS := -lwums -lwut -lromfs -lz
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr $(WUMS_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 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)))
|
||||
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) $(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).wms $(TARGET).elf
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#-------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).wms
|
||||
|
||||
$(OUTPUT).wms : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#-------------------------------------------------------------------------------
|
||||
%.o: %.def
|
||||
$(SILENTMSG) $(notdir $<)
|
||||
$(SILENTCMD)rplimportgen $< $*.s $*.ld $(ERROR_FILTER)
|
||||
$(SILENTCMD)$(CC) -x assembler-with-cpp $(ASFLAGS) -c $*.s -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.s
|
||||
@echo $(notdir $<)
|
||||
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
27
README.md
Normal file
27
README.md
Normal file
@ -0,0 +1,27 @@
|
||||
[![CI-Release](https://github.com/wiiu-env/WUHBUtilsModule/actions/workflows/ci.yml/badge.svg)](https://github.com/wiiu-env/WUHBUtilsModule/actions/workflows/ci.yml)
|
||||
|
||||
## Usage
|
||||
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
|
||||
|
||||
1. Copy the file `WUHBUtils.wms` into `sd:/wiiu/environments/[ENVIRONMENT]/modules`.
|
||||
2. Requires the [WUMSLoader](https://github.com/wiiu-env/WUMSLoader) in `sd:/wiiu/environments/[ENVIRONMENT]/modules/setup`.
|
||||
3. Use [libwuhbutils](https://github.com/wiiu-env/libwuhbutils).
|
||||
|
||||
## 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 wuhbutils-builder
|
||||
|
||||
# make
|
||||
docker run -it --rm -v ${PWD}:/project wuhbutils-builder make
|
||||
|
||||
# make clean
|
||||
docker run -it --rm -v ${PWD}:/project wuhbutils-builder make clean
|
||||
```
|
||||
|
||||
## Format the code via docker
|
||||
|
||||
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./src -i`
|
79
src/FileUtils.cpp
Normal file
79
src/FileUtils.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "FileUtils.h"
|
||||
#include "export.h"
|
||||
#include "utils/logger.h"
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <wuhb_utils/utils.h>
|
||||
|
||||
int32_t 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;
|
||||
}
|
||||
|
||||
WUHBRPXInfoResultCode getRPXInfoForPath(const std::string &path, BundleSource source, WUHBRPXInfo *outFileInfo) {
|
||||
auto realSource = RomfsSource_FileDescriptor;
|
||||
switch (source) {
|
||||
case BundleSource_FileDescriptor:
|
||||
realSource = RomfsSource_FileDescriptor;
|
||||
break;
|
||||
case BundleSource_FileDescriptor_CafeOS:
|
||||
realSource = RomfsSource_FileDescriptor_CafeOS;
|
||||
break;
|
||||
}
|
||||
if (romfsMount("wuu_rpxinfo", path.c_str(), realSource) < 0) {
|
||||
return WUHB_UTILS_RPX_INFO_MOUNT_FAILED;
|
||||
}
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if (!(dir = opendir("wuu_rpxinfo:/code/"))) {
|
||||
romfsUnmount("wuu_rpxinfo");
|
||||
return WUHB_UTILS_RPX_INFO_OPENDIR_FAILED;
|
||||
}
|
||||
bool found = false;
|
||||
WUHBRPXInfoResultCode res;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
if (std::string_view(entry->d_name).ends_with(".rpx")) {
|
||||
romfs_fileInfo info;
|
||||
if (romfsGetFileInfoPerPath("wuu_rpxinfo", (std::string("code/") + entry->d_name).c_str(), &info) >= 0) {
|
||||
found = true;
|
||||
res = WUHB_UTILS_RPX_INFO_SUCCESS;
|
||||
outFileInfo->length = info.length;
|
||||
outFileInfo->offset = info.offset;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to get info for %s", entry->d_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
romfsUnmount("wuu_rpxinfo");
|
||||
|
||||
if (!found) {
|
||||
return WUHB_UTILS_RPX_INFO_NO_RPX_FOUND;
|
||||
}
|
||||
return res;
|
||||
}
|
11
src/FileUtils.h
Normal file
11
src/FileUtils.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "export.h"
|
||||
#include <coreinit/filesystem.h>
|
||||
#include <functional>
|
||||
#include <romfs_dev.h>
|
||||
#include <string>
|
||||
#include <wuhb_utils/utils.h>
|
||||
|
||||
WUHBRPXInfoResultCode getRPXInfoForPath(const std::string &path, BundleSource source, WUHBRPXInfo *outFileInfo);
|
||||
|
||||
int32_t CheckFile(const char *filepath);
|
172
src/export.cpp
Normal file
172
src/export.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include "FileUtils.h"
|
||||
#include "utils/FileReader.h"
|
||||
#include "utils/FileReaderCompressed.h"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <wuhb_utils/utils.h>
|
||||
#include <wums/exports.h>
|
||||
|
||||
std::vector<FileReader *> openFiles;
|
||||
std::map<std::string, std::string> mountedWUHB;
|
||||
std::mutex mutex;
|
||||
|
||||
void WUHBUtils_CleanUp() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto &file : openFiles) {
|
||||
delete file;
|
||||
}
|
||||
openFiles.clear();
|
||||
|
||||
for (const auto &[name, path] : mountedWUHB) {
|
||||
romfsUnmount(name.c_str());
|
||||
}
|
||||
mountedWUHB.clear();
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_MountBundle(const char *name, const char *path, BundleSource source, int32_t *outRes) {
|
||||
if (!name || !path || (source != BundleSource_FileDescriptor && source != BundleSource_FileDescriptor_CafeOS)) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (const auto &[key, value] : mountedWUHB) {
|
||||
if (key == name) {
|
||||
if (value == path) {
|
||||
*outRes = 0;
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
return WUHB_UTILS_API_ERROR_MOUNT_NAME_TAKEN;
|
||||
}
|
||||
}
|
||||
|
||||
auto res = romfsMount(name, path, (RomfsSource) source);
|
||||
if (res == 0) {
|
||||
mountedWUHB[name] = path;
|
||||
*outRes = res;
|
||||
}
|
||||
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_UnmountBundle(const char *name, int32_t *outRes) {
|
||||
if (!name) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (mountedWUHB.count(name) > 0) {
|
||||
auto res = romfsUnmount(name);
|
||||
if (outRes) {
|
||||
*outRes = res;
|
||||
}
|
||||
mountedWUHB.erase(name);
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
return WUHB_UTILS_API_ERROR_MOUNT_NOT_FOUND;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_FileOpen(const char *name, uint32_t *outHandle) {
|
||||
if (!outHandle || !name) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
FileReader *reader;
|
||||
std::string path = std::string(name);
|
||||
std::string pathGZ = path + ".gz";
|
||||
|
||||
if (CheckFile(path.c_str())) {
|
||||
reader = new (std::nothrow) FileReader(path);
|
||||
} else if (CheckFile(pathGZ.c_str())) {
|
||||
reader = new (std::nothrow) FileReaderCompressed(pathGZ);
|
||||
} else {
|
||||
return WUHB_UTILS_API_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
if (reader == nullptr) {
|
||||
return WUHB_UTILS_API_ERROR_NO_MEMORY;
|
||||
}
|
||||
openFiles.push_back(reader);
|
||||
*outHandle = (uint32_t) reader;
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_FileRead(uint32_t handle, uint8_t *buffer, uint32_t size, int32_t *outRes) {
|
||||
if (!buffer | !outRes) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto found = false;
|
||||
FileReader *reader;
|
||||
for (auto &cur : openFiles) {
|
||||
if ((uint32_t) cur == handle) {
|
||||
found = true;
|
||||
reader = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND;
|
||||
}
|
||||
|
||||
*outRes = (int32_t) reader->read(buffer, size);
|
||||
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_FileClose(uint32_t handle) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
auto count = 0;
|
||||
auto found = false;
|
||||
FileReader *reader;
|
||||
for (auto &cur : openFiles) {
|
||||
if ((uint32_t) cur == handle) {
|
||||
found = true;
|
||||
reader = cur;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!found) {
|
||||
return WUHB_UTILS_API_ERROR_FILE_HANDLE_NOT_FOUND;
|
||||
}
|
||||
openFiles.erase(openFiles.begin() + count);
|
||||
delete reader;
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_FileExists(const char *name, int32_t *outRes) {
|
||||
if (!outRes || !name) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::string checkgz = std::string(name) + ".gz";
|
||||
*outRes = CheckFile(name) || CheckFile(checkgz.c_str());
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
WUHBUtilsApiErrorType WUU_GetRPXInfo(const char *path, BundleSource source, WUHBRPXInfo *outFileInfo) {
|
||||
if (!outFileInfo || !path || (source != BundleSource_FileDescriptor && source != BundleSource_FileDescriptor_CafeOS)) {
|
||||
return WUHB_UTILS_API_ERROR_INVALID_ARG;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
auto res = getRPXInfoForPath(path, source, outFileInfo);
|
||||
|
||||
if (res == WUHB_UTILS_RPX_INFO_MOUNT_FAILED) {
|
||||
return WUHB_UTILS_API_ERROR_MOUNT_FAILED;
|
||||
} else if (res == WUHB_UTILS_RPX_INFO_NO_RPX_FOUND || res == WUHB_UTILS_RPX_INFO_OPENDIR_FAILED) {
|
||||
return WUHB_UTILS_API_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return WUHB_UTILS_API_ERROR_NONE;
|
||||
}
|
||||
|
||||
uint32_t WUU_GetVersion() {
|
||||
return WUHB_UTILS_MODULE_VERSION;
|
||||
}
|
||||
|
||||
WUMS_EXPORT_FUNCTION(WUU_MountBundle);
|
||||
WUMS_EXPORT_FUNCTION(WUU_UnmountBundle);
|
||||
WUMS_EXPORT_FUNCTION(WUU_FileOpen);
|
||||
WUMS_EXPORT_FUNCTION(WUU_FileRead);
|
||||
WUMS_EXPORT_FUNCTION(WUU_FileClose);
|
||||
WUMS_EXPORT_FUNCTION(WUU_FileExists);
|
||||
WUMS_EXPORT_FUNCTION(WUU_GetRPXInfo);
|
||||
WUMS_EXPORT_FUNCTION(WUU_GetVersion);
|
10
src/export.h
Normal file
10
src/export.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum WUHBRPXInfoResultCode {
|
||||
WUHB_UTILS_RPX_INFO_SUCCESS = 0,
|
||||
WUHB_UTILS_RPX_INFO_MOUNT_FAILED = -1,
|
||||
WUHB_UTILS_RPX_INFO_OPENDIR_FAILED = -2,
|
||||
WUHB_UTILS_RPX_INFO_NO_RPX_FOUND = -3,
|
||||
} WUHBRPXInfoResultCode;
|
||||
|
||||
void WUHBUtils_CleanUp();
|
16
src/main.cpp
Normal file
16
src/main.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "export.h"
|
||||
#include "utils/logger.h"
|
||||
#include <wums.h>
|
||||
|
||||
WUMS_MODULE_EXPORT_NAME("homebrew_wuhb_utils");
|
||||
|
||||
WUMS_USE_WUT_DEVOPTAB();
|
||||
|
||||
WUMS_APPLICATION_STARTS() {
|
||||
initLogging();
|
||||
}
|
||||
|
||||
WUMS_APPLICATION_ENDS() {
|
||||
WUHBUtils_CleanUp();
|
||||
deinitLogging();
|
||||
}
|
50
src/utils/FileReader.cpp
Normal file
50
src/utils/FileReader.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "FileReader.h"
|
||||
#include "logger.h"
|
||||
#include <cstring>
|
||||
|
||||
int64_t FileReader::read(uint8_t *buffer, uint32_t size) {
|
||||
if (isReadFromBuffer) {
|
||||
if (input_buffer == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
uint32_t toRead = size;
|
||||
if (toRead > input_size - input_pos) {
|
||||
toRead = input_size - input_pos;
|
||||
}
|
||||
if (toRead == 0) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(buffer, &input_buffer[input_pos], toRead);
|
||||
input_pos += toRead;
|
||||
return toRead;
|
||||
} else if (isReadFromFile) {
|
||||
int res = ::read(file_fd, buffer, size);
|
||||
return res;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
FileReader::FileReader(std::string &path) {
|
||||
int fd;
|
||||
if ((fd = open(path.c_str(), O_RDONLY)) >= 0) {
|
||||
this->isReadFromFile = true;
|
||||
this->isReadFromBuffer = false;
|
||||
this->file_fd = fd;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("## INFO ## Failed to open file %s", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
FileReader::~FileReader() {
|
||||
if (isReadFromFile) {
|
||||
::close(this->file_fd);
|
||||
}
|
||||
}
|
||||
|
||||
FileReader::FileReader(uint8_t *buffer, uint32_t size) {
|
||||
this->input_buffer = buffer;
|
||||
this->input_size = size;
|
||||
this->input_pos = 0;
|
||||
this->isReadFromBuffer = true;
|
||||
this->isReadFromFile = false;
|
||||
}
|
28
src/utils/FileReader.h
Normal file
28
src/utils/FileReader.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class FileReader {
|
||||
|
||||
public:
|
||||
FileReader(uint8_t *buffer, uint32_t size);
|
||||
|
||||
explicit FileReader(std::string &path);
|
||||
|
||||
virtual ~FileReader();
|
||||
|
||||
virtual int64_t read(uint8_t *buffer, uint32_t size);
|
||||
|
||||
private:
|
||||
bool isReadFromBuffer = false;
|
||||
uint8_t *input_buffer = nullptr;
|
||||
uint32_t input_size = 0;
|
||||
uint32_t input_pos = 0;
|
||||
|
||||
bool isReadFromFile = false;
|
||||
int file_fd = 0;
|
||||
};
|
89
src/utils/FileReaderCompressed.cpp
Normal file
89
src/utils/FileReaderCompressed.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "FileReaderCompressed.h"
|
||||
|
||||
int64_t FileReaderCompressed::read(uint8_t *buffer, uint32_t size) {
|
||||
if (!initDone) {
|
||||
return -11;
|
||||
}
|
||||
uint32_t startValue = this->strm.total_out;
|
||||
uint32_t newSize = 0;
|
||||
int ret;
|
||||
do {
|
||||
uint32_t nextOut = BUFFER_SIZE;
|
||||
if (nextOut > size) {
|
||||
nextOut = size;
|
||||
}
|
||||
if (this->strm.avail_in == 0) {
|
||||
auto read_res = FileReader::read(this->zlib_in_buf, BUFFER_SIZE);
|
||||
if (read_res <= 0) {
|
||||
break;
|
||||
}
|
||||
this->strm.avail_in = read_res;
|
||||
this->strm.next_in = this->zlib_in_buf;
|
||||
}
|
||||
/* run inflate() on input until output buffer not full */
|
||||
do {
|
||||
if (nextOut > size - newSize) {
|
||||
nextOut = size - newSize;
|
||||
}
|
||||
|
||||
this->strm.avail_out = nextOut;
|
||||
this->strm.next_out = buffer + newSize;
|
||||
ret = inflate(&this->strm, Z_NO_FLUSH);
|
||||
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Z_STREAM_ERROR");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case Z_NEED_DICT:
|
||||
DEBUG_FUNCTION_LINE_ERR("Z_NEED_DICT");
|
||||
ret = Z_DATA_ERROR;
|
||||
[[fallthrough]]; /* and fall through */
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
DEBUG_FUNCTION_LINE_ERR("Z_MEM_ERROR or Z_DATA_ERROR");
|
||||
(void) inflateEnd(&this->strm);
|
||||
return ret;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
newSize = this->strm.total_out - startValue;
|
||||
if (newSize == size) {
|
||||
break;
|
||||
}
|
||||
nextOut = BUFFER_SIZE;
|
||||
if (newSize + nextOut >= (size)) {
|
||||
nextOut = (size) -newSize;
|
||||
}
|
||||
} while (this->strm.avail_out == 0 && newSize < (size));
|
||||
|
||||
/* done when inflate() says it's done */
|
||||
} while (ret != Z_STREAM_END && newSize < size);
|
||||
|
||||
return newSize;
|
||||
}
|
||||
|
||||
FileReaderCompressed::FileReaderCompressed(std::string &file) : FileReader(file) {
|
||||
this->initCompressedData();
|
||||
}
|
||||
|
||||
void FileReaderCompressed::initCompressedData() {
|
||||
/* allocate inflate state */
|
||||
this->strm.zalloc = Z_NULL;
|
||||
this->strm.zfree = Z_NULL;
|
||||
this->strm.opaque = Z_NULL;
|
||||
this->strm.avail_in = 0;
|
||||
this->strm.next_in = Z_NULL;
|
||||
int ret = inflateInit2(&this->strm, MAX_WBITS | 16); //gzip
|
||||
if (ret != Z_OK) {
|
||||
DEBUG_FUNCTION_LINE_ERR("inflateInit2 failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
[[maybe_unused]] FileReaderCompressed::FileReaderCompressed(uint8_t *buffer, uint32_t size) : FileReader(buffer, size) {
|
||||
this->initCompressedData();
|
||||
}
|
25
src/utils/FileReaderCompressed.h
Normal file
25
src/utils/FileReaderCompressed.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "FileReader.h"
|
||||
#include "logger.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#define BUFFER_SIZE 0x20000
|
||||
|
||||
class FileReaderCompressed : public FileReader {
|
||||
public:
|
||||
[[maybe_unused]] FileReaderCompressed(uint8_t *buffer, uint32_t size);
|
||||
|
||||
explicit FileReaderCompressed(std::string &file);
|
||||
|
||||
~FileReaderCompressed() override = default;
|
||||
|
||||
int64_t read(uint8_t *buffer, uint32_t size) override;
|
||||
|
||||
private:
|
||||
bool initDone = false;
|
||||
alignas(0x40) uint8_t zlib_in_buf[BUFFER_SIZE]{};
|
||||
z_stream strm{};
|
||||
|
||||
void initCompressedData();
|
||||
};
|
36
src/utils/logger.c
Normal file
36
src/utils/logger.c
Normal file
@ -0,0 +1,36 @@
|
||||
#ifdef DEBUG
|
||||
#include <stdint.h>
|
||||
#include <whb/log_cafe.h>
|
||||
#include <whb/log_module.h>
|
||||
#include <whb/log_udp.h>
|
||||
|
||||
uint32_t moduleLogInit = false;
|
||||
uint32_t cafeLogInit = false;
|
||||
uint32_t udpLogInit = false;
|
||||
#endif // DEBUG
|
||||
|
||||
void initLogging() {
|
||||
#ifdef DEBUG
|
||||
if (!(moduleLogInit = WHBLogModuleInit())) {
|
||||
cafeLogInit = WHBLogCafeInit();
|
||||
udpLogInit = WHBLogUdpInit();
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void deinitLogging() {
|
||||
#ifdef DEBUG
|
||||
if (moduleLogInit) {
|
||||
WHBLogModuleDeinit();
|
||||
moduleLogInit = false;
|
||||
}
|
||||
if (cafeLogInit) {
|
||||
WHBLogCafeDeinit();
|
||||
cafeLogInit = false;
|
||||
}
|
||||
if (udpLogInit) {
|
||||
WHBLogUdpDeinit();
|
||||
udpLogInit = false;
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
58
src/utils/logger.h
Normal file
58
src/utils/logger.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <string.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
|
||||
// #define VERBOSE_DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) \
|
||||
do { \
|
||||
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
#endif
|
||||
|
||||
#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)
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) \
|
||||
do { \
|
||||
OSReport("## ERROR ## [%23s]%30s@L%04d: ##ERROR## " FMT "\n", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
|
||||
void initLogging();
|
||||
|
||||
void deinitLogging();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user