From e3c5feaf15431f18a7f77739f9b449b19eac731b Mon Sep 17 00:00:00 2001 From: Raphael Assenat Date: Thu, 15 Oct 2015 23:33:20 -0400 Subject: [PATCH] Add gc2n64 mapping read/write --- tool/gc2n64_adapter.c | 338 ++++++++++++++++++++++++++++++++++++++++-- tool/gc2n64_adapter.h | 31 ++++ tool/main.c | 79 ++++++++++ 3 files changed, 432 insertions(+), 16 deletions(-) diff --git a/tool/gc2n64_adapter.c b/tool/gc2n64_adapter.c index 52aad52..4775211 100644 --- a/tool/gc2n64_adapter.c +++ b/tool/gc2n64_adapter.c @@ -8,6 +8,10 @@ #include "ihex.h" #include "delay.h" +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + int gc2n64_adapter_echotest(gcn64_hdl_t hdl, int channel, int verbose) { unsigned char cmd[30]; @@ -37,61 +41,359 @@ int gc2n64_adapter_echotest(gcn64_hdl_t hdl, int channel, int verbose) return (n!= sizeof(buf)) || memcmp(cmd, buf, sizeof(buf)); } -int gc2n64_adapter_getMapping(gcn64_hdl_t hdl, int channel, int id) +int gc2n64_adapter_storeCurrentMapping(gcn64_hdl_t hdl, int channel, int dst_slot) +{ + int n; + unsigned char cmd[3]; + + cmd[0] = 'R'; + cmd[1] = 0x04; // Save current mapping + cmd[2] = dst_slot; + + n = gcn64lib_rawSiCommand(hdl, channel, cmd, sizeof(cmd), cmd, 1); + if (n<0) { + return n; + } + if (n != 1) { + fprintf(stderr, "Communication error while storing mapping\n"); + return -1; + } + + if (cmd[0] == 0x00) { + return gc2n64_adapter_waitNotBusy(hdl, channel, 0); + } + else { + fprintf(stderr, "storeCurrentMapping: Command NACKed\n"); + return -1; + } +} + +int gc2n64_adapter_setMapping(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_mapping *mapping) +{ + unsigned char buf[64]; + unsigned char mapdata[64]; + int i, n; + int maplen, togo, done, chunk; + + + maplen = mapping->n_pairs * 2; + + if (maplen > sizeof(mapdata)) { + fprintf(stderr, "Mapping too large\n"); + return -1; + } + + for (i=0; in_pairs; i++) { + mapdata[i*2] = mapping->pairs[i].gc; + mapdata[i*2 + 1] = mapping->pairs[i].n64; + } + + printf("Map data : "); + printHexBuf(mapdata, maplen); + + togo = maplen; + done = 0; + chunk = 0; + while (togo) { + int len; + + if (togo > 32) { + len = 32; + } else { + len = togo; + } + + buf[0] = 'R'; + buf[1] = 0x03; // set mapping + buf[2] = chunk; + memcpy(buf + 3, mapdata + done, len); + done+= len; + +// printf("Mapping chunk : "); +// printHexBuf(buf, len + 2); + + n = gcn64lib_rawSiCommand(hdl, channel, buf, len + 3, buf, 1); + if (n<0) { + return n; + } + if (n != 1) { + fprintf(stderr, "Communication error setting mapping\n"); + return -1; + } + + togo -= len; + chunk++; + } + + return 0; +} + +int gc2n64_adapter_getMapping(gcn64_hdl_t hdl, int channel, int mapping_id, struct gc2n64_adapter_mapping *dst_mapping) { unsigned char buf[64]; unsigned char cmd[4]; int n; int mapping_size; + int togo; cmd[0] = 'R'; cmd[1] = 0x02; // Get mapping - cmd[2] = id; + cmd[2] = mapping_id; cmd[3] = 0; // chunk 0 (size) - n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf, 4); + n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf, 1); if (n<0) return n; if (n == 1) { int i, pos; mapping_size = buf[0]; - printf("Mapping %d size: %d\n", id, mapping_size); +// printf("Mapping %d size: %d\n", mapping_id, mapping_size); + togo = mapping_size; for (pos=0, i=0; pos 32 ? 32 : togo); if (n<0) { return n; } - printf("ret: %d\n", n); +// printf("ret: %d\n", n); if (n==0) break; pos += n; + togo -= n; } - printf("Received %d bytes\n", pos); - printHexBuf(buf, pos); + //printf("Received %d bytes\n", pos); + if (n%2) { + fprintf(stderr, "Error: Odd length mapping received\n"); + printHexBuf(buf, pos); + return -1; + } + + // TODO : Decode this to dst_mapping + dst_mapping->n_pairs = pos/2; + for (i=0; in_pairs; i++) { + dst_mapping->pairs[i].gc = buf[i*2]; + dst_mapping->pairs[i].n64 = buf[i*2+1]; + } } return 0; } +const char *gc2n64_adapter_getMappingSlotName(unsigned char id, int default_context) +{ + switch (id) + { + case MAPPING_SLOT_BUILTIN_CURRENT: + if (default_context) { + return "[Built-in default]"; + } else { + return "[Current mapping]"; + } + case MAPPING_SLOT_DPAD_UP: return "[D-Pad UP]"; + case MAPPING_SLOT_DPAD_DOWN: return "[D-Pad DOWN]"; + case MAPPING_SLOT_DPAD_LEFT: return "[D-Pad LEFT]"; + case MAPPING_SLOT_DPAD_RIGHT: return "[D-Pad RIGHT]"; + } + return "Invalid ID"; +} + +const char *gc2n64_adapter_getGCname(unsigned char id) +{ + const char *names[] = { + "A","B","Z","Start", + "L","R", + "C-stick up (50% threshold)", + "C-stick down (50% threshold)", + "C-stick left (50% threshold)", + "C-stick right (50% threshold)", + "Dpad-up","Dpad-down","Dpad-left","Dpad-right", + "Joystick left-right axis","Joystick up-down axis", + // Extras + "X","Y", + "Joystick up (50% threshold)", "Joystick down (50% threshold)", + "Joystick left (50% threshold)", "Joystick right (50% threshold)", + "Analogic L slider (50% threshold)", + "Analogic R slider (50% threshold)", + "C-stick left-right axis","C-stick up-down axis", + }; + + if (id == 0xff) + return "None"; + + if (id < 0 || id >= ARRAY_SIZE(names)) { + return "Error"; + } + return names[id]; +} + +const char *gc2n64_adapter_getN64name(unsigned char id) +{ + const char *names[] = { + "A","B","Z","Start","L","R", + "C-up","C-down","C-left","C-right", + "Dpad-up","Dpad-down","Dpad-left","Dpad-right", + "Joystick left-right axis","Joystick up-down axis", + "Joystick up", "Joystick down", + "Joystick left", "Joystick right", + "None" + }; + + if (id == 0xff) + return "None"; + + if (id < 0 || id >= ARRAY_SIZE(names)) { + return "Error"; + } + return names[id]; +} + +struct gc2n64_adapter_mapping *gc2n64_adapter_loadMapping(const char *srcfile) +{ + FILE *fptr; + struct gc2n64_adapter_mapping *map = NULL;; + char linebuf[64]; + int line = 0, pair = 0; + + fptr = fopen(srcfile, "r"); + if (!fptr) { + perror("fopen"); + return NULL; + } + + map = malloc(sizeof(struct gc2n64_adapter_mapping)); + if (!map) { + perror("malloc"); + goto err; + } + + do { + if (fgets(linebuf, sizeof(linebuf), fptr)) { + int gc, n64, n; + line++; + + if (line == 1) { + const char *magic = "# gc2n64 mapping"; + if (strncmp(magic, linebuf, strlen(magic))) { + fprintf(stderr, "Does not appear to be a valid mapping file\n"); + goto err; + } + continue; + } + + n = sscanf(linebuf, "%03d;%03d", &gc, &n64); + if (n != 2) { + // printf("Ignoring line %d\n", line); + } else { + // printf("%d -> %d\n", gc, n64); + map->pairs[pair].gc = gc; + map->pairs[pair].n64 = n64; + + pair++; + if (pair >= GC2N64_MAX_MAPPING_PAIRS) { + fprintf(stderr, "too many pairs, cannot load mapping.\n"); + goto err; + } + } + } + } while (!feof(fptr)); + + map->n_pairs = pair; + + fclose(fptr); + return map; + +err: + if (map) { + free(map); + } + fclose(fptr); + return NULL; +} + +int gc2n64_adapter_saveMapping(struct gc2n64_adapter_mapping *map, const char *dstfile) +{ + FILE *fptr; + int i; + + fptr = fopen(dstfile, "w"); + if (!fptr) { + perror("fopen"); + return -1; + } + + fprintf(fptr, "# gc2n64 mapping\n"); + for (i=0; in_pairs; i++) { + fprintf(fptr, "%03d;%03d # %s -> %s\n", + map->pairs[i].gc, map->pairs[i].n64, + gc2n64_adapter_getGCname(map->pairs[i].gc), + gc2n64_adapter_getN64name(map->pairs[i].n64)); + } + fflush(fptr); + fclose(fptr); + + return 0; +} + +void gc2n64_adapter_printMapping(struct gc2n64_adapter_mapping *map) +{ + int i; + int is_default; + + for (i=0; in_pairs; i++) { + // Do not display the terminator + if (map->pairs[i].gc == 0xff || map->pairs[i].n64 == 0xff) { + break; + } + + /* 0 .. 15 is a 1:1 (same button name) mapping by default */ + if (map->pairs[i].gc < 16) { + if (map->pairs[i].gc == map->pairs[i].n64) { + is_default = 1; + } + else { + is_default = 0; + } + } + else { + // 16 and above maps to NONE by default + if (map->pairs[i].n64 == 20) { + is_default = 1; + } else { + is_default = 0; + } + } + + if (!is_default) { + printf("%s -> %s, ", gc2n64_adapter_getGCname(map->pairs[i].gc), + gc2n64_adapter_getN64name(map->pairs[i].n64)); + } + } +} void gc2n64_adapter_printInfo(struct gc2n64_adapter_info *inf) { + int i; + if (!inf->in_bootloader) { printf("gc_to_n64 adapter info: {\n"); - printf("\tDefault mapping id: %d\n", inf->app.default_mapping_id); + printf("\tDefault mapping id: %d (%s)\n", inf->app.default_mapping_id, gc2n64_adapter_getMappingSlotName(inf->app.default_mapping_id, 1) ); printf("\tDeadzone enabled: %d\n", inf->app.deadzone_enabled); printf("\tOld v1.5 conversion: %d\n", inf->app.old_v1_5_conversion); printf("\tFirmware version: %s\n", inf->app.version); printf("\tUpgradable: %s\n", inf->app.upgradeable ? "Yes":"No (Atmega8)"); + for (i=0; iapp.mappings[i]); + printf(" }\n"); + } } else { printf("gc_to_n64 adapter in bootloader mode: {\n"); @@ -139,6 +441,10 @@ int gc2n64_adapter_getInfo(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_i strncpy(inf->bootldr.version, (char*)buf+10, sizeof(inf->bootldr.version)-1); } + for (n=0; napp.mappings[n]); + } + } else { printf("No answer (old version?)\n"); return -1; @@ -147,7 +453,7 @@ int gc2n64_adapter_getInfo(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_i return 0; } -int gc2n64_adapter_boot_isBusy(gcn64_hdl_t hdl, int channel) +int gc2n64_adapter_isBusy(gcn64_hdl_t hdl, int channel) { unsigned char buf[64]; int n; @@ -170,13 +476,13 @@ int gc2n64_adapter_boot_isBusy(gcn64_hdl_t hdl, int channel) return 0; // Idle } -int gc2n64_adapter_boot_waitNotBusy(gcn64_hdl_t hdl, int channel, int verbose) +int gc2n64_adapter_waitNotBusy(gcn64_hdl_t hdl, int channel, int verbose) { char spinner[4] = { '|','/','-','\\' }; int busy, no_reply_count=0; int c=0; - while ((busy = gc2n64_adapter_boot_isBusy(hdl, channel))) + while ((busy = gc2n64_adapter_isBusy(hdl, channel))) { if (busy < 0) { return -1; @@ -376,7 +682,7 @@ int gc2n64_adapter_sendFirmwareBlocks(gcn64_hdl_t hdl, int channel, unsigned cha } if (buf[1]) { - if (gc2n64_adapter_boot_waitNotBusy(hdl, channel, 1)) { + if (gc2n64_adapter_waitNotBusy(hdl, channel, 1)) { fprintf(stderr, "Error waiting not busy\n"); return -1; } @@ -506,7 +812,7 @@ int gc2n64_adapter_updateFirmware(gcn64_hdl_t hdl, int channel, const char *hexf printf("step [4/7] : Erase current firmware... "); fflush(stdout); gc2n64_adapter_boot_eraseAll(hdl, channel); - if (gc2n64_adapter_boot_waitNotBusy(hdl, channel, 1)) { + if (gc2n64_adapter_waitNotBusy(hdl, channel, 1)) { ret = -1; goto err; } diff --git a/tool/gc2n64_adapter.h b/tool/gc2n64_adapter.h index f5b68e8..91f2501 100644 --- a/tool/gc2n64_adapter.h +++ b/tool/gc2n64_adapter.h @@ -3,12 +3,25 @@ #include "gcn64.h" +#define GC2N64_MAX_MAPPING_PAIRS 32 +#define GC2N64_NUM_MAPPINGS 5 + +struct gc2n64_adapter_mapping_pair { + int gc, n64; +}; + +struct gc2n64_adapter_mapping { + int n_pairs; + struct gc2n64_adapter_mapping_pair pairs[GC2N64_MAX_MAPPING_PAIRS]; +}; + struct gc2n64_adapter_info_app { unsigned char default_mapping_id; unsigned char deadzone_enabled; unsigned char old_v1_5_conversion; unsigned char upgradeable; char version[16]; + struct gc2n64_adapter_mapping mappings[GC2N64_NUM_MAPPINGS]; }; struct gc2n64_adapter_info_bootloader { @@ -28,6 +41,24 @@ struct gc2n64_adapter_info { int gc2n64_adapter_echotest(gcn64_hdl_t hdl, int channel, int verbosee); int gc2n64_adapter_getInfo(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_info *inf); void gc2n64_adapter_printInfo(struct gc2n64_adapter_info *inf); +void gc2n64_adapter_printMapping(struct gc2n64_adapter_mapping *map); + +#define MAPPING_SLOT_BUILTIN_CURRENT 0 +#define MAPPING_SLOT_DPAD_UP 1 +#define MAPPING_SLOT_DPAD_DOWN 2 +#define MAPPING_SLOT_DPAD_LEFT 3 +#define MAPPING_SLOT_DPAD_RIGHT 4 +const char *gc2n64_adapter_getMappingSlotName(unsigned char id, int default_context); + +int gc2n64_adapter_getMapping(gcn64_hdl_t hdl, int channel, int mapping_id, struct gc2n64_adapter_mapping *dst_mapping); +int gc2n64_adapter_setMapping(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_mapping *mapping); +int gc2n64_adapter_storeCurrentMapping(gcn64_hdl_t hdl, int channel, int dst_slot); + +int gc2n64_adapter_saveMapping(struct gc2n64_adapter_mapping *map, const char *dstfile); +struct gc2n64_adapter_mapping *gc2n64_adapter_loadMapping(const char *srcfile); + +int gc2n64_adapter_waitNotBusy(gcn64_hdl_t hdl, int channel, int verbose); +int gc2n64_adapter_boot_isBusy(gcn64_hdl_t hdl, int channel); int gc2n64_adapter_boot_eraseAll(gcn64_hdl_t hdl, int channel); int gc2n64_adapter_boot_readBlock(gcn64_hdl_t hdl, int channel, unsigned int block_id, unsigned char dst[32]); diff --git a/tool/main.c b/tool/main.c index e72de17..5dadbc3 100644 --- a/tool/main.c +++ b/tool/main.c @@ -44,6 +44,7 @@ static void printUsage(void) printf(" -s serial Operate on specified device (required unless -f is specified)\n"); printf(" -f, --force If no serial is specified, use first device detected.\n"); printf(" -o, --outfile file Output file for read operations (eg: --n64-mempak-dump)\n"); + //printf(" -i, --infile file Input file for write operations (eg: --gc_to_n64_update)\n"); printf(" --nonstop Continue testing forever or until an error occurs.\n"); printf("\n"); printf("Configuration commands:\n"); @@ -68,6 +69,9 @@ static void printUsage(void) printf("GC to N64 adapter commands: (For GC to N64 adapter connected to GC/N64 to USB adapter)\n"); printf(" --gc_to_n64_info Display info on adapter (version, config, etc)\n"); printf(" --gc_to_n64_update file.hex Update GC to N64 adapter firmware\n"); + printf(" --gc_to_n64_read_mapping id Dump a mapping (Use with --outfile to write to file)\n"); + printf(" --gc_to_n64_load_mapping file Load a mapping from a file and send it to the adapter\n"); + printf(" --gc_to_n64_store_current_mapping slot Store the current mapping to one of the D-Pad slots.\n"); printf("\n"); printf("GC to N64 adapter, development/debug commands:\n"); printf(" --gc_to_n64_echotest Perform a communication test (usable with --nonstop)\n"); @@ -82,6 +86,7 @@ static void printUsage(void) #define OPT_OUTFILE 'o' +#define OPT_INFILE 'i' #define OPT_SET_SERIAL 257 #define OPT_GET_SERIAL 258 #define OPT_BOOTLOADER 300 @@ -104,6 +109,9 @@ static void printUsage(void) #define OPT_GC_TO_N64_ENTER_BOOTLOADER 317 #define OPT_GC_TO_N64_BOOT_APPLICATION 318 #define OPT_NONSTOP 319 +#define OPT_GC_TO_N64_READ_MAPPING 320 +#define OPT_GC_TO_N64_LOAD_MAPPING 321 +#define OPT_GC_TO_N64_STORE_CURRENT_MAPPING 322 struct option longopts[] = { { "help", 0, NULL, 'h' }, @@ -120,6 +128,7 @@ struct option longopts[] = { { "suspend_polling", 0, NULL, OPT_SUSPEND_POLLING }, { "resume_polling", 0, NULL, OPT_RESUME_POLLING }, { "outfile", 1, NULL, OPT_OUTFILE }, + { "infile", 1, NULL, OPT_INFILE }, { "set_poll_rate", 1, NULL, OPT_SET_POLL_INTERVAL }, { "get_poll_rate", 0, NULL, OPT_GET_POLL_INTERVAL }, { "n64_mempak_write", 1, NULL, OPT_N64_MEMPAK_WRITE }, @@ -131,6 +140,9 @@ struct option longopts[] = { { "gc_to_n64_dump", 0, NULL, OPT_GC_TO_N64_DUMP }, { "gc_to_n64_enter_bootloader", 0, NULL, OPT_GC_TO_N64_ENTER_BOOTLOADER }, { "gc_to_n64_boot_application", 0, NULL, OPT_GC_TO_N64_BOOT_APPLICATION }, + { "gc_to_n64_read_mapping", 1, NULL, OPT_GC_TO_N64_READ_MAPPING }, + { "gc_to_n64_load_mapping", 1, NULL, OPT_GC_TO_N64_LOAD_MAPPING }, + { "gc_to_n64_store_current_mapping", 1, NULL, OPT_GC_TO_N64_STORE_CURRENT_MAPPING }, { "nonstop", 0, NULL, OPT_NONSTOP }, { }, }; @@ -171,6 +183,7 @@ int main(int argc, char **argv) wchar_t target_serial[TARGET_SERIAL_CHARS]; const char *short_optstr = "hls:vfo:"; const char *outfile = NULL; + const char *infile = NULL; int gc2n64_channel = 0; while((opt = getopt_long(argc, argv, short_optstr, longopts, NULL)) != -1) { @@ -203,6 +216,10 @@ int main(int argc, char **argv) outfile = optarg; printf("Output file: %s\n", outfile); break; + case 'i': + infile = optarg; + printf("Input file: %s\n", infile); + break; case OPT_NONSTOP: nonstop = 1; break; @@ -408,6 +425,68 @@ int main(int argc, char **argv) case OPT_GC_TO_N64_BOOT_APPLICATION: gc2n64_adapter_bootApplication(hdl, gc2n64_channel); break; + + case OPT_GC_TO_N64_READ_MAPPING: + { + struct gc2n64_adapter_info inf; + int map_id; + + map_id = atoi(optarg); + if ((map_id <= 0) || (map_id > GC2N64_NUM_MAPPINGS)) { + fprintf(stderr, "Invalid mapping id (1 to 4)\n"); + return -1; + } + + gc2n64_adapter_getInfo(hdl, gc2n64_channel, &inf); + printf("Mapping %d : { ", map_id); + gc2n64_adapter_printMapping(&inf.app.mappings[map_id-1]); + printf(" }\n"); + if (outfile) { + printf("Writing mapping to file '%s'\n", outfile); + gc2n64_adapter_saveMapping(&inf.app.mappings[map_id-1], outfile); + } + } + break; + + case OPT_GC_TO_N64_LOAD_MAPPING: + { + struct gc2n64_adapter_mapping *mapping; + + printf("Reading mapping from file '%s'\n", optarg); + mapping = gc2n64_adapter_loadMapping(optarg); + if (!mapping) { + fprintf(stderr, "Failed to load mapping\n"); + return -1; + } + + printf("Mapping : { "); + gc2n64_adapter_printMapping(mapping); + printf(" }\n"); + + gc2n64_adapter_setMapping(hdl, gc2n64_channel, mapping); + + free(mapping); + } + break; + + case OPT_GC_TO_N64_STORE_CURRENT_MAPPING: + { + int slot; + + slot = atoi(optarg); + + if (slot < 1 || slot > 4) { + fprintf(stderr, "Mapping out of range (1-4)\n"); + return -1; + } + + if (0 == gc2n64_adapter_storeCurrentMapping(hdl, gc2n64_channel, slot)) { + printf("Stored mapping to slot %d (%s)\n", slot, gc2n64_adapter_getMappingSlotName(slot, 0)); + } else { + printf("Error storing mapping\n"); + } + } + break; } if (do_exchange) {