1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*********************************************************************
formats/apd_dsk.c
Archimedes Protected Disk Image format
APD file structure
------------------
The APD file is a GZip compressed version of the original APD file.
Compressed file always starts:
1F 8B 08 00 00 00 00 00 00 0B EC BD
^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^
| | | | | | | | | |
| | | | | | | | | +- OS
| | | | | | | | +---- xfl
| | | | +--+--+--+------- time
| | | +------------------- gzip flags
| | +---------------------- gzip compression*
| |
+--+------------------------- gzip header
* Compression method: 8 is the only supported format
Original APD file structure:
0 - 7 "APDX0001" identifier
8 - B t0sd - Track 0 SD length in bits
C - F t0dd - Track 0 DD length in bits
10 - 13 t0qd - Track 0 QD length in bits
14 - 1F t1sd - Track 1 SD length in bits
20 - 23 t1dd - Track 1 DD length in bits
24 - 27 t1qd - Track 1 QD length in bits
... ... repeated to Track 159
77C - 787 Track 160 (blank)
7C4 - 7CF Track 166 (blank)
7D0 Track 0 SD data
+ (t0sd + 7) >> 3 Track 0 DD data
+ (t0dd + 7) >> 3 Track 0 QD data
+ (t0qd + 7) >> 3 Track 1 SD data
+ (t1sd + 7) >> 3 Track 1 DD data
+ (t1dd + 7) >> 3 Track 1 QD data
SD data is big-endian raw FM words
DD/QD data is big-endian raw MFM words
As far as I can tell, the tracks are always sequential, so
physical tracks translate as:
Physical APD
-------------- -------
Side 0 Track 0 > Track 0
Side 1 Track 0 > Track 1
Side 0 Track 1 > Track 2
etc.
*********************************************************************/
#include <zlib.h>
#include "formats/apd_dsk.h"
static const uint8_t APD_HEADER[8] = { 'A', 'P', 'D', 'X', '0', '0', '0', '1' };
static const uint8_t GZ_HEADER[2] = { 0x1f, 0x8b };
apd_format::apd_format()
{
}
const char *apd_format::name() const
{
return "apd";
}
const char *apd_format::description() const
{
return "Archimedes Protected Disk Image";
}
const char *apd_format::extensions() const
{
return "apd";
}
int apd_format::identify(io_generic *io, uint32_t form_factor)
{
uint64_t size = io_generic_size(io);
std::vector<uint8_t> img(size);
io_generic_read(io, &img[0], 0, size);
int err;
std::vector<uint8_t> gz_ptr(8);
z_stream d_stream;
if (!memcmp(&img[0], GZ_HEADER, sizeof(GZ_HEADER))) {
d_stream.zalloc = nullptr;
d_stream.zfree = nullptr;
d_stream.opaque = nullptr;
d_stream.next_in = &img[0];
d_stream.avail_in = size;
d_stream.next_out = &gz_ptr[0];
d_stream.avail_out = 8;
err = inflateInit2(&d_stream, MAX_WBITS | 16);
if (err != Z_OK) return 0;
err = inflate(&d_stream, Z_SYNC_FLUSH);
if (err != Z_OK) return 0;
err = inflateEnd(&d_stream);
if (err != Z_OK) return 0;
img = gz_ptr;
}
if (!memcmp(&img[0], APD_HEADER, sizeof(APD_HEADER))) {
return 100;
}
return 0;
}
bool apd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
uint64_t size = io_generic_size(io);
std::vector<uint8_t> img(size);
io_generic_read(io, &img[0], 0, size);
int err;
std::vector<uint8_t> gz_ptr;
z_stream d_stream;
int inflate_size = (img[size - 1] << 24) | (img[size - 2] << 16) | (img[size - 3] << 8) | img[size - 4];
uint8_t *in_ptr = &img[0];
if (!memcmp(&img[0], GZ_HEADER, sizeof(GZ_HEADER))) {
gz_ptr.resize(inflate_size);
d_stream.zalloc = nullptr;
d_stream.zfree = nullptr;
d_stream.opaque = nullptr;
d_stream.next_in = in_ptr;
d_stream.avail_in = size;
d_stream.next_out = &gz_ptr[0];
d_stream.avail_out = inflate_size;
err = inflateInit2(&d_stream, MAX_WBITS | 16);
if (err != Z_OK) {
osd_printf_error("inflateInit2 error: %d\n", err);
return false;
}
err = inflate(&d_stream, Z_FINISH);
if (err != Z_STREAM_END && err != Z_OK) {
osd_printf_error("inflate error: %d\n", err);
return false;
}
err = inflateEnd(&d_stream);
if (err != Z_OK) {
osd_printf_error("inflateEnd error: %d\n", err);
return false;
}
size = inflate_size;
img = gz_ptr;
}
int data = 0x7d0;
for (int track = 0; track < 166; track++) {
uint32_t sdlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x0]));
uint32_t ddlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x4]));
uint32_t qdlen = little_endianize_int32(*(uint32_t *)(&img[(track * 12) + 8 + 0x8]));
if (sdlen > 0) {
generate_track_from_bitstream(track / 2, track % 2, &img[data], sdlen, image);
data += (sdlen + 7) >> 3;
}
if (ddlen > 0) {
generate_track_from_bitstream(track / 2, track % 2, &img[data], ddlen, image);
data += (ddlen + 7) >> 3;
}
if (qdlen > 0) {
generate_track_from_bitstream(track / 2, track % 2, &img[data], qdlen, image);
data += (qdlen + 7) >> 3;
}
}
image->set_variant(floppy_image::DSDD);
return true;
}
bool apd_format::supports_save() const
{
return false;
}
const floppy_format_type FLOPPY_APD_FORMAT = &floppy_image_format_creator<apd_format>;
|