- 0 Comments
-
Detection of SMC header in BSNES, Snes9x
BSNES and Snes9x use different methods to detect if the ROM image of some SNES game has an SMC header of 512 bytes ($200 bytes in hexadecimal).
Snes9x
Edit
For Snes9x, the crucial function is CMemory::LoadROM in memmap.cpp, which calls FileLoader to read the ROM image. FileLoader reads the file and calls HeaderRemove. For each file, HeaderRemove removes the header if the file size divided by $2000 has remainder $200.
uint32 CMemory::HeaderRemove (uint32 size, int32 &headerCount, uint8 *buf)
{
uint32 calc_size = (size / 0x2000) * 0x2000;
if ((size - calc_size == 512 && !Settings.ForceNoHeader) || Settings.ForceHeader)
{
...
memmove(buf, buf + 512, calc_size);
headerCount++;
size -= 512;
}
return (size);
}
If HeaderRemove removed the header, then headerCount is 1, else headerCount if 0. (For a multi-file ROM image, FileLoader calls HeaderRemove for each file, so headerCount can be greater than 1.)
Then LoadROM calls ScoreLoROM and ScoreHiROM to examine the candidate SNES headers at offsets x7fc0 and xffc0. If headerCount is zero, then FileLoader again calls ScoreLoROM and ScoreHiROM to examine the candidate SNES headers at offsets x7fc0 + x200 and xffc0 + x200. If the SNES header at x7fc0 + x200 or xffc0 + x200 has the highest score, then LoadROM removes the SMC header.
int CMemory::ScoreHiROM (bool8 skip_header, int32 romoff)
{
uint8 *buf = ROM + 0xff00 + romoff + (skip_header ? 0x200 : 0);
...
int CMemory::ScoreLoROM (bool8 skip_header, int32 romoff)
{
uint8 *buf = ROM + 0x7f00 + romoff + (skip_header ? 0x200 : 0);
...
bool8 CMemory::LoadROM (const char *filename)
{
...
hi_score = ScoreHiROM(FALSE);
lo_score = ScoreLoROM(FALSE);
if (HeaderCount == 0 && !Settings.ForceNoHeader &&
((hi_score > lo_score && ScoreHiROM(TRUE) > hi_score) ||
(hi_score <= lo_score && ScoreLoROM(TRUE) > lo_score)))
{
memmove(ROM, ROM + 512, totalFileSize - 512);
totalFileSize -= 512;
...
// modifying ROM, so we need to rescore
hi_score = ScoreHiROM(FALSE);
lo_score = ScoreLoROM(FALSE);
}
BSNES
Edit
For BSNES, the crucial function is Utility::loadCartridge in src/ui_qt/utility/cartridge.cpp, which has exactly one line of code to check the SMC header. BSNES removes the header if the file size divided by $8000 has remainder $200.
//remove copier header, if it exists if((size & 0x7fff) == 512) memmove(data, data + 512, size -= 512);
BSNES, unlike Snes9x, never uses the SNES header to decide whether to remove the SMC header. In BSNES, Cartridge::find_header in src/cartridge/header.cpp only examines the candidate SNES headers at x7fc0, xffc0 and x40ffc0.
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) const {
unsigned score_lo = score_header(data, size, 0x007fc0);
unsigned score_hi = score_header(data, size, 0x00ffc0);
unsigned score_ex = score_header(data, size, 0x40ffc0);
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
if(score_lo >= score_hi && score_lo >= score_ex) {
return 0x007fc0;
} else if(score_hi >= score_ex) {
return 0x00ffc0;
} else {
return 0x40ffc0;
}
}
Conclusion
Edit
Snes9x divides the ROM size by 0x2000; BSNES divides the ROM size by $8000. If the remainder equals 0x200, then these emulators remove the SMC header of $200 bytes.
Snes9x also removes the SMC header if the candidate SNES header at offset x7fc0 + x200 or xffc0 + x200 has a higher score. BSNES does not consider these candidates.