mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Give the game an amiibo's UID and model info in the Switch's layout
TagInfo took the UID as dump bytes 0..6, so the check byte BCC0 (byte 3) replaced the UID's last byte. ModelInfo had the series and the figure type swapped, because the dump offsets were misread (0x57 is the figure type, 0x5A the series), and the model number in the tag's big-endian order, while the game reads it little-endian. The game tells figures of one character apart by that number, so scans gave the wrong rewards. The years of the common and register info dates go little-endian too. --probe-services checks TagInfo and ModelInfo of a Link (Archer) dump. The same fixes are in pull requests #519 (genres) and #582 (Gelomban6), which agree on the ModelInfo layout; their event and feature changes are not part of this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
42d46c1324
commit
3a556c5048
2 files changed
+36
-17
No files matched your search
+22
-17
@@ -56,6 +56,12 @@ static bool amiibo_validate_dump(const uint8_t *data, uint64_t size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The 7-byte UID of an NTAG215 dump: bytes 0..2, then 4..7. Byte 3 is the check byte BCC0.
|
||||
static void copy_uid(uint8_t out[7], const uint8_t *dump) {
|
||||
memcpy(out, dump, 3);
|
||||
memcpy(out + 3, dump + 4, 4);
|
||||
}
|
||||
|
||||
static void parse_dump_info(const char *path) {
|
||||
memset(&g_info, 0, sizeof(g_info));
|
||||
if (path) {
|
||||
@@ -69,15 +75,15 @@ static void parse_dump_info(const char *path) {
|
||||
snprintf(g_info.filename, sizeof(g_info.filename), "%s", name);
|
||||
}
|
||||
|
||||
// UID: bytes 0..6 (7 bytes) or up to 9
|
||||
memcpy(g_info.uuid, g_dump, 7);
|
||||
copy_uid(g_info.uuid, g_dump);
|
||||
g_info.uuid_len = 7;
|
||||
|
||||
// Amiibo ID at offset 0x54 (8 bytes)
|
||||
// Amiibo ID at offset 0x54 (8 bytes, 3dbrew "Amiibo"): character ID and variant, figure type, model number
|
||||
// (big-endian), series, format version 02
|
||||
memcpy(g_info.character_id, g_dump + 0x54, 3);
|
||||
g_info.series_id = g_dump[0x57];
|
||||
g_info.nfp_type = g_dump[0x57];
|
||||
g_info.numbering_id = ((uint16_t)g_dump[0x58] << 8) | g_dump[0x59];
|
||||
g_info.nfp_type = g_dump[0x5A];
|
||||
g_info.series_id = g_dump[0x5A];
|
||||
g_info.version = g_dump[0x5B];
|
||||
}
|
||||
|
||||
@@ -321,7 +327,7 @@ void amiibo_get_tag_info(void *out_tag_info) {
|
||||
uint8_t *out = (uint8_t *)out_tag_info;
|
||||
memset(out, 0, 0x58);
|
||||
// UUID: offset 0x00, 10 bytes max
|
||||
memcpy(out + 0x00, g_dump, 7);
|
||||
copy_uid(out, g_dump);
|
||||
out[0x0A] = 7; // UUID length
|
||||
// Protocol at offset 0x20: TypeA = 1
|
||||
*(uint32_t *)(out + 0x20) = 1;
|
||||
@@ -334,15 +340,14 @@ void amiibo_get_model_info(void *out_model_info) {
|
||||
plat_mutex_lock(&g_amiibo_lock);
|
||||
uint8_t *out = (uint8_t *)out_model_info;
|
||||
memset(out, 0, 0x40);
|
||||
// Character ID at 0x00 (3 bytes)
|
||||
memcpy(out + 0x00, g_dump + 0x54, 3);
|
||||
// Series ID at 0x03 (1 byte)
|
||||
out[0x03] = g_dump[0x57];
|
||||
// Numbering ID at 0x04 (2 bytes, big-endian from 0x58)
|
||||
out[0x04] = g_dump[0x58];
|
||||
out[0x05] = g_dump[0x59];
|
||||
// NFP Type at 0x06 (1 byte)
|
||||
out[0x06] = g_dump[0x5A];
|
||||
// nn::nfp::ModelInfo from the amiibo ID at 0x54: character ID and variant in the tag's byte order, series, model
|
||||
// number little-endian (big-endian on the tag), figure type. The game tells figures of one character apart by the
|
||||
// model number.
|
||||
memcpy(out + 0x00, g_dump + 0x54, 3); // character ID, variant
|
||||
out[0x03] = g_dump[0x5A]; // series
|
||||
out[0x04] = g_dump[0x59]; // model number
|
||||
out[0x05] = g_dump[0x58];
|
||||
out[0x06] = g_dump[0x57]; // figure type
|
||||
plat_mutex_unlock(&g_amiibo_lock);
|
||||
}
|
||||
|
||||
@@ -351,7 +356,7 @@ void amiibo_get_common_info(void *out_common_info) {
|
||||
uint8_t *out = (uint8_t *)out_common_info;
|
||||
memset(out, 0, 0x40);
|
||||
// Last write date: 0x00..0x03
|
||||
out[0x00] = 0x07; out[0x01] = 0xE7; // 2023
|
||||
out[0x00] = 0xE7; out[0x01] = 0x07; // 2023, u16 little-endian
|
||||
out[0x02] = 0x05; out[0x03] = 0x0C; // May 12
|
||||
// Write counter: 0x04..0x05
|
||||
out[0x04] = 0x00; out[0x05] = 0x01;
|
||||
@@ -369,7 +374,7 @@ void amiibo_get_register_info(void *out_register_info) {
|
||||
// Mii CharInfo at 0x00..0x57 (dummy valid mii)
|
||||
out[0x00] = 0x03; // version 3
|
||||
// First write date at 0x58..0x5B
|
||||
out[0x58] = 0x07; out[0x59] = 0xE7;
|
||||
out[0x58] = 0xE7; out[0x59] = 0x07; // 2023, u16 little-endian
|
||||
out[0x5A] = 0x05; out[0x5B] = 0x0C;
|
||||
// Amiibo Name at 0x5C: copy up to 40 bytes NUL-terminated
|
||||
const char *name = g_info.filename[0] ? g_info.filename : "Amiibo";
|
||||
|
||||
@@ -405,8 +405,22 @@ static void probe_applet(void) {
|
||||
0x33 ^ 0x44 ^ 0x55 ^ 0x66, 0x48, 0x0F, 0xE0, 0xF1, 0x10, 0xFF, 0xEE};
|
||||
uint8_t dump[AMIIBO_DUMP_SIZE] = {0};
|
||||
memcpy(dump, head, sizeof head);
|
||||
// Link (Archer): character 0x0100, variant 0, figure type 0, model number 0x0353, series 9, format 02
|
||||
static const uint8_t id[8] = {0x01, 0x00, 0x00, 0x00, 0x03, 0x53, 0x09, 0x02};
|
||||
memcpy(dump + 0x54, id, sizeof id);
|
||||
const char *dump_path = "probe-amiibo.bin";
|
||||
int amiibo_ok = plat_file_write(dump_path, dump, sizeof dump) && amiibo_load_file(dump_path);
|
||||
// TagInfo carries the 7-byte UID without its check byte; ModelInfo has the series at 3, the model number
|
||||
// little-endian at 4 and the figure type at 6
|
||||
uint8_t ti[0x58], mi[0x40];
|
||||
amiibo_get_tag_info(ti);
|
||||
amiibo_get_model_info(mi);
|
||||
static const uint8_t want_uid[7] = {0x04, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
static const uint8_t want_model[7] = {0x01, 0x00, 0x00, 0x09, 0x53, 0x03, 0x00};
|
||||
CHECK(amiibo_ok && !memcmp(ti, want_uid, 7) && ti[0x0A] == 7, "amiibo TagInfo UID %02x %02x %02x %02x %02x %02x %02x",
|
||||
ti[0], ti[1], ti[2], ti[3], ti[4], ti[5], ti[6]);
|
||||
CHECK(amiibo_ok && !memcmp(mi, want_model, 7), "amiibo ModelInfo %02x %02x %02x %02x %02x %02x %02x", mi[0], mi[1],
|
||||
mi[2], mi[3], mi[4], mi[5], mi[6]);
|
||||
dump[0x0C] = 0xE1;
|
||||
amiibo_ok = amiibo_ok && plat_file_write(dump_path, dump, sizeof dump) && !amiibo_load_file(dump_path);
|
||||
plat_fs_unlink(dump_path);
|
||||
|
||||
Reference in new issue
Block a user