Initial commit

This commit is contained in:
e1ite007 2024-04-27 04:27:28 -06:00
commit 9ef4252e8a
16 changed files with 866 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

19
.github/workflows/build_depoly.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
container: devkitpro/devkita64:latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@master
- name: Build
run: make dist -j2
- uses: actions/upload-artifact@master
with:
name: sigpatch-updater
path: sigpatch-updater.zip

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
build
*.elf
*.nacp
*.nro
.vscode/*
!.vscode/c_cpp_properties.json
*.zip
out

18
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "switch",
"includePath": [
"${default}",
"${workspaceFolder}/**",
"${DEVKITPRO}/libnx/include/",
"${DEVKITPRO}/portlibs/switch/include/"
],
"defines": [],
"cStandard": "c17",
"cppStandard": "c++23",
"compilerPath": "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc"
}
],
"version": 4
}

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 ITotalJustice
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

240
Makefile Normal file
View File

@ -0,0 +1,240 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := sigpatch-updater
APP_AUTHOR := TotalJustice
APP_VERSION := 0.3.0
TARGET := sigpatch-updater
BUILD := build
SOURCES := source
DATA := data
INCLUDES := includes
#ROMFS := romfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lminizip -lz -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@rm -rf out/
@mkdir -p out/switch/sigpatch-updater/
@cp $(CURDIR)/$(TARGET).nro out/switch/sigpatch-updater/
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
dist: all
@echo making dist ...
@rm -f sigpatch-updater.zip
@cd out; zip -r ../sigpatch-updater.zip ./*; cd ../
#---------------------------------------------------------------------------------
nxlink: all
@echo making and nxlinking ...
nxlink $(CURDIR)/$(TARGET).nro
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

35
README.md Normal file
View File

@ -0,0 +1,35 @@
# Sigpatch-Updater
This is a updater for you sigpatches!
Currently it has the option to:
- Update patches needed for Atmosphere
- Update sys-patch and auto run the sysmod
- Update the app
---
![Img](images/example.jpg)
----
## Where does it download the patches from?
The patches are download from [a new host](https://sigmapatches.coomer.party). Huge thanks to them!
---
## What is sys-patch?
you can read more about it [here](https://github.com/ITotalJustice/sys-patch).
----
## Special Thanks!
- [toph](https://github.com/sudot0ph) for the design of the app icon
- The-4n for previously updating the sigpatch thread on gbatemp
- Joonie for their help with hekate patches in the past
- TeJay for previously maintaining the patches repo
- Archbox for being very helpful as always

BIN
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
images/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

17
includes/download.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef _DOWNLOAD_H_
#define _DOWNLOAD_H_
#define AMS_SIG_URL "https://sigmapatches.su/sigpatches.zip"
#define APP_URL "https://gitflic.ru/project/e1ite007/sigpatch-updater/releases/latest/download/sigpatch-updater.nro"
#define SYS_PATCH_URL "https://sigmapatches.su/sys-patch.zip"
#define TEMP_FILE "/switch/sigpatch-updater/temp"
#include <stdbool.h>
#include <switch.h>
typedef bool(*DlProgressCallback)(u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow);
//
bool downloadFile(const char *url, const char *output, DlProgressCallback pcall);
#endif

6
includes/unzip.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _UNZIP_H_
#define _UNZIP_H_
bool unzip(const char *output);
#endif

10
includes/util.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _UTIL_H_
#define _UTIL_H_
#include <stdbool.h>
void consolePrint(const char* s, ...) __attribute__ ((format (printf, 1, 2)));
bool stillRunning(void);
#endif

126
source/download.c Normal file
View File

@ -0,0 +1,126 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curl/curl.h>
#include <switch.h>
#include "download.h"
#include "util.h"
#define API_AGENT "ITotalJustice"
#define _1MiB 0x100000
typedef struct {
char *memory;
size_t size;
} MemoryStruct_t;
typedef struct {
uint8_t *data;
size_t data_size;
uint64_t offset;
FILE *out;
} ntwrk_struct_t;
static size_t WriteMemoryCallback(void *contents, size_t size, size_t num_files, void *userp) {
ntwrk_struct_t *data_struct = (ntwrk_struct_t *)userp;
const size_t realsize = size * num_files;
if (realsize + data_struct->offset >= data_struct->data_size) {
fwrite(data_struct->data, data_struct->offset, 1, data_struct->out);
data_struct->offset = 0;
}
memcpy(&data_struct->data[data_struct->offset], contents, realsize);
data_struct->offset += realsize;
data_struct->data[data_struct->offset] = 0;
return realsize;
}
static int download_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
const DlProgressCallback callback = (DlProgressCallback)clientp;
if (!callback(dltotal, dlnow, ultotal, ulnow)) {
return 1;
}
return 0;
}
bool downloadFile(const char *url, const char *output, DlProgressCallback pcall) {
CURL *curl = curl_easy_init();
if (curl) {
FILE *fp = fopen(output, "wb");
if (fp) {
consolePrint("\nDownload in progress, Press (B) to cancel...\n\n");
ntwrk_struct_t chunk = {0};
chunk.data = malloc(_1MiB);
chunk.data_size = _1MiB;
chunk.out = fp;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
// write calls
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, download_progress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, pcall);
CURLM *multi_handle = curl_multi_init();
curl_multi_add_handle(multi_handle, curl);
int still_running = 1;
CURLMcode mc;
while (still_running) {
mc = curl_multi_perform(multi_handle, &still_running);
if (!mc)
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
if (mc) {
break;
}
}
int msgs_left;
CURLMsg *msg;
CURLcode res = 1;
while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
if (msg->msg == CURLMSG_DONE) {
res = msg->data.result;
}
}
// write from mem to file
if (chunk.offset) {
fwrite(chunk.data, 1, chunk.offset, fp);
}
// clean
curl_multi_remove_handle(multi_handle, curl);
curl_easy_cleanup(curl);
curl_multi_cleanup(multi_handle);
free(chunk.data);
fclose(chunk.out);
if (res == CURLE_OK) {
consolePrint("\n\ndownload complete!\n\n");
return true;
} else {
consolePrint("\n\ncurl error: %s", curl_easy_strerror(res));
}
}
}
consolePrint("\n\ndownload failed...\n\n");
return false;
}

272
source/main.c Normal file
View File

@ -0,0 +1,272 @@
#include <curl/curl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h> // chdir
#include <dirent.h> // mkdir
#include <time.h>
#include <math.h>
#include <switch.h>
#include "download.h"
#include "unzip.h"
#include "util.h"
#define ROOT "/"
#define APP_PATH "/switch/sigpatch-updater/"
#define APP_OUTPUT "/switch/sigpatch-updater/sigpatch-updater.nro"
#define OLD_APP_PATH "/switch/sigpatch-updater.nro"
#define APP_VERSION "0.3.0"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
typedef bool (*func_handler)(void);
struct Entry {
const char* display_text;
func_handler func;
const char* description;
};
static PadState g_pad;
static bool progressCallback(u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow) {
if (!stillRunning()) {
return false;
}
padUpdate(&g_pad);
const u64 kDown = padGetButtonsDown(&g_pad);
if (kDown & HidNpadButton_B) {
return false;;
}
if (dltotal > 0) {
struct timeval tv = {0};
gettimeofday(&tv, NULL);
const int counter = round(tv.tv_usec / 100000);
if (counter == 0 || counter == 2 || counter == 4 || counter == 6 || counter == 8) {
const double mb = 1024*1024;
consolePrint("* DOWNLOADING: %.2fMB of %.2fMB *\r", (double)dlnow / mb, (double)dltotal / mb);
}
}
return true;
}
static bool update_sigpatches_handler(void) {
if (downloadFile(AMS_SIG_URL, TEMP_FILE, progressCallback)) {
if (!unzip(TEMP_FILE)) {
return false;
}
consolePrint("\nfinished!\n\nRemember to reboot for the patches to be loaded!\n");
return true;
} else {
return false;
}
}
static bool update_syspatch_handler(void) {
if (downloadFile(SYS_PATCH_URL, TEMP_FILE, progressCallback)) {
if (!unzip(TEMP_FILE)) {
return false;
}
u64 pid = 0;
const NcmProgramLocation location = {
.program_id = 0x420000000000000BULL,
.storageID = NcmStorageId_None,
};
if (R_FAILED(pmshellLaunchProgram(0, &location, &pid))) {
consolePrint(
"\nFailed to start sys-patch!\n"
"A reboot is needed...\n"
"Report this issue on GitHub please!\n");
return false;
} else {
consolePrint("\nsys-patch ran successfully, patches should be applied!\n");
return true;
}
} else {
return false;
}
}
static bool update_app_handler(void) {
if (downloadFile(APP_URL, TEMP_FILE, progressCallback)) {
remove(APP_OUTPUT);
rename(TEMP_FILE, APP_OUTPUT);
remove(OLD_APP_PATH);
consolePrint("\nApp updated!\nRestart app to take effect");
return true;
} else {
return false;
}
}
static const struct Entry ENTRIES[] = {
{ "Update sigpatches", update_sigpatches_handler, "sigpatches require a reboot" },
{ "Update sys-patch", update_syspatch_handler, "sys-patch applies patches without reboot" },
{ "Update app ", update_app_handler, "requires restarting the app" },
};
enum WifiState {
WifiState_AIRPLANE,
WifiState_0, // no signal
WifiState_1, // low strength
WifiState_2, // medium strength
WifiState_3, // max strength
};
static enum WifiState get_wifi_state() {
Result rc = 0;
NifmInternetConnectionType type = {0};
NifmInternetConnectionStatus status = {0};
u32 strength = 0;
if (R_FAILED(rc = nifmGetInternetConnectionStatus(&type, &strength, &status))) {
return WifiState_AIRPLANE;
}
if (type == NifmInternetConnectionType_Ethernet) {
if (status != NifmInternetConnectionStatus_Connected) {
return WifiState_0;
} else {
return WifiState_3;
}
}
switch (strength) {
case 0: return WifiState_0;
case 2: return WifiState_2;
case 3: return WifiState_3;
default: return WifiState_1;
}
}
static void refreshScreen(int cursor) {
consoleClear();
printf("\x1B[36mSigpatch-Updater: v%s\x1B[37m\n\n\n", APP_VERSION);
// printf("This app is unmaintained, please consider using a different tool to update your patches!\n\n\n");
printf("Press (A) to select option\n\n");
printf("Press (+) to exit\n\n\n");
for (int i = 0; i < ARRAY_SIZE(ENTRIES); i++) {
printf("[%c] = %s\t\t(%s)\n\n", cursor == i ? 'X' : ' ', ENTRIES[i].display_text, ENTRIES[i].description);
}
consoleUpdate(NULL);
}
// update the cursor so that it wraps around
static void update_cursor(int* cur, int new_value, int max) {
if (new_value >= max) {
new_value = 0;
} else if (new_value < 0) {
new_value = max - 1;
}
*cur = new_value;
refreshScreen(new_value);
}
// where the app starts
int main(int argc, char **argv) {
curl_global_init(CURL_GLOBAL_ALL);
// init stuff
mkdir(APP_PATH, 0777);
// move nro to app folder
if (!strstr(argv[0], APP_OUTPUT)) {
remove(APP_OUTPUT);
rename(argv[0], APP_OUTPUT);
}
// change directory to root (defaults to /switch/)
chdir(ROOT);
// set the cursor position to 0
int cursor = 0;
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&g_pad);
// main menu
refreshScreen(cursor);
// muh loooooop
while (stillRunning()) {
padUpdate(&g_pad);
const u64 kDown = padGetButtonsDown(&g_pad);
// move cursor down...
if (kDown & HidNpadButton_AnyDown) {
update_cursor(&cursor, cursor + 1, ARRAY_SIZE(ENTRIES));
}
// move cursor up...
else if (kDown & HidNpadButton_AnyUp) {
update_cursor(&cursor, cursor - 1, ARRAY_SIZE(ENTRIES));
}
else if (kDown & HidNpadButton_A) {
if (get_wifi_state() < WifiState_1) {
consolePrint(
"\n\n[Error] not connected to the internet!\n\n"
"An internet connection is required to download updates!\n");
}
else {
if (!ENTRIES[cursor].func()) {
consolePrint("Failed to %s\n", ENTRIES[cursor].display_text);
}
}
consolePrint("\nPress (A) to continue...\n");
while (stillRunning()) {
padUpdate(&g_pad);
const u64 kDown = padGetButtonsDown(&g_pad);
if (kDown & HidNpadButton_A) {
break;
}
svcSleepThread(1e+9 / 60);
}
refreshScreen(cursor);
}
// exit...
else if (kDown & HidNpadButton_Plus) {
break;
}
// 1e+9 = 1 second
svcSleepThread(1e+9 / 60);
}
curl_global_cleanup();
return 0;
}
// this is called before main
void userAppInit(void) {
appletLockExit();
pmshellInitialize();
consoleInit(NULL);
socketInitializeDefault();
nifmInitialize(NifmServiceType_User);
}
// this is called after main exits
void userAppExit(void) {
pmshellExit();
nifmExit();
socketExit();
consoleExit(NULL);
appletUnlockExit();
}

67
source/unzip.c Normal file
View File

@ -0,0 +1,67 @@
#include <stdio.h>
#include <minizip/unzip.h>
#include <string.h>
#include <dirent.h>
#include <switch.h>
#include "unzip.h"
#define WRITEBUFFERSIZE 0x1000 // 4KiB
#define MAXFILENAME 0x301
bool unzip(const char *output) {
unzFile zfile = unzOpen(output);
if (!zfile) {
return false;
}
unz_global_info gi = {0};
if (UNZ_OK != unzGetGlobalInfo(zfile, &gi)) {
return false;
}
for (int i = 0; i < gi.number_entry; i++) {
char filename_inzip[MAXFILENAME] = {0};
unz_file_info file_info = {0};
unzOpenCurrentFile(zfile);
unzGetCurrentFileInfo(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
// check if the string ends with a /, if so, then its a directory.
if ((filename_inzip[strlen(filename_inzip) - 1]) == '/') {
// check if directory exists
DIR *dir = opendir(filename_inzip);
if (dir) {
closedir(dir);
} else {
printf("creating directory: %s\n", filename_inzip);
mkdir(filename_inzip, 0777);
}
} else {
const char *write_filename = filename_inzip;
FILE *outfile = fopen(write_filename, "wb");
void *buf = malloc(WRITEBUFFERSIZE);
printf("writing file: %s\n", filename_inzip);
consoleUpdate(NULL);
for (int j = unzReadCurrentFile(zfile, buf, WRITEBUFFERSIZE); j > 0; j = unzReadCurrentFile(zfile, buf, WRITEBUFFERSIZE)) {
fwrite(buf, 1, j, outfile);
}
fclose(outfile);
free(buf);
}
unzCloseCurrentFile(zfile);
unzGoToNextFile(zfile);
consoleUpdate(NULL);
}
unzClose(zfile);
remove(output);
// todo: i assume everything above works fine
// this might not always be the case, add error handling!
return true;
}

25
source/util.c Normal file
View File

@ -0,0 +1,25 @@
#include <switch.h>
#include <stdarg.h>
#include <stdio.h>
#include "util.h"
void consolePrint(const char* s, ...) {
va_list v;
va_start(v, s);
vprintf(s, v);
consoleUpdate(NULL);
va_end(v);
}
bool stillRunning(void) {
static Mutex mutex = {0};
static bool running = true;
mutexLock(&mutex);
if (!appletMainLoop()) {
running = false;
}
const bool result = running;
mutexUnlock(&mutex);
return result;
}