Created by John K. Morris
jmorris@evolutioninteractive.com
Version 2.0.1 – December 2, 2018
The A2R format is the raw flux images as recorded by the Applesauce hardware and software. The A2R has evolved a few times since I originally started work on Applesauce. As of release 1.0.3 of the Applesauce software, I have created a version 2.0 of the A2R file format. This document describes that version. If you need to convert earlier A2R files to the current version, it can be done with the Applesauce software.
What is the thinking behind the A2R format?
Why did I bother with creating the A2R format when everybody just wants to play with WOZ files? There are a few reasons for this decision. First off is that creating WOZ files is an insanely complex process. It isn’t even remotely like just capturing bits and stuffing them into a file. There is a ton of signal processing, analysis and validation that goes on in order to make everything work. This leaves a lot of room for mistakes to happen in the process. The second is that many floppy disks don’t have a lot of life left in them. Bits are rotting away and we may not have many more opportunities to capture this data. So, with these factors in mind, I created an intermediate file format. One that contains purely the lowest level information at the highest resolution that is possible to capture from a disk. No analysis or discarding of potentially useful information. The A2R file stores the data below the bit level. It records the amount of time that exists between flux transitions on the media surface with an accuracy of 125 nanoseconds (that is 125 billionths of a second). This gives us the ability to preserve and work with data that is an exact copy of a floppy disk.
If we have such a precise disk image format, then why bother with the WOZ format? The first reason is that an A2R is huge. On average, a 143 kilobyte floppy disk is represented by a 20 megabyte A2R file. The other difference is that an A2R file is what was read from a disk while a WOZ is what was originally written to the disk. This is where all the signal processing and such that I mentioned earlier comes into play. Due to electrical quirks of floppy drives and the laws of physics, what is read from a disk does not necessarily match what was written to the disk. If you want to delve deeper into that topic, I suggest that you dig into the WOZ Disk Image Reference.
The gist of all of this is that the A2R file is intended to be an archival backup of a disk. One that can be brought out and used for analysis or generation of WOZ files or anything else someone may come up with in the future. And it can be reused over and over again as the WOZ generation process matures, without potentially wearing out or causing further deterioration of the original floppy.
What has changed in the A2R specification for the 2.0.1 release?
This was a pretty minor update with some cleanup for 3.5 disks and Macintosh support.
STRM Chunk Changes
The Location value used for 3.5 disks has changed to have both sides of a track next to each other using the formula ((track << 1) + side), instead of all tracks for side 0 followed by all tracks for side 1.
META Chunk Changes
Added “mac” as a new standard value for the requires_machine metadata key.
A2R File Format Specification
An A2R file uses a chunk-based file binary format that provides future-proof expandability in a way that is safe for older software which may not recognize newer data chunks.
All data is stored little-endian.
A2R files begin with the following 8-byte header in order to identify the file type as well as detect any corruption that may have occurred.
Byte | Value | Purpose |
---|---|---|
0 | 41 32 52 32 | The ASCII string ‘A2R2’. 0x32523241 |
4 | FF | Make sure that high bits are valid (no 7-bit data transmission) |
5 | 0A 0D 0A | LF CR LF – File translators will often try to convert these. |
After the header comes a sequence of chunks which each contain information about the disk image. Using chunks allows for the A2R disk format to provide forward compatibility as chunks can be added to the specification and will just be safely ignored by applications that do not care (or know) about the information.
All chunks have the following structure:
Offset | Size | Name | Usage |
---|---|---|---|
+0 | 4 bytes | Chunk ID | 4 ASCII characters that make up the ID of the chunk |
+4 | uint32 | Chunk Size | The size of the chunk data in bytes. |
+8 | … | Chunk Data | The chunk data. |
To process the file, you start at the first Chunk ID which will be located at byte 8 of the file, immediately following the header. You read the Chunk ID and the Chunk Size following it. If you want to process this chunk, then your file pointer will be at the start of the data. If you don’t care about this chunk, then skip the number of bytes as Chunk Size indicates and you will now be at the next Chunk ID.
while(data_stream.availableToRead() > 8) { uint32_t chunk_id = data_stream.readU32(); uint32_t chunk_size = data_stream.readU32(); switch(chunk_id) { case INFO_CHUNK_ID: // read the INFO chunk break; case STRM_CHUNK_ID: // read the STRM chunk break; case META_CHUNK_ID: // read the META chunk break; default: // no idea what this chunk is, so skip it data_stream.skip(chunk_size); } }
INFO Chunk
The first chunk in an Applesauce file is always an ‘INFO’ chunk. This contains some fundamental information about the contained image. The data of the ‘INFO’ chunk begins at byte 16 of the file and version 1 is 36 bytes long.
Offset | Type | Vers | Name | Usage |
---|---|---|---|---|
uint32 | ‘INFO’ Chunk ID | 0x4F464E49 | ||
uint32 | Chunk Size | Version 1 is 36 bytes long. | ||
+0 | uint8 | 1 | INFO Version | Version number of the INFO chunk. Current version is 1. |
+1 | UTF-8 32 bytes |
1 | Creator | Name of software that created the A2R file. String in UTF-8. No BOM. Padded to 32 bytes using space character (0x20). ex: “Applesauce v1.0 ” |
+33 | uint8 | 1 | Disk Type | 1 = 5.25, 2 = 3.5 |
+34 | uint8 | 1 | Write Protected | 1 = Floppy is write protected |
+35 | uint8 | 1 | Synchronized | 1 = Cross track sync was used during imaging |
The chunk is versioned to allow for adding additional info in the future. The “Vers” column in the table above indicates at which version the data field became available. When reading data from the chunk, make sure that value you are looking for actually exists within the version of the chunk you are reading.
STRM Chunk
The ‘STRM’ chunk contains all of the raw data streams as captured by the Applesauce hardware. To understand this information, you will need a bit of background as to the different types of data captures that Applesauce performs.
- A timing capture is the standard data capture that Applesauce uses. It starts capturing data when it sees the sync sensor trigger and grabs data for 250 milliseconds. This will grab about 450 degrees of rotation.
- A xtiming (extended timing) capture is basically the same as a timing one except that the capture is 450 milliseconds long (about 810 degrees of rotation). This is currently the primary data capture type used by Applesauce. This was previously only used when Applesauce felt that a regular timing capture was ambiguous when creating a proper loop of the track data.
- A bits capture was originally used to perform the same function as xtiming, but has been deprecated in Applesauce v1.0.3. Instead of capturing flux timing, it captures a bit stream that is 16384 bytes long (averaging about 900 degrees of rotation).
The STRM chunk contains all of the captures that took place during the imaging process. All of these captures are stored end-to-end as packed data. There is no padding or delimiters between the data. The STRM chunk begins like this:
Offset | Type | Name | Usage |
---|---|---|---|
uint32 | ‘STRM’ Chunk ID | 0x4D525453 | |
uint32 | Chunk Size | Length of the stream data. | |
+0 | … | Stream Capture Data |
Each capture has the following format:
Offset | Type | Name | Usage |
---|---|---|---|
+0 | uint8 | Location | Track where this capture happened. For 5.25 disks, this value is in halfphases or quarter tracks. For example track 0.00 is halfphase 0 and track 1.00 is halfphase 4. For 3.5 disks, this value indicates track number as well as side. The formula ((track << 1) + side) can be used (0 = Track 0 Side 0, 1 = Track 0 Side 1, 2 = Track 1 Side 0). |
+1 | uint8 | Capture Type | 1 = timing, 2 = bits, 3 = xtiming |
+2 | uint32 | Data Length | Length of Data in bytes. |
+6 | uint32 | Estimated Loop Point | Duration until sync sensor was triggered at loop point. |
+10 | … | Data |
Since Applesauce does multiple captures of each track, there will be multiple sets of capture data which use the same Location value. After the final capture, the STRM chunk is ended with a single 0xFF byte. If you are walking through the STRM chunk, encountering a Location of 0xFF is your signal that you are done.
For the timing and xtiming Capture Types, the Data is stored as a stream of unsigned bytes. Each byte represents a single flux transition and its value in the number of ticks since the previous flux transition. A single tick is 125 nanoseconds. Therefore the normal 4 microsecond spacing between sequential 1 bits is represented by approximately 32 ticks. This also puts 101 and 1001 bit sequences at approximately 64 and 96 ticks. You are probably thinking to yourself that when it comes to longer runs of no transitions, how is this unsigned byte going to handle representing the time? That is taken care of via the special value of 255. When you encounter a 255, you need to keep adding the values up until you reach a byte that has a non-255 value. You then add this value to all of your accumulated 255s to give you the tick count. For example 255, 255, 10 should be treated as 255 + 255 + 10 = 520 ticks.
For the bits Capture Type, the Data is a 16384 byte bitstream. The bitstream data is the series of bits recorded from the floppy drive and normalized to 4µs intervals. The bits are packed into bytes, but the bytes will not necessarily be representative of nibble values as timing bits are also represented within the bitstream. When processing the bitstream, the order of bits within each byte is high to low, meaning the high bit goes first and the low bit is last.
Estimated Loop Point contains the number of ticks from the start of the capture till the sync sensor is triggered. This is a hint that can be used to determine the track length.
META Chunk
The ‘META’ chunk contains metadata for the disk image. The metadata is stored as a tab-delimited UTF-8 list of keys and values. Columns are by separated by a tab character (‘\t’ 0x09). All rows end with a linefeed character (‘\n’ 0x0A).
Offset | Type | Name | Usage |
---|---|---|---|
uint32 | ‘META’ Chunk ID | 0x4154454D | |
uint32 | Chunk Size | Length of the metadata string in bytes. | |
+0 | String | Metadata | Metadata string in UTF-8. No BOM. |
This is the list of standard metadata keys. Multiple values are pipe-separated.
Key | Purpose | Example Value |
---|---|---|
title | Name/Title of the product. | Prince of Persia |
subtitle | Subtitle of the product. | |
publisher | Publisher of the software. | Brøderbund Software, Inc. |
developer | Developer of the software. Pipe-delimited list if needed. | Jordan Mechner |
copyright | Copyright date. Free form text allowed. | 1989 1987 Muse Software |
version | Version number of the software. Free form text allowed. | 1.0 19870115P |
language | Language (see table A) | English |
requires_ram | RAM requirements (see table B) | 64K |
requires_machine | Which computers does this run on? Pipe-delimited list. (see table C) | 2+|2e|2c|2gs |
notes | Additional notes. | |
side | Physical disk side formatted as: “Disk #, Side [A|B]” |
Disk 1, Side A |
side_name | Name of the disk side. If the disk side is named on the label like Player, Town, Dungeon, etc then it goes here. | Front |
contributor | Name of the person who imaged the disk. | Mr. Pirate |
image_date | ISO8601 date of the imaging. | 2018-01-07T05:00:02.511Z |
If a standard key has no value, then the value will be an empty string. Key names are case-sensitive. Values cannot contain pipe, linefeed or tab characters. It would also be a good idea to keep all values as ASCII-friendly as possible to ensure compatibility with the widest range of devices that will consume these files. No duplicate keys are allowed and key order does not matter. Standard keys that have values laid out in the tables below cannot have values other that those shown below. Implementors are free to add additional keys to the metadata as long as they follow the same rules laid out here.
TABLE A – LANGUAGES
English | Spanish | French | German |
Chinese | Japanese | Italian | Dutch |
Portuguese | Danish | Finnish | Norwegian |
Swedish | Russian | Polish | Turkish |
Arabic | Thai | Czech | Hungarian |
Catalan | Croatian | Greek | Hebrew |
Romanian | Slovak | Ukrainian | Indonesian |
Malay | Vietnamese | Other |
TABLE B – REQUIRES_RAM
16K | 24K | 32K | 48K |
64K | 128K | 256K | 512K |
768K | 1M | 1.25M | 1.5M+ |
Unknown |
TABLE C – REQUIRES_MACHINE
2 | 2+ | 2e | 2c |
2e+ | 2gs | 2c+ | 3 |
3+ | mac |