mirror of
https://github.com/rvtr/unlaunch-installer_dev.git
synced 2026-01-26 13:43:08 -05:00
Add sanitization to loaded GIF images
Parse the provided loaded background images and reconstruct a new GIF file on the fly to ensure they don't contain any extension that might not be supported by unalunch. The generated GIF file will have a global color table, and single section, being the image descriptor. It also won't have any looping block.
This commit is contained in:
parent
5bfa4b1a00
commit
6edcde25c3
@ -1,13 +1,19 @@
|
|||||||
#include "bgMenu.h"
|
#include "bgMenu.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "gifConverter.h"
|
||||||
|
#include "message.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <format>
|
||||||
#include <nds.h>
|
#include <nds.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
static std::array<uint8_t, MAX_GIF_SIZE> currentlyLoadedGif;
|
||||||
|
|
||||||
static const auto& getBackgroundList()
|
static const auto& getBackgroundList()
|
||||||
{
|
{
|
||||||
static auto bgs = []{
|
static auto bgs = []{
|
||||||
@ -39,7 +45,7 @@ static const auto& getBackgroundList()
|
|||||||
return bgs;
|
return bgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* backgroundMenu()
|
std::span<uint8_t> backgroundMenu()
|
||||||
{
|
{
|
||||||
//top screen
|
//top screen
|
||||||
clearScreen(&topScreen);
|
clearScreen(&topScreen);
|
||||||
@ -79,12 +85,17 @@ const char* backgroundMenu()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* result = nullptr;
|
auto selection = static_cast<size_t>(m->cursor);
|
||||||
if(static_cast<size_t>(m->cursor) < bgs.size())
|
|
||||||
result = bgs[m->cursor].second.data();
|
|
||||||
else if(static_cast<size_t>(m->cursor) == bgs.size())
|
|
||||||
result = "default";
|
|
||||||
freeMenu(m);
|
freeMenu(m);
|
||||||
|
|
||||||
return result;
|
if(selection < bgs.size()) {
|
||||||
|
try {
|
||||||
|
return parseGif(bgs[selection].second.data(), currentlyLoadedGif);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
messageBox(std::format("\x1B[31mError:\x1B[33m The image could not\n"
|
||||||
|
"be loaded: {}", e.what()).data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
@ -1,14 +1,9 @@
|
|||||||
#ifndef BGMENU_H
|
#ifndef BGMENU_H
|
||||||
#define BGMENU_H
|
#define BGMENU_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <cstdint>
|
||||||
extern "C" {
|
#include <span>
|
||||||
#endif
|
|
||||||
|
|
||||||
const char* backgroundMenu();
|
std::span<uint8_t> backgroundMenu();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
225
arm9/src/gifConverter.cpp
Normal file
225
arm9/src/gifConverter.cpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "storage.h"
|
||||||
|
#include "unlaunch.h"
|
||||||
|
|
||||||
|
struct Gif {
|
||||||
|
struct Header {
|
||||||
|
char signature[6];
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint8_t gctSize: 3;
|
||||||
|
uint8_t sortFlag: 1;
|
||||||
|
uint8_t colorResolution: 3;
|
||||||
|
uint8_t gctFlag: 1;
|
||||||
|
uint8_t bgColor;
|
||||||
|
uint8_t pixelAspectRatio;
|
||||||
|
} __attribute__ ((__packed__)) header;
|
||||||
|
static_assert(sizeof(Header) == 13);
|
||||||
|
|
||||||
|
std::array<uint8_t, 255 * 3> colorTable;
|
||||||
|
size_t numColors;
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
struct Descriptor {
|
||||||
|
uint16_t x;
|
||||||
|
uint16_t y;
|
||||||
|
uint16_t w;
|
||||||
|
uint16_t h;
|
||||||
|
uint8_t lctSize: 3;
|
||||||
|
uint8_t reserved: 2;
|
||||||
|
uint8_t sortFlag: 1;
|
||||||
|
uint8_t interlaceFlag: 1;
|
||||||
|
uint8_t lctFlag: 1;
|
||||||
|
} __attribute__ ((__packed__)) descriptor;
|
||||||
|
static_assert(sizeof(Descriptor) == 9);
|
||||||
|
|
||||||
|
struct Image {
|
||||||
|
uint8_t lzwMinimumCodeSize;
|
||||||
|
std::vector<std::vector<uint8_t>> imageDataChunks;
|
||||||
|
} image;
|
||||||
|
} frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto WIDTH = 256;
|
||||||
|
static constexpr auto HEIGHT = 192;
|
||||||
|
|
||||||
|
Gif getGif(std::string_view path) {
|
||||||
|
Gif gif;
|
||||||
|
auto file = fopen(path.data(), "rb");
|
||||||
|
if (!file)
|
||||||
|
throw std::runtime_error("Failed to open file");
|
||||||
|
|
||||||
|
auto fileSptr = std::shared_ptr<FILE>(file, fclose);
|
||||||
|
|
||||||
|
if(auto size = getFileSize(file); size < 7 || size > MAX_GIF_SIZE)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Gif file too big.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& header = gif.header;
|
||||||
|
|
||||||
|
// Read header
|
||||||
|
fread(&header, 1, sizeof(header), file);
|
||||||
|
|
||||||
|
// Check that this is a GIF
|
||||||
|
if (memcmp(header.signature, "GIF87a", sizeof(header.signature)) != 0 && memcmp(header.signature, "GIF89a", sizeof(header.signature)) != 0) {
|
||||||
|
throw std::runtime_error("File not a gif");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(header.width != WIDTH || header.height != HEIGHT) {
|
||||||
|
throw std::runtime_error("Invalid gif size");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& numColors = gif.numColors;
|
||||||
|
auto& color_table = gif.colorTable;
|
||||||
|
// Load global color table
|
||||||
|
if (header.gctFlag) {
|
||||||
|
numColors = (2 << header.gctSize);
|
||||||
|
fread(color_table.data(), 1, numColors * 3, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& frame = gif.frame;
|
||||||
|
bool gotImage = false;
|
||||||
|
while (1) {
|
||||||
|
switch (fgetc(file)) {
|
||||||
|
case 0x21: { // Extension
|
||||||
|
switch (fgetc(file)) {
|
||||||
|
case 0xF9: { // Graphics Control
|
||||||
|
uint8_t toRead = fgetc(file);
|
||||||
|
fseek(file, toRead, SEEK_CUR);
|
||||||
|
fgetc(file); // Terminator
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x01: { // Plain text
|
||||||
|
throw std::runtime_error("Plain text found");
|
||||||
|
#if 0
|
||||||
|
fseek(file, 12, SEEK_CUR);
|
||||||
|
while (uint8_t size = fgetc(file)) {
|
||||||
|
fseek(file, size, SEEK_CUR);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
case 0xFF: { // Application extension
|
||||||
|
throw std::runtime_error("Application extension found");
|
||||||
|
#if 0
|
||||||
|
if (fgetc(file) == 0xB) {
|
||||||
|
char buffer[0xC] = {0};
|
||||||
|
fread(buffer, 1, 0xB, file);
|
||||||
|
if (strcmp(buffer, "NETSCAPE2.0") == 0) { // Check for Netscape loop count
|
||||||
|
fseek(file, 2, SEEK_CUR);
|
||||||
|
fseek(file, 2, SEEK_CUR);
|
||||||
|
fgetc(file); //terminator
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
case 0xFE: { // Comment
|
||||||
|
// Skip comments and unsupported application extionsions
|
||||||
|
while (uint8_t size = fgetc(file)) {
|
||||||
|
fseek(file, size, SEEK_CUR);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw std::runtime_error("Unknown GIF extension found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} case 0x2C: { // Image descriptor
|
||||||
|
gotImage = true;
|
||||||
|
fread(&frame.descriptor, 1, sizeof(frame.descriptor), file);
|
||||||
|
if (frame.descriptor.lctFlag) {
|
||||||
|
header.gctFlag = 1;
|
||||||
|
header.gctSize = frame.descriptor.lctSize;
|
||||||
|
header.sortFlag = frame.descriptor.sortFlag;
|
||||||
|
numColors = 2 << header.gctSize;
|
||||||
|
fread(color_table.data(), 1, numColors * 3, file);
|
||||||
|
frame.descriptor.lctFlag = 0;
|
||||||
|
frame.descriptor.lctSize = 0;
|
||||||
|
frame.descriptor.sortFlag = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame.descriptor.w != WIDTH || frame.descriptor.h != HEIGHT) {
|
||||||
|
throw std::runtime_error("Wrong frame size");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame.descriptor.x != 0 || frame.descriptor.y != 0) {
|
||||||
|
throw std::runtime_error("Wrong frame coordinates");
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.image.lzwMinimumCodeSize = fgetc(file);
|
||||||
|
while (uint8_t size = fgetc(file)) {
|
||||||
|
std::vector<uint8_t> dataChunk;
|
||||||
|
dataChunk.resize(size);
|
||||||
|
fread(dataChunk.data(), 1, size, file);
|
||||||
|
frame.image.imageDataChunks.push_back(std::move(dataChunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
goto breakWhile;
|
||||||
|
} case 0x3B: { // Trailer
|
||||||
|
goto breakWhile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(feof(file)){
|
||||||
|
throw std::runtime_error("Unexpected file termination");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
breakWhile:
|
||||||
|
if(!gotImage){
|
||||||
|
throw std::runtime_error("Image data not found in gif");
|
||||||
|
}
|
||||||
|
if(!header.gctFlag) {
|
||||||
|
throw std::runtime_error("Invalid gif (missing color table)");
|
||||||
|
}
|
||||||
|
return gif;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<uint8_t> writeGif(const Gif& gif, std::array<uint8_t, MAX_GIF_SIZE>& outArr) {
|
||||||
|
size_t totalWritten = 0;
|
||||||
|
auto writeArr = [&, ptr = outArr.data()](const void* data, size_t len) mutable {
|
||||||
|
if((totalWritten + len) > MAX_GIF_SIZE)
|
||||||
|
throw std::runtime_error("Gif too big");
|
||||||
|
memcpy(ptr, data, len);
|
||||||
|
ptr += len;
|
||||||
|
totalWritten += len;
|
||||||
|
};
|
||||||
|
auto writeCh = [&](char ch) mutable {
|
||||||
|
writeArr(&ch, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
writeArr(&gif.header, sizeof(gif.header));
|
||||||
|
writeArr(gif.colorTable.data(), gif.numColors * 3);
|
||||||
|
|
||||||
|
// write single image descriptor
|
||||||
|
writeCh(0x2C);
|
||||||
|
writeArr(&gif.frame.descriptor, sizeof(gif.frame.descriptor));
|
||||||
|
|
||||||
|
// write image data
|
||||||
|
writeCh(gif.frame.image.lzwMinimumCodeSize);
|
||||||
|
for(const auto& chunk : gif.frame.image.imageDataChunks){
|
||||||
|
writeCh(chunk.size());
|
||||||
|
writeArr(chunk.data(), chunk.size());
|
||||||
|
}
|
||||||
|
writeCh('\0');
|
||||||
|
|
||||||
|
// write trailer
|
||||||
|
writeCh(0x3B);
|
||||||
|
return std::span{outArr.data(), totalWritten};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr) {
|
||||||
|
const auto gif = getGif(path);
|
||||||
|
return writeGif(gif, outArr);
|
||||||
|
}
|
||||||
12
arm9/src/gifConverter.h
Normal file
12
arm9/src/gifConverter.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef GIF_CONVERTER_H
|
||||||
|
#define GIF_CONVERTER_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "unlaunch.h"
|
||||||
|
|
||||||
|
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr);
|
||||||
|
|
||||||
|
#endif //GIF_CONVERTER_H
|
||||||
@ -26,7 +26,7 @@ static UNLAUNCH_VERSION foundUnlaunchInstallerVersion = INVALID;
|
|||||||
static bool disableAllPatches = false;
|
static bool disableAllPatches = false;
|
||||||
static bool enableSoundAndSplash = false;
|
static bool enableSoundAndSplash = false;
|
||||||
static const char* splashSoundBinaryPatchPath = NULL;
|
static const char* splashSoundBinaryPatchPath = NULL;
|
||||||
static const char* customBgPath = NULL;
|
static std::span<uint8_t> customBgSpan{};
|
||||||
volatile bool charging = false;
|
volatile bool charging = false;
|
||||||
volatile u8 batteryLevel = 0;
|
volatile u8 batteryLevel = 0;
|
||||||
static bool advancedOptionsUnlocked = false;
|
static bool advancedOptionsUnlocked = false;
|
||||||
@ -590,7 +590,7 @@ void install(consoleInfo& info) {
|
|||||||
}
|
}
|
||||||
if(installUnlaunch(info, disableAllPatches,
|
if(installUnlaunch(info, disableAllPatches,
|
||||||
enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL,
|
enableSoundAndSplash ? splashSoundBinaryPatchPath : NULL,
|
||||||
customBgPath))
|
customBgSpan))
|
||||||
{
|
{
|
||||||
messageBox("Install successful!\n");
|
messageBox("Install successful!\n");
|
||||||
info.tmdGood = false;
|
info.tmdGood = false;
|
||||||
@ -615,19 +615,7 @@ void customBg() {
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const char* customBg = backgroundMenu();
|
customBgSpan = backgroundMenu();
|
||||||
if(!customBg)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(strcmp(customBg, "default") == 0)
|
|
||||||
{
|
|
||||||
customBgPath = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
customBgPath = customBg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void doMainMenu(consoleInfo& info) {
|
void doMainMenu(consoleInfo& info) {
|
||||||
|
|||||||
@ -3,10 +3,12 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "tonccpy.h"
|
#include "tonccpy.h"
|
||||||
#include "unlaunch.h"
|
#include "unlaunch.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <nds/sha1.h>
|
#include <nds/sha1.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <format>
|
#include <format>
|
||||||
@ -425,38 +427,19 @@ static bool verifyUnlaunchInstaller(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool patchCustomBackground(const char* customBackgroundPath)
|
static bool patchCustomBackground(std::span<uint8_t> customBackground)
|
||||||
{
|
{
|
||||||
if(!customBackgroundPath)
|
auto size = customBackground.size();
|
||||||
|
if(size == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto bgGif = fopen(customBackgroundPath, "rb");
|
if(size < 7 || size > MAX_GIF_SIZE)
|
||||||
if(!bgGif)
|
|
||||||
{
|
{
|
||||||
messageBox("\x1B[31mError:\x1B[33m Failed to open custom bg gif.\n");
|
messageBox("\x1B[31mError:\x1B[33m Gif file too big.\n");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto size = getFileSize(bgGif);
|
|
||||||
if(size < 7 || size > 0x3C70)
|
|
||||||
{
|
|
||||||
messageBox("\x1B[31mError:\x1B[33m Invalid gif file.\n");
|
|
||||||
fclose(bgGif);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u16 gifWidth;
|
|
||||||
u16 gifHeight;
|
|
||||||
if((fseek(bgGif, 6, SEEK_SET) != 0) || (fread(&gifWidth, 1, sizeof(u16), bgGif) != sizeof(u16)) || (fread(&gifHeight, 1, sizeof(u16), bgGif) != sizeof(u16)))
|
|
||||||
{
|
|
||||||
messageBox("\x1B[31mError:\x1B[33m Failed to parse gif file.\n");
|
|
||||||
fclose(bgGif);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (gifWidth != 256 || gifHeight != 192) {
|
|
||||||
messageBox("\x1B[31mError:\x1B[33m Gif file has invalid dimensions.\n");
|
|
||||||
fclose(bgGif);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 gifSignatureStart = 0x38464947;
|
const u32 gifSignatureStart = 0x38464947;
|
||||||
const u32 gifSignatureEnd = 0x3B000044;
|
const u32 gifSignatureEnd = 0x3B000044;
|
||||||
|
|
||||||
@ -468,14 +451,11 @@ static bool patchCustomBackground(const char* customBackgroundPath)
|
|||||||
if(*gifStart != gifSignatureStart || *gifEnd != gifSignatureEnd)
|
if(*gifStart != gifSignatureStart || *gifEnd != gifSignatureEnd)
|
||||||
{
|
{
|
||||||
messageBox("\x1B[31mError:\x1B[33m Gif offsets not matching.\n");
|
messageBox("\x1B[31mError:\x1B[33m Gif offsets not matching.\n");
|
||||||
fclose(bgGif);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(bgGif, 0, SEEK_SET);
|
|
||||||
|
|
||||||
//read the whole file, could be less than 0x3C70, but unlaunch should then just ignore the leftover data
|
std::memcpy(gifStart, customBackground.data(), size);
|
||||||
fread(gifStart, 1, 0x3C70, bgGif);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -507,7 +487,7 @@ static bool applyBinaryPatch(const char* path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSoundBinaryPatchPath, std::span<uint8_t> customBackground)
|
||||||
{
|
{
|
||||||
tonccpy(unlaunchInstallerBuffer, ogUnlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
tonccpy(unlaunchInstallerBuffer, ogUnlaunchInstallerBuffer, sizeof(unlaunchInstallerBuffer));
|
||||||
if (splashSoundBinaryPatchPath)
|
if (splashSoundBinaryPatchPath)
|
||||||
@ -530,7 +510,7 @@ static bool patchUnlaunchInstaller(bool disableAllPatches, const char* splashSou
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!patchCustomBackground(customBackgroundPath))
|
if(!patchCustomBackground(customBackground))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -559,9 +539,9 @@ const char* getUnlaunchVersionString(UNLAUNCH_VERSION version)
|
|||||||
return unlaunchVersionStrings[version];
|
return unlaunchVersionStrings[version];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool installUnlaunch(const consoleInfo& info, bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath)
|
bool installUnlaunch(const consoleInfo& info, bool disableAllPatches, const char* splashSoundBinaryPatchPath, std::span<uint8_t> customBackground)
|
||||||
{
|
{
|
||||||
if (installerVersion == INVALID || !patchUnlaunchInstaller(disableAllPatches, splashSoundBinaryPatchPath, customBackgroundPath))
|
if (installerVersion == INVALID || !patchUnlaunchInstaller(disableAllPatches, splashSoundBinaryPatchPath, customBackground))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Treat protos differently
|
// Treat protos differently
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#ifndef UNLAUNCH_H
|
#ifndef UNLAUNCH_H
|
||||||
#define UNLAUNCH_H
|
#define UNLAUNCH_H
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "consoleInfo.h"
|
#include "consoleInfo.h"
|
||||||
|
|
||||||
@ -9,10 +10,12 @@ typedef enum UNLAUNCH_VERSION {
|
|||||||
INVALID,
|
INVALID,
|
||||||
} UNLAUNCH_VERSION;
|
} UNLAUNCH_VERSION;
|
||||||
|
|
||||||
|
static constexpr auto MAX_GIF_SIZE = 0x3C70;
|
||||||
|
|
||||||
const char* getUnlaunchVersionString(UNLAUNCH_VERSION);
|
const char* getUnlaunchVersionString(UNLAUNCH_VERSION);
|
||||||
|
|
||||||
bool uninstallUnlaunch(const consoleInfo& info, bool removeHNAABackup);
|
bool uninstallUnlaunch(const consoleInfo& info, bool removeHNAABackup);
|
||||||
bool installUnlaunch(const consoleInfo& info, bool disableAllPatches, const char* splashSoundBinaryPatchPath, const char* customBackgroundPath);
|
bool installUnlaunch(const consoleInfo& info, bool disableAllPatches, const char* splashSoundBinaryPatchPath, std::span<uint8_t> customBackground);
|
||||||
|
|
||||||
UNLAUNCH_VERSION loadUnlaunchInstaller(std::string_view path);
|
UNLAUNCH_VERSION loadUnlaunchInstaller(std::string_view path);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user