update mappings to reflect recent changes
[AROS.git] / workbench / devs / diskimage / plugins / ccd.c
blob01ee2b64ac56326b2ac0457414720f4d75bb6194
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 <ctype.h>
38 #include <SDI_compiler.h>
39 #include "rev/diskimage.device_rev.h"
41 PLUGIN_VERSTAG("CCD")
43 extern struct DiskImagePlugin ccd_plugin;
45 PLUGIN_TABLE(&ccd_plugin)
47 #define LINE_BUFFER_SIZE 512
49 struct CCDTrack {
50 struct CCDTrack *next;
51 UBYTE track_num;
52 UBYTE audio;
53 UWORD sector_size;
54 UBYTE sync_size;
55 UQUAD offset;
56 UQUAD length;
57 UQUAD sectors;
58 ULONG index1;
59 ULONG index2;
62 struct CCDImage {
63 BPTR file;
64 UBYTE first_track;
65 UBYTE last_track;
66 UBYTE num_tracks;
67 struct CCDTrack *tracks;
68 ULONG block_size;
69 ULONG total_blocks;
70 QUAD file_size;
71 UBYTE *out_buf;
72 ULONG out_size;
75 BOOL CCD_Init (struct DiskImagePlugin *Self, const struct PluginData *data);
76 BOOL CCD_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
77 const UBYTE *test, LONG testsize);
78 APTR CCD_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file, CONST_STRPTR name);
79 void CCD_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr);
80 LONG CCD_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg);
81 LONG CCD_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io);
82 void CCD_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
83 ULONG *num_tracks);
84 LONG CCD_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
85 ULONG size);
87 struct DiskImagePlugin ccd_plugin = {
88 PLUGIN_NODE(0, "CCD"),
89 PLUGIN_FLAG_M68K,
90 64,
91 ZERO,
92 NULL,
93 CCD_Init,
94 NULL,
95 CCD_CheckImage,
96 CCD_OpenImage,
97 CCD_CloseImage,
98 CCD_Geometry,
99 CCD_Read,
100 NULL,
101 NULL,
102 NULL,
103 CCD_GetCDTracks,
104 CCD_ReadCDDA
107 struct Library *SysBase;
108 struct Library *DOSBase;
109 static struct Library *UtilityBase;
110 static struct DIPluginIFace *IPlugin;
112 BOOL CCD_Init (struct DiskImagePlugin *Self, const struct PluginData *data) {
113 SysBase = data->SysBase;
114 DOSBase = data->DOSBase;
115 UtilityBase = data->UtilityBase;
116 IPlugin = data->IPlugin;
117 return TRUE;
120 BOOL CCD_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
121 const UBYTE *test, LONG testsize)
123 return testsize >= 9 && !memcmp(test, "[CloneCD]", 9) && IsText(test, testsize);
126 static LONG ccd_get_img_filename (BPTR file, STRPTR buffer, LONG size);
127 static LONG ccd_parse_ini_line (STRPTR line, STRPTR *section, STRPTR *attr, STRPTR *value);
129 APTR CCD_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file, CONST_STRPTR name) {
130 LONG done = FALSE;
131 LONG error = NO_ERROR;
132 LONG error_string = NO_ERROR_STRING;
133 IPTR error_args[4] = {0};
134 struct CCDImage *image = NULL;
135 struct CCDTrack *track, *prev_track;
136 STRPTR line_buffer;
137 LONG track_num, mode, index;
138 STRPTR section, attr, value;
139 BPTR new_lock = ZERO, old_lock = ZERO;
140 TEXT filename[256];
141 UQUAD offset, length, sectors;
143 new_lock = ParentOfFH(file);
144 if (new_lock) {
145 old_lock = CurrentDir(new_lock);
148 line_buffer = AllocVec(LINE_BUFFER_SIZE, MEMF_ANY);
149 image = AllocVec(sizeof(*image), MEMF_CLEAR);
150 if (!line_buffer || !image) {
151 error = ERROR_NO_FREE_STORE;
152 goto error;
154 error = ccd_get_img_filename(file, filename, sizeof(filename));
155 if (error != NO_ERROR) goto error;
157 image->file = Open(filename, MODE_OLDFILE);
158 if (!image->file) {
159 error = IoErr();
160 goto error;
163 image->file_size = GetFileSize(image->file);
164 if (image->file_size == -1) {
165 error = IoErr();
166 goto error;
169 track = prev_track = NULL;
170 while (FGets(file, line_buffer, LINE_BUFFER_SIZE)) {
171 error = ccd_parse_ini_line(line_buffer, &section, &attr, &value);
172 if (error != NO_ERROR) break;
173 if (section) {
174 if (!strncmp(section, "TRACK ", 6)) {
175 if (!isdigit(section[6]) || StrToLong(&section[6], &track_num) == -1) {
176 error = ERROR_BAD_NUMBER;
177 break;
179 prev_track = track;
180 if (!track) {
181 track = image->tracks = AllocVec(sizeof(*track), MEMF_CLEAR);
182 } else {
183 track = track->next = AllocVec(sizeof(*track), MEMF_CLEAR);
185 if (!track) {
186 error = ERROR_NO_FREE_STORE;
187 break;
189 track->track_num = track_num;
190 if (!image->first_track || track_num < image->first_track) {
191 image->first_track = track_num;
193 if (!image->last_track || track_num > image->last_track) {
194 image->last_track = track_num;
196 track->sector_size = 2352;
197 if (track_num == 1) {
198 track->audio = FALSE;
199 track->sync_size = 16;
200 } else {
201 track->audio = TRUE;
202 track->sync_size = 0;
205 } else {
206 if (track) {
207 if (!strcmp(attr, "MODE") && value) {
208 if (StrToLong(value, &mode) == -1) {
209 error = ERROR_BAD_NUMBER;
210 break;
212 switch (mode) {
213 case 0:
214 track->audio = TRUE;
215 track->sync_size = 0;
216 break;
217 case 1:
218 track->audio = FALSE;
219 track->sync_size = 16;
220 break;
221 case 2:
222 track->audio = FALSE;
223 track->sync_size = 24;
224 break;
225 default:
226 error = ERROR_NOT_IMPLEMENTED;
227 break;
229 if (error != NO_ERROR) break;
230 } else if (!strcmp(attr, "INDEX 1") && value) {
231 if (StrToLong(value, &index) == -1) {
232 error = ERROR_BAD_NUMBER;
233 break;
235 track->index1 = index;
236 if (prev_track && !prev_track->index2) {
237 prev_track->index2 = index;
239 } else if (!strcmp(attr, "INDEX 2") && value) {
240 if (StrToLong(value, &index) == -1) {
241 error = ERROR_BAD_NUMBER;
242 break;
244 track->index2 = index;
249 if (error != NO_ERROR) {
250 goto error;
252 error = IoErr();
253 if (error != NO_ERROR) {
254 goto error;
257 if (!image->tracks) {
258 error = ERROR_REQUIRED_ARG_MISSING;
259 goto error;
262 image->block_size = 2048;
263 image->total_blocks = 0;
264 image->num_tracks = 0;
265 track = image->tracks;
266 while (track) {
267 offset = (UQUAD)track->index1 * 2352ULL;
268 if (!track->index2) {
269 sectors = (image->file_size - offset) / track->sector_size;
270 track->index2 = track->index1 + sectors;
272 if (track->index2 < track->index1) {
273 error = ERROR_BAD_NUMBER;
274 break;
276 sectors = track->index2 - track->index1;
277 length = sectors * 2352ULL;
278 track->offset = offset;
279 track->sectors = sectors;
280 track->length = length;
281 image->total_blocks += sectors;
282 image->num_tracks++;
283 track = track->next;
285 if (error != NO_ERROR) {
286 goto error;
289 done = TRUE;
291 error:
292 FreeVec(line_buffer);
293 if (new_lock) {
294 CurrentDir(old_lock);
295 UnLock(new_lock);
297 Close(file);
298 if (!done) {
299 Plugin_CloseImage(Self, image);
300 image = NULL;
301 if (error == NO_ERROR) {
302 error = ERROR_OBJECT_WRONG_TYPE;
303 error_string = MSG_EOF;
305 IPlugin_SetDiskImageErrorA(unit, error, error_string, error_args);
307 return image;
310 static void strip_file_extension (STRPTR name, CONST_STRPTR ext) {
311 LONG len = strlen(name) - strlen(ext);
312 if (len >= 0) {
313 name += len;
314 if (!Stricmp(name, ext)) {
315 *name = 0;
320 static LONG ccd_get_img_filename (BPTR file, STRPTR buffer, LONG size) {
321 struct FileInfoBlock *fib;
322 LONG error = NO_ERROR;
323 fib = AllocDosObject(DOS_FIB, NULL);
324 if (fib) {
325 if (ExamineFH(file, fib)) {
326 Strlcpy(buffer, fib->fib_FileName, size);
327 strip_file_extension(buffer, ".ccd");
328 Strlcat(buffer, ".img", size);
329 } else {
330 error = IoErr();
332 FreeDosObject(DOS_FIB, fib);
333 } else {
334 error = ERROR_NO_FREE_STORE;
336 return error;
339 static void rtrim (STRPTR string) {
340 int len = strlen(string);
341 int i;
342 for (i = len-1; i >= 0; i--) {
343 if (isspace(string[i])) {
344 string[i] = 0;
345 } else {
346 break;
351 static LONG ccd_parse_ini_line (STRPTR line, STRPTR *section, STRPTR *attr, STRPTR *value) {
352 int len;
353 *section = *attr = *value = NULL;
354 rtrim(line);
355 len = strlen(line);
356 if (line[0] == '[' && line[len-1] == ']') {
357 *section = line+1;
358 line[len-1] = 0;
359 } else {
360 *value = strchr(*attr = line, '=');
361 if (*value) *(*value)++ = 0;
363 return NO_ERROR;
366 void CCD_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr) {
367 struct CCDImage *image = image_ptr;
368 if (image) {
369 struct CCDTrack *track, *next_track;
370 track = image->tracks;
371 while (track) {
372 next_track = track->next;
373 FreeVec(track);
374 track = next_track;
376 FreeVec(image->out_buf);
377 Close(image->file);
378 FreeVec(image);
382 LONG CCD_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg) {
383 struct CCDImage *image = image_ptr;
384 dg->dg_DeviceType = DG_CDROM;
385 dg->dg_SectorSize = image->block_size;
386 dg->dg_Heads =
387 dg->dg_TrackSectors =
388 dg->dg_CylSectors = 1;
389 dg->dg_Cylinders =
390 dg->dg_TotalSectors = image->total_blocks;
391 return IOERR_SUCCESS;
394 LONG CCD_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io) {
395 struct CCDImage *image = image_ptr;
396 struct CCDTrack *track = image->tracks;
397 BPTR file = image->file;
398 UBYTE *buffer;
399 UQUAD offset;
400 ULONG size;
401 UQUAD read_offs, next_offs;
402 ULONG to_skip, to_read;
403 UQUAD read_pos;
404 ULONG read_size;
406 buffer = io->io_Data;
407 offset = ((UQUAD)io->io_Offset)|((UQUAD)io->io_Actual << 32);
408 size = io->io_Length;
409 io->io_Actual = 0;
411 if (offset & 0x7ff) return IOERR_BADADDRESS;
412 if (size & 0x7ff) return IOERR_BADLENGTH;
414 offset >>= 11;
415 size >>= 11;
416 if (offset >= image->total_blocks) {
417 return TDERR_SeekError;
419 read_offs = next_offs = 0;
420 for (;;) {
421 next_offs += track->sectors;
422 if (next_offs > offset) break;
423 read_offs = next_offs;
424 track = track->next;
425 if (!track) return TDERR_SeekError;
428 to_skip = offset - read_offs;
429 while (size) {
430 if (track->audio) {
431 return TDERR_NoSecHdr;
433 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
434 size -= to_read;
435 read_pos = track->offset + ((UQUAD)to_skip * track->sector_size);
436 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING)) {
437 return TDERR_SeekError;
439 if (track->sector_size == 2048) {
440 read_size = to_read << 11;
441 if (Read(file, buffer, read_size) != read_size) {
442 return IPlugin_DOS2IOErr(IoErr());
444 buffer += read_size;
445 io->io_Actual += read_size;
446 } else {
447 read_size = track->sector_size;
448 if (!(image->out_buf = ReAllocBuf(image->out_buf, &image->out_size, read_size))) {
449 return ERROR_NO_FREE_STORE;
451 while (to_read--) {
452 if (Read(file, image->out_buf, read_size) != read_size) {
453 return IPlugin_DOS2IOErr(IoErr());
455 CopyMem(image->out_buf + track->sync_size, buffer, 2048);
456 buffer += 2048;
457 io->io_Actual += 2048;
460 if (size) {
461 to_skip = 0;
462 track = track->next;
463 if (!track) return IOERR_BADLENGTH;
466 return IOERR_SUCCESS;
469 void CCD_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
470 ULONG *num_tracks)
472 struct CCDImage *image = image_ptr;
473 *tracks = (struct CDTrack *)image->tracks;
474 *num_tracks = image->num_tracks;
477 LONG CCD_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
478 ULONG size)
480 struct CCDImage *image = image_ptr;
481 UBYTE *buffer = buffer_ptr;
482 struct CCDTrack *track = image->tracks;
483 BPTR file = image->file;
484 UQUAD read_offs, next_offs;
485 ULONG to_skip, to_read;
486 UQUAD read_pos;
487 ULONG read_size;
488 ULONG bytes_read = 0;
490 if (offset >= image->total_blocks) {
491 return bytes_read;
493 read_offs = next_offs = 0;
494 for (;;) {
495 next_offs += track->sectors;
496 if (next_offs > offset) break;
497 read_offs = next_offs;
498 track = track->next;
499 if (!track) return bytes_read;
502 to_skip = offset - read_offs;
503 while (size) {
504 if (!track->audio || track->sector_size != 2352) {
505 return bytes_read;
507 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
508 size -= to_read;
509 read_pos = track->offset + ((UQUAD)to_skip * 2352ULL);
510 read_size = to_read * 2352;
511 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING) ||
512 Read(file, buffer, read_size) != read_size)
514 return bytes_read;
516 buffer += read_size;
517 bytes_read += read_size;
518 to_skip = 0;
519 track = track->next;
520 if (!track) return bytes_read;
522 return bytes_read;