mirror of
https://github.com/rvtr/unlaunch-installer_dev.git
synced 2026-01-26 13:43:08 -05:00
Show background preview when selecting it
This commit is contained in:
parent
58a2ac164f
commit
e88dc13af8
@ -15,8 +15,8 @@ include $(DEVKITARM)/ds_rules
|
|||||||
# all directories are relative to this makefile
|
# all directories are relative to this makefile
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := src src/nand src/nand/polarssl src/nand/twltool
|
SOURCES := src src/lzw src/nand src/nand/polarssl src/nand/twltool
|
||||||
INCLUDES := include src/nand
|
INCLUDES := include src/nand src/lzw
|
||||||
DATA :=
|
DATA :=
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <format>
|
#include <format>
|
||||||
#include <nds.h>
|
#include <nds.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ std::span<uint8_t> backgroundMenu()
|
|||||||
clearScreen(&topScreen);
|
clearScreen(&topScreen);
|
||||||
|
|
||||||
//menu
|
//menu
|
||||||
Menu* m = newMenu();
|
auto mSptr = std::shared_ptr<Menu>(newMenu(), freeMenu);
|
||||||
|
auto* m = mSptr.get();
|
||||||
setMenuHeader(m, "BACKGROUNDS");
|
setMenuHeader(m, "BACKGROUNDS");
|
||||||
|
|
||||||
const auto& bgs = getBackgroundList();
|
const auto& bgs = getBackgroundList();
|
||||||
@ -68,6 +70,7 @@ std::span<uint8_t> backgroundMenu()
|
|||||||
//bottom screen
|
//bottom screen
|
||||||
printMenu(m);
|
printMenu(m);
|
||||||
|
|
||||||
|
while (!programEnd) {
|
||||||
while (!programEnd)
|
while (!programEnd)
|
||||||
{
|
{
|
||||||
swiWaitForVBlank();
|
swiWaitForVBlank();
|
||||||
@ -86,15 +89,24 @@ std::span<uint8_t> backgroundMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto selection = static_cast<size_t>(m->cursor);
|
auto selection = static_cast<size_t>(m->cursor);
|
||||||
freeMenu(m);
|
|
||||||
|
|
||||||
if(selection < bgs.size()) {
|
if(selection > bgs.size())
|
||||||
|
return {};
|
||||||
try {
|
try {
|
||||||
return parseGif(bgs[selection].second.data(), currentlyLoadedGif);
|
const auto res = parseGif(bgs[selection].second.data(), currentlyLoadedGif, bgGetGfxPtr(bgGifTop));
|
||||||
|
bgHide(topScreen.bgId);
|
||||||
|
bgShow(bgGifTop);
|
||||||
|
auto confirmed = (choiceBox("Confirm this background?") == YES);
|
||||||
|
bgShow(topScreen.bgId);
|
||||||
|
bgHide(bgGifTop);
|
||||||
|
if(confirmed)
|
||||||
|
return res;
|
||||||
} catch(const std::exception& e) {
|
} catch(const std::exception& e) {
|
||||||
messageBox(std::format("\x1B[31mError:\x1B[33m The image could not\n"
|
messageBox(std::format("\x1B[31mError:\x1B[33m The image could not\n"
|
||||||
"be loaded: {}", e.what()).data());
|
"be loaded: {}", e.what()).data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printMenu(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <format>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -10,6 +9,8 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "unlaunch.h"
|
#include "unlaunch.h"
|
||||||
|
|
||||||
|
#include "lzw/lzw.hpp"
|
||||||
|
|
||||||
struct Gif {
|
struct Gif {
|
||||||
struct Header {
|
struct Header {
|
||||||
char signature[6];
|
char signature[6];
|
||||||
@ -24,7 +25,7 @@ struct Gif {
|
|||||||
} __attribute__ ((__packed__)) header;
|
} __attribute__ ((__packed__)) header;
|
||||||
static_assert(sizeof(Header) == 13);
|
static_assert(sizeof(Header) == 13);
|
||||||
|
|
||||||
std::array<uint8_t, 256 * 3> colorTable;
|
std::array<uint8_t, 256 * 3> colorTable{};
|
||||||
size_t numColors;
|
size_t numColors;
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
@ -51,7 +52,7 @@ struct Gif {
|
|||||||
static constexpr auto WIDTH = 256;
|
static constexpr auto WIDTH = 256;
|
||||||
static constexpr auto HEIGHT = 192;
|
static constexpr auto HEIGHT = 192;
|
||||||
|
|
||||||
Gif getGif(std::string_view path) {
|
Gif getGif(std::string_view path, volatile uint16_t* decompressedData) {
|
||||||
Gif gif;
|
Gif gif;
|
||||||
auto file = fopen(path.data(), "rb");
|
auto file = fopen(path.data(), "rb");
|
||||||
if (!file)
|
if (!file)
|
||||||
@ -139,10 +140,29 @@ Gif getGif(std::string_view path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame.image.lzwMinimumCodeSize = fgetc(file);
|
frame.image.lzwMinimumCodeSize = fgetc(file);
|
||||||
|
|
||||||
|
LZWReader reader(frame.image.lzwMinimumCodeSize, [&, it = decompressedData](auto begin, auto end) mutable {
|
||||||
|
const auto ds_color_table = [&]{
|
||||||
|
std::array<uint16_t, 256> ret{};
|
||||||
|
for(int i = 0; i < numColors; ++i){
|
||||||
|
auto r = color_table[(i * 3) + 0] >> 3;
|
||||||
|
auto g = color_table[(i * 3) + 1] >> 3;
|
||||||
|
auto b = color_table[(i * 3) + 2] >> 3;
|
||||||
|
ret[i] = RGB15(r,g,b) | BIT(15);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
std::transform(begin, end, it, [&](auto idx) {
|
||||||
|
return ds_color_table[idx];
|
||||||
|
});
|
||||||
|
it += std::distance(begin, end);
|
||||||
|
});
|
||||||
|
|
||||||
while (uint8_t size = fgetc(file)) {
|
while (uint8_t size = fgetc(file)) {
|
||||||
std::vector<uint8_t> dataChunk;
|
std::vector<uint8_t> dataChunk;
|
||||||
dataChunk.resize(size);
|
dataChunk.resize(size);
|
||||||
fread(dataChunk.data(), 1, size, file);
|
fread(dataChunk.data(), 1, size, file);
|
||||||
|
reader.decode(dataChunk.begin(), dataChunk.end());
|
||||||
frame.image.imageDataChunks.push_back(std::move(dataChunk));
|
frame.image.imageDataChunks.push_back(std::move(dataChunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +219,7 @@ std::span<uint8_t> writeGif(const Gif& gif, std::array<uint8_t, MAX_GIF_SIZE>& o
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr) {
|
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr, volatile uint16_t* decompressedData) {
|
||||||
const auto gif = getGif(path);
|
const auto gif = getGif(path, decompressedData);
|
||||||
return writeGif(gif, outArr);
|
return writeGif(gif, outArr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
#include "unlaunch.h"
|
#include "unlaunch.h"
|
||||||
|
|
||||||
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr);
|
std::span<uint8_t> parseGif(const char* path, std::array<uint8_t, MAX_GIF_SIZE>& outArr, volatile uint16_t* decompressedData);
|
||||||
|
|
||||||
#endif //GIF_CONVERTER_H
|
#endif //GIF_CONVERTER_H
|
||||||
125
arm9/src/lzw/lzw.cpp
Normal file
125
arm9/src/lzw/lzw.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#include "lzw.hpp"
|
||||||
|
|
||||||
|
u16 LZWReader::readLSB(std::vector<u8>::iterator &begin, const std::vector<u8>::iterator &end) {
|
||||||
|
while (nBits < width) {
|
||||||
|
if (begin == end) {
|
||||||
|
err = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u8 x = *(begin++);
|
||||||
|
bits |= x << nBits;
|
||||||
|
nBits += 8;
|
||||||
|
}
|
||||||
|
u16 code = bits & ((1 << width) - 1);
|
||||||
|
bits >>= width;
|
||||||
|
nBits -= width;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LZWReader::decode(std::vector<u8>::iterator begin, std::vector<u8>::iterator end) {
|
||||||
|
o = 0;
|
||||||
|
err = false;
|
||||||
|
// Loop over the code stream, converting codes into decompressed bytes.
|
||||||
|
while (begin != end) {
|
||||||
|
u16 code = readLSB(begin, end);
|
||||||
|
if (err) {
|
||||||
|
flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code < clear) { // Literal
|
||||||
|
output[o++] = code;
|
||||||
|
if (last != DECODER_INVALID_CODE) {
|
||||||
|
// Save what the hi code expands to.
|
||||||
|
suffix[hi] = code;
|
||||||
|
prefix[hi] = last;
|
||||||
|
}
|
||||||
|
} else if (code == clear) { // Clear
|
||||||
|
width = 1 + litWidth;
|
||||||
|
hi = eof;
|
||||||
|
overflow = 1 << width;
|
||||||
|
last = DECODER_INVALID_CODE;
|
||||||
|
continue;
|
||||||
|
} else if (code == eof) { // End
|
||||||
|
flush();
|
||||||
|
return true;
|
||||||
|
} else if (code <= hi) {
|
||||||
|
u16 c = code;
|
||||||
|
uint i = output.size() - 1;
|
||||||
|
if (code == hi && last != DECODER_INVALID_CODE) {
|
||||||
|
// code == hi is a special case which expands to the last expansion
|
||||||
|
// followed by the head of the last expansion. To find the head, we walk
|
||||||
|
// the prefix chain until we find a literal code.
|
||||||
|
c = last;
|
||||||
|
while (c >= clear)
|
||||||
|
c = prefix[c];
|
||||||
|
output[i] = c;
|
||||||
|
i--;
|
||||||
|
c = last;
|
||||||
|
}
|
||||||
|
// Copy the suffix chain into output and then write that to w.
|
||||||
|
while (c >= clear) {
|
||||||
|
output[i] = suffix[c];
|
||||||
|
i--;
|
||||||
|
c = prefix[c];
|
||||||
|
}
|
||||||
|
output[i] = c;
|
||||||
|
std::copy(output.begin() + i, output.end(), output.begin() + o);
|
||||||
|
o += std::distance(output.begin() + i, output.end());
|
||||||
|
if (last != DECODER_INVALID_CODE) {
|
||||||
|
// Save what the hi code expands to
|
||||||
|
suffix[hi] = c;
|
||||||
|
prefix[hi] = last;
|
||||||
|
}
|
||||||
|
} else { // Error
|
||||||
|
flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = code;
|
||||||
|
hi++;
|
||||||
|
if (hi >= overflow) {
|
||||||
|
if (hi > overflow) {
|
||||||
|
flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == MAX_WIDTH) {
|
||||||
|
last = DECODER_INVALID_CODE;
|
||||||
|
// Undo the d.hi++ a few lines above, so that (1) we maintain
|
||||||
|
// the invariant that d.hi < d.overflow, and (2) d.hi does not
|
||||||
|
// eventually overflow a uint16.
|
||||||
|
hi--;
|
||||||
|
} else {
|
||||||
|
width++;
|
||||||
|
overflow = 1 << width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o >= FLUSH_BUFFER) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LZWReader::LZWReader(int minCodeSize, std::function<void(u8_itr, u8_itr)> flushFunction) : litWidth(minCodeSize), flushFn(flushFunction) {
|
||||||
|
width = 1 + litWidth;
|
||||||
|
clear = 1 << litWidth;
|
||||||
|
eof = clear + 1;
|
||||||
|
hi = clear + 1;
|
||||||
|
overflow = 1 << width;
|
||||||
|
last = DECODER_INVALID_CODE;
|
||||||
|
|
||||||
|
suffix = std::vector<u8>(1 << MAX_WIDTH);
|
||||||
|
prefix = std::vector<u16>(1 << MAX_WIDTH);
|
||||||
|
output = std::vector<u8>(2 * (1 << MAX_WIDTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LZWReader::flush(void) {
|
||||||
|
if (flushFn && o > 0) {
|
||||||
|
flushFn(output.begin(), output.begin() + o);
|
||||||
|
}
|
||||||
|
o = 0;
|
||||||
|
}
|
||||||
44
arm9/src/lzw/lzw.hpp
Normal file
44
arm9/src/lzw/lzw.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef LZW_HPP
|
||||||
|
#define LZW_HPP
|
||||||
|
|
||||||
|
#include <nds.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef unsigned int uint;
|
||||||
|
typedef std::vector<u8>::const_iterator u8_itr;
|
||||||
|
|
||||||
|
class LZWReader {
|
||||||
|
constexpr static u16 MAX_WIDTH = 12;
|
||||||
|
constexpr static u16 DECODER_INVALID_CODE = 0xFFFF;
|
||||||
|
constexpr static u16 FLUSH_BUFFER = 1 << MAX_WIDTH;
|
||||||
|
|
||||||
|
int litWidth;
|
||||||
|
std::function<void(u8_itr, u8_itr)> flushFn;
|
||||||
|
u32 bits = 0;
|
||||||
|
uint nBits = 0;
|
||||||
|
uint width;
|
||||||
|
bool err = false;
|
||||||
|
|
||||||
|
u16 clear, eof, hi, overflow, last;
|
||||||
|
|
||||||
|
std::vector<u8> suffix;
|
||||||
|
std::vector<u16> prefix;
|
||||||
|
|
||||||
|
std::vector<u8> output;
|
||||||
|
int o = 0;
|
||||||
|
// std::vector<u8> toRead;
|
||||||
|
|
||||||
|
u16 readLSB(std::vector<u8>::iterator &it, const std::vector<u8>::iterator &end);
|
||||||
|
|
||||||
|
int read(std::vector<u8> &buffer);
|
||||||
|
|
||||||
|
void flush(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
LZWReader(int minCodeSize, std::function<void(u8_itr, u8_itr)> flushFunction);
|
||||||
|
|
||||||
|
bool decode(std::vector<u8>::iterator begin, std::vector<u8>::iterator end);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -35,6 +35,9 @@ static bool isLauncherVersionSupported = true;
|
|||||||
PrintConsole topScreen;
|
PrintConsole topScreen;
|
||||||
PrintConsole bottomScreen;
|
PrintConsole bottomScreen;
|
||||||
|
|
||||||
|
int bgGifTop;
|
||||||
|
int bgGifBottom;
|
||||||
|
|
||||||
struct Stage2 {
|
struct Stage2 {
|
||||||
Sha1Digest sha;
|
Sha1Digest sha;
|
||||||
bool unlaunch_supported;
|
bool unlaunch_supported;
|
||||||
@ -71,20 +74,22 @@ enum {
|
|||||||
|
|
||||||
static void setupScreens()
|
static void setupScreens()
|
||||||
{
|
{
|
||||||
REG_DISPCNT = MODE_FB0;
|
videoSetMode(MODE_5_2D);
|
||||||
VRAM_A_CR = VRAM_ENABLE;
|
videoSetModeSub(MODE_5_2D);
|
||||||
|
|
||||||
videoSetMode(MODE_0_2D);
|
|
||||||
videoSetModeSub(MODE_0_2D);
|
|
||||||
|
|
||||||
vramSetBankA(VRAM_A_MAIN_BG);
|
vramSetBankA(VRAM_A_MAIN_BG);
|
||||||
vramSetBankC(VRAM_C_SUB_BG);
|
vramSetBankC(VRAM_C_SUB_BG);
|
||||||
|
|
||||||
consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
|
consoleInit(&topScreen, 1, BgType_Text4bpp, BgSize_T_256x256, 14, 0, true, true);
|
||||||
consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
|
consoleInit(&bottomScreen, 1, BgType_Text4bpp, BgSize_T_256x256, 14, 0, false, true);
|
||||||
|
|
||||||
clearScreen(&bottomScreen);
|
clearScreen(&bottomScreen);
|
||||||
|
|
||||||
|
bgGifTop = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 2, 0);
|
||||||
|
bgHide(bgGifTop);
|
||||||
|
bgGifBottom = bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 2, 0);
|
||||||
|
bgHide(bgGifBottom);
|
||||||
|
|
||||||
VRAM_A[100] = 0xFFFF;
|
VRAM_A[100] = 0xFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,9 @@ extern volatile u8 batteryLevel;
|
|||||||
extern PrintConsole topScreen;
|
extern PrintConsole topScreen;
|
||||||
extern PrintConsole bottomScreen;
|
extern PrintConsole bottomScreen;
|
||||||
|
|
||||||
|
extern int bgGifTop;
|
||||||
|
extern int bgGifBottom;
|
||||||
|
|
||||||
void clearScreen(PrintConsole* screen);
|
void clearScreen(PrintConsole* screen);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user