From 76b5f386ff79b7587cbfda37a715975f531ade55 Mon Sep 17 00:00:00 2001 From: Raphael Assenat Date: Sun, 1 Nov 2015 14:12:44 -0500 Subject: [PATCH] Cleaned-up N64 Mempak read/write through adapter --- tool/Makefile | 4 +- tool/main.c | 91 ++++++++++- tool/mempak_gcn64usb.c | 239 +++++++++++++++++++++++++++++ tool/mempak_gcn64usb.h | 13 ++ tool/mempak_old.c | 335 ----------------------------------------- tool/mempak_old.h | 8 - 6 files changed, 338 insertions(+), 352 deletions(-) create mode 100644 tool/mempak_gcn64usb.c create mode 100644 tool/mempak_gcn64usb.h delete mode 100644 tool/mempak_old.c delete mode 100644 tool/mempak_old.h diff --git a/tool/Makefile b/tool/Makefile index 7aef01c..cd96c2a 100644 --- a/tool/Makefile +++ b/tool/Makefile @@ -26,10 +26,10 @@ MEMPAKLIB_OBJS=mempak.o mempak_fs.o all: gcn64ctl mempak_ls mempak_format mempak_extract_note mempak_insert_note mempak_rm mempak_convert gcn64ctl_gui -gcn64ctl_gui: gcn64ctl_gui.o gcn64ctl_gui_mpkedit.o gcn64.o gcn64lib.o hexdump.o ihex.o $(COMPAT_OBJS) $(MEMPAKLIB_OBJS) +gcn64ctl_gui: gcn64ctl_gui.o gcn64ctl_gui_mpkedit.o gcn64.o gcn64lib.o hexdump.o ihex.o mempak_gcn64usb.o $(COMPAT_OBJS) $(MEMPAKLIB_OBJS) $(LD) $^ $(LDFLAGS) $(GTK_LDFLAGS) -o $@ -gcn64ctl: main.o gcn64.o mempak_old.o gcn64lib.o hexdump.o gc2n64_adapter.o ihex.o delay.o $(COMPAT_OBJS) +gcn64ctl: main.o gcn64.o gcn64lib.o hexdump.o gc2n64_adapter.o ihex.o delay.o mempak_gcn64usb.o $(COMPAT_OBJS) $(MEMPAKLIB_OBJS) $(LD) $^ $(LDFLAGS) -o $@ gcn64ctl_gui.o: gcn64ctl_gui.c gcn64ctl_gui.h diff --git a/tool/main.c b/tool/main.c index 08a486e..19528fb 100644 --- a/tool/main.c +++ b/tool/main.c @@ -29,7 +29,8 @@ #include "gcn64.h" #include "gcn64lib.h" #include "gc2n64_adapter.h" -#include "mempak_old.h" +#include "mempak.h" +#include "mempak_gcn64usb.h" #include "../requests.h" #include "../gcn64_protocol.h" @@ -156,6 +157,16 @@ struct option longopts[] = { { }, }; +static void mempak_read_progress_cb(int addr) +{ + printf("\rReading address 0x%04x / 0x%04x ", addr, MEMPAK_MEM_SIZE); fflush(stdout); +} + +static void mempak_write_progress_cb(int addr) +{ + printf("\rWriting address 0x%04x / 0x%04x ", addr, MEMPAK_MEM_SIZE); fflush(stdout); +} + static int listDevices(void) { int n_found = 0; @@ -368,16 +379,82 @@ int main(int argc, char **argv) break; case OPT_N64_MEMPAK_DUMP: - if (outfile) { - mempak_dumpToFile(hdl, outfile); - } else { - mempak_dump(hdl); + { + mempak_structure_t *pak; + int res; + + printf("Reading mempak...\n"); + res = gcn64lib_mempak_download(hdl, 0, &pak, mempak_read_progress_cb); + printf("\n"); + switch (res) + { + case 0: + if (outfile) { + int file_format; + + file_format = mempak_getFilenameFormat(outfile); + if (file_format == MPK_FORMAT_INVALID) { + fprintf(stderr, "Unknown file format (neither .MPK nor .N64). Not saving.\n"); + } else { + if (0 == mempak_saveToFile(pak, outfile, file_format)) { + printf("Wrote file '%s' in %s format\n", outfile, mempak_format2string(file_format)); + } else { + fprintf(stderr, "error writing file\n"); + } + } + } else { // No outfile + mempak_hexdump(pak); + } + mempak_free(pak); + break; + case -1: + fprintf(stderr, "No mempak detected\n"); + break; + case -2: + fprintf(stderr, "I/O error reading pak\n"); + break; + default: + case -3: + fprintf(stderr, "Error\n"); + break; + + } + } break; case OPT_N64_MEMPAK_WRITE: - printf("Input file: %s\n", optarg); - mempak_writeFromFile(hdl, optarg); + { + mempak_structure_t *pak; + int res; + printf("Input file: %s\n", optarg); + + pak = mempak_loadFromFile(optarg); + if (!pak) { + fprintf(stderr, "Failed to load mempak\n"); + return -1; + } + + printf("Writing to mempak...\n"); + res = gcn64lib_mempak_upload(hdl, 0, pak, mempak_write_progress_cb); + printf("\n"); + if (res) { + switch(res) + { + case -1: + fprintf(stderr, "Error: No mempak detected.\n"); + break; + case -2: + fprintf(stderr, "I/O error writing to pak.\n"); + break; + default: + fprintf(stderr, "Error uploading mempak\n"); + } + } else { + printf("Mempak uploaded\n"); + } + mempak_free(pak); + } break; case OPT_SI8BIT_SCAN: diff --git a/tool/mempak_gcn64usb.c b/tool/mempak_gcn64usb.c new file mode 100644 index 0000000..dad0db0 --- /dev/null +++ b/tool/mempak_gcn64usb.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include "gcn64lib.h" +#include "gcn64.h" +#include "mempak.h" +#include "mempak_gcn64usb.h" +#include "../gcn64_protocol.h" +#include "../requests.h" + +/* __calc_address_crc is from libdragon which is public domain. */ + +/** + * @brief Calculate the 5 bit CRC on a mempak address + * + * This function, given an address intended for a mempak read or write, will + * calculate the CRC on the address, returning the corrected address | CRC. + * + * @param[in] address + * The mempak address to calculate CRC over + * + * @return The mempak address | CRC + */ +static uint16_t __calc_address_crc( uint16_t address ) +{ + /* CRC table */ + uint16_t xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 }; + uint16_t crc = 0; + int i; + + /* Make sure we have a valid address */ + address &= ~0x1F; + + /* Go through each bit in the address, and if set, xor the right value into the output */ + for( i = 15; i >= 5; i-- ) + { + /* Is this bit set? */ + if( ((address >> i) & 0x1) ) + { + crc ^= xor_table[i]; + } + } + + /* Just in case */ + crc &= 0x1F; + + /* Create a new address with the CRC appended */ + return address | crc; +} + +/* __calc_data_crc is from libdragon which is public domain. */ + +/** + * @brief Calculate the 8 bit CRC over a 32-byte block of data + * + * This function calculates the 8 bit CRC appropriate for checking a 32-byte + * block of data intended for or retrieved from a mempak. + * + * @param[in] data + * Pointer to 32 bytes of data to run the CRC over + * + * @return The calculated 8 bit CRC over the data + */ +static uint8_t __calc_data_crc( uint8_t *data ) +{ + uint8_t ret = 0; + int i,j; + + for( i = 0; i <= 32; i++ ) + { + for( j = 7; j >= 0; j-- ) + { + int tmp = 0; + + if( ret & 0x80 ) + { + tmp = 0x85; + } + + ret <<= 1; + + if( i < 32 ) + { + if( data[i] & (0x01 << j) ) + { + ret |= 0x1; + } + } + + ret ^= tmp; + } + } + + return ret; +} + +int gcn64lib_mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]) +{ + unsigned char cmd[64]; + //int cmdlen; + int n; + uint16_t addr_crc; + unsigned char crc; + + addr_crc = __calc_address_crc(addr); + + cmd[0] = N64_EXPANSION_READ; + cmd[1] = addr_crc>>8; // Address high byte + cmd[2] = addr_crc&0xff; // Address low byte + + n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd)); + if (n != 33) { + printf("Hey! %d\n", n); + return -1; + } + + memcpy(dst, cmd, 0x20); + + crc = __calc_data_crc(dst); + if (crc != cmd[32]) { + fprintf(stderr, "Bad CRC reading address 0x%04x\n", addr); + return -1; + } + + return 0x20; +} + +int gcn64lib_mempak_detect(gcn64_hdl_t hdl) +{ + unsigned char buf[0x20]; + int res; + + printf("Init 1\n"); + memset(buf, 0xfe, 32); + res = gcn64lib_expansionWrite(hdl, 0x8000, buf); + if (res != 0xe1) { + printf("res: %d\n", res); + return 0; + } + + printf("Init 2\n"); + memset(buf, 0x80, 32); + res = gcn64lib_expansionWrite(hdl, 0x8000, buf); + if (res != 0xe1) { + printf("res: %d\n", res); + return 0; + } + return -1; +} + +int gcn64lib_mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32]) +{ + return gcn64lib_expansionWrite(hdl, __calc_address_crc(addr), data); +} + +/** + * \brief Read a physical mempak + * \param hdl The Adapter handler + * \param channel The adapter channel (for multi-port adapters) + * \param pak Pointer to mempak_structure pointer to store the new mempak + * \param progressCb Callback to notify read progress (called after each block) + * \return 0: Success, -1: No mempak, -2: IO/error, -3: Other errors + */ +int gcn64lib_mempak_download(gcn64_hdl_t hdl, int channel, mempak_structure_t **mempak, void (*progressCb)(int cur_addr)) +{ + mempak_structure_t *pak; + unsigned short addr; + + if (!mempak) { + return -3; + } + + if (gcn64lib_mempak_detect(hdl)) { + return -1; + } + + pak = calloc(1, sizeof(mempak_structure_t)); + if (!pak) { + return -3; + } + pak->file_format = MPK_FORMAT_MPK; + + for (addr = 0x0000; addr < MEMPAK_MEM_SIZE; addr+= 0x20) + { + if (gcn64lib_mempak_readBlock(hdl, addr, &pak->data[addr]) != 0x20) { + fprintf(stderr, "Error: Short read\n"); + free(pak); + return -2; + } + if (progressCb) { + progressCb(addr); + } + } + *mempak = pak; + + return 0; +} + +int gcn64lib_mempak_upload(gcn64_hdl_t hdl, int channel, mempak_structure_t *pak, void (*progressCb)(int cur_addr)) +{ + unsigned short addr; + unsigned char readback[0x20]; + int res; + + if (!pak) { + return -3; + } + if (gcn64lib_mempak_detect(hdl)) { + return -1; + } + + for (addr = 0x0000; addr < MEMPAK_MEM_SIZE; addr+= 0x20) + { + res = gcn64lib_mempak_writeBlock(hdl, addr, &pak->data[addr]); + if (res < 0) { + fprintf(stderr, "Write error\n"); + return -2; + } + + if (0x20 != gcn64lib_mempak_readBlock(hdl, addr, readback)) { + // TODO : Why not retry? + fprintf(stderr, "readback failed\n"); + return -2; + } + + if (memcmp(readback, &pak->data[addr], 0x20)) { + fprintf(stderr, "Readback compare failed\n"); + return -2; + } + + if (progressCb) { + progressCb(addr); + } + } + + return 0; +} + diff --git a/tool/mempak_gcn64usb.h b/tool/mempak_gcn64usb.h new file mode 100644 index 0000000..0293fc5 --- /dev/null +++ b/tool/mempak_gcn64usb.h @@ -0,0 +1,13 @@ +#ifndef _mempak_gcn64usb_h__ +#define _mempak_gcn64usb_h__ + +#include "mempak.h" + +int gcn64lib_mempak_detect(gcn64_hdl_t hdl); +int gcn64lib_mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]); +int gcn64lib_mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32]); + +int gcn64lib_mempak_download(gcn64_hdl_t hdl, int channel, mempak_structure_t **mempak, void (*progressCb)(int cur_addr)); +int gcn64lib_mempak_upload(gcn64_hdl_t hdl, int channel, mempak_structure_t *pak, void (*progressCb)(int cur_addr)); + +#endif // _mempak_gcn64usb_h__ diff --git a/tool/mempak_old.c b/tool/mempak_old.c deleted file mode 100644 index 0be28db..0000000 --- a/tool/mempak_old.c +++ /dev/null @@ -1,335 +0,0 @@ -#include -#include -#include -#include -#include "hexdump.h" -#include "gcn64.h" -#include "gcn64lib.h" -#include "mempak_old.h" -#include "../gcn64_protocol.h" -#include "../requests.h" - -/* __calc_address_crc is from libdragon which is public domain. */ - -/** - * @brief Calculate the 5 bit CRC on a mempak address - * - * This function, given an address intended for a mempak read or write, will - * calculate the CRC on the address, returning the corrected address | CRC. - * - * @param[in] address - * The mempak address to calculate CRC over - * - * @return The mempak address | CRC - */ -static uint16_t __calc_address_crc( uint16_t address ) -{ - /* CRC table */ - uint16_t xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 }; - uint16_t crc = 0; - int i; - - /* Make sure we have a valid address */ - address &= ~0x1F; - - /* Go through each bit in the address, and if set, xor the right value into the output */ - for( i = 15; i >= 5; i-- ) - { - /* Is this bit set? */ - if( ((address >> i) & 0x1) ) - { - crc ^= xor_table[i]; - } - } - - /* Just in case */ - crc &= 0x1F; - - /* Create a new address with the CRC appended */ - return address | crc; -} - -/* __calc_data_crc is from libdragon which is public domain. */ - -/** - * @brief Calculate the 8 bit CRC over a 32-byte block of data - * - * This function calculates the 8 bit CRC appropriate for checking a 32-byte - * block of data intended for or retrieved from a mempak. - * - * @param[in] data - * Pointer to 32 bytes of data to run the CRC over - * - * @return The calculated 8 bit CRC over the data - */ -static uint8_t __calc_data_crc( uint8_t *data ) -{ - uint8_t ret = 0; - int i,j; - - for( i = 0; i <= 32; i++ ) - { - for( j = 7; j >= 0; j-- ) - { - int tmp = 0; - - if( ret & 0x80 ) - { - tmp = 0x85; - } - - ret <<= 1; - - if( i < 32 ) - { - if( data[i] & (0x01 << j) ) - { - ret |= 0x1; - } - } - - ret ^= tmp; - } - } - - return ret; -} - - -int mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32]) -{ - unsigned char cmd[40]; - int cmdlen; - int n; - uint16_t addr_crc; - - addr_crc = __calc_address_crc(addr); - - cmd[0] = N64_EXPANSION_WRITE; - cmd[1] = addr_crc>>8; // Address high byte - cmd[2] = addr_crc&0xff; // Address low byte - memcpy(cmd + 3, data, 0x20); - cmdlen = 3 + 0x20; - - n = gcn64lib_rawSiCommand(hdl, 0, cmd, cmdlen, cmd, sizeof(cmd)); - if (n != 1) { - printf("write block returned != 1 (%d)\n", n); - return -1; - } - - return cmd[0]; -} - -int mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]) -{ - unsigned char cmd[64]; - //int cmdlen; - int n; - uint16_t addr_crc; - unsigned char crc; - - addr_crc = __calc_address_crc(addr); - - cmd[0] = N64_EXPANSION_READ; - cmd[1] = addr_crc>>8; // Address high byte - cmd[2] = addr_crc&0xff; // Address low byte - - n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd)); - if (n != 33) { - printf("Hey! %d\n", n); - return -1; - } - - memcpy(dst, cmd, 0x20); - - crc = __calc_data_crc(dst); - if (crc != cmd[32]) { - fprintf(stderr, "Bad CRC reading address 0x%04x\n", addr); - return -1; - } - - return 0x20; -} - -int mempak_init(gcn64_hdl_t hdl) -{ - unsigned char buf[0x20]; - int res; - - memset(buf, 0xfe, 32); - res = mempak_readBlock(hdl, 0x8001, buf); - - if (res == 0xe1) { - return 0; - } - - return -1; -} - -#define DUMP_SIZE 0x8000 - -int mempak_writeAll(gcn64_hdl_t hdl, unsigned char srcbuf[0x8000]) -{ - unsigned short addr; - unsigned char readback[0x20]; - int res; - - for (addr = 0x0000; addr < DUMP_SIZE; addr+= 0x20) - { - printf("Writing address 0x%04x / 0x8000\r", addr); fflush(stdout); - res = mempak_writeBlock(hdl, addr, &srcbuf[addr]); - if (res < 0) { - fprintf(stderr, "Write error\n"); - return -1; - } - - if (0x20 != mempak_readBlock(hdl, addr, readback)) { - // TODO : Why not retry? - fprintf(stderr, "readback failed\n"); - return -2; - } - - } - printf("\nDone!\n"); - - return 0; -} - - -int mempak_readAll(gcn64_hdl_t hdl, unsigned char dstbuf[0x8000]) -{ - unsigned short addr; - - for (addr = 0x0000; addr < DUMP_SIZE; addr+= 0x20) - { - printf("Reading address 0x%04x / 0x8000\r", addr); fflush(stdout); - if (mempak_readBlock(hdl, addr, &dstbuf[addr]) != 0x20) { - fprintf(stderr, "Error: Short read\n"); - return -1; - } - } - printf("\nDone!\n"); - - return 0; -} - -int mempak_dump(gcn64_hdl_t hdl) -{ - unsigned char cardbuf[0x8000]; - int i,j; - - printf("Init pak\n"); - mempak_init(hdl); - - printf("Reading card...\n"); - i = mempak_readAll(hdl, cardbuf); - if (i<0) { - return i; - } - - for (i=0; i