Implemented a sort-of-working SNES/SFC file format

This commit is contained in:
usr_share 2017-03-27 08:44:48 +03:00 committed by pancake
parent 5fc6dc30a8
commit 6d9db7e891
6 changed files with 372 additions and 4 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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}

301
libr/bin/p/bin_sfc.c Normal file
View File

@ -0,0 +1,301 @@
/* radare - LGPL3 - 2015-2016 - maijin */
#include <r_bin.h>
#include <r_lib.h>
#include "sfc/sfc_specs.h"
#include <r_endian.h>
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

10
libr/bin/p/sfc.mk Normal file
View File

@ -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)