https://github.com/scummvm/scummvm
Raw File
Tip revision: 1d1f34d39441a25dc570a8ab38372739e12f51bc authored by Orgad Shaneh on 05 March 2022, 21:22:14 UTC
CI: Use ccache also for MSVC build
Tip revision: 1d1f34d
dumper-companion.py
#!/usr/bin/env python3
#
# 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 unicodedata

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, "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 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:
                print(f"WARNING: Mac Japanese sequence missing second byte 0x{hi:02x}")
                return text.decode('mac-roman')
            hi_key = f'{hi:02x}'
            lo_key = lo - 0x40
            if lo_key < 0:
                print(f"WARNING: second byte out of range 0x{lo:02x}")
                return text.decode('mac-roman')
            elif decode_map.get(hi_key) is None or decode_map[hi_key][lo_key] is None:
                print(f"WARNING: No mapping for MacJapanese sequence 0x{hi_key}{lo:02x}")
                return text.decode('mac-roman')
            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(f: machfs.File, name: ByteString) -> bytes:
    oldFlags = f.flags >> 8
    newFlags = f.flags & 0xFF
    macbin = 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 += 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():
        # Encode the path
        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

        # Write the file to disk
        if isinstance(obj, machfs.Folder):
            upath.mkdir(exist_ok=True)
            # Set the modified time for folders
            os.utime(upath, (obj.mddate - 2082844800, obj.mddate - 2082844800))
        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))
            # This needs to be done after writing files as writing files resets
            # the parent folder's modified time that was set before
            parent_folder_modtime = vol.get(hpath[:-1]).mddate - 2082844800
            os.utime(upath.parent, (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:
                print(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))
    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
    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 + 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: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))

                    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)

    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(
            "--source-encoding",
            metavar="source_encoding",
            type=str,
            help="encoding used for filenames in this path",
        )
        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()
    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", 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"],
        ["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"]
    ]
    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
back to top