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
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := src src/nand src/nand/polarssl src/nand/twltool
|
||||
INCLUDES := include src/nand
|
||||
SOURCES := src src/lzw src/nand src/nand/polarssl src/nand/twltool
|
||||
INCLUDES := include src/nand src/lzw
|
||||
DATA :=
|
||||
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <format>
|
||||
#include <nds.h>
|
||||
#include <dirent.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -51,7 +52,8 @@ std::span<uint8_t> backgroundMenu()
|
||||
clearScreen(&topScreen);
|
||||
|
||||
//menu
|
||||
Menu* m = newMenu();
|
||||
auto mSptr = std::shared_ptr<Menu>(newMenu(), freeMenu);
|
||||
auto* m = mSptr.get();
|
||||
setMenuHeader(m, "BACKGROUNDS");
|
||||
|
||||
const auto& bgs = getBackgroundList();
|
||||
@ -68,33 +70,43 @@ std::span<uint8_t> backgroundMenu()
|
||||
//bottom screen
|
||||
printMenu(m);
|
||||
|
||||
while (!programEnd)
|
||||
{
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (moveCursor(m))
|
||||
printMenu(m);
|
||||
|
||||
if (auto keys = keysDown(); keys & KEY_A)
|
||||
break;
|
||||
else if(keys & KEY_B)
|
||||
while (!programEnd) {
|
||||
while (!programEnd)
|
||||
{
|
||||
m->cursor = bgs.size() + 1;
|
||||
break;
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if (moveCursor(m))
|
||||
printMenu(m);
|
||||
|
||||
if (auto keys = keysDown(); keys & KEY_A)
|
||||
break;
|
||||
else if(keys & KEY_B)
|
||||
{
|
||||
m->cursor = bgs.size() + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto selection = static_cast<size_t>(m->cursor);
|
||||
freeMenu(m);
|
||||
auto selection = static_cast<size_t>(m->cursor);
|
||||
|
||||
if(selection < bgs.size()) {
|
||||
if(selection > bgs.size())
|
||||
return {};
|
||||
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) {
|
||||
messageBox(std::format("\x1B[31mError:\x1B[33m The image could not\n"
|
||||
"be loaded: {}", e.what()).data());
|
||||
}
|
||||
|
||||
printMenu(m);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
@ -10,6 +9,8 @@
|
||||
#include "storage.h"
|
||||
#include "unlaunch.h"
|
||||
|
||||
#include "lzw/lzw.hpp"
|
||||
|
||||
struct Gif {
|
||||
struct Header {
|
||||
char signature[6];
|
||||
@ -24,7 +25,7 @@ struct Gif {
|
||||
} __attribute__ ((__packed__)) header;
|
||||
static_assert(sizeof(Header) == 13);
|
||||
|
||||
std::array<uint8_t, 256 * 3> colorTable;
|
||||
std::array<uint8_t, 256 * 3> colorTable{};
|
||||
size_t numColors;
|
||||
|
||||
struct Frame {
|
||||
@ -51,7 +52,7 @@ struct Gif {
|
||||
static constexpr auto WIDTH = 256;
|
||||
static constexpr auto HEIGHT = 192;
|
||||
|
||||
Gif getGif(std::string_view path) {
|
||||
Gif getGif(std::string_view path, volatile uint16_t* decompressedData) {
|
||||
Gif gif;
|
||||
auto file = fopen(path.data(), "rb");
|
||||
if (!file)
|
||||
@ -59,10 +60,10 @@ Gif getGif(std::string_view path) {
|
||||
|
||||
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");
|
||||
}
|
||||
if(auto size = getFileSize(file); size < 7 || size > MAX_GIF_SIZE)
|
||||
{
|
||||
throw std::runtime_error("Gif file too big.\n");
|
||||
}
|
||||
|
||||
auto& header = gif.header;
|
||||
|
||||
@ -139,10 +140,29 @@ Gif getGif(std::string_view path) {
|
||||
}
|
||||
|
||||
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)) {
|
||||
std::vector<uint8_t> dataChunk;
|
||||
dataChunk.resize(size);
|
||||
fread(dataChunk.data(), 1, size, file);
|
||||
reader.decode(dataChunk.begin(), dataChunk.end());
|
||||
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) {
|
||||
const auto gif = getGif(path);
|
||||
return writeGif(gif, 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, decompressedData);
|
||||
return writeGif(gif, outArr);
|
||||
}
|
||||
|
||||
@ -7,6 +7,6 @@
|
||||
|
||||
#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
|
||||
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 bottomScreen;
|
||||
|
||||
int bgGifTop;
|
||||
int bgGifBottom;
|
||||
|
||||
struct Stage2 {
|
||||
Sha1Digest sha;
|
||||
bool unlaunch_supported;
|
||||
@ -71,20 +74,22 @@ enum {
|
||||
|
||||
static void setupScreens()
|
||||
{
|
||||
REG_DISPCNT = MODE_FB0;
|
||||
VRAM_A_CR = VRAM_ENABLE;
|
||||
|
||||
videoSetMode(MODE_0_2D);
|
||||
videoSetModeSub(MODE_0_2D);
|
||||
videoSetMode(MODE_5_2D);
|
||||
videoSetModeSub(MODE_5_2D);
|
||||
|
||||
vramSetBankA(VRAM_A_MAIN_BG);
|
||||
vramSetBankC(VRAM_C_SUB_BG);
|
||||
|
||||
consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
|
||||
consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
|
||||
consoleInit(&topScreen, 1, BgType_Text4bpp, BgSize_T_256x256, 14, 0, true, true);
|
||||
consoleInit(&bottomScreen, 1, BgType_Text4bpp, BgSize_T_256x256, 14, 0, false, true);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@ extern volatile u8 batteryLevel;
|
||||
extern PrintConsole topScreen;
|
||||
extern PrintConsole bottomScreen;
|
||||
|
||||
extern int bgGifTop;
|
||||
extern int bgGifBottom;
|
||||
|
||||
void clearScreen(PrintConsole* screen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Loading…
Reference in New Issue
Block a user