tl;dr Scores can be saved as decimal, hexdecimal or ASCII, in big or little order, and sometimes may need an additional zero on the end.
So far I’ve looked at the high score files for 1941,1942,1943, Bank Panic, BombJack, Bubble Bobble, Rainbow Islands, Ghosts N Goblins, Pang!, Missile Command, Marble Madness, Hypersports, Robotron, R-Type, R-Type 2, Rygar and Wonderboy. I thought I should write a post about the different formats and structures I’ve seen so far.
The highscore files are stored in a binary file, and from what I’ve read is a snapshot of certain areas of a games memory as identified in the highscore.dat files. This means when read the bytes area displayed as hexdecimal and generally need converting to decimal, but its not as simple as that may seem.
Here’s an example of the scores in Ghosts N Goblins
The scores here are decimal, the score 00 05 15 00 is the current high score which is 51500, however, 15 as a byte is not 15, but 21 as the values are shown as hex which means a conversion is needed.
def DecConvert(n):
#converts a hex number to decimal
tens = int(n / 16)
dec = n - (tens * 6)
return dec
For cataloguing these, I use “d” for decimal. However, whilst Ghosts N Goblins is in order, others are not.
The appending zero depends on the game. The vast majority of games don’t have high scores with increments under tens (Robotron being an exception), so there is no need to store the additional character, just add “0” onto the value. In these cases I add a “0” to the categorisation, “d0” for decimal extra zero, or “dr0” for decimal reverse extra zero.
Some games aren’t as concerned with saving memory and use an entire byte for a single digit. Bank Panic is an example of this. Forgive the messiness of the following image
Bank Panic is an interesting example of a high score table as it also saves the round and it saves the duration of the game, but for now I’m just going to focus on the score. The top score in this example is 153350, but the seven bytes used save only one byte. This makes it easy for appending onto a string and for categorisation I label this “o”. I’ll come back to Bank Panic at a later date but the bytes circled in purple and white are seconds. The first is a hexdecimal value storing between 0 and 255 seconds. The second byte is multiples of 256.
The next type is a score saved as ASCII and can be seen in Wonderboy
The blue circled values are the score. 20 is a space, but 30 to 39 are the ASCII values for 0 to 9. As can be seen in the preview, the score is clearly visiable, although it does need an additional zero. Thus the categorisation is “a0” “a” for ASCII and “0” for add an extra zero.
The final example I’ve seen so far is one in hex, and has been documented in detail in Marble Madness
The first three bytes are score. 0038A4 = 14500. The names are more difficult to figure out, but check out the link above.
So that’s it for now. The different formats I’ve got so far are:
“d” = decimal
“dr” = decimal reversed
“d0” = decimal add extra zero
“dr0” = decimal reversed add extra zero
“o” = decimal but one digit per byte
“a0” = ASCII add extra zero
“h” = hexdecimal
And a sneaky peak at the code.
for j in range(scoreFirstByte,scoreFirstByte + scoreByteLength ):
# loop from the first byte that holds the score, to the end of thatsegment
if numberType == "d" or numberType == "d0":
#if the numberType is a decimal and in the correct order
tempScore = tempScore + str(DecConvert(byteArray[i][j])).zfill(2)
# take the previous string and append the byte as a
# two digit string on the end
if numberType == "a0":
#if the score is already in ascii
tempScore = tempScore + charArray[byteArray[i][j]]
#append the character onto end of the string
if numberType == "h":
# a hex based score. 0A = 10, 10 = 16
tempIntScore.append(byteArray[i][j])
tempScore = str(int.from_bytes(tempIntScore,byteorder='big'))
#converts the byte array to int 00 38 A4 becomes 14500
if numberType == "o":
#a score the a byte is just one digit
tempScore = tempScore + str(byteArray[i][j]).zfill(1)
if numberType == "dr0" or numberType == "dr":
#number is decimal but stored in reverse order
tempScore = str(DecConvert(byteArray[i][j])).zfill(2) + tempScore