#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