mirror of
https://github.com/HamletDuFromage/aio-switch-updater.git
synced 2025-03-02 20:25:49 +00:00
Initial Commit
This commit is contained in:
parent
4c1eee22be
commit
5e9026d5ea
38 changed files with 22814 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
.vscode/
|
||||
build/
|
||||
lib/borealis/*
|
||||
lib/minizip/*
|
||||
screenshots/
|
||||
switch/
|
||||
*.elf
|
||||
*.nacp
|
||||
*.nro
|
||||
*.zip
|
247
Makefile
Normal file
247
Makefile
Normal file
|
@ -0,0 +1,247 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.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.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source lib/minizip/source
|
||||
RESOURCES := resources
|
||||
DATA := data
|
||||
INCLUDES := include lib/minizip/include
|
||||
APP_TITLE := AIO Switch Updater
|
||||
APP_AUTHOR := HamletDuFromage
|
||||
APP_VERSION := 1.0
|
||||
|
||||
#ROMFS := $(BUILD)/romfs
|
||||
BOREALIS_PATH := lib/borealis
|
||||
#BOREALIS_RESOURCES := romfs:/borealis/
|
||||
#APP_RESOURCES := romfs:/
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# version control constants
|
||||
#---------------------------------------------------------------------------------
|
||||
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
||||
#APP_VERSION := $(TARGET_VERSION)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__ \
|
||||
-DBOREALIS_RESOURCES="\"$(BOREALIS_RESOURCES)\"" \
|
||||
-DAPP_VERSION="\"$(APP_VERSION)\"" \
|
||||
-DAPP_TITLE="\"$(APP_TITLE)\"" -DAPP_TITLE_LOWER="\"$(TARGET)\""
|
||||
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fexceptions -Wno-reorder
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lm -lcurl -lnx -lz -lmbedtls -lmbedx509 -lmbedcrypto -lstdc++fs `freetype-config --libs`
|
||||
#LIBS := -ltwili -lm -lcurl -lnx -lz -lmbedtls -lmbedx509 -lmbedcrypto -lstdc++fs `freetype-config --libs`
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
include $(TOPDIR)/$(BOREALIS_PATH)/library/borealis.mk
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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) $(ROMFS) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(ROMFS):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@echo Merging ROMFS...
|
||||
@cp -ruf $(CURDIR)/$(BOREALIS_PATH)/resources/. $(CURDIR)/$(ROMFS)/borealis/
|
||||
|
||||
$(BUILD): $(ROMFS)
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@MSYS2_ARG_CONV_EXCL="-D;$(MSYS2_ARG_CONV_EXCL)" $(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
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
|
||||
#---------------------------------------------------------------------------------------
|
34
README.md
34
README.md
|
@ -1,2 +1,36 @@
|
|||
# AIO-switch-updater
|
||||

|
||||
|
||||
All-in-One Nintendo Switch Updater
|
||||
<p align="center">
|
||||
<img src = "https://user-images.githubusercontent.com/61667930/93691188-7833f000-fad1-11ea-866d-42e19be54425.jpg"\><br>
|
||||
</p>
|
||||
|
||||
A Nintendo Switch homebrew app download and update CFWs, sigpatches, FWs and cheat codes. Support Atmosphere, ReiNX and SXOS.
|
||||
|
||||
|
||||
## How to install
|
||||
|
||||
Copy the `aio-switch-updater/` directory to `/switch/` in your sdcard
|
||||
|
||||
## Extras
|
||||
|
||||
This app can also reboot to specific payload and change software color scheme of Joy-Cons
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div class="flex-container" style=" display: flex; flex-direction: row;">
|
||||
<div><img src = "https://user-images.githubusercontent.com/61667930/93691403-30fb2e80-fad4-11ea-9701-7992a1de53e0.jpg"\></div>
|
||||
</div>
|
||||
<div class="flex-container" style=" display: flex; flex-direction: row;">
|
||||
<div><img src = "https://user-images.githubusercontent.com/61667930/93691404-3193c500-fad4-11ea-9647-927c979960bc.jpg"\></div>
|
||||
<div><img src = "https://user-images.githubusercontent.com/61667930/93691405-3193c500-fad4-11ea-960d-b68d413aedd4.jpg"\></div>
|
||||
</div>
|
||||
<div class="flex-container" style=" display: flex; flex-direction: row;">
|
||||
<div><img src = "https://user-images.githubusercontent.com/61667930/93691407-322c5b80-fad4-11ea-8879-78047724d9e7.jpg"\></div>
|
||||
<div><img src = "https://user-images.githubusercontent.com/61667930/93691465-16758500-fad5-11ea-8a5c-c0f9694cfb0e.jpg"\></div>
|
||||
</div>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
I do not own, host nor distribute any of the files that can be downloaded with this homebrew tool. At the owner's request, I will immediately remove the ability to download the problematic files.
|
BIN
icon.jpg
Executable file
BIN
icon.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
24
include/JC_color_swapper.hpp
Normal file
24
include/JC_color_swapper.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
#include <switch.h>
|
||||
#include "constants.hpp"
|
||||
#include "progress_event.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
int hexToBGR(std::string hex);
|
||||
std::string BGRToHex(int v);
|
||||
bool isHexaAnd3Bytes(std::string str);
|
||||
int setColor(std::vector<int> colors);
|
||||
int backupToJSON(nlohmann::json &profiles, const char* path);
|
||||
void writeJSONToFile(nlohmann::json &profiles, const char* path);
|
||||
std::tuple<std::vector<std::string>, std::vector<std::vector<int>>> getProfiles(const char* path);
|
||||
void changeJCColor(std::vector<int> values);
|
||||
nlohmann::json backupProfile();
|
||||
void backupJCColor(const char* path);
|
20
include/JC_page.hpp
Normal file
20
include/JC_page.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "JC_color_swapper.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "confirm_page.hpp"
|
||||
#include "worker_page.hpp"
|
||||
|
||||
class JCPage : public brls::AppletFrame
|
||||
{
|
||||
private:
|
||||
brls::List* list;
|
||||
brls::Label* label;
|
||||
std::vector<brls::ListItem*> items;
|
||||
brls::ListItem* restore;
|
||||
brls::ListItem* backup;
|
||||
|
||||
public:
|
||||
JCPage();
|
||||
};
|
16
include/about_tab.hpp
Normal file
16
include/about_tab.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
|
||||
#include <borealis.hpp>
|
||||
|
||||
class AboutTab : public brls::List
|
||||
{
|
||||
public:
|
||||
AboutTab();
|
||||
|
||||
View* getDefaultFocus() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
41
include/app_page.hpp
Normal file
41
include/app_page.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
#include <switch.h>
|
||||
#include <cstring>
|
||||
#include "utils.hpp"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "worker_page.hpp"
|
||||
#include "confirm_page.hpp"
|
||||
|
||||
#define PROFILE_BADGE "\uE3E0"
|
||||
|
||||
typedef char NsApplicationName[0x201];
|
||||
typedef uint8_t NsApplicationIcon[0x20000];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t tid;
|
||||
NsApplicationName name;
|
||||
NsApplicationIcon icon;
|
||||
|
||||
brls::ListItem* listItem;
|
||||
} App;
|
||||
|
||||
class AppPage : public brls::AppletFrame
|
||||
{
|
||||
private:
|
||||
brls::List* list;
|
||||
brls::Label* label;
|
||||
brls::ListItem* download;
|
||||
std::vector<App*> apps;
|
||||
std::set<std::string> titles;
|
||||
std::vector<brls::ListItem*> items;
|
||||
|
||||
public:
|
||||
AppPage();
|
||||
};
|
26
include/confirm_page.hpp
Normal file
26
include/confirm_page.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
#include <chrono>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "download.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "main_frame.hpp"
|
||||
|
||||
class ConfirmPage : public brls::View
|
||||
{
|
||||
private:
|
||||
brls::Button* button = nullptr;
|
||||
brls::Label* label = nullptr;
|
||||
std::chrono::system_clock::time_point start = std::chrono::high_resolution_clock::now();
|
||||
bool done = false;
|
||||
|
||||
public:
|
||||
ConfirmPage(brls::StagedAppletFrame* frame, std::string text, bool done = false);
|
||||
~ConfirmPage();
|
||||
|
||||
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
|
||||
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
|
||||
brls::View* getDefaultFocus() override;
|
||||
};
|
53
include/constants.hpp
Normal file
53
include/constants.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#define ROOT_PATH "/"
|
||||
#define DOWNLOAD_PATH "/config/aio-switch-updater/"
|
||||
#define CONFIG_PATH "/config/aio-switch-updater/"
|
||||
|
||||
#define APP_URL "https://github.com/HamletDuFromage/sigpatches-updater/releases/latest/download/aio-switch-updater.zip"
|
||||
#define APP_FILENAME "/config/aio-switch-updater/app.zip"
|
||||
|
||||
#define SIGPATCHES_URL "https://hamletdufromage.github.io/sigpatches-updater/sigpatches.html"
|
||||
#define SIGPATCHES_FILENAME "/config/aio-switch-updater/sigpatches.zip"
|
||||
|
||||
#define FIRMWARE_URL "https://hamletdufromage.github.io/switch-fw/firmwares.html"
|
||||
#define FIRMWARE_FILENAME "/config/aio-switch-updater/firmware.zip"
|
||||
#define FIRMWARE_PATH "/firmware/"
|
||||
|
||||
#define CFW_URL "https://hamletdufromage.github.io/switch-cfw/cfw.html"
|
||||
#define CFW_FILENAME "/config/aio-switch-updater/cfw.zip"
|
||||
|
||||
#define CHEATS_RELEASE_URL "https://github.com/HamletDuFromage/switch-cheats-db/releases/tag/v1.0"
|
||||
#define CHEATS_URL_TITLES "https://github.com/HamletDuFromage/switch-cheats-db/releases/download/v1.0/titles.zip"
|
||||
#define CHEATS_URL_CONTENTS "https://github.com/HamletDuFromage/switch-cheats-db/releases/download/v1.0/contents.zip"
|
||||
#define CHEATS_FILENAME "/config/aio-switch-updater/cheats.zip"
|
||||
#define CHEATS_EXCLUDE "/config/aio-switch-updater/exclude.txt"
|
||||
#define UPDATED_TITLES_PATH "/config/aio-switch-updater/updated.dat"
|
||||
#define AMS_CONTENTS "/atmosphere/contents/"
|
||||
#define REINX_CONTENTS "/ReiNX/contents/"
|
||||
#define SXOS_TITLES "/sxos/titles/"
|
||||
#define AMS_PATH "/atmosphere/"
|
||||
#define SXOS_PATH "/sxos/"
|
||||
#define REINX_PATH "/ReiNX/"
|
||||
#define CONTENTS_PATH "contents/"
|
||||
#define TITLES_PATH "titles/"
|
||||
|
||||
#define COLOR_PROFILES_PATH "/config/aio-switch-updater/jc_profiles.json"
|
||||
|
||||
#define PAYLOAD_PATH "/payloads/"
|
||||
#define BOOTLOADER_PL_PATH "/bootloader/payloads/"
|
||||
|
||||
|
||||
enum archiveType{
|
||||
sigpatches,
|
||||
cheats,
|
||||
fw,
|
||||
app,
|
||||
cfw,
|
||||
};
|
||||
|
||||
enum CFW{
|
||||
rnx,
|
||||
sxos,
|
||||
ams,
|
||||
};
|
32
include/download.hpp
Normal file
32
include/download.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef _DOWNLOAD_H_
|
||||
#define _DOWNLOAD_H_
|
||||
|
||||
#define ON 1
|
||||
#define OFF 0
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include "progress_event.hpp"
|
||||
|
||||
void downloadFile(const char *url, const char *output, int api);
|
||||
|
||||
std::tuple<std::vector<std::string>, std::vector<std::string>> fetchLinks(const char *url);
|
||||
|
||||
std::string fetchTitle(const char *url);
|
||||
|
||||
#endif
|
47
include/extract.hpp
Normal file
47
include/extract.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <switch.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <set>
|
||||
|
||||
#include <unzipper.h>
|
||||
|
||||
#include <borealis.hpp>
|
||||
|
||||
#include "progress_event.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
static constexpr u32 MaxTitleCount = 64000;
|
||||
|
||||
typedef struct Title {
|
||||
std::string id;
|
||||
std::string name;
|
||||
bool operator ==(const Title&x) const {
|
||||
return id == x.id;
|
||||
}
|
||||
|
||||
bool operator <(const Title&x) const {
|
||||
return id < x.id;
|
||||
}
|
||||
} Title;
|
||||
|
||||
void extract(const char* filename, const char* workingPath = ROOT_PATH);
|
||||
std::string formatApplicationId(u64 ApplicationId);
|
||||
std::vector<Title> getInstalledTitlesNs();
|
||||
std::vector<Title> excludeTitles(const char* path, std::vector<Title> listedTitles);
|
||||
void writeTitlesToFile(std::set<std::string> titles);
|
||||
void extractCheats(const char * zipPath, std::vector<Title> titles, CFW cfw, bool credits = false);
|
||||
void removeCheats(CFW cfw);
|
20406
include/json.hpp
Normal file
20406
include/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
22
include/list_download_tab.hpp
Normal file
22
include/list_download_tab.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
#include <string>
|
||||
#include "download.hpp"
|
||||
#include "extract.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "confirm_page.hpp"
|
||||
#include "worker_page.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
class ListDownloadTab : public brls::List
|
||||
{
|
||||
private:
|
||||
std::tuple<std::vector<std::string>, std::vector<std::string>> links;
|
||||
std::vector<brls::ListItem*> linkItems;
|
||||
brls::Label *notFound;
|
||||
brls::Label *description;
|
||||
public:
|
||||
ListDownloadTab(archiveType type);
|
||||
~ListDownloadTab();
|
||||
};
|
17
include/main_frame.hpp
Normal file
17
include/main_frame.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "about_tab.hpp"
|
||||
#include "list_download_tab.hpp"
|
||||
#include "tools_tab.hpp"
|
||||
|
||||
class MainFrame : public brls::TabFrame
|
||||
{
|
||||
private:
|
||||
//RefreshTask *refreshTask;
|
||||
|
||||
public:
|
||||
MainFrame();
|
||||
};
|
17
include/payload_page.hpp
Normal file
17
include/payload_page.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "reboot_payload.h"
|
||||
|
||||
class PayloadPage : public brls::AppletFrame
|
||||
{
|
||||
private:
|
||||
brls::Label* label;
|
||||
brls::List* list;
|
||||
std::vector<brls::ListItem*> items;
|
||||
brls::ListItem* reboot;
|
||||
brls::ListItem* shutDown;
|
||||
|
||||
public:
|
||||
PayloadPage();
|
||||
};
|
30
include/progress_event.hpp
Normal file
30
include/progress_event.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
class ProgressEvent{
|
||||
private:
|
||||
ProgressEvent() {}
|
||||
int _current = 0;
|
||||
int _max = 60;
|
||||
|
||||
public:
|
||||
ProgressEvent(const ProgressEvent&) = delete;
|
||||
ProgressEvent& operator=(const ProgressEvent &) = delete;
|
||||
ProgressEvent(ProgressEvent &&) = delete;
|
||||
ProgressEvent & operator=(ProgressEvent &&) = delete;
|
||||
|
||||
static auto& instance(){
|
||||
static ProgressEvent event;
|
||||
return event;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_current = 0;
|
||||
_max = 60;
|
||||
}
|
||||
|
||||
inline void setTotalSteps(int steps) { _max = steps; }
|
||||
inline void setStep(int step) { _current = step; }
|
||||
inline int getStep() { return _current; }
|
||||
inline bool finished() { return (_current == _max) ; }
|
||||
inline int getMax() { return _max; }
|
||||
};
|
19
include/reboot_payload.h
Normal file
19
include/reboot_payload.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef __REBOOT_PAYLOAD_H__
|
||||
#define __REBOOT_PAYLOAD_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <switch.h>
|
||||
|
||||
int reboot_to_payload(const char* path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
26
include/tools_tab.hpp
Normal file
26
include/tools_tab.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
#include "confirm_page.hpp"
|
||||
#include "worker_page.hpp"
|
||||
#include "app_page.hpp"
|
||||
#include "payload_page.hpp"
|
||||
#include "JC_page.hpp"
|
||||
#include "extract.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
class ToolsTab : public brls::List
|
||||
{
|
||||
private:
|
||||
brls::Label* label;
|
||||
brls::ListItem* deleteCheats;
|
||||
brls::ListItem* viewCheats;
|
||||
brls::ListItem* JCcolor;
|
||||
brls::ListItem* updateApp;
|
||||
brls::ListItem* rebootPayload;
|
||||
brls::StagedAppletFrame* stagedFrame;
|
||||
|
||||
public:
|
||||
ToolsTab();
|
||||
|
||||
};
|
24
include/utils.hpp
Normal file
24
include/utils.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include "download.hpp"
|
||||
#include "extract.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "progress_event.hpp"
|
||||
|
||||
CFW getCFW();
|
||||
bool isServiceRunning(const char *serviceName);
|
||||
std::vector<std::string> htmlProcess(std::string s, std::regex rgx);
|
||||
void createTree(std::string path);
|
||||
void clearConsole();
|
||||
bool isArchive(const char * path);
|
||||
void downloadArchive(std::string url, archiveType type);
|
||||
void extractArchive(archiveType type);
|
||||
void progressTest(std::string url, archiveType type);
|
||||
std::string formatListItemTitle(const std::string str, size_t maxScore = 140);
|
||||
std::string formatApplicationId(u64 ApplicationId);
|
||||
std::set<std::string> readLineByLine(const char * path);
|
||||
std::vector<std::string> fetchPayloads();
|
||||
void shut_down(bool reboot = false);
|
36
include/worker_page.hpp
Normal file
36
include/worker_page.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <borealis.hpp>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "download.hpp"
|
||||
#include "extract.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "progress_event.hpp"
|
||||
|
||||
typedef std::function<void()> worker_func_t;
|
||||
|
||||
class WorkerPage : public brls::View
|
||||
{
|
||||
private:
|
||||
brls::ProgressDisplay* progressDisp = nullptr;
|
||||
brls::StagedAppletFrame* frame;
|
||||
brls::Button* button;
|
||||
brls::Label* label;
|
||||
int progressValue = 0;
|
||||
bool workStarted = false;
|
||||
std::thread* workerThread;
|
||||
worker_func_t workerFunc;
|
||||
|
||||
public:
|
||||
WorkerPage(brls::StagedAppletFrame* frame, const std::string& text, worker_func_t worker_func);
|
||||
~WorkerPage();
|
||||
|
||||
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
|
||||
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
|
||||
void doWork();
|
||||
brls::View* getDefaultFocus() override;
|
||||
};
|
107
jc_profiles.json
Normal file
107
jc_profiles.json
Normal file
|
@ -0,0 +1,107 @@
|
|||
[
|
||||
{
|
||||
"L_BTN": "0A1E0A",
|
||||
"L_JC": "82FF96",
|
||||
"R_BTN": "0A1E28",
|
||||
"R_JC": "96F5F5",
|
||||
"name": "Animal Crossing: New Horizons"
|
||||
},
|
||||
{
|
||||
"L_BTN": "0F0F0F",
|
||||
"L_JC": "828282",
|
||||
"R_BTN": "0F0F0F",
|
||||
"R_JC": "828282",
|
||||
"name": "Gray"
|
||||
},
|
||||
{
|
||||
"L_BTN": "001E1E",
|
||||
"L_JC": "0AB9E6",
|
||||
"R_BTN": "1E0A0A",
|
||||
"R_JC": "FF3C28",
|
||||
"name": "Neon Blue (L) & Neon Red (R)"
|
||||
},
|
||||
{
|
||||
"L_BTN": "1E0A0A",
|
||||
"L_JC": "FF3C28",
|
||||
"R_BTN": "001E1E",
|
||||
"R_JC": "0AB9E6",
|
||||
"name": "Neon Red (L) & Neon Blue (R)"
|
||||
},
|
||||
{
|
||||
"L_BTN": "001E1E",
|
||||
"L_JC": "0AB9E6",
|
||||
"R_BTN": "001E1E",
|
||||
"R_JC": "0AB9E6",
|
||||
"name": "Neon Blue"
|
||||
},
|
||||
{
|
||||
"L_BTN": "1E0A0A ",
|
||||
"L_JC": "FF3C28",
|
||||
"R_BTN": "1E0A0A ",
|
||||
"R_JC": "FF3C28",
|
||||
"name": "Neon Red"
|
||||
},
|
||||
{
|
||||
"L_BTN": "142800",
|
||||
"L_JC": "E6FF00",
|
||||
"R_BTN": "142800",
|
||||
"R_JC": "E6FF00",
|
||||
"name": "Neon Yellow"
|
||||
},
|
||||
{
|
||||
"L_BTN": "28001E",
|
||||
"L_JC": "FF3278",
|
||||
"R_BTN": "28001E",
|
||||
"R_JC": "FF3278",
|
||||
"name": "Neon Pink"
|
||||
},
|
||||
{
|
||||
"L_BTN": "002800",
|
||||
"L_JC": "1EDC00",
|
||||
"R_BTN": "002800",
|
||||
"R_JC": "1EDC00",
|
||||
"name": "Neon Green"
|
||||
},
|
||||
{
|
||||
"L_BTN": "280A0A",
|
||||
"L_JC": "E10F00",
|
||||
"R_BTN": "280A0A",
|
||||
"R_JC": "E10F00",
|
||||
"name": "Red"
|
||||
},
|
||||
{
|
||||
"L_BTN": "00000A",
|
||||
"L_JC": "4655F5",
|
||||
"R_BTN": "00000A",
|
||||
"R_JC": "4655F5",
|
||||
"name": "Blue"
|
||||
},
|
||||
{
|
||||
"L_BTN": "FFFFF",
|
||||
"L_JC": "323232",
|
||||
"R_BTN": "FFFFF",
|
||||
"R_JC": "323232",
|
||||
"name": "Pro Black"
|
||||
},
|
||||
{
|
||||
"L_BTN": "281900",
|
||||
"L_JC": "C88C32",
|
||||
"R_BTN": "322800",
|
||||
"R_JC": "FFDC00",
|
||||
"name": "Pokemon Let's Go! Eevee (L) and Pikachu (R)"
|
||||
},
|
||||
{
|
||||
"L_BTN": "1E1914",
|
||||
"L_JC": "D7AA73",
|
||||
"R_BTN": "1E1914",
|
||||
"R_JC": "D7AA73",
|
||||
"name": "Nintendo Labo Creators Contest Edition"
|
||||
},
|
||||
{
|
||||
"L_BTN": "0A1E0A",
|
||||
"L_JC": "82FF96",
|
||||
"R_BTN": "0A1E28",
|
||||
"R_JC": "96F5F5",
|
||||
"name": "Animal Crossing: New Horizons"
|
||||
}
|
||||
]
|
8
pack_release.sh
Normal file
8
pack_release.sh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd $DIR
|
||||
rm -r switch/aio-switch-updater/
|
||||
mkdir -p switch/aio-switch-updater/
|
||||
mv *.nro switch/aio-switch-updater/aio-switch-updater.nro
|
||||
zip -r aio-switch-updater.zip switch/
|
201
source/JC_color_swapper.cpp
Normal file
201
source/JC_color_swapper.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
#include "JC_color_swapper.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int hexToBGR(std::string hex){
|
||||
std::string R = hex.substr(0, 2);
|
||||
std::string G = hex.substr(2, 2);
|
||||
std::string B = hex.substr(4, 2);
|
||||
return std::stoi(B + G + R, 0, 16);
|
||||
}
|
||||
|
||||
std::string BGRToHex(int v){
|
||||
std::stringstream ss;
|
||||
v = ((v & 0xFF) << 16) + (v & 0xFF00) + (v >> 16) + 256;
|
||||
ss << std::setfill('0') << std::setw(6) << std::right << std::hex << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool isHexaAnd3Bytes(std::string str){
|
||||
if(str.size()!=6) return false;
|
||||
for(char const &c : str){
|
||||
if(!isxdigit(c)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int setColor(std::vector<int> colors){
|
||||
Result pads, ljc, rjc;
|
||||
int res = 0;
|
||||
s32 nbEntries;
|
||||
u64 UniquePadIds[2] = {};
|
||||
|
||||
pads = hidsysGetUniquePadsFromNpad(CONTROLLER_HANDHELD, UniquePadIds, 2,&nbEntries);
|
||||
if(R_SUCCEEDED(pads)){
|
||||
ljc = hiddbgUpdateControllerColor(colors[0], colors[1], UniquePadIds[0]);
|
||||
if (R_FAILED(ljc)) res +=1;
|
||||
rjc = hiddbgUpdateControllerColor(colors[2], colors[3], UniquePadIds[1]);
|
||||
if (R_FAILED(rjc)) res +=2;
|
||||
}
|
||||
else{
|
||||
res +=4;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int backupToJSON(json &profiles, const char* path){
|
||||
HidControllerColors colors;
|
||||
hidGetControllerColors(CONTROLLER_P1_AUTO, &colors);
|
||||
std::vector<int> oldBackups;
|
||||
if (colors.splitSet) {
|
||||
int i = 0;
|
||||
for (const auto& x : profiles.items()){
|
||||
if(x.value()["name"] == "_backup") {
|
||||
oldBackups.push_back(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
for (auto &k : oldBackups){
|
||||
profiles.erase(profiles.begin() + k);
|
||||
}
|
||||
json newBackup = json::object({
|
||||
{"name", "_backup"},
|
||||
{"L_JC", BGRToHex(colors.leftColorBody)},
|
||||
{"L_BTN", BGRToHex(colors.leftColorButtons)},
|
||||
{"R_JC", BGRToHex(colors.rightColorBody)},
|
||||
{"R_BTN", BGRToHex(colors.rightColorButtons)}
|
||||
});
|
||||
profiles.push_back(newBackup);
|
||||
writeJSONToFile(profiles, path);
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
json backupProfile(){
|
||||
json newBackup;
|
||||
HidControllerColors colors;
|
||||
hidGetControllerColors(CONTROLLER_P1_AUTO, &colors);
|
||||
//if(0){
|
||||
if (colors.splitSet) {
|
||||
newBackup = json::object({
|
||||
{"name", "_backup"},
|
||||
{"L_JC", BGRToHex(colors.leftColorBody)},
|
||||
{"L_BTN", BGRToHex(colors.leftColorButtons)},
|
||||
{"R_JC", BGRToHex(colors.rightColorBody)},
|
||||
{"R_BTN", BGRToHex(colors.rightColorButtons)}
|
||||
});
|
||||
}
|
||||
return newBackup;
|
||||
|
||||
}
|
||||
|
||||
void writeJSONToFile(json &profiles, const char* path){
|
||||
std::fstream newProfiles;
|
||||
newProfiles.open(path, std::fstream::out | std::fstream::trunc);
|
||||
newProfiles << std::setw(4) << profiles << std::endl;
|
||||
newProfiles.close();
|
||||
}
|
||||
|
||||
std::tuple<std::vector<std::string>, std::vector<std::vector<int>>> getProfiles(const char* path){
|
||||
std::vector<std::string> names;
|
||||
std::vector<std::vector<int>> colorValues;
|
||||
bool properData;
|
||||
std::fstream profilesFile;
|
||||
json profilesJson;
|
||||
if(std::filesystem::exists(path)){
|
||||
profilesFile.open(path, std::fstream::in);
|
||||
profilesFile >> profilesJson;
|
||||
profilesFile.close();
|
||||
}
|
||||
if(profilesJson.empty()){
|
||||
profilesJson = {{
|
||||
{"L_BTN", "0A1E0A"},
|
||||
{"L_JC", "82FF96"},
|
||||
{"R_BTN", "0A1E28"},
|
||||
{"R_JC", "96F5F5"},
|
||||
{"name", "Animal Crossing: New Horizons"}
|
||||
}};
|
||||
writeJSONToFile(profilesJson, path);
|
||||
}
|
||||
for (const auto& x : profilesJson.items()){
|
||||
std::string name = x.value()["name"];
|
||||
std::vector<std::string> values = {
|
||||
std::string(x.value()["L_JC"]),
|
||||
std::string(x.value()["L_BTN"]),
|
||||
std::string(x.value()["R_JC"]),
|
||||
std::string(x.value()["R_BTN"])
|
||||
};
|
||||
properData = true;
|
||||
for(auto& str : values){
|
||||
if(!isHexaAnd3Bytes(str)){
|
||||
properData = false;
|
||||
}
|
||||
}
|
||||
if(properData){
|
||||
if(name == "") name = "Unamed";
|
||||
names.push_back(name);
|
||||
colorValues.push_back({
|
||||
hexToBGR(values[0]),
|
||||
hexToBGR(values[1]),
|
||||
hexToBGR(values[2]),
|
||||
hexToBGR(values[3])
|
||||
});
|
||||
}
|
||||
}
|
||||
return std::make_tuple(names, colorValues);
|
||||
}
|
||||
|
||||
void changeJCColor(std::vector<int> values){
|
||||
hiddbgInitialize();
|
||||
hidsysInitialize();
|
||||
ProgressEvent::instance().reset();
|
||||
ProgressEvent::instance().setStep(1);
|
||||
int res = -1;
|
||||
while(res !=0){
|
||||
res = setColor(values);
|
||||
}
|
||||
hiddbgExit();
|
||||
hidsysExit();
|
||||
ProgressEvent::instance().setStep(ProgressEvent::instance().getMax());
|
||||
}
|
||||
|
||||
void backupJCColor(const char* path){
|
||||
hiddbgInitialize();
|
||||
hidsysInitialize();
|
||||
ProgressEvent::instance().reset();
|
||||
ProgressEvent::instance().setStep(1);
|
||||
json backup;
|
||||
json profiles;
|
||||
std::fstream profilesFile;
|
||||
if(std::filesystem::exists(path)){
|
||||
profilesFile.open(path, std::fstream::in);
|
||||
profilesFile >> profiles;
|
||||
profilesFile.close();
|
||||
}
|
||||
|
||||
std::vector<int> oldBackups;
|
||||
int i = 0;
|
||||
for (const auto& x : profiles.items()){
|
||||
if(x.value()["name"] == "_backup") {
|
||||
oldBackups.push_back(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
for (auto &k : oldBackups){
|
||||
profiles.erase(profiles.begin() + k);
|
||||
}
|
||||
|
||||
while(backup.empty()){
|
||||
backup = backupProfile();
|
||||
}
|
||||
|
||||
profiles.push_back(backup);
|
||||
//backup.push_back(profiles);
|
||||
writeJSONToFile(profiles, path);
|
||||
hiddbgExit();
|
||||
hidsysExit();
|
||||
ProgressEvent::instance().setStep(ProgressEvent::instance().getMax());
|
||||
}
|
55
source/JC_page.cpp
Normal file
55
source/JC_page.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "JC_page.hpp"
|
||||
|
||||
JCPage::JCPage() : AppletFrame(true, true)
|
||||
{
|
||||
this->setTitle("Joy-Con color swapper");
|
||||
list = new brls::List();
|
||||
std::string labelText = "You can change the internal color of your Joy-Cons. Make sure they're docked.\n"\
|
||||
"Color profiles are stored in '" + std::string(COLOR_PROFILES_PATH) + "'. Go to http://bit.ly/JC-color "\
|
||||
"to generate your own custom profiles.";
|
||||
label = new brls::Label(brls::LabelStyle::DESCRIPTION, labelText, true);
|
||||
list->addView(label);
|
||||
|
||||
backup = new brls::ListItem("Backup current color profile");
|
||||
backup->getClickEvent()->subscribe([&](brls::View* view) {
|
||||
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle("Joy-Con color swapper");
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Backing up the current color profile. Make sure the Joy-Con are docked. "\
|
||||
"If the process hangs, try docking/undocking the JCs.",
|
||||
[](){backupJCColor(COLOR_PROFILES_PATH);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done!", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
list->addView(backup);
|
||||
|
||||
list->addView(new brls::ListItemGroupSpacing(true));
|
||||
|
||||
auto profiles = getProfiles(COLOR_PROFILES_PATH);
|
||||
std::vector<std::string> names = std::get<0>(profiles);
|
||||
int nbProfiles = names.size();
|
||||
items.reserve(nbProfiles);
|
||||
for (int i = nbProfiles - 1; i >= 0; i--){
|
||||
std::string name = std::get<0>(profiles)[i];
|
||||
std::vector<int> value = std::get<1>(profiles)[i];
|
||||
items[i] = new brls::ListItem(names[i]);
|
||||
items[i]->getClickEvent()->subscribe([&, value](brls::View* view) {
|
||||
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle("Joy-Con color swapper");
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Changing color. Make sure the Joy-Con are docked. "\
|
||||
"If the process hangs, try docking/undocking the JCs.",
|
||||
[value](){changeJCColor(value);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done! You may need to dock/undock your Joy-Cons for the change to take effect", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
list->addView(items[i]);
|
||||
}
|
||||
this->setContentView(list);
|
||||
}
|
34
source/about_tab.cpp
Normal file
34
source/about_tab.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "about_tab.hpp"
|
||||
|
||||
AboutTab::AboutTab()
|
||||
{
|
||||
// Subtitle
|
||||
brls::Label *subTitle = new brls::Label(
|
||||
brls::LabelStyle::REGULAR,
|
||||
"All-in-One Nintendo Switch Updater",
|
||||
true
|
||||
);
|
||||
subTitle->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->addView(subTitle);
|
||||
|
||||
// Copyright
|
||||
brls::Label *copyright = new brls::Label(
|
||||
brls::LabelStyle::DESCRIPTION,
|
||||
"AIO-switch-updater is licensed under GPL-3.0\n" \
|
||||
"\u00A9 2020 HamletDuFromage",
|
||||
true
|
||||
);
|
||||
copyright->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->addView(copyright);
|
||||
|
||||
// Links
|
||||
this->addView(new brls::Header("Disclaimers"));
|
||||
brls::Label *links = new brls::Label(
|
||||
brls::LabelStyle::SMALL,
|
||||
"\uE016 Aside from cheat codes that are mirrored from the main Gbatemp thread, "\
|
||||
"HamletDuFromage isn't hosting anything. All credits go to respective owners\n"\
|
||||
"\uE016 Links are refreshed every three hours. If a link remains broken after 3 hours have passed, please open a Github issue\n",
|
||||
true
|
||||
);
|
||||
this->addView(links);
|
||||
}
|
99
source/app_page.cpp
Normal file
99
source/app_page.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "app_page.hpp"
|
||||
//TODO: Serialize it in extract.cpp
|
||||
|
||||
AppPage::AppPage() : AppletFrame(true, true)
|
||||
{
|
||||
this->setTitle("Installed cheats");
|
||||
list = new brls::List();
|
||||
label = new brls::Label(
|
||||
brls::LabelStyle::DESCRIPTION,
|
||||
"The following titles have had cheats downloaded for last time you used the app. Please note despite having been "\
|
||||
"downloaded for a game, cheats may not match its current update.",
|
||||
true
|
||||
);
|
||||
list->addView(label);
|
||||
|
||||
NsApplicationRecord record;
|
||||
uint64_t tid;
|
||||
NsApplicationControlData controlData;
|
||||
NacpLanguageEntry* langEntry = NULL;
|
||||
|
||||
Result rc;
|
||||
size_t i = 0;
|
||||
int recordCount = 0;
|
||||
size_t controlSize = 0;
|
||||
|
||||
titles = readLineByLine(UPDATED_TITLES_PATH);
|
||||
|
||||
if(!titles.empty()){
|
||||
while (true)
|
||||
{
|
||||
rc = nsListApplicationRecord(&record, sizeof(record), i, &recordCount);
|
||||
if (R_FAILED(rc)) break;
|
||||
|
||||
if(recordCount <= 0)
|
||||
break;
|
||||
|
||||
tid = record.application_id;
|
||||
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, tid, &controlData, sizeof(controlData), &controlSize);
|
||||
if (R_FAILED(rc)) break;
|
||||
rc = nacpGetLanguageEntry(&controlData.nacp, &langEntry);
|
||||
if (R_FAILED(rc)) break;
|
||||
if (!langEntry->name || titles.find(formatApplicationId(tid)) == titles.end())
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
App* app = (App*) malloc(sizeof(App));
|
||||
app->tid = tid;
|
||||
|
||||
memset(app->name, 0, sizeof(app->name));
|
||||
strncpy(app->name, langEntry->name, sizeof(app->name)-1);
|
||||
|
||||
memcpy(app->icon, controlData.icon, sizeof(app->icon));
|
||||
|
||||
// Add the ListItem
|
||||
brls::ListItem *listItem = new brls::ListItem(formatListItemTitle(std::string(app->name)), "", formatApplicationId(app->tid));
|
||||
|
||||
listItem->setThumbnail(app->icon, sizeof(app->icon));
|
||||
list->addView(listItem);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
std::string text("Downloading:\nLatest cheat codes\n\nFrom:\n");
|
||||
std::string url = "";
|
||||
switch(getCFW()){
|
||||
case ams:
|
||||
url += CHEATS_URL_CONTENTS;
|
||||
break;
|
||||
case rnx:
|
||||
url += CHEATS_URL_CONTENTS;
|
||||
break;
|
||||
case sxos:
|
||||
url += CHEATS_URL_CONTENTS;
|
||||
break;
|
||||
}
|
||||
text += url;
|
||||
download = new brls::ListItem("Download latest cheat codes");
|
||||
archiveType type = cheats;
|
||||
download->getClickEvent()->subscribe([&, url, text, type](brls::View* view) {
|
||||
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle("Getting cheat codes");
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, text)
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Downloading...", [url, type](){downloadArchive(url, type);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Extracting...", [type](){extractArchive(type);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done!", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
list->addView(download);
|
||||
this->setContentView(list);
|
||||
|
||||
}
|
74
source/confirm_page.cpp
Normal file
74
source/confirm_page.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "confirm_page.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
ConfirmPage::ConfirmPage(brls::StagedAppletFrame* frame, std::string text, bool done): done(done)
|
||||
{
|
||||
this->button = (new brls::Button(brls::ButtonStyle::BORDERLESS))->setLabel(done ? "Back": "Continue");
|
||||
this->button->setParent(this);
|
||||
this->button->getClickEvent()->subscribe([frame, this](View* view) {
|
||||
if (!frame->isLastStage()) frame->nextStage();
|
||||
else if (this->done) {
|
||||
brls::Application::pushView(new MainFrame());
|
||||
}
|
||||
});
|
||||
|
||||
this->label = new brls::Label(brls::LabelStyle::DIALOG, text, true);
|
||||
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->label->setParent(this);
|
||||
|
||||
this->registerAction("Back", brls::Key::B, [this] {
|
||||
brls::Application::popView();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void ConfirmPage::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
|
||||
{
|
||||
if(!this->done){
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto missing = std::max(2l - std::chrono::duration_cast<std::chrono::seconds>(end - start).count(), 0l);
|
||||
auto text = std::string("Continue");
|
||||
if (missing > 0) {
|
||||
this->button->setLabel(text + " (" + std::to_string(missing) + ")");
|
||||
this->button->setState(brls::ButtonState::DISABLED);
|
||||
} else {
|
||||
this->button->setLabel(text);
|
||||
this->button->setState(brls::ButtonState::ENABLED);
|
||||
}
|
||||
this->button->invalidate();
|
||||
}
|
||||
this->label->frame(ctx);
|
||||
this->button->frame(ctx);
|
||||
}
|
||||
|
||||
brls::View* ConfirmPage::getDefaultFocus()
|
||||
{
|
||||
return this->button;
|
||||
}
|
||||
|
||||
void ConfirmPage::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
|
||||
{
|
||||
this->label->setWidth(this->width);
|
||||
this->label->invalidate(true);
|
||||
// this->label->setBackground(brls::ViewBackground::DEBUG);
|
||||
this->label->setBoundaries(
|
||||
this->x + this->width / 2 - this->label->getWidth() / 2,
|
||||
this->y + (this->height - this->label->getHeight() - this->y - style->CrashFrame.buttonHeight)/2,
|
||||
this->label->getWidth(),
|
||||
this->label->getHeight());
|
||||
|
||||
this->button->setBoundaries(
|
||||
this->x + this->width / 2 - style->CrashFrame.buttonWidth / 2,
|
||||
this->y + (this->height-style->CrashFrame.buttonHeight*3),
|
||||
style->CrashFrame.buttonWidth,
|
||||
style->CrashFrame.buttonHeight);
|
||||
this->button->invalidate();
|
||||
|
||||
start = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(150);
|
||||
}
|
||||
|
||||
ConfirmPage::~ConfirmPage()
|
||||
{
|
||||
delete this->label;
|
||||
delete this->button;
|
||||
}
|
191
source/download.cpp
Normal file
191
source/download.cpp
Normal file
|
@ -0,0 +1,191 @@
|
|||
#include "download.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#define API_AGENT "HamletDuFromage"
|
||||
#define _1MiB 0x100000
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *memory;
|
||||
size_t size;
|
||||
} MemoryStruct_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u_int8_t *data;
|
||||
size_t data_size;
|
||||
u_int64_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;
|
||||
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;
|
||||
}
|
||||
|
||||
int download_progress(void *p, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
if (dltotal <= 0.0) return 0;
|
||||
|
||||
double fractionDownloaded = dlnow / dltotal;
|
||||
int counter = (int) (fractionDownloaded * ProgressEvent::instance().getMax()); //20 is the number of increments
|
||||
ProgressEvent::instance().setStep(std::min(ProgressEvent::instance().getMax() - 1, counter));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void downloadFile(const char *url, const char *output, int api)
|
||||
{
|
||||
ProgressEvent::instance().reset();
|
||||
CURL *curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
FILE *fp = fopen(output, "wb");
|
||||
if (fp)
|
||||
{
|
||||
printf("\n");
|
||||
|
||||
ntwrk_struct_t chunk = {0};
|
||||
chunk.data = static_cast<u_int8_t *>(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);
|
||||
|
||||
// write calls
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
|
||||
|
||||
if (api == OFF)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
||||
//curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, download_progress);
|
||||
}
|
||||
curl_easy_perform(curl);
|
||||
if (chunk.offset)
|
||||
fwrite(chunk.data, 1, chunk.offset, fp);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
free(chunk.data);
|
||||
fclose(chunk.out);
|
||||
|
||||
ProgressEvent::instance().setStep(ProgressEvent::instance().getMax());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MemoryStruct {
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static size_t WriteMemoryCallback2(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||
|
||||
char *ptr = static_cast<char *>(realloc(mem->memory, mem->size + realsize + 1));
|
||||
if(ptr == NULL) {
|
||||
/* out of memory! */
|
||||
printf("not enough memory (realloc returned NULL)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mem->memory = ptr;
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::vector<std::string>, std::vector<std::string>> fetchLinks(const char *url){
|
||||
CURL *curl_handle;
|
||||
struct MemoryStruct chunk;
|
||||
|
||||
chunk.memory = static_cast<char *>(malloc(1)); /* will be grown as needed by the realloc above */
|
||||
chunk.size = 0; /* no data at this point */
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl_handle = curl_easy_init();
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback2);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, API_AGENT);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_perform(curl_handle);
|
||||
|
||||
std::tuple <std::vector<std::string>, std::vector<std::string>> links;
|
||||
|
||||
std::string s = std::string(chunk.memory);
|
||||
//<a href=.*?> links
|
||||
//>(?:(?!>).)*?</a> titles
|
||||
auto titles = htmlProcess(s, std::regex(">(?:(?!>).)*?</a>"));
|
||||
std::transform(titles.begin(), titles.end(), titles.begin(),
|
||||
[](std::string s) -> std::string {return s.substr(1, s.size() - 1 - 4);});
|
||||
auto targets = htmlProcess(s, std::regex("<a href=.*?>"));
|
||||
std::transform(targets.begin(), targets.end(), targets.begin(),
|
||||
[](std::string s) -> std::string {return s.substr(8, s.size() - 8 - 1);});
|
||||
links = std::make_tuple(titles, targets);
|
||||
|
||||
curl_easy_cleanup(curl_handle);
|
||||
free(chunk.memory);
|
||||
curl_global_cleanup();
|
||||
return links;
|
||||
}
|
||||
|
||||
std::string fetchTitle(const char *url){
|
||||
CURL *curl_handle;
|
||||
struct MemoryStruct chunk;
|
||||
|
||||
chunk.memory = static_cast<char *>(malloc(1)); /* will be grown as needed by the realloc above */
|
||||
chunk.size = 0; /* no data at this point */
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl_handle = curl_easy_init();
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback2);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, API_AGENT);
|
||||
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_perform(curl_handle);
|
||||
|
||||
/* check for errors */
|
||||
std::string ver = "-1";
|
||||
std::string s = std::string(chunk.memory);
|
||||
//std::cout << "start html " << s.substr(0, 199) << std::endl;
|
||||
std::regex rgx("<title>.+</title>");
|
||||
std::smatch match;
|
||||
if (std::regex_search(s, match, rgx)){
|
||||
//ver = std::stoi(match[0]);
|
||||
//std::cout << match[0].str().substr(match[0].str().find(" ") + 1, 6) << std::endl;
|
||||
ver = match[0].str().substr(match[0].str().find(" ") + 1, 5);
|
||||
}
|
||||
curl_easy_cleanup(curl_handle);
|
||||
free(chunk.memory);
|
||||
|
||||
curl_global_cleanup();
|
||||
|
||||
return ver;
|
||||
}
|
287
source/extract.cpp
Normal file
287
source/extract.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
#include "extract.hpp"
|
||||
|
||||
void extract(const char * filename, const char* workingPath){
|
||||
ProgressEvent::instance().reset();
|
||||
chdir(workingPath);
|
||||
zipper::Unzipper unzipper(filename);
|
||||
std::vector<zipper::ZipEntry> entries = unzipper.entries();
|
||||
ProgressEvent::instance().setTotalSteps(entries.size());
|
||||
for (int i = 0; i < (int) entries.size(); i++){
|
||||
unzipper.extractEntry(entries[i].name);
|
||||
ProgressEvent::instance().setStep(i);
|
||||
}
|
||||
unzipper.close();
|
||||
ProgressEvent::instance().setStep(entries.size());
|
||||
}
|
||||
|
||||
|
||||
std::vector<Title> getInstalledTitlesNs(){
|
||||
// This function has been cobbled together from the "app_controldata" example in devkitpro.
|
||||
|
||||
// Set the rc variable to begin with
|
||||
Result rc = 0;
|
||||
|
||||
// Initialise a vector of tuples, storing the title ID and the title name.
|
||||
std::vector<Title> titles;
|
||||
|
||||
// Initialise an application record array, where the size is MaxTitleCount
|
||||
NsApplicationRecord *recs = new NsApplicationRecord[MaxTitleCount]();
|
||||
|
||||
// Set the buffer to NULL
|
||||
NsApplicationControlData *buf=NULL;
|
||||
// Set outsize to 0
|
||||
u64 outsize=0;
|
||||
// Set the language entry to NULL
|
||||
NacpLanguageEntry *langentry = NULL;
|
||||
|
||||
// Create a char array to store the name of the title
|
||||
char name[0x201];
|
||||
|
||||
// Set the total records to 0
|
||||
s32 total = 0;
|
||||
// Set a failed counter to 0
|
||||
int totalFailed = 0;
|
||||
// Fill the recs array with application records
|
||||
rc = nsListApplicationRecord(recs, MaxTitleCount, 0, &total);
|
||||
|
||||
// If filling the recs array was successful
|
||||
if (R_SUCCEEDED(rc)){
|
||||
// Loop through records
|
||||
for (s32 i = 0; i < total; i++){
|
||||
|
||||
// Reset varaibles for accessing memory
|
||||
outsize=0;
|
||||
buf = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
|
||||
if (buf==NULL) {
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||
} else {
|
||||
memset(buf, 0, sizeof(NsApplicationControlData));
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = nsInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
printf("nsInitialize() failed: 0x%x\n", rc);
|
||||
}
|
||||
}
|
||||
// Get the application control data for the current record
|
||||
rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, recs[i].application_id, buf, sizeof(NsApplicationControlData), &outsize);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
totalFailed++;
|
||||
std::cout << "nsGetApplicationControlData() failed: 0x" << std::hex << rc << " for Title ID: " << formatApplicationId(recs[i].application_id) << std::endl;
|
||||
}
|
||||
|
||||
if (outsize < sizeof(buf->nacp)) {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
// If application control data was retrieved successfully
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = nacpGetLanguageEntry(&buf->nacp, &langentry);
|
||||
}
|
||||
|
||||
// If nacp language entry was retrieved successfully
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
memset(name, 0, sizeof(name));
|
||||
strncpy(name, langentry->name, sizeof(name)-1); //Don't assume the nacp string is NUL-terminated for safety.
|
||||
titles.push_back({formatApplicationId(recs[i].application_id), name});
|
||||
}
|
||||
|
||||
nsExit();
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
delete[] recs;
|
||||
std::sort(titles.begin(), titles.end());
|
||||
return titles;
|
||||
}
|
||||
|
||||
std::vector<Title> excludeTitles(const char* path, std::vector<Title> listedTitles){
|
||||
// Initialise a vector of titles
|
||||
std::vector<Title> titles;
|
||||
// Open a file stream
|
||||
std::ifstream file(path);
|
||||
// Declare a total variable and set to 0
|
||||
int total = 0;
|
||||
// Declare a name variable
|
||||
std::string name;
|
||||
|
||||
|
||||
if (file.is_open()) {
|
||||
std::string line;
|
||||
// Read each line of the file in
|
||||
while (std::getline(file, line)) {
|
||||
name = "No name provided";
|
||||
// Change the line to uppercase
|
||||
std::transform(line.begin(), line.end(), line.begin(), ::toupper);
|
||||
|
||||
for(int i = 0; i < (int)listedTitles.size(); i++) {
|
||||
// Iterate through the listedTitles, and compare the id's in the file, to the installed title ids
|
||||
// When a match is found, it sets the name of the excluded title to the name of the installed title
|
||||
if(listedTitles.at(i).id == line) {
|
||||
name = listedTitles.at(i).name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Push a new title to the titles vector and increment the total number of excluded titles
|
||||
titles.push_back({line, name});
|
||||
total++;
|
||||
}
|
||||
// Close the file
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Sort the titles list (uses an overloaded < operator to sort based on ID's)
|
||||
std::sort(titles.begin(), titles.end());
|
||||
|
||||
// Create a vector to store the difference between the excluded titles list, and the installed title list
|
||||
std::vector<Title> diff;
|
||||
// Populate the difference vector
|
||||
std::set_difference(listedTitles.begin(), listedTitles.end(), titles.begin(), titles.end(),
|
||||
std::inserter(diff, diff.begin()));
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool caselessCompare (const std::string& a, const std::string& b){
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
|
||||
void extractCheats(const char * zipPath, std::vector<Title> titles, CFW cfw, bool credits){
|
||||
//TODO: REWRITE WITH SETS INSTEAD OF VECTORS
|
||||
ProgressEvent::instance().reset();
|
||||
zipper::Unzipper unzipper(zipPath);
|
||||
std::vector<zipper::ZipEntry> entries = unzipper.entries();
|
||||
std::set<std::string> extractedTitles;
|
||||
int offset = 0;
|
||||
switch(cfw){
|
||||
case ams:
|
||||
offset = std::string(CONTENTS_PATH).length();
|
||||
createTree(AMS_CONTENTS);
|
||||
chdir(AMS_PATH);
|
||||
break;
|
||||
case rnx:
|
||||
offset = std::string(CONTENTS_PATH).length();
|
||||
createTree(REINX_CONTENTS);
|
||||
chdir(REINX_PATH);
|
||||
break;
|
||||
case sxos:
|
||||
offset = std::string(TITLES_PATH).length();
|
||||
createTree(SXOS_TITLES);
|
||||
chdir(SXOS_PATH);
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> entriesNames;
|
||||
std::vector<int> parentIndexes;
|
||||
for (size_t i = 1; i < entries.size(); i++){
|
||||
entriesNames.push_back(entries[i].name);
|
||||
}
|
||||
|
||||
|
||||
std::sort(entriesNames.begin(), entriesNames.end(), caselessCompare);
|
||||
|
||||
std::vector<std::string> parents;
|
||||
std::vector<std::vector<std::string>> children;
|
||||
std::vector<std::string> tempChildren;
|
||||
|
||||
|
||||
size_t k = 0;
|
||||
while(k < entriesNames.size()){
|
||||
if(entriesNames[k].length() == (size_t) (offset + 17)){
|
||||
parents.push_back(entriesNames[k]);
|
||||
k++;
|
||||
while(entriesNames[k].length() != (size_t) (offset + 17) && k < entriesNames.size()){
|
||||
if(credits == false){
|
||||
if(strcasecmp(entriesNames[k].substr(offset + 16, 7).c_str(), "/cheats") == 0){
|
||||
tempChildren.push_back(entriesNames[k]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
tempChildren.push_back(entriesNames[k]);
|
||||
}
|
||||
k++;
|
||||
}
|
||||
children.push_back(tempChildren);
|
||||
tempChildren.clear ();
|
||||
}
|
||||
else{
|
||||
k++;
|
||||
}
|
||||
}
|
||||
brls::Logger::debug("Titles size:");
|
||||
std::cout << titles.size() << std::endl;
|
||||
|
||||
brls::Logger::debug("Parents size:");
|
||||
std::cout << parents.size() << std::endl;
|
||||
|
||||
size_t lastL = 0;
|
||||
std::string name;
|
||||
std::string id;
|
||||
ProgressEvent::instance().setTotalSteps(titles.size());
|
||||
for(size_t j = 0; j < titles.size(); j++){
|
||||
for(size_t l = lastL; l < parents.size(); l++){
|
||||
if(strcasecmp((titles.at(j).id).c_str(), parents[l].substr(offset, 16).c_str()) == 0){
|
||||
unzipper.extractEntry(parents[l]);
|
||||
for(auto& e : children[l]){
|
||||
unzipper.extractEntry(e);
|
||||
extractedTitles.insert(id);
|
||||
ProgressEvent::instance().setStep(j);
|
||||
name = "No name retrieved.";
|
||||
id = e.substr(offset, 16);
|
||||
std::transform(id.begin(), id.end(), id.begin(), ::toupper);
|
||||
}
|
||||
lastL = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
unzipper.close();
|
||||
writeTitlesToFile(extractedTitles);
|
||||
ProgressEvent::instance().setStep(ProgressEvent::instance().getMax());
|
||||
}
|
||||
|
||||
void writeTitlesToFile(std::set<std::string> titles){
|
||||
std::ofstream updatedTitlesFile;
|
||||
std::set<std::string>::iterator it = titles.begin();
|
||||
updatedTitlesFile.open(UPDATED_TITLES_PATH, std::ofstream::out | std::ofstream::trunc);
|
||||
if(updatedTitlesFile.is_open()) {
|
||||
while (it != titles.end()){
|
||||
updatedTitlesFile << (*it) << std::endl;
|
||||
it++;
|
||||
}
|
||||
updatedTitlesFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void removeCheats(CFW cfw){
|
||||
ProgressEvent::instance().reset();
|
||||
ProgressEvent::instance().setStep(1);
|
||||
std::string path;
|
||||
switch(cfw){
|
||||
case ams:
|
||||
path = std::string(AMS_PATH) + std::string(CONTENTS_PATH);
|
||||
break;
|
||||
case rnx:
|
||||
path = std::string(REINX_PATH) + std::string(CONTENTS_PATH);
|
||||
break;
|
||||
case sxos:
|
||||
path = std::string(SXOS_PATH) + std::string(TITLES_PATH);
|
||||
break;
|
||||
}
|
||||
for (const auto & entry : std::filesystem::directory_iterator(path)){
|
||||
std::string cheatsPath = entry.path().string() + "/cheats";
|
||||
if(std::filesystem::exists(cheatsPath)){
|
||||
for (const auto & cheat : std::filesystem::directory_iterator(cheatsPath)){
|
||||
std::filesystem::remove(cheat);
|
||||
}
|
||||
rmdir(cheatsPath.c_str());
|
||||
if(std::filesystem::is_empty(entry)){
|
||||
rmdir(entry.path().string().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::filesystem::remove(UPDATED_TITLES_PATH);
|
||||
ProgressEvent::instance().setStep(ProgressEvent::instance().getMax());
|
||||
}
|
105
source/list_download_tab.cpp
Normal file
105
source/list_download_tab.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "list_download_tab.hpp"
|
||||
|
||||
ListDownloadTab::ListDownloadTab(archiveType type) :
|
||||
brls::List()
|
||||
{
|
||||
std::string operation = "Getting ";
|
||||
this->description = new brls::Label(brls::LabelStyle::DESCRIPTION, "", true);
|
||||
switch(type){
|
||||
case sigpatches:
|
||||
links = fetchLinks(SIGPATCHES_URL);
|
||||
operation += "sigpatches";
|
||||
this->description->setText(
|
||||
"\uE016 Sigpatches allow your Switch to install and run unofficial NSP file. " \
|
||||
"Make sure you pick the correct sigpatches for your setup (pure Atmosphere or Hekate+Atmosphere)."
|
||||
);
|
||||
break;
|
||||
case fw:
|
||||
links = fetchLinks(FIRMWARE_URL);
|
||||
operation += "firmware";
|
||||
this->description->setText(
|
||||
"\uE016 Here are firmware dumps from \"https://darthsternie.net/switch-firmwares/\". "\
|
||||
"Once downloaded, it will be extracted in \"/firmware\". You can then install the update though Daybreak or ChoiDuJour."
|
||||
);
|
||||
break;
|
||||
case app:
|
||||
std::get<0>(links).push_back("Latest version");
|
||||
std::get<1>(links).push_back(APP_URL);
|
||||
operation += "app";
|
||||
break;
|
||||
case cfw:
|
||||
links = fetchLinks(CFW_URL);
|
||||
operation += "CFW";
|
||||
this->description->setText(
|
||||
"\uE016 Here is a list of the main Switch CFWs. If you want to use Atmosphere + Hekate, download Atmosphere and then Hekate."
|
||||
);
|
||||
break;
|
||||
case cheats:
|
||||
std::string cheatsVer = fetchTitle(CHEATS_RELEASE_URL);
|
||||
if(cheatsVer != "-1"){
|
||||
std::get<0>(links).push_back("Latest (ver " + cheatsVer + ")");
|
||||
switch(getCFW()){
|
||||
case sxos:
|
||||
std::get<1>(links).push_back(CHEATS_URL_TITLES);
|
||||
break;
|
||||
case ams:
|
||||
std::get<1>(links).push_back(CHEATS_URL_CONTENTS);
|
||||
break;
|
||||
case rnx:
|
||||
std::get<1>(links).push_back(CHEATS_URL_CONTENTS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
operation += "cheats";
|
||||
this->description->setText(
|
||||
"\uE016 This will downloaded a daily updated archive of cheat code from gbatemp.net. "\
|
||||
"Cheat codes for games you don't have installed won't be extracted to your SD card. "\
|
||||
"Title IDs listed in '" + std::string(CHEATS_EXCLUDE) + "' won't recieve cheat updates."
|
||||
);
|
||||
break;
|
||||
}
|
||||
this->addView(description);
|
||||
|
||||
int nbLinks = std::get<0>(links).size();
|
||||
if(nbLinks){
|
||||
linkItems.reserve(nbLinks);
|
||||
for (int i = 0; i<nbLinks; i++){
|
||||
std::string url = std::get<1>(links)[i];
|
||||
std::string text("Downloading:\n" + std::get<0>(links)[i] + "\n\nFrom:\n" + url);
|
||||
linkItems[i] = new brls::ListItem(std::get<0>(links)[i]);
|
||||
linkItems[i]->getClickEvent()->subscribe([&, text, url, type, operation](brls::View* view) {
|
||||
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle(operation);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, text)
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Downloading...", [url, type](){downloadArchive(url, type);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Extracting...", [type](){extractArchive(type);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done!", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
this->addView(linkItems[i]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
notFound = new brls::Label(
|
||||
brls::LabelStyle::DESCRIPTION,
|
||||
"Could not find a download link, make sure the Switch has access to the internet.\n"\
|
||||
"If this problem persists, please open an issue on Github",
|
||||
true
|
||||
);
|
||||
notFound->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->addView(notFound);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ListDownloadTab::~ListDownloadTab(){
|
||||
|
||||
}
|
58
source/main.cpp
Normal file
58
source/main.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
|
||||
#include <borealis.hpp>
|
||||
|
||||
#include "main_frame.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Init the app
|
||||
if (!brls::Application::init(APP_TITLE))
|
||||
{
|
||||
brls::Logger::error("Unable to init Borealis application");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Setup verbose logging on PC
|
||||
#ifndef __SWITCH__
|
||||
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
|
||||
#endif
|
||||
|
||||
// Initialize services with a PC shim
|
||||
nsInitialize();
|
||||
socketInitializeDefault();
|
||||
nxlinkStdio();
|
||||
|
||||
std::cout << R_SUCCEEDED(splInitialize()) << std::endl;
|
||||
createTree(CONFIG_PATH);
|
||||
|
||||
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
|
||||
brls::Logger::debug("Start");
|
||||
|
||||
// Create root view
|
||||
MainFrame *mainFrame = new MainFrame();
|
||||
|
||||
// Add the root view to the stack
|
||||
brls::Application::pushView(mainFrame);
|
||||
|
||||
// Run the app
|
||||
while (brls::Application::mainLoop());
|
||||
|
||||
// Exit
|
||||
splExit();
|
||||
socketExit();
|
||||
nsExit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
22
source/main_frame.cpp
Normal file
22
source/main_frame.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "main_frame.hpp"
|
||||
|
||||
MainFrame::MainFrame() : TabFrame()
|
||||
{
|
||||
|
||||
//this->setIcon(new Logo(LogoStyle::HEADER));
|
||||
brls::Logger::debug("MainFrame");
|
||||
setTitle(std::string(APP_TITLE) + " v" + std::string(APP_VERSION));
|
||||
this->addTab("About", new AboutTab());
|
||||
|
||||
this->addSeparator();
|
||||
|
||||
this->addTab("Update CFW", new ListDownloadTab(cfw));
|
||||
this->addTab("Update sigpatches", new ListDownloadTab(sigpatches));
|
||||
this->addTab("Download firmwares", new ListDownloadTab(fw));
|
||||
this->addTab("Download cheats", new ListDownloadTab(cheats));
|
||||
|
||||
|
||||
this->addSeparator();
|
||||
|
||||
this->addTab("Tools", new ToolsTab());
|
||||
}
|
43
source/payload_page.cpp
Normal file
43
source/payload_page.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "payload_page.hpp"
|
||||
|
||||
PayloadPage::PayloadPage() : AppletFrame(true, true)
|
||||
{
|
||||
|
||||
this->setTitle("Reboot menu");
|
||||
list = new brls::List();
|
||||
label = new brls::Label(
|
||||
brls::LabelStyle::DESCRIPTION,
|
||||
"Select a payload to reboot to it.",
|
||||
true
|
||||
);
|
||||
list->addView(label);
|
||||
std::vector<std::string> payloads = fetchPayloads();
|
||||
int nbPayloads = payloads.size();
|
||||
items.reserve(nbPayloads);
|
||||
for (int i = 0; i < nbPayloads; i++){
|
||||
std::string payload = payloads[i];
|
||||
items[i] = new brls::ListItem(payload);
|
||||
items[i]->getClickEvent()->subscribe([&, payload](brls::View* view) {
|
||||
reboot_to_payload(payload.c_str());
|
||||
brls::Application::popView();
|
||||
});
|
||||
list->addView(items[i]);
|
||||
}
|
||||
list->addView(new brls::ListItemGroupSpacing(true));
|
||||
|
||||
shutDown = new brls::ListItem("Shut Down");
|
||||
shutDown->getClickEvent()->subscribe([](brls::View* view) {
|
||||
shut_down(false);
|
||||
brls::Application::popView();
|
||||
});
|
||||
list->addView(shutDown);
|
||||
|
||||
reboot = new brls::ListItem("Reboot");
|
||||
reboot->getClickEvent()->subscribe([](brls::View* view) {
|
||||
shut_down(true);
|
||||
brls::Application::popView();
|
||||
});
|
||||
list->addView(reboot);
|
||||
this->setContentView(list);
|
||||
|
||||
}
|
77
source/reboot_payload.c
Normal file
77
source/reboot_payload.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "reboot_payload.h"
|
||||
#include "constants.hpp"
|
||||
|
||||
#define IRAM_PAYLOAD_MAX_SIZE 0x2F000
|
||||
#define IRAM_PAYLOAD_BASE 0x40010000
|
||||
|
||||
static alignas(0x1000) u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE];
|
||||
static alignas(0x1000) u8 g_ff_page[0x1000];
|
||||
static alignas(0x1000) u8 g_work_page[0x1000];
|
||||
|
||||
|
||||
void do_iram_dram_copy(void *buf, uintptr_t iram_addr, size_t size, int option) {
|
||||
memcpy(g_work_page, buf, size);
|
||||
|
||||
SecmonArgs args = {0};
|
||||
args.X[0] = 0xF0000201; /* smcAmsIramCopy */
|
||||
args.X[1] = (uintptr_t)g_work_page; /* DRAM Address */
|
||||
args.X[2] = iram_addr; /* IRAM Address */
|
||||
args.X[3] = size; /* Copy size */
|
||||
args.X[4] = option; /* 0 = Read, 1 = Write */
|
||||
svcCallSecureMonitor(&args);
|
||||
|
||||
memcpy(buf, g_work_page, size);
|
||||
}
|
||||
|
||||
void copy_to_iram(uintptr_t iram_addr, void *buf, size_t size) {
|
||||
do_iram_dram_copy(buf, iram_addr, size, 1);
|
||||
}
|
||||
|
||||
void copy_from_iram(void *buf, uintptr_t iram_addr, size_t size) {
|
||||
do_iram_dram_copy(buf, iram_addr, size, 0);
|
||||
}
|
||||
|
||||
static void clear_iram(void) {
|
||||
memset(g_ff_page, 0xFF, sizeof(g_ff_page));
|
||||
for (size_t i = 0; i < IRAM_PAYLOAD_MAX_SIZE; i += sizeof(g_ff_page)) {
|
||||
copy_to_iram(IRAM_PAYLOAD_BASE + i, g_ff_page, sizeof(g_ff_page));
|
||||
}
|
||||
}
|
||||
|
||||
static void inject_payload(void) {
|
||||
clear_iram();
|
||||
|
||||
for (size_t i = 0; i < IRAM_PAYLOAD_MAX_SIZE; i += 0x1000) {
|
||||
copy_to_iram(IRAM_PAYLOAD_BASE + i, &g_reboot_payload[i], 0x1000);
|
||||
}
|
||||
|
||||
splSetConfig((SplConfigItem)65001, 2);
|
||||
}
|
||||
|
||||
int reboot_to_payload(const char* path){
|
||||
bool can_reboot = true;
|
||||
//Result rc = splInitialize();
|
||||
/* if(0){
|
||||
//if (R_FAILED(rc)) {
|
||||
can_reboot = false;
|
||||
printf("Can't reboot");
|
||||
} */
|
||||
const char* lol = "lol";
|
||||
printf(lol);
|
||||
printf(path);
|
||||
FILE *f;
|
||||
f = fopen(path, "rb");
|
||||
if (f == NULL) can_reboot = false;
|
||||
else {
|
||||
printf("Can't open payload");
|
||||
fread(g_reboot_payload, 1, sizeof(g_reboot_payload), f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (can_reboot) {
|
||||
printf("injecting payload");
|
||||
inject_payload();
|
||||
}
|
||||
if (can_reboot) splExit();
|
||||
return 0;
|
||||
}
|
61
source/tools_tab.cpp
Normal file
61
source/tools_tab.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include "tools_tab.hpp"
|
||||
|
||||
ToolsTab::ToolsTab() : brls::List()
|
||||
{
|
||||
this->label = new brls::Label(brls::LabelStyle::DESCRIPTION, "\uE016 Miscellaneous tools", true);
|
||||
this->addView(label);
|
||||
|
||||
viewCheats = new brls::ListItem("View installed cheats");
|
||||
viewCheats->getClickEvent()->subscribe([&](brls::View* view){
|
||||
brls::Application::pushView(new AppPage());
|
||||
});
|
||||
this->addView(viewCheats);
|
||||
|
||||
deleteCheats = new brls::ListItem("Delete all existing cheat codes");
|
||||
deleteCheats->getClickEvent()->subscribe([&](brls::View* view){
|
||||
stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle("Delete all cheats");
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Deleting", [](){removeCheats(getCFW());})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done!", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
this->addView(deleteCheats);
|
||||
|
||||
JCcolor = new brls::ListItem("Change the Joy-Cons color");
|
||||
JCcolor->getClickEvent()->subscribe([&](brls::View* view){
|
||||
brls::Application::pushView(new JCPage());
|
||||
});
|
||||
this->addView(JCcolor);
|
||||
|
||||
updateApp = new brls::ListItem("Update the app");
|
||||
std::string text("Downloading:\nAIO-switch-updater\n\nFrom:\n" + std::string(APP_URL));
|
||||
updateApp->getClickEvent()->subscribe([&, text](brls::View* view) {
|
||||
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
|
||||
stagedFrame->setTitle("Updating app");
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, text)
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Downloading...", [](){downloadArchive(APP_URL, app);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new WorkerPage(stagedFrame, "Extracting....", [](){extractArchive(app);})
|
||||
);
|
||||
stagedFrame->addStage(
|
||||
new ConfirmPage(stagedFrame, "All done!", true)
|
||||
);
|
||||
brls::Application::pushView(stagedFrame);
|
||||
});
|
||||
this->addView(updateApp);
|
||||
|
||||
rebootPayload = new brls::ListItem("Shut down / Inject payload");
|
||||
rebootPayload->getClickEvent()->subscribe([&](brls::View* view){
|
||||
brls::Application::pushView(new PayloadPage());
|
||||
});
|
||||
this->addView(rebootPayload);
|
||||
|
||||
}
|
167
source/utils.cpp
Normal file
167
source/utils.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
#include "utils.hpp"
|
||||
|
||||
bool isServiceRunning(const char *serviceName) {
|
||||
Handle handle;
|
||||
SmServiceName service_name = smEncodeName(serviceName);
|
||||
bool running = R_FAILED(smRegisterService(&handle, service_name, false, 1));
|
||||
svcCloseHandle(handle);
|
||||
if (!running)
|
||||
smUnregisterService(service_name);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
CFW getCFW(){
|
||||
if(isServiceRunning("rnx")) return rnx;
|
||||
else if(isServiceRunning("tx")) return sxos;
|
||||
else return ams;
|
||||
}
|
||||
|
||||
std::vector<std::string> htmlProcess(std::string s, std::regex rgx){
|
||||
//std::vector<std::regex> rgx = {std::regex(">(?:(?!>).)*?</a>"), std::regex(">(?:(?!>).)*?</a>")};
|
||||
std::vector<std::string> res;
|
||||
std::smatch match;
|
||||
while (std::regex_search(s, match, rgx)){
|
||||
res.push_back(match[0].str());
|
||||
s = match.suffix();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void createTree(std::string path){
|
||||
std::string delimiter = "/";
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
std::string directories("");
|
||||
while ((pos = path.find(delimiter)) != std::string::npos) {
|
||||
token = path.substr(0, pos);
|
||||
directories += token + "/";
|
||||
std::filesystem::create_directory(directories);
|
||||
path.erase(0, pos + delimiter.length());
|
||||
}
|
||||
}
|
||||
|
||||
bool isArchive(const char * path){
|
||||
std::fstream file;
|
||||
std::string fileContent;
|
||||
if(std::filesystem::exists(path)){
|
||||
file.open(path, std::fstream::in);
|
||||
file >> fileContent;
|
||||
file.close();
|
||||
}
|
||||
return fileContent.find("DOCTYPE") == std::string::npos;
|
||||
}
|
||||
|
||||
void downloadArchive(std::string url, archiveType type){
|
||||
createTree(DOWNLOAD_PATH);
|
||||
switch(type){
|
||||
case sigpatches:
|
||||
downloadFile(url.c_str(), SIGPATCHES_FILENAME, OFF);
|
||||
break;
|
||||
case cheats:
|
||||
downloadFile(url.c_str(), CHEATS_FILENAME, OFF);
|
||||
break;
|
||||
case fw:
|
||||
downloadFile(url.c_str(), FIRMWARE_FILENAME, OFF);
|
||||
break;
|
||||
case app:
|
||||
downloadFile(url.c_str(), APP_FILENAME, OFF);
|
||||
break;
|
||||
case cfw:
|
||||
downloadFile(url.c_str(), CFW_FILENAME, OFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void extractArchive(archiveType type){
|
||||
std::vector<Title> titles;
|
||||
switch(type){
|
||||
case sigpatches:
|
||||
if(isArchive(SIGPATCHES_FILENAME)) extract(SIGPATCHES_FILENAME);
|
||||
break;
|
||||
case cheats:
|
||||
titles = getInstalledTitlesNs();
|
||||
titles = excludeTitles((std::string(DOWNLOAD_PATH) + "exclude.txt").c_str(), titles);
|
||||
extractCheats(CHEATS_FILENAME, titles, getCFW());
|
||||
break;
|
||||
case fw:
|
||||
createTree(FIRMWARE_PATH);
|
||||
extract(FIRMWARE_FILENAME, FIRMWARE_PATH);
|
||||
break;
|
||||
case app:
|
||||
extract(APP_FILENAME);
|
||||
break;
|
||||
case cfw:
|
||||
extract(CFW_FILENAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void progressTest(std::string url, archiveType type){
|
||||
ProgressEvent::instance().reset();
|
||||
ProgressEvent::instance().setTotalSteps(2);
|
||||
ProgressEvent::instance().setStep(1);
|
||||
ProgressEvent::instance().setStep(2);
|
||||
}
|
||||
|
||||
std::string formatListItemTitle(const std::string str, size_t maxScore) {
|
||||
size_t score = 0;
|
||||
for (size_t i = 0; i < str.length(); i++)
|
||||
{
|
||||
score += std::isupper(str[i]) ? 4 : 3;
|
||||
if(score > maxScore)
|
||||
{
|
||||
return str.substr(0, i-1) + "\u2026";
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string formatApplicationId(u64 ApplicationId){
|
||||
std::stringstream strm;
|
||||
strm << std::uppercase << std::setfill('0') << std::setw(16) << std::hex << ApplicationId;
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
std::set<std::string> readLineByLine(const char * path){
|
||||
std::set<std::string> titles;
|
||||
std::string str;
|
||||
std::ifstream in(path);
|
||||
if(in){
|
||||
while (std::getline(in, str))
|
||||
{
|
||||
// Line contains string of length > 0 then save it in vector
|
||||
if(str.size() > 0)
|
||||
titles.insert(str);
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
return titles;
|
||||
}
|
||||
|
||||
std::vector<std::string> fetchPayloads(){
|
||||
std::vector<std::string> payloadPaths;
|
||||
payloadPaths.push_back(ROOT_PATH);
|
||||
if(std::filesystem::exists(PAYLOAD_PATH)) payloadPaths.push_back(PAYLOAD_PATH);
|
||||
if(std::filesystem::exists(AMS_PATH)) payloadPaths.push_back(AMS_PATH);
|
||||
if(std::filesystem::exists(REINX_PATH)) payloadPaths.push_back(REINX_PATH);
|
||||
if(std::filesystem::exists(BOOTLOADER_PL_PATH)) payloadPaths.push_back(BOOTLOADER_PL_PATH);
|
||||
if(std::filesystem::exists(SXOS_PATH)) payloadPaths.push_back(SXOS_PATH);
|
||||
std::vector<std::string> res;
|
||||
for (auto& path : payloadPaths){
|
||||
for (const auto & entry : std::filesystem::directory_iterator(path)){
|
||||
if(entry.path().extension().string() == ".bin")
|
||||
res.push_back(entry.path().string().c_str());
|
||||
}
|
||||
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void shut_down(bool reboot){
|
||||
bpcInitialize();
|
||||
if(reboot) bpcRebootSystem();
|
||||
else bpcShutdownSystem();
|
||||
bpcExit();
|
||||
}
|
78
source/worker_page.cpp
Normal file
78
source/worker_page.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "worker_page.hpp"
|
||||
|
||||
WorkerPage::WorkerPage(brls::StagedAppletFrame* frame, const std::string& text, worker_func_t worker_func): frame(frame), workerFunc(worker_func)
|
||||
{
|
||||
this->progressDisp = new brls::ProgressDisplay();
|
||||
this->progressDisp->setParent(this);
|
||||
|
||||
this->label = new brls::Label(brls::LabelStyle::DIALOG, text, true);
|
||||
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->label->setParent(this);
|
||||
|
||||
this->button = new brls::Button(brls::ButtonStyle::BORDERLESS); // avoid back button bug
|
||||
this->button->setParent(this);
|
||||
appletSetMediaPlaybackState(true);
|
||||
}
|
||||
|
||||
void WorkerPage::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
|
||||
{
|
||||
if (!this->workStarted)
|
||||
{
|
||||
this->workStarted = true;
|
||||
ProgressEvent::instance().reset();
|
||||
workerThread = new std::thread(&WorkerPage::doWork, this);
|
||||
}
|
||||
else if (ProgressEvent::instance().finished())
|
||||
{
|
||||
brls::Logger::debug("Worker done");
|
||||
appletSetMediaPlaybackState(false);
|
||||
frame->nextStage();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->progressDisp->setProgress(ProgressEvent::instance().getStep(), ProgressEvent::instance().getMax());
|
||||
this->progressDisp->frame(ctx);
|
||||
this->label->frame(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerPage::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
|
||||
{
|
||||
this->label->setWidth(roundf((float)this->width * style->CrashFrame.labelWidth));
|
||||
|
||||
this->label->setBoundaries(
|
||||
this->x + this->width / 2 - this->label->getWidth() / 2,
|
||||
this->y + (this->height - style->AppletFrame.footerHeight) / 2,
|
||||
this->label->getWidth(),
|
||||
this->label->getHeight());
|
||||
|
||||
this->progressDisp->setBoundaries(
|
||||
this->x + this->width / 2 - style->CrashFrame.buttonWidth,
|
||||
this->y + this->height / 2,
|
||||
style->CrashFrame.buttonWidth * 2,
|
||||
style->CrashFrame.buttonHeight);
|
||||
}
|
||||
|
||||
void WorkerPage::doWork()
|
||||
{
|
||||
if (this->workerFunc)
|
||||
this->workerFunc();
|
||||
}
|
||||
|
||||
brls::View* WorkerPage::getDefaultFocus()
|
||||
{
|
||||
return this->button;
|
||||
}
|
||||
|
||||
WorkerPage::~WorkerPage()
|
||||
{
|
||||
if (this->workStarted && workerThread->joinable())
|
||||
{
|
||||
this->workerThread->join();
|
||||
if (this->workerThread)
|
||||
delete this->workerThread;
|
||||
}
|
||||
delete this->progressDisp;
|
||||
delete this->label;
|
||||
delete this->button;
|
||||
}
|
Loading…
Add table
Reference in a new issue