Example:
Asteroid Deluxe
There is only one nvram file nativly saved for this game, without any
location.dat entry.
Get the nvram file size: <mame>/nvram/astdelux/earom => size: 64 bytes
[XML]
<structure>...
<check>...
<size>64</size>
</check>
</structure>
Let's take a snapshot of the ingame high scores, after some trials.
We are seeing X high scores:
1: 2240 VA
2: 1880 C B
3: 1580 YAA
4: 610 QAE
Let's have a look inside the nvram.
By chance, sometimes seems quite obvious about the scores, confirmed after some
other trials.
First, only 3 high scores are stored in the nvram.
Then, the hexa decimal value is the decimal value to display, meaning that
attributes type = INT and base =16 must be used.
So, each score are stored on 3 bytes (attibute size = 3).
Finally, the 3 bytes are inverted, i.e. stored in little endian, driving the
attribute endianness = little_endian.
And.. perhaps names are between, allowing us to use a loop?
[XML]
<loop count="3">
<elt size="3" type="int" id="SCORE"
endianness="little_endian" base="16"/>
<elt size="4" type="raw" id="UNKNWON"/> <!-- name here? //-->
</loop>
Our target names are: <space>VA, C<space>B, YAA
As the last 'unknwon' data has two 0x0B, so 0x0B means perhaps 'A' and letters
seem to be in the natural order.
Let put the letters in front of their hexa and decimal codes, as well as the
targeted ascii decimal, to see if some transformations can be highlighted.
Letter |
->hexa |
->decimal |
->all decimal |
-> transformation 1 |
-> ascii decimal |
-> target ascii decimal |
A |
0B |
11 |
11 |
+54
|
65 |
65 |
B |
0C
|
12
|
12 |
+54 |
66 |
66 |
C |
0D
|
13
|
13 |
+54 |
67 |
67 |
D |
|
|
14 |
+54 |
68 |
68 |
... |
|
|
... |
+54 |
... |
... |
<space> |
00
|
0
|
0 |
+32 |
32 |
32 |
So, 1 transformation can be applied: ascii offset = 54, expect for
<space>.
And last byte seems to be no part of the name, meaning size = 3.
[XML]
<loop count="3">
<elt size="3" type="int" id="SCORE" endianness="little_endian"
base="16"/>
<elt size="3" type="text" id="NAME"
ascii-offset="54"/>
<elt size="1" type="raw" id="UNKNWON"/>
</loop>
If we modify directly a score inside the nvram and reload the game, the new
score is not accepted.
So, the guess is that the last unknwon byte is a control byte, some sort of
checksum, preventing easy hack in memory or nvram.
[XML]
<loop count="3">
<elt size="3" type="int" id="SCORE" endianness="little_endian"
base="16"/>
<elt size="3" type="text" id="NAME" ascii-offset="54"/>
<elt size="1" type="raw"
id="CHECKSUM"/>
</loop>
Testing all possible hexadecimal positions and specifically 'space' leads to
the following additional charset and decoding.
[XML]
<structure>
...
<loop count="3">
<elt size="3" type="int" id="SCORE" endianness="little_endian"
base="16"/>
<elt size="3" type="text" id="NAME" charset="astdelux" ascii-offset="54"/>
<elt size="1" type="raw" id="CHECKSUM"/>
</loop>
</structure>
<charset id="astdelux">
<char src="0x00" dst=" "/>
<char src="0xFF" dst=" "/>
</charset>
Now we need to display the decoded elements into the output part.
The 'loop' will turn into a 'table', 'elements' into 'columns'.
[XML]
<output>
<table>
<column id="SCORE"/>
<column id="NAME"/>
<column id="CHECKSUM"/>
</table>
</output>
Let's make explicit in the output that checksum has been kept in hexa (default
behavior of the RAW type), by adding the "0x" prefix, using the implicit
special format dedicated.
We will also display it only if debug mode is activated (-rd), as having a
checksum is not really displaying high scores, by using display = "debug" :)
[XML]
<column id="CHECKSUM" format="0x" display="debug"/>
Finally, adding a rank column is always welcome to mimic a high score table,
using the current index of each line inside the table (src = "index"), starting
from 1 instead of 0 (format = "+1", another direct implicit format)
[XML]
<output>
<table>
<column id="RANK" src="index"
format="+1"/>
<column id="SCORE"/>
<column id="NAME"/>
<column id="CHECKSUM" format="0x" display="debug"/>
</table>
</output>
And that's all! Here is the final XML description:
[XML]
<?xml version="1.0" encoding="utf-8"
standalone="no"?>
<!DOCTYPE hi2txt SYSTEM "hi2txt.dtd">
<hi2txt>
<!--
tested with mame 0.148 and related hiscore.dat
source: HiToText
//-->
<structure file="earom">
<check>
<!-- optional: if defined, it allows to select the good structure
versus the provided file
useful if multiple structures are defined but hiscore.dat
not provided //-->
<size>64</size>
</check>
<loop count="3">
<elt size="3" type="int" id="SCORE" endianness="little_endian"
base="16"/>
<elt size="3" type="text" id="NAME" charset="astdelux"
ascii-offset="54"/>
<elt size="1" type="raw" id="CHECKSUM"/>
</loop>
</structure>
<output>
<table>
<column id="RANK" src="index" format="+1"/>
<column id="SCORE"/>
<column id="NAME"/>
<column id="CHECKSUM" format="0x" display="debug"/>
</table>
</output>
<charset id="astdelux">
<char src="0x00" dst=" "/>
<char src="0xFF" dst=" "/>
</charset>
</hi2txt>
...leading to such display, using -ra parameter:
RANK|SCORE|NAME
1|2240| VA
2|1880|C B
3|1580|YAA
or this one, using -rd parameter:
RANK|SCORE|NAME|CHECKSUM
1|2240| VA|0x8D
2|1880|C B|0xB1
3|1580|YAA|0xCE
A front-end can then easily display (with some fancy colors):
RANK SCORE NAME
1 2240 VA
2 1880 C B
3 1580 YAA