A2R 2.x Disk Image Reference

Created by John K. Morris

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) {
		// read the INFO chunk
		// read the STRM chunk
		// read the META chunk
		// no idea what this chunk is, so skip it

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
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.

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
16K 24K 32K 48K
64K 128K 256K 512K
768K 1M 1.25M 1.5M+
2 2+ 2e 2c
2e+ 2gs 2c+ 3
3+ mac