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:
co-authored by
Claude Opus 5.5
parent
93316ec2c9
commit
f05fd2fc16
+81
-27
@@ -13,6 +13,7 @@
|
||||
#include "net.h"
|
||||
#include "store.h"
|
||||
|
||||
#define COPY(dst, src) snprintf(dst, sizeof(dst), "%s", src)
|
||||
#define LIST_FILE SHOP_DIR "/list.txt"
|
||||
#define INSTALLED_FILE SHOP_DIR "/installed.txt"
|
||||
#define MAX_SHEETS 8
|
||||
@@ -37,6 +38,35 @@ App *catalog_app(int i) { return &apps[i]; }
|
||||
int catalog_sheet_count(void) { return sheetCount; }
|
||||
const char *catalog_sheet_path(int n) { return n >= 0 && n < sheetCount ? sheetPath[n] : ""; }
|
||||
|
||||
// Bevorzugte Variante, wenn es beide gibt (zuletzt gewählt, Standard CIA)
|
||||
#define KIND_FILE SHOP_DIR "/format.txt"
|
||||
static char preferredKind[6] = "cia";
|
||||
|
||||
static void load_preferred_kind(void)
|
||||
{
|
||||
FILE *f = fopen(KIND_FILE, "r");
|
||||
if (!f) return;
|
||||
char k[8] = "";
|
||||
if (fgets(k, sizeof(k), f) && !strncmp(k, "3dsx", 4)) strcpy(preferredKind, "3dsx");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
bool app_has_both(const App *a) { return a->ciaFile[0] && a->dsxFile[0]; }
|
||||
|
||||
void app_choose(App *a, const char *kind, bool remember)
|
||||
{
|
||||
bool cia = !strcmp(kind, "cia");
|
||||
if (cia ? !a->ciaFile[0] : !a->dsxFile[0]) return;
|
||||
COPY(a->kind, cia ? "cia" : "3dsx");
|
||||
COPY(a->file, cia ? a->ciaFile : a->dsxFile);
|
||||
a->size = cia ? a->ciaSize : a->dsxSize;
|
||||
if (remember) {
|
||||
COPY(preferredKind, a->kind);
|
||||
FILE *f = fopen(KIND_FILE, "w");
|
||||
if (f) { fputs(preferredKind, f); fclose(f); }
|
||||
}
|
||||
}
|
||||
|
||||
AppStatus app_status(const App *a)
|
||||
{
|
||||
if (!a->installed) return APP_NEW;
|
||||
@@ -90,15 +120,14 @@ static int split(char *line, char **fields, int max)
|
||||
return n;
|
||||
}
|
||||
|
||||
#define COPY(dst, src) snprintf(dst, sizeof(dst), "%s", src)
|
||||
|
||||
static bool parse_list(char *text, int *revision)
|
||||
{
|
||||
char *save = NULL, *line = strtok_r(text, "\n", &save);
|
||||
char *f[16];
|
||||
if (!line || split(line, f, 20) < 5 || strcmp(f[0], "MBMNETSHOP") != 0) return false;
|
||||
char *f[24];
|
||||
if (!line || split(line, f, 24) < 5 || strcmp(f[0], "MBMNETSHOP") != 0) return false;
|
||||
int format = atoi(f[1]);
|
||||
int fields = format >= 2 ? 17 : 13;
|
||||
int fields = format >= 3 ? 21 : format >= 2 ? 17 : 13;
|
||||
*revision = atoi(f[2]);
|
||||
int count = atoi(f[3]);
|
||||
sheetCount = atoi(f[4]);
|
||||
@@ -109,7 +138,7 @@ static bool parse_list(char *text, int *revision)
|
||||
|
||||
appCount = 0;
|
||||
while ((line = strtok_r(NULL, "\n", &save)) && appCount < count) {
|
||||
if (split(line, f, 20) < fields) continue;
|
||||
if (split(line, f, 24) < fields) continue;
|
||||
App *a = &apps[appCount++];
|
||||
COPY(a->slug, f[0]);
|
||||
COPY(a->title, f[1]);
|
||||
@@ -129,7 +158,20 @@ static bool parse_list(char *text, int *revision)
|
||||
a->colorBg = f[14][0] == '#' ? strtoul(f[14] + 1, NULL, 16) : 0;
|
||||
COPY(a->license, f[15]);
|
||||
}
|
||||
if (format >= 3) {
|
||||
COPY(a->ciaFile, f[16]);
|
||||
a->ciaSize = strtoul(f[17], NULL, 10);
|
||||
COPY(a->dsxFile, f[18]);
|
||||
a->dsxSize = strtoul(f[19], NULL, 10);
|
||||
} else if (!strcmp(a->kind, "cia")) {
|
||||
COPY(a->ciaFile, a->file);
|
||||
a->ciaSize = a->size;
|
||||
} else {
|
||||
COPY(a->dsxFile, a->file);
|
||||
a->dsxSize = a->size;
|
||||
}
|
||||
a->description = strdup(f[fields - 1]);
|
||||
if (app_has_both(a)) app_choose(a, preferredKind, false);
|
||||
}
|
||||
return appCount > 0;
|
||||
}
|
||||
@@ -167,8 +209,10 @@ static void load_installed(void)
|
||||
}
|
||||
a->installed = true;
|
||||
COPY(a->installedVersion, f[1]);
|
||||
COPY(a->instKind, f[2]);
|
||||
a->titleId = strtoull(f[3], NULL, 16);
|
||||
if (f[4][0]) COPY(a->file, f[4]);
|
||||
COPY(a->instFile, f[4]);
|
||||
app_choose(a, a->instKind, false); // die Seite zeigt die installierte Variante
|
||||
}
|
||||
free(text);
|
||||
}
|
||||
@@ -180,7 +224,7 @@ static void save_installed(void)
|
||||
for (int i = 0; i < appCount; i++) {
|
||||
App *a = &apps[i];
|
||||
if (a->installed)
|
||||
fprintf(f, "%s\t%s\t%s\t%016llX\t%s\n", a->slug, a->installedVersion, a->kind, a->titleId, a->file);
|
||||
fprintf(f, "%s\t%s\t%s\t%016llX\t%s\n", a->slug, a->installedVersion, a->instKind, a->titleId, a->instFile);
|
||||
}
|
||||
if (extraInstalled) fputs(extraInstalled, f);
|
||||
fclose(f);
|
||||
@@ -215,7 +259,8 @@ static void catalog_worker(void *arg)
|
||||
mkdir(SHOP_DIR, 0777);
|
||||
|
||||
snprintf(catMsg, sizeof(catMsg), "Lade den Katalog ...");
|
||||
snprintf(url, sizeof(url), "http://%s/shop/list2", SHOP_SERVER);
|
||||
snprintf(url, sizeof(url), "http://%s/shop/list3", SHOP_SERVER);
|
||||
load_preferred_kind();
|
||||
u32 status = net_get(url, &data, &size, 4 * 1024 * 1024, &rc);
|
||||
char *text = NULL;
|
||||
if (status == 200 && size > 10) {
|
||||
@@ -372,13 +417,32 @@ static bool install_sink(void *p, const u8 *data, u32 len, u32 total)
|
||||
return write_part(in, data, len);
|
||||
}
|
||||
|
||||
// Installierte Variante entfernen (CIA über AM, .3dsx von der SD). Schon von Hand gelöscht zählt als Erfolg.
|
||||
static Result remove_files(const App *a)
|
||||
{
|
||||
if (strcmp(a->instKind, "cia") == 0) {
|
||||
if (!a->titleId) return 0;
|
||||
Result rc = AM_DeleteTitle(MEDIATYPE_SD, a->titleId);
|
||||
AM_TitleEntry info;
|
||||
u64 id = a->titleId;
|
||||
if (R_FAILED(rc) && R_FAILED(AM_GetTitleInfo(MEDIATYPE_SD, 1, &id, &info))) rc = 0;
|
||||
return rc;
|
||||
}
|
||||
if (a->instFile[0]) {
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "sdmc:/3ds/%s", a->instFile);
|
||||
remove(path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void install_worker(App *a)
|
||||
{
|
||||
char url[192];
|
||||
Install in = { .cia = strcmp(a->kind, "cia") == 0 };
|
||||
in.head = malloc(HEAD_SIZE);
|
||||
if (!in.head) { snprintf(jobMsg, sizeof(jobMsg), "Zu wenig Speicher."); jobState = JOB_FAILED; return; }
|
||||
snprintf(url, sizeof(url), "http://%s/shop/get/%s", SHOP_SERVER, a->slug);
|
||||
snprintf(url, sizeof(url), "http://%s/shop/get/%s/%s", SHOP_SERVER, a->slug, a->kind);
|
||||
Result rc = 0;
|
||||
u32 status = net_stream(url, install_sink, &in, &rc);
|
||||
if (status == 200 && !in.started && !in.error) in.error = "Die Datei ist keine gültige App.";
|
||||
@@ -403,9 +467,13 @@ static void install_worker(App *a)
|
||||
|
||||
if (ok) {
|
||||
bool update = a->installed;
|
||||
// Vorher die andere Variante installiert? Die alte wegräumen (nur eine von beiden behalten)
|
||||
if (a->installed && strcmp(a->instKind, a->kind) != 0) remove_files(a);
|
||||
a->installed = true;
|
||||
COPY(a->installedVersion, a->version);
|
||||
if (in.cia) a->titleId = cia_title_id(in.head, in.headLen);
|
||||
COPY(a->instKind, a->kind);
|
||||
COPY(a->instFile, a->file);
|
||||
a->titleId = in.cia ? cia_title_id(in.head, in.headLen) : 0;
|
||||
save_installed();
|
||||
snprintf(jobMsg, sizeof(jobMsg), in.cia ? "%s %s!\nDu findest die App jetzt im HOME-Menü."
|
||||
: "%s %s!\nStarte sie über den Homebrew Launcher (/3ds/%s).",
|
||||
@@ -423,25 +491,11 @@ static void install_worker(App *a)
|
||||
|
||||
static void remove_worker(App *a)
|
||||
{
|
||||
bool ok = true;
|
||||
Result rc = 0;
|
||||
if (strcmp(a->kind, "cia") == 0) {
|
||||
if (a->titleId) {
|
||||
rc = AM_DeleteTitle(MEDIATYPE_SD, a->titleId);
|
||||
// schon von Hand gelöscht? Dann ist es auch gut.
|
||||
AM_TitleEntry info;
|
||||
u64 id = a->titleId;
|
||||
if (R_FAILED(rc) && R_FAILED(AM_GetTitleInfo(MEDIATYPE_SD, 1, &id, &info))) rc = 0;
|
||||
ok = R_SUCCEEDED(rc);
|
||||
}
|
||||
} else {
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "sdmc:/3ds/%s", a->file);
|
||||
remove(path);
|
||||
}
|
||||
Result rc = remove_files(a);
|
||||
bool ok = R_SUCCEEDED(rc);
|
||||
if (ok) {
|
||||
a->installed = false;
|
||||
a->installedVersion[0] = 0;
|
||||
a->installedVersion[0] = a->instKind[0] = a->instFile[0] = 0;
|
||||
a->titleId = 0;
|
||||
save_installed();
|
||||
snprintf(jobMsg, sizeof(jobMsg), "%s entfernt.", a->title);
|
||||
|
||||
Reference in New Issue
Block a user