Hacking the Xyron Design Runner Chapter 2: Card FAT Format

EDIT 6/10

We’ve found a mistake in the XAT decoding and corrected it.  There was an offset in the entires, and too many bytes in the header row.  Ignore any old versions of this page that you may see.

Welcome back to another session on hacking around with the Xyron Design Runner.  As you will recall, we left you at the end of our first post on this device with a couple of meager disc images, and not much else to go on.  Today, we’ll be investigating the XAT – the Xyron Allocation Table.  This table of entries exists at the beginning of each card, and it tells the XDR where to go find the data you’d like to print.  Before we can begin decoding the image format, we’ve got to figure out where the heck the image is in that big mass of bytes!

XAT Table Digging

First, we must apologize – trying to decipher the crappy test images we posted for you was way too confusing for our tiny haxxor brains.  there was just no way for us to figure out where each entry started and stopped, let alone how to parse out the image data.  We had to back off, and go one step at a time – which turned out to be fairly straightforward.  A good lesson on KISS, perhaps.

The best way we found to determine this device’s data formatting is to write a blank card with one entry at  a time, and inspect the changes at each step.  So let’s play around and see what we see.  Starting with a totally blank card and saving one entry of a single letter ‘a’, the start of the bin file looks like:

0000: 01 F0 23 00 00 11 EE 00 00 16 0B 01 E0 00 00 00
0010: 00 00 00 2E BB FF FF FF FF FF FF FF FF FF FF FF
0020: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0030: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Where the address entries on the left side are in HEX.

Then, reinserting the card and saving a second entry of the same thing:

0000: 02 09 1E 00 00 11 EE 00 00 16 0B 01 E0 00 00 00
0010: 00 00 00 2E BB 00 00 32 D8 01 E0 00 00 00 00 00
0020: 00 4B 88 FF FF FF FF FF FF FF FF FF FF FF FF FF
0030: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

You’ll notice a couple of things.  The first byte (byte 0) incremented from 01 to 02.  Looks like a counter of the entries, eh?  And starting at address $15, another 14 ($0E) bytes were added.  So each entry must be 14 bytes long!

And just for grins, saving it a third time:

0000: 03 21 BB 00 00 11 EE 00 00 16 0B 01 E0 00 00 00
0010: 00 00 00 2E BB 00 00 32 D8 01 E0 00 00 00 00 00
0020: 00 4B 88 00 00 4F A5 01 E0 00 00 00 00 00 00 68
0030: 55 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF

Yup, byte 0 incremented again, and another 14 byte entry were added.  Let’s reorganize the data to see if we can make some sense out of it.

First we start with our “Header”, containing the number of entries and 2 unknown bytes.  Next, three entries at 14 bytes each.

0000: 03 XX XX
0003: 00 00 11 EE 00 00 16 0B 01 E0 00 00 00 00
0011: 00 00 2E BB 00 00 32 D8 01 E0 00 00 00 00
001F: 00 00 4B 88 00 00 4F A5 01 E0 00 00 00 00
002D: 00 00 68 55 FF FF FF FF FF FF FF FF FF FF

Starting with the header row, byte 0 is definitely the number of entries.  Bytes 1 and 2 keep changing for every entry – size remaining, next block, or just something stupid like a timestamp?  We don’t know yet.  The rest of the header row is constant, so we’ve got nothing there to work from. [EDIT - Byte 2 is a checksum of the file data, but not the image data]

Based on the fact that each of these entries is identical, we know they’re the same size.  So the size parameter (which is a common entry for a table like this) should be uniform.  The nonuniform parts are usually just the starting address, but we see two nonuniform sections in each entry.  For example, the bytes at $09-0A and the bytes at $13-14.  Let’s start subtracting stuff to see how these offsets match up.  First, we’ll look at the deltas within one entry.

32D8 – 2EBB = 041D, and so does 4FA5 – 4B88.  As a matter of fact, 160B – 11EE also equals 041D.  Something is $041d (1053) bytes long, and it begins at the first word in each entry.

[EDIT - The $041D bytes consist of $D0 bytes of "file header", $270 bytes of "Preview Bitmap", and $DD bytes of unknown, usually blank.  Special thanks goes to David from the ATX Hackerspace, who did the initial decoding of the $270 bytes of preview bitmap into a 104 x 31 PBM (1 bit per pixel) Black & White image.  Sweet!]

Now, looking at the delta between entries: 2EBB – 160B = 18B0.  And so does 4B88 – 32D8, and 6855 – 4FA5.  Something else is $18B0 (6320) bytes long, and it is contained entirely in the entry.  Image data, maybe?

The next line is already partially generated, consisting of stuffing the address of the next available byte, and then leaving the rest of the entry as $FF’s – the DataFlash default “blank” value.

Now, we’ll do something wacky and save an entry with two instances of the “a” character.  Same data, but double size.  Hopefully!

Here’s the “organized” data:

0000: 04 42 86
0003: 00 00 11 EE 00 00 16 0B 01 E0 00 00 00 00
0011: 00 00 2E BB 00 00 32 D8 01 E0 00 00 00 00
001F: 00 00 4B 88 00 00 4F A5 01 E0 00 00 00 00
002D: 00 00 68 55 00 00 6C 72 01 E0 00 00 00 00
003B: 00 00 9D D2 FF FF FF FF FF FF FF FF FF FF

OK.  6855 to 6C72 is our trusty 041D byte offset, but look at the entry size: 9DD2 – 6C72 = 3160 (12,640 bytes in decimal) , which is exactly twice the size of a single character entry.  Hot damn, this is really coming together!  Our intial speculation now is that the data is saved as a streaming bitmap, meaning the height is fixed and it’s variable length in increments of 6320 bytes.  When printing, this variable length swatch of data is pumped directly to the print head as the little optical mousie-sensor increments the X-axis counter.

[EDIT - It ain't as easy as we thought.  Definitely not a vanilla bitmap. Still Looking!]

Last, save something random from one of the image discs, and again parse out the XAT data.  We now have 5 entries – three single lowercase “a” entries, one “aa” entry, and now one image.

0000: 05 92 DF
0003: 00 00 11 EE 00 00 16 0B 01 E0 00 00 00 00
0011: 00 00 2E BB 00 00 32 D8 01 E0 00 00 00 00
001F: 00 00 4B 88 00 00 4F A5 01 E0 00 00 00 00
002D: 00 00 68 55 00 00 6C 72 01 E0 00 00 00 00
003B: 00 00 9D D2 00 00 A1 EF 01 F4 00 00 00 00
0049: 00 01 9F FF FF FF FF FF FF FF FF FF FF FF

Lookie!  We learned something new!  The size of the entry was large enough to roll it over to three bytes.  So THAT’s why each entry starts with 00 00 – those are space for addressing!  It’s a 4GB range, so is there a possibility of a MA55IVE homebrew datacard in the works?  Only time will tell.

And our size check?  019FFF – A1EF = FE10, which is more than 5 instances of our original $18B0 entry size.  So each bitmap entry seems to be truly variable-length.  Summoning up the power of the interwebs, we use a Greatest Common Denominator Finder to determine that the decimal values 6320, 12640, and 65040 have a GCD of 80 bytes.  That seems to be a very reasonable value for the modulus of the Y-height of the bitmaps we’re looking for.  The X-width, of course, is variable.  With this, we can make an educated guess that if we go to the initial offset stored in one of those entries and start pulling out 80, 160, 240, or 320  byte columns of data, we just may get something..

So let’s go fishing for some data, shall we?


This entry was posted in Hacks and tagged , , , . Bookmark the permalink.

4 Responses to "Hacking the Xyron Design Runner Chapter 2: Card FAT Format"

Leave a reply