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
39 # include <libraries/expat.h>
40 # include <libraries/z.h>
41 # include <libraries/bz2.h>
42 # include <proto/expat.h>
44 # include <proto/bz2.h>
50 #include "device_locale.h"
52 #include <SDI_compiler.h>
53 #include "rev/diskimage.device_rev.h"
57 extern struct DiskImagePlugin dmg_plugin
;
59 PLUGIN_TABLE(&dmg_plugin
)
69 struct MinNode
*pnode
;
83 UBYTE
*in_buf
, *out_buf
;
84 ULONG in_size
, out_size
;
85 struct DMGPart
*part_in_buf
;
89 struct Library
*expatbase
;
90 struct Library
*zbase
;
91 struct Library
*bz2base
;
94 BOOL
DMG_Init (struct DiskImagePlugin
*Self
, const struct PluginData
*data
);
95 BOOL
DMG_CheckImage (struct DiskImagePlugin
*Self
, BPTR file
, CONST_STRPTR name
, QUAD file_size
,
96 const UBYTE
*test
, LONG testsize
);
97 APTR
DMG_OpenImage (struct DiskImagePlugin
*Self
, APTR unit
, BPTR file
, CONST_STRPTR name
);
98 void DMG_CloseImage (struct DiskImagePlugin
*Self
, APTR image_ptr
);
99 LONG
DMG_Geometry (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct DriveGeometry
*dg
);
100 LONG
DMG_Read (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct IOStdReq
*io
);
102 struct DiskImagePlugin dmg_plugin
= {
103 PLUGIN_NODE(0, "DMG"),
104 PLUGIN_FLAG_FOOTER
|PLUGIN_FLAG_M68K
,
122 struct Library
*SysBase
;
123 struct Library
*DOSBase
;
124 static struct DIPluginIFace
*IPlugin
;
126 #define ZBase image->zbase
127 #define BZ2Base image->bz2base
129 struct Library
*BZ2Base
;
130 struct Library
*Z1Base
;
131 struct Library
*ExpatBase
;
134 BOOL
DMG_Init (struct DiskImagePlugin
*Self
, const struct PluginData
*data
) {
135 SysBase
= data
->SysBase
;
136 DOSBase
= data
->DOSBase
;
137 IPlugin
= data
->IPlugin
;
141 #define ID_koly MAKE_ID('k','o','l','y')
142 #define ID_mish MAKE_ID('m','i','s','h')
144 BOOL
DMG_CheckImage (struct DiskImagePlugin
*Self
, BPTR file
, CONST_STRPTR name
, QUAD file_size
,
145 const UBYTE
*test
, LONG testsize
)
147 return testsize
>= 512 && rbe32(&test
[testsize
-512]) == ID_koly
;
157 UQUAD running_data_fork_offset
;
158 UQUAD data_fork_offset
;
159 UQUAD data_fork_length
;
160 UQUAD rsrc_fork_offset
;
161 UQUAD rsrc_fork_length
;
162 ULONG segment_number
;
168 ULONG data_fork_checksum_type
;
170 ULONG data_fork_checksum
;
175 ULONG master_checksum_type
;
177 ULONG master_checksum
;
190 ULONG decompressed_buffer_size
;
191 ULONG blocks_descriptor
;
197 ULONG blocks_run_count
;
200 #define PT_ZERO 0x00000000
201 #define PT_COPY 0x00000001
202 #define PT_IGNORE 0x00000002
203 #define PT_COMMENT 0x7ffffffe
204 #define PT_ADC 0x80000004
205 #define PT_ZLIB 0x80000005
206 #define PT_BZLIB 0x80000006
207 #define PT_END 0xffffffff
212 struct Library
*expatbase
;
214 struct DMGImage
*image
;
215 LONG current_tag_depth
;
224 static LONG
add_to_plist (struct DMGImage
*image
, CONST_STRPTR src
, LONG len
);
226 static void xml_start_element_handler (void *user_data
,
227 const char *name
, const char **attrs
);
228 static void xml_end_element_handler (void *user_data
,
230 static void xml_character_data_handler (void *user_data
,
231 const char *s
, int len
);
233 APTR
DMG_OpenImage (struct DiskImagePlugin
*Self
, APTR unit
, BPTR file
,
237 LONG error
= NO_ERROR
;
238 LONG error_string
= NO_ERROR_STRING
;
239 IPTR error_args
[4] = {0};
240 dmg_koly_t
*koly
= NULL
;
242 struct DMGImage
*image
= NULL
;
243 struct MinNode
*pnode
;
244 struct DMGPart
*part
;
247 struct DMGHash
*hash
;
248 UQUAD offset
, next_offset
;
251 koly
= AllocVec(sizeof(*koly
), MEMF_ANY
);
253 error
= ERROR_NO_FREE_STORE
;
257 if (!ChangeFilePosition(file
, -(int)sizeof(*koly
), OFFSET_END
) ||
258 Read(file
, koly
, sizeof(*koly
)) != sizeof(*koly
))
264 if (rbe32(&koly
->id
) != ID_koly
) {
265 error
= ERROR_OBJECT_WRONG_TYPE
;
269 image
= AllocVec(sizeof(*image
), MEMF_CLEAR
);
271 error
= ERROR_NO_FREE_STORE
;
276 image
->plist
= AllocVec(sizeof(struct MinList
), MEMF_ANY
);
278 error
= ERROR_NO_FREE_STORE
;
281 image
->plist
->lh_Head
= (struct Node
*)&image
->plist
->lh_Tail
;
282 image
->plist
->lh_Tail
= NULL
;
283 image
->plist
->lh_TailPred
= (struct Node
*)&image
->plist
->lh_Head
;
285 if (koly
->xml_offset
&& koly
->xml_length
) {
289 XML_Parser_Data parser_data
;
292 struct Library
*ExpatBase
;
295 xml_offset
= rbe64(&koly
->xml_offset
);
296 xml_length
= rbe64(&koly
->xml_length
);
298 data
= AllocVec(xml_length
, MEMF_ANY
);
300 error
= ERROR_NO_FREE_STORE
;
304 if (!ChangeFilePosition(file
, xml_offset
, OFFSET_BEGINNING
) ||
305 Read(file
, data
, xml_length
) != xml_length
)
311 image
->expatbase
= OpenLibrary("expat.library", 4);
312 if (!image
->expatbase
) {
313 error
= ERROR_OBJECT_NOT_FOUND
;
314 error_string
= MSG_REQVER
;
315 error_args
[0] = (IPTR
)"expat.library";
320 ExpatBase
= image
->expatbase
;
322 parser
= XML_ParserCreate(NULL
);
324 error
= ERROR_NO_FREE_STORE
;
328 memset(&parser_data
, 0, sizeof(parser_data
));
329 parser_data
.parser
= parser
;
330 parser_data
.image
= image
;
332 XML_SetUserData(parser
, &parser_data
);
333 XML_SetElementHandler(parser
,
334 xml_start_element_handler
,
335 xml_end_element_handler
);
336 XML_SetCharacterDataHandler(parser
,
337 xml_character_data_handler
);
338 if (!XML_Parse(parser
, data
, xml_length
, TRUE
)) {
339 xml_error
= XML_GetErrorCode(parser
);
341 xml_error
= XML_ERROR_NONE
;
343 XML_ParserFree(parser
);
344 if (parser_data
.error
) {
345 error
= parser_data
.error
;
346 error_string
= parser_data
.error_string
;
347 CopyMem(parser_data
.error_args
, error_args
, sizeof(error_args
));
350 if (xml_error
!= XML_ERROR_NONE
) {
351 if (xml_error
== XML_ERROR_NO_MEMORY
) {
352 error
= ERROR_NO_FREE_STORE
;
354 error
= ERROR_OBJECT_WRONG_TYPE
;
355 error_string
= MSG_EXPATERR
;
360 CloseLibrary(image
->expatbase
);
361 image
->expatbase
= NULL
;
362 } else if (koly
->rsrc_fork_offset
&& koly
->rsrc_fork_length
) {
370 rsrc_offset
= rbe64(&koly
->rsrc_fork_offset
);
371 rsrc_length
= rbe64(&koly
->rsrc_fork_length
);
373 data
= AllocVec(rsrc_length
, MEMF_ANY
);
375 error
= ERROR_NO_FREE_STORE
;
379 if (!ChangeFilePosition(file
, rsrc_offset
, OFFSET_BEGINNING
) ||
380 Read(file
, data
, rsrc_length
) != rsrc_length
)
387 len
= rsrc_length
- 0x104;
388 while (len
>= 0xcc) {
389 mish
= (dmg_mish_t
*)src
;
390 if (rbe32(&mish
->id
) != ID_mish
) {
391 error
= ERROR_OBJECT_WRONG_TYPE
;
395 num_parts
= rbe32(&mish
->blocks_run_count
);
396 error
= add_to_plist(image
, src
+ 0xcc, 0x28*num_parts
);
397 if (error
!= NO_ERROR
) goto error
;
399 src
+= (0xcc + (0x28*num_parts
) + 0x04);
400 len
-= (0xcc + (0x28*num_parts
) + 0x04);
403 error
= ERROR_NOT_IMPLEMENTED
;
407 if (image
->uses_zlib
) {
409 image
->zbase
= OpenLibrary("z1.library", 1);
410 Z1Base
= image
->zbase
;
412 image
->zbase
= OpenLibrary("z.library", 1);
414 if (!image
->zbase
|| !CheckLib(image
->zbase
, 1, 6)) {
415 error
= ERROR_OBJECT_NOT_FOUND
;
416 error_string
= MSG_REQVER
;
417 error_args
[0] = (IPTR
)"z.library";
424 if (image
->uses_bzlib
) {
425 image
->bz2base
= OpenLibrary("bz2.library", 1);
427 BZ2Base
= image
->bz2base
;
429 if (!image
->bz2base
) {
430 error
= ERROR_OBJECT_NOT_FOUND
;
431 error_string
= MSG_REQVER
;
432 error_args
[0] = (IPTR
)"bz2.library";
439 if (image
->total_bytes
== 0) {
443 image
->block_size
= 512;
444 image
->total_blocks
= image
->total_bytes
/ image
->block_size
;
446 hash_size
= ((image
->total_bytes
>> HASH_FUNC
) + 1) * sizeof(*hash
);
447 image
->hash
= hash
= AllocVec(hash_size
, MEMF_ANY
);
449 error
= ERROR_NO_FREE_STORE
;
453 offset
= next_offset
= hash_offset
= 0;
454 pnode
= (struct MinNode
*)image
->plist
->lh_Head
;
455 while (pnode
->mln_Succ
) {
456 part
= (struct DMGPart
*)(pnode
+ 1);
466 next_offset
+= part
->out_size
;
467 while (next_offset
> hash_offset
) {
470 hash
->offset
= offset
;
472 hash_offset
+= (1 << HASH_FUNC
);
474 offset
= next_offset
;
478 } while (type
!= PT_END
);
479 pnode
= pnode
->mln_Succ
;
489 Plugin_CloseImage(Self
, image
);
494 if (error
== NO_ERROR
) {
495 error
= ERROR_OBJECT_WRONG_TYPE
;
496 error_string
= MSG_EOF
;
498 IPlugin_SetDiskImageErrorA(unit
, error
, error_string
, error_args
);
503 static LONG
add_to_plist (struct DMGImage
*image
, CONST_STRPTR src
, LONG len
) {
504 struct MinNode
*pnode
;
505 LONG num_parts
= len
/ 0x28;
506 struct DMGPart
*part
;
509 if (num_parts
<= 0) {
513 pnode
= AllocVec(sizeof(*pnode
) + (num_parts
+1)*sizeof(*part
), MEMF_CLEAR
);
515 return ERROR_NO_FREE_STORE
;
518 AddTail(image
->plist
, (struct Node
*)pnode
);
520 part
= (struct DMGPart
*)(pnode
+ 1);
522 part
->type
= type
= rbe32(src
);
523 part
->in_offs
= rbe32(src
+28);
524 part
->in_size
= rbe32(src
+36);
525 part
->out_size
= rbe32(src
+20) << 9;
530 image
->total_bytes
+= part
->out_size
;
535 image
->uses_adc
= TRUE
;
536 image
->total_bytes
+= part
->out_size
;
539 image
->uses_zlib
= TRUE
;
540 image
->total_bytes
+= part
->out_size
;
543 image
->uses_bzlib
= TRUE
;
544 image
->total_bytes
+= part
->out_size
;
549 return ERROR_NOT_IMPLEMENTED
;
554 } while (type
!= PT_END
&& --num_parts
);
563 static const char *XML_GetAttrVal (const char *attr
, const char **attrs
, const char *defVal
) {
565 if (!strcmp(*attrs
, attr
)) {
573 static void xml_start_element_handler (void *user_data
,
574 const char *name
, const char **attrs
)
576 XML_Parser_Data
*data
= user_data
;
578 struct Library
*ExpatBase
= data
->expatbase
;
580 if (data
->error
) return;
582 if (data
->current_tag_depth
== 0) {
583 if (strcmp(name
, "plist") ||
584 strcmp(XML_GetAttrVal("version", attrs
, ""), "1.0"))
586 data
->error
= ERROR_OBJECT_WRONG_TYPE
;
587 XML_StopParser(data
->parser
, XML_TRUE
);
591 if (!strcmp(name
, "data")) {
592 if (data
->is_in_data_tag
) {
593 data
->error
= ERROR_TOO_MANY_LEVELS
;
594 XML_StopParser(data
->parser
, XML_TRUE
);
597 data
->is_in_data_tag
= TRUE
;
601 data
->current_tag_depth
++;
604 static void xml_end_element_handler (void *user_data
,
607 XML_Parser_Data
*data
= user_data
;
609 struct Library
*ExpatBase
= data
->expatbase
;
611 if (data
->error
) return;
613 data
->current_tag_depth
--;
614 if (data
->current_tag_depth
== 0) {
615 if (strcmp(name
, "plist")) {
616 data
->error
= ERROR_OBJECT_WRONG_TYPE
;
617 XML_StopParser(data
->parser
, XML_TRUE
);
621 if (!strcmp(name
, "data")) {
622 if (!data
->is_in_data_tag
) {
623 data
->error
= ERROR_TOO_MANY_LEVELS
;
624 XML_StopParser(data
->parser
, XML_TRUE
);
627 data
->is_in_data_tag
= FALSE
;
629 struct DMGImage
*image
= data
->image
;
632 cleanup_base64(data
->data
);
633 len
= decode_base64(data
->data
, data
->data
);
635 data
->error
= add_to_plist(image
, data
->data
+ 0xcc, len
- 0xcc);
636 if (data
->error
!= NO_ERROR
) {
637 XML_StopParser(data
->parser
, XML_TRUE
);
645 static void xml_character_data_handler (void *user_data
,
646 const char *s
, int len
)
648 XML_Parser_Data
*data
= user_data
;
650 struct Library
*ExpatBase
= data
->expatbase
;
652 if (data
->error
) return;
654 if (data
->is_in_data_tag
&& len
> 0) {
655 if (len
<= (data
->size
- data
->len
)) {
656 CopyMem(s
, data
->data
+ data
->len
, len
);
658 data
->data
[data
->len
] = 0;
661 new_data
= AllocVec(data
->len
+ len
+ 1, MEMF_ANY
);
663 data
->error
= ERROR_NO_FREE_STORE
;
664 XML_StopParser(data
->parser
, XML_TRUE
);
668 CopyMem(data
->data
, new_data
, data
->len
);
671 CopyMem(s
, new_data
+ data
->len
, len
);
672 data
->data
= new_data
;
674 data
->size
= data
->len
;
675 data
->data
[data
->len
] = 0;
680 void DMG_CloseImage (struct DiskImagePlugin
*Self
, APTR image_ptr
) {
681 struct DMGImage
*image
= image_ptr
;
683 if (image
->bz2base
) CloseLibrary(image
->bz2base
);
684 if (image
->zbase
) CloseLibrary(image
->zbase
);
685 if (image
->expatbase
) CloseLibrary(image
->expatbase
);
686 FreeVec(image
->hash
);
689 while ((pnode
= RemHead(image
->plist
))) {
692 FreeVec(image
->plist
);
694 FreeVec(image
->in_buf
);
695 FreeVec(image
->out_buf
);
701 LONG
DMG_Geometry (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct DriveGeometry
*dg
) {
702 struct DMGImage
*image
= image_ptr
;
703 dg
->dg_SectorSize
= image
->block_size
;
705 dg
->dg_TrackSectors
=
706 dg
->dg_CylSectors
= 1;
708 dg
->dg_TotalSectors
= image
->total_blocks
;
709 return IOERR_SUCCESS
;
712 LONG
DMG_Read (struct DiskImagePlugin
*Self
, APTR image_ptr
, struct IOStdReq
*io
) {
713 struct DMGImage
*image
= image_ptr
;
714 BPTR file
= image
->file
;
718 struct DMGHash
*hash
;
719 struct MinNode
*pnode
;
720 struct DMGPart
*part
;
721 UQUAD read_offs
, next_offs
;
722 ULONG to_skip
, to_read
;
724 buffer
= io
->io_Data
;
725 offset
= ((UQUAD
)io
->io_Offset
)|((UQUAD
)io
->io_Actual
<< 32);
726 size
= io
->io_Length
;
729 if (offset
>= image
->total_bytes
) {
730 return TDERR_SeekError
;
733 hash
= &image
->hash
[offset
>> HASH_FUNC
];
736 read_offs
= next_offs
= hash
->offset
;
738 switch (part
->type
) {
745 next_offs
+= part
->out_size
;
749 pnode
= (struct MinNode
*)GetSucc((struct Node
*)pnode
);
751 return TDERR_SeekError
;
753 part
= (struct DMGPart
*)(pnode
+ 1);
759 if (next_offs
> offset
) break;
760 read_offs
= next_offs
;
764 to_skip
= offset
- read_offs
;
766 to_read
= max((LONG
)min(size
, part
->out_size
- to_skip
), 0);
768 switch (part
->type
) {
771 if (!part
->out_size
) break;
772 if (image
->part_in_buf
!= part
) {
773 image
->part_in_buf
= NULL
;
775 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
776 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
781 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
782 return TDERR_SeekError
;
784 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
787 return error
? IPlugin_DOS2IOErr(error
) : IOERR_BADLENGTH
;
790 adc_decompress(image
->out_buf
, part
->out_size
, image
->in_buf
, part
->in_size
);
792 image
->part_in_buf
= part
;
794 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
799 if (!part
->out_size
) break;
800 if (image
->part_in_buf
!= part
) {
803 image
->part_in_buf
= NULL
;
805 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
806 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
811 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
812 return TDERR_SeekError
;
814 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
815 return IPlugin_DOS2IOErr(IoErr());
818 out_len
= part
->out_size
;
819 if ((status
= Uncompress(image
->out_buf
, &out_len
, image
->in_buf
,
820 part
->in_size
)) != Z_OK
)
822 return TDERR_NotSpecified
;
825 image
->part_in_buf
= part
;
827 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
832 if (!part
->out_size
) break;
833 if (image
->part_in_buf
!= part
) {
835 unsigned int out_len
;
836 image
->part_in_buf
= NULL
;
838 if (!(image
->in_buf
= ReAllocBuf(image
->in_buf
, &image
->in_size
, part
->in_size
)) ||
839 !(image
->out_buf
= ReAllocBuf(image
->out_buf
, &image
->out_size
, part
->out_size
)))
844 if (!ChangeFilePosition(file
, part
->in_offs
, OFFSET_BEGINNING
)) {
845 return TDERR_SeekError
;
847 if (Read(file
, image
->in_buf
, part
->in_size
) != part
->in_size
) {
848 return IPlugin_DOS2IOErr(IoErr());
851 out_len
= part
->out_size
;
852 if ((status
= BZ2_bzBuffToBuffDecompress(image
->out_buf
, &out_len
,
853 image
->in_buf
, part
->in_size
, 0, 0)) != BZ_OK
)
855 return TDERR_NotSpecified
;
858 image
->part_in_buf
= part
;
860 CopyMem(image
->out_buf
+ to_skip
, buffer
, to_read
);
865 if (!part
->out_size
) break;
866 if (!ChangeFilePosition(file
, part
->in_offs
+ to_skip
, OFFSET_BEGINNING
)) {
867 return TDERR_SeekError
;
869 if (Read(file
, buffer
, to_read
) != to_read
) {
872 return error
? IPlugin_DOS2IOErr(error
) : IOERR_BADLENGTH
;
879 memset(buffer
, 0, to_read
);
885 pnode
= (struct MinNode
*)GetSucc((struct Node
*)pnode
);
886 if (!pnode
) return IOERR_BADLENGTH
;
887 part
= (struct DMGPart
*)(pnode
+ 1);
899 io
->io_Actual
+= to_read
;
901 return IOERR_SUCCESS
;