|
7 | 7 | # See https://wiki.scummvm.org/index.php?title=HOWTO-Dump_Macintosh_Media for
|
8 | 8 | # the full documentation
|
9 | 9 | #
|
10 |
| -# prerequisites: pip3 install machfs |
| 10 | +# prerequisites: pip3 install machfs pycdlib |
11 | 11 | #
|
12 | 12 | # Development information:
|
13 | 13 | # This file contains tests. They can be run with:
|
|
31 | 31 | from typing import Any
|
32 | 32 |
|
33 | 33 | import machfs
|
| 34 | +import pycdlib |
34 | 35 |
|
35 | 36 |
|
36 | 37 | # fmt: off
|
@@ -289,7 +290,7 @@ def encode_string(args: argparse.Namespace) -> int:
|
289 | 290 | return 0
|
290 | 291 |
|
291 | 292 |
|
292 |
| -def extract_volume(args: argparse.Namespace) -> int: |
| 293 | +def extract_volume_hfs(args: argparse.Namespace) -> int: |
293 | 294 | """Extract an HFS volume"""
|
294 | 295 | source_volume: Path = args.src
|
295 | 296 | loglevel: str = args.log
|
@@ -340,6 +341,62 @@ def extract_volume(args: argparse.Namespace) -> int:
|
340 | 341 | extract_partition(args, vol)
|
341 | 342 |
|
342 | 343 |
|
| 344 | +def extract_volume_iso(args: argparse.Namespace): |
| 345 | + """Extract an ISO volume""" |
| 346 | + source_volume = args.src |
| 347 | + loglevel: str = args.log |
| 348 | + |
| 349 | + numeric_level = getattr(logging, loglevel.upper(), None) |
| 350 | + if not isinstance(numeric_level, int): |
| 351 | + raise ValueError("Invalid log level: %s" % loglevel) |
| 352 | + logging.basicConfig(format="%(levelname)s: %(message)s", level=numeric_level) |
| 353 | + |
| 354 | + logging.info(f"Loading {source_volume} ...") |
| 355 | + |
| 356 | + iso = pycdlib.PyCdlib() |
| 357 | + iso.open(source_volume) |
| 358 | + |
| 359 | + output_dir = str(args.dir) |
| 360 | + |
| 361 | + for dirname, dirlist, filelist in iso.walk(iso_path='/'): |
| 362 | + pwd = output_dir + dirname |
| 363 | + for dir in dirlist: |
| 364 | + joined_path = os.path.join(pwd, dir) |
| 365 | + os.makedirs(joined_path, exist_ok=True) |
| 366 | + for file in filelist: |
| 367 | + filename = file.split(';')[0] |
| 368 | + if dirname != '/': |
| 369 | + iso_file_path = dirname + '/' + file |
| 370 | + else: |
| 371 | + iso_file_path = dirname + file |
| 372 | + with open(os.path.join(pwd, filename), 'wb') as f: |
| 373 | + iso.get_file_from_iso_fp(outfp=f, iso_path=iso_file_path) |
| 374 | + |
| 375 | + iso.close() |
| 376 | + |
| 377 | + |
| 378 | +def extract_volume_hybrid(args: argparse.Namespace): |
| 379 | + source_volume = args.src |
| 380 | + loglevel: str = args.log |
| 381 | + |
| 382 | + numeric_level = getattr(logging, loglevel.upper(), None) |
| 383 | + if not isinstance(numeric_level, int): |
| 384 | + raise ValueError("Invalid log level: %s" % loglevel) |
| 385 | + logging.basicConfig(format="%(levelname)s: %(message)s", level=numeric_level) |
| 386 | + |
| 387 | + logging.info(f"Loading {source_volume} ...") |
| 388 | + |
| 389 | + if not args.macdump and not args.iso9660dump: |
| 390 | + logging.error("Please provide at least one dump for the hybrid drive") |
| 391 | + return |
| 392 | + if args.macdump: |
| 393 | + args.dir = args.macdump |
| 394 | + extract_volume_hfs(args) |
| 395 | + if args.iso9660dump: |
| 396 | + args.dir = args.iso9660dump |
| 397 | + extract_volume_iso(args) |
| 398 | + |
| 399 | + |
343 | 400 | def extract_partition(args: argparse.Namespace, vol) -> int:
|
344 | 401 | destination_dir: Path = args.dir
|
345 | 402 | japanese: bool = args.japanese
|
@@ -731,35 +788,81 @@ def generate_parser() -> argparse.ArgumentParser:
|
731 | 788 | """
|
732 | 789 | parser = argparse.ArgumentParser()
|
733 | 790 | subparsers = parser.add_subparsers()
|
734 |
| - parser_iso = subparsers.add_parser("iso", help="Dump HFS ISOs") |
| 791 | + parser_hfs = subparsers.add_parser("hfs", help="Dump HFS ISOs") |
735 | 792 |
|
736 |
| - parser_iso.add_argument("src", metavar="INPUT", type=Path, help="Disk image") |
737 |
| - parser_iso.add_argument( |
| 793 | + parser_hfs.add_argument("src", metavar="INPUT", type=Path, help="Disk image") |
| 794 | + parser_hfs.add_argument( |
738 | 795 | "--nopunycode", action="store_true", help="never encode pathnames into punycode"
|
739 | 796 | )
|
740 |
| - parser_iso.add_argument( |
| 797 | + parser_hfs.add_argument( |
741 | 798 | "--japanese", action="store_true", help="read MacJapanese HFS"
|
742 | 799 | )
|
743 |
| - parser_iso.add_argument( |
| 800 | + parser_hfs.add_argument( |
744 | 801 | "--dryrun", action="store_true", help="do not write any files"
|
745 | 802 | )
|
746 |
| - parser_iso.add_argument( |
| 803 | + parser_hfs.add_argument( |
747 | 804 | "--log", metavar="LEVEL", help="set logging level", default="INFO"
|
748 | 805 | )
|
749 |
| - parser_iso.add_argument( |
| 806 | + parser_hfs.add_argument( |
750 | 807 | "--forcemacbinary",
|
751 | 808 | action="store_true",
|
752 | 809 | help="always encode using MacBinary, even for files with no resource fork",
|
753 | 810 | )
|
754 |
| - parser_iso.add_argument( |
| 811 | + parser_hfs.add_argument( |
755 | 812 | "--addmacbinaryext",
|
756 | 813 | action="store_true",
|
757 | 814 | help="add .bin extension when using MacBinary",
|
758 | 815 | )
|
759 |
| - parser_iso.add_argument( |
| 816 | + parser_hfs.add_argument( |
760 | 817 | "dir", metavar="OUTPUT", type=Path, help="Destination folder"
|
761 | 818 | )
|
762 |
| - parser_iso.set_defaults(func=extract_volume) |
| 819 | + parser_hfs.set_defaults(func=extract_volume_hfs) |
| 820 | + |
| 821 | + parser_iso9660 = subparsers.add_parser( |
| 822 | + "iso9660", help="Dump ISO9660 ISOs" |
| 823 | + ) |
| 824 | + parser_iso9660.add_argument( |
| 825 | + "--log", metavar="LEVEL", help="set logging level", default="INFO" |
| 826 | + ) |
| 827 | + parser_iso9660.add_argument("src", metavar="INPUT", type=Path, help="Disk image") |
| 828 | + parser_iso9660.add_argument( |
| 829 | + "dir", metavar="OUTPUT", type=Path, help="Destination folder" |
| 830 | + ) |
| 831 | + parser_iso9660.set_defaults(func=extract_volume_iso) |
| 832 | + |
| 833 | + parser_hybrid = subparsers.add_parser( |
| 834 | + "hybrid", help="Dump Hybrid ISOs" |
| 835 | + ) |
| 836 | + parser_hybrid.add_argument("src", metavar="INPUT", type=Path, help="Disk image") |
| 837 | + parser_hybrid.add_argument( |
| 838 | + "--nopunycode", action="store_true", help="never encode pathnames into punycode" |
| 839 | + ) |
| 840 | + parser_hybrid.add_argument( |
| 841 | + "--japanese", action="store_true", help="read MacJapanese HFS" |
| 842 | + ) |
| 843 | + parser_hybrid.add_argument( |
| 844 | + "--dryrun", action="store_true", help="do not write any files" |
| 845 | + ) |
| 846 | + parser_hybrid.add_argument( |
| 847 | + "--log", metavar="LEVEL", help="set logging level", default="INFO" |
| 848 | + ) |
| 849 | + parser_hybrid.add_argument( |
| 850 | + "--forcemacbinary", |
| 851 | + action="store_true", |
| 852 | + help="always encode using MacBinary, even for files with no resource fork", |
| 853 | + ) |
| 854 | + parser_hybrid.add_argument( |
| 855 | + "--addmacbinaryext", |
| 856 | + action="store_true", |
| 857 | + help="add .bin extension when using MacBinary", |
| 858 | + ) |
| 859 | + parser_hybrid.add_argument( |
| 860 | + "--macdump", metavar="OUTPUT", type=Path, help="Destination Folder for HFS Dump" |
| 861 | + ) |
| 862 | + parser_hybrid.add_argument( |
| 863 | + "--iso9660dump", metavar="OUTPUT", type=Path, help="Destination Folder for ISO9660 Dump" |
| 864 | + ) |
| 865 | + parser_hybrid.set_defaults(func=extract_volume_hybrid) |
763 | 866 |
|
764 | 867 | parser_dir = subparsers.add_parser(
|
765 | 868 | "dir", help="Punyencode all files and dirs in place"
|
|
0 commit comments