v2.9.7: Shop - Auswahl CIA oder 3DSX, Katalog-Leser ohne Pufferüberlauf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nico Meister
2026-10-19 18:40:00 +02:00
co-authored by Claude Opus 5.5
parent 93316ec2c9
commit f05fd2fc16
4 changed files with 126 additions and 34 deletions
+35 -5
View File
@@ -57,6 +57,10 @@ static const char *SORT_NAMES[] = { "nach Name", "beliebteste zuerst", "neueste
typedef struct { float x, y, w, h; } Box;
static const Box backBox = { 6, 8, 30, 46 };
static const Box actionBox = { 12, 70, 296, 40 }, actionBoxNarrow = { 12, 70, 208, 40 }, removeBox = { 226, 70, 82, 40 };
static const Box kindBox = { 206, 116, 102, 26 }; // Umschalter CIA | 3DSX (links / rechts)
// Ist die gewählte Variante (CIA/3DSX) eine andere als die installierte?
static bool other_variant(const App *a) { return a->installed && strcmp(a->kind, a->instKind) != 0; }
static C2D_TextBuf textBuf, measureBuf; // measureBuf nur zum Messen (wird jedes Mal geleert)
static C2D_SpriteSheet sheets[8];
@@ -359,7 +363,8 @@ static void draw_top_detail(const App *a, int shot)
{
draw_hero(a, a->shots ? shot : -1);
char right[48];
snprintf(right, sizeof(right), a->shots > 1 ? "L / R: Bilder B: Zurück" : "B: Zurück");
snprintf(right, sizeof(right), "%s%sB: Zurück", a->shots > 1 ? "L/R: Bilder " : "",
app_has_both(a) ? "X: CIA/3DSX " : "");
draw_top_bar(a->title, right);
// Punkte für die Screenshots
if (a->shots > 1) {
@@ -446,8 +451,11 @@ static void draw_bottom_detail(const App *a, int armed, int descScroll)
AppStatus s = app_status(a);
bool running = job_state() == JOB_RUNNING;
Box b = s == APP_NEW ? actionBox : actionBoxNarrow;
const char *label = s == APP_NEW ? "Kostenlos herunterladen" : s == APP_UPDATE ? "Aktualisieren"
: strcmp(a->kind, "cia") == 0 ? "Öffnen" : "Neu laden";
bool switchKind = other_variant(a);
const char *label = s == APP_NEW ? "Kostenlos herunterladen"
: switchKind ? (strcmp(a->kind, "cia") == 0 ? "Als CIA installieren" : "Als 3DSX installieren")
: s == APP_UPDATE ? "Aktualisieren" : strcmp(a->kind, "cia") == 0 ? "Öffnen" : "Neu laden";
if (switchKind) s = APP_UPDATE; // wie ein Update: roter Knopf, Bestätigung
u32 col = s == APP_INSTALLED ? COL_GREEN : COL_RED;
if (armed == 0) { label = s == APP_NEW ? "Nochmal tippen: Laden" : "Nochmal tippen"; col = COL_AMBER; }
char progress[48];
@@ -473,13 +481,25 @@ static void draw_bottom_detail(const App *a, int armed, int descScroll)
// Infos
char size[16];
size_text(size, sizeof(size), a->size);
snprintf(line, sizeof(line), "%s · %s · %s", a->version[0] ? a->version : "ohne Version", size, category_name(a->category));
char info1[96];
snprintf(info1, sizeof(info1), "%s · %s · %s", a->version[0] ? a->version : "ohne Version", size, category_name(a->category));
bool both = app_has_both(a);
fit(line, sizeof(line), info1, 0.42f, both ? kindBox.x - 22 : BOT_W - 28);
text(line, 14, 118, 0.42f, COL_WHITE, 0);
// Umschalter CIA | 3DSX (nur wenn es beide gibt)
if (both) {
Box k = kindBox;
bool cia = strcmp(a->kind, "cia") == 0;
round_rect(k.x, k.y, k.w, k.h, k.h / 2, COL_CARD);
round_rect(cia ? k.x + 2 : k.x + k.w / 2, k.y + 2, k.w / 2 - 2, k.h - 4, (k.h - 4) / 2, COL_RED);
text("CIA", k.x + k.w / 4, k.y + 5, 0.42f, cia ? COL_WHITE : COL_MUTED, C2D_AlignCenter);
text("3DSX", k.x + k.w * 3 / 4, k.y + 5, 0.42f, cia ? COL_MUTED : COL_WHITE, C2D_AlignCenter);
}
char info2[128];
snprintf(info2, sizeof(info2), "%s%s%s%s", strcmp(a->kind, "cia") == 0 ? "Im HOME-Menü" : "Im Homebrew Launcher",
a->updated[0] ? " · aktualisiert " : "", a->updated, a->license[0] ? " · " : "");
if (a->license[0]) snprintf(info2 + strlen(info2), sizeof(info2) - strlen(info2), "%s", a->license);
fit(line, sizeof(line), info2, 0.38f, BOT_W - 28);
fit(line, sizeof(line), info2, 0.38f, both ? kindBox.x - 22 : BOT_W - 28);
text(line, 14, 134, 0.38f, COL_MUTED, 0);
C2D_DrawRectSolid(12, 150, 0.5f, BOT_W - 24, 1, COL_CARD_HI);
@@ -654,11 +674,17 @@ int main(int argc, char **argv)
} else if (ready && a) {
// --- App-Seite ---
AppStatus s = app_status(a);
bool switchKind = other_variant(a);
if (switchKind) s = APP_UPDATE; // andere Variante gewählt: Knopf installiert sie
bool hit = false, hitRemove = false;
if (down & KEY_TOUCH) {
hidTouchRead(&tp);
touchStartY = tp.py; touchStartScroll = descScroll; dragged = false;
if (inside(backBox, tp)) down |= KEY_B;
else if (app_has_both(a) && !busy && inside(kindBox, tp)) {
app_choose(a, tp.px < kindBox.x + kindBox.w / 2 ? "cia" : "3dsx", true);
armed = -1;
}
else if (inside(s == APP_NEW ? actionBox : actionBoxNarrow, tp)) hit = true;
else if (s != APP_NEW && inside(removeBox, tp)) hitRemove = true;
} else if (held & KEY_TOUCH && touchStartY >= DESC_Y - 10) {
@@ -674,6 +700,10 @@ int main(int argc, char **argv)
if (a->shots > 1 && down & KEY_R) shot = (shot + 1) % a->shots;
if (a->shots > 1 && down & KEY_L) shot = (shot + a->shots - 1) % a->shots;
if (down & KEY_X && app_has_both(a) && !busy) {
app_choose(a, strcmp(a->kind, "cia") == 0 ? "3dsx" : "cia", true);
armed = -1;
}
if (down & KEY_B && !busy) { detail = false; armed = -1; }
else if (!busy && (down & KEY_A || hit)) {
if (s == APP_INSTALLED && strcmp(a->kind, "cia") == 0) {