scummvm/devtools/dumper-companion.py

555 lines
69 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
#
2021-08-24 02:08:12 +02:00
# Dumper for HFS/HFS+ images and files with non-ASCII
# file names.
#
# See https://wiki.scummvm.org/index.php?title=HOWTO-Dump_Macintosh_Media for
# the full documentation
#
# prerequisites: pip3 install machfs
#
# Development information:
# This file contains tests. They can be run with:
# $ pytest dumper-companion.py
#
# Code is formatted with `black `
import argparse
import io
import os
import sys
from binascii import crc_hqx
from pathlib import Path
from struct import pack, unpack
from typing import Any, ByteString, List, Tuple
import machfs
if sys.platform == "darwin":
try:
import xattr
except ImportError:
print("xattr is required for the 'mac' mode to work\n")
# fmt: off
decode_map = {
"81": [" ", "", "", "", "", "", "", "", "", "", "", "", "´", "", "¨", "", "", "_", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "±", "×", None, "÷", "", "", "", "", "", "", "", "", "", "", "°", "", "", "", "", "", "¢", "£", "", "", "", "", "", "§", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, "", "", "¬", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", None, None, None, None, ""],
"82": [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"83": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", None, None, None, None, None, None, None, None, "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ", "φ", "χ", "ψ", "ω"],
"84": ["А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж", "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "а", "б", "в", "г", "д", "е", "ё", "ж", "з", "и", "й", "к", "л", "м", "н", None, "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", "ь", "э", "ю", "я", None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"85": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, "", "", "", None, "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, "0.", "", "", "", "", "", "", "", "", "", None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "XIII", "XIV", "XV", None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "xiii", "xiv", "xv", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"86": ["", "", "", "", "", "m", "", "", "", "", "", "g", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "TB", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "FAX", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "↓↑", "", "", "", "", "⇨", "⇦", "⇧", "⇩"],
"87": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "大⃝", "小⃝", "", "", "", "", "", "", "", "", "", "", "控⃝", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "有限会社", "財団法人"],
"88": ["", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", None, "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"89": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "沿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"8a": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "橿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "竿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"8b": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"8c": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"8d": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "稿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"8e": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "使", "", "", "", "", "", "", "", "", "姿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "鹿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "湿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "寿", "", "", "", "", "", "", ""],
"8f": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "宿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "駿", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"90": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "西", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "穿", "", ""],
"91": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "退", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"92": ["", "", "", "", "", "", "", "", "辿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "調", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "椿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"93": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "殿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "禿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "廿", "", "", ""],
"94": ["", "尿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"95": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "便", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "簿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"96": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "貿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "麿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "婿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "綿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"97": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "輿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "耀", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"98": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "", "", "", "", "", "", "", "丿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"99": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"9a": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"9b": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "广", "", "", "", "", "", ""],
"9c": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "彿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "忿", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"9d": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"9e": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "榿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "槿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"9f": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "歿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "滿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "漿", "", "", "", "", ""],
"e0": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e1": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e2": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "祿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "窿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e3": ["", "", "", "", "", "", "", "", "紿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "繿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e4": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e5": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e6": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "覿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "谿", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "跿", "", "", "", "", "", "", "", "", ""],
"e7": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e8": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"e9": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "鴿", "", "", ""],
"ea": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", None, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"eb": [None, "、", "。", None, None, None, None, None, None, None, None, None, None, None, None, None, " ̄", "", None, None, None, None, None, None, None, None, None, "ー", "", "‐", None, None, "〜", "‖", "|", "…", "", None, None, None, None, "", "", "", "", "[", "]", "", "", "︿", "", "", "", "", "", "", "", "", "", None, None, None, None, None, None, "="],
"ec": [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "ぁ", None, "ぃ", None, "ぅ", None, "ぇ", None, "ぉ", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "っ", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "ゃ", None, "ゅ", None, "ょ", None, None, None, None, None, None, "ゎ"],
"ed": ["ァ", None, "ィ", None, "ゥ", None, "ェ", None, "ォ", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "ッ", None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, "ャ", None, "ュ", None, "ョ", None, None, None, None, None, None, "ヮ", None, None, None, None, None, None, "ヵ", "ヶ"],
}
# fmt: on
def decode_macjapanese(text: ByteString) -> str:
"""
Decode Mac Japanse
Mac OS Japanese
https://en.wikipedia.org/wiki/Shift_JIS#MacJapanese
https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/JAPANESE.TXT
"""
res = ""
i_text = iter(text)
hi = next(i_text, None)
while hi:
if hi <= 0x7F: # ASCII
res += chr(hi)
elif hi == 0x80: # reverse solidus
res += "\u005C"
elif (0x81 <= hi <= 0x9F) or (0xE0 <= hi <= 0xFC): # two-byte sequence
lo = next(i_text, None)
if lo is None:
raise Exception("Mac Japanese sequence missing second byte")
hi_key = hex(hi)[2:]
lo_key = lo - 0x40
if decode_map.get(hi_key) is None or decode_map[hi_key][lo_key] is None:
raise Exception(
f"No mapping for Mac Japanese sequence 0x{hi_key}{hex(lo)[2:]}"
)
assert_tmp = decode_map[hi_key][lo_key]
assert assert_tmp # mypy assert
res += assert_tmp
elif hi == 0xA0: # no-break space
res += "\u00A0"
elif 0xA1 <= hi <= 0xDF: # Katakana
res += chr(hi - 0xA1 + 0xFF61)
elif hi == 0xFD: # copyrig ht sign
res += "\u00A9"
elif hi == 0xFE: # trade mark sign
res += "\u2122"
elif hi == 0xFF: # halfwidth horizontal ellipsis
res += "\u2026\uF87F"
else:
raise Exception(f"No mapping for Mac Japanese sequece {hex(hi)}")
hi = next(i_text, None)
return res
def file_to_macbin(f: machfs.File, name: ByteString) -> bytes:
oldFlags = f.flags >> 8
newFlags = f.flags & 8
macbin = pack(
">xB63s4s4sBxHHHBxIIIIHB14xIHBB",
len(name),
name,
f.type,
f.creator,
oldFlags,
0,
0,
0,
f.locked,
len(f.data),
len(f.rsrc),
f.crdate, # TODO: dates are wrong, investigate
f.mddate, # TODO: dates are wrong, investigate
0,
newFlags,
0,
0,
129,
129,
)
macbin += pack(">H2x", crc_hqx(macbin, 0))
if f.data:
macbin += f.data
if len(f.data) % 128:
macbin += b"\x00" * (128 - len(f.data) % 128)
if f.rsrc:
macbin += f.rsrc
if len(f.rsrc) % 128:
macbin += b"\x00" * (128 - len(f.rsrc) % 128)
return macbin
def escape_string(s: str) -> str:
"""
Escape strings
Escape the following:
- escape char: \x81
- unallowed filename chars: https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
- control chars < 0x20
"""
new_name = ""
for char in s:
if char == "\x81":
new_name += "\x81\x79"
elif char in '/":*|\\?%<>' or ord(char) < 0x20:
new_name += "\x81" + chr(0x80 + ord(char))
else:
new_name += char
return new_name
def unescape_string(s: str) -> str:
"""unescape strings"""
orig_name = ""
s_iter = iter(s)
hi = next(s_iter, None)
while hi is not None:
if hi == "\x81":
low = next(s_iter, None)
assert low is not None, "Error decoding string"
if low == "\x79":
orig_name += "\x81"
else:
orig_name += chr(ord(low) - 0x80)
else:
orig_name += hi
hi = next(s_iter, None)
return orig_name
def needs_punyencoding(orig: str) -> bool:
"""
A filename needs to be punyencoded when it:
- contains a char that should be escaped or
- ends with a dot or a space.
"""
if orig != escape_string(orig):
return True
if orig[-1] in " .":
return True
return False
def punyencode(orig: str) -> str:
"""
Punyencode strings
- escape special characters and
- ensure filenames can't end in a space or dot
"""
s = escape_string(orig)
encoded = s.encode("punycode").decode("ascii")
# punyencoding adds an '-' at the end when there are no special chars
# don't use it for comparing
compare = encoded
if encoded.endswith("-"):
compare = encoded[:-1]
if orig != compare or compare[-1] in " .":
return "xn--" + encoded
return orig
def decode_string(orig: str) -> str:
"""
Decode punyencoded strings
"""
st = orig[4:].encode("ascii").decode("punycode")
return unescape_string(st)
def encode_string(args: argparse.Namespace) -> int:
if args.string:
var = args.string
if args.stdin:
var = input()
if var.startswith("xn--"):
print(decode_string(var))
else:
print(punyencode(var))
return 0
def generate_punyencoded_path(destination_dir: Path, hpath: Tuple[str]) -> Path:
"""Convert a filepath to a punyencoded one"""
upath = destination_dir
for el in hpath:
upath /= punyencode(el)
return upath
def extract_volume(args: argparse.Namespace) -> int:
"""Extract an mac iso"""
source_volume: Path = args.src
destination_dir: Path = args.dir
punify: bool = args.punycode
japanese: bool = args.japanese
print(f"Loading {source_volume} ...")
vol = machfs.Volume()
vol.read(source_volume.read_bytes())
destination_dir.mkdir(parents=True, exist_ok=True)
for hpath, obj in vol.iter_paths():
upath = destination_dir
for el in hpath:
if japanese:
el = decode_macjapanese(el.encode("mac_roman"))
if punify or needs_punyencoding(el):
el = punyencode(el)
upath /= el
if isinstance(obj, machfs.Folder):
upath.mkdir(exist_ok=True)
else:
print(upath)
file = obj.data
if obj.rsrc:
file = file_to_macbin(obj, hpath[-1].encode("mac_roman"))
upath.write_bytes(file)
os.utime(upath, (obj.mddate - 2082844800, obj.mddate - 2082844800))
return 0
def punyencode_paths(paths: List[Path], verbose: bool = False) -> int:
"""Rename filepaths to their punyencoded names"""
count = 0
for path in paths:
new_name = punyencode(path.name)
if path.stem != new_name:
count += 1
new_path = path.parent / new_name
if verbose:
print(f"Renamed {path} to {new_path}")
path.rename(new_path)
return count
def punyencode_arg(args: argparse.Namespace) -> int:
"""wrapper function"""
punyencode_dir(args.directory, verbose=True)
return 0
def punyencode_dir(directory: Path, verbose: bool = False) -> int:
"""
Recursively punyencode all directory and filenames
Renames the leaves, i.e. files, first and the works it way up the tree by renaming the
"""
files: List[Path] = []
dirs: List[Path] = []
path_glob = directory.glob("**/*")
for item in path_glob:
if item.is_file():
files.append(item)
if item.is_dir():
dirs.append(item)
dirs.reverse() # start renaming with the one at the bottom
count = punyencode_paths(files, verbose=verbose)
count += punyencode_paths(dirs, verbose=verbose)
return count
def has_resource_fork(dirpath: str, filename: str) -> bool:
"""
Check if file has a resource fork
Ease of compatibility between macOS and linux
"""
filepath = os.path.join(dirpath, filename)
return os.path.exists(os.path.join(filepath, "..namedfork/rsrc"))
def collect_forks(args: argparse.Namespace) -> int:
"""
Collect resource forks and move them to a macbinary file
- combine them with the data fork when it's available
- punyencode the filename when requested
"""
directory: Path = args.dir
punify: bool = args.punycode
count_resources = 0
count_renames = 0
for dirpath, _, filenames in os.walk(directory):
for filename in filenames:
if has_resource_fork(dirpath, filename):
print(f"Resource in {filename}")
count_resources += 1
resource_filename = filename + "/..namedfork/rsrc"
to_filename = filename
filepath = os.path.join(dirpath, filename)
resourcepath = os.path.join(dirpath, resource_filename)
file = machfs.File()
# Set the file times and convert them to Mac epoch
info = os.stat(filename)
file.crdate = 2082844800 + int(info.st_birthtime)
file.mddate = 2082844800 + int(info.st_mtime)
# Get info on creator and type
try:
finderInfo = xattr.xattr(filepath)["com.apple.FinderInfo"][0:8]
except (IOError, OSError) as e:
print(f"Error getting type and creator for: {filename}")
return 1
file.type, file.creator = unpack("4s4s", finderInfo)
with open(resourcepath, "rb") as rsrc:
file.rsrc = rsrc.read()
with open(filepath, "rb") as data:
file.data = data.read()
with open(filepath, "wb") as to_file:
to_file.write(file_to_macbin(file, to_filename.encode("mac_roman")))
if to_filename != filename:
os.remove(filepath) # Remove the original file
os.utime(
filepath,
(info.st_mtime, info.st_mtime),
)
if punify:
count_renames = punyencode_dir(directory, verbose=True)
print(f"Macbinary {count_resources}, Renamed {count_renames} files")
return 0
def generate_parser() -> argparse.ArgumentParser:
"""
Generate the parser
The parser is split into multiple subparsers.
One for each mode we support.
Each subparser has a default function that handles that mode.
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
parser_iso = subparsers.add_parser("iso", help="Dump hfs isos")
parser_iso.add_argument("src", metavar="INPUT", type=Path, help="Disk image")
parser_iso.add_argument(
"--punycode", action="store_true", help="encode pathnames into punycode"
)
parser_iso.add_argument(
"--japanese", action="store_true", help="read mac_japanese hfs+"
)
parser_iso.add_argument(
"dir", metavar="OUTPUT", type=Path, help="Destination folder"
)
parser_iso.set_defaults(func=extract_volume)
parser_dir = subparsers.add_parser(
"dir", help="Punyencode all files and dirs in place"
)
parser_dir.add_argument("directory", metavar="directory ", type=Path, help="Path")
parser_dir.set_defaults(func=punyencode_arg)
parser_str = subparsers.add_parser(
"str", help="Convert strings or standard in to or from punycode"
)
parser_str.add_argument(
"--stdin", action="store_true", help="Convert stdin to punycode"
)
parser_str.add_argument(
"string",
metavar="STRING",
type=str,
help="Convert string to or from punycode",
nargs="?",
)
parser_str.set_defaults(func=encode_string)
if sys.platform == "darwin":
parser_macbinary = subparsers.add_parser(
"mac",
help="MacOS only: Operate in MacBinary encoding mode. Recursively encode all resource forks in the current directory",
)
parser_macbinary.add_argument(
"--punycode",
action="store_true",
help="encode pathnames into punycode",
)
parser_macbinary.add_argument(
"dir", metavar="directory", type=Path, help="input directory"
)
parser_macbinary.set_defaults(func=collect_forks)
return parser
if __name__ == "__main__":
parser = generate_parser()
args = parser.parse_args()
exit(args.func(args))
### Test functions
def call_test_parser(input_args: List[str]) -> Any:
"""Helper function to call the parser"""
parser = generate_parser()
args = parser.parse_args(input_args)
args.func(args)
def test_decode_mac_japanese():
checks = [
[
b"QuickTime\xfe \x89\xb9\x90F\x91\xce\x89\x9e\x95\\",
"QuickTime™ 音色対応表",
]
]
for input, expected in checks:
assert decode_macjapanese(input) == expected
def test_encode_string(capsys):
checks = [["Icon\r", "xn--Icon-ja6e"]]
for input, output in checks:
call_test_parser(["str", input])
captured = capsys.readouterr()
assert captured.out == output + "\n"
call_test_parser(["str", output])
captured = capsys.readouterr()
assert captured.out == input + "\n"
def test_encode_stdin(capsys, monkeypatch):
monkeypatch.setattr("sys.stdin", io.StringIO("Icon\r"))
call_test_parser(["str", "--stdin"])
captured = capsys.readouterr()
assert captured.out == "xn--Icon-ja6e\n"
def test_decode_name():
checks = [
["Icon\r", "xn--Icon-ja6e"],
["ends with dot .", "xn--ends with dot .-"],
["ends with space ", "xn--ends with space -"],
["バッドデイ(Power PC)", "xn--(Power PC)-jx4ilmwb1a7h"],
]
for input, output in checks:
assert punyencode(input) == output
assert decode_string(output) == input
def test_needs_punyencoding():
checks = [
["Icon\r", True],
["ascii", False],
["バッドデイ(Power PC)", False],
["ends_with_dot .", True],
["ends_with_space ", True],
]
for input, expected in checks:
assert needs_punyencoding(input) == expected
def test_escape_string():
checks = [["\r", "\x81\x8d"], ["\x81", "\x81\x79"]]
for input, output in checks:
assert escape_string(input) == output
assert unescape_string(output) == input