1 /* Copyright 2007-2012 Fredrik Wikstrom. All rights reserved.
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
7 ** 1. Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
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>
37 # define Uncompress uncompress
38 struct Library
*BZ2Base
;
39 struct Library
*ZBase
;
40 struct Library
*ExpatBase
;
42 # include <libraries/expat.h>
43 # include <libraries/z.h>
44 # include <libraries/bz2.h>
45 # include <proto/expat.h>
47 # include <proto/bz2.h>
53 #include "device_locale.h"
55 #include <SDI_compiler.h>
56 #include "rev/diskimage.device_rev.h"
60 extern struct DiskImagePlugin dmg_plugin
;
62 PLUGIN_TABLE(&dmg_plugin
)
72 struct MinNode
*pnode
;
86 UBYTE
*in_buf
, *out_buf
;
87 ULONG in_size
, out_size
;
88 struct DMGPart
*part_in_buf
;
92 struct Library
*expatbase
;
93 struct Library
*zbase
;
94 struct Library
*bz2base
;
97 BOOL
DMG_Init (struct DiskImagePlugin
*Self
, const struct PluginData
*data
);
98 BOOL
DMG_CheckImage (struct DiskImagePlugin
*Self
, BPTR file
, CONST_STRPTR name
, QUAD file_size
,
99 const UBYTE
*test
, LONG testsize
);
100 APTR
DMG_OpenImage (struct DiskImagePlugin
*Self
, APTR unit
, BPTR file
, CONST_STRPTR name
);
101 void DMG_CloseImage (struct DiskImagePlugin
*Self
, APTR image_ptr
);
102 LONG
DMG_Geometry (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct DriveGeometry
*dg
);
103 LONG
DMG_Read (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct IOStdReq
*io
);
105 struct DiskImagePlugin dmg_plugin
= {
106 PLUGIN_NODE(0, "DMG"),
107 PLUGIN_FLAG_FOOTER
|PLUGIN_FLAG_M68K
,
125 struct Library
*SysBase
;
126 struct Library
*DOSBase
;
127 static struct DIPluginIFace
*IPlugin
;
128 #define ZBase image->zbase
129 #define BZ2Base image->bz2base
131 BOOL
DMG_Init (struct DiskImagePlugin
*Self
, const struct PluginData
*data
) {
132 SysBase
= data
->SysBase
;
133 DOSBase
= data
->DOSBase
;
134 IPlugin
= data
->IPlugin
;
138 #define ID_koly MAKE_ID('k','o','l','y')
139 #define ID_mish MAKE_ID('m','i','s','h')
141 BOOL
DMG_CheckImage (struct DiskImagePlugin
*Self
, BPTR file
, CONST_STRPTR name
, QUAD file_size
,
142 const UBYTE
*test
, LONG testsize
)
144 return testsize
>= 512 && rbe32(&test
[testsize
-512]) == ID_koly
;
154 UQUAD running_data_fork_offset
;
155 UQUAD data_fork_offset
;
156 UQUAD data_fork_length
;
157 UQUAD rsrc_fork_offset
;
158 UQUAD rsrc_fork_length
;
159 ULONG segment_number
;
165 ULONG data_fork_checksum_type
;
167 ULONG data_fork_checksum
;
172 ULONG master_checksum_type
;
174 ULONG master_checksum
;
187 ULONG decompressed_buffer_size
;
188 ULONG blocks_descriptor
;
194 ULONG blocks_run_count
;
197 #define PT_ZERO 0x00000000
198 #define PT_COPY 0x00000001
199 #define PT_IGNORE 0x00000002
200 #define PT_COMMENT 0x7ffffffe
201 #define PT_ADC 0x80000004
202 #define PT_ZLIB 0x80000005
203 #define PT_BZLIB 0x80000006
204 #define PT_END 0xffffffff
209 struct Library
*expatbase
;
211 struct DMGImage
*image
;
212 LONG current_tag_depth
;
221 static LONG
add_to_plist (struct DMGImage
*image
, CONST_STRPTR src
, LONG len
);
223 static void xml_start_element_handler (void *user_data
,
224 const char *name
, const char **attrs
);
225 static void xml_end_element_handler (void *user_data
,
227 static void xml_character_data_handler (void *user_data
,
228 const char *s
, int len
);
230 APTR
DMG_OpenImage (struct DiskImagePlugin
*Self
, APTR unit
, BPTR file
,
234 LONG error
= NO_ERROR
;
235 LONG error_string
= NO_ERROR_STRING
;
236 IPTR error_args
[4] = {0};
237 dmg_koly_t
*koly
= NULL
;
239 struct DMGImage
*image
= NULL
;
240 struct MinNode
*pnode
;
241 struct DMGPart
*part
;
244 struct DMGHash
*hash
;
245 UQUAD offset
, next_offset
;
248 koly
= AllocVec(sizeof(*koly
), MEMF_ANY
);
250 error
= ERROR_NO_FREE_STORE
;
254 if (!ChangeFilePosition(file
, -(int)sizeof(*koly
), OFFSET_END
) ||
255 Read(file
, koly
, sizeof(*koly
)) != sizeof(*koly
))
261 if (rbe32(&koly
->id
) != ID_koly
) {
262 error
= ERROR_OBJECT_WRONG_TYPE
;
266 image
= AllocVec(sizeof(*image
), MEMF_CLEAR
);
268 error
= ERROR_NO_FREE_STORE
;
273 image
->plist
= AllocVec(sizeof(struct MinList
), MEMF_ANY
);
275 error
= ERROR_NO_FREE_STORE
;
278 image
->plist
->lh_Head
= (struct Node
*)&image
->plist
->lh_Tail
;
279 image
->plist
->lh_Tail
= NULL
;
280 image
->plist
->lh_TailPred
= (struct Node
*)&image
->plist
->lh_Head
;
282 if (koly
->xml_offset
&& koly
->xml_length
) {
286 XML_Parser_Data parser_data
;
288 struct Library __unused
*ExpatBase
;
290 xml_offset
= rbe64(&koly
->xml_offset
);
291 xml_length
= rbe64(&koly
->xml_length
);
293 data
= AllocVec(xml_length
, MEMF_ANY
);
295 error
= ERROR_NO_FREE_STORE
;
299 if (!ChangeFilePosition(file
, xml_offset
, OFFSET_BEGINNING
) ||
300 Read(file
, data
, xml_length
) != xml_length
)
307 image
->expatbase
= OpenLibrary("expat_au.library", 1);
309 image
->expatbase
= OpenLibrary("expat.library", 4);
311 if (!image
->expatbase
) {
312 error
= ERROR_OBJECT_NOT_FOUND
;
313 error_string
= MSG_REQVER
;
314 error_args
[0] = (IPTR
)"expat.library";
319 ExpatBase
= image
->expatbase
;
321 parser
= XML_ParserCreate(NULL
);
323 error
= ERROR_NO_FREE_STORE
;
327 memset(&parser_data
, 0, sizeof(parser_data
));
328 parser_data
.parser
= parser
;
329 parser_data
.image
= image
;
331 XML_SetUserData(parser
, &parser_data
);
332 XML_SetElementHandler(parser
,
333 xml_start_element_handler
,
334 xml_end_element_handler
);
335 XML_SetCharacterDataHandler(parser
,
336 xml_character_data_handler
);
337 if (!XML_Parse(parser
, data
, xml_length
, TRUE
)) {
338 xml_error
= XML_GetErrorCode(parser
);
340 xml_error
= XML_ERROR_NONE
;
342 XML_ParserFree(parser
);
343 if (parser_data
.error
) {
344 error
= parser_data
.error
;
345 error_string
= parser_data
.error_string
;
346 CopyMem(parser_data
.error_args
, error_args
, sizeof(error_args
));
349 if (xml_error
!= XML_ERROR_NONE
) {
350 if (xml_error
== XML_ERROR_NO_MEMORY
) {
351 error
= ERROR_NO_FREE_STORE
;
353 error
= ERROR_OBJECT_WRONG_TYPE
;
354 error_string
= MSG_EXPATERR
;
359 CloseLibrary(image
->expatbase
);
360 image
->expatbase
= NULL
;
361 } else if (koly
->rsrc_fork_offset
&& koly
->rsrc_fork_length
) {
369 rsrc_offset
= rbe64(&koly
->rsrc_fork_offset
);
370 rsrc_length
= rbe64(&koly
->rsrc_fork_length
);
372 data
= AllocVec(rsrc_length
, MEMF_ANY
);
374 error
= ERROR_NO_FREE_STORE
;
378 if (!ChangeFilePosition(file
, rsrc_offset
, OFFSET_BEGINNING
) ||
379 Read(file
, data
, rsrc_length
) != rsrc_length
)
386 len
= rsrc_length
- 0x104;
387 while (len
>= 0xcc) {
388 mish
= (dmg_mish_t
*)src
;
389 if (rbe32(&mish
->id
) != ID_mish
) {
390 error
= ERROR_OBJECT_WRONG_TYPE
;
394 num_parts
= rbe32(&mish
->blocks_run_count
);
395 error
= add_to_plist(image
, src
+ 0xcc, 0x28*num_parts
);
396 if (error
!= NO_ERROR
) goto error
;
398 src
+= (0xcc + (0x28*num_parts
) + 0x04);
399 len
-= (0xcc + (0x28*num_parts
) + 0x04);
402 error
= ERROR_NOT_IMPLEMENTED
;
406 if (image
->uses_zlib
) {
408 image
->zbase
= OpenLibrary("z_au.library", 1);
409 ZBase
= image
->zbase
;
411 image
->zbase
= OpenLibrary("z.library", 1);
413 if (!image
->zbase
|| !CheckLib(image
->zbase
, 1, 6)) {
414 error
= ERROR_OBJECT_NOT_FOUND
;
415 error_string
= MSG_REQVER
;
416 error_args
[0] = (IPTR
)"z.library";
423 if (image
->uses_bzlib
) {
425 image
->bz2base
= OpenLibrary("bz2_au.library", 1);
426 BZ2Base
= image
->bz2base
;
428 image
->bz2base
= OpenLibrary("bz2.library", 1);
430 if (!image
->bz2base
) {
431 error
= ERROR_OBJECT_NOT_FOUND
;
432 error_string
= MSG_REQVER
;
433 error_args
[0] = (IPTR
)"bz2.library";
440 if (image
->total_bytes
== 0) {
444 image
->block_size
= 512;
445 image
->total_blocks
= image
->total_bytes
/ image
->block_size
;
447 hash_size
= ((image
->total_bytes
>> HASH_FUNC
) + 1) * sizeof(*hash
);
448 image
->hash
= hash
= AllocVec(hash_size
, MEMF_ANY
);
450 error
= ERROR_NO_FREE_STORE
;
454 offset
= next_offset
= hash_offset
= 0;
455 pnode
= (struct MinNode
*)image
->plist
->lh_Head
;
456 while (pnode
->mln_Succ
) {
457 part
= (struct DMGPart
*)(pnode
+ 1);
467 next_offset
+= part
->out_size
;
468 while (next_offset
> hash_offset
) {
471 hash
->offset
= offset
;
473 hash_offset
+= (1 << HASH_FUNC
);
475 offset
= next_offset
;
479 } while (type
!= PT_END
);
480 pnode
= pnode
->mln_Succ
;
490 Plugin_CloseImage(Self
, image
);
495 if (error
== NO_ERROR
) {
496 error
= ERROR_OBJECT_WRONG_TYPE
;
497 error_string
= MSG_EOF
;
499 IPlugin_SetDiskImageErrorA(unit
, error
, error_string
, error_args
);
504 static LONG
add_to_plist (struct DMGImage
*image
, CONST_STRPTR src
, LONG len
) {
505 struct MinNode
*pnode
;
506 LONG num_parts
= len
/ 0x28;
507 struct DMGPart
*part
;
510 if (num_parts
<= 0) {
514 pnode
= AllocVec(sizeof(*pnode
) + (num_parts
+1)*sizeof(*part
), MEMF_CLEAR
);
516 return ERROR_NO_FREE_STORE
;
519 AddTail(image
->plist
, (struct Node
*)pnode
);
521 part
= (struct DMGPart
*)(pnode
+ 1);
523 part
->type
= type
= rbe32(src
);
524 part
->in_offs
= rbe32(src
+28);
525 part
->in_size
= rbe32(src
+36);
526 part
->out_size
= rbe32(src
+20) << 9;
531 image
->total_bytes
+= part
->out_size
;
536 image
->uses_adc
= TRUE
;
537 image
->total_bytes
+= part
->out_size
;
540 image
->uses_zlib
= TRUE
;
541 image
->total_bytes
+= part
->out_size
;
544 image
->uses_bzlib
= TRUE
;
545 image
->total_bytes
+= part
->out_size
;
550 return ERROR_NOT_IMPLEMENTED
;
555 } while (type
!= PT_END
&& --num_parts
);
564 static const char *XML_GetAttrVal (const char *attr
, const char **attrs
, const char *defVal
) {
566 if (!strcmp(*attrs
, attr
)) {
574 static void xml_start_element_handler (void *user_data
,
575 const char *name
, const char **attrs
)
577 XML_Parser_Data
*data
= user_data
;
578 struct Library __unused
*ExpatBase
= data
->expatbase
;
579 if (data
->error
) return;
581 if (data
->current_tag_depth
== 0) {
582 if (strcmp(name
, "plist") ||
583 strcmp(XML_GetAttrVal("version", attrs
, ""), "1.0"))
585 data
->error
= ERROR_OBJECT_WRONG_TYPE
;
586 XML_StopParser(data
->parser
, XML_TRUE
);
590 if (!strcmp(name
, "data")) {
591 if (data
->is_in_data_tag
) {
592 data
->error
= ERROR_TOO_MANY_LEVELS
;
593 XML_StopParser(data
->parser
, XML_TRUE
);
596 data
->is_in_data_tag
= TRUE
;
600 data
->current_tag_depth
++;
603 static void xml_end_element_handler (void *user_data
,
606 XML_Parser_Data
*data
= user_data
;
607 struct Library __unused
*ExpatBase
= data
->expatbase
;
608 if (data
->error
) return;
610 data
->current_tag_depth
--;
611 if (data
->current_tag_depth
== 0) {
612 if (strcmp(name
, "plist")) {
613 data
->error
= ERROR_OBJECT_WRONG_TYPE
;
614 XML_StopParser(data
->parser
, XML_TRUE
);
618 if (!strcmp(name
, "data")) {
619 if (!data
->is_in_data_tag
) {
620 data
->error
= ERROR_TOO_MANY_LEVELS
;
621 XML_StopParser(data
->parser
, XML_TRUE
);
624 data
->is_in_data_tag
= FALSE
;
626 struct DMGImage
*image
= data
->image
;
629 cleanup_base64(data
->data
);
630 len
= decode_base64(data
->data
, data
->data
);
632 data
->error
= add_to_plist(image
, data
->data
+ 0xcc, len
- 0xcc);
633 if (data
->error
!= NO_ERROR
) {
634 XML_StopParser(data
->parser
, XML_TRUE
);
642 static void xml_character_data_handler (void *user_data
,
643 const char *s
, int len
)
645 XML_Parser_Data
*data
= user_data
;
646 struct Library __unused
*ExpatBase
= data
->expatbase
;
647 if (data
->error
) return;
649 if (data
->is_in_data_tag
&& len
> 0) {
650 if (len
<= (data
->size
- data
->len
)) {
651 CopyMem(s
, data
->data
+ data
->len
, len
);
653 data
->data
[data
->len
] = 0;
656 new_data
= AllocVec(data
->len
+ len
+ 1, MEMF_ANY
);
658 data
->error
= ERROR_NO_FREE_STORE
;
659 XML_StopParser(data
->parser
, XML_TRUE
);
663 CopyMem(data
->data
, new_data
, data
->len
);
666 CopyMem(s
, new_data
+ data
->len
, len
);
667 data
->data
= new_data
;
669 data
->size
= data
->len
;
670 data
->data
[data
->len
] = 0;
675 void DMG_CloseImage (struct DiskImagePlugin
*Self
, APTR image_ptr
) {
676 struct DMGImage
*image
= image_ptr
;
678 if (image
->bz2base
) CloseLibrary(image
->bz2base
);
679 if (image
->zbase
) CloseLibrary(image
->zbase
);
680 if (image
->expatbase
) CloseLibrary(image
->expatbase
);
681 FreeVec(image
->hash
);
684 while ((pnode
= RemHead(image
->plist
))) {
687 FreeVec(image
->plist
);
689 FreeVec(image
->in_buf
);
690 FreeVec(image
->out_buf
);
696 LONG
DMG_Geometry (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct DriveGeometry
*dg
) {
697 struct DMGImage
*image
= image_ptr
;
698 dg
->dg_SectorSize
= image
->block_size
;
700 dg
->dg_TrackSectors
=
701 dg
->dg_CylSectors
= 1;
703 dg
->dg_TotalSectors
= image
->total_blocks
;
704 return IOERR_SUCCESS
;
707 LONG
DMG_Read (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct IOStdReq
*io
) {
708 struct DMGImage
*image
= image_ptr
;
709 BPTR file
= image
->file
;
713 struct DMGHash
*hash
;
714 struct MinNode
*pnode
;
715 struct DMGPart
*part
;
716 UQUAD read_offs
, next_offs
;
717 ULONG to_skip
, to_read
;
719 buffer
= io
->io_Data
;
720 offset
= ((UQUAD
)io
->io_Offset
)|((UQUAD
)io
->io_Actual
<< 32);
721 size
= io
->io_Length
;
724 if (offset
>= image
->total_bytes
) {
725 return TDERR_SeekError
;
728 hash
= &image
->hash
[offset
>> HASH_FUNC
];
731 read_offs
= next_offs
= hash
->offset
;
733 switch (part
->type
) {
740 next_offs
+= part
->out_size
;
744 pnode
= (struct MinNode
*)GetSucc((struct Node
*)pnode
);
746 return TDERR_SeekError
;
748 part
= (struct DMGPart
*)(pnode
+ 1);
754 if (next_offs
> offset
) break;
755 read_offs
= next_offs
;
759 to_skip
= offset
- read_offs
;
761 to_read
= max((LONG
)min(size
, part
->out_size
- to_skip
), 0);
763 switch (part
->type
) {
766 if (!part
->out_size
) break;
767 if (image
->part_in_buf
!= part
) {
768 image
->part_in_buf
= NULL
;
770 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
771 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
776 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
777 return TDERR_SeekError
;
779 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
782 return error
? IPlugin_DOS2IOErr(error
) : IOERR_BADLENGTH
;
785 adc_decompress(image
->out_buf
, part
->out_size
, image
->in_buf
, part
->in_size
);
787 image
->part_in_buf
= part
;
789 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
794 if (!part
->out_size
) break;
795 if (image
->part_in_buf
!= part
) {
798 image
->part_in_buf
= NULL
;
800 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
801 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
806 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
807 return TDERR_SeekError
;
809 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
810 return IPlugin_DOS2IOErr(IoErr());
813 out_len
= part
->out_size
;
814 if ((status
= Uncompress(image
->out_buf
, &out_len
, image
->in_buf
,
815 part
->in_size
)) != Z_OK
)
817 return TDERR_NotSpecified
;
820 image
->part_in_buf
= part
;
822 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
827 if (!part
->out_size
) break;
828 if (image
->part_in_buf
!= part
) {
830 unsigned int out_len
;
831 image
->part_in_buf
= NULL
;
833 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
834 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
839 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
840 return TDERR_SeekError
;
842 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
843 return IPlugin_DOS2IOErr(IoErr());
846 out_len
= part
->out_size
;
847 if ((status
= BZ2_bzBuffToBuffDecompress(image
->out_buf
, &out_len
,
848 image
->in_buf
, part
->in_size
, 0, 0)) != BZ_OK
)
850 return TDERR_NotSpecified
;
853 image
->part_in_buf
= part
;
855 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
860 if (!part
->out_size
) break;
861 if (!ChangeFilePosition(file
, part
->in_offs
+ to_skip
, OFFSET_BEGINNING
)) {
862 return TDERR_SeekError
;
864 if (Read(file
, buffer
, to_read
) != to_read
) {
867 return error
? IPlugin_DOS2IOErr(error
) : IOERR_BADLENGTH
;
874 memset(buffer
, 0, to_read
);
880 pnode
= (struct MinNode
*)GetSucc((struct Node
*)pnode
);
881 if (!pnode
) return IOERR_BADLENGTH
;
882 part
= (struct DMGPart
*)(pnode
+ 1);
894 io
->io_Actual
+= to_read
;
896 return IOERR_SUCCESS
;