ragb/io/mbc2.c
2024-10-14 16:44:06 +02:00

282 lines
6.3 KiB
C

#include <stdio.h>
//#include <gb.h>
#include <r_io.h>
#include <r_util.h>
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