All checks were successful
Build NRO / build (push) Successful in 1m48s
Replace the console UI with a Borealis-based flow, bundle ROMFS assets and borealis as a submodule, and apply small upstream patches at build time. Self-delete runs after romfsExit on quit so the NRO can be removed like the old console build.
135 lines
3.1 KiB
C++
135 lines
3.1 KiB
C++
#include "extractor.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef __SWITCH__
|
|
#include <switch.h>
|
|
#endif
|
|
|
|
static constexpr size_t READ_BUF_SIZE = 8192;
|
|
|
|
int PatchExtractor::mkdirs(const char* path) {
|
|
char tmp[512];
|
|
snprintf(tmp, sizeof(tmp), "%s", path);
|
|
size_t len = strlen(tmp);
|
|
if (len == 0) return 0;
|
|
if (tmp[len - 1] == '/') tmp[len - 1] = '\0';
|
|
|
|
for (char* p = tmp + 1; *p; p++) {
|
|
if (*p == '/') {
|
|
*p = '\0';
|
|
mkdir(tmp, 0755);
|
|
*p = '/';
|
|
}
|
|
}
|
|
return mkdir(tmp, 0755);
|
|
}
|
|
|
|
bool PatchExtractor::open() {
|
|
zip = unzOpen(PATCHES_ZIP);
|
|
if (!zip) return false;
|
|
|
|
unz_global_info gi{};
|
|
if (unzGetGlobalInfo(zip, &gi) != UNZ_OK) {
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
total = gi.number_entry;
|
|
extracted = 0;
|
|
skipped = 0;
|
|
finished = false;
|
|
err = unzGoToFirstFile(zip);
|
|
return true;
|
|
}
|
|
|
|
void PatchExtractor::close() {
|
|
if (zip) {
|
|
unzClose(zip);
|
|
zip = nullptr;
|
|
}
|
|
}
|
|
|
|
int PatchExtractor::getProgressPercent() const {
|
|
if (total == 0) return 100;
|
|
return static_cast<int>((extracted * 100) / total);
|
|
}
|
|
|
|
bool PatchExtractor::step() {
|
|
if (!zip || finished) return false;
|
|
|
|
if (err != UNZ_OK) {
|
|
finished = true;
|
|
return false;
|
|
}
|
|
|
|
char filename[512];
|
|
char fullpath[1024];
|
|
unz_file_info fi{};
|
|
|
|
unzGetCurrentFileInfo(zip, &fi, filename, sizeof(filename), nullptr, 0, nullptr, 0);
|
|
snprintf(fullpath, sizeof(fullpath), "%s%s", EXTRACT_DIR, filename);
|
|
currentFile = filename;
|
|
|
|
size_t flen = strlen(filename);
|
|
if (flen > 0 && filename[flen - 1] == '/') {
|
|
mkdirs(fullpath);
|
|
} else {
|
|
char dirpart[1024];
|
|
snprintf(dirpart, sizeof(dirpart), "%s", fullpath);
|
|
char* last_slash = strrchr(dirpart, '/');
|
|
if (last_slash) {
|
|
*last_slash = '\0';
|
|
mkdirs(dirpart);
|
|
}
|
|
|
|
if (unzOpenCurrentFile(zip) == UNZ_OK) {
|
|
FILE* out = fopen(fullpath, "wb");
|
|
if (out) {
|
|
unsigned char buf[READ_BUF_SIZE];
|
|
int bytes;
|
|
while ((bytes = unzReadCurrentFile(zip, buf, READ_BUF_SIZE)) > 0) {
|
|
fwrite(buf, 1, static_cast<size_t>(bytes), out);
|
|
}
|
|
fclose(out);
|
|
} else {
|
|
skipped++;
|
|
}
|
|
unzCloseCurrentFile(zip);
|
|
} else {
|
|
skipped++;
|
|
}
|
|
}
|
|
|
|
extracted++;
|
|
err = unzGoToNextFile(zip);
|
|
if (err != UNZ_OK) finished = true;
|
|
|
|
return !finished;
|
|
}
|
|
|
|
bool PatchExtractor::cleanup() {
|
|
cleanupOk_ = true;
|
|
|
|
if (remove(PATCHES_ZIP) != 0)
|
|
cleanupOk_ = false;
|
|
|
|
// Cannot delete our own NRO while the app is still running; try anyway for edge cases.
|
|
remove(SELF_NRO);
|
|
|
|
remove("sdmc:/switch/.PatchExtractor.nro.star");
|
|
remove("sdmc:/switch/.packages/boot_package.ini");
|
|
|
|
return cleanupOk_;
|
|
}
|
|
|
|
void PatchExtractor::tryDeleteSelfOnExit() {
|
|
#ifdef __SWITCH__
|
|
// Embedded ROMFS keeps the .nro open on SD; unmount before unlink (console build had no ROMFS).
|
|
romfsExit();
|
|
#endif
|
|
remove(SELF_NRO);
|
|
}
|