Were did that svn:executable come from?
[AROS.git] / workbench / devs / diskimage / plugins / nrg.c
blob42023594191f7e0ee9a2ba5a7087733c4db82e4d
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 <string.h>
36 #include <SDI_compiler.h>
37 #include "rev/diskimage.device_rev.h"
39 PLUGIN_VERSTAG("NRG")
41 extern struct DiskImagePlugin nrg_plugin;
43 PLUGIN_TABLE(&nrg_plugin)
45 struct NRGTrack {
46 struct NRGTrack *next;
47 UBYTE track_num;
48 UBYTE audio;
49 UWORD sector_size;
50 UBYTE sync_size;
51 UQUAD offset;
52 UQUAD length;
53 UQUAD sectors;
56 struct NRGImage {
57 BPTR file;
58 UBYTE nrg_version;
59 UBYTE first_track;
60 UBYTE last_track;
61 UBYTE num_tracks;
62 struct NRGTrack *tracks;
63 ULONG block_size;
64 ULONG total_blocks;
65 UQUAD total_bytes;
66 UBYTE *out_buf;
67 ULONG out_size;
70 BOOL NRG_Init (struct DiskImagePlugin *Self, const struct PluginData *data);
71 BOOL NRG_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
72 const UBYTE *test, LONG testsize);
73 APTR NRG_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file, CONST_STRPTR name);
74 void NRG_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr);
75 LONG NRG_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg);
76 LONG NRG_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io);
77 void NRG_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
78 ULONG *num_tracks);
79 LONG NRG_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
80 ULONG size);
82 struct DiskImagePlugin nrg_plugin = {
83 PLUGIN_NODE(0, "NRG"),
84 PLUGIN_FLAG_FOOTER|PLUGIN_FLAG_M68K,
85 12,
86 ZERO,
87 NULL,
88 NRG_Init,
89 NULL,
90 NRG_CheckImage,
91 NRG_OpenImage,
92 NRG_CloseImage,
93 NRG_Geometry,
94 NRG_Read,
95 NULL,
96 NULL,
97 NULL,
98 NRG_GetCDTracks,
99 NRG_ReadCDDA
102 struct Library *SysBase;
103 struct Library *DOSBase;
104 static struct DIPluginIFace *IPlugin;
106 BOOL NRG_Init (struct DiskImagePlugin *Self, const struct PluginData *data) {
107 SysBase = data->SysBase;
108 DOSBase = data->DOSBase;
109 IPlugin = data->IPlugin;
110 return TRUE;
113 #define ID_NERO MAKE_ID('N','E','R','O') /* v1 */
114 #define ID_NER5 MAKE_ID('N','E','R','5') /* v2 */
115 #define ID_CUES MAKE_ID('C','U','E','S') /* v1 */
116 #define ID_CUEX MAKE_ID('C','U','E','X') /* v2 */
117 #define ID_DAOI MAKE_ID('D','A','O','I') /* v1 */
118 #define ID_DAOX MAKE_ID('D','A','O','X') /* v2 */
119 #define ID_TAOI MAKE_ID('T','A','O','I') /* v1 */
120 #define ID_TAOX MAKE_ID('T','A','O','X') /* v2 */
121 #define ID_CDTX MAKE_ID('C','D','T','X') /* v2 */
122 #define ID_ETNF MAKE_ID('E','T','N','F') /* v1 */
123 #define ID_ETN2 MAKE_ID('E','T','N','2') /* v2 */
124 #define ID_SINF MAKE_ID('S','I','N','F') /* v1 & v2 */
125 #define ID_MTYP MAKE_ID('M','T','Y','P') /* v1 & v2 */
126 #define ID_END MAKE_ID('E','N','D','!') /* v1 & v2 */
128 BOOL NRG_CheckImage (struct DiskImagePlugin *Self, BPTR file, CONST_STRPTR name, QUAD file_size,
129 const UBYTE *test, LONG testsize)
131 return (testsize >= 8 && rbe32(&test[testsize-8]) == ID_NERO) ||
132 (testsize >= 12 && rbe32(&test[testsize-12]) == ID_NER5);
135 #pragma pack(1)
137 typedef union {
138 struct {
139 ULONG pad;
140 ULONG id;
141 ULONG offset;
142 } v1;
143 struct {
144 ULONG id;
145 UQUAD offset;
146 } v2;
147 } nrg_footer_t;
149 typedef struct {
150 ULONG id;
151 ULONG size;
152 } nrg_chunk_t;
154 typedef struct {
155 UBYTE reserved[10];
156 ULONG sector_size;
157 ULONG mode;
158 ULONG index0; // pre gap
159 ULONG index1; // start of track
160 ULONG index2; // end of track + 1
161 } nrg_daoi_track_t;
163 typedef struct {
164 ULONG chunk_size; // chunk size in little-endian format
165 ULONG reserved[3];
166 ULONG toc_type;
167 UBYTE first_track;
168 UBYTE last_track;
169 nrg_daoi_track_t tracks[];
170 } nrg_daoi_t;
172 typedef struct {
173 UBYTE reserved[10];
174 ULONG sector_size;
175 ULONG mode;
176 UQUAD index0; // pre gap
177 UQUAD index1; // start of track
178 UQUAD index2; // end of track + 1
179 } nrg_daox_track_t;
181 typedef struct {
182 ULONG chunk_size; // chunk size in little-endian format
183 ULONG reserved[3];
184 ULONG toc_type;
185 UBYTE first_track;
186 UBYTE last_track;
187 nrg_daox_track_t tracks[];
188 } nrg_daox_t;
190 typedef struct {
191 ULONG track_offset;
192 ULONG track_length;
193 } nrg_taoi_t;
195 typedef struct {
196 UQUAD track_offset;
197 UQUAD track_length;
198 } nrg_taox_t;
200 #pragma pack()
202 static APTR nrg_read_chunk (BPTR file, nrg_chunk_t *chunk, APTR *buf, LONG *buf_size);
204 APTR NRG_OpenImage (struct DiskImagePlugin *Self, APTR unit, BPTR file,
205 CONST_STRPTR name)
207 LONG done = FALSE;
208 LONG error = NO_ERROR;
209 LONG error_string = NO_ERROR_STRING;
210 IPTR error_args[4] = {0};
211 struct NRGImage *image = NULL;
212 nrg_footer_t footer;
213 QUAD offset;
214 nrg_chunk_t chunk;
215 APTR buf = NULL;
216 LONG buf_size = 0;
217 nrg_daoi_t *daoi;
218 nrg_daox_t *daox;
219 nrg_taoi_t *taoi;
220 nrg_taox_t *taox;
221 struct NRGTrack *track;
222 ULONG i;
224 image = AllocVec(sizeof(*image), MEMF_CLEAR);
225 if (!image) {
226 error = ERROR_NO_FREE_STORE;
227 goto error;
229 image->file = file;
231 if (!ChangeFilePosition(file, -(int)sizeof(footer), OFFSET_END) ||
232 Read(file, &footer, sizeof(footer)) != sizeof(footer))
234 error = IoErr();
235 goto error;
238 if (rbe32(&footer.v1.id) == ID_NERO) {
239 image->nrg_version = 1;
240 offset = rbe32(&footer.v1.offset);
241 } else if (rbe32(&footer.v2.id) == ID_NER5) {
242 image->nrg_version = 2;
243 offset = rbe64(&footer.v2.offset);
244 } else {
245 error = ERROR_OBJECT_WRONG_TYPE;
246 goto error;
248 if (!ChangeFilePosition(file, offset, OFFSET_BEGINNING)) {
249 error = IoErr();
250 goto error;
253 while (!done) {
254 if (Read(file, &chunk, sizeof(chunk)) != sizeof(chunk)) {
255 error = IoErr();
256 goto error;
258 wbe32(&chunk.id, chunk.id);
259 wbe32(&chunk.size, chunk.size);
260 switch (chunk.id) {
262 case ID_DAOI:
263 if (chunk.size < (sizeof(nrg_daoi_t) + sizeof(nrg_daoi_track_t))) {
264 error = ERROR_BAD_NUMBER;
265 goto error;
267 if (!(daoi = nrg_read_chunk(file, &chunk, &buf, &buf_size))) {
268 error = IoErr();
269 goto error;
271 track = (struct NRGTrack *)&image->tracks;
272 image->first_track = daoi->first_track;
273 image->last_track = daoi->last_track;
274 image->num_tracks = image->last_track - image->first_track + 1;
275 for (i = 0; i < image->num_tracks; i++) {
276 track->next = AllocVec(sizeof(*track), MEMF_ANY);
277 if (!(track = track->next)) {
278 error = ERROR_NO_FREE_STORE;
279 goto error;
281 track->next = NULL;
282 track->track_num = image->first_track+i;
283 track->sector_size = rbe32(&daoi->tracks[i].sector_size);
284 track->sync_size = 0;
285 switch (track->sector_size) {
286 case 2048:
287 track->audio = FALSE;
288 break;
289 case 2352:
290 track->audio = TRUE;
291 break;
292 default:
293 error = ERROR_BAD_NUMBER;
294 goto error;
296 track->offset = rbe32(&daoi->tracks[i].index1);
297 track->length = rbe32(&daoi->tracks[i].index2) - track->offset;
298 track->sectors = track->length / track->sector_size;
299 image->total_bytes += track->sectors << 11;
300 if (track->sector_size != 2048 && track->sector_size != 2352) {
301 error = ERROR_BAD_NUMBER;
302 goto error;
305 done = TRUE;
306 break;
308 case ID_DAOX:
309 if (chunk.size < (sizeof(nrg_daox_t) + sizeof(nrg_daox_track_t))) {
310 error = ERROR_BAD_NUMBER;
311 goto error;
313 if (!(daox = nrg_read_chunk(file, &chunk, &buf, &buf_size))) {
314 error = IoErr();
315 goto error;
317 track = (struct NRGTrack *)&image->tracks;
318 image->first_track = daox->first_track;
319 image->last_track = daox->last_track;
320 image->num_tracks = image->last_track - image->first_track + 1;
321 for (i = 0; i < image->num_tracks; i++) {
322 track->next = AllocVec(sizeof(*track), MEMF_ANY);
323 if (!(track = track->next)) {
324 error = ERROR_NO_FREE_STORE;
325 goto error;
327 track->next = NULL;
328 track->track_num = image->first_track+i;
329 track->sector_size = rbe32(&daox->tracks[i].sector_size);
330 track->sync_size = 0;
331 switch (track->sector_size) {
332 case 2048:
333 track->audio = FALSE;
334 break;
335 case 2352:
336 track->audio = TRUE;
337 break;
338 default:
339 error = ERROR_BAD_NUMBER;
340 goto error;
342 track->offset = rbe64(&daox->tracks[i].index1);
343 track->length = rbe64(&daox->tracks[i].index2) - track->offset;
344 track->sectors = track->length / track->sector_size;
345 image->total_bytes += track->sectors << 11;
346 if (track->sector_size != 2048 && track->sector_size != 2352) {
347 error = ERROR_BAD_NUMBER;
348 goto error;
351 done = TRUE;
352 break;
354 case ID_TAOI:
355 if (chunk.size < sizeof(nrg_taoi_t)) {
356 error = ERROR_BAD_NUMBER;
357 goto error;
359 if (!(taoi = nrg_read_chunk(file, &chunk, &buf, &buf_size))) {
360 error = IoErr();
361 goto error;
363 image->first_track = 1;
364 image->last_track = 1;
365 image->num_tracks = image->last_track - image->first_track + 1;
366 image->tracks = AllocVec(sizeof(*track), MEMF_ANY);
367 if (!(track = image->tracks)) {
368 error = ERROR_NO_FREE_STORE;
369 goto error;
371 track->next = NULL;
372 track->track_num = 1;
373 track->audio = FALSE;
374 track->sector_size = 2048;
375 track->sync_size = 0;
376 track->offset = rbe32(&taoi->track_offset);
377 track->length = rbe32(&taoi->track_length);
378 track->sectors = track->length / track->sector_size;
379 image->total_bytes += track->sectors << 11;
380 done = TRUE;
381 break;
383 case ID_TAOX:
384 if (chunk.size < sizeof(nrg_taox_t)) {
385 error = ERROR_BAD_NUMBER;
386 goto error;
388 if (!(taox = nrg_read_chunk(file, &chunk, &buf, &buf_size))) {
389 error = IoErr();
390 goto error;
392 image->first_track = 1;
393 image->last_track = 1;
394 image->num_tracks = image->last_track - image->first_track + 1;
395 image->tracks = AllocVec(sizeof(*track), MEMF_ANY);
396 if (!(track = image->tracks)) {
397 error = ERROR_NO_FREE_STORE;
398 goto error;
400 track->next = NULL;
401 track->track_num = 1;
402 track->audio = FALSE;
403 track->sector_size = 2048;
404 track->sync_size = 0;
405 track->offset = rbe64(&taox->track_offset);
406 track->length = rbe64(&taox->track_length);
407 track->sectors = track->length / track->sector_size;
408 image->total_bytes += track->sectors << 11;
409 done = TRUE;
410 break;
412 case ID_END:
413 error = ERROR_OBJECT_NOT_FOUND;
414 goto error;
416 default:
417 if (!ChangeFilePosition(file, chunk.size, OFFSET_CURRENT)) {
418 error = IoErr();
419 goto error;
421 break;
425 image->block_size = 2048;
426 image->total_blocks = image->total_bytes >> 11;
428 error:
429 FreeVec(buf);
430 if (!done) {
431 if (image) {
432 Plugin_CloseImage(Self, image);
433 image = NULL;
434 } else {
435 Close(file);
437 if (error == NO_ERROR) {
438 error = ERROR_OBJECT_WRONG_TYPE;
439 error_string = MSG_EOF;
441 IPlugin_SetDiskImageErrorA(unit, error, error_string, error_args);
443 return image;
446 static APTR nrg_read_chunk (BPTR file, nrg_chunk_t *chunk, APTR *buf, LONG *buf_size) {
447 if (*buf_size < chunk->size) {
448 FreeVec(*buf);
449 *buf = AllocVec(chunk->size, MEMF_ANY);
450 *buf_size = chunk->size;
451 if (!*buf) {
452 *buf_size = 0;
453 SetIoErr(ERROR_NO_FREE_STORE);
454 return NULL;
457 if (Read(file, *buf, chunk->size) != chunk->size) {
458 return NULL;
460 return *buf;
463 void NRG_CloseImage (struct DiskImagePlugin *Self, APTR image_ptr) {
464 struct NRGImage *image = image_ptr;
465 if (image) {
466 struct NRGTrack *track, *next_track;
467 track = image->tracks;
468 while (track) {
469 next_track = track->next;
470 FreeVec(track);
471 track = next_track;
473 FreeVec(image->out_buf);
474 Close(image->file);
475 FreeVec(image);
479 LONG NRG_Geometry (struct DiskImagePlugin *Self, APTR image_ptr, struct DriveGeometry *dg) {
480 struct NRGImage *image = image_ptr;
481 dg->dg_DeviceType = DG_CDROM;
482 dg->dg_SectorSize = image->block_size;
483 dg->dg_Heads =
484 dg->dg_TrackSectors =
485 dg->dg_CylSectors = 1;
486 dg->dg_Cylinders =
487 dg->dg_TotalSectors = image->total_blocks;
488 return IOERR_SUCCESS;
491 LONG NRG_Read (struct DiskImagePlugin *Self, APTR image_ptr, struct IOStdReq *io) {
492 struct NRGImage *image = image_ptr;
493 struct NRGTrack *track = image->tracks;
494 BPTR file = image->file;
495 UBYTE *buffer;
496 UQUAD offset;
497 ULONG size;
498 UQUAD read_offs, next_offs;
499 ULONG to_skip, to_read;
500 UQUAD read_pos;
501 ULONG read_size;
503 buffer = io->io_Data;
504 offset = ((UQUAD)io->io_Offset)|((UQUAD)io->io_Actual << 32);
505 size = io->io_Length;
506 io->io_Actual = 0;
508 if (offset & 0x7ff) return IOERR_BADADDRESS;
509 if (size & 0x7ff) return IOERR_BADLENGTH;
511 offset >>= 11;
512 size >>= 11;
513 if (offset >= image->total_blocks) {
514 return TDERR_SeekError;
516 read_offs = next_offs = 0;
517 for (;;) {
518 next_offs += track->sectors;
519 if (next_offs > offset) break;
520 read_offs = next_offs;
521 track = track->next;
522 if (!track) return TDERR_SeekError;
525 to_skip = offset - read_offs;
526 while (size) {
527 if (track->audio) {
528 return TDERR_NoSecHdr;
530 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
531 size -= to_read;
532 read_pos = track->offset + ((UQUAD)to_skip * track->sector_size);
533 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING)) {
534 return TDERR_SeekError;
536 if (track->sector_size == 2048) {
537 read_size = to_read << 11;
538 if (Read(file, buffer, read_size) != read_size) {
539 return IPlugin_DOS2IOErr(IoErr());
541 buffer += read_size;
542 io->io_Actual += read_size;
543 } else {
544 read_size = track->sector_size;
545 if (!(image->out_buf = ReAllocBuf(image->out_buf, &image->out_size, read_size))) {
546 return ERROR_NO_FREE_STORE;
548 while (to_read--) {
549 if (Read(file, image->out_buf, read_size) != read_size) {
550 return IPlugin_DOS2IOErr(IoErr());
552 CopyMem(image->out_buf + track->sync_size, buffer, 2048);
553 buffer += 2048;
554 io->io_Actual += 2048;
557 if (size) {
558 to_skip = 0;
559 track = track->next;
560 if (!track) return IOERR_BADLENGTH;
563 return IOERR_SUCCESS;
566 void NRG_GetCDTracks (struct DiskImagePlugin *Self, APTR image_ptr, struct CDTrack **tracks,
567 ULONG *num_tracks)
569 struct NRGImage *image = image_ptr;
570 *tracks = (struct CDTrack *)image->tracks;
571 *num_tracks = image->num_tracks;
574 LONG NRG_ReadCDDA (struct DiskImagePlugin *Self, APTR image_ptr, APTR buffer_ptr, ULONG offset,
575 ULONG size)
577 struct NRGImage *image = image_ptr;
578 UBYTE *buffer = buffer_ptr;
579 struct NRGTrack *track = image->tracks;
580 BPTR file = image->file;
581 UQUAD read_offs, next_offs;
582 ULONG to_skip, to_read;
583 UQUAD read_pos;
584 ULONG read_size;
585 ULONG bytes_read = 0;
587 if (offset >= image->total_blocks) {
588 return bytes_read;
590 read_offs = next_offs = 0;
591 for (;;) {
592 next_offs += track->sectors;
593 if (next_offs > offset) break;
594 read_offs = next_offs;
595 track = track->next;
596 if (!track) return bytes_read;
599 to_skip = offset - read_offs;
600 while (size) {
601 if (!track->audio || track->sector_size != 2352) {
602 return bytes_read;
604 to_read = max((LONG)min(size, track->sectors - to_skip), 0);
605 size -= to_read;
606 read_pos = track->offset + ((UQUAD)to_skip * 2352ULL);
607 read_size = to_read * 2352;
608 if (!ChangeFilePosition(file, read_pos, OFFSET_BEGINNING) ||
609 Read(file, buffer, read_size) != read_size)
611 return bytes_read;
613 buffer += read_size;
614 bytes_read += read_size;
615 to_skip = 0;
616 track = track->next;
617 if (!track) return bytes_read;
619 return bytes_read;