PSI file format (2020-04-12)
========================================================================

File structure
------------------------------------------------------------------------

<file header chunk>
<chunk 0>
<chunk 1>
...
<chunk n>
<end chunk>

All integers are in big-endian format.

Unknown chunks should be skipped.


Chunk format
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID
4	4	Chunk size (n)
0	n	Chunk data
n	4	Chunk CRC

	- The size does not include the chunk ID, chunk size or chunk CRC
	  fields.

	- The chunk CRC covers the chunk ID, chunk size and chunk data.


CHUNK "PSI ": File header chunk
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('PSI ' = 0x50534920)
4	4	Chunk size (4)
0	2	Format version (0)
2	2	Default sector format
			00 00	unknown
			01 00	IBM FM
			02 00	IBM MFM DD
			02 01	IBM MFM HD
			02 02	IBM MFM ED
			03 00	MAC GCR
4	4	Chunk CRC

	- This must be the first chunk in the file.


CHUNK "END ": End chunk
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('END ')
4	4	Chunk size (0)
0	4	Chunk CRC (0x3d64af78)

	- This must be the last chunk in the file. Any data after
	  an 'END ' chunk should be ignored.


CHUNK "TEXT": Comments
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('TEXT')
4	4	Chunk size
0	n	Comment
n	4	Chunk CRC

	- Comments should be UTF-8, with lines separated by LF (0x0a).

	- If there are multiple TEXT chunks, their contents should be
	  concatenated.


CHUNK "SECT": Sector header
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('SECT')
4	4	Chunk size (8)

0	2	cylinder
2	1	head
3	1	sector
4	2	size in bytes
6	1	flags
			1	Compressed
			2	Alternate sector
			4	Data CRC error
7	1	Compressed sector data

8	4	Chunk CRC

	- The alternate sector flag indicates that this is the same
	  physical sector as the one immediately preceding it.

	- The sector header chunk starts a new sector. All non sector
	  header chunks that follow refer to the same sector.

	- If the sector is marked as compressed, the sector data is
	  filled with the byte in <compressed sector data> and no
	  data chunk follows.


CHUNK "DATA": Sector data
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('DATA')
4	4	Chunk size (n)
0	n	Sector data
n	4	Chunk CRC


CHUNK "WEAK": Weak bit mask
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('WEAK')
4	4	Chunk size (n)
0	n	Weak bit mask
n	4	Chunk CRC

	- The weak bit mask is the same size as the sector data. Each bit
	  indicates whether the corresponding bit in the data field is
	  weak.


CHUNK "IBMF": IBM FM sector header
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('IBMF')
4	4	Chunk size (6)
0	1	Cylinder
1	1	Head
2	1	Sector
3	1	Size
4	1	Flags
			1	CRC error in ID field
			2	CRC error in data field
			4	Deleted data adress mark
			8	Missing data adress mark
5	1	Encoding subtype
			0	Single density (125 KBit/s)
			1	High density (250 KBit/s)
6	4	Chunk CRC


CHUNK "IBMM": IBM MFM sector header
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('IBMM')
4	4	Chunk size (6)
0	1	Cylinder
1	1	Head
2	1	Sector
3	1	Size
4	1	Flags
			1	CRC error in ID field
			2	CRC error in data field
			4	Deleted data adress mark
			8	Missing data adress mark
5	1	Encoding subtype
			0	Double density (250 KBit/s)
			1	High density (500 KBit/s)
			2	Extra density (1000 KBit/s)
6	4	Chunk CRC


CHUNK "MACG": Macintosh GCR sector header
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('MACG')
4	4	Chunk size (18)
0	2	Cylinder
2	1	Head
3	1	Sector
4	1	Sector format
5	1	Flags
			1	Checksum error in ID field
			2	Checksum error in data field
			4	Missing data mark
6	12	Tag data
18	4	Chunk CRC


CHUNK "OFFS": Sector position
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('OFFS')
4	4	Chunk size (4)
0	4	Sector position in data bits from the start of the track
4	4	Chunk CRC


CHUNK "TIME": Clock rate adjustment
------------------------------------------------------------------------

offset	size	description

0	4	Chunk ID ('TIME')
4	4	Chunk size (4)
0	4	Sector read time in data bits
4	4	Chunk CRC

	- The time it takes to read the data field of this sector is
	  <sector read time> / <number of bits in data field>.
	  For example, for a 512 byte sector a sector read time of 4096
	  would be normal. Larger values would indicate that it takes
	  longer to read the sector.


CRC
------------------------------------------------------------------------

	- The algorithm used is big-endian CRC-32 with generator
	  polynomial 0x1edc6f41. The CRC value is initialized to 0.

	unsigned long psi_crc (const unsigned char *buf, unsigned cnt)
	{
		unsigned      i, j;
		unsigned long crc;

		crc = 0;

		for (i = 0; i < cnt; i++) {
			crc ^= (unsigned long) (buf[i] & 0xff) << 24;

			for (j = 0; j < 8; j++) {
				if (crc & 0x80000000) {
					crc = (crc << 1) ^ 0x1edc6f41;
				}
				else {
					crc = crc << 1;
				}
			}
		}

		return (crc & 0xffffffff);
	}
