The Xexor ARC file format
Xexor is a powerful disc utility written by Richard Wilson for the Amstrad CPC/CPC+.
This document describes the structure of the "ARC" file written by the "ARCHIVE" command of this utility program and his Winape emulator.
The syntax of the archive command is:
|ARCHIVE <filename> <track range>
An example of it's usage is:
|ARCHIVE "disc.arc" 1-13
This will create a file called "disc.arc" which will contain the data from tracks 1 to 13 inclusive.
Structure of the "ARC" file
The "ARC" file is a binary file and will have an AMSDOS header.
In addition, "ARC" files written by the Winape emulator have a different header.
If you are reading an "ARC" file into an emulator be aware that there may be an AMSDOS header, and that the file may have been created by Winape.
The ARC file data starts with a header which describes the start and end range of tracks stored in the file:
ARC files written by Winape have the following header:
Length | Description |
---|---|
2 | 'XA' identifier |
1 | Drive Definition Byte |
1 | First track |
1 | Last track |
The drive definition byte is as follows:
Bit | Description |
---|---|
3 | Double-stepped if set |
2 | Head when not double-sided |
1 | Unused |
0 | Double-sided when set |
If the image is double-sided, alternate heads are stored for each cylinder.
ARC files written by older versions of Xexor have the following header (ie. XA missing):
Length | Description |
---|---|
1 | First track |
1 | Last track |
Following this is the data for each track, starting with the first track, in ascending numeric order. All tracks between the first track and last track are stored in the file. No tracks may be omitted.
The length of data for each track varies and is dependant on the number of sectors in the track. Each track has the same header layout which gives the number of tracks, and the IDs of these sectors.
The data for each sector is stored in the order given by the sector IDs.
i.e. if there are 9 sectors per track and the sector IDS are listed as &C1,&C2,&C3, &C4,&C5,&C6,&C7,&C8 and &C9, then the data for sector &C1 will be first, and this will be followed by the data for sector &C2 and so on until sector &C9.
The data for each sector is compressed seperatly using a run-length encoding method.
Track data:
Length | Description |
---|---|
1 | Number of sectors |
4*Number of sectors | Sector ID information (see below) |
? | Sector data (see below) |
Sector ID information:
Length | Description |
---|---|
1 | C from ID field |
1 | H from ID field |
1 | R from ID field |
1 | N from ID field |
Sector data:
Length | Description |
---|---|
2 | Sector Size and Flags |
? | Compressed sector data |
The Sector Size and Flags are stored as a word (LSB first), containing the stored data length (when not empty), compression flag, and deleted data address mark.
The top 3 bits of the Size are flags, with the bottom 13 bits containing the size of the stored data (when not empty).
A sector which does not have a deleted data address mark and contains only E5 bytes is considered empty. In this case, the top 3 bits will be 100 and the size is ignored. WinAPE stores this as #8009, but it could be any value from #0000 to #1FFF or #8000 to #9FFF.
For all other sectors:
Bit | Description |
---|---|
15 | RLE compressed if set (see below) |
14 | Sector has Deleted Data Address Mark if set (DDAM) |
13 | Sector data is present if set (not empty) |
Sector data is also assumed to be present if bit 14 is set, regardless of bit 13.
Compression scheme
The compression method is "run length encoding".
"run length encoding" is a simple compression algorithm which can compress sequences of the same repeated byte.
The compressed will contain single data bytes and run-length packets.
A "marker" byte of &E5 is used to identify a run-length packet. (&E5 is the byte used to fill empty sectors when a disc is formatted to the standard SYSTEM, DATA and IBM formats).
Run-length packets:
Byte sequence | Result |
---|---|
&E5,&00 | Output a single &E5 byte |
&E5,<count>,<data> | Output the byte <data> <count> times. e.g. &E5,&03,&25 -> &25,&25,&25 |
<value> | <value> must not be &E5. Output <value> once |