#include #include #include #include typedef struct gb_mbc1_data { RIO *io; ut32 *rombank_to_io_map; ut32 *rambank_to_io_map; ut64 seek; ut32 size; int file_fd; int mem_fd; ut8 n_rombanks; ut8 n_rambanks; ut8 rombank_mask; ut8 rombank; union { ut8 high_rombank; ut8 rambank; }; bool ram_enable; bool small_ram; } MBC1; ut8 mbc1_mask[] = { 0x1, 0x3, 0x7, 0xf, 0x1f, 0x1f, 0x1f }; static RIODesc *__open(RIO *io, const char *pathname, int rw, int mode) { if (!r_str_startswith (pathname, "gb_mbc1://")) { 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[3] = {0xfb, 0xff, 0xff}; r_io_desc_read_at (desc, 0x147, mbc_info, 3); r_io_desc_close (desc); if (R_UNLIKELY (!mbc_info[0] || (mbc_info[0] > 3) || (mbc_info[1] > 6) || (mbc_info[2] > 2))) { //not a mbc1 game return NULL; } MBC1 *mbc = R_NEW0 (MBC1); if (!mbc) { return NULL; } mbc->n_rombanks = 0x1 << (mbc_info[1] + 1); mbc->rombank_to_io_map = R_NEWS0 (ut32, mbc->n_rombanks); if (!mbc->rombank_to_io_map) { free (mbc); return NULL; } mbc->rombank_mask = mbc1_mask[mbc_info[1]]; if (mbc_info[0] != 1) { switch (mbc_info[2]) { case 0x1: mbc->small_ram = true; case 0x3: mbc->n_rambanks = 4; break; case 0x2: mbc->n_rambanks = 1; break; } mbc->rambank_to_io_map = R_NEWS0 (ut32, mbc->n_rambanks); if (!mbc->rambank_to_io_map) { free (mbc->rombank_to_io_map); free (mbc); return NULL; } } mbc->io = r_io_new (); if (!mbc->io) { free (mbc->rombank_to_io_map); free (mbc->rambank_to_io_map); free (mbc); return NULL; } mbc->file_fd = r_io_fd_open (mbc->io, &pathname[10], R_PERM_R, 0); { char *malloc_uri = r_str_newf ("malloc://0x%"PFMT64x, mbc->small_ram? 0x800: (0x2000 * mbc->n_rambanks)); mbc->mem_fd = r_io_fd_open (mbc->io, malloc_uri, R_PERM_RW, 0); free (malloc_uri); } if ((mbc->file_fd < 0) || (mbc->mem_fd < 0)) { goto fail; } RIOMap *map = r_io_map_add (mbc->io, mbc->file_fd, R_PERM_R, 0ULL, 0ULL, 0x4000); if (!map) { goto fail; } mbc->rombank_to_io_map[0] = map->id; ut32 i; for (i = 1; i < mbc->n_rombanks; i++) { map = r_io_map_add_bottom (mbc->io, mbc->file_fd, R_PERM_R, 0x4000 * i, 0x4000, 0x4000); if (!map) { goto fail; } mbc->rombank_to_io_map[i] = map->id; } if (mbc->small_ram) { for (i = 0; i < 4; i++) { map = r_io_map_add (mbc->io, mbc->mem_fd, R_PERM_RW, 0ULL, 0xa000 + 0x800 * i, 0x800); if (!map) { goto fail; } mbc->rambank_to_io_map[i] = map->id; } } else { for (i = 0; i < 4; i++) { map = r_io_map_add_bottom (mbc->io, mbc->mem_fd, R_PERM_RW, 0x2000 * i, 0xa000, 0x2000); if (!map) { goto fail; } mbc->rambank_to_io_map[i] = map->id; } } mbc->rombank = 1; mbc->io->cache.mode = 0; desc = r_io_desc_new (io, &r_io_plugin_gb_mbc1, pathname, R_PERM_RWX, mode, mbc); return desc; fail: r_io_free (mbc->io); free (mbc->rombank_to_io_map); free (mbc->rambank_to_io_map); free (mbc); return NULL; }