diff --git a/include/gb.h b/include/gb.h index 362b10c..0a08e93 100644 --- a/include/gb.h +++ b/include/gb.h @@ -28,5 +28,6 @@ void gb_timers_update(GBTimers *timers, ut32 cycles); extern RIOPlugin r_io_plugin_gb_timers; extern RIOPlugin r_io_plugin_gb_mbc1; +extern RIOPlugin r_io_plugin_gb_mbc2; #endif diff --git a/io/mbc1.c b/io/mbc1.c index dc9cd1c..1a5de35 100644 --- a/io/mbc1.c +++ b/io/mbc1.c @@ -3,6 +3,10 @@ #include #include +#ifndef GB_H +RIOPlugin r_io_plugin_gb_mbc1; +#endif + typedef struct gb_mbc1_data { RIO *io; ut32 *rombank_to_io_map; @@ -110,6 +114,9 @@ static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) { } mbc->io->va = true; mbc->file_fd = r_io_fd_open (mbc->io, &pathname[10], R_PERM_R, 0); + if (mbc->file_fd < 0) { + goto fail; + } if (mbc->n_rambanks) { char *malloc_uri = r_str_newf ("malloc://0x%"PFMT64x, mbc->small_ram? 0x800: (0x2000 * mbc->n_rambanks)); @@ -119,9 +126,6 @@ static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) { goto fail; } } - if (mbc->file_fd < 0) { - goto fail; - } RIOMap *map; ut32 i; for (i = 0; i < mbc->n_rombanks; i++) { diff --git a/io/mbc2.c b/io/mbc2.c new file mode 100644 index 0000000..247174a --- /dev/null +++ b/io/mbc2.c @@ -0,0 +1,281 @@ +#include +//#include +#include +#include + +typedef struct gb_mbc2_ram_data { + ut64 seek; + ut8 buf[512]; +} MBC2Ram; + +RIOPlugin r_io_plugin_gb_mbc2_ram; +#ifndef GB_H +RIOPlugin r_io_plugin_gb_mbc2; +#endif + +static bool __ram_check(RIO *io, const char *pathname, bool many) { + return r_str_startswith (pathname, "gb_mbc2_ram://"); +} + +static RIODesc *__ram_open(RIO *io, const char *pathname, int rw, int mode) { + if (!r_str_startswith (pathname, "gb_mbc2_ram://")) { + return NULL; + } + MBC2Ram *ram = R_NEW0 (MBC2Ram); + if (!ram) { + return NULL; + } + RIODesc *desc = r_io_desc_new (io, &r_io_plugin_gb_mbc2_ram, pathname, + R_PERM_RWX, mode, ram); + if (!desc) { + free (ram); + return NULL; + } + memset (ram->buf, 0xf0, 512 * sizeof (ut8)); + return desc; +} + +static bool __ram_close(RIODesc *desc) { + free (desc->data); + return true; +} + +static ut64 __ram_lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { + MBC2Ram *ram = (MBC2Ram *)desc->data; + switch (whence) { + case R_IO_SEEK_SET: + return ram->seek = R_MIN (0x200, offset); + case R_IO_SEEK_CUR: + return ram->seek = R_MIN (0x200, ram->seek + offset); + case R_IO_SEEK_END: + return ram->seek = 0x200; + } + return ram->seek; +} + +static int __ram_read(RIO *io, RIODesc *desc, ut8 *buf, int len) { + MBC2Ram *ram = (MBC2Ram *)desc->data; + len = R_MIN (len, 0x200 - ram->seek); + if (len < 0) { + return -1; + } + memcpy (buf, &ram->buf[ram->seek], len); + ram->seek += len; + return len; +} + +static int __ram_write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { + MBC2Ram *ram = (MBC2Ram *)desc->data; + const int ret = len = R_MIN (len, 0x200 - ram->seek); + if (len < 0) { + return -1; + } + memcpy (&ram->buf[ram->seek], buf, len); + if (R_UNLIKELY (len > 7)) { + ut32 ut64len = len >> 3; + do { + ut64 *buf64 = (ut64 *)(&ram->buf[ram->seek]); + *buf64 |= 0xf0f0f0f0f0f0f0f0; + ram->seek += 8; + } while (--ut64len); + len &= 7; + } + do { + ram->buf[ram->seek] |= 0xf0; + ram->seek++; + } while (--len); + return ret; +} + +RIOPlugin r_io_plugin_gb_mbc2_ram = { + .meta = { + .name = "gb_mbc2_ram", + .desc = "gb_mbc2_ram", + .license = "LGPL", + }, + .uris = "gb_mbc2_ram://", + .open = __ram_open, + .close = __ram_close, + .read = __ram_read, + .check = __ram_check, + .seek = __ram_lseek, + .write = __ram_write, +}; + +typedef struct gb_mbc2_data { + RIO *io; + RIODesc *mem_desc; + ut32 rombank_to_io_map[16]; + ut64 seek; + int file_fd; + ut8 rombank; + bool ram_enable; +} MBC2; + +static void disable_ram (MBC2 *mbc) { + mbc->mem_desc->perm = 0; + mbc->ram_enable = false; +} + +static void enable_ram (MBC2 *mbc) { + mbc->mem_desc->perm = R_PERM_RW; + mbc->ram_enable = true; +} + +static bool __check(RIO *io, const char *pathname, bool many) { + return r_str_startswith (pathname, "gb_mbc2://"); +} + + +static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) { + if (!r_str_startswith (pathname, "gb_mbc2://")) { + return NULL; + } + if (!r_file_exists (&pathname[10]) || !r_file_is_regular (&pathname[10])) { + return NULL; + } + RIODesc *desc = r_io_desc_open (io, &pathname[10], R_PERM_R, 0); + if (!desc) { + return NULL; + } + ut8 mbc_info = 0xfb; + r_io_desc_read_at (desc, 0x147, &mbc_info, 1); + r_io_desc_close (desc); + if (R_UNLIKELY (mbc_info != 0x5 || mbc_info != 0x6)) { + //not a mbc2 rom + return NULL; + } + MBC2 *mbc = R_NEW0 (MBC2); + if (!mbc) { + return NULL; + } + mbc->io = r_io_new (); + if (!mbc->io) { + free (mbc); + return NULL; + } + mbc->io->va = true; + mbc->file_fd = r_io_fd_open (mbc->io, &pathname[10], R_PERM_R, 0); + if (mbc->file_fd < 0) { + goto fail; + } + mbc->mem_desc = r_io_desc_open_plugin (mbc->io, &r_io_plugin_gb_mbc2_ram, "gb_mbc2_ram://", + R_PERM_RW, 0); + if (!mbc->mem_desc) { + goto fail; + } + RIOMap *map = r_io_map_add (mbc->io, mbc->file_fd, R_PERM_R, 0ULL, 0ULL, 0x4000); + mbc->rombank_to_io_map[0] = map->id; + if (R_UNLIKELY (!r_io_map_add (mbc->io, mbc->mem_desc->fd, R_PERM_RW, 0ULL, + 0xa000, 0x200))) { + goto fail; + } + ut32 i; + for (i = 1; i < 16; i++) { + map = r_io_map_add_bottom (mbc->io, mbc->file_fd, R_PERM_R, + 0x4000 * i, 0x4000, 0x4000); + if (R_UNLIKELY (!map)) { + goto fail; + } + if (R_UNLIKELY (!r_io_map_add (mbc->io, mbc->mem_desc->fd, R_PERM_RW, + 0ULL, 0xa000 + i * 0x200, 0x200))) { + goto fail; + } + mbc->rombank_to_io_map[i] = map->id; + } + mbc->io->cache.mode = 0; + desc = r_io_desc_new (io, &r_io_plugin_gb_mbc2, pathname, R_PERM_RWX, mode, mbc); + if (!desc) { + goto fail; + } + mbc->io->va = true; + mbc->rombank = 1; + disable_ram (mbc); + return desc; +fail: + r_io_free (mbc->io); + free (mbc); + return NULL; +} + +static bool __close(RIODesc *desc) { + MBC2 *mbc = (MBC2 *)desc->data; + r_io_free (mbc->io); + R_FREE (desc->data); + return true; +} + +static ut64 __lseek(RIO* io, RIODesc *desc, ut64 offset, int whence) { + MBC2 *mbc = (MBC2 *)desc->data; + switch (whence) { + case R_IO_SEEK_SET: + return mbc->seek = R_MIN (0xc000, offset); + case R_IO_SEEK_CUR: + return mbc->seek = R_MIN (0xc000, mbc->seek + offset); + case R_IO_SEEK_END: + return mbc->seek = 0xc000; + } + return mbc->seek; +} + +static int __write(RIO *io, RIODesc *desc, const ut8 *buf, int len) { + MBC2 *mbc = (MBC2 *)desc->data; + len = R_MIN (len, 0xc000 - mbc->seek); + r_io_write_at (mbc->io, mbc->seek, buf, len); + ut64 endseek = mbc->seek + len; + while ((mbc->seek < 0x4000) && (mbc->seek < endseek)) { + const ut32 i = mbc->seek - (endseek - len); + if (mbc->seek & 0x100) { + ut8 rombank = buf[i] & 0x0f; + if (!rombank) { + rombank++; + } + r_io_map_priorize (mbc->io, mbc->rombank_to_io_map[rombank]); + mbc->rombank = rombank; + } else { + if (buf[i] == 0x0a) { + enable_ram (mbc); + } else { + disable_ram (mbc); + } + } + mbc->seek++; + } + mbc->seek = endseek; + return len; +} + +static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { + MBC2 *mbc = (MBC2 *)desc->data; + len = R_MIN (len, 0xc000 - mbc->seek); + if (!r_io_read_at (mbc->io, mbc->seek, buf, len)) { + eprintf ("r_io_read_at failed\n"); + return -1; + } + eprintf ("read @ 0x%"PFMT64x" %d bytes\n", mbc->seek, len); + mbc->seek += len; + return len; +} + +RIOPlugin r_io_plugin_gb_mbc2 = { + .meta = { + .name = "gb_mbc2", + .desc = "gb_mbc2", + .license = "LGPL", + }, + .uris = "gb_mbc2://", + .open = __open, + .close = __close, + .read = __read, + .check = __check, + .seek = __lseek, + .write = __write, +}; + +#ifndef R2_PLUGIN_INCORE +R_API RLibStruct radare_plugin = { + .type = R_LIB_TYPE_IO, + .data = &r_io_plugin_gb_mbc2, + .version = R2_VERSION +}; +#endif