import os
import zipfile
import subprocess
import tempfile
# CFRのパス
cfr_jar_path="cfr-0.152.jar"
# 処理する拡張子のリスト
target_extensions = ['.MF', '.LIST', '.properties', '.xml', '.dot']
def collect_jar_tree(jar_path) -> str:
lines = []
with zipfile.ZipFile(jar_path, 'r') as jar:
entries = sorted(jar.namelist())
indent_stack = []
for entry in entries:
levels = entry.strip("/").split("/")
for i in range(len(indent_stack), len(levels) - 1):
indent_stack.append("│ ")
indent = "".join(indent_stack[:len(levels) - 1])
prefix = "├── " if len(indent_stack) > 0 else ""
lines.append(f"{indent}{prefix}{levels[-1]}")
if entry.endswith("/"):
indent_stack = indent_stack[:len(levels)]
else:
indent_stack = indent_stack[:len(levels) - 1]
return "\n".join(lines)
def decompile_jar_to_single_txt(jar_path, output_txt_path, cfr_jar_path):
# 1. ツリー構造取得
jar_tree_text = collect_jar_tree(jar_path)
with tempfile.TemporaryDirectory() as temp_dir:
with zipfile.ZipFile(jar_path, 'r') as jar:
jar.extractall(temp_dir)
jar_entries = jar.namelist()
decompiled_dir = os.path.join(temp_dir, "decompiled_java")
os.makedirs(decompiled_dir, exist_ok=True)
# 2. .class をデコンパイルして .java に保存
for root, _, files in os.walk(temp_dir):
for file in files:
if file.endswith(".class"):
class_path = os.path.abspath(os.path.join(root, file))
print(f"Decompiling: {file}")
result = subprocess.run(
["java", "-jar", cfr_jar_path, class_path, "--outputdir", decompiled_dir],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if result.returncode != 0:
print(f" Failed: {file}\n{result.stderr}")
# 3. 出力ファイルを開く(階層 + .java + その他ファイルの中身)
with open(output_txt_path, "w", encoding="utf-8") as out_file:
# ツリー構造を最初に書く
out_file.write(jar_tree_text)
out_file.write("\n\n")
# 各 .java の内容を追記
for root, _, files in os.walk(decompiled_dir):
for file in sorted(files):
if file.endswith(".java"):
java_path = os.path.join(root, file)
with open(java_path, "r", encoding="utf-8") as jf:
out_file.write("------------------------------\n")
out_file.write(f"{file}\n")
out_file.write("------------------------------\n")
out_file.write(jf.read())
out_file.write("\n\n")
# 4. .class 以外のファイルをJARから抽出して書き出す
for entry in jar_entries:
if entry.endswith("/") or entry.endswith(".class"):
continue # ディレクトリや.classは除外(すでに処理済み)
if not any(entry.endswith(ext) for ext in target_extensions):
continue
file_path = os.path.join(temp_dir, entry)
if os.path.isfile(file_path):
try:
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
out_file.write("------------------------------\n")
out_file.write(f"{entry}\n")
out_file.write("------------------------------\n")
out_file.write(content)
out_file.write("\n\n")
except Exception as e:
out_file.write("------------------------------\n")
out_file.write(f"{entry}\n")
out_file.write("------------------------------\n")
out_file.write(f" Could not read file: {e}\n\n")
def main():
# 現在の作業ディレクトリを取得
current_dir = os.getcwd()
# JARファイルの入力を促す
jar_path = input("JARファイルを入力してください: ")
# output.txtの初期化
output_file = os.path.join(current_dir, "output.txt")
with open(output_file, "w", encoding="utf-8") as file_output:
file_output.write("\n\n")
# JARファイルの存在チェック
if not os.path.isfile(jar_path):
with open(output_file, "w", encoding="utf-8") as file_output:
file_output.write("指定されたJARファイルは存在しません。\n")
print("指定されたJARファイルは存在しません。")
return
decompile_jar_to_single_txt(
jar_path=jar_path,
output_txt_path=output_file,
cfr_jar_path=cfr_jar_path
)
# cmdに表示
with open(output_file, "r", encoding="utf-8") as file_output:
contents = file_output.read()
print(contents)
# 入力待ち
input("続行するには何かキーを押してください...")
if __name__ == "__main__":
main()