252 lines
10 KiB
C
252 lines
10 KiB
C
// Eigenes MBMNET-Freundekonto, wie bei Pretendos Nimbus:
|
|
//
|
|
// Freundekonto 1: Nintendo - mit Konto-Platz 1 (Nintendo Network ID) und der Nintendo-Freundesliste
|
|
// Freundekonto 3: MBMNET - mit eigenem Konto-Platz (MBMNET-Konto) und eigener Freundesliste
|
|
//
|
|
// (Freundekonto 2 benutzt Pretendos Nimbus, das bleibt unangetastet.)
|
|
//
|
|
// Umschalten: beide Konten abmelden, das andere laden, seinen Konto-Platz zum Standard machen, Neustart.
|
|
// Beim ersten Mal legt die App das MBMNET-Freundekonto an. Es hat noch keine Kennung; beim ersten
|
|
// Login holt es sich bei MBMNET ein neues Konto (NASC "passwd"), der Server gibt ihm den Freundescode
|
|
// des MBMNET-Kontos von der Webseite.
|
|
#include <3ds.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "frdswitch.h"
|
|
|
|
#define RESULT_LOCAL_ACCOUNT_NOT_EXISTS 0xC880C4ED
|
|
#define LOG_FILE "sdmc:/3ds/mbmnet/konto.log"
|
|
|
|
static bool ready;
|
|
static FILE *logf;
|
|
static const char *failedStep;
|
|
|
|
static Result step(const char *name, Result rc)
|
|
{
|
|
if (logf) fprintf(logf, "%-36s %08lX%s\n", name, (unsigned long)rc, R_FAILED(rc) ? " <- FEHLER" : "");
|
|
if (R_FAILED(rc) && !failedStep) failedStep = name;
|
|
return rc;
|
|
}
|
|
|
|
static void note(const char *name, Result rc)
|
|
{
|
|
if (logf) fprintf(logf, "%-36s %08lX%s\n", name, (unsigned long)rc, R_FAILED(rc) ? " (ignoriert)" : "");
|
|
}
|
|
|
|
Result frdswitch_init(void)
|
|
{
|
|
Result rc = ndmuInit();
|
|
if (R_SUCCEEDED(rc)) rc = frdInit(false); // frd:a (Verwaltung)
|
|
if (R_SUCCEEDED(rc)) rc = actInit(false); // act:a
|
|
// Ohne diese SDK-Version lehnt der Freunde-Dienst Anlegen und Umschalten ab (so macht es Nimbus;
|
|
// daran scheiterte unser erster Versuch mit C960C4F6)
|
|
if (R_SUCCEEDED(rc)) rc = FRD_SetClientSdkVersion(0x70000c8);
|
|
ready = R_SUCCEEDED(rc);
|
|
return rc;
|
|
}
|
|
|
|
void frdswitch_exit(void)
|
|
{
|
|
actExit();
|
|
frdExit();
|
|
ndmuExit();
|
|
}
|
|
|
|
u8 frdswitch_current(void)
|
|
{
|
|
u8 id = 0;
|
|
if (ready) FRD_GetMyLocalAccountId(&id);
|
|
return id;
|
|
}
|
|
|
|
// Konto-Platz (act) zu einem Freundekonto. 0 = keiner.
|
|
static u32 act_slot_of(u8 friendAccount)
|
|
{
|
|
u8 count = 0;
|
|
if (R_FAILED(ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS))) return 0;
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
u8 fp = 0;
|
|
if (R_SUCCEEDED(ACT_GetAccountInfo(&fp, sizeof(fp), slot, INFO_TYPE_FP_LOCAL_ACCOUNT_ID)) && fp == friendAccount) return slot;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool frdswitch_slot_linked(u8 friendAccount, char *id, size_t len)
|
|
{
|
|
id[0] = 0;
|
|
u32 slot = ready ? act_slot_of(friendAccount) : 0;
|
|
if (!slot) return false;
|
|
AccountId acc = { 0 };
|
|
if (R_FAILED(ACT_GetAccountInfo(acc, sizeof(acc), slot, INFO_TYPE_ACCOUNT_ID))) return false;
|
|
snprintf(id, len, "%s", acc);
|
|
return id[0] != 0;
|
|
}
|
|
|
|
// Beide Konten abmelden. Das Freundekonto lässt sich nur offline entladen.
|
|
static Result unload(void)
|
|
{
|
|
note("Konto (act) abmelden", ACTA_UnloadConsoleAccount()); // ohne geladenes Konto: "ungültiger Zustand"
|
|
note("Freunde-Dienst abmelden", FRD_Logout()); // laufende Anmeldung beenden (sonst "ungültiger Zustand")
|
|
|
|
Result rc = step("Offline gehen (exklusiv)", NDMU_EnterExclusiveState(NDM_EXCLUSIVE_STATE_LOCAL_COMMUNICATIONS));
|
|
if (R_FAILED(rc)) return rc;
|
|
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(10 * 1000 * 1000);
|
|
}
|
|
// Hängt der Freunde-Dienst noch an einer Anfrage, kurz wiederholen
|
|
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(100 * 1000 * 1000);
|
|
}
|
|
return step("Freundekonto abmelden (20 s)", rc);
|
|
}
|
|
|
|
// Freundekonto laden. "Schon geladen" (z. B. direkt nach dem Anlegen) zählt als Erfolg.
|
|
static Result load_friend_account(u8 friendAccount)
|
|
{
|
|
Result rc = FRDA_LoadLocalAccount(friendAccount);
|
|
if (R_FAILED(rc) && frdswitch_current() == friendAccount) {
|
|
note("Freundekonto laden (war schon geladen)", rc);
|
|
return 0;
|
|
}
|
|
return step("Freundekonto laden", rc);
|
|
}
|
|
|
|
// Konto-Platz (act) des Freundekontos zum Standard machen; fehlt er, anlegen. *created: neu angelegt.
|
|
static Result use_act_slot(u8 friendAccount, u32 *slotOut, bool *created)
|
|
{
|
|
Result rc = 0;
|
|
u32 slot = act_slot_of(friendAccount);
|
|
*created = false;
|
|
if (!slot) {
|
|
u8 count = 0;
|
|
rc = step("Konto-Plätze zählen", ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS));
|
|
if (R_SUCCEEDED(rc)) rc = step("Konto-Platz anlegen", ACTA_CreateConsoleAccount());
|
|
if (R_FAILED(rc)) return rc;
|
|
slot = count + 1;
|
|
rc = step("Konto-Platz speichern", ACTA_CommitConsoleAccount(slot));
|
|
if (R_FAILED(rc)) return rc;
|
|
*created = true;
|
|
}
|
|
if (logf) fprintf(logf, " -> Konto-Platz %lu\n", (unsigned long)slot);
|
|
*slotOut = slot;
|
|
rc = step("Standard-Konto setzen", ACTA_SetDefaultAccount(slot));
|
|
// Ausdrücklich speichern: Beim Soft-Reboot (seit v2.9.8) ging die Einstellung sonst verloren, dann stand
|
|
// act nach dem Neustart noch auf dem alten Platz (z. B. Pretendo) und meldete sich damit bei MBMNET an.
|
|
if (R_SUCCEEDED(rc)) note("Konto-Einstellungen speichern", ACT_Save());
|
|
return rc;
|
|
}
|
|
|
|
bool frdswitch_to(u8 target, bool *created, char *msg, size_t msglen)
|
|
{
|
|
*created = false;
|
|
if (!ready) {
|
|
snprintf(msg, msglen, "Kontodienste nicht verfügbar.\nIst Luma3DS aktuell?");
|
|
return false;
|
|
}
|
|
mkdir("sdmc:/3ds/mbmnet", 0777);
|
|
logf = fopen(LOG_FILE, "w");
|
|
failedStep = NULL;
|
|
u8 from = frdswitch_current();
|
|
if (logf) fprintf(logf, "MBMNET: Freundekonto %u -> %u (App v%s)\n\n", from, target, APP_VERSION);
|
|
|
|
u32 slot = 0;
|
|
bool newSlot = false;
|
|
Result rc = unload();
|
|
if (R_SUCCEEDED(rc)) {
|
|
rc = FRDA_LoadLocalAccount(target);
|
|
if (rc == (Result)RESULT_LOCAL_ACCOUNT_NOT_EXISTS && target == FRIEND_ACCOUNT_MBMNET) {
|
|
// Erstes Mal: eigenes MBMNET-Freundekonto anlegen (normale Umgebung, die Patches lenken sie um)
|
|
note("Freundekonto laden (gibt es noch nicht)", rc);
|
|
rc = step("MBMNET-Freundekonto anlegen", FRDA_CreateLocalAccount(FRIEND_ACCOUNT_MBMNET, 0, 0, 1));
|
|
if (R_SUCCEEDED(rc)) { *created = true; rc = load_friend_account(target); }
|
|
} else if (R_FAILED(rc) && frdswitch_current() == target) {
|
|
note("Freundekonto laden (war schon geladen)", rc);
|
|
rc = 0;
|
|
} else {
|
|
step("Freundekonto laden", rc);
|
|
}
|
|
if (R_SUCCEEDED(rc)) rc = use_act_slot(target, &slot, &newSlot);
|
|
// Neuer Platz: frisch zurücksetzen, damit "Vorhandene ID verknüpfen" angeboten wird (darf scheitern)
|
|
if (R_SUCCEEDED(rc) && newSlot) note("Konto-Platz zurücksetzen", ACTA_UnbindServerAccount(slot, true));
|
|
}
|
|
|
|
if (R_FAILED(rc)) {
|
|
// Zurück auf das vorherige Freundekonto, damit der 3DS nicht ohne/mit falschem Konto dasteht.
|
|
// Erst entladen - ein geladenes Konto lässt sich nicht einfach ersetzen.
|
|
u8 back = from ? from : FRIEND_ACCOUNT_NINTENDO;
|
|
if (frdswitch_current() != back) {
|
|
note("Rückweg: Freundekonto abmelden", FRDA_UnloadLocalAccount());
|
|
note("Rückweg: altes Freundekonto laden", FRDA_LoadLocalAccount(back));
|
|
}
|
|
NDMU_LeaveExclusiveState();
|
|
}
|
|
if (logf) { fprintf(logf, "\nErgebnis: %s\n", R_SUCCEEDED(rc) ? "OK" : "FEHLER"); fclose(logf); logf = NULL; }
|
|
if (R_FAILED(rc)) {
|
|
snprintf(msg, msglen, "Umschalten fehlgeschlagen bei:\n%s (Fehler %08lX)\nProtokoll: /3ds/mbmnet/konto.log",
|
|
failedStep ? failedStep : "?", (unsigned long)rc);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u32 frdswitch_my_pid(void)
|
|
{
|
|
FriendKey key = { 0 };
|
|
if (!ready || R_FAILED(FRD_GetMyFriendKey(&key))) return 0;
|
|
return key.principalId;
|
|
}
|
|
|
|
bool frdswitch_pid_is_mbmnet(u32 pid)
|
|
{
|
|
return pid >= MBMNET_PID_FIRST && pid <= MBMNET_PID_LAST;
|
|
}
|
|
|
|
// Das MBMNET-Freundekonto neu anlegen (es hat eine Kennung von einem anderen Server, z. B. Pretendo).
|
|
// Der Konto-Platz mit der verknüpften MBMNET-ID bleibt, nur das Freundekonto wird ersetzt. Danach
|
|
// Neustart: Beim ersten Login holt es sich bei MBMNET eine neue Kennung.
|
|
bool frdswitch_recreate_mbmnet(char *msg, size_t msglen)
|
|
{
|
|
if (!ready || frdswitch_current() != FRIEND_ACCOUNT_MBMNET) {
|
|
snprintf(msg, msglen, "MBMNET-Freundekonto ist nicht geladen.");
|
|
return false;
|
|
}
|
|
mkdir("sdmc:/3ds/mbmnet", 0777);
|
|
logf = fopen(LOG_FILE, "w");
|
|
failedStep = NULL;
|
|
if (logf) fprintf(logf, "MBMNET: Freundekonto 3 neu anlegen, alte Kennung %lu (App v%s)\n\n",
|
|
(unsigned long)frdswitch_my_pid(), APP_VERSION);
|
|
|
|
u32 slot = 0;
|
|
bool newSlot = false;
|
|
Result rc = unload();
|
|
if (R_SUCCEEDED(rc)) rc = step("MBMNET-Freundekonto neu anlegen", FRDA_CreateLocalAccount(FRIEND_ACCOUNT_MBMNET, 0, 0, 1));
|
|
if (R_SUCCEEDED(rc)) rc = load_friend_account(FRIEND_ACCOUNT_MBMNET);
|
|
if (R_SUCCEEDED(rc)) rc = use_act_slot(FRIEND_ACCOUNT_MBMNET, &slot, &newSlot);
|
|
if (R_FAILED(rc)) {
|
|
if (frdswitch_current() != FRIEND_ACCOUNT_MBMNET) note("Rückweg: Freundekonto laden", FRDA_LoadLocalAccount(FRIEND_ACCOUNT_MBMNET));
|
|
NDMU_LeaveExclusiveState();
|
|
}
|
|
if (logf) { fprintf(logf, "\nErgebnis: %s\n", R_SUCCEEDED(rc) ? "OK" : "FEHLER"); fclose(logf); logf = NULL; }
|
|
if (R_FAILED(rc)) {
|
|
snprintf(msg, msglen, "Neu anlegen fehlgeschlagen bei:\n%s (Fehler %08lX)\nProtokoll: /3ds/mbmnet/konto.log",
|
|
failedStep ? failedStep : "?", (unsigned long)rc);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u8 frdswitch_slot_of(u8 friendAccount)
|
|
{
|
|
return ready ? (u8)act_slot_of(friendAccount) : 0;
|
|
}
|