Checksum

From TLoZ: ALTTP Hacking Resources
Jump to: navigation, search

See also: Standard Notation

There are different ways to implement the concept of a checksum, but for SNES games the checksum is literally a sum used to check for corruption of the cartridge's data. The checksum is calculated by adding together all of the bytes of the ROM, then taking the lowest two bytes of the sum as the checksum. Because the sum of all bytes includes the checksum itself, to prevent a chicken-and-egg situation the complement (also known as "bitwise NOT") of the checksum is also stored, so that the two combined always add up to $1FE no matter what the actual checksum ends up being.

When trying to calculate the checksum for the first time, or if the checksum bytes have been corrupted, simply set the checksum to $0000 and the complement to $FFFF. When recalculating the checksum after modifying the cartridge's data, leave the checksum alone while calculating the new one (because every valid checksum plus its complement will always be $1FE), then write the new checksum.

Note: There are complications to calculating the checksums of ROMs that are not a power of two in size, but that does not apply to ALTTP which is a power of two in size. When expanding the ROM, either double or quadruple the size to keep the size a power of two.

For LoROM games like this one, the checksum is located at $7FDE[$2] and the checksum's complement is located at $7FDC[$2].

Sample C# Code

public static class SnesChecksum
{
	public const int RomOffsetToRomChecksum = 0x7FDE , RomOffsetToComplementedRomChecksum = 0x7FDC ;

	public static void WriteMachineWord ( byte [ ] Data , int Offset , int Value )
	{
		Data [ Offset ] = ( byte ) ( Value & 0xFF ) ;
		Data [ Offset + 1 ] = ( byte ) ( Value >> 8 ) ;
	}

	public static void WriteChecksum ( byte [ ] Data , int Checksum )
	{
		WriteMachineWord ( Data , RomOffsetToRomChecksum , Checksum ) ;

		WriteMachineWord ( Data , RomOffsetToComplementedRomChecksum , ( ~ Checksum ) & 0xFFFF ) ;
	}

	public static int ReadMachineWord ( byte [ ] Data , int Offset )
	{
		return ( Data [ Offset ] | ( Data [ Offset + 1 ] << 8 ) ) ;
	}

	public static int CalculateChecksum ( byte [ ] Data )
	{
		int Checksum = 0 ;

		foreach ( byte CurrentByte in Data )
		{
			Checksum += CurrentByte ;
		}

		return ( Checksum & 0xFFFF ) ;
	}

	public static bool ValidateChecksum ( byte [ ] Data )
	{
		return ( ReadMachineWord ( Data , RomOffsetToRomChecksum ) == CalculateChecksum ( Data ) ) ;
	}

	public static void ClearChecksum ( byte [ ] Data )
	{
		WriteChecksum ( Data , 0 ) ;
	}

	public static void UpdateChecksum ( byte [ ] Data )
	{
		WriteChecksum ( Data , CalculateChecksum ( Data ) ) ;
	}
}