From 6d9db7e8918e92d8ac28da4fd91a21bf19a39960 Mon Sep 17 00:00:00 2001 From: usr_share Date: Mon, 27 Mar 2017 08:44:48 +0300 Subject: [PATCH] Implemented a sort-of-working SNES/SFC file format --- libr/anal/p/anal_snes.c | 4 +- libr/asm/p/asm_snes.c | 1 - libr/bin/format/sfc/sfc_specs.h | 58 ++++++ libr/bin/p/Makefile | 2 +- libr/bin/p/bin_sfc.c | 301 ++++++++++++++++++++++++++++++++ libr/bin/p/sfc.mk | 10 ++ 6 files changed, 372 insertions(+), 4 deletions(-) create mode 100644 libr/bin/format/sfc/sfc_specs.h create mode 100644 libr/bin/p/bin_sfc.c create mode 100644 libr/bin/p/sfc.mk diff --git a/libr/anal/p/anal_snes.c b/libr/anal/p/anal_snes.c index 5d50fadc0b..c8dacc0644 100644 --- a/libr/anal/p/anal_snes.c +++ b/libr/anal/p/anal_snes.c @@ -152,7 +152,7 @@ static int snes_anop(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int l break; case 0x4c: // jmp addr op->eob = true; - op->jump = ut8p_bw (data+1); + op->jump = (addr & 0xFF0000) | ut8p_bw (data+1); op->type = R_ANAL_OP_TYPE_JMP; break; case 0x5c: // jmp long @@ -202,7 +202,7 @@ static int snes_anop(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int l op->type = R_ANAL_OP_TYPE_CJMP; break; case 0x20: // jsr addr - op->jump = ut8p_bw (data+1); + op->jump = (addr & 0xFF0000) | ut8p_bw (data+1); op->type = R_ANAL_OP_TYPE_CALL; break; case 0x22: // jsr long diff --git a/libr/asm/p/asm_snes.c b/libr/asm/p/asm_snes.c index 4d923683f7..7c10b4eca9 100644 --- a/libr/asm/p/asm_snes.c +++ b/libr/asm/p/asm_snes.c @@ -19,7 +19,6 @@ static bool snes_asm_fini (void* user) { free(snesflags); snesflags = NULL; return 0; -} static int disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) { int dlen = snesDisass (snesflags->M, snesflags->X, a->pc, op, buf, len); diff --git a/libr/bin/format/sfc/sfc_specs.h b/libr/bin/format/sfc/sfc_specs.h new file mode 100644 index 0000000000..9a974c72c7 --- /dev/null +++ b/libr/bin/format/sfc/sfc_specs.h @@ -0,0 +1,58 @@ +/* radare - LGPL - 2015 - maijin */ + +//CPU_memory_map: http://wiki.nesdev.com/w/index.php/CPU_memory_map + +#ifndef _SFC_SPECS_H +#define _SFC_SPECS_H + +#define LOROM_PAGE_SIZE 0x8000 +#define HIROM_PAGE_SIZE 0x10000 +#define BANK_SIZE 0x10000 + +#define SFC_HDR_SIZE sizeof (sfc_int_hdr) +#define LOROM_HDR_LOC 0x7FC0 +#define HIROM_HDR_LOC 0xFFC0 + +#define ADDMEM_START_ADDRESS 0x6000 +#define ADDMEM_SIZE 0x2000 + +//identical for both LoROM and HiROM + +#define PPU1_REG_ADDRESS 0x2100 +#define PPU1_REG_SIZE 0x0100 + +#define DSP_REG_ADDRESS 0x3000 +#define DSP_REG_SIZE 0x1000 + +#define OLDJOY_REG_ADDRESS 0x4000 +#define OLDJOY_REG_SIZE 0x0100 + +#define PPU2_REG_ADDRESS 0x4200 +#define PPU2_REG_SIZE 0x0300 + +#define LOWRAM_START_ADDRESS 0x7E0000 +#define LOWRAM_SIZE 0x2000 + +#define LOWRAM_MIRROR_START_ADDRESS 0x0000 +#define LOWRAM_MIRROR_SIZE 0x2000 + +#define HIRAM_START_ADDRESS 0x7E2000 +#define HIRAM_SIZE 0x6000 + +#define EXTRAM_START_ADDRESS 0x7E8000 +#define EXTRAM_SIZE 0x18000 + +typedef struct __attribute__((__packed__)) { + char name[0x15]; //game title. + ut8 rom_setup; //ROM setup (LoROM/HiROM, etc.) + ut8 rom_type; + ut8 rom_size; //in 1kb chunks + ut8 sram_size; //in 1kb chunks + ut8 dest_code; + ut8 fixed_0x33; //should be equal to 0x33 + ut8 rom_version; + ut16 comp_check; //should be equal to ~checksum + ut16 checksum; +} sfc_int_hdr; + +#endif // _SFC_SPECS_H diff --git a/libr/bin/p/Makefile b/libr/bin/p/Makefile index 4b594f1b38..35703531a8 100644 --- a/libr/bin/p/Makefile +++ b/libr/bin/p/Makefile @@ -14,7 +14,7 @@ FORMATS=any.mk elf.mk elf64.mk pe.mk pe64.mk te.mk mach0.mk FORMATS+=bios.mk mach064.mk fatmach0.mk dyldcache.mk java.mk FORMATS+=dex.mk fs.mk ningb.mk coff.mk ningba.mk xbe.mk zimg.mk FORMATS+=omf.mk cgc.mk dol.mk nes.mk mbn.mk psxexe.mk spc700.mk -FORMATS+=vsf.mk nin3ds.mk xtr_dyldcache.mk bflt.mk wasm.mk +FORMATS+=vsf.mk nin3ds.mk xtr_dyldcache.mk bflt.mk wasm.mk sfk.mk include $(FORMATS) all: ${ALL_TARGETS} diff --git a/libr/bin/p/bin_sfc.c b/libr/bin/p/bin_sfc.c new file mode 100644 index 0000000000..b88765fc26 --- /dev/null +++ b/libr/bin/p/bin_sfc.c @@ -0,0 +1,301 @@ +/* radare - LGPL3 - 2015-2016 - maijin */ + +#include +#include +#include "sfc/sfc_specs.h" +#include + +static bool check_bytes(const ut8 *buf, ut64 length) { + + const ut8* buf_hdr = buf; + ut16 cksum1, cksum2; + + if ((length & 0x8000) == 0x200) buf_hdr += 0x200; + + //determine if ROM is headered, and add a 0x200 gap if so. + cksum1 = r_read_le16(buf_hdr + 0x7FDC); + cksum2 = r_read_le16(buf_hdr + 0x7FDE); + + if (cksum1 == (ut16)~cksum2) return true; + + cksum1 = r_read_le16(buf_hdr + 0xFFDC); + cksum2 = r_read_le16(buf_hdr + 0xFFDE); + + if (cksum1 == (ut16)~cksum2) return true; + return false; +} + +static bool check(RBinFile *arch) { + const ut8 *bytes = arch ? r_buf_buffer (arch->buf) : NULL; + ut64 sz = arch ? r_buf_size (arch->buf): 0; + return check_bytes (bytes, sz); +} + +static void * load_bytes(RBinFile *arch, const ut8 *buf, ut64 sz, ut64 loadaddr, Sdb *sdb){ + check_bytes (buf, sz); + return R_NOTNULL; +} + +static RBinInfo* info(RBinFile *arch) { + RBinInfo *ret = NULL; + + int hdroffset = 0; + if ((arch->size & 0x8000) == 0x200) hdroffset = 0x200; + + sfc_int_hdr sfchdr; + memset (&sfchdr, 0, SFC_HDR_SIZE); + + int reat = r_buf_read_at (arch->buf, 0x7FC0 + hdroffset, (ut8*)&sfchdr, SFC_HDR_SIZE); + if (reat != SFC_HDR_SIZE) { + eprintf ("Unable to read SFC/SNES header\n"); + return NULL; + } + + if ( (sfchdr.comp_check != (ut16)~(sfchdr.checksum)) || ((sfchdr.rom_setup & 0x1) != 0) ){ + + // if the fixed 0x33 byte or the LoROM indication are not found, then let's try interpreting the ROM as HiROM + + reat = r_buf_read_at (arch->buf, 0xFFC0 + hdroffset, (ut8*)&sfchdr, SFC_HDR_SIZE); + if (reat != SFC_HDR_SIZE) { + eprintf ("Unable to read SFC/SNES header\n"); + return NULL; + } + + if ( (sfchdr.comp_check != (ut16)~(sfchdr.checksum)) || ((sfchdr.rom_setup & 0x1) != 1) ) { + + eprintf ("Cannot determine if this is a LoROM or HiROM file\n"); + return NULL; + } + } + + if (!(ret = R_NEW0 (RBinInfo))) + return NULL; + + ret->file = strdup (arch->file); + ret->type = strdup ("ROM"); + ret->machine = strdup ("Super NES / Super Famicom"); + ret->os = strdup ("snes"); + ret->arch = strdup ("snes"); + ret->bits = 16; + ret->has_va = 1; + + return ret; +} + +static void addrom(RList *ret, const char *name, int i, ut64 paddr, ut64 vaddr, ut32 size) { + RBinSection *ptr = R_NEW0 (RBinSection); + if (!ptr) return; + sprintf(ptr->name,"%s_%02x",name,i); + ptr->paddr = paddr; + ptr->vaddr = vaddr; + ptr->size = ptr->vsize = size; + ptr->srwx = R_BIN_SCN_READABLE | R_BIN_SCN_EXECUTABLE | R_BIN_SCN_MAP; + ptr->add = true; + r_list_append (ret, ptr); +} + +static void addsym(RList *ret, const char *name, ut64 addr, ut32 size) { + RBinSymbol *ptr = R_NEW0 (RBinSymbol); + if (!ptr) return; + ptr->name = strdup (name? name: ""); + ptr->paddr = ptr->vaddr = addr; + ptr->size = size; + ptr->ordinal = 0; + r_list_append (ret, ptr); +} + +static RList* symbols(RBinFile *arch) { + RList *ret = NULL; + if (!(ret = r_list_new ())) + return NULL; + ret->free = free; + return ret; +} + +static RList* sections(RBinFile *arch) { + RList *ret = NULL; + RBinSection *ptr = NULL; + int hdroffset = 0; + ut8 is_hirom = 0; + int i=0; //0x8000-long bank number for loops + + if ((arch->size & 0x8000) == 0x200) hdroffset = 0x200; + + sfc_int_hdr sfchdr; + memset (&sfchdr, 0, SFC_HDR_SIZE); + + int reat = r_buf_read_at (arch->buf, 0x7FC0 + hdroffset, (ut8*)&sfchdr, SFC_HDR_SIZE); + if (reat != SFC_HDR_SIZE) { + eprintf ("Unable to read SFC/SNES header\n"); + return NULL; + } + + if ( (sfchdr.comp_check != (ut16)~(sfchdr.checksum)) || ((sfchdr.rom_setup & 0x1) != 0) ){ + + // if the fixed 0x33 byte or the LoROM indication are not found, then let's try interpreting the ROM as HiROM + + reat = r_buf_read_at (arch->buf, 0xFFC0 + hdroffset, (ut8*)&sfchdr, SFC_HDR_SIZE); + if (reat != SFC_HDR_SIZE) { + eprintf ("Unable to read SFC/SNES header\n"); + return NULL; + } + + if ( (sfchdr.comp_check != (ut16)~(sfchdr.checksum)) || ((sfchdr.rom_setup & 0x1) != 1) ) { + + eprintf ("Cannot determine if this is a LoROM or HiROM file\n"); + return NULL; + } + is_hirom = 1; + } + + if (!(ret = r_list_new ())) + return NULL; + + + if (is_hirom) { + + for (i=0; i < ((arch->size - hdroffset)/ 0x8000) ; i++) { + + addrom(ret,"ROM",i,hdroffset + i*0x8000,0x400000 + (i*0x8000), 0x8000); + if (i % 2) addrom(ret,"ROM_MIRROR",i,hdroffset + i*0x8000,(i*0x8000), 0x8000); + } + + } else { + for (i=0; i < ((arch->size - hdroffset)/ 0x8000) ; i++) { + + addrom(ret,"ROM",i,hdroffset + i*0x8000,0x8000 + (i*0x10000), 0x8000); + } + } + return ret; +} + +static RList *mem (RBinFile *arch) { + RList *ret; + RBinMem *m, *n; + if (!(ret = r_list_new())) + return NULL; + ret->free = free; + if (!(m = R_NEW0 (RBinMem))) { + r_list_free (ret); + return NULL; + } + + m->name = strdup ("LOWRAM"); + m->addr = LOWRAM_START_ADDRESS; + m->size = LOWRAM_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(n = R_NEW0 (RBinMem))) + return ret; + + m->mirrors = r_list_new(); + + n->name = strdup ("LOWRAM_MIRROR"); + n->addr = LOWRAM_MIRROR_START_ADDRESS; + n->size = LOWRAM_MIRROR_SIZE; + n->perms = r_str_rwx ("rwx"); + r_list_append (m->mirrors, n); + if (!(n = R_NEW0 (RBinMem))) { + r_list_free (m->mirrors); + m->mirrors = NULL; + return ret; + } + + m->name = strdup ("HIRAM"); + m->addr = HIRAM_START_ADDRESS; + m->size = HIRAM_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(n = R_NEW0 (RBinMem))) + return ret; + + m->name = strdup ("EXTRAM"); + m->addr = EXTRAM_START_ADDRESS; + m->size = EXTRAM_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(n = R_NEW0 (RBinMem))) + return ret; + + + m->name = strdup ("PPU1_REG"); + m->addr = PPU1_REG_ADDRESS; + m->size = PPU1_REG_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(m = R_NEW0 (RBinMem))) { + r_list_free (ret); + return NULL; + } + + m->name = strdup ("DSP_REG"); + m->addr = DSP_REG_ADDRESS; + m->size = DSP_REG_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(m = R_NEW0 (RBinMem))) { + r_list_free (ret); + return NULL; + } + + m->name = strdup ("OLDJOY_REG"); + m->addr = OLDJOY_REG_ADDRESS; + m->size = OLDJOY_REG_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(m = R_NEW0 (RBinMem))) { + r_list_free (ret); + return NULL; + } + + m->name = strdup ("PPU2_REG"); + m->addr = PPU2_REG_ADDRESS; + m->size = PPU2_REG_SIZE; + m->perms = r_str_rwx ("rwx"); + r_list_append (ret, m); + if (!(m = R_NEW0 (RBinMem))) { + r_list_free (ret); + return NULL; + } + + return ret; +} + +static RList* entries(RBinFile *arch) { //Should be 3 offsets pointed by NMI, RESET, IRQ after mapping && default = 1st CHR + RList *ret; + if (!(ret = r_list_new ())) { + return NULL; + } + /* + RBinAddr *ptr = NULL; + if (!(ptr = R_NEW0 (RBinAddr))) { + return ret; + } + ptr->paddr = INES_HDR_SIZE; + ptr->vaddr = ROM_START_ADDRESS; + r_list_append (ret, ptr); + */ + return ret; +} + +RBinPlugin r_bin_plugin_sfc = { + .name = "sfc", + .desc = "Super NES / Super Famicom ROM file", + .license = "LGPL3", + .load_bytes = &load_bytes, + .check = &check, + .check_bytes = &check_bytes, + .entries = &entries, + .sections = sections, + .symbols = &symbols, + .info = &info, + .mem = &mem, +}; + +#ifndef CORELIB +RLibStruct radare_plugin = { + .type = R_LIB_TYPE_BIN, + .data = &r_bin_plugin_sfc, + .version = R2_VERSION +}; +#endif diff --git a/libr/bin/p/sfc.mk b/libr/bin/p/sfc.mk new file mode 100644 index 0000000000..632eda22b7 --- /dev/null +++ b/libr/bin/p/sfc.mk @@ -0,0 +1,10 @@ +OBJ_SFC=bin_sfc.o + +STATIC_OBJ+=${OBJ_SFC} +TARGET_SFC=bin_sfc.${EXT_SO} + +ALL_TARGETS+=${TARGET_SFC} + +${TARGET_SFC}: ${OBJ_SFC} + ${CC} $(call libname,bin_sfc) -shared ${CFLAGS} \ + -o ${TARGET_SFC} ${OBJ_SFC} $(LINK) $(LDFLAGS)