v3.0.0: Umschalten wartet nicht mehr aufs Auffrischen (abbrechbar, CA-Abruf zuletzt), schnellere Prüfintervalle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c384c20558
commit
f5fc0861c7
@@ -17,7 +17,7 @@ SOURCES := source
|
||||
INCLUDES := source
|
||||
|
||||
# Bei jedem Release erhöhen - das Auto-Update vergleicht diese Nummer
|
||||
VERSION := 2.9.9
|
||||
VERSION := 3.0.0
|
||||
|
||||
APP_TITLE := MBMNET
|
||||
APP_DESCRIPTION := Dein eigenes 3DS-Netzwerk
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ bool admin_is(void) { return isAdmin; }
|
||||
bool admin_finish(void)
|
||||
{
|
||||
if (!thread) return true;
|
||||
if (R_FAILED(threadJoin(thread, 300ULL * 1000 * 1000))) return false; // läuft noch
|
||||
if (R_FAILED(threadJoin(thread, 100ULL * 1000 * 1000))) return false; // läuft noch
|
||||
threadFree(thread);
|
||||
thread = NULL;
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
void admin_start(const char *server, u32 pid);
|
||||
// Admin? (gemerkte Antwort, bis der Server geantwortet hat)
|
||||
bool admin_is(void);
|
||||
bool admin_finish(void); // wartet höchstens 0,3 s, false = läuft noch
|
||||
bool admin_finish(void); // wartet höchstens 0,1 s, false = läuft noch
|
||||
|
||||
+5
-5
@@ -92,20 +92,20 @@ static Result unload(void)
|
||||
|
||||
Result rc = step("Offline gehen (exklusiv)", NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_LOCAL_COMMUNICATIONS));
|
||||
if (R_FAILED(rc)) return rc;
|
||||
for (int i = 0; i < 150; i++) { // höchstens 15 Sekunden
|
||||
for (int i = 0; i < 1500; i++) { // höchstens 15 Sekunden, alle 10 ms nachsehen
|
||||
bool online = true;
|
||||
if (R_FAILED(FRD_IsOnline(&online)) || !online) break;
|
||||
svcSleepThread(100 * 1000 * 1000);
|
||||
svcSleepThread(10 * 1000 * 1000);
|
||||
}
|
||||
// Hängt der Freunde-Dienst noch an einer Anfrage, kurz wiederholen
|
||||
for (int attempt = 1; attempt <= 40; attempt++) {
|
||||
if (attempt == 21) note("Freunde-Dienst erneut abmelden", FRD_Logout());
|
||||
for (int attempt = 1; attempt <= 200; attempt++) { // alle 100 ms, höchstens 20 s
|
||||
if (attempt == 100) note("Freunde-Dienst erneut abmelden", FRD_Logout());
|
||||
rc = FRDA_UnloadLocalAccount();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (logf) fprintf(logf, "%-36s %08lX (Versuch %d)\n", "Freundekonto abmelden", (unsigned long)rc, attempt);
|
||||
return rc;
|
||||
}
|
||||
svcSleepThread(500 * 1000 * 1000);
|
||||
svcSleepThread(100 * 1000 * 1000);
|
||||
}
|
||||
return step("Freundekonto abmelden (20 s)", rc);
|
||||
}
|
||||
|
||||
+57
-17
@@ -18,6 +18,7 @@
|
||||
#include "ftpctl.h"
|
||||
#include "spotpass.h"
|
||||
#include "shopctl.h"
|
||||
#include "olvpatch.h"
|
||||
|
||||
#define TOP_W 400
|
||||
#define BOT_W 320
|
||||
@@ -271,32 +272,65 @@ static void draw_bottom(int selected, PatchState st, bool admin)
|
||||
#define UNLINKED_MARKER "sdmc:/3ds/mbmnet/nnid-geloest"
|
||||
static bool old_mbmnet_link(void) { FILE *f = fopen(UNLINKED_MARKER, "r"); if (f) fclose(f); return f != NULL; }
|
||||
|
||||
// Auffrischen beim Start (SpotPass anmelden, Patches neu bauen) läuft im Hintergrund: Dabei holt die App
|
||||
// das Miiverse-Zertifikat vom Server, und solange das WLAN noch verbindet, dauert das Sekunden.
|
||||
static volatile bool maintaining;
|
||||
// Auffrischen beim Start läuft im Hintergrund, in Schritten. Tippt man auf Umschalten, bricht es ab, wo es kann:
|
||||
// Das Umschalten wartet nur, solange Patch-Dateien geschrieben werden oder der Konto-Dienst beschäftigt ist.
|
||||
static volatile bool maintaining; // Thread läuft noch (evtl. nur noch das Zertifikat vom Server holen)
|
||||
static volatile bool maintenanceBusy; // schreibt Patches / gleicht das Konto ab: Umschalten muss warten
|
||||
static volatile bool switchRequested; // jemand will umschalten: nichts Neues mehr anfangen
|
||||
static volatile bool maintenanceChanged;
|
||||
static Thread maintenanceThread;
|
||||
|
||||
// Erst "beschäftigt" melden, dann nachsehen, ob umgeschaltet werden soll (sonst könnten beide gleichzeitig schreiben)
|
||||
static bool begin_busy(void)
|
||||
{
|
||||
maintenanceBusy = true;
|
||||
if (!switchRequested) return true;
|
||||
maintenanceBusy = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool refresh_patches(void)
|
||||
{
|
||||
char detail[160];
|
||||
bool changed = false;
|
||||
if (!begin_busy()) return false;
|
||||
if (patch_enable(MBMNET_SERVER, &changed, detail, sizeof(detail)) && changed) maintenanceChanged = true;
|
||||
maintenanceBusy = false;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void maintenance_worker(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
char ignore[96], detail[160];
|
||||
char ignore[96];
|
||||
// 1. Patches auffrischen (Miiverse-CA aus dem Zwischenspeicher; nur beim allerersten Mal vom Server)
|
||||
bool hadCa = olvpatch_ca_cached();
|
||||
refresh_patches();
|
||||
// 2. SpotPass-Aufgabe anmelden
|
||||
if (begin_busy()) {
|
||||
spotpass_register(MBMNET_SERVER, ignore, sizeof(ignore));
|
||||
// Konto mit dem MBMNET-Server abgleichen: holt u. a. den Mii, den man auf der Webseite eingestellt hat
|
||||
maintenanceBusy = false;
|
||||
}
|
||||
// 3. Konto mit dem MBMNET-Server abgleichen (holt u. a. den Mii von der Webseite). Höchstens 5 s,
|
||||
// bei einem Umschalt-Wunsch sofort abbrechen.
|
||||
u8 slot = frdswitch_slot_of(FRIEND_ACCOUNT_MBMNET);
|
||||
char id[20];
|
||||
if (slot && frdswitch_slot_linked(FRIEND_ACCOUNT_MBMNET, id, sizeof(id))) {
|
||||
Handle done;
|
||||
if (R_SUCCEEDED(svcCreateEvent(&done, RESET_ONESHOT))) {
|
||||
if (R_SUCCEEDED(ACTA_SyncAccountInfo(slot, done))) svcWaitSynchronization(done, 15ULL * 1000 * 1000 * 1000);
|
||||
if (slot && frdswitch_slot_linked(FRIEND_ACCOUNT_MBMNET, id, sizeof(id)) && begin_busy()
|
||||
&& R_SUCCEEDED(svcCreateEvent(&done, RESET_ONESHOT))) {
|
||||
if (R_SUCCEEDED(ACTA_SyncAccountInfo(slot, done))) {
|
||||
// Warten in 100-ms-Schritten (eine Zeitüberschreitung ist kein Fehlercode, nur != 0)
|
||||
for (int i = 0; i < 50 && svcWaitSynchronization(done, 100ULL * 1000 * 1000) != 0; i++) {
|
||||
if (switchRequested) { ACT_Cancel(); svcWaitSynchronization(done, 2ULL * 1000 * 1000 * 1000); break; }
|
||||
}
|
||||
}
|
||||
svcCloseHandle(done);
|
||||
}
|
||||
}
|
||||
bool changed = false;
|
||||
patchFreshCa = true; // im Hintergrund darf es dauern: Miiverse-CA frisch vom Server
|
||||
// Patches auffrischen: neue Versionen bringen neue Ziele mit (z. B. Miiverse ab v2.8.0)
|
||||
if (patch_enable(MBMNET_SERVER, &changed, detail, sizeof(detail)) && changed) maintenanceChanged = true;
|
||||
patchFreshCa = false;
|
||||
maintenanceBusy = false;
|
||||
// 4. Miiverse-CA frisch vom Server (nur Netz; Patches nur neu bauen, wenn sie sich geändert hat)
|
||||
bool caChanged = false;
|
||||
if (!switchRequested && olvpatch_refresh_ca(MBMNET_SERVER, &caChanged) && caChanged && hadCa)
|
||||
refresh_patches(); // prüft selbst noch einmal, ob umgeschaltet werden soll
|
||||
maintaining = false;
|
||||
}
|
||||
|
||||
@@ -621,10 +655,14 @@ int main(int argc, char **argv)
|
||||
// Umschalten & Co. warten, bis das Auffrischen im Hintergrund durch ist (meist nur einen Moment)
|
||||
bool waitForMaintenance = maintaining && queued >= 0 && queued != BTN_FTP && queued != BTN_SPOTPASS
|
||||
&& queued != BTN_SHOP && queued != BTN_SHOP_DEL;
|
||||
if (waitForMaintenance) {
|
||||
switchRequested = true; // Auffrischen: nichts Neues mehr anfangen
|
||||
waitForMaintenance = maintenanceBusy; // nur warten, solange es Dateien schreibt / das Konto abgleicht
|
||||
}
|
||||
if (queued >= 0 && !waitForMaintenance) {
|
||||
int a = queued;
|
||||
queued = -1;
|
||||
if (run(a, msg, sizeof(msg))) rebootIn = 45; // ca. 0,75 Sekunden zum Lesen
|
||||
if (run(a, msg, sizeof(msg))) rebootIn = 15; // ca. 0,25 Sekunden zum Lesen
|
||||
st = patch_state();
|
||||
} else if (action >= 0 && rebootIn == 0 && queued < 0) {
|
||||
queued = action;
|
||||
@@ -695,8 +733,10 @@ int main(int argc, char **argv)
|
||||
// ist es ohnehin schon fertig, Aktionen warten darauf).
|
||||
bool clean = true;
|
||||
if (maintenanceThread) {
|
||||
if (R_SUCCEEDED(threadJoin(maintenanceThread, 20ULL * 1000 * 1000 * 1000))) threadFree(maintenanceThread);
|
||||
else clean = false;
|
||||
switchRequested = true;
|
||||
for (int i = 0; i < 2000 && maintenanceBusy; i++) svcSleepThread(10ULL * 1000 * 1000); // höchstens 20 s
|
||||
if (R_SUCCEEDED(threadJoin(maintenanceThread, 100ULL * 1000 * 1000))) threadFree(maintenanceThread);
|
||||
else clean = false; // holt nur noch das Zertifikat: hart beenden ist ungefährlich (atomar geschrieben)
|
||||
}
|
||||
clean = admin_finish() && clean;
|
||||
clean = update_finish() && clean;
|
||||
|
||||
+37
-12
@@ -64,26 +64,51 @@ static u32 read_cached(u8 *der)
|
||||
return n > 0 && n <= CA_MAX ? n : 0;
|
||||
}
|
||||
|
||||
bool olvpatch_fetch_ca(const char *server, u8 *der, u32 *derLen, bool fresh)
|
||||
// Erst in eine Hilfsdatei schreiben, dann umbenennen: Wird die App mittendrin beendet, bleibt die alte Datei heil
|
||||
static bool write_atomic(const char *path, const u8 *data, u32 n)
|
||||
{
|
||||
if (!fresh && (*derLen = read_cached(der))) return true;
|
||||
static u8 pem[2048];
|
||||
u32 n = fetch(server, "/olv/ca.der", der, CA_MAX + 1);
|
||||
if (n == 0 || n > CA_MAX) return !fresh ? false : (*derLen = read_cached(der)) != 0;
|
||||
char tmp[96];
|
||||
snprintf(tmp, sizeof(tmp), "%s.neu", path);
|
||||
FILE *f = fopen(tmp, "wb");
|
||||
bool ok = f && fwrite(data, 1, n, f) == n;
|
||||
if (f) fclose(f);
|
||||
if (ok) { remove(path); ok = rename(tmp, path) == 0; }
|
||||
if (!ok) remove(tmp);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Holt die CA vom Server in den Zwischenspeicher. *changed: anders als vorher.
|
||||
bool olvpatch_refresh_ca(const char *server, bool *changed)
|
||||
{
|
||||
static u8 der[CA_MAX + 1], old[CA_MAX + 1], pem[2048];
|
||||
*changed = false;
|
||||
u32 n = fetch(server, "/olv/ca.der", der, sizeof(der));
|
||||
if (n == 0 || n > CA_MAX) return false;
|
||||
u32 p = fetch(server, "/olv/ca.pem", pem, sizeof(pem));
|
||||
if (p == 0) return false;
|
||||
// Der Browser der Miiverse-App liest die CA von der SD-Karte
|
||||
FILE *f = fopen(CA_PEM_FILE, "wb");
|
||||
bool ok = f && fwrite(pem, 1, p, f) == p;
|
||||
if (f) fclose(f);
|
||||
u32 oldLen = read_cached(old);
|
||||
if (oldLen == n && memcmp(old, der, n) == 0) return true; // unverändert
|
||||
mkdir("sdmc:/3ds/mbmnet", 0777);
|
||||
mkdir("sdmc:/3ds/mbmnet/cache", 0777);
|
||||
f = fopen(CA_DER_CACHE, "wb");
|
||||
if (f) { fwrite(der, 1, n, f); fclose(f); }
|
||||
*derLen = n;
|
||||
// Der Browser der Miiverse-App liest die CA (PEM) von der SD-Karte
|
||||
bool ok = write_atomic(CA_PEM_FILE, pem, p) && write_atomic(CA_DER_CACHE, der, n);
|
||||
*changed = ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool olvpatch_fetch_ca(const char *server, u8 *der, u32 *derLen)
|
||||
{
|
||||
if ((*derLen = read_cached(der))) return true;
|
||||
bool changed;
|
||||
return olvpatch_refresh_ca(server, &changed) && (*derLen = read_cached(der)) != 0;
|
||||
}
|
||||
|
||||
bool olvpatch_ca_cached(void)
|
||||
{
|
||||
static u8 der[CA_MAX + 1];
|
||||
return read_cached(der) != 0;
|
||||
}
|
||||
|
||||
bool olvpatch_supported(const u8 *code, u32 size)
|
||||
{
|
||||
u32 off = DISCOVERY_ADDR - CODE_BASE;
|
||||
|
||||
+5
-3
@@ -3,9 +3,11 @@
|
||||
|
||||
// Miiverse-Patch (siehe olvpatch.c). Die CA kommt vom MBMNET-Server (/olv/ca.der, /olv/ca.pem).
|
||||
|
||||
// Holt die MBMNET-CA (DER, höchstens 889 Bytes) und legt die PEM-Fassung für den Browser auf die SD.
|
||||
// fresh = false: erst den Zwischenspeicher auf der SD versuchen (schnell), sonst vom Server holen
|
||||
bool olvpatch_fetch_ca(const char *server, u8 *der, u32 *derLen, bool fresh);
|
||||
// CA aus dem Zwischenspeicher auf der SD (schnell); ist keiner da, einmal vom Server holen
|
||||
bool olvpatch_fetch_ca(const char *server, u8 *der, u32 *derLen);
|
||||
// CA frisch vom Server in den Zwischenspeicher. *changed: hat sich geändert (dann Patches neu bauen)
|
||||
bool olvpatch_refresh_ca(const char *server, bool *changed);
|
||||
bool olvpatch_ca_cached(void);
|
||||
// Ist das genau die Miiverse-Version, für die der Patch gebaut ist? (entpackter .code)
|
||||
bool olvpatch_supported(const u8 *code, u32 size);
|
||||
// Baut den IPS-Patch. Gibt die Länge zurück, 0 bei Fehler.
|
||||
|
||||
+2
-6
@@ -414,17 +414,13 @@ static bool write_ips(const char *path, const u8 *data, u32 n, bool *changed)
|
||||
|
||||
// Miiverse-Patch (olvpatch.c): eigene Code-Änderungen + MBMNET-CA statt einfacher Text-Regeln.
|
||||
// 0 = nicht installiert, andere Version oder CA nicht vom Server zu holen (dann bleibt ein alter Patch stehen).
|
||||
bool patchFreshCa; // CA frisch vom Server holen (Auffrischen im Hintergrund), sonst aus dem Zwischenspeicher
|
||||
|
||||
static u32 build_miiverse(const Target *t, const char *server, u8 *out, u32 cap)
|
||||
{
|
||||
static u8 der[1024];
|
||||
static u32 derLen;
|
||||
static int caState; // 0 = noch nicht geholt, 1 = da, -1 = nicht zu holen
|
||||
u32 derLen = 0;
|
||||
char fp[64];
|
||||
fingerprint(t->tid, fp, sizeof(fp)); // leer = kein Zwischenspeicher, gebaut wird trotzdem
|
||||
if (caState == 0) caState = olvpatch_fetch_ca(server, der, &derLen, patchFreshCa) ? 1 : -1;
|
||||
if (caState < 0) return 0;
|
||||
if (!olvpatch_fetch_ca(server, der, &derLen)) return 0; // aus dem Zwischenspeicher (aktualisiert das Auffrischen)
|
||||
|
||||
u32 sum = 2166136261u; // FNV-1a über die CA: neue CA -> neuer Patch
|
||||
for (u32 i = 0; i < derLen; i++) sum = (sum ^ der[i]) * 16777619u;
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
// Offizieller MBMNET-Server. Über "Server-Adresse" lässt sich ein anderer eintragen (z. B. zum Testen).
|
||||
// Fester MBMNET-Server: einfaches HTTP über den NetBird-TCP-Service auf Port 81.
|
||||
// Höchstens 21 Zeichen passen in den Patch.
|
||||
// true: patch_enable holt die Miiverse-CA frisch vom Server (sonst aus dem Zwischenspeicher auf der SD)
|
||||
extern bool patchFreshCa;
|
||||
|
||||
#define MBMNET_SERVER "mbmnetapi.mbm.wtf:81"
|
||||
|
||||
#define APP_DIR "sdmc:/3ds/mbmnet"
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ bool update_finish(void)
|
||||
if (!thread) return true;
|
||||
// Läuft die Abfrage noch, nicht freigeben (sonst schreibt der Thread in freigegebenen Speicher).
|
||||
// false = läuft noch, die App muss sich hart beenden (main.c).
|
||||
if (R_FAILED(threadJoin(thread, 300ULL * 1000 * 1000))) return false;
|
||||
if (R_FAILED(threadJoin(thread, 100ULL * 1000 * 1000))) return false;
|
||||
threadFree(thread);
|
||||
thread = NULL;
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ void update_download(void);
|
||||
bool update_pending(void);
|
||||
UpdateState update_state(void);
|
||||
const char *update_new_version(void);
|
||||
// Wartet auf den Hintergrund-Thread (höchstens 0,3 s). false = läuft noch.
|
||||
// Wartet auf den Hintergrund-Thread (höchstens 0,1 s). false = läuft noch.
|
||||
bool update_finish(void);
|
||||
// Laufender Download-Versuch (1-3)
|
||||
int update_attempt(void);
|
||||
|
||||
Reference in New Issue
Block a user