#!/usr/bin/python3

# require zip and apktool installed

import sys
import os
import tempfile
from pathlib import Path
import json


def parse_smali(path: Path) -> tuple[str, dict, dict]:
    class_name: str | None = None
    fields = {}
    methods = {}
    with path.open() as file:
        for line in file.readlines():
            if line.startswith(".class"):
                if class_name is None:
                    class_name = line.strip().split(" ")[-1]
                else:
                    raise RuntimeError(f"Two classes found in {path}")
            if line.startswith(".field"):
                k, v = parse_field(line)
                fields[k] = v
            elif line.startswith(".method"):
                k, v = parse_method(line)
                methods[k] = v
    if class_name is None:
        raise RuntimeError(f"No classe found in {path}")
    return (class_name, fields, methods)


def parse_field(line: str) -> tuple[str, dict]:
    data: dict[str, None | str | list[str]] = {
        "value": None,
        "other": [],
        "hiddenapi": None,
        "hiddenapi_domain": None,
    }
    if "=" in line:
        line, val = line.split("=")[0], "=".join(line.split("=")[1:])
        data["value"] = val.strip()
    line, ty = line.split(":")
    vals = list(map(str.strip, line.split(" ")))
    name = f"{vals[-1]}:{ty.strip()}"
    vals = vals[:-1]
    if vals:
        if "api" in vals[-1]:
            data["hiddenapi_domain"] = vals[-1]
            vals = vals[:-1]
    if vals:
        if "list" in vals[-1]:
            data["hiddenapi"] = vals[-1]
            vals = vals[:-1]
    data["other"] = vals
    return name, data


def parse_method(line: str) -> tuple[str, dict]:
    data: dict[str, None | str | list[str]] = {
        "other": [],
        "hiddenapi": None,
        "hiddenapi_domain": None,
    }
    vals = list(map(str.strip, line.split(" ")))
    name = vals[-1]
    vals = vals[:-1]
    if vals:
        if "api" in vals[-1]:
            data["hiddenapi_domain"] = vals[-1]
            vals = vals[:-1]
    if vals:
        if "list" in vals[-1]:
            data["hiddenapi"] = vals[-1]
            vals = vals[:-1]
    data["other"] = vals
    return name, data


def main():
    if len(sys.argv) != 3:
        print("rought smali parser")
        print("usage:")
        print(f"    {sys.argv[0]} some_classes.dex output.json")
        exit()
    file = sys.argv[1]
    dst = sys.argv[2]
    data = {}
    with tempfile.TemporaryDirectory() as tmp:
        os.system(f"zip {tmp}/apk.apk {file}")
        os.system(f"apktool d -o {tmp}/apktool.out {tmp}/apk.apk")
        smalidir = Path(f"{tmp}/apktool.out/smali_{file.removesuffix('.dex')}")
        for root, dirs, files in smalidir.walk():
            for file in files:
                class_name, fields, methods = parse_smali(root / file)
                data[class_name] = {"fields": fields, "methods": methods}
    with open(dst, "w") as file:
        json.dump(data, file)


if __name__ == "__main__":
    main()
