An explanation about different score structures

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

This image has an empty alt attribute; its file name is image-8.png

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.

Above is the data from Bubble Bobble. The first three bytes is the top score, but in reverse order
10 34 08, which should be 08 34 10 0. The conversion is needed for each byte, but they need to be appended in reverse order. As the intent of reading in the high scores is to display, I append each byte as a string in this case “08” + “34” + “10”. In these examples I categorise them as “dr” for decimal reversed.

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
              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
Posted in Uncategorized | Comments Off on An explanation about different score structures

High Scores (Pang!)

Pang is quite a straight forward hi score file. The scores are held in decimal although they need a final zero to be added, the names in ASCII and the stage reached as a decimal. The scores are also in order.

M.S 195040 Round 9
DAZ 156910 Round 7
M.S 100000 Round 32
M.K 80000 Round 24
T.U 70000 Round 23
Z. 62110 Round 4
K.H 60000 Round 21
Y.N 50000 Round 19
.,. 45400 Round 3
Y.K 40000 Round 18

Posted in Uncategorized | Comments Off on High Scores (Pang!)

High Scores (Ghosts N Goblins)

The Ghosts and Goblins High Scores is quite a straight forward set of data, although with a much larger array of characters with upper and lowercase letters, as well as a handful of symbols.

The first 20 characters don’t appear to hold any data needed for the high scores, there seems to be a duplicated array of data for the highest score at the end. The scores are unsorted so need to be arranged based on score value. Each entry alternates between score and name, the scores are held as decimals and in the correct order, names are pretty much ASCII. Starting with byte 20, scores can be put into sub arrays of seven bytes, 0-3 are the score, 4-6 names.

The byte to character table starts with the first character at 1D for a “.” and the last byte is 7A for “z”

Posted in Uncategorized | Comments Off on High Scores (Ghosts N Goblins)

High Scores (Missile Command)

Missile Command saves it’s high scores with all names first, then all scores, rather than cycle by name,score,name,score… A few things to note. 3 bytes for name, 3 bytes for score. The score is saved as decimals and in reverse order so 50 69 00 is 006950. In the system I’ve written this I would assign as “dr” for decimal reversed. However the scores themselves are in reverse order with lowest score first, same for the corresponding names.

DFT 7500
DLS 7495
SRC 7330
RDA 7250
MJP 7200
JED 7150
DEW 7005
GJL 6950

Seeing that I was missing a few characters and couldn’t be certain that the all the characters related to ASCII, I loaded up Missile Command and got two new high scores.

Testing a space shows it as 5B which would be after Z in ASCII,

Posted in Uncategorized | Comments Off on High Scores (Missile Command)

HighScores (Marble Madness)

The high score file for Marble Madness is purely in hexdecimal. The scores are saved as 3 byte integers and the name is stored as 2 bytes for 3 characters. Here’s a sample of the data

Yellow bytes are score, blue bytes are the name

C R 14500
UFO 14000
GJL 13500
SKP 13000
PCT 12500
PTR 12000
JDH 11500
DAZ 11170
DAT 11000
JFS 10500

Each entry in the high score table is stored in 5 bytes. The first three bytes are score. 0038A4 = 14500. In Python I appended each of the bytes into a three byte array, converted into an int and then a string with tempScore = str(int.from_bytes(tempIntScore,byteorder='big'))

The name was a lot trickier to work out, 2 bytes for 3 characters. As by pure fluke when I created a test entry and used my name “DAZ”, the one right below was just one character difference “DAT”
DAZ = (hex)1942 = (dec)6466
DAT = (hex) 193C = (dec)6460
The decimal value had a difference of 6, the number of positions in the alphabet between T and Z. This leaves a question of the values for the first two characters. Taking a punt that the char values starting with A = 1, B = 2 and so on, I divided the decimal values by the number val of the first character so….
DAZ = (hex)1942 = (dec)6466 / (D)4 = 1616
SKP = (hex)7888 = (dec)30856 / (S)19 = 1624
UFO = (hex)843F = (dec)33855 / (U)21= 1612
A little guess work fixed the value to the first character as the decimal value / 1600. Knowing this and knowing that the last character was to the power of 1 lead to the relisation that the second character was worth a multiple of 40.
DAZ = 6466. 6466 – (1600*(D)4) = 66
66-Z(26) = 40 and as A=1 then 1*40 works.
4 1 26
(4*1600) + (1*40) + (26*1)
6400 + 40 + 26

Spaces are worth 0.

0 = ‘ ‘, 1 = ‘A’, 2 = ‘B’ …… 26 = ‘Z’

Posted in Uncategorized | Comments Off on HighScores (Marble Madness)

An ex-website


If you’re reading this, somehow you found my website, but most of what was posted here was so old that it’s pretty much nonsense now, so I purged everything and just have this page now tell you this.

Posted in Uncategorized | Comments Off on An ex-website