mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-27 03:33:16 -03:00
Many changes. Good things are coming.
This commit is contained in:
parent
59e48748ae
commit
edd7eee294
11 changed files with 4060 additions and 3712 deletions
3
Makefile
3
Makefile
|
@ -56,6 +56,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__SWITCH__ -D__LINUX_ERRNO_EXTENSIONS__
|
CFLAGS += $(INCLUDE) -D__SWITCH__ -D__LINUX_ERRNO_EXTENSIONS__
|
||||||
|
CFLAGS += `freetype-config --cflags`
|
||||||
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
|
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
|
||||||
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags`
|
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags`
|
||||||
CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
|
CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
|
||||||
|
@ -65,7 +66,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lcurl -lxml2 -lz -lnx -ljson-c -lm
|
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lnx -ljson-c -lm `freetype-config --libs`
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
|
498
source/dumper.c
498
source/dumper.c
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,7 @@
|
||||||
#define HFS0_SIZE_ADDR 0x138
|
#define HFS0_SIZE_ADDR 0x138
|
||||||
#define HFS0_MAGIC 0x48465330 // "HFS0"
|
#define HFS0_MAGIC 0x48465330 // "HFS0"
|
||||||
#define HFS0_FILE_COUNT_ADDR 0x04
|
#define HFS0_FILE_COUNT_ADDR 0x04
|
||||||
|
#define HFS0_STR_TABLE_SIZE_ADDR 0x08
|
||||||
#define HFS0_ENTRY_TABLE_ADDR 0x10
|
#define HFS0_ENTRY_TABLE_ADDR 0x10
|
||||||
|
|
||||||
#define GAMECARD_TYPE1_PARTITION_CNT 3 // "update" (0), "normal" (1), "update" (2)
|
#define GAMECARD_TYPE1_PARTITION_CNT 3 // "update" (0), "normal" (1), "update" (2)
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#define GAMECARD_SIZE_16GiB (u64)0x400000000
|
#define GAMECARD_SIZE_16GiB (u64)0x400000000
|
||||||
#define GAMECARD_SIZE_32GiB (u64)0x800000000
|
#define GAMECARD_SIZE_32GiB (u64)0x800000000
|
||||||
|
|
||||||
|
/* Reference: https://switchbrew.org/wiki/Title_list */
|
||||||
#define GAMECARD_UPDATE_TITLEID (u64)0x0100000000000816
|
#define GAMECARD_UPDATE_TITLEID (u64)0x0100000000000816
|
||||||
|
|
||||||
#define SYSUPDATE_100 (u32)450
|
#define SYSUPDATE_100 (u32)450
|
||||||
|
@ -56,6 +58,13 @@
|
||||||
#define SYSUPDATE_501 (u32)335609886
|
#define SYSUPDATE_501 (u32)335609886
|
||||||
#define SYSUPDATE_502 (u32)335675432
|
#define SYSUPDATE_502 (u32)335675432
|
||||||
#define SYSUPDATE_510 (u32)336592976
|
#define SYSUPDATE_510 (u32)336592976
|
||||||
|
#define SYSUPDATE_600 (u32)402653544
|
||||||
|
#define SYSUPDATE_601 (u32)402718730
|
||||||
|
#define SYSUPDATE_610 (u32)403701850
|
||||||
|
#define SYSUPDATE_620 (u32)404750376
|
||||||
|
#define SYSUPDATE_700 (u32)469762248
|
||||||
|
#define SYSUPDATE_701 (u32)469827614
|
||||||
|
#define SYSUPDATE_800 (u32)536871442
|
||||||
|
|
||||||
#define bswap_32(a) ((((a) << 24) & 0xff000000) | (((a) << 8) & 0xff0000) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
|
#define bswap_32(a) ((((a) << 24) & 0xff000000) | (((a) << 8) & 0xff0000) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
|
||||||
#define round_up(x, y) ((x) + (((y) - ((x) % (y))) % (y))) // Aligns 'x' bytes to a 'y' bytes boundary
|
#define round_up(x, y) ((x) + (((y) - ((x) % (y))) % (y))) // Aligns 'x' bytes to a 'y' bytes boundary
|
||||||
|
|
149
source/fsext.c
149
source/fsext.c
|
@ -5,7 +5,7 @@
|
||||||
#include "fsext.h"
|
#include "fsext.h"
|
||||||
|
|
||||||
// IFileSystemProxy
|
// IFileSystemProxy
|
||||||
Result fsOpenGameCardStorage(FsStorage* out, u32 handle, u32 partition)
|
Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, u32 partition)
|
||||||
{
|
{
|
||||||
IpcCommand c;
|
IpcCommand c;
|
||||||
ipcInitialize(&c);
|
ipcInitialize(&c);
|
||||||
|
@ -17,11 +17,11 @@ Result fsOpenGameCardStorage(FsStorage* out, u32 handle, u32 partition)
|
||||||
u32 partition;
|
u32 partition;
|
||||||
} *raw;
|
} *raw;
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
raw->magic = SFCI_MAGIC;
|
||||||
raw->cmd_id = 30;
|
raw->cmd_id = 30;
|
||||||
raw->handle = handle;
|
raw->handle = handle->value;
|
||||||
raw->partition = partition;
|
raw->partition = partition;
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(fsGetServiceSession());
|
Result rc = serviceIpcDispatch(fsGetServiceSession());
|
||||||
|
@ -29,22 +29,24 @@ Result fsOpenGameCardStorage(FsStorage* out, u32 handle, u32 partition)
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
IpcParsedCommand r;
|
IpcParsedCommand r;
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u64 magic;
|
u64 magic;
|
||||||
u64 result;
|
u64 result;
|
||||||
} *resp = r.Raw;
|
} *resp;
|
||||||
|
|
||||||
|
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
|
||||||
|
resp = r.Raw;
|
||||||
|
|
||||||
rc = resp->result;
|
rc = resp->result;
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) serviceCreate(&out->s, r.Handles[0]);
|
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result fsOpenGameCardFileSystem(FsFileSystem* out, u32 handle, u32 partition)
|
Result fsOpenGameCardFileSystem(FsFileSystem* out, const FsGameCardHandle* handle, u32 partition)
|
||||||
{
|
{
|
||||||
IpcCommand c;
|
IpcCommand c;
|
||||||
ipcInitialize(&c);
|
ipcInitialize(&c);
|
||||||
|
@ -56,11 +58,11 @@ Result fsOpenGameCardFileSystem(FsFileSystem* out, u32 handle, u32 partition)
|
||||||
u32 partition;
|
u32 partition;
|
||||||
} *raw;
|
} *raw;
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
raw->magic = SFCI_MAGIC;
|
||||||
raw->cmd_id = 31;
|
raw->cmd_id = 31;
|
||||||
raw->handle = handle;
|
raw->handle = handle->value;
|
||||||
raw->partition = partition;
|
raw->partition = partition;
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(fsGetServiceSession());
|
Result rc = serviceIpcDispatch(fsGetServiceSession());
|
||||||
|
@ -68,95 +70,62 @@ Result fsOpenGameCardFileSystem(FsFileSystem* out, u32 handle, u32 partition)
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
IpcParsedCommand r;
|
IpcParsedCommand r;
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u64 magic;
|
u64 magic;
|
||||||
u64 result;
|
u64 result;
|
||||||
} *resp = r.Raw;
|
} *resp;
|
||||||
|
|
||||||
|
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
|
||||||
|
resp = r.Raw;
|
||||||
|
|
||||||
rc = resp->result;
|
rc = resp->result;
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) serviceCreate(&out->s, r.Handles[0]);
|
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out)
|
||||||
|
{
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u64 magic;
|
||||||
|
u64 cmd_id;
|
||||||
|
} *raw;
|
||||||
|
|
||||||
|
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCI_MAGIC;
|
||||||
|
raw->cmd_id = 501;
|
||||||
|
|
||||||
|
Result rc = serviceIpcDispatch(fsGetServiceSession());
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
{
|
||||||
|
IpcParsedCommand r;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *resp;
|
||||||
|
|
||||||
|
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
|
||||||
|
resp = r.Raw;
|
||||||
|
|
||||||
|
rc = resp->result;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDeviceOperator
|
// IDeviceOperator
|
||||||
Result fsDeviceOperatorIsGameCardInserted(FsDeviceOperator* d, bool* out)
|
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, const FsGameCardHandle* handle, u32* out_title_version, u64* out_title_id)
|
||||||
{
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 200;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(&d->s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc))
|
|
||||||
{
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
u8 is_inserted;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) *out = resp->is_inserted != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result fsDeviceOperatorGetGameCardHandle(FsDeviceOperator* d, u32* out)
|
|
||||||
{
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 202;
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(&d->s);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc))
|
|
||||||
{
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
u32 handle;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) *out = resp->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, u32 handle, u32* out_title_version, u64* out_title_id)
|
|
||||||
{
|
{
|
||||||
IpcCommand c;
|
IpcCommand c;
|
||||||
ipcInitialize(&c);
|
ipcInitialize(&c);
|
||||||
|
@ -167,25 +136,27 @@ Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, u32 handle, u32*
|
||||||
u32 handle;
|
u32 handle;
|
||||||
} *raw;
|
} *raw;
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
raw = serviceIpcPrepareHeader(&d->s, &c, sizeof(*raw));
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
raw->magic = SFCI_MAGIC;
|
||||||
raw->cmd_id = 203;
|
raw->cmd_id = 203;
|
||||||
raw->handle = handle;
|
raw->handle = handle->value;
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(&d->s);
|
Result rc = serviceIpcDispatch(&d->s);
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
IpcParsedCommand r;
|
IpcParsedCommand r;
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u64 magic;
|
u64 magic;
|
||||||
u64 result;
|
u64 result;
|
||||||
u32 title_ver;
|
u32 title_ver;
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
} *resp = r.Raw;
|
} *resp;
|
||||||
|
|
||||||
|
serviceIpcParse(&d->s, &r, sizeof(*resp));
|
||||||
|
resp = r.Raw;
|
||||||
|
|
||||||
rc = resp->result;
|
rc = resp->result;
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,11 @@
|
||||||
#include <switch/services/fs.h>
|
#include <switch/services/fs.h>
|
||||||
|
|
||||||
// IFileSystemProxy
|
// IFileSystemProxy
|
||||||
Result fsOpenGameCardStorage(FsStorage* out, u32 handle, u32 partition);
|
Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, u32 partition);
|
||||||
Result fsOpenGameCardFileSystem(FsFileSystem* out, u32 handle, u32 partition);
|
Result fsOpenGameCardFileSystem(FsFileSystem* out, const FsGameCardHandle* handle, u32 partition);
|
||||||
|
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out);
|
||||||
|
|
||||||
// IDeviceOperator
|
// IDeviceOperator
|
||||||
Result fsDeviceOperatorIsGameCardInserted(FsDeviceOperator* d, bool* out);
|
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, const FsGameCardHandle* handle, u32* out_title_version, u64* out_title_id);
|
||||||
Result fsDeviceOperatorGetGameCardHandle(FsDeviceOperator* d, u32* out);
|
|
||||||
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator* d, u32 handle, u32* out_title_version, u64* out_title_id);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
242
source/main.c
242
source/main.c
|
@ -1,4 +1,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
@ -6,133 +8,82 @@
|
||||||
#include "dumper.h"
|
#include "dumper.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "fsext.h"
|
||||||
|
|
||||||
FsDeviceOperator fsOperatorInstance;
|
/* Extern variables */
|
||||||
|
|
||||||
bool gameCardInserted;
|
extern FsDeviceOperator fsOperatorInstance;
|
||||||
|
|
||||||
u64 gameCardSize = 0, trimmedCardSize = 0;
|
extern FsEventNotifier fsGameCardEventNotifier;
|
||||||
char gameCardSizeStr[32] = {'\0'}, trimmedCardSizeStr[32] = {'\0'};
|
extern Handle fsGameCardEventHandle;
|
||||||
|
extern Event fsGameCardKernelEvent;
|
||||||
|
extern UEvent exitEvent;
|
||||||
|
|
||||||
char *hfs0_header = NULL;
|
extern char *hfs0_header;
|
||||||
u64 hfs0_offset = 0, hfs0_size = 0;
|
extern char *partitionHfs0Header;
|
||||||
u32 hfs0_partition_cnt = 0;
|
|
||||||
|
|
||||||
//char *partitionHfs0Header = NULL;
|
extern bool gameCardInserted;
|
||||||
//u64 partitionHfs0HeaderSize = 0;
|
|
||||||
|
|
||||||
u64 gameCardTitleID = 0;
|
int main(int argc, char *argv[])
|
||||||
u32 gameCardVersion = 0;
|
|
||||||
char gameCardName[0x201] = {'\0'}, fixedGameCardName[0x201] = {'\0'}, gameCardAuthor[0x101] = {'\0'}, gameCardVersionStr[64] = {'\0'};
|
|
||||||
|
|
||||||
u64 gameCardUpdateTitleID = 0;
|
|
||||||
u32 gameCardUpdateVersion = 0;
|
|
||||||
char gameCardUpdateVersionStr[128] = {'\0'};
|
|
||||||
|
|
||||||
u32 currentFBWidth, currentFBHeight;
|
|
||||||
u8 *currentFB;
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
{
|
||||||
gfxInitResolutionDefault();
|
/* Initialize UI */
|
||||||
gfxInitDefault();
|
if (!uiInit()) return -1;
|
||||||
gfxConfigureAutoResolutionDefault(true);
|
|
||||||
|
|
||||||
uiInit();
|
|
||||||
|
|
||||||
currentFB = gfxGetFramebuffer(¤tFBWidth, ¤tFBHeight);
|
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
Result result;
|
Result result;
|
||||||
char strbuf[512] = {'\0'};
|
char strbuf[512] = {'\0'};
|
||||||
|
|
||||||
if (R_SUCCEEDED(result = fsInitialize()))
|
/* Initialize the fsp-srv service */
|
||||||
|
result = fsInitialize();
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
if (R_SUCCEEDED(result = fsOpenDeviceOperator(&fsOperatorInstance)))
|
/* Open device operator */
|
||||||
|
result = fsOpenDeviceOperator(&fsOperatorInstance);
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
if (R_SUCCEEDED(result = ncmInitialize()))
|
/* Initialize the ncm service */
|
||||||
|
result = ncmInitialize();
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
if (R_SUCCEEDED(result = nsInitialize()))
|
/* Initialize the ns service */
|
||||||
|
result = nsInitialize();
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
if (R_SUCCEEDED(result = timeInitialize()))
|
/* Initialize the time service */
|
||||||
|
result = timeInitialize();
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
bool exitLoop = false;
|
/* Open gamecard detection event notifier */
|
||||||
|
result = fsOpenGameCardDetectionEventNotifier(&fsGameCardEventNotifier);
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
/* Retrieve kernel event handle */
|
||||||
|
result = fsEventNotifierGetEventHandle(&fsGameCardEventNotifier, &fsGameCardEventHandle);
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
/* Retrieve initial gamecard status */
|
||||||
|
gameCardInserted = isGameCardInserted();
|
||||||
|
|
||||||
|
/* Load gamecard detection kernel event */
|
||||||
|
eventLoadRemote(&fsGameCardKernelEvent, fsGameCardEventHandle, false);
|
||||||
|
|
||||||
|
/* Create usermode exit event */
|
||||||
|
ueventCreate(&exitEvent, false);
|
||||||
|
|
||||||
|
/* Create gamecard detection thread */
|
||||||
|
Thread thread;
|
||||||
|
result = threadCreate(&thread, fsGameCardDetectionThreadFunc, NULL, 0x10000, 0x2C, -2);
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
/* Start gamecard detection thread */
|
||||||
|
result = threadStart(&thread);
|
||||||
|
if (R_SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
/* Main application loop */
|
||||||
|
bool exitLoop = false;
|
||||||
while(appletMainLoop())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
currentFB = gfxGetFramebuffer(¤tFBWidth, ¤tFBHeight);
|
UIResult result = uiProcess();
|
||||||
|
|
||||||
uiPrintHeadline();
|
|
||||||
|
|
||||||
gameCardInserted = isGameCardInserted(&fsOperatorInstance);
|
|
||||||
|
|
||||||
if (gameCardInserted)
|
|
||||||
{
|
|
||||||
if (hfs0_header == NULL)
|
|
||||||
{
|
|
||||||
// Don't access the gamecard immediately to avoid conflicts with the fsp-srv, ncm and ns services
|
|
||||||
uiPleaseWait();
|
|
||||||
|
|
||||||
if (getRootHfs0Header(&fsOperatorInstance))
|
|
||||||
{
|
|
||||||
if (getGameCardTitleIDAndVersion(&gameCardTitleID, &gameCardVersion))
|
|
||||||
{
|
|
||||||
convertTitleVersionToDecimal(gameCardVersion, gameCardVersionStr, sizeof(gameCardVersionStr));
|
|
||||||
|
|
||||||
getGameCardControlNacp(gameCardTitleID, gameCardName, sizeof(gameCardName), gameCardAuthor, sizeof(gameCardAuthor));
|
|
||||||
|
|
||||||
strtrim(gameCardName);
|
|
||||||
if (strlen(gameCardName))
|
|
||||||
{
|
|
||||||
snprintf(fixedGameCardName, sizeof(fixedGameCardName) / sizeof(fixedGameCardName[0]), "%s", gameCardName);
|
|
||||||
removeIllegalCharacters(fixedGameCardName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uiPrintHeadline();
|
|
||||||
uiUpdateStatusMsg();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (hfs0_header != NULL)
|
|
||||||
{
|
|
||||||
gameCardSize = 0;
|
|
||||||
memset(gameCardSizeStr, 0, sizeof(gameCardSizeStr));
|
|
||||||
|
|
||||||
trimmedCardSize = 0;
|
|
||||||
memset(trimmedCardSizeStr, 0, sizeof(trimmedCardSizeStr));
|
|
||||||
|
|
||||||
free(hfs0_header);
|
|
||||||
hfs0_header = NULL;
|
|
||||||
hfs0_offset = hfs0_size = 0;
|
|
||||||
hfs0_partition_cnt = 0;
|
|
||||||
|
|
||||||
/*if (partitionHfs0Header != NULL)
|
|
||||||
{
|
|
||||||
free(partitionHfs0Header);
|
|
||||||
partitionHfs0Header = NULL;
|
|
||||||
partitionHfs0HeaderSize = 0;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
gameCardTitleID = 0;
|
|
||||||
gameCardVersion = 0;
|
|
||||||
|
|
||||||
memset(gameCardName, 0, sizeof(gameCardName));
|
|
||||||
memset(fixedGameCardName, 0, sizeof(fixedGameCardName));
|
|
||||||
memset(gameCardAuthor, 0, sizeof(gameCardAuthor));
|
|
||||||
memset(gameCardVersionStr, 0, sizeof(gameCardVersionStr));
|
|
||||||
|
|
||||||
gameCardUpdateTitleID = 0;
|
|
||||||
gameCardUpdateVersion = 0;
|
|
||||||
|
|
||||||
memset(gameCardUpdateVersionStr, 0, sizeof(gameCardUpdateVersionStr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hidScanInput();
|
|
||||||
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
|
||||||
|
|
||||||
UIResult result = uiLoop(keysDown);
|
|
||||||
switch(result)
|
switch(result)
|
||||||
{
|
{
|
||||||
case resultShowMainMenu:
|
case resultShowMainMenu:
|
||||||
|
@ -185,62 +136,107 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitLoop) break;
|
if (exitLoop) break;
|
||||||
|
|
||||||
syncDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Signal the exit event to terminate the gamecard detection thread */
|
||||||
|
ueventSignal(&exitEvent);
|
||||||
|
|
||||||
|
/* Wait for the gamecard detection thread to exit */
|
||||||
|
threadWaitForExit(&thread);
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to start gamecard detection thread! (0x%08X)", result);
|
||||||
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
|
uiRefreshDisplay();
|
||||||
|
delay(5);
|
||||||
|
ret = -10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close gamecard detection thread */
|
||||||
|
threadClose(&thread);
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to create gamecard detection thread! (0x%08X)", result);
|
||||||
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
|
uiRefreshDisplay();
|
||||||
|
delay(5);
|
||||||
|
ret = -9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close kernel event */
|
||||||
|
eventClose(&fsGameCardKernelEvent);
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to retrieve gamecard detection event handle! (0x%08X)", result);
|
||||||
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
|
uiRefreshDisplay();
|
||||||
|
delay(5);
|
||||||
|
ret = -8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close gamecard event notifier */
|
||||||
|
fsEventNotifierClose(&fsGameCardEventNotifier);
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open gamecard detection event notifier! (0x%08X)", result);
|
||||||
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
|
uiRefreshDisplay();
|
||||||
|
delay(5);
|
||||||
|
ret = -7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Denitialize the time service */
|
||||||
timeExit();
|
timeExit();
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the time service! (0x%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the time service! (0x%08X)", result);
|
||||||
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
delay(5);
|
delay(5);
|
||||||
ret = -5;
|
ret = -6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Denitialize the ns service */
|
||||||
nsExit();
|
nsExit();
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ns service! (0x%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ns service! (0x%08X)", result);
|
||||||
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
delay(5);
|
delay(5);
|
||||||
ret = -4;
|
ret = -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Denitialize the ncm service */
|
||||||
ncmExit();
|
ncmExit();
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ncm service! (0x%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the ncm service! (0x%08X)", result);
|
||||||
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
delay(5);
|
delay(5);
|
||||||
ret = -3;
|
ret = -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Close device operator */
|
||||||
fsDeviceOperatorClose(&fsOperatorInstance);
|
fsDeviceOperatorClose(&fsOperatorInstance);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open device operator! (0x%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open device operator! (0x%08X)", result);
|
||||||
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
delay(5);
|
delay(5);
|
||||||
ret = -2;
|
ret = -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Denitialize the fs-srv service */
|
||||||
fsExit();
|
fsExit();
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the fsp-srv service! (0x%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to initialize the fsp-srv service! (0x%08X)", result);
|
||||||
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
uiDrawString(strbuf, 0, 0, 255, 255, 255);
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
delay(5);
|
delay(5);
|
||||||
ret = -1;
|
ret = -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free resources */
|
||||||
if (hfs0_header != NULL) free(hfs0_header);
|
if (hfs0_header != NULL) free(hfs0_header);
|
||||||
|
if (partitionHfs0Header != NULL) free(partitionHfs0Header);
|
||||||
|
|
||||||
//if (partitionHfs0Header != NULL) free(partitionHfs0Header);
|
/* Deinitialize UI */
|
||||||
|
|
||||||
uiDeinit();
|
uiDeinit();
|
||||||
|
|
||||||
gfxExit();
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
696
source/ui.c
696
source/ui.c
File diff suppressed because it is too large
Load diff
38
source/ui.h
38
source/ui.h
|
@ -3,8 +3,25 @@
|
||||||
#ifndef __UI_H__
|
#ifndef __UI_H__
|
||||||
#define __UI_H__
|
#define __UI_H__
|
||||||
|
|
||||||
#define FILENAME_BUFFER_SIZE (1024 * 32) // 32 KiB
|
#define FB_WIDTH 1280
|
||||||
#define FILENAME_MAX_CNT 2048
|
#define FB_HEIGHT 720
|
||||||
|
|
||||||
|
#define CHAR_PT_SIZE 12
|
||||||
|
#define SCREEN_DPI_CNT 96
|
||||||
|
|
||||||
|
#define BG_COLOR_RGB 50
|
||||||
|
|
||||||
|
#define HIGHLIGHT_BG_COLOR_R 33
|
||||||
|
#define HIGHLIGHT_BG_COLOR_G 34
|
||||||
|
#define HIGHLIGHT_BG_COLOR_B 39
|
||||||
|
|
||||||
|
#define HIGHLIGHT_FONT_COLOR_R 0
|
||||||
|
#define HIGHLIGHT_FONT_COLOR_G 255
|
||||||
|
#define HIGHLIGHT_FONT_COLOR_B 197
|
||||||
|
|
||||||
|
#define XCIDUMP_OPTIONS_X_POS (35 * CHAR_PT_SIZE)
|
||||||
|
|
||||||
|
#define TAB_WIDTH 4
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
resultNone,
|
resultNone,
|
||||||
|
@ -43,24 +60,31 @@ typedef enum {
|
||||||
} UIState;
|
} UIState;
|
||||||
|
|
||||||
void uiFill(int x, int y, int width, int height, u8 r, u8 g, u8 b);
|
void uiFill(int x, int y, int width, int height, u8 r, u8 g, u8 b);
|
||||||
|
|
||||||
void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b);
|
void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b);
|
||||||
|
|
||||||
|
void uiRefreshDisplay();
|
||||||
|
|
||||||
void uiStatusMsg(const char *fmt, ...);
|
void uiStatusMsg(const char *fmt, ...);
|
||||||
|
|
||||||
void uiUpdateStatusMsg();
|
void uiUpdateStatusMsg();
|
||||||
|
|
||||||
void uiPleaseWait();
|
void uiPleaseWait();
|
||||||
|
|
||||||
void uiUpdateFreeSpace();
|
void uiUpdateFreeSpace();
|
||||||
|
|
||||||
void uiInit();
|
void uiClearScreen();
|
||||||
|
|
||||||
|
void uiPrintHeadline();
|
||||||
|
|
||||||
|
int uiInit();
|
||||||
|
|
||||||
void uiDeinit();
|
void uiDeinit();
|
||||||
|
|
||||||
void uiSetState(UIState state);
|
void uiSetState(UIState state);
|
||||||
|
|
||||||
UIState uiGetState();
|
UIState uiGetState();
|
||||||
|
|
||||||
void uiClearScreen();
|
UIResult uiProcess();
|
||||||
void uiPrintHeadline();
|
|
||||||
|
|
||||||
UIResult uiLoop(u32 keysDown);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
475
source/util.c
475
source/util.c
|
@ -20,11 +20,15 @@
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
extern int breaks;
|
/* Extern variables */
|
||||||
|
|
||||||
extern u64 gameCardSize;
|
extern int breaks;
|
||||||
extern u64 gameCardTitleID;
|
extern int font_height;
|
||||||
extern u32 hfs0_partition_cnt;
|
|
||||||
|
extern int cursor;
|
||||||
|
extern int scroll;
|
||||||
|
|
||||||
|
/* Constants */
|
||||||
|
|
||||||
const char *nswReleasesXmlUrl = "http://nswdb.com/xml.php";
|
const char *nswReleasesXmlUrl = "http://nswdb.com/xml.php";
|
||||||
const char *nswReleasesXmlTmpPath = "sdmc:/NSWreleases.xml.tmp";
|
const char *nswReleasesXmlTmpPath = "sdmc:/NSWreleases.xml.tmp";
|
||||||
|
@ -43,22 +47,73 @@ const char *gcDumpToolPath = "sdmc:/switch/gcdumptool.nro";
|
||||||
|
|
||||||
const char *userAgent = "gcdumptool/" APP_VERSION " (Nintendo Switch)";
|
const char *userAgent = "gcdumptool/" APP_VERSION " (Nintendo Switch)";
|
||||||
|
|
||||||
|
/* Statically allocated variables */
|
||||||
|
|
||||||
static char *result_buf = NULL;
|
static char *result_buf = NULL;
|
||||||
static size_t result_sz = 0;
|
static size_t result_sz = 0;
|
||||||
static size_t result_written = 0;
|
static size_t result_written = 0;
|
||||||
|
|
||||||
bool isGameCardInserted(FsDeviceOperator* o)
|
char currentDirectory[NAME_BUF_LEN] = {'\0'};
|
||||||
|
|
||||||
|
char *filenameBuffer = NULL;
|
||||||
|
char *filenames[FILENAME_MAX_CNT];
|
||||||
|
int filenamesCount = 0;
|
||||||
|
|
||||||
|
FsDeviceOperator fsOperatorInstance;
|
||||||
|
FsEventNotifier fsGameCardEventNotifier;
|
||||||
|
Handle fsGameCardEventHandle;
|
||||||
|
Event fsGameCardKernelEvent;
|
||||||
|
UEvent exitEvent;
|
||||||
|
|
||||||
|
bool gameCardInserted;
|
||||||
|
|
||||||
|
u64 gameCardSize = 0, trimmedCardSize = 0;
|
||||||
|
char gameCardSizeStr[32] = {'\0'}, trimmedCardSizeStr[32] = {'\0'};
|
||||||
|
|
||||||
|
char *hfs0_header = NULL;
|
||||||
|
u64 hfs0_offset = 0, hfs0_size = 0;
|
||||||
|
u32 hfs0_partition_cnt = 0;
|
||||||
|
|
||||||
|
char *partitionHfs0Header = NULL;
|
||||||
|
u64 partitionHfs0HeaderSize = 0;
|
||||||
|
|
||||||
|
u64 gameCardTitleID = 0;
|
||||||
|
u32 gameCardVersion = 0;
|
||||||
|
char gameCardName[0x201] = {'\0'}, fixedGameCardName[0x201] = {'\0'}, gameCardAuthor[0x101] = {'\0'}, gameCardVersionStr[64] = {'\0'};
|
||||||
|
|
||||||
|
u64 gameCardUpdateTitleID = 0;
|
||||||
|
u32 gameCardUpdateVersion = 0;
|
||||||
|
char gameCardUpdateVersionStr[128] = {'\0'};
|
||||||
|
|
||||||
|
bool isGameCardInserted()
|
||||||
{
|
{
|
||||||
bool inserted;
|
bool inserted;
|
||||||
if (R_FAILED(fsDeviceOperatorIsGameCardInserted(o, &inserted))) return false;
|
if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&fsOperatorInstance, &inserted))) return false;
|
||||||
return inserted;
|
return inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncDisplay()
|
void fsGameCardDetectionThreadFunc(void *arg)
|
||||||
{
|
{
|
||||||
gfxFlushBuffers();
|
int idx;
|
||||||
gfxSwapBuffers();
|
Result rc;
|
||||||
gfxWaitForVsync();
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
rc = waitMulti(&idx, -1, waiterForEvent(&fsGameCardKernelEvent), waiterForUEvent(&exitEvent));
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
{
|
||||||
|
if (idx == 0)
|
||||||
|
{
|
||||||
|
// Retrieve current gamecard status
|
||||||
|
gameCardInserted = isGameCardInserted();
|
||||||
|
eventClear(&fsGameCardKernelEvent);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitMulti(&idx, 0, waiterForEvent(&fsGameCardKernelEvent), waiterForUEvent(&exitEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
void delay(u8 seconds)
|
void delay(u8 seconds)
|
||||||
|
@ -68,7 +123,7 @@ void delay(u8 seconds)
|
||||||
u64 nanoseconds = seconds * (u64)1000000000;
|
u64 nanoseconds = seconds * (u64)1000000000;
|
||||||
svcSleepThread(nanoseconds);
|
svcSleepThread(nanoseconds);
|
||||||
|
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getGameCardTitleIDAndVersion(u64 *titleID, u32 *version)
|
bool getGameCardTitleIDAndVersion(u64 *titleID, u32 *version)
|
||||||
|
@ -157,6 +212,100 @@ bool getGameCardControlNacp(u64 titleID, char *nameBuf, int nameBufSize, char *a
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void removeIllegalCharacters(char *name)
|
||||||
|
{
|
||||||
|
u32 i, len = strlen(name);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (memchr("?[]/\\=+<>:;\",*|^", name[i], sizeof("?[]/\\=+<>:;\",*|^") - 1) || name[i] < 0x20 || name[i] > 0x7E) name[i] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void strtrim(char *str)
|
||||||
|
{
|
||||||
|
if (!str || !*str) return;
|
||||||
|
|
||||||
|
char *start = str;
|
||||||
|
char *end = start + strlen(str);
|
||||||
|
|
||||||
|
while(--end >= start)
|
||||||
|
{
|
||||||
|
if (!isspace(*end)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(++end) = '\0';
|
||||||
|
|
||||||
|
while(isspace(*start)) start++;
|
||||||
|
|
||||||
|
if (start != str) memmove(str, start, end - start + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadGameCardInfo()
|
||||||
|
{
|
||||||
|
if (gameCardInserted)
|
||||||
|
{
|
||||||
|
if (hfs0_header == NULL)
|
||||||
|
{
|
||||||
|
/* Don't access the gamecard immediately to avoid conflicts with the fsp-srv, ncm and ns services */
|
||||||
|
uiPleaseWait();
|
||||||
|
|
||||||
|
if (getRootHfs0Header(&fsOperatorInstance))
|
||||||
|
{
|
||||||
|
if (getGameCardTitleIDAndVersion(&gameCardTitleID, &gameCardVersion))
|
||||||
|
{
|
||||||
|
convertTitleVersionToDecimal(gameCardVersion, gameCardVersionStr, sizeof(gameCardVersionStr));
|
||||||
|
|
||||||
|
getGameCardControlNacp(gameCardTitleID, gameCardName, sizeof(gameCardName), gameCardAuthor, sizeof(gameCardAuthor));
|
||||||
|
|
||||||
|
strtrim(gameCardName);
|
||||||
|
if (strlen(gameCardName))
|
||||||
|
{
|
||||||
|
snprintf(fixedGameCardName, sizeof(fixedGameCardName) / sizeof(fixedGameCardName[0]), "%s", gameCardName);
|
||||||
|
removeIllegalCharacters(fixedGameCardName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uiPrintHeadline();
|
||||||
|
uiUpdateStatusMsg();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hfs0_header != NULL)
|
||||||
|
{
|
||||||
|
gameCardSize = 0;
|
||||||
|
memset(gameCardSizeStr, 0, sizeof(gameCardSizeStr));
|
||||||
|
|
||||||
|
trimmedCardSize = 0;
|
||||||
|
memset(trimmedCardSizeStr, 0, sizeof(trimmedCardSizeStr));
|
||||||
|
|
||||||
|
free(hfs0_header);
|
||||||
|
hfs0_header = NULL;
|
||||||
|
hfs0_offset = hfs0_size = 0;
|
||||||
|
hfs0_partition_cnt = 0;
|
||||||
|
|
||||||
|
if (partitionHfs0Header != NULL)
|
||||||
|
{
|
||||||
|
free(partitionHfs0Header);
|
||||||
|
partitionHfs0Header = NULL;
|
||||||
|
partitionHfs0HeaderSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameCardTitleID = 0;
|
||||||
|
gameCardVersion = 0;
|
||||||
|
|
||||||
|
memset(gameCardName, 0, sizeof(gameCardName));
|
||||||
|
memset(fixedGameCardName, 0, sizeof(fixedGameCardName));
|
||||||
|
memset(gameCardAuthor, 0, sizeof(gameCardAuthor));
|
||||||
|
memset(gameCardVersionStr, 0, sizeof(gameCardVersionStr));
|
||||||
|
|
||||||
|
gameCardUpdateTitleID = 0;
|
||||||
|
gameCardUpdateVersion = 0;
|
||||||
|
|
||||||
|
memset(gameCardUpdateVersionStr, 0, sizeof(gameCardUpdateVersionStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int getSdCardFreeSpace(u64 *out)
|
int getSdCardFreeSpace(u64 *out)
|
||||||
{
|
{
|
||||||
struct statvfs st;
|
struct statvfs st;
|
||||||
|
@ -173,10 +322,6 @@ int getSdCardFreeSpace(u64 *out)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define KiB (1024.0)
|
|
||||||
#define MiB (1024.0 * KiB)
|
|
||||||
#define GiB (1024.0 * MiB)
|
|
||||||
|
|
||||||
void convertSize(u64 size, char *out, int bufsize)
|
void convertSize(u64 size, char *out, int bufsize)
|
||||||
{
|
{
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
|
@ -226,14 +371,13 @@ void convertSize(u64 size, char *out, int bufsize)
|
||||||
|
|
||||||
void waitForButtonPress()
|
void waitForButtonPress()
|
||||||
{
|
{
|
||||||
uiDrawString("Press any button to continue", 0, breaks * 8, 255, 255, 255);
|
uiDrawString("Press any button to continue", 0, breaks * font_height, 255, 255, 255);
|
||||||
|
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
hidScanInput();
|
hidScanInput();
|
||||||
|
|
||||||
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
if (keysDown && !(keysDown & KEY_TOUCH)) break;
|
if (keysDown && !(keysDown & KEY_TOUCH)) break;
|
||||||
}
|
}
|
||||||
|
@ -296,6 +440,17 @@ void getDirectoryContents(char *filenameBuffer, char **filenames, int *filenames
|
||||||
qsort(filenames + 1, (*filenamesCount) - 1, sizeof(char*), &sortAlpha);
|
qsort(filenames + 1, (*filenamesCount) - 1, sizeof(char*), &sortAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enterDirectory(const char *path)
|
||||||
|
{
|
||||||
|
snprintf(currentDirectory, sizeof(currentDirectory) / sizeof(currentDirectory[0]), "%s", path);
|
||||||
|
|
||||||
|
filenamesCount = FILENAME_MAX_CNT;
|
||||||
|
getDirectoryContents(filenameBuffer, &filenames[0], &filenamesCount, currentDirectory, (!strcmp(currentDirectory, "view:/") && strlen(currentDirectory) == 6));
|
||||||
|
|
||||||
|
cursor = 0;
|
||||||
|
scroll = 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u32 crc)
|
bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u32 crc)
|
||||||
{
|
{
|
||||||
xmlChar *key;
|
xmlChar *key;
|
||||||
|
@ -365,12 +520,12 @@ bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u32 crc)
|
||||||
if (xmlImageSize == imageSize && xmlTitleID == gameCardTitleID && xmlCrc == crc && xmlCard == card)
|
if (xmlImageSize == imageSize && xmlTitleID == gameCardTitleID && xmlCrc == crc && xmlCard == card)
|
||||||
{
|
{
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found matching Scene release: \"%s\" (CRC32: %08X). This is a good dump!", xmlReleaseName, xmlCrc);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found matching Scene release: \"%s\" (CRC32: %08X). This is a good dump!", xmlReleaseName, xmlCrc);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 0, 255, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 0, 255, 0);
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump doesn't match Scene release: \"%s\"! (CRC32: %08X)", xmlReleaseName, xmlCrc);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dump doesn't match Scene release: \"%s\"! (CRC32: %08X)", xmlReleaseName, xmlCrc);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
breaks++;
|
breaks++;
|
||||||
|
@ -410,10 +565,12 @@ void gameCardDumpNSWDBCheck(u32 crc)
|
||||||
xmlXPathObjectPtr nodeSet = getNodeSet(doc, (xmlChar*)strbuf);
|
xmlXPathObjectPtr nodeSet = getNodeSet(doc, (xmlChar*)strbuf);
|
||||||
if (nodeSet)
|
if (nodeSet)
|
||||||
{
|
{
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found %d %s with Title ID \"%016lX\"", nodeSet->nodesetval->nodeNr, (nodeSet->nodesetval->nodeNr > 1 ? "releases" : "release"), gameCardTitleID);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Found %d %s with Title ID \"%016lX\".", nodeSet->nodesetval->nodeNr, (nodeSet->nodesetval->nodeNr > 1 ? "releases" : "release"), gameCardTitleID);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
breaks++;
|
breaks++;
|
||||||
|
|
||||||
|
uiRefreshDisplay();
|
||||||
|
|
||||||
u32 i;
|
u32 i;
|
||||||
for (i = 0; i < nodeSet->nodesetval->nodeNr; i++)
|
for (i = 0; i < nodeSet->nodesetval->nodeNr; i++)
|
||||||
{
|
{
|
||||||
|
@ -425,7 +582,7 @@ void gameCardDumpNSWDBCheck(u32 crc)
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
uiDrawString("No matches found in XML document! This could either be a bad dump or an undumped cartridge.", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("No matches found in XML document! This could either be a bad dump or an undumped cartridge.", 0, breaks * font_height, 255, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
breaks--;
|
breaks--;
|
||||||
}
|
}
|
||||||
|
@ -433,13 +590,13 @@ void gameCardDumpNSWDBCheck(u32 crc)
|
||||||
xmlXPathFreeObject(nodeSet);
|
xmlXPathFreeObject(nodeSet);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find records with Title ID \"%016lX\" within the XML document!", gameCardTitleID);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unable to find records with Title ID \"%016lX\" within the XML document!", gameCardTitleID);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open and/or parse \"%s\"!", nswReleasesXmlPath);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open and/or parse \"%s\"!", nswReleasesXmlPath);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,8 +652,92 @@ static size_t writeCurlBuffer(char *buffer, size_t size, size_t number_of_items,
|
||||||
return bsz;
|
return bsz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateNSWDBXml()
|
||||||
|
{
|
||||||
|
Result result;
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
long http_code = 0;
|
||||||
|
double size = 0.0;
|
||||||
|
char strbuf[512] = {'\0'};
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(result = networkInit()))
|
||||||
|
{
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (curl)
|
||||||
|
{
|
||||||
|
FILE *nswdbXml = fopen(nswReleasesXmlTmpPath, "wb");
|
||||||
|
if (nswdbXml)
|
||||||
|
{
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Downloading XML database from \"%s\", please wait...", nswReleasesXmlUrl);
|
||||||
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
|
breaks++;
|
||||||
|
|
||||||
|
uiRefreshDisplay();
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, nswReleasesXmlUrl);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCurlFile);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nswdbXml);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
|
||||||
|
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &size);
|
||||||
|
|
||||||
|
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
||||||
|
{
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size);
|
||||||
|
uiDrawString(strbuf, 0, breaks * font_height, 0, 255, 0);
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request XML database! HTTP status code: %ld", http_code);
|
||||||
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(nswdbXml);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
remove(nswReleasesXmlPath);
|
||||||
|
rename(nswReleasesXmlTmpPath, nswReleasesXmlPath);
|
||||||
|
} else {
|
||||||
|
remove(nswReleasesXmlTmpPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", nswReleasesXmlTmpPath);
|
||||||
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
} else {
|
||||||
|
uiDrawString("Error: failed to initialize CURL context!", 0, breaks * font_height, 255, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
networkDeinit();
|
||||||
|
} else {
|
||||||
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result);
|
||||||
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
breaks += 2;
|
||||||
|
}
|
||||||
|
|
||||||
int versionNumCmp(char *ver1, char *ver2)
|
int versionNumCmp(char *ver1, char *ver2)
|
||||||
{
|
{
|
||||||
|
int i, curPart, res;
|
||||||
|
char *token = NULL;
|
||||||
|
|
||||||
|
// Define a struct for comparison purposes
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int major;
|
int major;
|
||||||
int minor;
|
int minor;
|
||||||
|
@ -507,13 +748,17 @@ int versionNumCmp(char *ver1, char *ver2)
|
||||||
memset(&versionNum1, 0, sizeof(version_t));
|
memset(&versionNum1, 0, sizeof(version_t));
|
||||||
memset(&versionNum2, 0, sizeof(version_t));
|
memset(&versionNum2, 0, sizeof(version_t));
|
||||||
|
|
||||||
int i, curPart, res;
|
// Create copies of the version strings to avoid modifications by strtok()
|
||||||
char *token, *rest;
|
char ver1tok[64] = {'\0'};
|
||||||
|
snprintf(ver1tok, 63, ver1);
|
||||||
|
|
||||||
|
char ver2tok[64] = {'\0'};
|
||||||
|
snprintf(ver2tok, 63, ver2);
|
||||||
|
|
||||||
// Parse version string 1
|
// Parse version string 1
|
||||||
i = 0;
|
i = 0;
|
||||||
rest = ver1;
|
token = strtok(ver1tok, ".");
|
||||||
while((token = strtok_r(rest, ".", &rest)))
|
while(token != NULL && i < 3)
|
||||||
{
|
{
|
||||||
curPart = atoi(token);
|
curPart = atoi(token);
|
||||||
|
|
||||||
|
@ -532,14 +777,15 @@ int versionNumCmp(char *ver1, char *ver2)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, ".");
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
if (i >= 3) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse version string 2
|
// Parse version string 2
|
||||||
i = 0;
|
i = 0;
|
||||||
rest = ver2;
|
token = strtok(ver2tok, ".");
|
||||||
while((token = strtok_r(rest, ".", &rest)))
|
while(token != NULL && i < 3)
|
||||||
{
|
{
|
||||||
curPart = atoi(token);
|
curPart = atoi(token);
|
||||||
|
|
||||||
|
@ -558,8 +804,9 @@ int versionNumCmp(char *ver1, char *ver2)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, ".");
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
if (i >= 3) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare version_t structs
|
// Compare version_t structs
|
||||||
|
@ -595,86 +842,6 @@ int versionNumCmp(char *ver1, char *ver2)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateNSWDBXml()
|
|
||||||
{
|
|
||||||
Result result;
|
|
||||||
CURL *curl;
|
|
||||||
CURLcode res;
|
|
||||||
long http_code = 0;
|
|
||||||
double size = 0.0;
|
|
||||||
char strbuf[512] = {'\0'};
|
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(result = networkInit()))
|
|
||||||
{
|
|
||||||
curl = curl_easy_init();
|
|
||||||
if (curl)
|
|
||||||
{
|
|
||||||
FILE *nswdbXml = fopen(nswReleasesXmlTmpPath, "wb");
|
|
||||||
if (nswdbXml)
|
|
||||||
{
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Downloading XML database from \"%s\", please wait...", nswReleasesXmlUrl);
|
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
|
||||||
breaks++;
|
|
||||||
|
|
||||||
syncDisplay();
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, nswReleasesXmlUrl);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCurlFile);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nswdbXml);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
|
|
||||||
|
|
||||||
res = curl_easy_perform(curl);
|
|
||||||
|
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
|
||||||
curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &size);
|
|
||||||
|
|
||||||
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
|
||||||
{
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size);
|
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 0, 255, 0);
|
|
||||||
success = true;
|
|
||||||
} else {
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request XML database! HTTP status code: %ld", http_code);
|
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(nswdbXml);
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
remove(nswReleasesXmlPath);
|
|
||||||
rename(nswReleasesXmlTmpPath, nswReleasesXmlPath);
|
|
||||||
} else {
|
|
||||||
remove(nswReleasesXmlTmpPath);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", nswReleasesXmlTmpPath);
|
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
} else {
|
|
||||||
uiDrawString("Error: failed to initialize CURL context!", 0, breaks * 8, 255, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
networkDeinit();
|
|
||||||
} else {
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result);
|
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
breaks += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateApplication()
|
void updateApplication()
|
||||||
{
|
{
|
||||||
Result result;
|
Result result;
|
||||||
|
@ -693,10 +860,10 @@ void updateApplication()
|
||||||
if (curl)
|
if (curl)
|
||||||
{
|
{
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Requesting latest release information from \"%s\"...", githubReleasesApiUrl);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Requesting latest release information from \"%s\"...", githubReleasesApiUrl);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
breaks++;
|
breaks++;
|
||||||
|
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
|
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, githubReleasesApiUrl);
|
curl_easy_setopt(curl, CURLOPT_URL, githubReleasesApiUrl);
|
||||||
|
@ -719,10 +886,10 @@ void updateApplication()
|
||||||
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
||||||
{
|
{
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Parsing response JSON data from \"%s\"...", githubReleasesApiUrl);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Parsing response JSON data from \"%s\"...", githubReleasesApiUrl);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
breaks++;
|
breaks++;
|
||||||
|
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
|
|
||||||
jobj = json_tokener_parse(result_buf);
|
jobj = json_tokener_parse(result_buf);
|
||||||
if (jobj != NULL)
|
if (jobj != NULL)
|
||||||
|
@ -731,14 +898,19 @@ void updateApplication()
|
||||||
{
|
{
|
||||||
snprintf(releaseTag, sizeof(releaseTag) / sizeof(releaseTag[0]), json_object_get_string(name));
|
snprintf(releaseTag, sizeof(releaseTag) / sizeof(releaseTag[0]), json_object_get_string(name));
|
||||||
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Latest release: %s", releaseTag);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Latest release: %s.", releaseTag);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
breaks++;
|
breaks++;
|
||||||
|
|
||||||
syncDisplay();
|
uiRefreshDisplay();
|
||||||
|
|
||||||
// Compare versions
|
// Compare versions
|
||||||
if (releaseTag[0] == 'v' || releaseTag[0] == 'V' || releaseTag[0] == 'r' || releaseTag[0] == 'R') memmove(releaseTag, releaseTag + 1, strlen(releaseTag));
|
if (releaseTag[0] == 'v' || releaseTag[0] == 'V' || releaseTag[0] == 'r' || releaseTag[0] == 'R')
|
||||||
|
{
|
||||||
|
u32 releaseTagLen = strlen(releaseTag);
|
||||||
|
memmove(releaseTag, releaseTag + 1, releaseTagLen - 1);
|
||||||
|
releaseTag[releaseTagLen - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
if (versionNumCmp(releaseTag, APP_VERSION) > 0)
|
if (versionNumCmp(releaseTag, APP_VERSION) > 0)
|
||||||
{
|
{
|
||||||
|
@ -751,11 +923,14 @@ void updateApplication()
|
||||||
{
|
{
|
||||||
snprintf(downloadUrl, sizeof(downloadUrl) / sizeof(downloadUrl[0]), json_object_get_string(assets));
|
snprintf(downloadUrl, sizeof(downloadUrl) / sizeof(downloadUrl[0]), json_object_get_string(assets));
|
||||||
|
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Download URL: \"%s\"", downloadUrl);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Download URL: \"%s\".", downloadUrl);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 255, 255);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
|
||||||
breaks++;
|
breaks++;
|
||||||
|
|
||||||
syncDisplay();
|
uiDrawString("Please wait...", 0, breaks * font_height, 255, 255, 255);
|
||||||
|
breaks += 2;
|
||||||
|
|
||||||
|
uiRefreshDisplay();
|
||||||
|
|
||||||
gcDumpToolNro = fopen(gcDumpToolTmpPath, "wb");
|
gcDumpToolNro = fopen(gcDumpToolTmpPath, "wb");
|
||||||
if (gcDumpToolNro)
|
if (gcDumpToolNro)
|
||||||
|
@ -784,11 +959,11 @@ void updateApplication()
|
||||||
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
if (res == CURLE_OK && http_code >= 200 && http_code <= 299 && size > 0)
|
||||||
{
|
{
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Successfully downloaded %.0lf bytes!", size);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 0, 255, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 0, 255, 0);
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest update binary! HTTP status code: %ld", http_code);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest update binary! HTTP status code: %ld", http_code);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(gcDumpToolNro);
|
fclose(gcDumpToolNro);
|
||||||
|
@ -802,73 +977,45 @@ void updateApplication()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", gcDumpToolTmpPath);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open \"%s\" in write mode!", gcDumpToolTmpPath);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: unable to parse download URL from JSON response!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: unable to parse download URL from JSON response!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: unable to parse object at index 0 from \"assets\" array in JSON response!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: unable to parse object at index 0 from \"assets\" array in JSON response!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: unable to parse \"assets\" array from JSON response!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: unable to parse \"assets\" array from JSON response!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("You already have the latest version!", 0, breaks * 8, 255, 255, 255);
|
uiDrawString("You already have the latest version!", 0, breaks * font_height, 255, 255, 255);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: unable to parse version tag from JSON response!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: unable to parse version tag from JSON response!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_put(jobj);
|
json_object_put(jobj);
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: unable to parse JSON response!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: unable to parse JSON response!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest release information! HTTP status code: %ld", http_code);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to request latest release information! HTTP status code: %ld", http_code);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result_buf) free(result_buf);
|
if (result_buf) free(result_buf);
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
} else {
|
} else {
|
||||||
uiDrawString("Error: failed to initialize CURL context!", 0, breaks * 8, 255, 0, 0);
|
uiDrawString("Error: failed to initialize CURL context!", 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
networkDeinit();
|
networkDeinit();
|
||||||
} else {
|
} else {
|
||||||
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result);
|
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to initialize socket! (%08X)", result);
|
||||||
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
|
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
breaks += 2;
|
breaks += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeIllegalCharacters(char *name)
|
|
||||||
{
|
|
||||||
u32 i, len = strlen(name);
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (memchr("?[]/\\=+<>:;\",*|^", name[i], sizeof("?[]/\\=+<>:;\",*|^") - 1) || name[i] < 0x20 || name[i] > 0x7E) name[i] = '_';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void strtrim(char *str)
|
|
||||||
{
|
|
||||||
if (!str || !*str) return;
|
|
||||||
|
|
||||||
char *start = str;
|
|
||||||
char *end = start + strlen(str);
|
|
||||||
|
|
||||||
while(--end >= start)
|
|
||||||
{
|
|
||||||
if (!isspace(*end)) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*(++end) = '\0';
|
|
||||||
|
|
||||||
while(isspace(*start)) start++;
|
|
||||||
|
|
||||||
if (start != str) memmove(str, start, end - start + 1);
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
|
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
|
#define KiB (1024.0)
|
||||||
|
#define MiB (1024.0 * KiB)
|
||||||
|
#define GiB (1024.0 * MiB)
|
||||||
|
|
||||||
#define APP_VERSION "1.0.6"
|
#define APP_VERSION "1.0.6"
|
||||||
|
|
||||||
#define NAME_BUF_LEN 4096
|
#define NAME_BUF_LEN 4096
|
||||||
|
@ -13,9 +17,12 @@
|
||||||
|
|
||||||
#define META_DATABASE_FILTER 0x80 // Regular Application
|
#define META_DATABASE_FILTER 0x80 // Regular Application
|
||||||
|
|
||||||
bool isGameCardInserted(FsDeviceOperator* o);
|
#define FILENAME_BUFFER_SIZE (1024 * 512) // 512 KiB
|
||||||
|
#define FILENAME_MAX_CNT 2048
|
||||||
|
|
||||||
void syncDisplay();
|
bool isGameCardInserted();
|
||||||
|
|
||||||
|
void fsGameCardDetectionThreadFunc(void *arg);
|
||||||
|
|
||||||
void delay(u8 seconds);
|
void delay(u8 seconds);
|
||||||
|
|
||||||
|
@ -25,6 +32,12 @@ void convertTitleVersionToDecimal(u32 version, char *versionBuf, int versionBufS
|
||||||
|
|
||||||
bool getGameCardControlNacp(u64 titleID, char *nameBuf, int nameBufSize, char *authorBuf, int authorBufSize);
|
bool getGameCardControlNacp(u64 titleID, char *nameBuf, int nameBufSize, char *authorBuf, int authorBufSize);
|
||||||
|
|
||||||
|
void removeIllegalCharacters(char *name);
|
||||||
|
|
||||||
|
void strtrim(char *str);
|
||||||
|
|
||||||
|
void loadGameCardInfo();
|
||||||
|
|
||||||
int getSdCardFreeSpace(u64 *out);
|
int getSdCardFreeSpace(u64 *out);
|
||||||
|
|
||||||
void convertSize(u64 size, char *out, int bufsize);
|
void convertSize(u64 size, char *out, int bufsize);
|
||||||
|
@ -37,14 +50,12 @@ void addString(char **filenames, int *filenamesCount, char **nextFilename, const
|
||||||
|
|
||||||
void getDirectoryContents(char *filenameBuffer, char **filenames, int *filenamesCount, const char *directory, bool skipParent);
|
void getDirectoryContents(char *filenameBuffer, char **filenames, int *filenamesCount, const char *directory, bool skipParent);
|
||||||
|
|
||||||
|
void enterDirectory(const char *path);
|
||||||
|
|
||||||
void gameCardDumpNSWDBCheck(u32 crc);
|
void gameCardDumpNSWDBCheck(u32 crc);
|
||||||
|
|
||||||
void updateNSWDBXml();
|
void updateNSWDBXml();
|
||||||
|
|
||||||
void updateApplication();
|
void updateApplication();
|
||||||
|
|
||||||
void removeIllegalCharacters(char *name);
|
|
||||||
|
|
||||||
void strtrim(char *str);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue