3 # AppleSingle/AppleDouble dump
5 # (c) 2009 by HAT <hat@fa2.so-net.ne.jp>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
20 use bigint; # require perl >= 5.8
22 # parse command line -----------------------------------------------
24 $finderinfo = 0; # 0: unknown 1: file 2: directory
25 while ($arg = shift @ARGV)
27 if ($arg =~ /^(-h|-help|--help)$/ ) {
28 printf ("usage: %s [-a] FILE|DIR\n" ,basename($0));
29 printf (" or: %s -f FILE\n" ,basename($0));
30 printf (" or: %s -d FILE\n" ,basename($0));
31 printf (" or: %s -h|-help|--help\n" ,basename($0));
32 printf ("Dump AppleSingle/AppleDouble format file.\n");
34 printf (" -a (default) Dump a AppleSingle/AppleDouble file for FILE or DIR\n");
35 printf (" automatically.\n");
36 printf (" Extrapolate FinderInfo type from absolute path.\n");
37 printf (" If FILE is not AppleSingle/AppleDouble format,\n");
38 printf (" look for '.AppleDouble/FILE' and '._FILE'.\n");
39 printf (" If DIR, look for 'DIR/.AppleDouble/.Parent' and '._DIR'.\n");
40 printf (" -f Dump FILE. Assume FinderInfo to be FileInfo.\n");
41 printf (" -d Dump FILE. Assume FinderInfo to be DirInfo.\n");
42 printf (" -h,-help,--help Display this help and exit\n");
44 printf ("There is no way to detect whether FinderInfo is FileInfo or DirInfo.\n");
45 printf ("By default, %s examins whether file or directory,\n" ,basename($0));
46 printf ("a parent directory is .AppleDouble, filename is ._*, filename is .Parent,\n");
47 printf ("and so on.\n");
48 printf ("If setting option -f or -d, %s assume FinderInfo and doesn't look for\n");
49 printf ("another file.\n");
51 } elsif ($arg eq "-a") {
53 } elsif ($arg eq "-f") {
55 } elsif ($arg eq "-d") {
57 } elsif ($arg =~ /^-/) {
58 printf (STDERR "%s: invalid option %s\n", basename($0), $arg);
59 printf (STDERR "Try \`%s\ -h' for more information.\n", basename($0));
67 printf (STDERR "missing file operand.\n");
71 printf (STDERR "\"%s\" not found.\n", $afile);
75 # detect FinderInfo, and search AppleSingle/AppleDouble file --------------
77 $abspath = File::Spec->rel2abs($afile);
78 ($basename, $path, $ext) = fileparse($abspath);
80 if ( $finderinfo != 0 ) {
82 } elsif ( -f $afile ) {
83 if ( $basename eq ".Parent") {
85 } elsif ( $path =~ /\/.AppleDouble\/$/ ) {
87 } elsif ( $basename =~ /^._/ ) {
88 if ( -f $path.substr($basename, 2) ) {
90 } elsif ( -d $path.substr($basename, 2) ) {
94 if (!open(INFILE, "<$afile")) {
95 printf (STDERR "cannot open %s\n", $afile);
99 $val = unpack("N", $buf );
101 if ($val == 0x00051600 || $val == 0x00051607) {
104 printf ("\"%s\" is not AppleSingle/AppleDouble format.\n", $afile);
106 $netatalkfile = $path.".AppleDouble/".$basename;
107 $osxfile = $path."._".$basename;
108 if ( -e $netatalkfile ) {
109 $afile = $netatalkfile;
110 } elsif ( -e $osxfile ) {
111 printf ("\"%s\" not found.\n", $netatalkfile);
114 printf ("\"%s\" not found.\n", $netatalkfile);
115 printf ("\"%s\" not found.\n", $osxfile);
119 } elsif ( -d $afile) {
120 printf ("\"%s\" is directory.\n", $afile);
122 $netatalkfile = $path.$basename."/.AppleDouble/.Parent";
123 $osxfile = $path."._".$basename;
124 if ( -e $netatalkfile ) {
125 $afile = $netatalkfile;
126 } elsif ( -e $osxfile ) {
127 printf ("\"%s\" not found.\n", $netatalkfile);
130 printf ("\"%s\" not found.\n", $netatalkfile);
131 printf ("\"%s\" not found.\n", $osxfile);
135 printf (STDERR "unknown error: %s\n", $afile);
139 if (!open(INFILE, "<$afile")) {
140 printf (STDERR "cannot open %s\n", $afile);
143 printf ("%s:\n\n", $afile);
145 # Magic Number -----------------------------------------------
148 $val = unpack("N", $buf );
149 printf("Magic Num. : %08X", $val);
150 if ( $val == 0x00051600 ) {
151 printf(" : AppleSingle");
153 elsif ( $val == 0x00051607 ) {
154 printf(" : AppleDouble");
157 printf(" : Unknown" );
161 # Version Number ---------------------------------------------
164 $val = unpack("N", $buf );
165 printf("Ver. Num. : %08X", $val);
166 if ( $val == 0x00010000 ) {
167 printf(" : Version 1");
169 elsif ( $val == 0x00020000 ) {
170 printf(" : Version 2");
173 printf(" : Unknown" );
177 # v1:Home file system / v2:Filler ----------------------------
179 read(INFILE,$buf,16);
181 hexdump($buf, 16, 16, " ");
183 # Number of entities -----------------------------------------
186 $entnum = unpack("n", $buf );
187 printf("Num. of ent: %04X ", $entnum);
188 printf(" : %d", $entnum);
191 # data -------------------------------------------------------
193 for ( $num = 0 ; $num < $entnum ; $num++) {
195 seek(INFILE, ($num * 12 + 26), 0);
197 # Entry ---------------------------------------------------
200 $entid = unpack("N", $buf );
201 printf("\nEntry ID : %08X", $entid);
202 if ( $entid == 1 ) { printf(" : Data Fork ---------------------------------------------"); }
203 elsif ( $entid == 2 ) { printf(" : Resource Fork -----------------------------------------"); }
204 elsif ( $entid == 3 ) { printf(" : Real Name ---------------------------------------------"); }
205 elsif ( $entid == 4 ) { printf(" : Comment -----------------------------------------------"); }
206 elsif ( $entid == 5 ) { printf(" : Icon, B&W ---------------------------------------------"); }
207 elsif ( $entid == 6 ) { printf(" : Icon Color --------------------------------------------"); }
208 elsif ( $entid == 7 ) { printf(" : File Info ---------------------------------------------"); }
209 elsif ( $entid == 8 ) { printf(" : File Dates Info ---------------------------------------"); }
210 elsif ( $entid == 9 ) { printf(" : Finder Info -------------------------------------------"); }
211 elsif ( $entid == 10 ) { printf(" : Macintosh File Info -----------------------------------"); }
212 elsif ( $entid == 11 ) { printf(" : ProDOS File Info --------------------------------------"); }
213 elsif ( $entid == 12 ) { printf(" : MS-DOS File Info --------------------------------------"); }
214 elsif ( $entid == 13 ) { printf(" : Short Name --------------------------------------------"); }
215 elsif ( $entid == 14 ) { printf(" : AFP File Info -----------------------------------------"); }
216 elsif ( $entid == 15 ) { printf(" : Directory ID ------------------------------------------"); }
217 elsif ( $entid == 0x8053567E ) { printf(" : CNID (Netatalk Extended) ------------------------------"); }
218 elsif ( $entid == 0x8053594E ) { printf(" : DB stamp (Netatalk Extended) --------------------------"); }
219 elsif ( $entid == 0x80444556 ) { printf(" : dev (Netatalk Extended) -------------------------------"); }
220 elsif ( $entid == 0x80494E4F ) { printf(" : inode (Netatalk Extended) -----------------------------"); }
221 else { printf(" : Unknown -----------------------------------------------"); }
224 # Offset -------------------------------------------------
227 $ofst = unpack("N", $buf );
228 printf("Offset : %08X", $ofst);
229 printf(" : %d ", $ofst);
231 # Length -------------------------------------------------
234 $len = unpack("N", $buf );
235 printf("\nLength : %08X", $len);
236 printf(" : %d", $len);
241 # Dump for each Entry ID --------------------------------
243 # if ( $entid == 1 ) { ; } # Data Fork
244 # if ( $entid == 2 ) { ; } # Resource Fork
245 # if ( $entid == 3 ) { ; } # Real Name
246 # if ( $entid == 4 ) { ; } # Comment
247 # if ( $entid == 5 ) { ; } # Icon, B&W
248 # if ( $entid == 6 ) { ; } # Icon Color
249 # if ( $entid == 7 ) { ; } # File Info
250 if ( $entid == 8 ) { filedatesdump($ofst,$len); }
251 elsif ( $entid == 9 ) { finderinfodump($ofst,$len); }
252 # if ( $entid == 10 ) { ; } # Macintosh File Info
253 # if ( $entid == 11 ) { ; } # ProDOS File Info
254 # if ( $entid == 12 ) { ; } # MS-DOS File Info
255 # if ( $entid == 13 ) { ; } # Short Name
256 # if ( $entid == 14 ) { ; } # AFP File Info
257 elsif ( $entid == 15 ) { bedump($ofst,$len); } # Directory ID
258 elsif ( $entid == 0x8053567E ) { bedump($ofst,$len); } # CNID (Netatalk Extended)
259 elsif ( $entid == 0x8053594E ) { bedump($ofst,$len); ledump($ofst,$len); } # DB stamp (Netatalk Extended)
260 elsif ( $entid == 0x80444556 ) { bedump($ofst,$len); ledump($ofst,$len); } # dev (Netatalk Extended)
261 elsif ( $entid == 0x80494E4F ) { bedump($ofst,$len); ledump($ofst,$len); } # inode (Netatalk Extended)
263 # RAW Dump ---------------------------------------------------
265 if ( ($quo > 0) || ($rem > 0)) {
266 print "-RAW DUMP--: 0 1 2 3 4 5 6 7 8 9 A B C D E F : (ASCII)\n";
269 seek(INFILE, $ofst, 0);
271 for ( $line = 0 ; $line < $quo ; $line++) {
272 read(INFILE, $buf, 16);
273 printf ( "%08X :", $addrs);
274 hexdump($buf, 16, 16, " ");
275 $addrs = $addrs + 0x10;
278 read(INFILE, $buf, $rem);
279 printf ( "%08X :", $addrs);
280 hexdump($buf, $rem, 16, " ");
287 #sub -----------------------------------------------------------
290 my ($ofst, $len) = @_;
295 @datetype =('create ', 'modify ', 'backup ', 'access ');
297 seek(INFILE, $ofst, 0);
299 printf ("-DATE------: : (GMT) : (Local)\n");
301 for ( $i = 0 ; $i < 4 ; $i++) {
303 $datedata = unpack("N", $buf );
304 if ($datedata < 0x80000000) {
305 $datestr = gmtime( $datedata + 946684800)
307 .localtime( $datedata + 946684800);
308 } elsif ($datedata == 0x80000000) {
309 $datestr = "Unknown or Initial";
311 $datestr = gmtime( $datedata - 3348282496)
313 .localtime( $datedata - 3348282496);
315 printf ("%s : %08X : %s\n",$datetype[$i], $datedata, $datestr);
320 my ($ofst, $len) = @_;
322 seek(INFILE, $ofst, 0);
324 if ($finderinfo == 0) {
325 print "-NOTE------: cannot detect whether FInfo or DInfo. assume FInfo.\n";
328 if ($finderinfo == 0 || $finderinfo == 1) {
329 filefinderinfodump();
330 } elsif ($finderinfo == 2) {
333 print STDERR "unknown FinderInfo type\n"
336 if ($len > 32) { eadump(); }
339 sub filefinderinfodump {
341 print "-FInfo-----:\n";
345 hexdump($buf, 4, 4, "");
349 hexdump($buf, 4, 4, "");
352 $flags = unpack("n", $buf );
353 printf ("isAlias : %d\n", ($flags >> 15) & 1);
354 printf ("Invisible : %d\n", ($flags >> 14) & 1);
355 printf ("hasBundle : %d\n", ($flags >> 13) & 1);
356 printf ("nameLocked : %d\n", ($flags >> 12) & 1);
357 printf ("Stationery : %d\n", ($flags >> 11) & 1);
358 printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
359 printf ("Reserved : %d\n", ($flags >> 9) & 1);
360 printf ("Inited : %d\n", ($flags >> 8) & 1);
361 printf ("NoINITS : %d\n", ($flags >> 7) & 1);
362 printf ("Shared : %d\n", ($flags >> 6) & 1);
363 printf ("SwitchLaunc: %d\n", ($flags >> 5) & 1);
364 printf ("ExtHidden : %d\n", ($flags >> 4) & 1);
365 printf ("color : %d%d%d\n", ($flags >> 3) & 1,
368 printf ("isOnDesk : %d\n", ($flags >> 0) & 1);
371 $val = unpack("n", $buf );
372 printf("Location v : %04X", $val);
373 printf(" : %d \n", $val);
376 $val = unpack("n", $buf );
377 printf("Location h : %04X", $val);
378 printf(" : %d \n", $val);
382 hexdump($buf, 2, 4, "");
384 print "-FXInfo----:\n";
388 hexdump($buf, 2, 4, "");
392 hexdump($buf, 2, 4, "");
395 hexdump($buf, 2, 4, "");
398 hexdump($buf, 2, 4, "");
402 hexdump($buf, 1, 4, "");
406 hexdump($buf, 1, 4, "");
410 hexdump($buf, 2, 4, "");
414 hexdump($buf, 4, 4, "");
418 sub dirfinderinfodump {
420 print "-DInfo-----:\n";
423 $val = unpack("n", $buf );
424 printf("Rect top : %04X", $val);
425 printf(" : %d \n", $val);
428 $val = unpack("n", $buf );
429 printf("Rect left : %04X", $val);
430 printf(" : %d \n", $val);
433 $val = unpack("n", $buf );
434 printf("Rect bottom: %04X", $val);
435 printf(" : %d \n", $val);
438 $val = unpack("n", $buf );
439 printf("Rect right : %04X", $val);
440 printf(" : %d \n", $val);
443 $flags = unpack("n", $buf );
444 printf ("isAlias : %d\n", ($flags >> 15) & 1);
445 printf ("Invisible : %d\n", ($flags >> 14) & 1);
446 printf ("hasBundle : %d\n", ($flags >> 13) & 1);
447 printf ("nameLocked : %d\n", ($flags >> 12) & 1);
448 printf ("Stationery : %d\n", ($flags >> 11) & 1);
449 printf ("CustomIcon : %d\n", ($flags >> 10) & 1);
450 printf ("Reserved : %d\n", ($flags >> 9) & 1);
451 printf ("Inited : %d\n", ($flags >> 8) & 1);
452 printf ("NoINITS : %d\n", ($flags >> 7) & 1);
453 printf ("Shared : %d\n", ($flags >> 6) & 1);
454 printf ("SwitchLaunc: %d\n", ($flags >> 5) & 1);
455 printf ("ExtHidden : %d\n", ($flags >> 4) & 1);
456 printf ("color : %d%d%d\n", ($flags >> 3) & 1,
459 printf ("isOnDesk : %d\n", ($flags >> 0) & 1);
462 $val = unpack("n", $buf );
463 printf("Location v : %04X", $val);
464 printf(" : %d \n", $val);
467 $val = unpack("n", $buf );
468 printf("Location h : %04X", $val);
469 printf(" : %d \n", $val);
473 hexdump($buf, 2, 4, "");
475 print "-DXInfo----:\n";
479 hexdump($buf, 4, 4, "");
482 print "OpenChain : ";
483 hexdump($buf, 4, 4, "");
487 hexdump($buf, 1, 4, "");
491 hexdump($buf, 1, 4, "");
495 hexdump($buf, 2, 4, "");
499 hexdump($buf, 4, 4, "");
505 print "-EA--------:\n";
509 hexdump($buf, 2, 4, "");
513 hexdump($buf, 4, 4, "");
516 print "debug_tag : ";
517 hexdump($buf, 4, 4, "");
520 $ea_total_size = unpack("N", $buf );
521 printf("total_size : %08X", $ea_total_size);
522 printf(" : %d \n", $ea_total_size);
525 $ea_data_start = unpack("N", $buf );
526 printf("data_start : %08X", $ea_data_start);
527 printf(" : %d \n", $ea_data_start);
530 $ea_data_length = unpack("N", $buf );
531 printf("data_length: %08X", $ea_data_length);
532 printf(" : %d \n", $ea_data_length);
535 print "reserved[0]: ";
536 hexdump($buf, 4, 4, "");
539 print "reserved[1]: ";
540 hexdump($buf, 4, 4, "");
543 print "reserved[2]: ";
544 hexdump($buf, 4, 4, "");
548 hexdump($buf, 2, 4, "");
551 $ea_num_attrs = unpack("n", $buf );
552 printf("num_attrs : %04X", $ea_num_attrs);
553 printf(" : %d \n", $ea_num_attrs);
558 my ($ofst, $len) = @_;
562 seek(INFILE, $ofst, 0);
564 printf("%2dbit-BE : ", $len * 8 );
567 for ( $i=0 ; $i < $len ; $i++ ) {
569 $bytedata[$i] = unpack("C", $buf );
570 $value += $bytedata[$i] << (($len - $i -1) * 8) ;
573 for ( $i=0 ; $i < $len ; $i++ ) {
574 printf("%02X", $bytedata[$i]);
577 printf(" : %s", $value);
583 my ($ofst, $len) = @_;
587 seek(INFILE, $ofst, 0);
589 printf("%2dbit-LE : ", $len * 8 );
592 for ( $i=0 ; $i < $len ; $i++ ) {
594 $bytedata[$len - $i - 1] = unpack("C", $buf );
595 $value += $bytedata[$len - $i -1] << ($i * 8) ;
598 for ( $i=0 ; $i < $len ; $i++ ) {
599 printf("%02X", $bytedata[$i]);
602 printf(" : %s", $value);
608 my ($buf, $len, $col, $delimit) = @_;
614 for ( $i=0 ; $i < $len ; $i++ ) {
615 $val = substr($buf, $i, 1);
617 $hexstr .= sprintf("%s%02X", $delimit, $ascval);
619 if (($ascval < 32) || ( $ascval > 126 )) {
624 for ( ; $i < $col ; $i++) {
625 $hexstr .= " ".$delimit;
629 printf("%s : %s", $hexstr,$ascstr);