mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 20:20:19 +02:00
Add launcher-styled framebuffer UI for Switch OTA prompts.
Replace the console flash with a quiet branded screen (RGB rail, logo, A/B buttons) that only appears when an update needs confirm.
This commit is contained in:
@@ -37,6 +37,7 @@ BUILD := build
|
||||
SOURCES := src
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
ROMFS := romfs
|
||||
|
||||
APP_TITLE := gen1recomp
|
||||
APP_AUTHOR := bryanthaboi, port by andrewqsantos
|
||||
@@ -45,6 +46,7 @@ APP_VERSION ?= 0.0.0
|
||||
|
||||
# Prefer project Switch icon if present
|
||||
ICON := $(TOPDIR)/../../assets/switch/icon.jpg
|
||||
LOGO_SRC := $(TOPDIR)/../../assets/logo/logo.png
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
@@ -124,12 +126,20 @@ ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all host-test
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all host-test sync-romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
all: sync-romfs $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
sync-romfs:
|
||||
@mkdir -p $(CURDIR)/$(ROMFS)
|
||||
@cp -f $(LOGO_SRC) $(CURDIR)/$(ROMFS)/logo.png
|
||||
|
||||
$(BUILD): sync-romfs
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@@ -137,6 +147,7 @@ $(BUILD):
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf $(TARGET).lst build-host
|
||||
@rm -f $(CURDIR)/$(ROMFS)/logo.png
|
||||
|
||||
host-test:
|
||||
@mkdir -p build-host
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Native Switch OTA launcher
|
||||
|
||||
In-console updates for Gen1Recomp on Nintendo Switch. This NRO is the **hbmenu
|
||||
entry** (`gen1recomp.nro`). It checks GitHub Releases quietly (no console flash
|
||||
when you are already up to date or offline). Only if a newer release exists
|
||||
does it prompt to download the same `gen1recomp-*-switch.zip` used for install,
|
||||
verify SHA-256 from `sha256sums.txt`, replace **both** `gen1recomp-game.nro`
|
||||
and `gen1recomp.nro` (matching NACP version for hbmenu/Sphaira), then hand off
|
||||
entry** (`gen1recomp.nro`). It checks GitHub Releases quietly (no UI when you
|
||||
are already up to date or offline). Only if a newer release exists does it show
|
||||
a **launcher-style screen** (black + RGB rail + project logo + flat A/B
|
||||
buttons), download the same `gen1recomp-*-switch.zip` used for install, verify
|
||||
SHA-256 from `sha256sums.txt`, replace **both** `gen1recomp-game.nro` and
|
||||
`gen1recomp.nro` (matching NACP version for hbmenu/Sphaira), then hand off
|
||||
with `envSetNextLoad`.
|
||||
|
||||
The LÖVE self-updater (`src/update/Check.lua`) stays **disabled** on NX.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef GEN1_OTA_UI_H
|
||||
#define GEN1_OTA_UI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Framebuffer UI matching the in-game launcher look (black + RGB rail +
|
||||
* flat semantic buttons). Silent by default — only call when user-facing. */
|
||||
|
||||
/* Returns 1 = update, 0 = skip / play installed. */
|
||||
int ota_ui_prompt_update(const char *installed, const char *latest);
|
||||
|
||||
/* Status screen (download / install). Redraws each call. */
|
||||
void ota_ui_show_status(const char *title, const char *detail);
|
||||
|
||||
/* Progress 0..1; detail optional. */
|
||||
void ota_ui_show_progress(const char *title, const char *detail, float progress01);
|
||||
|
||||
/* Error / info + wait for B. */
|
||||
void ota_ui_alert(const char *title, const char *line1, const char *line2);
|
||||
|
||||
/* Missing game install screen; wait for +. */
|
||||
void ota_ui_missing_game(void);
|
||||
|
||||
/* Tear down framebuffer if active. Safe to call when UI never opened. */
|
||||
void ota_ui_shutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,16 +1,17 @@
|
||||
/*
|
||||
* gen1recomp Switch OTA launcher
|
||||
*
|
||||
* Quiet by default: checks GitHub Releases with no console flash. Only shows
|
||||
* a short prompt when an update is available. Downloads the same SD zip
|
||||
* published for install (gen1recomp-*-switch.zip), verifies SHA-256, extracts
|
||||
* switch/gen1recomp/gen1recomp-game.nro, then envSetNextLoad to the game.
|
||||
* Never touches pokemon-love2d/. LÖVE self-updater stays off on NX.
|
||||
* Quiet by default: checks GitHub Releases with no UI. Only shows the
|
||||
* branded launcher-style screen when an update is available. Downloads the
|
||||
* same SD zip (gen1recomp-*-switch.zip), verifies SHA-256, replaces game +
|
||||
* launcher NROs, then envSetNextLoad. Never touches pokemon-love2d/.
|
||||
* LÖVE self-updater stays off on NX.
|
||||
*/
|
||||
|
||||
#include "ota_fs.h"
|
||||
#include "ota_net.h"
|
||||
#include "ota_protocol.h"
|
||||
#include "ota_ui.h"
|
||||
#include "ota_unzip.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -31,61 +32,6 @@
|
||||
#define GAME_MEMBER_IN_ZIP "switch/gen1recomp/" OTA_GAME_NRO_NAME
|
||||
#define LAUNCHER_MEMBER_IN_ZIP "switch/gen1recomp/" OTA_LAUNCHER_NRO_NAME
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
static int g_ui = 0;
|
||||
|
||||
static void ui_begin(void) {
|
||||
if (g_ui) return;
|
||||
consoleInit(NULL);
|
||||
consoleClear();
|
||||
g_ui = 1;
|
||||
}
|
||||
|
||||
static void ui_end(void) {
|
||||
if (!g_ui) return;
|
||||
consoleExit(NULL);
|
||||
g_ui = 0;
|
||||
}
|
||||
|
||||
static void ui_line(const char *msg) {
|
||||
ui_begin();
|
||||
printf("%s\n", msg);
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
|
||||
static void ui_blank(void) {
|
||||
if (!g_ui) return;
|
||||
consoleClear();
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
#else
|
||||
static void ui_begin(void) {}
|
||||
static void ui_end(void) {}
|
||||
static void ui_line(const char *msg) { fprintf(stderr, "%s\n", msg); }
|
||||
static void ui_blank(void) {}
|
||||
#endif
|
||||
|
||||
/* Returns 1 = update, 0 = skip. Only called when UI is already needed. */
|
||||
static int wait_confirm_or_skip(void) {
|
||||
#if defined(__SWITCH__)
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
ui_line("");
|
||||
ui_line(" A — Update");
|
||||
ui_line(" B — Play without updating");
|
||||
while (appletMainLoop()) {
|
||||
padUpdate(&pad);
|
||||
u64 k = padGetButtonsDown(&pad);
|
||||
if (k & HidNpadButton_A) return 1;
|
||||
if (k & HidNpadButton_B) return 0;
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int run_update_flow(const char *install_dir) {
|
||||
char installed[64] = "0.0.0";
|
||||
(void)ota_fs_read_installed_version(install_dir, installed, sizeof(installed));
|
||||
@@ -114,19 +60,7 @@ static int run_update_flow(const char *install_dir) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update available — only now show UI */
|
||||
{
|
||||
char msg[160];
|
||||
ui_blank();
|
||||
ui_line("");
|
||||
ui_line(" gen1recomp");
|
||||
ui_line("");
|
||||
snprintf(msg, sizeof(msg), " Update available: %s → %s", installed, dec.version);
|
||||
ui_line(msg);
|
||||
}
|
||||
|
||||
if (!wait_confirm_or_skip()) {
|
||||
ui_end();
|
||||
if (!ota_ui_prompt_update(installed, dec.version)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -141,50 +75,27 @@ static int run_update_flow(const char *install_dir) {
|
||||
mkdir(updates_dir, 0755);
|
||||
#endif
|
||||
|
||||
ui_blank();
|
||||
ui_line("");
|
||||
ui_line(" Downloading…");
|
||||
ota_ui_show_progress("Downloading…", "This may take a minute.", 0.15f);
|
||||
if (ota_net_download_file(dec.download_url, zip_path, 180000L, err, sizeof(err)) != 0) {
|
||||
ui_line("");
|
||||
ui_line(" Download failed.");
|
||||
ui_line(" Playing the installed version.");
|
||||
#if defined(__SWITCH__)
|
||||
{
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
ui_line("");
|
||||
ui_line(" Press B to continue");
|
||||
while (appletMainLoop()) {
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & HidNpadButton_B) break;
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ui_end();
|
||||
ota_ui_alert("Download failed", "Playing the installed version.", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ota_ui_show_progress("Checking update…", "Verifying file integrity.", 0.55f);
|
||||
char sums_url[512];
|
||||
snprintf(sums_url, sizeof(sums_url),
|
||||
"https://github.com/bryanthaboi/gen1recomp/releases/download/%s/sha256sums.txt",
|
||||
rel.tag);
|
||||
if (ota_net_download_file(sums_url, sums_path, CHECK_TIMEOUT_MS, err, sizeof(err)) != 0) {
|
||||
ui_line("");
|
||||
ui_line(" Could not verify the update.");
|
||||
ui_line(" Playing the installed version.");
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
#if defined(__SWITCH__)
|
||||
svcSleepThread(1200000000ULL);
|
||||
#endif
|
||||
ota_ui_alert("Could not verify", "Playing the installed version.", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *sf = fopen(sums_path, "rb");
|
||||
if (!sf) {
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
fseek(sf, 0, SEEK_END);
|
||||
@@ -193,7 +104,7 @@ static int run_update_flow(const char *install_dir) {
|
||||
char *sums = (char *)malloc((size_t)slen + 1);
|
||||
if (!sums) {
|
||||
fclose(sf);
|
||||
ui_end();
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
fread(sums, 1, (size_t)slen, sf);
|
||||
@@ -204,70 +115,49 @@ static int run_update_flow(const char *install_dir) {
|
||||
if (ota_fs_sha256_file(zip_path, hex, sizeof(hex)) != 0) {
|
||||
free(sums);
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
ota_verify_t ver;
|
||||
ota_verify_sha256(dec.asset_name, hex, sums, &ver);
|
||||
free(sums);
|
||||
if (!ver.ok) {
|
||||
ui_line("");
|
||||
ui_line(" Update file failed checks.");
|
||||
ui_line(" Playing the installed version.");
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
#if defined(__SWITCH__)
|
||||
svcSleepThread(1200000000ULL);
|
||||
#endif
|
||||
ota_ui_alert("Update check failed", "Playing the installed version.", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char extracted[256];
|
||||
char extracted_launcher[256];
|
||||
snprintf(extracted, sizeof(extracted), "%s/updates/gen1recomp-game.nro.verified", install_dir);
|
||||
snprintf(extracted_launcher, sizeof(extracted_launcher),
|
||||
"%s/updates/gen1recomp.nro.verified", install_dir);
|
||||
snprintf(extracted_launcher, sizeof(extracted_launcher), "%s/updates/gen1recomp.nro.verified",
|
||||
install_dir);
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
ui_line("");
|
||||
ui_line(" Installing…");
|
||||
/* Prefer path inside SD zip; fall back to bare name. */
|
||||
ota_ui_show_progress("Installing…", "Keeping your saves safe.", 0.8f);
|
||||
if (ota_unzip_extract_file(zip_path, GAME_MEMBER_IN_ZIP, extracted, err, sizeof(err)) != 0) {
|
||||
if (ota_unzip_extract_file(zip_path, OTA_GAME_NRO_NAME, extracted, err, sizeof(err)) != 0) {
|
||||
ui_line("");
|
||||
ui_line(" Install failed.");
|
||||
ui_line(" Playing the installed version.");
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
svcSleepThread(1200000000ULL);
|
||||
ota_ui_alert("Install failed", "Playing the installed version.", NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ota_fs_atomic_replace_game(install_dir, extracted, err, sizeof(err)) != 0) {
|
||||
ui_line("");
|
||||
ui_line(" Install failed.");
|
||||
ui_line(" Playing the installed version.");
|
||||
remove(extracted);
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
svcSleepThread(1200000000ULL);
|
||||
ota_ui_alert("Install failed", "Playing the installed version.", NULL);
|
||||
return 0;
|
||||
}
|
||||
remove(extracted);
|
||||
|
||||
/* Also replace launcher so hbmenu/Sphaira see the matching NACP version.
|
||||
* Best-effort: game update already succeeded if this fails. */
|
||||
if (ota_unzip_extract_file(zip_path, LAUNCHER_MEMBER_IN_ZIP, extracted_launcher, err,
|
||||
sizeof(err)) == 0 ||
|
||||
ota_unzip_extract_file(zip_path, OTA_LAUNCHER_NRO_NAME, extracted_launcher, err,
|
||||
sizeof(err)) == 0) {
|
||||
if (ota_fs_atomic_replace_nro(install_dir, OTA_LAUNCHER_NRO_NAME, extracted_launcher, err,
|
||||
sizeof(err)) == 0) {
|
||||
remove(extracted_launcher);
|
||||
} else {
|
||||
remove(extracted_launcher);
|
||||
}
|
||||
(void)ota_fs_atomic_replace_nro(install_dir, OTA_LAUNCHER_NRO_NAME, extracted_launcher, err,
|
||||
sizeof(err));
|
||||
remove(extracted_launcher);
|
||||
}
|
||||
|
||||
char vpath[192];
|
||||
@@ -277,12 +167,15 @@ static int run_update_flow(const char *install_dir) {
|
||||
fprintf(vf, "%s\n", dec.version);
|
||||
fclose(vf);
|
||||
}
|
||||
|
||||
ota_ui_show_progress("Ready", "Starting the game…", 1.0f);
|
||||
svcSleepThread(600000000ULL);
|
||||
#else
|
||||
(void)extracted;
|
||||
(void)extracted_launcher;
|
||||
#endif
|
||||
remove(zip_path);
|
||||
ui_end();
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -303,26 +196,11 @@ int main(int argc, char **argv) {
|
||||
char err[128];
|
||||
err[0] = '\0';
|
||||
if (ota_fs_handoff_to_game(game, err, sizeof(err)) != 0) {
|
||||
ui_line("");
|
||||
ui_line(" gen1recomp");
|
||||
ui_line("");
|
||||
ui_line(" Game files are missing.");
|
||||
ui_line(" Copy the Switch zip onto your microSD,");
|
||||
ui_line(" then open gen1recomp again.");
|
||||
#if defined(__SWITCH__)
|
||||
ui_line("");
|
||||
ui_line(" Press + to exit");
|
||||
PadState pad;
|
||||
padInitializeDefault(&pad);
|
||||
while (appletMainLoop()) {
|
||||
padUpdate(&pad);
|
||||
if (padGetButtonsDown(&pad) & HidNpadButton_Plus) break;
|
||||
consoleUpdate(NULL);
|
||||
}
|
||||
#endif
|
||||
ui_end();
|
||||
ota_ui_missing_game();
|
||||
}
|
||||
|
||||
ota_ui_shutdown();
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
socketExit();
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Switch OTA UI — matches in-game launcher language (Theme.lua):
|
||||
* black field, RGB version rail, flat yellow/white buttons, white ink.
|
||||
* Quiet until called. Uses framebuffer + 8x8 font + optional romfs logo.
|
||||
*/
|
||||
#include "ota_ui.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__SWITCH__)
|
||||
#include <switch.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_THREAD_LOCALS
|
||||
#include "../third_party/stb_image.h"
|
||||
#include "../third_party/font8x8_basic.h"
|
||||
|
||||
#define FB_W 1280
|
||||
#define FB_H 720
|
||||
|
||||
/* Theme.PAL (0–255) → RGBA8 */
|
||||
#define COL_BG RGBA8_MAXALPHA(0, 0, 0)
|
||||
#define COL_INK RGBA8_MAXALPHA(255, 255, 255)
|
||||
#define COL_DETAIL RGBA8_MAXALPHA(200, 200, 200)
|
||||
#define COL_MUTED RGBA8_MAXALPHA(150, 150, 150)
|
||||
#define COL_INVERSE RGBA8_MAXALPHA(0, 0, 0)
|
||||
#define COL_YELLOW RGBA8_MAXALPHA(255, 214, 0)
|
||||
#define COL_GREEN RGBA8_MAXALPHA(0, 255, 140)
|
||||
#define COL_RAIL_R RGBA8_MAXALPHA(255, 60, 72)
|
||||
#define COL_RAIL_B RGBA8_MAXALPHA(70, 150, 255)
|
||||
#define COL_RAIL_G RGBA8_MAXALPHA(255, 203, 5)
|
||||
#define COL_LINE RGBA8_MAXALPHA(90, 90, 90)
|
||||
#define COL_RAISED RGBA8_MAXALPHA(20, 20, 20)
|
||||
|
||||
static int g_ready = 0;
|
||||
static Framebuffer g_fb;
|
||||
static PadState g_pad;
|
||||
static u32 *g_logo = NULL;
|
||||
static int g_logo_w = 0, g_logo_h = 0;
|
||||
|
||||
static void fill_rect(u32 *fb, u32 stride_px, int x, int y, int w, int h, u32 color) {
|
||||
if (w <= 0 || h <= 0) return;
|
||||
if (x < 0) {
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
if (y < 0) {
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
if (x + w > FB_W) w = FB_W - x;
|
||||
if (y + h > FB_H) h = FB_H - y;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
for (int row = 0; row < h; row++) {
|
||||
u32 *line = fb + (y + row) * stride_px + x;
|
||||
for (int col = 0; col < w; col++) line[col] = color;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_glyph(u32 *fb, u32 stride_px, int x, int y, char ch, u32 color, int scale) {
|
||||
unsigned char c = (unsigned char)ch;
|
||||
if (c > 127) c = '?';
|
||||
const char *bits = font8x8_basic[c];
|
||||
for (int row = 0; row < 8; row++) {
|
||||
unsigned char line = (unsigned char)bits[row];
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if (line & (1 << col)) {
|
||||
fill_rect(fb, stride_px, x + col * scale, y + row * scale, scale, scale, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int text_width(const char *s, int scale) {
|
||||
if (!s) return 0;
|
||||
return (int)strlen(s) * 8 * scale;
|
||||
}
|
||||
|
||||
static void draw_text(u32 *fb, u32 stride_px, int x, int y, const char *s, u32 color, int scale) {
|
||||
if (!s) return;
|
||||
int cx = x;
|
||||
for (; *s; s++) {
|
||||
if (*s == '\n') {
|
||||
cx = x;
|
||||
y += 8 * scale + 4;
|
||||
continue;
|
||||
}
|
||||
draw_glyph(fb, stride_px, cx, y, *s, color, scale);
|
||||
cx += 8 * scale;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_text_centered(u32 *fb, u32 stride_px, int y, const char *s, u32 color, int scale) {
|
||||
int w = text_width(s, scale);
|
||||
draw_text(fb, stride_px, (FB_W - w) / 2, y, s, color, scale);
|
||||
}
|
||||
|
||||
static void draw_rail(u32 *fb, u32 stride_px) {
|
||||
int h = 6;
|
||||
int third = FB_W / 3;
|
||||
fill_rect(fb, stride_px, 0, 0, third, h, COL_RAIL_R);
|
||||
fill_rect(fb, stride_px, third, 0, third, h, COL_RAIL_B);
|
||||
fill_rect(fb, stride_px, third * 2, 0, FB_W - third * 2, h, COL_RAIL_G);
|
||||
}
|
||||
|
||||
static void blit_logo(u32 *fb, u32 stride_px, int dst_x, int dst_y, int max_w) {
|
||||
if (!g_logo || g_logo_w <= 0 || g_logo_h <= 0) return;
|
||||
int dw = g_logo_w;
|
||||
int dh = g_logo_h;
|
||||
if (dw > max_w) {
|
||||
dh = dh * max_w / dw;
|
||||
dw = max_w;
|
||||
}
|
||||
for (int y = 0; y < dh; y++) {
|
||||
int sy = y * g_logo_h / dh;
|
||||
for (int x = 0; x < dw; x++) {
|
||||
int sx = x * g_logo_w / dw;
|
||||
u32 px = g_logo[sy * g_logo_w + sx];
|
||||
u8 a = (px >> 24) & 0xff;
|
||||
if (a < 16) continue;
|
||||
int dx = dst_x + x;
|
||||
int dy = dst_y + y;
|
||||
if (dx < 0 || dy < 0 || dx >= FB_W || dy >= FB_H) continue;
|
||||
fb[dy * stride_px + dx] = px | 0xff000000u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void load_logo(void) {
|
||||
if (g_logo) return;
|
||||
romfsInit();
|
||||
int w = 0, h = 0, n = 0;
|
||||
unsigned char *data = stbi_load("romfs:/logo.png", &w, &h, &n, 4);
|
||||
if (!data || w <= 0 || h <= 0) {
|
||||
if (data) stbi_image_free(data);
|
||||
return;
|
||||
}
|
||||
g_logo = (u32 *)malloc((size_t)w * (size_t)h * sizeof(u32));
|
||||
if (!g_logo) {
|
||||
stbi_image_free(data);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < w * h; i++) {
|
||||
unsigned char *p = data + i * 4;
|
||||
/* Premultiply-ish: keep RGB, store A in high byte for skip */
|
||||
g_logo[i] = RGBA8(p[0], p[1], p[2], p[3]);
|
||||
}
|
||||
g_logo_w = w;
|
||||
g_logo_h = h;
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
static int ui_ensure(void) {
|
||||
if (g_ready) return 1;
|
||||
NWindow *win = nwindowGetDefault();
|
||||
if (R_FAILED(framebufferCreate(&g_fb, win, FB_W, FB_H, PIXEL_FORMAT_RGBA_8888, 2))) return 0;
|
||||
framebufferMakeLinear(&g_fb);
|
||||
padInitializeDefault(&g_pad);
|
||||
load_logo();
|
||||
g_ready = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ota_ui_shutdown(void) {
|
||||
if (!g_ready) return;
|
||||
framebufferClose(&g_fb);
|
||||
free(g_logo);
|
||||
g_logo = NULL;
|
||||
g_logo_w = g_logo_h = 0;
|
||||
romfsExit();
|
||||
g_ready = 0;
|
||||
}
|
||||
|
||||
static void draw_button(u32 *fb, u32 stride_px, int x, int y, int w, int h, u32 fill, u32 ink,
|
||||
const char *label, int focused) {
|
||||
fill_rect(fb, stride_px, x, y, w, h, fill);
|
||||
if (focused) {
|
||||
/* 2px white focus ring (launcher focus outline) */
|
||||
fill_rect(fb, stride_px, x - 3, y - 3, w + 6, 2, COL_INK);
|
||||
fill_rect(fb, stride_px, x - 3, y + h + 1, w + 6, 2, COL_INK);
|
||||
fill_rect(fb, stride_px, x - 3, y - 3, 2, h + 6, COL_INK);
|
||||
fill_rect(fb, stride_px, x + w + 1, y - 3, 2, h + 6, COL_INK);
|
||||
} else {
|
||||
fill_rect(fb, stride_px, x, y, w, 1, COL_LINE);
|
||||
fill_rect(fb, stride_px, x, y + h - 1, w, 1, COL_LINE);
|
||||
fill_rect(fb, stride_px, x, y, 1, h, COL_LINE);
|
||||
fill_rect(fb, stride_px, x + w - 1, y, 1, h, COL_LINE);
|
||||
}
|
||||
int scale = 3;
|
||||
int tw = text_width(label, scale);
|
||||
int tx = x + (w - tw) / 2;
|
||||
int ty = y + (h - 8 * scale) / 2;
|
||||
draw_text(fb, stride_px, tx, ty, label, ink, scale);
|
||||
}
|
||||
|
||||
static void draw_chrome(u32 *fb, u32 stride_px) {
|
||||
fill_rect(fb, stride_px, 0, 0, FB_W, FB_H, COL_BG);
|
||||
draw_rail(fb, stride_px);
|
||||
int logo_max = 320;
|
||||
int logo_h = g_logo ? (g_logo_h * logo_max / g_logo_w) : 40;
|
||||
if (logo_h > 90) logo_h = 90;
|
||||
int logo_y = 48;
|
||||
if (g_logo) {
|
||||
int logo_w = logo_max;
|
||||
if (g_logo_w * logo_h / g_logo_h < logo_max) logo_w = g_logo_w * logo_h / g_logo_h;
|
||||
blit_logo(fb, stride_px, (FB_W - logo_w) / 2, logo_y, logo_max);
|
||||
} else {
|
||||
draw_text_centered(fb, stride_px, logo_y + 16, "gen1recomp", COL_INK, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void present(void (*paint)(u32 *fb, u32 stride_px, void *ctx), void *ctx) {
|
||||
if (!ui_ensure()) return;
|
||||
u32 stride = 0;
|
||||
u32 *fb = (u32 *)framebufferBegin(&g_fb, &stride);
|
||||
u32 stride_px = stride / sizeof(u32);
|
||||
paint(fb, stride_px, ctx);
|
||||
framebufferEnd(&g_fb);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *installed;
|
||||
const char *latest;
|
||||
int focus; /* 0 = Update, 1 = Play */
|
||||
} prompt_ctx_t;
|
||||
|
||||
static void paint_prompt(u32 *fb, u32 stride_px, void *ctx) {
|
||||
prompt_ctx_t *p = (prompt_ctx_t *)ctx;
|
||||
draw_chrome(fb, stride_px);
|
||||
|
||||
int y = 180;
|
||||
draw_text_centered(fb, stride_px, y, "Update available", COL_INK, 3);
|
||||
y += 48;
|
||||
|
||||
char line[96];
|
||||
snprintf(line, sizeof(line), "v%s -> v%s", p->installed ? p->installed : "?",
|
||||
p->latest ? p->latest : "?");
|
||||
draw_text_centered(fb, stride_px, y, line, COL_YELLOW, 3);
|
||||
y += 56;
|
||||
|
||||
draw_text_centered(fb, stride_px, y, "Your saves stay on this console.", COL_MUTED, 2);
|
||||
y += 64;
|
||||
|
||||
int bw = 420;
|
||||
int bh = 64;
|
||||
int bx = (FB_W - bw) / 2;
|
||||
draw_button(fb, stride_px, bx, y, bw, bh, COL_YELLOW, COL_INVERSE, "A Update", p->focus == 0);
|
||||
y += bh + 24;
|
||||
draw_button(fb, stride_px, bx, y, bw, bh, COL_INK, COL_INVERSE, "B Play without updating",
|
||||
p->focus == 1);
|
||||
}
|
||||
|
||||
int ota_ui_prompt_update(const char *installed, const char *latest) {
|
||||
if (!ui_ensure()) return 0;
|
||||
prompt_ctx_t ctx = {installed, latest, 0};
|
||||
while (appletMainLoop()) {
|
||||
present(paint_prompt, &ctx);
|
||||
padUpdate(&g_pad);
|
||||
u64 k = padGetButtonsDown(&g_pad);
|
||||
if (k & HidNpadButton_A) {
|
||||
int do_update = (ctx.focus == 0);
|
||||
if (!do_update) ota_ui_shutdown();
|
||||
return do_update;
|
||||
}
|
||||
if (k & HidNpadButton_B) {
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
if (k & (HidNpadButton_Up | HidNpadButton_Down | HidNpadButton_Left | HidNpadButton_Right)) {
|
||||
ctx.focus = 1 - ctx.focus;
|
||||
}
|
||||
svcSleepThread(16000000ULL);
|
||||
}
|
||||
ota_ui_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *title;
|
||||
const char *detail;
|
||||
float progress;
|
||||
int show_bar;
|
||||
} status_ctx_t;
|
||||
|
||||
static void paint_status(u32 *fb, u32 stride_px, void *ctx) {
|
||||
status_ctx_t *s = (status_ctx_t *)ctx;
|
||||
draw_chrome(fb, stride_px);
|
||||
int y = 200;
|
||||
draw_text_centered(fb, stride_px, y, s->title ? s->title : "", COL_INK, 3);
|
||||
y += 48;
|
||||
if (s->detail && s->detail[0]) {
|
||||
draw_text_centered(fb, stride_px, y, s->detail, COL_DETAIL, 2);
|
||||
y += 40;
|
||||
}
|
||||
if (s->show_bar) {
|
||||
int bw = 520;
|
||||
int bh = 18;
|
||||
int bx = (FB_W - bw) / 2;
|
||||
float t = s->progress;
|
||||
if (t < 0.f) t = 0.f;
|
||||
if (t > 1.f) t = 1.f;
|
||||
fill_rect(fb, stride_px, bx, y, bw, bh, COL_RAISED);
|
||||
fill_rect(fb, stride_px, bx, y, bw, 1, COL_LINE);
|
||||
fill_rect(fb, stride_px, bx, y + bh - 1, bw, 1, COL_LINE);
|
||||
fill_rect(fb, stride_px, bx, y, 1, bh, COL_LINE);
|
||||
fill_rect(fb, stride_px, bx + bw - 1, y, 1, bh, COL_LINE);
|
||||
int fw = (int)(bw * t);
|
||||
if (fw > 0) fill_rect(fb, stride_px, bx, y, fw, bh, COL_GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
void ota_ui_show_status(const char *title, const char *detail) {
|
||||
status_ctx_t s = {title, detail, 0.f, 0};
|
||||
present(paint_status, &s);
|
||||
}
|
||||
|
||||
void ota_ui_show_progress(const char *title, const char *detail, float progress01) {
|
||||
status_ctx_t s = {title, detail, progress01, 1};
|
||||
present(paint_status, &s);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *title;
|
||||
const char *line1;
|
||||
const char *line2;
|
||||
} alert_ctx_t;
|
||||
|
||||
static void paint_alert(u32 *fb, u32 stride_px, void *ctx) {
|
||||
alert_ctx_t *a = (alert_ctx_t *)ctx;
|
||||
draw_chrome(fb, stride_px);
|
||||
int y = 200;
|
||||
draw_text_centered(fb, stride_px, y, a->title ? a->title : "", COL_YELLOW, 3);
|
||||
y += 52;
|
||||
if (a->line1) draw_text_centered(fb, stride_px, y, a->line1, COL_DETAIL, 2);
|
||||
y += 36;
|
||||
if (a->line2) draw_text_centered(fb, stride_px, y, a->line2, COL_MUTED, 2);
|
||||
y += 64;
|
||||
int bw = 360;
|
||||
int bh = 56;
|
||||
draw_button(fb, stride_px, (FB_W - bw) / 2, y, bw, bh, COL_INK, COL_INVERSE, "B Continue", 1);
|
||||
}
|
||||
|
||||
void ota_ui_alert(const char *title, const char *line1, const char *line2) {
|
||||
if (!ui_ensure()) return;
|
||||
alert_ctx_t a = {title, line1, line2};
|
||||
while (appletMainLoop()) {
|
||||
present(paint_alert, &a);
|
||||
padUpdate(&g_pad);
|
||||
if (padGetButtonsDown(&g_pad) & HidNpadButton_B) break;
|
||||
svcSleepThread(16000000ULL);
|
||||
}
|
||||
ota_ui_shutdown();
|
||||
}
|
||||
|
||||
static void paint_missing(u32 *fb, u32 stride_px, void *ctx) {
|
||||
(void)ctx;
|
||||
draw_chrome(fb, stride_px);
|
||||
int y = 190;
|
||||
draw_text_centered(fb, stride_px, y, "Game files missing", COL_YELLOW, 3);
|
||||
y += 52;
|
||||
draw_text_centered(fb, stride_px, y, "Copy the Switch zip onto your microSD,", COL_DETAIL, 2);
|
||||
y += 32;
|
||||
draw_text_centered(fb, stride_px, y, "then open gen1recomp again.", COL_DETAIL, 2);
|
||||
y += 64;
|
||||
int bw = 320;
|
||||
int bh = 56;
|
||||
draw_button(fb, stride_px, (FB_W - bw) / 2, y, bw, bh, COL_INK, COL_INVERSE, "+ Exit", 1);
|
||||
}
|
||||
|
||||
void ota_ui_missing_game(void) {
|
||||
if (!ui_ensure()) return;
|
||||
while (appletMainLoop()) {
|
||||
present(paint_missing, NULL);
|
||||
padUpdate(&g_pad);
|
||||
if (padGetButtonsDown(&g_pad) & HidNpadButton_Plus) break;
|
||||
svcSleepThread(16000000ULL);
|
||||
}
|
||||
ota_ui_shutdown();
|
||||
}
|
||||
|
||||
#else /* host stub */
|
||||
|
||||
int ota_ui_prompt_update(const char *installed, const char *latest) {
|
||||
fprintf(stderr, "[ota_ui] update %s -> %s (host stub: skip)\n", installed ? installed : "?",
|
||||
latest ? latest : "?");
|
||||
return 0;
|
||||
}
|
||||
void ota_ui_show_status(const char *title, const char *detail) {
|
||||
fprintf(stderr, "[ota_ui] %s %s\n", title ? title : "", detail ? detail : "");
|
||||
}
|
||||
void ota_ui_show_progress(const char *title, const char *detail, float progress01) {
|
||||
fprintf(stderr, "[ota_ui] %s %s (%.0f%%)\n", title ? title : "", detail ? detail : "",
|
||||
progress01 * 100.f);
|
||||
}
|
||||
void ota_ui_alert(const char *title, const char *line1, const char *line2) {
|
||||
fprintf(stderr, "[ota_ui] alert: %s / %s / %s\n", title ? title : "", line1 ? line1 : "",
|
||||
line2 ? line2 : "");
|
||||
}
|
||||
void ota_ui_missing_game(void) { fprintf(stderr, "[ota_ui] missing game\n"); }
|
||||
void ota_ui_shutdown(void) {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 8x8 monochrome bitmap fonts for rendering
|
||||
* Author: Daniel Hepper <daniel@hepper.net>
|
||||
*
|
||||
* License: Public Domain
|
||||
*
|
||||
* Based on:
|
||||
* // Summary: font8x8.h
|
||||
* // 8x8 monochrome bitmap fonts for rendering
|
||||
* //
|
||||
* // Author:
|
||||
* // Marcel Sondaar
|
||||
* // International Business Machines (public domain VGA fonts)
|
||||
* //
|
||||
* // License:
|
||||
* // Public Domain
|
||||
*
|
||||
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
|
||||
**/
|
||||
|
||||
// Constant: font8x8_basic
|
||||
// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin)
|
||||
char font8x8_basic[128][8] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
|
||||
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
|
||||
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
|
||||
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
|
||||
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
|
||||
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
|
||||
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
|
||||
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
|
||||
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
|
||||
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
|
||||
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
|
||||
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
|
||||
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
|
||||
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
|
||||
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
|
||||
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
|
||||
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
|
||||
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
|
||||
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
|
||||
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
|
||||
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
|
||||
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
|
||||
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
|
||||
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
|
||||
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
|
||||
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
|
||||
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
|
||||
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
|
||||
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
|
||||
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
|
||||
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
|
||||
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
|
||||
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
|
||||
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
|
||||
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
|
||||
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
|
||||
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
|
||||
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
|
||||
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
|
||||
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
|
||||
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
|
||||
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
|
||||
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
|
||||
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
|
||||
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
|
||||
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
|
||||
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
|
||||
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
|
||||
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
|
||||
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
|
||||
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
|
||||
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
|
||||
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
|
||||
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
|
||||
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
|
||||
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
|
||||
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
|
||||
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
|
||||
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
|
||||
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
|
||||
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
|
||||
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
|
||||
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
|
||||
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
|
||||
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
|
||||
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
|
||||
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
|
||||
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
|
||||
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
|
||||
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
|
||||
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
|
||||
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
|
||||
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
|
||||
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
|
||||
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
|
||||
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
|
||||
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
|
||||
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
|
||||
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
|
||||
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
|
||||
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
|
||||
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
|
||||
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
|
||||
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
|
||||
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
|
||||
};
|
||||
+7988
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user