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:
co-authored by
Claude Opus 5.5
parent
ca77952a9d
commit
1f69ff6c56
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user