update mappings to reflect recent changes
[AROS.git] / workbench / devs / diskimage / plugins / mds.c
bloba87ae3dd36f159fcefa77e7de074db96330adbb2
1 /* Copyright 2007-2012 Fredrik Wikstrom. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 **
7 ** 1. Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 **
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 ** notice, this list of conditions and the following disclaimer in the
12 ** documentation and/or other materials provided with the distribution.
14 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
15 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 ** POSSIBILITY OF SUCH DAMAGE.
27 #define USED_PLUGIN_API_VERSION 8
28 #include <devices/diskimage.h>
29 #include <proto/exec.h>
30 #include <proto/dos.h>
31 #include <proto/utility.h>
32 #include "endian.h"
33 #include "device_locale.h"
34 #include "scsicmd.h"
35 #include "support.h"
36 #include <string.h>
37 #include <SDI_compiler.h>
38 #include "rev/diskimage.device_rev.h"
40 PLUGIN_VERSTAG("MDS")
42 extern struct DiskImagePlugin mds_plugin;
44 PLUGIN_TABLE(&mds_plugin)
46 struct MDSTrack {
47 struct MDSTrack *next;
48 UBYTE track_num;
49 UBYTE audio;
50 UWORD sector_size;
51 UBYTE sync_size;
52 UQUAD offset;
53 UQUAD length;
54 UQUAD sectors;
57 struct MDSImage {
58 BPTR file;
59 UBYTE first_track;
60 UBYTE last_track;
61 UBYTE num_tracks;
62 struct MDSTrack *tracks;
63 ULONG block_size;
64 ULONG total_blocks;
65 UBYTE *out_buf;
66 ULONG out_size;
69 BOOL MDS_Init (struct DiskImagePlugin *Self, const struct PluginData *data);
70 BOOL MDS_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
71 const UBYTE *test, LONG testsize);
72 APTR MDS_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file, CONST_STRPTR name);
73 void MDS_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr);
74 LONG MDS_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg);
75 LONG MDS_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io);
76 void MDS_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
77 ULONG *num_tracks);
78 LONG MDS_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
79 ULONG size);
81 struct DiskImagePlugin mds_plugin = {
82 PLUGIN_NODE(0, "MDS"),
83 PLUGIN_FLAG_M68K,
84 16,
85 ZERO,
86 NULL,
87 MDS_Init,
88 NULL,
89 MDS_CheckImage,
90 MDS_OpenImage,
91 MDS_CloseImage,
92 MDS_Geometry,
93 MDS_Read,
94 NULL,
95 NULL,
96 NULL,
97 MDS_GetCDTracks,
98 MDS_ReadCDDA
101 struct Library *SysBase;
102 struct Library *DOSBase;
103 static struct Library *UtilityBase;
104 static struct DIPluginIFace *IPlugin;
106 BOOL MDS_Init (struct DiskImagePlugin *Self, const struct PluginData *data) {
107 SysBase = data->SysBase;
108 DOSBase = data->DOSBase;
109 UtilityBase = data->UtilityBase;
110 IPlugin = data->IPlugin;
111 return TRUE;
114 #define MDS_SIGNATURE "MEDIA DESCRIPTOR"
116 BOOL MDS_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
117 const UBYTE *test, LONG testsize)
119 return testsize >= 16 && !memcmp(test, MDS_SIGNATURE, 16);
122 #pragma pack(1)
124 typedef struct {
125 TEXT signature[16];
126 UBYTE version[2];
127 UWORD medium_type;
128 UWORD num_sessions;
129 UBYTE unknown1[4];
130 UWORD bca_len;
131 UBYTE unknown2[8];
132 ULONG bca_data_offset;
133 UBYTE unknown3[24];
134 ULONG disc_structures_offset;
135 UBYTE unknown4[12];
136 ULONG sessions_blocks_offset;
137 ULONG dpm_blocks_offset;
138 } mds_header_t;
140 #define MDS_MEDIUM_CD 0x00
141 #define MDS_MEDIUM_CD_R 0x01
142 #define MDS_MEDIUM_CD_RW 0x02
143 #define MDS_MEDIUM_DVD 0x10
144 #define MDS_MEDIUM_DVD_MINUS_R 0x12
146 typedef struct {
147 LONG session_start;
148 LONG session_end;
149 UWORD session_number;
150 UBYTE num_all_blocks;
151 UBYTE num_nontrack_blocks;
152 UWORD num_sessions;
153 UWORD num_tracks;
154 UBYTE unknown1[4];
155 ULONG tracks_blocks_offset;
156 } mds_session_block_t;
158 typedef struct {
159 UBYTE mode;
160 UBYTE subchannel;
161 UBYTE adr_ctl;
162 UBYTE unknown1;
163 UBYTE track_num;
164 UBYTE unknown2[4];
165 UBYTE min;
166 UBYTE sec;
167 UBYTE frame;
168 ULONG extra_offset;
169 UWORD sector_size;
170 UBYTE unknown3[18];
171 ULONG start_sector;
172 UQUAD start_offset;
173 ULONG number_of_files;
174 ULONG footer_offset;
175 UBYTE unknown4[24];
176 } mds_track_block_t;
178 #define MDS_TRACKMODE_UNKNOWN 0x00
179 #define MDS_TRACKMODE_AUDIO 0xA9
180 #define MDS_TRACKMODE_MODE1 0xAA
181 #define MDS_TRACKMODE_MODE2 0xAB
182 #define MDS_TRACKMODE_MODE2_FORM1 0xAC
183 #define MDS_TRACKMODE_MODE2_FORM2 0xAD
185 #define MDS_LEADIN_TRACK_FIRST 0xA0
186 #define MDS_LEADIN_TRACK_LAST 0xA1
187 #define MDS_LEADIN_TRACK_LEADOUT 0xA2
189 typedef struct {
190 ULONG pregap;
191 ULONG sectors;
192 } mds_extra_block_t;
194 typedef struct {
195 ULONG filename_offset;
196 ULONG widechar_filename;
197 UBYTE unknown1[8];
198 } mds_footer_t;
200 #pragma pack()
202 static LONG mds_get_mdf_filename (BPTR file, STRPTR buffer, LONG size);
204 APTR MDS_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file, CONST_STRPTR name) {
205 LONG done = FALSE;
206 LONG error = NO_ERROR;
207 LONG error_string = NO_ERROR_STRING;
208 IPTR error_args[4] = {0};
209 struct MDSImage *image = NULL;
210 struct MDSTrack *track = NULL;
211 mds_header_t mds_header;
212 mds_session_block_t mds_session;
213 mds_track_block_t mds_track;
214 mds_extra_block_t mds_extra;
215 ULONG track_block_offset;
216 ULONG extra_block_offset;
217 BPTR new_lock = ZERO, old_lock = ZERO;
218 TEXT filename[256];
219 UBYTE mode;
220 LONG i;
222 new_lock = ParentOfFH(file);
223 if (new_lock) {
224 old_lock = CurrentDir(new_lock);
227 image = AllocVec(sizeof(*image), MEMF_CLEAR);
228 if (!image) {
229 error = ERROR_NO_FREE_STORE;
230 goto error;
233 error = mds_get_mdf_filename(file, filename, sizeof(filename));
234 if (error != NO_ERROR) goto error;
236 image->file = Open(filename, MODE_OLDFILE);
237 if (!image->file) {
238 error = IoErr();
239 goto error;
242 if (Read(file, &mds_header, sizeof(mds_header)) != sizeof(mds_header) ||
243 !ChangeFilePosition(file, rle32(&mds_header.sessions_blocks_offset), OFFSET_BEGINNING) ||
244 Read(file, &mds_session, sizeof(mds_session)) != sizeof(mds_session))
246 error = IoErr();
247 goto error;
250 if (memcmp(mds_header.signature, MDS_SIGNATURE, sizeof(mds_header.signature))) {
251 error = ERROR_OBJECT_WRONG_TYPE;
252 goto error;
255 image->num_tracks = rle16(&mds_session.num_tracks);
256 image->block_size = 2048;
257 image->total_blocks = rle32(&mds_session.session_end);
259 track = (struct MDSTrack *)&image->tracks;
260 track_block_offset = rle32(&mds_session.tracks_blocks_offset);
261 for (i = 0; i < mds_session.num_all_blocks; i++) {
262 if (!ChangeFilePosition(file, track_block_offset, OFFSET_BEGINNING) ||
263 Read(file, &mds_track, sizeof(mds_track)) != sizeof(mds_track))
265 error = IoErr();
266 break;
268 track_block_offset += sizeof(mds_track);
269 mode = mds_track.mode;
270 if (mode != MDS_TRACKMODE_UNKNOWN) {
271 track = track->next = AllocVec(sizeof(*track), MEMF_ANY);
272 if (!track) {
273 error = ERROR_NO_FREE_STORE;
274 break;
276 track->next = NULL;
277 track->track_num = mds_track.track_num;
278 if (!image->first_track || track->track_num < image->first_track) {
279 image->first_track = track->track_num;
281 if (!image->last_track || track->track_num > image->last_track) {
282 image->last_track = track->track_num;
284 track->sector_size = rle16(&mds_track.sector_size);
285 track->sync_size = 0;
286 switch (mode) {
287 case MDS_TRACKMODE_AUDIO:
288 track->audio = TRUE;
289 if (track->sector_size != 2352) {
290 error = ERROR_BAD_NUMBER;
292 break;
293 case MDS_TRACKMODE_MODE1:
294 track->audio = FALSE;
295 switch (track->sector_size) {
296 case 2048:
297 break;
298 case 2352:
299 track->sync_size = 16;
300 break;
301 default:
302 error = ERROR_BAD_NUMBER;
303 break;
305 break;
307 if (error != NO_ERROR) break;
308 track->offset = rle64(&mds_track.start_offset);
309 extra_block_offset = rle32(&mds_track.extra_offset);
310 if (!extra_block_offset) {
311 error = ERROR_REQUIRED_ARG_MISSING;
312 break;
314 if (!ChangeFilePosition(file, extra_block_offset, OFFSET_BEGINNING) ||
315 Read(file, &mds_extra, sizeof(mds_extra)) != sizeof(mds_extra))
317 error = IoErr();
318 break;
320 track->sectors = rle32(&mds_extra.sectors);
321 track->length = track->sectors * track->sector_size;
324 if (error != NO_ERROR) goto error;
326 done = TRUE;
328 error:
329 if (new_lock) {
330 CurrentDir(old_lock);
331 UnLock(new_lock);
333 Close(file);
334 if (!done) {
335 Plugin_CloseImage(Self, image);
336 image = NULL;
337 if (error == NO_ERROR) {
338 error = ERROR_OBJECT_WRONG_TYPE;
339 error_string = MSG_EOF;
341 IPlugin_SetDiskImageErrorA(unit, error, error_string, error_args);
343 return image;
346 static void strip_file_extension (STRPTR name, CONST_STRPTR ext) {
347 LONG len = strlen(name) - strlen(ext);
348 if (len >= 0) {
349 name += len;
350 if (!Stricmp(name, ext)) {
351 *name = 0;
356 static LONG mds_get_mdf_filename (BPTR file, STRPTR buffer, LONG size) {
357 struct FileInfoBlock *fib;
358 LONG error = NO_ERROR;
359 fib = AllocDosObject(DOS_FIB, NULL);
360 if (fib) {
361 if (ExamineFH(file, fib)) {
362 Strlcpy(buffer, fib->fib_FileName, size);
363 strip_file_extension(buffer, ".mds");
364 Strlcat(buffer, ".mdf", size);
365 } else {
366 error = IoErr();
368 FreeDosObject(DOS_FIB, fib);
369 } else {
370 error = ERROR_NO_FREE_STORE;
372 return error;
375 void MDS_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr) {
376 struct MDSImage *image = image_ptr;
377 if (image) {
378 struct MDSTrack *track, *next_track;
379 track = image->tracks;
380 while (track) {
381 next_track = track->next;
382 FreeVec(track);
383 track = next_track;
385 FreeVec(image->out_buf);
386 Close(image->file);
387 FreeVec(image);
391 LONG MDS_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg) {
392 struct MDSImage *image = image_ptr;
393 dg->dg_DeviceType = DG_CDROM;
394 dg->dg_SectorSize = image->block_size;
395 dg->dg_Heads =
396 dg->dg_TrackSectors =
397 dg->dg_CylSectors = 1;
398 dg->dg_Cylinders =
399 dg->dg_TotalSectors = image->total_blocks;
400 return IOERR_SUCCESS;
403 LONG MDS_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io) {
404 struct MDSImage *image = image_ptr;
405 struct MDSTrack *track = image->tracks;
406 BPTR file = image->file;
407 UBYTE *buffer;
408 UQUAD offset;
409 ULONG size;
410 UQUAD read_offs, next_offs;
411 ULONG to_skip, to_read;
412 UQUAD read_pos;
413 ULONG read_size;
415 buffer = io->io_Data;
416 offset = ((UQUAD)io->io_Offset)|((UQUAD)io->io_Actual << 32);
417 size = io->io_Length;
418 io->io_Actual = 0;
420 if (offset & 0x7ff) return IOERR_BADADDRESS;
421 if (size & 0x7ff) return IOERR_BADLENGTH;
423 offset >>= 11;
424 size >>= 11;
425 if (offset >= image->total_blocks) {
426 return TDERR_SeekError;
428 read_offs = next_offs = 0;
429 for (;;) {
430 next_offs += track->sectors;
431 if (next_offs > offset) break;
432 read_offs = next_offs;
433 track = track->next;
434 if (!track) return TDERR_SeekError;
437 to_skip = offset - read_offs;
438 while (size) {
439 if (track->audio) {
440 return TDERR_NoSecHdr;
442 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
443 size -= to_read;
444 read_pos = track->offset + ((UQUAD)to_skip * track->sector_size);
445 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING)) {
446 return TDERR_SeekError;
448 if (track->sector_size == 2048) {
449 read_size = to_read << 11;
450 if (Read(file, buffer, read_size) != read_size) {
451 return IPlugin_DOS2IOErr(IoErr());
453 buffer += read_size;
454 io->io_Actual += read_size;
455 } else {
456 read_size = track->sector_size;
457 if (!(image->out_buf = ReAllocBuf(image->out_buf, &image->out_size, read_size))) {
458 return ERROR_NO_FREE_STORE;
460 while (to_read--) {
461 if (Read(file, image->out_buf, read_size) != read_size) {
462 return IPlugin_DOS2IOErr(IoErr());
464 CopyMem(image->out_buf + track->sync_size, buffer, 2048);
465 buffer += 2048;
466 io->io_Actual += 2048;
469 if (size) {
470 to_skip = 0;
471 track = track->next;
472 if (!track) return IOERR_BADLENGTH;
475 return IOERR_SUCCESS;
478 void MDS_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
479 ULONG *num_tracks)
481 struct MDSImage *image = image_ptr;
482 *tracks = (struct CDTrack *)image->tracks;
483 *num_tracks = image->num_tracks;
486 LONG MDS_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
487 ULONG size)
489 struct MDSImage *image = image_ptr;
490 UBYTE *buffer = buffer_ptr;
491 struct MDSTrack *track = image->tracks;
492 BPTR file = image->file;
493 UQUAD read_offs, next_offs;
494 ULONG to_skip, to_read;
495 UQUAD read_pos;
496 ULONG read_size;
497 ULONG bytes_read = 0;
499 if (offset >= image->total_blocks) {
500 return bytes_read;
502 read_offs = next_offs = 0;
503 for (;;) {
504 next_offs += track->sectors;
505 if (next_offs > offset) break;
506 read_offs = next_offs;
507 track = track->next;
508 if (!track) return bytes_read;
511 to_skip = offset - read_offs;
512 while (size) {
513 if (!track->audio || track->sector_size != 2352) {
514 return bytes_read;
516 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
517 size -= to_read;
518 read_pos = track->offset + ((UQUAD)to_skip * 2352ULL);
519 read_size = to_read * 2352;
520 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING) ||
521 Read(file, buffer, read_size) != read_size)
523 return bytes_read;
525 buffer += read_size;
526 bytes_read += read_size;
527 to_skip = 0;
528 track = track->next;
529 if (!track) return bytes_read;
531 return bytes_read;