mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 21:31:53 +00:00
884 lines
82 KiB
Python
Executable File
884 lines
82 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# coding: utf-8
|
||
#
|
||
# 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 logging
|
||
import os
|
||
import sys
|
||
import unicodedata
|
||
import urllib.request
|
||
import zipfile
|
||
from binascii import crc_hqx
|
||
from io import BytesIO, StringIO, IOBase
|
||
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:
|
||
logging.error("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, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", None, None, None, None, None, None, None, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", None, None, None, None, None, None, None, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", 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 MacJapanese
|
||
|
||
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:
|
||
logging.warning(
|
||
f"MacJapanese sequence missing second byte 0x{hi:02x}, decoding as MacRoman"
|
||
)
|
||
return text.decode("mac-roman")
|
||
if 0xF0 <= hi <= 0xFC: # Shift-JIS mapping
|
||
logging.warning(
|
||
f"MacJapanese sequence has high first byte 0x{hi:02x}, mapping to Shift-JIS"
|
||
)
|
||
hilo = (hi << 8) | lo & 0x00FF
|
||
if (0x40 <= lo <= 0x7E) or (0x80 <= lo <= 0xFC) and (lo != 0x7F):
|
||
hilo = (
|
||
0xE000
|
||
+ ((hi & 0xFF) - 0xF0) * 0xBC
|
||
+ ((lo & 0xFF) - ((0x41, 0x40)[(lo & 0xFF) >= 0x80]))
|
||
)
|
||
|
||
n = chr(hilo)
|
||
res += n
|
||
else:
|
||
hi_key = f"{hi:02x}"
|
||
lo_key = lo - 0x40
|
||
hilo = None
|
||
if (
|
||
hilo is None
|
||
and decode_map.get(hi_key) is None
|
||
or decode_map[hi_key][lo_key] is None
|
||
):
|
||
raise Exception(
|
||
f"No mapping for MacJapanese sequence 0x{hi_key}{lo:02x}"
|
||
)
|
||
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: # copyright 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 MacJapanese sequence 0x{hi:02x}")
|
||
hi = next(i_text, None)
|
||
return res
|
||
|
||
|
||
def file_to_macbin(out_f: IOBase, f: machfs.File, name: ByteString) -> bytes:
|
||
oldFlags = f.flags >> 8
|
||
newFlags = f.flags & 0xFF
|
||
macbin_header = pack(
|
||
">x64p4s4sBxHHHBxIIIIHB14xIHBB",
|
||
name,
|
||
f.type,
|
||
f.creator,
|
||
oldFlags,
|
||
0,
|
||
0,
|
||
0,
|
||
f.locked,
|
||
len(f.data),
|
||
len(f.rsrc),
|
||
f.crdate,
|
||
f.mddate,
|
||
0,
|
||
newFlags,
|
||
0,
|
||
0,
|
||
129,
|
||
129,
|
||
)
|
||
macbin_header += pack(">H2x", crc_hqx(macbin_header, 0))
|
||
out_f.write(macbin_header)
|
||
if f.data:
|
||
out_f.write(f.data)
|
||
out_f.write(b"\x00" * (-len(f.data) % 128))
|
||
|
||
if f.rsrc:
|
||
out_f.write(f.rsrc)
|
||
out_f.write(b"\x00" * (-len(f.rsrc) % 128))
|
||
|
||
|
||
def macbin_get_datafork(f: bytes) -> bytes:
|
||
(datalen,) = unpack(">I", f[0x53:0x57])
|
||
print("Data len is:", datalen)
|
||
return f[0x80 : 0x80 + datalen]
|
||
|
||
|
||
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 '/":*|\\?%<>\x7f' 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 extract_volume(args: argparse.Namespace) -> int:
|
||
"""Extract an HFS volume"""
|
||
source_volume: Path = args.src
|
||
destination_dir: Path = args.dir
|
||
japanese: bool = args.japanese
|
||
dryrun: bool = args.dryrun
|
||
rawtext: bool = args.nopunycode
|
||
loglevel: string = args.log
|
||
force_macbinary: bool = args.forcemacbinary
|
||
|
||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||
if not isinstance(numeric_level, int):
|
||
raise ValueError("Invalid log level: %s" % loglevel)
|
||
logging.basicConfig(format="%(levelname)s: %(message)s", level=numeric_level)
|
||
|
||
logging.info(f"Loading {source_volume} ...")
|
||
vol = machfs.Volume()
|
||
with source_volume.open(mode="rb") as f:
|
||
f.seek(0x200)
|
||
if f.read(4) == b"PM\0\0":
|
||
partition_num = 1
|
||
partition_type = ""
|
||
while partition_type != "Apple_HFS":
|
||
num_partitions, partition_start, partition_size = unpack(
|
||
">III", f.read(12)
|
||
)
|
||
f.seek(32, 1)
|
||
partition_type = f.read(32).decode("ascii").strip("\0")
|
||
if partition_num <= num_partitions and partition_type != "Apple_HFS":
|
||
# Move onto the next partition
|
||
partition_num += 1
|
||
f.seek(partition_num * 0x200 + 4)
|
||
else:
|
||
# We found the one we want or there's none
|
||
break
|
||
|
||
f.seek(partition_start * 0x200)
|
||
vol.read(f.read(partition_size * 0x200))
|
||
else:
|
||
f.seek(0)
|
||
vol.read(f.read())
|
||
|
||
if not dryrun:
|
||
destination_dir.mkdir(parents=True, exist_ok=True)
|
||
maybe_not_jp = False
|
||
maybe_not_jp_warned = False
|
||
for hpath, obj in vol.iter_paths():
|
||
# Encode the path
|
||
upath = destination_dir
|
||
for el in hpath:
|
||
if japanese:
|
||
el = decode_macjapanese(el.encode("mac_roman"))
|
||
else:
|
||
try:
|
||
if decode_macjapanese(
|
||
el.encode("mac_roman")
|
||
) != el and not isinstance(obj, machfs.Folder):
|
||
maybe_not_jp = True
|
||
except Exception:
|
||
# If we get an exception from trying to decode it as Mac-Japanese, it's probably not
|
||
pass
|
||
if not rawtext:
|
||
el = punyencode(el)
|
||
|
||
upath /= el
|
||
|
||
if maybe_not_jp and not maybe_not_jp_warned:
|
||
logging.warning(
|
||
"Possible Mac-Japanese string detected, did you mean to use --japanese?"
|
||
)
|
||
maybe_not_jp_warned = True
|
||
|
||
# Write the file to disk
|
||
if isinstance(obj, machfs.Folder):
|
||
if not dryrun:
|
||
upath.mkdir(exist_ok=True)
|
||
os.utime(upath, (obj.mddate - 2082844800, obj.mddate - 2082844800))
|
||
# Set the modified time for folders
|
||
else:
|
||
print(upath)
|
||
if not dryrun:
|
||
if obj.data and not obj.rsrc and not force_macbinary:
|
||
upath.write_bytes(obj.data)
|
||
|
||
elif obj.rsrc or force_macbinary:
|
||
with upath.open("wb") as out_file:
|
||
file_to_macbin(out_file, obj, hpath[-1].encode("mac_roman"))
|
||
|
||
elif not obj.data and not obj.rsrc:
|
||
upath.touch()
|
||
|
||
os.utime(upath, (obj.mddate - 2082844800, obj.mddate - 2082844800))
|
||
# This needs to be done after writing files as writing files resets
|
||
# the parent folder's modified time that was set before
|
||
if len(hpath) > 1:
|
||
for i in range(len(hpath), 0, -1):
|
||
parent_folder_modtime = vol.get(hpath[:i]).mddate - 2082844800
|
||
os.utime(
|
||
Path(*(upath.parts[:i])),
|
||
(parent_folder_modtime, parent_folder_modtime),
|
||
)
|
||
return 0
|
||
|
||
|
||
def punyencode_paths(
|
||
paths: List[Path], verbose: bool = False, source_encoding: str = None
|
||
) -> int:
|
||
"""Rename filepaths to their punyencoded names"""
|
||
count = 0
|
||
for path in paths:
|
||
if source_encoding is not None:
|
||
new_name = punyencode(
|
||
demojibake_hfs_bytestring(bytes(path.name, "utf8"), source_encoding)
|
||
)
|
||
else:
|
||
new_name = punyencode(path.name)
|
||
if path.stem != new_name:
|
||
count += 1
|
||
new_path = path.parent / new_name
|
||
if verbose:
|
||
logging.info(f"Renamed {path} to {new_path}")
|
||
path.rename(new_path)
|
||
return count
|
||
|
||
|
||
def demojibake_hfs_bytestring(s: ByteString, encoding: str):
|
||
"""
|
||
Takes misinterpreted bytestrings from macOS and transforms
|
||
them into the correct interpretation.
|
||
When not able to figure out the correct encoding for legacy
|
||
non-Unicode HFS filesystems, which is most of the time, macOS
|
||
interprets filenames as though they're MacRoman. Once mounted,
|
||
the files are presented via all of the macOS filesystem APIs
|
||
as though they're UTF-8.
|
||
This is great for Western European languages, but falls over for
|
||
other languages. For example, Japanese filenames will be rendered
|
||
as gibberish (mojibake). This can be fixed by normalizing the
|
||
filenames' UTF-8 encoding, transforming it back to "MacRoman",
|
||
then correctly reinterpreting via the correct encoding.
|
||
"""
|
||
return decode_bytestring(
|
||
# macOS renders paths as NFD, but to correctly translate
|
||
# this back to the original MacRoman, we first have to
|
||
# renormalize it to NFC.
|
||
unicodedata.normalize("NFC", s.decode("utf8")).encode("macroman"),
|
||
encoding,
|
||
)
|
||
|
||
|
||
def decode_bytestring(s: ByteString, encoding: str):
|
||
"""Wrapper for decode() that can dispatch to decode_macjapanese"""
|
||
if encoding == "mac_japanese":
|
||
return decode_macjapanese(s)
|
||
else:
|
||
return s.decode(encoding)
|
||
|
||
|
||
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, source_encoding: str = None
|
||
) -> 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] = []
|
||
if source_encoding is not None:
|
||
directory = Path(demojibake_hfs_bytestring(directory, source_encoding))
|
||
else:
|
||
directory = Path(os.fsdecode(directory))
|
||
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, source_encoding=source_encoding)
|
||
count += punyencode_paths(dirs, verbose=verbose, source_encoding=source_encoding)
|
||
return count
|
||
|
||
|
||
def has_resource_fork(dirpath: bytes, filename: bytes) -> 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, bytes("..namedfork/rsrc", "utf8")))
|
||
|
||
|
||
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: bytes = bytes(args.dir)
|
||
punify: bool = args.punycode
|
||
force_macbinary: bool = args.forcemacbinary
|
||
count_resources = 0
|
||
count_renames = 0
|
||
for dirpath, _, filenames in os.walk(directory):
|
||
for filename in filenames:
|
||
has_rsrc = has_resource_fork(dirpath, filename)
|
||
if has_rsrc or force_macbinary:
|
||
logging.info(f"Resource in {filename}")
|
||
count_resources += 1
|
||
resource_filename = filename + bytes("/..namedfork/rsrc", "utf8")
|
||
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(filepath)
|
||
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:9]
|
||
except (IOError, OSError) as e:
|
||
logging.info(f"Error getting type and creator for: {filename}")
|
||
return 1
|
||
|
||
file.type, file.creator, file.flags = unpack("4s4sB", finderInfo)
|
||
|
||
with open(filepath, "rb") as data:
|
||
file.data = data.read()
|
||
with open(filepath, "wb") as to_file:
|
||
if has_rsrc:
|
||
with open(resourcepath, "rb") as rsrc:
|
||
file.rsrc = rsrc.read()
|
||
file_to_macbin(to_file, file, to_filename)
|
||
|
||
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, source_encoding=args.source_encoding
|
||
)
|
||
|
||
logging.info(f"Macbinary {count_resources}, Renamed {count_renames} files")
|
||
return 0
|
||
|
||
|
||
def block_copy(dest, dest_offset, src, src_offset, size):
|
||
if size == 0:
|
||
return
|
||
dest[dest_offset : dest_offset + size] = src[src_offset : src_offset + size]
|
||
|
||
|
||
# Inserts bytes into sliding window ring buffer, returns new window position
|
||
def insert_sl(sl, sl_pos, bytes_to_insert, insert_src_offset, size):
|
||
available = 0x10000 - sl_pos
|
||
if available < size:
|
||
block_copy(sl, sl_pos, bytes_to_insert, insert_src_offset, available)
|
||
sl_pos = 0
|
||
sl_pos = insert_sl(
|
||
sl, sl_pos, bytes_to_insert, insert_src_offset + available, size - available
|
||
)
|
||
else:
|
||
block_copy(sl, sl_pos, bytes_to_insert, insert_src_offset, size)
|
||
sl_pos = sl_pos + size
|
||
return sl_pos
|
||
|
||
|
||
# Reads bytes from sliding window ring buffer
|
||
def read_sl(sl, sl_pos, out_buf, out_buf_pos, size):
|
||
available = 0x10000 - sl_pos
|
||
if available < size:
|
||
block_copy(out_buf, out_buf_pos, sl, sl_pos, available)
|
||
read_sl(sl, 0, out_buf, out_buf_pos + available, size - available)
|
||
else:
|
||
block_copy(out_buf, out_buf_pos, sl, sl_pos, size)
|
||
|
||
|
||
def read_lz(sl, sl_pos, out_buf, out_buf_pos, coded_offset, length):
|
||
actual_offset = coded_offset + 1
|
||
read_pos = (sl_pos + 0x10000 - actual_offset) % 0x10000
|
||
while actual_offset < length:
|
||
# Repeating sequence
|
||
read_sl(sl, read_pos, out_buf, out_buf_pos, actual_offset)
|
||
out_buf_pos += actual_offset
|
||
length -= actual_offset
|
||
# Copy
|
||
read_sl(sl, read_pos, out_buf, out_buf_pos, length)
|
||
|
||
|
||
def decompress(in_f, out_f, compressed_data_size):
|
||
sl = bytearray(0x10000)
|
||
lz_bytes = bytearray(128)
|
||
sl_pos = 0
|
||
chunk_size = 0
|
||
output_data = 0
|
||
while compressed_data_size > 0:
|
||
code_byte_0 = in_f.read(1)[0]
|
||
compressed_data_size -= 1
|
||
if code_byte_0 & 0x80:
|
||
# Literal
|
||
chunk_size = (code_byte_0 & 0x7F) + 1
|
||
output_data = in_f.read(chunk_size)
|
||
compressed_data_size -= chunk_size
|
||
elif code_byte_0 & 0x40:
|
||
# Large offset
|
||
code_bytes_12 = in_f.read(2)
|
||
compressed_data_size -= 2
|
||
chunk_size = (code_byte_0 & 0x3F) + 4
|
||
coded_offset = (code_bytes_12[0] << 8) + code_bytes_12[1]
|
||
read_lz(sl, sl_pos, lz_bytes, 0, coded_offset, chunk_size)
|
||
output_data = lz_bytes
|
||
else:
|
||
# Small offset
|
||
code_byte_1 = in_f.read(1)[0]
|
||
compressed_data_size -= 1
|
||
chunk_size = ((code_byte_0 & 0x3C) >> 2) + 3
|
||
coded_offset = ((code_byte_0 & 0x3) << 8) + code_byte_1
|
||
read_lz(sl, sl_pos, lz_bytes, 0, coded_offset, chunk_size)
|
||
output_data = lz_bytes
|
||
out_f.write(output_data[0:chunk_size])
|
||
sl_pos = insert_sl(sl, sl_pos, output_data, 0, chunk_size)
|
||
|
||
|
||
def create_macfonts(args: argparse.Namespace) -> int:
|
||
"""
|
||
Downloads System 7 image, extracts fonts from it and packs them
|
||
int classicmacfonts.dat
|
||
"""
|
||
print("Downloading System 7.0.1 image...", end="")
|
||
with urllib.request.urlopen(
|
||
"https://download.info.apple.com/Apple_Support_Area/Apple_Software_Updates/English-North_American/Macintosh/System/Older_System/System_7.0.x/System_7.0.1.smi.bin"
|
||
) as file:
|
||
output = file.read()
|
||
print("done")
|
||
|
||
datafork = BytesIO(macbin_get_datafork(output))
|
||
print("Decompressing...", end="")
|
||
datafork.seek(-0x200, 2)
|
||
alt_mdb_loc = datafork.tell()
|
||
datafork.seek(-(0x200 - 0x12), 2)
|
||
num_allocation_blocks, allocation_block_size, first_allocation_block = unpack(
|
||
">HI4xH", datafork.read(12)
|
||
)
|
||
compressed_data_start = first_allocation_block * allocation_block_size
|
||
compressed_data_end = alt_mdb_loc # ???
|
||
datafork.seek(0)
|
||
decdatafork = BytesIO()
|
||
decdatafork.write(datafork.read(compressed_data_start))
|
||
compressed_amount = compressed_data_end - compressed_data_start
|
||
decompress(datafork, decdatafork, compressed_amount)
|
||
datafork.seek(alt_mdb_loc)
|
||
decdatafork.write(datafork.read(0x200))
|
||
print("done")
|
||
|
||
decdatafork.seek(0)
|
||
vol = machfs.Volume()
|
||
vol.read(decdatafork.read())
|
||
for hpath, obj in vol.iter_paths():
|
||
if hpath == ("Fonts.image",):
|
||
fontsvol = obj.data[0x54:]
|
||
break
|
||
|
||
print("Reading Fonts.image...")
|
||
vol = machfs.Volume()
|
||
vol.read(fontsvol)
|
||
with zipfile.ZipFile(
|
||
"classicmacfonts.dat",
|
||
mode="w",
|
||
compression=zipfile.ZIP_DEFLATED,
|
||
compresslevel=9,
|
||
) as fontzip:
|
||
for hpath, obj in vol.iter_paths():
|
||
print(f"Compressing {hpath[-1]}...")
|
||
with io.BytesIO() as fonts_bytesio:
|
||
file_to_macbin(fonts_bytesio, obj, hpath[-1].encode("mac_roman"))
|
||
fontzip.writestr(f"{hpath[-1]}.bin", fonts_bytesio.getvalue())
|
||
|
||
print("Done")
|
||
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(
|
||
"--nopunycode", action="store_true", help="never encode pathnames into punycode"
|
||
)
|
||
parser_iso.add_argument(
|
||
"--japanese", action="store_true", help="read MacJapanese HFS"
|
||
)
|
||
parser_iso.add_argument(
|
||
"--dryrun", action="store_true", help="do not write any files"
|
||
)
|
||
parser_iso.add_argument(
|
||
"--log", metavar="LEVEL", help="set logging level", default="INFO"
|
||
)
|
||
parser_iso.add_argument(
|
||
"--forcemacbinary",
|
||
action="store_true",
|
||
help="always encode using MacBinary, even for files with no resource fork",
|
||
)
|
||
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",
|
||
default=True,
|
||
)
|
||
parser_macbinary.add_argument(
|
||
"--source-encoding",
|
||
metavar="source_encoding",
|
||
type=str,
|
||
help="encoding used for filenames in this path",
|
||
)
|
||
parser_macbinary.add_argument(
|
||
"--forcemacbinary",
|
||
action="store_true",
|
||
help="always encode using MacBinary, even for files with no resource fork",
|
||
default=False,
|
||
)
|
||
parser_macbinary.add_argument(
|
||
"dir", metavar="directory", type=Path, help="input directory"
|
||
)
|
||
parser_macbinary.set_defaults(func=collect_forks)
|
||
|
||
parser_macfonts = subparsers.add_parser(
|
||
"createmacfonts", help="Creates classicmacfonts.dat from Mac OS 7 system images"
|
||
)
|
||
parser_macfonts.set_defaults(func=create_macfonts)
|
||
|
||
return parser
|
||
|
||
|
||
if __name__ == "__main__":
|
||
parser = generate_parser()
|
||
args = parser.parse_args()
|
||
try:
|
||
f = args.func
|
||
except AttributeError:
|
||
parser.error("too few arguments")
|
||
exit(f(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™ 音色対応表",
|
||
],
|
||
[b"Asant\x8e", "Asanté"],
|
||
]
|
||
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", 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"],
|
||
["Hello*", "xn--Hello-la10a"],
|
||
["File I/O", "xn--File IO-oa82b"],
|
||
["HDにコピーして下さい。G3", "xn--HDG3-rw3c5o2dpa9kzb2170dd4tzyda5j4k"],
|
||
["Buried in Time™ Demo", "xn--Buried in Time Demo-eo0l"],
|
||
["•Main Menu", "xn--Main Menu-zd0e"],
|
||
["Spaceship Warlock™", "xn--Spaceship Warlock-306j"],
|
||
["ワロビージャックの大冒険<デモ>", "xn--baa0pja0512dela6bueub9gshf1k1a1rt742c060a2x4u"],
|
||
["Jönssonligan går på djupet.exe", "xn--Jnssonligan gr p djupet.exe-glcd70c"],
|
||
["Jönssonligan.exe", "xn--Jnssonligan.exe-8sb"],
|
||
["G3フォルダ", "xn--G3-3g4axdtexf"],
|
||
[
|
||
'Where \\ Do <you> Want / To: G* ? ;Unless=nowhere,or|"(everything)/":*|\\?%<>,;=',
|
||
"xn--Where Do you Want To G ;Unless=nowhere,or(everything),;=-5baedgdcbtamaaaaaaaaa99woa3wnnmb82aqb71ekb9g3c1f1cyb7bx6rfcv2pxa",
|
||
],
|
||
["Buried in Timeェ Demo", "xn--Buried in Time Demo-yp97h"],
|
||
["ぱそすけPPC", "xn--PPC-873bpbxa3l"],
|
||
["Madeline Pre-K\x7f Demo", "xn--Madeline Pre-K Demo-8a06x"],
|
||
]
|
||
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],
|
||
["Big[test]", False],
|
||
]
|
||
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
|