237 lines
9.3 KiB
C
237 lines
9.3 KiB
C
// Nintendo Network ID auf dem 3DS: anzeigen und lokal lösen.
|
|
//
|
|
// Solange eine NNID verknüpft ist, bieten die Systemeinstellungen kein
|
|
// "Vorhandene ID verknüpfen" an. Damit man sich mit dem MBMNET-Konto anmelden kann,
|
|
// löst die App die Verknüpfung lokal (ohne Server). Name und PID der alten ID werden
|
|
// vorher in /3ds/mbmnet/alte-nnid.txt gesichert.
|
|
#include <3ds.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
|
|
#include "nnid.h"
|
|
|
|
#define OLD_NNID_FILE "sdmc:/3ds/mbmnet/alte-nnid.txt"
|
|
#define LOG_FILE "sdmc:/3ds/mbmnet/konto.log"
|
|
|
|
static bool ready;
|
|
|
|
Result nnid_init(void)
|
|
{
|
|
Result rc = actInit(false); // false = act:a (Verwaltung)
|
|
ready = R_SUCCEEDED(rc);
|
|
return rc;
|
|
}
|
|
|
|
void nnid_exit(void)
|
|
{
|
|
if (ready) actExit();
|
|
}
|
|
|
|
static u8 current_slot(void)
|
|
{
|
|
u8 slot = 0;
|
|
ACT_GetCommonInfo(&slot, sizeof(slot), INFO_TYPE_COMMON_CURRENT_ACCOUNT_SLOT);
|
|
return slot;
|
|
}
|
|
|
|
bool nnid_linked(char *name, size_t len)
|
|
{
|
|
name[0] = 0;
|
|
if (!ready) return false;
|
|
AccountId id = { 0 };
|
|
if (R_FAILED(ACT_GetAccountInfo(id, sizeof(id), current_slot(), INFO_TYPE_ACCOUNT_ID))) return false;
|
|
snprintf(name, len, "%s", id);
|
|
return name[0] != 0;
|
|
}
|
|
|
|
u32 nnid_pid(void)
|
|
{
|
|
u32 pid = 0;
|
|
char name[20];
|
|
if (nnid_linked(name, sizeof(name))) ACT_GetAccountInfo(&pid, sizeof(pid), current_slot(), INFO_TYPE_PRINCIPAL_ID);
|
|
return pid;
|
|
}
|
|
|
|
// Verknüpfte IDs in ANDEREN Konto-Plätzen (z. B. die Pretendo-ID aus Nimbus' zweitem Platz).
|
|
// Eine ID, die dort steht, darf der 3DS nicht noch einmal verknüpfen (Fehler 022-2571).
|
|
int nnid_others(char *names, size_t len)
|
|
{
|
|
names[0] = 0;
|
|
if (!ready) return 0;
|
|
u8 count = 0, current = current_slot();
|
|
if (R_FAILED(ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS))) return 0;
|
|
int found = 0;
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
if (slot == current) continue;
|
|
AccountId id = { 0 };
|
|
if (R_FAILED(ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID)) || !id[0]) continue;
|
|
size_t n = strlen(names);
|
|
snprintf(names + n, len - n, "%s\"%s\"", found ? ", " : "", id);
|
|
found++;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool nnid_unlink_others(char *msg, size_t msglen)
|
|
{
|
|
if (!ready) return false;
|
|
u8 count = 0, current = current_slot();
|
|
if (R_FAILED(ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS))) return false;
|
|
|
|
mkdir("sdmc:/3ds/mbmnet", 0777);
|
|
FILE *log = fopen(LOG_FILE, "a");
|
|
FILE *old = fopen(OLD_NNID_FILE, "a");
|
|
bool ok = true;
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
if (slot == current) continue;
|
|
AccountId id = { 0 };
|
|
u32 pid = 0;
|
|
if (R_FAILED(ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID)) || !id[0]) continue;
|
|
ACT_GetAccountInfo(&pid, sizeof(pid), slot, INFO_TYPE_PRINCIPAL_ID);
|
|
if (old) fprintf(old, "Konto-Platz %u ID: %s PID: %lu (anderer Platz, z. B. Pretendo)\n", slot, id, (unsigned long)pid);
|
|
Result rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, "Platz %u (%s) lösen %08lX\n", slot, id, (unsigned long)rc);
|
|
if (R_FAILED(rc)) {
|
|
snprintf(msg, msglen, "ID \"%s\" (Platz %u) konnte nicht gelöst werden\n(Fehler %08lX).", id, slot, (unsigned long)rc);
|
|
ok = false;
|
|
}
|
|
}
|
|
if (old) fclose(old);
|
|
if (log) fclose(log);
|
|
return ok;
|
|
}
|
|
|
|
bool nnid_unlink(char *msg, size_t msglen)
|
|
{
|
|
if (!ready) {
|
|
snprintf(msg, msglen, "Kontodienst nicht verfügbar.\nIst Luma3DS aktuell?");
|
|
return false;
|
|
}
|
|
u8 slot = current_slot();
|
|
AccountId id = { 0 };
|
|
u32 pid = 0;
|
|
ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID);
|
|
ACT_GetAccountInfo(&pid, sizeof(pid), slot, INFO_TYPE_PRINCIPAL_ID);
|
|
if (!id[0]) return true; // nichts verknüpft
|
|
|
|
mkdir("sdmc:/3ds/mbmnet", 0777);
|
|
|
|
// Alte ID merken, damit der Name nicht verloren geht
|
|
FILE *f = fopen(OLD_NNID_FILE, "a");
|
|
if (f) {
|
|
time_t now = time(NULL);
|
|
char date[32];
|
|
strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&now));
|
|
fprintf(f, "%s Konto-Platz %u NNID: %s PID: %lu\n", date, slot, id, (unsigned long)pid);
|
|
fclose(f);
|
|
}
|
|
|
|
FILE *log = fopen(LOG_FILE, "w");
|
|
if (log) fprintf(log, "MBMNET: NNID lösen (App v%s)\nKonto-Platz %u, NNID %s\n\n", APP_VERSION, slot, id);
|
|
|
|
Result rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, "%-30s %08lX\n", "Verknüpfung lösen", (unsigned long)rc);
|
|
if (R_FAILED(rc)) {
|
|
// Manche Zustände erlauben das nur bei abgemeldetem Konto: abmelden und erneut versuchen
|
|
Result u = ACTA_UnloadConsoleAccount();
|
|
if (log) fprintf(log, "%-30s %08lX\n", "Konto abmelden", (unsigned long)u);
|
|
rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, "%-30s %08lX\n", "Verknüpfung lösen (2.)", (unsigned long)rc);
|
|
}
|
|
if (log) { fprintf(log, "\nErgebnis: %s\n", R_SUCCEEDED(rc) ? "OK" : "FEHLER"); fclose(log); }
|
|
|
|
if (R_FAILED(rc)) {
|
|
snprintf(msg, msglen, "NNID konnte nicht gelöst werden (Fehler %08lX).\nProtokoll: /3ds/mbmnet/konto.log", (unsigned long)rc);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Für die Diagnose: alle Konto-Plätze mit ihren Daten
|
|
void nnid_dump(FILE *f)
|
|
{
|
|
if (!ready) { fprintf(f, " Kontodienst nicht verfügbar\n"); return; }
|
|
u8 count = 0, current = 0, def = 0;
|
|
ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS);
|
|
ACT_GetCommonInfo(¤t, sizeof(current), INFO_TYPE_COMMON_CURRENT_ACCOUNT_SLOT);
|
|
ACT_GetCommonInfo(&def, sizeof(def), INFO_TYPE_COMMON_DEFAULT_ACCOUNT_SLOT);
|
|
u8 friendAccount = 0;
|
|
FRD_GetMyLocalAccountId(&friendAccount);
|
|
fprintf(f, " %u Plätze, aktiv: %u, Standard: %u, geladenes Freundekonto: %u (1 = Nintendo, 3 = MBMNET)\n",
|
|
count, current, def, friendAccount);
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
AccountId id = { 0 }, assigned = { 0 };
|
|
u32 pid = 0, assignedPid = 0;
|
|
u8 fp = 0;
|
|
bool committed = false;
|
|
Result r1 = ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID);
|
|
ACT_GetAccountInfo(assigned, sizeof(assigned), slot, INFO_TYPE_ASSIGNED_ACCOUNT_ID);
|
|
ACT_GetAccountInfo(&pid, sizeof(pid), slot, INFO_TYPE_PRINCIPAL_ID);
|
|
ACT_GetAccountInfo(&assignedPid, sizeof(assignedPid), slot, INFO_TYPE_ASSIGNED_PRINCIPAL_ID);
|
|
ACT_GetAccountInfo(&fp, sizeof(fp), slot, INFO_TYPE_FP_LOCAL_ACCOUNT_ID);
|
|
ACT_GetAccountInfo(&committed, sizeof(committed), slot, INFO_TYPE_IS_COMMITTED);
|
|
fprintf(f, " Platz %u: ID \"%s\" (Länge %u, Abfrage %08lX) PID %lu zugewiesen \"%s\"/%lu Freunde-Konto %u gespeichert %d\n",
|
|
slot, id, (unsigned)strlen(id), (unsigned long)r1, (unsigned long)pid, assigned,
|
|
(unsigned long)assignedPid, fp, committed);
|
|
}
|
|
}
|
|
|
|
// Gibt es die ID "name" in einem Konto-Platz außer exceptSlot?
|
|
bool nnid_name_elsewhere(const char *name, u8 exceptSlot)
|
|
{
|
|
if (!ready || !name[0]) return false;
|
|
u8 count = 0;
|
|
if (R_FAILED(ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS))) return false;
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
if (slot == exceptSlot) continue;
|
|
AccountId id = { 0 };
|
|
if (R_SUCCEEDED(ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID)) && !strcasecmp(id, name)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool nnid_unlink_slot(u8 slot, char *msg, size_t msglen)
|
|
{
|
|
if (!ready) return false;
|
|
AccountId id = { 0 };
|
|
ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID);
|
|
if (!id[0]) return true;
|
|
FILE *log = fopen(LOG_FILE, "a");
|
|
Result rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, "Platz %u (%s) lösen %08lX\n", slot, id, (unsigned long)rc);
|
|
if (R_FAILED(rc)) {
|
|
Result u = ACTA_UnloadConsoleAccount();
|
|
rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, " nach Abmelden (%08lX): %08lX\n", (unsigned long)u, (unsigned long)rc);
|
|
}
|
|
if (log) fclose(log);
|
|
if (R_FAILED(rc)) snprintf(msg, msglen, "\"%s\" (Platz %u) ließ sich nicht lösen (Fehler %08lX).", id, slot, (unsigned long)rc);
|
|
return R_SUCCEEDED(rc);
|
|
}
|
|
|
|
// Löst die ID "name" in allen Konto-Plätzen außer exceptSlot (nur auf dem 3DS). false + msg bei Fehler.
|
|
bool nnid_unlink_named(const char *name, u8 exceptSlot, char *msg, size_t msglen)
|
|
{
|
|
if (!ready) return false;
|
|
u8 count = 0;
|
|
if (R_FAILED(ACT_GetCommonInfo(&count, sizeof(count), INFO_TYPE_COMMON_NUM_ACCOUNTS))) return false;
|
|
FILE *log = fopen(LOG_FILE, "a");
|
|
bool ok = true;
|
|
for (u8 slot = 1; slot <= count; slot++) {
|
|
if (slot == exceptSlot) continue;
|
|
AccountId id = { 0 };
|
|
if (R_FAILED(ACT_GetAccountInfo(id, sizeof(id), slot, INFO_TYPE_ACCOUNT_ID)) || strcasecmp(id, name)) continue;
|
|
Result rc = ACTA_UnbindServerAccount(slot, true);
|
|
if (log) fprintf(log, "Platz %u (%s) lösen (gleicher Name wie MBMNET) %08lX\n", slot, id, (unsigned long)rc);
|
|
if (R_FAILED(rc)) {
|
|
snprintf(msg, msglen, "\"%s\" (Platz %u) ließ sich nicht lösen (Fehler %08lX).", id, slot, (unsigned long)rc);
|
|
ok = false;
|
|
}
|
|
}
|
|
if (log) fclose(log);
|
|
return ok;
|
|
}
|