Add gc2n64 mapping read/write

This commit is contained in:
Raphael Assenat 2015-10-15 23:33:20 -04:00
parent ead823947a
commit e3c5feaf15
3 changed files with 432 additions and 16 deletions

View File

@ -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; i<mapping->n_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<mapping_size; i++) {
cmd[0] = 'R';
cmd[1] = 0x02; // Get mapping
cmd[2] = id;
cmd[2] = mapping_id;
cmd[3] = i+1; // chunk 1 is first 32 byte block, 2nd is next 32 bytes, etc
printf("Getting block %d\n", i+1);
n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf + pos, 32);
// printf("Getting block %d\n", i+1);
n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf + pos, togo > 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; i<dst_mapping->n_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; i<map->n_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; i<map->n_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; i<GC2N64_NUM_MAPPINGS; i++) {
printf("\tMapping %d (%-13s): { ", i, gc2n64_adapter_getMappingSlotName(i, 0));
gc2n64_adapter_printMapping(&inf->app.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; n<GC2N64_NUM_MAPPINGS; n++) {
gc2n64_adapter_getMapping(hdl, channel, n, &inf->app.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;
}

View File

@ -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]);

View File

@ -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) {