first commit

This commit is contained in:
Maschell 2022-04-15 18:13:21 +02:00
commit 55b2c3da32
18 changed files with 930 additions and 0 deletions

67
.clang-format Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,10 @@
*.cbp
*.elf
*.layout
*.rpx
build/
*.save-failed
.idea/
cmake-build-debug/
CMakeLists.txt
*.wms

7
Dockerfile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
};

View 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();
}

View 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
View 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
View 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