diff --git a/tool/n64savetool/mempak.c b/tool/n64savetool/mempak.c index 1969d71..0dd00f5 100644 --- a/tool/n64savetool/mempak.c +++ b/tool/n64savetool/mempak.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "mempak.h" #define DEXDRIVE_DATA_OFFSET 0x1040 @@ -21,6 +22,168 @@ mempak_structure_t *mempak_new(void) return mpk; } +static int mempak_findFreeNote(mempak_structure_t *mpk, entry_structure_t *entry_data, int *note_id) +{ + int i; + + if (!mpk) + return -1; + if (!entry_data) + return -1; + + for (i=0; ivalid) { + if (note_id) { + *note_id = i; + } + return 0; + } + } + + return -1; +} + +/** + * \param mpk The memory pack to operate on + * \param notefile The filename of the note to load + * \param dst_note_id 0-15: (Over)write to specific note, -1: auto (first free) + * \param note_id Stores the id of the note that was used + */ +int mempak_importNote(mempak_structure_t *mpk, const char *notefile, int dst_note_id, int *note_id) +{ + int free_blocks = get_mempak_free_space(mpk); + FILE *fptr; + unsigned char entry_data[32]; + unsigned char *data = NULL; + entry_structure_t entry; + int res; + + if (dst_note_id < -1 || dst_note_id > 15) { + fprintf(stderr, "Out of bound dst_note_id\n"); + return -1; + } + + printf("Current free blocks: %d\n", free_blocks); + + fptr = fopen(notefile, "rb"); + if (!fptr) { + perror("fopen"); + return -1; + } + + if (1 != fread(entry_data, 32, 1, fptr)) { + perror("fread"); + fclose(fptr); + return -1; + } + + /* I follow the same convention as bryc's javascript mempak editor[1] + * by looking for an inode number of 0xCAFE. + * + * [1] https://github.com/bryc/mempak + * + * If there are other note formats to support, this will need updating. + */ + if ((entry_data[0x06] == 0xCA) && + (entry_data[0x07] == 0xFE)) + { + entry_structure_t oldentry; + long filesize; + + // Fixup the inode number (0xCAFE is invalid) + entry_data[0x06] = 0x00; + entry_data[0x07] = 0x05; // BLOCK_VALID_FIRST; + + if (0 != mempak_parse_entry(entry_data, &entry)) { + fprintf(stderr, "Error loading note (invalid)\n"); + fclose(fptr); + return -1; + } + + fseek(fptr, 0, SEEK_END); + filesize = ftell(fptr); + fseek(fptr, 32, SEEK_SET); + + // Remove the note header + filesize -= 32; + if (filesize % MEMPAK_BLOCK_SIZE) { + fprintf(stderr, "Invalid note file size\n"); + fclose(fptr); + return -1; + } + + entry.blocks = filesize / MEMPAK_BLOCK_SIZE; + + printf("Note size: %d blocks\n", entry.blocks); + printf("Note name: %s\n", entry.name); + + if (entry.blocks > free_blocks) { + fprintf(stderr, "Not enough space (note is %d blocks and only %d free blocks in mempak)\n", + entry.blocks, free_blocks); + fclose(fptr); + return -1; + } + + data = calloc(1, entry.blocks * MEMPAK_BLOCK_SIZE); + if (!data) { + perror("calloc"); + fclose(fptr); + return -1; + } + + if (1 != fread(data, entry.blocks * MEMPAK_BLOCK_SIZE, 1, fptr)) { + perror("could not load note data"); + free(data); + fclose(fptr); + return -1; + } + + if (dst_note_id == -1) { // Auto (first free note) + if (0 != mempak_findFreeNote(mpk, &oldentry, note_id)) { + fprintf(stderr, "Could not find an empty note\n"); + free(data); + fclose(fptr); + return -1; + } + } else { // Specific note + get_mempak_entry(mpk, dst_note_id, &oldentry); + if (oldentry.valid) { + printf("Overwriting note %d\n", dst_note_id); + delete_mempak_entry(mpk, &oldentry); + } else { + fprintf(stderr, "No note id %d\n", dst_note_id); + free(data); + fclose(fptr); + return -1; + } + if (note_id) + *note_id = dst_note_id; + } + + res = write_mempak_entry_data(mpk, &entry, data); + if (res != 0) { + fprintf(stderr, "Failed to write note (error %d)\n", res); + free(data); + fclose(fptr); + return -1; + } + + return 0; + } else { + fprintf(stderr, "Input file does not appear to be in a supported format.\n"); + } + + if (data) + free(data); + fclose(fptr); + return -1; +} + int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_filename) { FILE *fptr; @@ -45,7 +208,7 @@ int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_file return -1; } - fptr = fopen(dst_filename, "w"); + fptr = fopen(dst_filename, "wb"); if (!fptr) { perror("fopen"); return -1; @@ -56,8 +219,8 @@ int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_file * * [1] https://github.com/bryc/mempak */ - note_header.raw_data[0x07] = 0xCA; - note_header.raw_data[0x08] = 0xFE; + note_header.raw_data[0x06] = 0xCA; + note_header.raw_data[0x07] = 0xFE; fwrite(note_header.raw_data, 32, 1, fptr); fwrite(databuf, MEMPAK_BLOCK_SIZE, note_header.blocks, fptr); @@ -73,7 +236,7 @@ int mempak_saveToFile(mempak_structure_t *mpk, const char *dst_filename, unsigne if (!mpk) return -1; - fptr = fopen(dst_filename, "w"); + fptr = fopen(dst_filename, "wb"); if (!fptr) { perror("fopen"); return -1; @@ -143,9 +306,9 @@ mempak_structure_t *mempak_loadFromFile(const char *filename) /* Raw binary images. Those can contain more than one card's data. For * instance, Mupen64 seems to contain four saves. (I suppose each 32kB block is * for the virtual mempak of one controller) */ - for (i=0; i<4; i++) { + for (i=1; i<=4; i++) { if (file_size == 0x8000*i) { - num_images = i+1; + num_images = i; printf("MPK file Contains %d image(s)\n", num_images); if (file_size == 0x8000) { mpk->file_format = MPK_FORMAT_MPK; @@ -200,3 +363,30 @@ void mempak_free(mempak_structure_t *mpk) free(mpk); } +const char *mempak_format2string(int fmt) +{ + switch(fmt) + { + case MPK_FORMAT_MPK: return "MPK"; + case MPK_FORMAT_MPK4: return "MPK4"; + case MPK_FORMAT_N64: return "N64"; + case MPK_FORMAT_INVALID: return "Invalid"; + default: + return "Unknown"; + } +} + +int mempak_string2format(const char *str) +{ + if (0 == strcasecmp(str, "mpk")) + return MPK_FORMAT_MPK; + + if (0 == strcasecmp(str, "mpk4")) + return MPK_FORMAT_MPK4; + + if (0 == strcasecmp(str, "n64")) + return MPK_FORMAT_N64; + + return MPK_FORMAT_INVALID; +} + diff --git a/tool/n64savetool/mempak.h b/tool/n64savetool/mempak.h index dee8565..8ee8b40 100644 --- a/tool/n64savetool/mempak.h +++ b/tool/n64savetool/mempak.h @@ -5,9 +5,10 @@ #define MAX_NOTE_COMMENT_SIZE 257 // including 0 termination -#define MPK_FORMAT_MPK 0 -#define MPK_FORMAT_MPK4 1 // MPK + 3 times 32kB padding -#define MPK_FORMAT_N64 2 +#define MPK_FORMAT_INVALID 0 +#define MPK_FORMAT_MPK 1 +#define MPK_FORMAT_MPK4 2 // MPK + 3 times 32kB padding +#define MPK_FORMAT_N64 3 typedef struct mempak_structure { @@ -22,8 +23,12 @@ mempak_structure_t *mempak_loadFromFile(const char *filename); int mempak_saveToFile(mempak_structure_t *mpk, const char *dst_filename, unsigned char format); int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_filename); +int mempak_importNote(mempak_structure_t *mpk, const char *notefile, int dst_note_id, int *note_id); void mempak_free(mempak_structure_t *mpk); +int mempak_string2format(const char *str); +const char *mempak_format2string(int fmt); + #include "mempak_fs.h" #endif // _mempak_h__