Signatures in iNES headers

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Signatures in iNES headers
by on (#189026)
Snake's Revenge.7z <Snake's Revenge (U) [t4].nes> from GoodNES3.23b has the following header:

Code:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
4E 45 53 1A 10 00 23 64 65 6D 69 66 6F 72 63 65  NES...#demiforce


Usually, when signatures are present, they begin in byte 10; bytes 7, 8 and 9 have significant meaning. For instance, the upper nibble of the mapper number is in byte 7.

This can be resolved by hashing ROMs. But, this particular ROM is not present in NstDatabase.xml 1.47 or NesCarts (2012-10-22).xml. Yet, most emulators correctly recognize it as a Mapper 2 ROM, as opposed to the invalid Mapper 98. If it is not being hashed, how are emulators dealing with this signature issue on load?
Re: Signatures in iNES headers
by on (#189027)
This is the USA dump profile: http://bootgod.dyndns.org:7777/profile.php?id=738

You're asking something about "how do I load a bad dump within a dirty header"? I have a CRC32 check for a few special cases; other than that, my emulator warns about a dirty header. Plus, it's NOT mapper 2, but MMC1.
Re: Signatures in iNES headers
by on (#189029)
Nestopia:
Checks for the NES2.0 signature
If it's NOT present, checks if any of the last four bytes are nonzero
If they are, it clears the last nine bytes.
Re: Signatures in iNES headers
by on (#189030)
lidnariq wrote:
Nestopia:
Checks for the NES2.0 signature
If it's NOT present, checks if any of the last four bytes are nonzero
If they are, it clears the last nine bytes.


That's a good strategy. The last five bytes should actually be 0. Why does it use four?

Edit: Here's Nestopia's loading code:

Code:
         setup.version = ((header[7] & 0xCU) == 0x8 ? 2 : 0);

         if (!setup.version)
         {
            for (uint i=10; i < 16; ++i)
            {
               if (header[i])
               {
                  header[7] = 0;
                  header[8] = 0;
                  header[9] = 0;
                  result = RESULT_WARN_BAD_FILE_HEADER;
                  break;
               }
            }
         }


Byte 7 is still required to do the NES 2.0 check; so, it's not a flawless strategy. And, it clears 7--9, when any of 10--15 are nonzero.
Re: Signatures in iNES headers
by on (#189038)
Here we have 'd' (0x64) as header byte 0x07. This would add 0x60 to the mapper number, and put the "NES 2.0" flag at 01, which is invalid. (only 10 and 00 are valid)
The header from byte 0x07 on is obviously invalid, so replace it with zeroes, and clamp the mapper number to 0-15.

Even if the NES 2.0 flag was set to 10, there are other quick rejection tests. If PC10 and VS are set at the same time, that's invalid. If there's anything in the last two bytes (0x0E or 0x0F), you can reject all header bytes from 0x07 on as well.
Re: Signatures in iNES headers
by on (#189039)
zeroone wrote:
Byte 7 is still required to do the NES 2.0 check; so, it's not a flawless strategy
Kevtris specifically chose the signature he did for NES2.0 because it doesn't collide with any known dirty headers.
Re: Signatures in iNES headers
by on (#189044)
Dwedit wrote:
Here we have 'd' (0x64) as header byte 0x07. This would add 0x60 to the mapper number, and put the "NES 2.0" flag at 01, which is invalid. (only 10 and 00 are valid)
The header from byte 0x07 on is obviously invalid, so replace it with zeroes, and clamp the mapper number to 0-15.

Even if the NES 2.0 flag was set to 10, there are other quick rejection tests. If PC10 and VS are set at the same time, that's invalid. If there's anything in the last two bytes (0x0E or 0x0F), you can reject all header bytes from 0x07 on as well.


According to the wiki:

Code:
If equal to 2, flags 8-15 are in NES 2.0 format


It does not explicitly say that 0 indicates iNES; rather, it suggests that anything other than 2 is iNES. But, I can add a check for 2 or 0.

I also like the PC10-VS coincidence test.
Re: Signatures in iNES headers
by on (#189046)
zeroone wrote:
It does not explicitly say that 0 indicates iNES; rather, it suggests that anything other than 2 is iNES. But, I can add a check for 2 or 0.
byte7&$C == 0 is not sufficient; I've certainly found other headers with bad contents in the 8th and later bytes.
Re: Signatures in iNES headers
by on (#189048)
lidnariq wrote:
byte7&$C == 0 is not sufficient; I've certainly found other headers with bad contents in the 8th and later bytes.


I meant to do that test in addition to the others. Something like this:

Code:
    nes20Format = (header[7] & 0x0C) == 0x08 && (header[7] & 0x03) != 0x03;
   
    if (!nes20Format) {
      if ((header[7] & 0x0C) != 0x00 || (header[7] & 0x03) == 0x03) {
        header[7] = header[8] = header[9] = 0x00;
      } else {
        for(int i = 10; i < 16; i++) {
          if (header[i] != 0x00) {
            header[7] = header[8] = header[9] = 0x00;
            break;
          }
        }
      }
    }
Re: Signatures in iNES headers
by on (#189052)
zeroone wrote:
According to the wiki:

Code:
If equal to 2, flags 8-15 are in NES 2.0 format


It does not explicitly say that 0 indicates iNES; rather, it suggests that anything other than 2 is iNES. But, I can add a check for 2 or 0.

I seem to recall an older document stating that 00 meant "iNES", 10 meant "NES 2.0", 01 meant "DiskDude! corrupted header" (and apparently also "demiforce"), and 11 meant "reserved for future expansion" - it's possible that was lost during the transition to the wiki, or maybe it was just a discussion on IRC from over 10 years ago.