v2.9.1: Shop im eShop-Stil mit Screenshots und Suche, GodMode9-Skript zum Installieren

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nico Meister
2026-10-18 18:31:41 +02:00
co-authored by Claude Opus 5.5
parent ca77952a9d
commit 1f69ff6c56
8 changed files with 687 additions and 194 deletions
+170
View File
@@ -0,0 +1,170 @@
// Vorschaubilder im Hintergrund laden (immer nur das zuletzt gewünschte, ältere Wünsche verfallen).
#include <3ds.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "net.h"
#include "pics.h"
#include "store.h"
#define SLOTS 8
#define TEX_BYTES (512 * 256 * 2)
typedef struct {
char key[96];
PicState state;
C3D_Tex tex;
Tex3DS_SubTexture sub;
bool hasTex;
u32 used;
} Pic;
static Pic slots[SLOTS];
static u32 tick;
// Austausch mit dem Lade-Thread
static LightLock lock;
static LightEvent wake;
static char wantKey[96], wantUrl[192];
static char busyKey[96]; // lädt gerade
static char doneKey[96];
static u8 *doneBuf;
static bool doneOk, hasDone, quit;
static Thread thread;
static void worker(void *arg)
{
(void)arg;
char key[96], url[192];
while (true) {
LightEvent_Wait(&wake);
LightLock_Lock(&lock);
if (quit) { LightLock_Unlock(&lock); return; }
snprintf(key, sizeof(key), "%s", wantKey);
snprintf(url, sizeof(url), "%s", wantUrl);
wantKey[0] = 0;
snprintf(busyKey, sizeof(busyKey), "%s", key);
LightLock_Unlock(&lock);
if (!key[0]) continue;
u8 *data = NULL;
u32 size = 0;
Result rc;
u32 status = net_get(url, &data, &size, TEX_BYTES, &rc);
bool ok = status == 200 && size == TEX_BYTES;
if (!ok) { free(data); data = NULL; }
LightLock_Lock(&lock);
free(doneBuf); // nicht abgeholt (sollte nicht vorkommen)
snprintf(doneKey, sizeof(doneKey), "%s", key);
busyKey[0] = 0;
doneBuf = data;
// Netzwerkfehler (status 0): nicht als "kein Bild" merken, später nochmal versuchen
doneOk = ok || status != 0;
hasDone = true;
LightLock_Unlock(&lock);
}
}
void pics_init(void)
{
LightLock_Init(&lock);
LightEvent_Init(&wake, RESET_ONESHOT);
s32 prio = 0x30;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
thread = threadCreate(worker, NULL, 16 * 1024, prio + 1, -2, false);
}
void pics_exit(void)
{
if (thread) {
LightLock_Lock(&lock);
quit = true;
LightLock_Unlock(&lock);
LightEvent_Signal(&wake);
threadJoin(thread, U64_MAX);
threadFree(thread);
}
for (int i = 0; i < SLOTS; i++) if (slots[i].hasTex) C3D_TexDelete(&slots[i].tex);
free(doneBuf);
}
static Pic *find(const char *key)
{
for (int i = 0; i < SLOTS; i++) if (slots[i].key[0] && strcmp(slots[i].key, key) == 0) return &slots[i];
return NULL;
}
// Platz für ein neues Bild: frei oder am längsten nicht benutzt (nie das, was gerade lädt)
static Pic *take_slot(void)
{
Pic *best = NULL;
LightLock_Lock(&lock);
for (int i = 0; i < SLOTS; i++) {
Pic *p = &slots[i];
if (!p->key[0]) { best = p; break; }
if (!strcmp(p->key, busyKey) || !strcmp(p->key, wantKey) || (hasDone && !strcmp(p->key, doneKey))) continue;
if (!best || p->used < best->used) best = p;
}
LightLock_Unlock(&lock);
if (best && best->hasTex) { C3D_TexDelete(&best->tex); best->hasTex = false; }
if (best) best->key[0] = 0;
return best;
}
void pics_poll(void)
{
tick++;
LightLock_Lock(&lock);
if (!hasDone) { LightLock_Unlock(&lock); return; }
char key[96];
snprintf(key, sizeof(key), "%s", doneKey);
u8 *buf = doneBuf;
bool ok = doneOk;
doneBuf = NULL;
hasDone = false;
LightLock_Unlock(&lock);
Pic *p = find(key);
if (p && buf && C3D_TexInit(&p->tex, 512, 256, GPU_RGB565)) {
memcpy(p->tex.data, buf, TEX_BYTES);
C3D_TexFlush(&p->tex);
C3D_TexSetFilter(&p->tex, GPU_LINEAR, GPU_LINEAR);
p->sub = (Tex3DS_SubTexture){ 400, 240, 0.0f, 1.0f, 400.0f / 512.0f, 1.0f - 240.0f / 256.0f };
p->hasTex = true;
p->state = PIC_READY;
} else if (p) {
if (ok) p->state = PIC_MISSING;
else p->key[0] = 0; // Netzwerkfehler: beim nächsten Mal neu versuchen
}
free(buf);
}
PicState pics_get(const char *slug, int which, C2D_Image *out)
{
char key[96];
snprintf(key, sizeof(key), "%s/%d", slug, which);
Pic *p = find(key);
if (p) {
p->used = tick;
if (p->state == PIC_READY) { out->tex = &p->tex; out->subtex = &p->sub; }
// Lädt angeblich, aber der Thread hat inzwischen ein anderes Bild gewollt: nochmal anfordern
if (p->state != PIC_LOADING) return p->state;
} else {
p = take_slot();
if (!p) return PIC_LOADING;
snprintf(p->key, sizeof(p->key), "%s", key);
p->state = PIC_LOADING;
p->used = tick;
}
LightLock_Lock(&lock);
if (strcmp(wantKey, key) != 0 && strcmp(busyKey, key) != 0) {
snprintf(wantKey, sizeof(wantKey), "%s", key);
if (which == PIC_BANNER) snprintf(wantUrl, sizeof(wantUrl), "http://%s/shop/banner/%s", SHOP_SERVER, slug);
else snprintf(wantUrl, sizeof(wantUrl), "http://%s/shop/shot/%s/%d", SHOP_SERVER, slug, which);
LightEvent_Signal(&wake);
}
LightLock_Unlock(&lock);
return PIC_LOADING;
}