mirror of
https://bitbucket.org/e1ite007/sigpatch-updater.git
synced 2024-11-23 11:09:45 +00:00
Initial commit
This commit is contained in:
commit
9ef4252e8a
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
19
.github/workflows/build_depoly.yml
vendored
Normal file
19
.github/workflows/build_depoly.yml
vendored
Normal 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
8
.gitignore
vendored
Normal 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
18
.vscode/c_cpp_properties.json
vendored
Normal 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
21
LICENSE
Normal 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
240
Makefile
Normal 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
35
README.md
Normal 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
images/example.jpg
Normal file
BIN
images/example.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
17
includes/download.h
Normal file
17
includes/download.h
Normal 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
6
includes/unzip.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _UNZIP_H_
|
||||
#define _UNZIP_H_
|
||||
|
||||
bool unzip(const char *output);
|
||||
|
||||
#endif
|
10
includes/util.h
Normal file
10
includes/util.h
Normal 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
126
source/download.c
Normal 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
272
source/main.c
Normal 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
67
source/unzip.c
Normal 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
25
source/util.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user