// Vorschaubilder im Hintergrund laden (immer nur das zuletzt gewünschte, ältere Wünsche verfallen). #include <3ds.h> #include #include #include #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; }