mirror of
https://github.com/wiiu-env/RPXLoadingModule.git
synced 2024-11-25 11:26:53 +01:00
Patch home menu process to display the correct icon + name when a .wuhb is loaded
Export functions for accessing files inside a bundle
This commit is contained in:
parent
3bb7b0e4ab
commit
160d5b4f3e
2
Makefile
2
Makefile
@ -39,7 +39,7 @@ CXXFLAGS := $(CFLAGS) -std=c++17
|
|||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
|
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libfunctionpatcher.ld $(WUMSSPECS)
|
||||||
|
|
||||||
LIBS := -lwums -lwut -lfunctionpatcher -lromfs
|
LIBS := -lwums -lwut -lfunctionpatcher -lromfs -lz
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level
|
# list of directories containing libraries, this must be the top level
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <coreinit/filesystem.h>
|
#include <coreinit/filesystem.h>
|
||||||
#include <romfs_dev.h>
|
#include <romfs_dev.h>
|
||||||
|
|
||||||
|
struct WUT_PACKED FSCmdBlockBody {//! FSAsyncResult object used for this command.
|
||||||
struct WUT_PACKED FSCmdBlockBody {//! FSAsyncResult object used for this command.
|
|
||||||
|
WUT_UNKNOWN_BYTES(0x96C);
|
||||||
WUT_UNKNOWN_BYTES(0x96C);
|
FSAsyncResult asyncResult;
|
||||||
FSAsyncResult asyncResult;
|
|
||||||
|
};
|
||||||
};
|
WUT_CHECK_OFFSET(FSCmdBlockBody, 0x96C, asyncResult);
|
||||||
WUT_CHECK_OFFSET(FSCmdBlockBody, 0x96C, asyncResult);
|
WUT_CHECK_SIZE(FSCmdBlockBody, 0x96C + 0x28);
|
||||||
WUT_CHECK_SIZE(FSCmdBlockBody, 0x96C + 0x28);
|
|
||||||
|
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock);
|
||||||
FSCmdBlockBody *fsCmdBlockGetBody(FSCmdBlock *cmdBlock);
|
|
||||||
|
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus result);
|
||||||
FSStatus send_result_async(FSClient *client, FSCmdBlock *block, FSAsyncData *asyncData, FSStatus result);
|
|
||||||
|
int32_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count);
|
||||||
int32_t readIntoBuffer(int32_t handle, void *buffer, size_t size, size_t count);
|
|
||||||
|
int32_t CreateSubfolder(const char *fullpath);
|
||||||
int32_t CreateSubfolder(const char *fullpath);
|
|
||||||
|
int32_t getRPXInfoForPath(const std::string &path, romfs_fileInfo *info);
|
||||||
int32_t getRPXInfoForPath(const std::string &path, romfs_fileInfo *info);
|
|
||||||
|
int32_t CheckFile(const char * filepath);
|
@ -6,9 +6,99 @@
|
|||||||
#include "RPXLoading.h"
|
#include "RPXLoading.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
|
#include <nn/acp/title.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "utils/FileReader.h"
|
||||||
|
#include "utils/FileReaderCompressed.h"
|
||||||
|
#include "utils/StringTools.h"
|
||||||
|
#include "utils/ini.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
char gIconCache[65580] __attribute__((section(".data")));
|
||||||
|
|
||||||
bool loadRPXFromSDOnNextLaunch(const std::string &path) {
|
/*
|
||||||
|
* Patch the meta xml for the home menu
|
||||||
|
*/
|
||||||
|
DECL_FUNCTION(int32_t, HBM_NN_ACP_ACPGetTitleMetaXmlByDevice, uint32_t titleid_upper, uint32_t titleid_lower, ACPMetaXml *metaxml, uint32_t device) {
|
||||||
|
if (gReplacedRPX) {
|
||||||
|
memset(&metaxml->longname_ja, 0, 0x338C - 0x38C); // clear all names
|
||||||
|
strncpy(metaxml->longname_en, gBundleInfo.longname, 64);
|
||||||
|
strncpy(metaxml->shortname_en, gBundleInfo.shortname, 64);
|
||||||
|
strncpy(metaxml->publisher_en, gBundleInfo.author, 64);
|
||||||
|
metaxml->e_manual = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int result = real_HBM_NN_ACP_ACPGetTitleMetaXmlByDevice(titleid_upper, titleid_lower, metaxml, device);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(int, RPX_FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, int *handle, int error) {
|
||||||
|
const char *iconTex = "iconTex.tga";
|
||||||
|
if (StringTools::EndsWith(path, iconTex)) {
|
||||||
|
if (gReplacedRPX) {
|
||||||
|
if (StringTools::EndsWith(path, iconTex)) {
|
||||||
|
auto *reader = new FileReader(reinterpret_cast<uint8_t *>(gIconCache), sizeof(gIconCache));
|
||||||
|
*handle = (uint32_t) reader;
|
||||||
|
return FS_STATUS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int result = real_RPX_FSOpenFile(client, block, path, mode, handle, error);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, RPX_FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, FSFileHandle handle, uint32_t unk1, uint32_t flags) {
|
||||||
|
// We check if the handle is part of our heap (the MemoryMapping Module allocates to 0x80000000)
|
||||||
|
if ((handle & 0xF0000000) == 0x80000000) {
|
||||||
|
auto reader = (FileReader *) handle;
|
||||||
|
return (FSStatus) reader->read(buffer, size * count);
|
||||||
|
|
||||||
|
}
|
||||||
|
FSStatus result = real_RPX_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(FSStatus, RPX_FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, uint32_t flags) {
|
||||||
|
// We check if the handle is part of our heap (the MemoryMapping Module allocates to 0x80000000)
|
||||||
|
if ((handle & 0xF0000000) == 0x80000000) {
|
||||||
|
auto reader = (FileReader *) handle;
|
||||||
|
delete reader;
|
||||||
|
return FS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_RPX_FSCloseFile(client, block, handle, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function_replacement_data_t rpx_utils_function_replacements[] = {
|
||||||
|
REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(HBM_NN_ACP_ACPGetTitleMetaXmlByDevice, 0x2E36CE44, 0x0E36CE44, FP_TARGET_PROCESS_HOME_MENU),
|
||||||
|
REPLACE_FUNCTION_FOR_PROCESS(RPX_FSOpenFile, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_HOME_MENU),
|
||||||
|
REPLACE_FUNCTION_FOR_PROCESS(RPX_FSReadFile, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_HOME_MENU),
|
||||||
|
REPLACE_FUNCTION_FOR_PROCESS(RPX_FSCloseFile, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_HOME_MENU),
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t rpx_utils_function_replacements_size = sizeof(rpx_utils_function_replacements) / sizeof(function_replacement_data_t);
|
||||||
|
|
||||||
|
static int parseINIhandler(void *user, const char *section, const char *name,
|
||||||
|
const char *value) {
|
||||||
|
DEBUG_FUNCTION_LINE("%s %s %s", section, name, value);
|
||||||
|
auto *fInfo = (BundleInformation *) user;
|
||||||
|
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||||
|
if (MATCH("menu", "longname")) {
|
||||||
|
strncpy(fInfo->longname, value, 64 - 1);
|
||||||
|
} else if (MATCH("menu", "shortname")) {
|
||||||
|
strncpy(fInfo->shortname, value, 64 - 1);
|
||||||
|
} else if (MATCH("menu", "author")) {
|
||||||
|
strncpy(fInfo->author, value, 64 - 1);
|
||||||
|
} else {
|
||||||
|
return 0; /* unknown section/name, error */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RL_LoadFromSDOnNextLaunch(const char *bundle_path) {
|
||||||
LOAD_REQUEST request;
|
LOAD_REQUEST request;
|
||||||
memset(&request, 0, sizeof(request));
|
memset(&request, 0, sizeof(request));
|
||||||
|
|
||||||
@ -19,22 +109,66 @@ bool loadRPXFromSDOnNextLaunch(const std::string &path) {
|
|||||||
|
|
||||||
romfs_fileInfo info;
|
romfs_fileInfo info;
|
||||||
|
|
||||||
std::string completePath = "/vol/external01/" + path;
|
bool metaLoaded = false;
|
||||||
|
|
||||||
|
std::string completePath = std::string("/vol/external01/") + bundle_path;
|
||||||
int res = getRPXInfoForPath(completePath, &info);
|
int res = getRPXInfoForPath(completePath, &info);
|
||||||
bool isBundle = false;
|
bool isBundle = false;
|
||||||
if (res >= 0) {
|
if (res >= 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Is bundle :)");
|
||||||
isBundle = true;
|
isBundle = true;
|
||||||
request.filesize = ((uint32_t * ) & info.length)[1];
|
request.filesize = ((uint32_t *) &info.length)[1];
|
||||||
request.fileoffset = ((uint32_t * ) & info.offset)[1];
|
request.fileoffset = ((uint32_t *) &info.offset)[1];
|
||||||
|
|
||||||
|
if (romfsMount("rcc", completePath.c_str(), RomfsSource_FileDescriptor_CafeOS) == 0) {
|
||||||
|
if (ini_parse("rcc:/meta/meta.ini", parseINIhandler, &gBundleInfo) < 0) {
|
||||||
|
DEBUG_FUNCTION_LINE("Failed to load and parse meta.ini");
|
||||||
|
} else {
|
||||||
|
metaLoaded = true;
|
||||||
|
}
|
||||||
|
FileReader *reader = nullptr;
|
||||||
|
|
||||||
|
if (CheckFile("rcc:/meta/iconTex.tga")) {
|
||||||
|
std::string path = "rcc:/meta/iconTex.tga";
|
||||||
|
reader = new FileReader(path);
|
||||||
|
} else if (CheckFile("rcc:/meta/iconTex.tga.gz")) {
|
||||||
|
std::string path = "rcc:/meta/iconTex.tga.gz";
|
||||||
|
reader = new FileReaderCompressed(path);
|
||||||
|
}
|
||||||
|
if (reader) {
|
||||||
|
uint32_t alreadyRead = 0;
|
||||||
|
uint32_t toRead = sizeof(gIconCache);
|
||||||
|
do {
|
||||||
|
int read = reader->read(reinterpret_cast<uint8_t *>(&gIconCache[alreadyRead]), toRead);
|
||||||
|
if (read <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
alreadyRead += read;
|
||||||
|
toRead -= read;
|
||||||
|
} while (alreadyRead < sizeof(gIconCache));
|
||||||
|
delete reader;
|
||||||
|
} else {
|
||||||
|
memset(gIconCache, 0, sizeof(gIconCache));
|
||||||
|
}
|
||||||
|
romfsUnmount("rcc");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_FUNCTION_LINE("not a bundle %s %d", completePath.c_str(), res);
|
DEBUG_FUNCTION_LINE("not a bundle %s %d", completePath.c_str(), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(request.path, path.c_str(), 255);
|
if (!metaLoaded) {
|
||||||
|
strncpy(gBundleInfo.author, bundle_path, 64 - 1);
|
||||||
|
strncpy(gBundleInfo.shortname, bundle_path, 64 - 1);
|
||||||
|
strncpy(gBundleInfo.longname, bundle_path, 64 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(request.path, bundle_path, 255);
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("Loading file %s size: %08X offset: %08X", request.path, request.filesize, request.fileoffset);
|
DEBUG_FUNCTION_LINE("Loading file %s size: %08X offset: %08X", request.path, request.filesize, request.fileoffset);
|
||||||
|
|
||||||
|
DCFlushRange(gIconCache, sizeof(gIconCache));
|
||||||
DCFlushRange(&request, sizeof(LOAD_REQUEST));
|
DCFlushRange(&request, sizeof(LOAD_REQUEST));
|
||||||
|
DCFlushRange(&gBundleInfo, sizeof(gBundleInfo));
|
||||||
|
|
||||||
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
|
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
|
||||||
if (mcpFd >= 0) {
|
if (mcpFd >= 0) {
|
||||||
@ -43,11 +177,11 @@ bool loadRPXFromSDOnNextLaunch(const std::string &path) {
|
|||||||
IOS_Close(mcpFd);
|
IOS_Close(mcpFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isBundle){
|
if (isBundle) {
|
||||||
gTryToReplaceOnNextLaunch = true;
|
gTryToReplaceOnNextLaunch = true;
|
||||||
memset(gLoadedBundlePath,0, sizeof(gLoadedBundlePath));
|
memset(gLoadedBundlePath, 0, sizeof(gLoadedBundlePath));
|
||||||
strncpy(gLoadedBundlePath, completePath.c_str(), completePath.length());
|
strncpy(gLoadedBundlePath, completePath.c_str(), completePath.length());
|
||||||
}else {
|
} else {
|
||||||
if (!gIsMounted) {
|
if (!gIsMounted) {
|
||||||
gTryToReplaceOnNextLaunch = false;
|
gTryToReplaceOnNextLaunch = false;
|
||||||
memset(gLoadedBundlePath, 0, sizeof(gLoadedBundlePath));
|
memset(gLoadedBundlePath, 0, sizeof(gLoadedBundlePath));
|
||||||
@ -60,4 +194,57 @@ bool loadRPXFromSDOnNextLaunch(const std::string &path) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WUMS_EXPORT_FUNCTION(loadRPXFromSDOnNextLaunch);
|
int32_t RL_MountBundle(const char *name, const char *path, RomfsSource source) {
|
||||||
|
return romfsMount(name, path, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RL_UnmountBundle(const char *name) {
|
||||||
|
return romfsUnmount(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RL_FileOpen(const char *name, uint32_t *handle) {
|
||||||
|
if (handle == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileReader *reader = nullptr;
|
||||||
|
std::string path = std::string(name);
|
||||||
|
std::string pathGZ = path + ".gz";
|
||||||
|
|
||||||
|
if (CheckFile(path.c_str())) {
|
||||||
|
reader = new FileReader(path);
|
||||||
|
} else if (CheckFile(pathGZ.c_str())) {
|
||||||
|
reader = new FileReaderCompressed(pathGZ);
|
||||||
|
} else {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (reader == nullptr) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
*handle = (uint32_t) reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RL_FileRead(uint32_t handle, uint8_t *buffer, uint32_t size) {
|
||||||
|
auto reader = (FileReader *) handle;
|
||||||
|
return reader->read(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RL_FileClose(uint32_t handle) {
|
||||||
|
auto reader = (FileReader *) handle;
|
||||||
|
delete reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RL_FileExists(const char *name) {
|
||||||
|
std::string checkgz = std::string(name) + ".gz";
|
||||||
|
return CheckFile(name) || CheckFile(checkgz.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_LoadFromSDOnNextLaunch);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_MountBundle);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_UnmountBundle);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_FileOpen);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_FileRead);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_FileClose);
|
||||||
|
WUMS_EXPORT_FUNCTION(RL_FileExists);
|
@ -1,5 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <function_patcher/function_patching.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct __attribute((packed)) {
|
typedef struct __attribute((packed)) {
|
||||||
uint32_t command;
|
uint32_t command;
|
||||||
uint32_t target;
|
uint32_t target;
|
||||||
@ -8,3 +15,11 @@ typedef struct __attribute((packed)) {
|
|||||||
char path[256];
|
char path[256];
|
||||||
} LOAD_REQUEST;
|
} LOAD_REQUEST;
|
||||||
|
|
||||||
|
extern function_replacement_data_t rpx_utils_function_replacements[];
|
||||||
|
extern uint32_t rpx_utils_function_replacements_size;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,4 +6,6 @@ bool gTryToReplaceOnNextLaunch __attribute__((section(".data"))) = false;
|
|||||||
char gLoadedBundlePath[256] __attribute__((section(".data")));
|
char gLoadedBundlePath[256] __attribute__((section(".data")));
|
||||||
char gSavePath[256] __attribute__((section(".data")));
|
char gSavePath[256] __attribute__((section(".data")));
|
||||||
char gWorkingDir[256] __attribute__((section(".data")));
|
char gWorkingDir[256] __attribute__((section(".data")));
|
||||||
char gTempPath[256] __attribute__((section(".data")));
|
|
||||||
|
bool gReplacedRPX __attribute__((section(".data"))) = false;
|
||||||
|
BundleInformation gBundleInfo __attribute__((section(".data")));
|
@ -1,10 +1,18 @@
|
|||||||
#include <wums.h>
|
#include <wums.h>
|
||||||
#include <coreinit/mutex.h>
|
#include <coreinit/mutex.h>
|
||||||
|
|
||||||
|
typedef struct BundleInformation_t {
|
||||||
|
char shortname[64];
|
||||||
|
char longname[64];
|
||||||
|
char author[64];
|
||||||
|
} BundleInformation;
|
||||||
|
|
||||||
|
|
||||||
extern bool gIsMounted;
|
extern bool gIsMounted;
|
||||||
extern bool gTryToReplaceOnNextLaunch;
|
extern bool gTryToReplaceOnNextLaunch;
|
||||||
extern char gLoadedBundlePath[256];
|
extern char gLoadedBundlePath[256];
|
||||||
extern char gSavePath[256];
|
extern char gSavePath[256];
|
||||||
extern char gWorkingDir[256];
|
extern char gWorkingDir[256];
|
||||||
extern char gTempPath[256];
|
extern uint32_t gCurrentHash;
|
||||||
extern uint32_t gCurrentHash;
|
extern bool gReplacedRPX;
|
||||||
|
extern BundleInformation gBundleInfo;
|
27
src/main.cpp
27
src/main.cpp
@ -11,17 +11,21 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
#include "FSDirReplacements.h"
|
#include "FSDirReplacements.h"
|
||||||
|
#include "RPXLoading.h"
|
||||||
|
|
||||||
#include <romfs_dev.h>
|
#include <romfs_dev.h>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <nn/act.h>
|
||||||
|
|
||||||
WUMS_MODULE_EXPORT_NAME("homebrew_rpx_loader");
|
WUMS_MODULE_EXPORT_NAME("homebrew_rpx_loader");
|
||||||
|
|
||||||
WUMS_INITIALIZE(args) {
|
WUMS_INITIALIZE(args) {
|
||||||
WHBLogUdpInit();
|
WHBLogUdpInit();
|
||||||
DEBUG_FUNCTION_LINE("Patch functions");
|
DEBUG_FUNCTION_LINE("Patch functions");
|
||||||
// we only patch static functions :)
|
// we only patch static functions, we don't need re-patch them and every launch
|
||||||
FunctionPatcherPatchFunction(fs_file_function_replacements, fs_file_function_replacements_size);
|
FunctionPatcherPatchFunction(fs_file_function_replacements, fs_file_function_replacements_size);
|
||||||
FunctionPatcherPatchFunction(fs_dir_function_replacements, fs_dir_function_replacements_size);
|
FunctionPatcherPatchFunction(fs_dir_function_replacements, fs_dir_function_replacements_size);
|
||||||
|
FunctionPatcherPatchFunction(rpx_utils_function_replacements, rpx_utils_function_replacements_size);
|
||||||
DEBUG_FUNCTION_LINE("Patch functions finished");
|
DEBUG_FUNCTION_LINE("Patch functions finished");
|
||||||
gIsMounted = false;
|
gIsMounted = false;
|
||||||
}
|
}
|
||||||
@ -32,10 +36,10 @@ WUMS_APPLICATION_ENDS() {
|
|||||||
if (gIsMounted) {
|
if (gIsMounted) {
|
||||||
romfsUnmount("rom");
|
romfsUnmount("rom");
|
||||||
gIsMounted = false;
|
gIsMounted = false;
|
||||||
|
DCFlushRange(&gIsMounted, sizeof(gIsMounted));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WUMS_APPLICATION_STARTS() {
|
WUMS_APPLICATION_STARTS() {
|
||||||
uint32_t upid = OSGetUPID();
|
uint32_t upid = OSGetUPID();
|
||||||
if (upid != 2 && upid != 15) {
|
if (upid != 2 && upid != 15) {
|
||||||
@ -43,19 +47,28 @@ WUMS_APPLICATION_STARTS() {
|
|||||||
}
|
}
|
||||||
WHBLogUdpInit();
|
WHBLogUdpInit();
|
||||||
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) != OSGetTitleID()) {
|
if (_SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY) != OSGetTitleID()) {
|
||||||
DEBUG_FUNCTION_LINE("gTryToReplaceOnNextLaunch and gIsMounted to FALSE");
|
DEBUG_FUNCTION_LINE("gTryToReplaceOnNextLaunch, gReplacedRPX and gIsMounted to FALSE");
|
||||||
|
gReplacedRPX = false;
|
||||||
gTryToReplaceOnNextLaunch = false;
|
gTryToReplaceOnNextLaunch = false;
|
||||||
gIsMounted = false;
|
gIsMounted = false;
|
||||||
|
DCFlushRange(&gReplacedRPX, sizeof(gReplacedRPX));
|
||||||
|
DCFlushRange(&gTryToReplaceOnNextLaunch, sizeof(gTryToReplaceOnNextLaunch));
|
||||||
|
DCFlushRange(&gIsMounted, sizeof(gIsMounted));
|
||||||
} else {
|
} else {
|
||||||
if (gTryToReplaceOnNextLaunch) {
|
if (gTryToReplaceOnNextLaunch) {
|
||||||
gCurrentHash = StringTools::hash(gLoadedBundlePath);
|
gCurrentHash = StringTools::hash(gLoadedBundlePath);
|
||||||
|
|
||||||
|
nn::act::Initialize();
|
||||||
|
nn::act::SlotNo slot = nn::act::GetSlotNo();
|
||||||
|
nn::act::Finalize();
|
||||||
|
|
||||||
std::string basePath = StringTools::strfmt("/vol/external01/wiiu/apps/save/%08X", gCurrentHash);
|
std::string basePath = StringTools::strfmt("/vol/external01/wiiu/apps/save/%08X", gCurrentHash);
|
||||||
std::string common = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/common", gCurrentHash);
|
std::string common = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/common", gCurrentHash);
|
||||||
std::string user = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/80000002", gCurrentHash);
|
std::string user = StringTools::strfmt("fs:/vol/external01/wiiu/apps/save/%08X/%08X", gCurrentHash, 0x80000000 | slot);
|
||||||
|
|
||||||
strncpy(gSavePath,basePath.c_str(), 255);
|
strncpy(gSavePath,basePath.c_str(), 255);
|
||||||
memset(gWorkingDir,0, sizeof(gWorkingDir));
|
memset(gWorkingDir, 0, sizeof(gWorkingDir));
|
||||||
|
DCFlushRange(gWorkingDir, sizeof(gWorkingDir));
|
||||||
|
|
||||||
CreateSubfolder(common.c_str());
|
CreateSubfolder(common.c_str());
|
||||||
CreateSubfolder(user.c_str());
|
CreateSubfolder(user.c_str());
|
||||||
@ -67,6 +80,10 @@ WUMS_APPLICATION_STARTS() {
|
|||||||
DEBUG_FUNCTION_LINE("MOUNTED FAILED %s", gLoadedBundlePath);
|
DEBUG_FUNCTION_LINE("MOUNTED FAILED %s", gLoadedBundlePath);
|
||||||
gIsMounted = false;
|
gIsMounted = false;
|
||||||
}
|
}
|
||||||
|
gReplacedRPX = true;
|
||||||
|
DCFlushRange(&gReplacedRPX, sizeof(gReplacedRPX));
|
||||||
|
DCFlushRange(&gIsMounted, sizeof(gIsMounted));
|
||||||
|
DCFlushRange(&gTryToReplaceOnNextLaunch, sizeof(gTryToReplaceOnNextLaunch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
src/utils/FileReader.cpp
Normal file
51
src/utils/FileReader.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include "FileReader.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int FileReader::read(uint8_t *buffer, int 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, 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 = -1;
|
||||||
|
if ((fd = open(path.c_str(), O_RDONLY)) >= 0) {
|
||||||
|
this->isReadFromFile = true;
|
||||||
|
this->isReadFromBuffer = false;
|
||||||
|
this->file_fd = fd;
|
||||||
|
} else {
|
||||||
|
DEBUG_FUNCTION_LINE("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 = false;
|
||||||
|
this->isReadFromFile = false;
|
||||||
|
}
|
27
src/utils/FileReader.h
Normal file
27
src/utils/FileReader.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
class FileReader {
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileReader(uint8_t *buffer, uint32_t size);
|
||||||
|
explicit FileReader(std::string &path);
|
||||||
|
|
||||||
|
virtual ~FileReader();
|
||||||
|
|
||||||
|
virtual int read(uint8_t *buffer, int 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;
|
||||||
|
};
|
82
src/utils/FileReaderCompressed.cpp
Normal file
82
src/utils/FileReaderCompressed.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "FileReaderCompressed.h"
|
||||||
|
|
||||||
|
int FileReaderCompressed::read(uint8_t *buffer, int size) {
|
||||||
|
int startValue = this->strm.total_out;
|
||||||
|
uint32_t newSize = 0;
|
||||||
|
int ret = 0;
|
||||||
|
do {
|
||||||
|
uint32_t nextOut = BUFFER_SIZE;
|
||||||
|
if (nextOut > size) {
|
||||||
|
nextOut = size;
|
||||||
|
}
|
||||||
|
if (this->strm.avail_in == 0) {
|
||||||
|
this->strm.avail_in = FileReader::read(this->zlib_in_buf, BUFFER_SIZE);
|
||||||
|
if (this->strm.avail_in == 0 || this->strm.avail_in == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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("Z_STREAM_ERROR");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
DEBUG_FUNCTION_LINE("Z_NEED_DICT");
|
||||||
|
ret = Z_DATA_ERROR; /* and fall through */
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
DEBUG_FUNCTION_LINE("Z_MEM_ERROR or Z_DATA_ERROR");
|
||||||
|
(void) inflateEnd(&this->strm);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("inflateInit2 failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileReaderCompressed::FileReaderCompressed(uint8_t *buffer, uint32_t size) : FileReader(buffer, size) {
|
||||||
|
this->initCompressedData();
|
||||||
|
}
|
28
src/utils/FileReaderCompressed.h
Normal file
28
src/utils/FileReaderCompressed.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include "FileReader.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 0x20000
|
||||||
|
|
||||||
|
class FileReaderCompressed : public FileReader {
|
||||||
|
public:
|
||||||
|
FileReaderCompressed(uint8_t *buffer, uint32_t size);
|
||||||
|
|
||||||
|
|
||||||
|
explicit FileReaderCompressed(std::string &file);
|
||||||
|
|
||||||
|
~FileReaderCompressed() override{
|
||||||
|
DEBUG_FUNCTION_LINE("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(uint8_t *buffer, int size) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initDone = false;
|
||||||
|
uint8_t zlib_in_buf[BUFFER_SIZE]{};
|
||||||
|
z_stream strm{};
|
||||||
|
|
||||||
|
void initCompressedData();
|
||||||
|
};
|
272
src/utils/ini.c
Normal file
272
src/utils/ini.c
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/* inih -- simple .INI file parser
|
||||||
|
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ini.h"
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_SECTION 50
|
||||||
|
#define MAX_NAME 50
|
||||||
|
|
||||||
|
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
size_t num_left;
|
||||||
|
} ini_parse_string_ctx;
|
||||||
|
|
||||||
|
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||||
|
static char* rstrip(char* s)
|
||||||
|
{
|
||||||
|
char* p = s + strlen(s);
|
||||||
|
while (p > s && isspace((unsigned char)(*--p)))
|
||||||
|
*p = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first non-whitespace char in given string. */
|
||||||
|
static char* lskip(const char* s)
|
||||||
|
{
|
||||||
|
while (*s && isspace((unsigned char)(*s)))
|
||||||
|
s++;
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||||
|
or pointer to null at end of string if neither found. Inline comment must
|
||||||
|
be prefixed by a whitespace character to register as a comment. */
|
||||||
|
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||||
|
{
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
int was_space = 0;
|
||||||
|
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||||
|
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||||
|
was_space = isspace((unsigned char)(*s));
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (*s && (!chars || !strchr(chars, *s))) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||||
|
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||||
|
{
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wstringop-truncation"
|
||||||
|
strncpy(dest, src, size - 1);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
dest[size - 1] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user)
|
||||||
|
{
|
||||||
|
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||||
|
#if INI_USE_STACK
|
||||||
|
char line[INI_MAX_LINE];
|
||||||
|
int max_line = INI_MAX_LINE;
|
||||||
|
#else
|
||||||
|
char* line;
|
||||||
|
int max_line = INI_INITIAL_ALLOC;
|
||||||
|
#endif
|
||||||
|
#if INI_ALLOW_REALLOC
|
||||||
|
char* new_line;
|
||||||
|
int offset;
|
||||||
|
#endif
|
||||||
|
char section[MAX_SECTION] = "";
|
||||||
|
char prev_name[MAX_NAME] = "";
|
||||||
|
|
||||||
|
char* start;
|
||||||
|
char* end;
|
||||||
|
char* name;
|
||||||
|
char* value;
|
||||||
|
int lineno = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
line = (char*)malloc(INI_INITIAL_ALLOC);
|
||||||
|
if (!line) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||||
|
#else
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Scan through stream line by line */
|
||||||
|
while (reader(line, max_line, stream) != NULL) {
|
||||||
|
#if INI_ALLOW_REALLOC
|
||||||
|
offset = strlen(line);
|
||||||
|
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||||
|
max_line *= 2;
|
||||||
|
if (max_line > INI_MAX_LINE)
|
||||||
|
max_line = INI_MAX_LINE;
|
||||||
|
new_line = realloc(line, max_line);
|
||||||
|
if (!new_line) {
|
||||||
|
free(line);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
line = new_line;
|
||||||
|
if (reader(line + offset, max_line - offset, stream) == NULL)
|
||||||
|
break;
|
||||||
|
if (max_line >= INI_MAX_LINE)
|
||||||
|
break;
|
||||||
|
offset += strlen(line + offset);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lineno++;
|
||||||
|
|
||||||
|
start = line;
|
||||||
|
#if INI_ALLOW_BOM
|
||||||
|
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||||
|
(unsigned char)start[1] == 0xBB &&
|
||||||
|
(unsigned char)start[2] == 0xBF) {
|
||||||
|
start += 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
start = lskip(rstrip(start));
|
||||||
|
|
||||||
|
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||||
|
/* Start-of-line comment */
|
||||||
|
}
|
||||||
|
#if INI_ALLOW_MULTILINE
|
||||||
|
else if (*prev_name && *start && start > line) {
|
||||||
|
/* Non-blank line with leading whitespace, treat as continuation
|
||||||
|
of previous name's value (as per Python configparser). */
|
||||||
|
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (*start == '[') {
|
||||||
|
/* A "[section]" line */
|
||||||
|
end = find_chars_or_comment(start + 1, "]");
|
||||||
|
if (*end == ']') {
|
||||||
|
*end = '\0';
|
||||||
|
strncpy0(section, start + 1, sizeof(section));
|
||||||
|
*prev_name = '\0';
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No ']' found on section line */
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*start) {
|
||||||
|
/* Not a comment, must be a name[=:]value pair */
|
||||||
|
end = find_chars_or_comment(start, "=:");
|
||||||
|
if (*end == '=' || *end == ':') {
|
||||||
|
*end = '\0';
|
||||||
|
name = rstrip(start);
|
||||||
|
value = end + 1;
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
end = find_chars_or_comment(value, NULL);
|
||||||
|
if (*end)
|
||||||
|
*end = '\0';
|
||||||
|
#endif
|
||||||
|
value = lskip(value);
|
||||||
|
rstrip(value);
|
||||||
|
|
||||||
|
/* Valid name[=:]value pair found, call handler */
|
||||||
|
strncpy0(prev_name, name, sizeof(prev_name));
|
||||||
|
if (!HANDLER(user, section, name, value) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No '=' or ':' found on name[=:]value line */
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INI_STOP_ON_FIRST_ERROR
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
free(line);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
file = fopen(filename, "r");
|
||||||
|
if (!file)
|
||||||
|
return -1;
|
||||||
|
error = ini_parse_file(file, handler, user);
|
||||||
|
fclose(file);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An ini_reader function to read the next line from a string buffer. This
|
||||||
|
is the fgets() equivalent used by ini_parse_string(). */
|
||||||
|
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||||
|
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||||
|
const char* ctx_ptr = ctx->ptr;
|
||||||
|
size_t ctx_num_left = ctx->num_left;
|
||||||
|
char* strp = str;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (ctx_num_left == 0 || num < 2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (num > 1 && ctx_num_left != 0) {
|
||||||
|
c = *ctx_ptr++;
|
||||||
|
ctx_num_left--;
|
||||||
|
*strp++ = c;
|
||||||
|
if (c == '\n')
|
||||||
|
break;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
*strp = '\0';
|
||||||
|
ctx->ptr = ctx_ptr;
|
||||||
|
ctx->num_left = ctx_num_left;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||||
|
ini_parse_string_ctx ctx;
|
||||||
|
|
||||||
|
ctx.ptr = string;
|
||||||
|
ctx.num_left = strlen(string);
|
||||||
|
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||||
|
user);
|
||||||
|
}
|
130
src/utils/ini.h
Normal file
130
src/utils/ini.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/* inih -- simple .INI file parser
|
||||||
|
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __INI_H__
|
||||||
|
#define __INI_H__
|
||||||
|
|
||||||
|
/* Make this header file easier to include in C++ code */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||||
|
#ifndef INI_HANDLER_LINENO
|
||||||
|
#define INI_HANDLER_LINENO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of handler function. */
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value,
|
||||||
|
int lineno);
|
||||||
|
#else
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of fgets-style reader function. */
|
||||||
|
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||||
|
|
||||||
|
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||||
|
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||||
|
is "" if name=value pair parsed before any section heading. name:value
|
||||||
|
pairs are also supported as a concession to Python's configparser.
|
||||||
|
|
||||||
|
For each name=value pair parsed, call handler function with given user
|
||||||
|
pointer as well as section, name, and value (data only valid for duration
|
||||||
|
of handler call). Handler should return nonzero on success, zero on error.
|
||||||
|
|
||||||
|
Returns 0 on success, line number of first error on parse error (doesn't
|
||||||
|
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||||
|
error (only when INI_USE_STACK is zero).
|
||||||
|
*/
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||||
|
close the file when it's finished -- the caller must do that. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||||
|
filename. Used for implementing custom or string-based I/O (see also
|
||||||
|
ini_parse_string). */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||||
|
instead of a file. Useful for parsing INI data from a network socket or
|
||||||
|
already in memory. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||||
|
configparser. If allowed, ini_parse() will call the handler with the same
|
||||||
|
name for each subsequent line parsed. */
|
||||||
|
#ifndef INI_ALLOW_MULTILINE
|
||||||
|
#define INI_ALLOW_MULTILINE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||||
|
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
||||||
|
#ifndef INI_ALLOW_BOM
|
||||||
|
#define INI_ALLOW_BOM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||||
|
both ; and # comments at the start of a line by default. */
|
||||||
|
#ifndef INI_START_COMMENT_PREFIXES
|
||||||
|
#define INI_START_COMMENT_PREFIXES ";#"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||||
|
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||||
|
Python 3.2+ configparser behaviour. */
|
||||||
|
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||||
|
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||||
|
#endif
|
||||||
|
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||||
|
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||||
|
#ifndef INI_USE_STACK
|
||||||
|
#define INI_USE_STACK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||||
|
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||||
|
#ifndef INI_MAX_LINE
|
||||||
|
#define INI_MAX_LINE 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||||
|
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||||
|
zero. */
|
||||||
|
#ifndef INI_ALLOW_REALLOC
|
||||||
|
#define INI_ALLOW_REALLOC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||||
|
is zero. */
|
||||||
|
#ifndef INI_INITIAL_ALLOC
|
||||||
|
#define INI_INITIAL_ALLOC 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stop parsing on first error (default is to keep parsing). */
|
||||||
|
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||||
|
#define INI_STOP_ON_FIRST_ERROR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __INI_H__ */
|
Loading…
Reference in New Issue
Block a user