This topic is for discussing how to detect the SNES internal header. This is not the same thing as the copier header, which is trivial to detect (well, so long as file is padded to a multiple of 1kb or so.)
This is what I have at the moment:
EDIT: updated slightly per below.
Code: Select all
unsigned Cartridge::score_header(unsigned addr) {
if(cart.rom_size < addr + 64) return 0;
int score = 0;
uint8 *rom = cart.rom;
if((rom[addr + MAPPER] & ~0x10) == 0x20 && addr < 0x008000) score++;
if((rom[addr + MAPPER] & ~0x10) == 0x21 && addr >= 0x008000) score++;
if((rom[addr + MAPPER] & ~0x10) == 0x22 && addr < 0x008000) score++;
if((rom[addr + MAPPER] & ~0x10) == 0x25 && addr >= 0x408000) score++;
if(rom[addr + ROM_TYPE] < 0x08) score++;
if(rom[addr + ROM_SIZE] < 0x10) score++;
if(rom[addr + RAM_SIZE] < 0x08) score++;
if(rom[addr + REGION] < 14) score++;
if(rom[addr + COMPANY] == 0x33) score += 2;
uint16 cksum, icksum;
cksum = rom[addr + CKSUM] | (rom[addr + CKSUM + 1] << 8);
icksum = rom[addr + ICKSUM] | (rom[addr + ICKSUM + 1] << 8);
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) score += 4;
uint16 reset;
reset = rom[addr + RESL] | (rom[addr + RESL + 1] << 8);
if(reset < 0x8000) return 0;
uint8 resb = rom[(addr & ~0x7fff) + (reset & 0x7fff)];
if(resb == 0x18 //clc
|| resb == 0x78 //sei
|| resb == 0x4c //jmp $nnnn
|| resb == 0x5c //jml $nnnnnn
|| resb == 0x20 //jsr $nnnn
|| resb == 0x22 //jsl $nnnnnn
|| resb == 0x9c //stz $nnnn
) score += 8;
if(resb == 0xc2 //rep #$nn
|| resb == 0xe2 //sep #$nn
|| resb == 0xa9 //lda
|| resb == 0xa2 //ldx
|| resb == 0xa0 //ldy
) score += 4;
if(resb == 0x00 //brk #$nn
|| resb == 0xff //sbc $nnnnnn,x
|| resb == 0xcc //cpy $nnnn
) score -= 8;
printf("* resb = %0.2x\n", resb);
return score < 0 ? 0 : score;
}
void Cartridge::find_header() {
unsigned score_lo = score_header(0x007fc0);
unsigned score_hi = score_header(0x00ffc0);
unsigned score_ex = score_header(0x40ffc0);
if(score_ex) score_ex += 4;
printf("* score = %2d, %2d, %2d\n", score_lo, score_hi, score_ex);
if(score_lo >= score_hi && score_lo >= score_ex) {
info.header_index = 0x007fc0;
} else if(score_hi >= score_ex) {
info.header_index = 0x00ffc0;
} else {
info.header_index = 0x40ffc0;
}
}
Scores:
(Legend = Name: LoROM score, HiROM score, ExHiROM score)
Batman: RotJ: 13, 1, 0
Daikaijuu Monogatari 2: 0, 0, 16
Double Dragon: 17, 0, 0
Far East of Eden Zero: 0, 15, 0
Street Fighter Alpha II: 16, 15, 0
Star Ocean: 8, 0, 0
Tales of Phantasia (J): 0, 4, 9
Tales of Phantasia (Fan): 0, 8, 9
Ys 3 (J): 17, 8, 0
Highest score wins. If scores are equal, favors ExHiROM, then LoROM, then HiROM. All of the above scores obviously result in correct picks, but the wider the differences, the better.