unidecompiler-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: unidecompiler-cli
3
+ Version: 0.1.0
4
+ Summary: Command-line host for unidecompiler plugins
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: unidecompiler<0.2.0,>=0.1.0
8
+
9
+ # unidecompiler-cli
10
+
11
+ `unidecompiler-cli` is the command-line host for installed `unidecompiler`
12
+ frontend plugins. It discovers plugins through the `unidecompiler.frontends`
13
+ entry-point group and uses the public `DecompilerEngine` facade.
14
+
15
+ Install the CLI and one or more frontend packages:
16
+
17
+ ```sh
18
+ python -m pip install unidecompiler-cli unidecompiler-plugin-python-pyc
19
+ ```
20
+
21
+ Run `unidecompiler --help` for command-line usage.
@@ -0,0 +1,13 @@
1
+ # unidecompiler-cli
2
+
3
+ `unidecompiler-cli` is the command-line host for installed `unidecompiler`
4
+ frontend plugins. It discovers plugins through the `unidecompiler.frontends`
5
+ entry-point group and uses the public `DecompilerEngine` facade.
6
+
7
+ Install the CLI and one or more frontend packages:
8
+
9
+ ```sh
10
+ python -m pip install unidecompiler-cli unidecompiler-plugin-python-pyc
11
+ ```
12
+
13
+ Run `unidecompiler --help` for command-line usage.
@@ -0,0 +1,17 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "unidecompiler-cli"
7
+ version = "0.1.0"
8
+ description = "Command-line host for unidecompiler plugins"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = ["unidecompiler>=0.1.0,<0.2.0"]
12
+
13
+ [project.scripts]
14
+ unidecompiler = "unidecompiler_cli.cli:main"
15
+
16
+ [tool.setuptools.packages.find]
17
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """Command-line host for installed unidecompiler frontend plugins."""
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import base64
5
+ from dataclasses import fields, is_dataclass
6
+ import json
7
+ import math
8
+ import sys
9
+ from typing import Any
10
+ from pathlib import Path
11
+
12
+ from unidecompiler.backends.pseudocode import GenericPseudocodeBackend
13
+ from unidecompiler.core.astify import module_to_ast
14
+ from unidecompiler.plugin_registry import FrontendRegistry, FrontendSelectionError
15
+ from unidecompiler.plugins import FrontendDecodeError
16
+ from unidecompiler.input_sources import expand_input_path
17
+
18
+
19
+ def main(argv: list[str] | None = None, *, registry: FrontendRegistry | None = None) -> int:
20
+ parser = argparse.ArgumentParser(
21
+ prog="unidecompiler",
22
+ description="Decompile bytecode into generic pseudocode.",
23
+ )
24
+ parser.add_argument("input", nargs="?", help="input bytecode file")
25
+ parser.add_argument(
26
+ "--frontend",
27
+ help="explicit frontend plugin id, e.g. lua",
28
+ default=None,
29
+ )
30
+ parser.add_argument(
31
+ "--versions",
32
+ action="store_true",
33
+ help="print supported frontend version matrix",
34
+ )
35
+ parser.add_argument(
36
+ "--format",
37
+ choices=("pseudocode", "ast-json"),
38
+ default="pseudocode",
39
+ help="output format (default: pseudocode)",
40
+ )
41
+ args = parser.parse_args(argv)
42
+
43
+ registry = registry or FrontendRegistry.discover()
44
+ if args.versions:
45
+ for plugin, support in registry.version_support():
46
+ versions = ", ".join(support.versions)
47
+ print(
48
+ f"{plugin.id}: {support.family} | {versions} | "
49
+ f"{support.status} | parser: {support.parser}"
50
+ )
51
+ return 0
52
+
53
+ if args.input is None:
54
+ parser.error("input is required unless --versions is used")
55
+ input_path = Path(args.input)
56
+ artifacts = expand_input_path(input_path)
57
+ if not artifacts:
58
+ raise SystemExit(f"no input files found in {input_path}")
59
+
60
+ backend = GenericPseudocodeBackend() if args.format == "pseudocode" else None
61
+ ast_modules: list[dict[str, Any]] = []
62
+ processed = 0
63
+ for artifact in artifacts:
64
+ if artifact.kind == "resource":
65
+ print(f"resource: {artifact.display_path}", file=sys.stderr)
66
+ continue
67
+ try:
68
+ frontend = registry.select(
69
+ artifact.data, artifact.display_path, explicit_id=args.frontend
70
+ )
71
+ except FrontendSelectionError:
72
+ print(f"resource: {artifact.display_path}", file=sys.stderr)
73
+ continue
74
+ try:
75
+ decoded = frontend.decode(artifact.data, artifact.display_path)
76
+ except FrontendDecodeError:
77
+ print(f"resource: {artifact.display_path}", file=sys.stderr)
78
+ continue
79
+ try:
80
+ module = frontend.lift(decoded)
81
+ if args.format == "ast-json":
82
+ ast_modules.append(_json_value(module_to_ast(module)))
83
+ else:
84
+ assert backend is not None
85
+ emitted = backend.emit(module)
86
+ print(emitted.text)
87
+ except Exception as error:
88
+ print(
89
+ f"error: {artifact.display_path}: {type(error).__name__}: {error}",
90
+ file=sys.stderr,
91
+ )
92
+ continue
93
+ processed += 1
94
+
95
+ if processed == 0:
96
+ raise SystemExit(f"no supported input files found in {input_path}")
97
+ if args.format == "ast-json":
98
+ print(
99
+ json.dumps(
100
+ {"schema_version": 1, "modules": ast_modules},
101
+ ensure_ascii=False,
102
+ sort_keys=True,
103
+ allow_nan=False,
104
+ )
105
+ )
106
+ return 0
107
+
108
+
109
+ def _json_value(value: Any) -> Any:
110
+ """Convert the generic AST dataclasses into a JSON-compatible tree."""
111
+ if is_dataclass(value):
112
+ return {
113
+ "node_type": type(value).__name__,
114
+ **{field.name: _json_value(getattr(value, field.name)) for field in fields(value)},
115
+ }
116
+ if isinstance(value, bytes | bytearray | memoryview):
117
+ return {
118
+ "value_type": "bytes",
119
+ "base64": base64.b64encode(bytes(value)).decode("ascii"),
120
+ }
121
+ if isinstance(value, complex):
122
+ return {"value_type": "complex", "real": value.real, "imag": value.imag}
123
+ if isinstance(value, float) and not math.isfinite(value):
124
+ return {"value_type": "float", "value": repr(value)}
125
+ if isinstance(value, tuple | list):
126
+ return [_json_value(item) for item in value]
127
+ if isinstance(value, dict):
128
+ if all(isinstance(key, str) for key in value):
129
+ return {key: _json_value(item) for key, item in value.items()}
130
+ return {
131
+ "value_type": "map",
132
+ "entries": [[_json_value(key), _json_value(item)] for key, item in value.items()],
133
+ }
134
+ if isinstance(value, set | frozenset):
135
+ return {"value_type": "set", "items": [_json_value(item) for item in sorted(value, key=repr)]}
136
+ if value is None or isinstance(value, str | int | float | bool):
137
+ return value
138
+ raise TypeError(f"AST JSON cannot encode {type(value).__name__}")
139
+
140
+
141
+ if __name__ == "__main__":
142
+ raise SystemExit(main())
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ """Compatibility import for the library-owned input expansion API."""
4
+ from unidecompiler.input_sources import ARCHIVE_SUFFIXES, InputArtifact, expand_input_path, iter_input_path
5
+
6
+ __all__ = ("ARCHIVE_SUFFIXES", "InputArtifact", "expand_input_path", "iter_input_path")
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: unidecompiler-cli
3
+ Version: 0.1.0
4
+ Summary: Command-line host for unidecompiler plugins
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: unidecompiler<0.2.0,>=0.1.0
8
+
9
+ # unidecompiler-cli
10
+
11
+ `unidecompiler-cli` is the command-line host for installed `unidecompiler`
12
+ frontend plugins. It discovers plugins through the `unidecompiler.frontends`
13
+ entry-point group and uses the public `DecompilerEngine` facade.
14
+
15
+ Install the CLI and one or more frontend packages:
16
+
17
+ ```sh
18
+ python -m pip install unidecompiler-cli unidecompiler-plugin-python-pyc
19
+ ```
20
+
21
+ Run `unidecompiler --help` for command-line usage.
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/unidecompiler_cli/__init__.py
4
+ src/unidecompiler_cli/cli.py
5
+ src/unidecompiler_cli/input_sources.py
6
+ src/unidecompiler_cli.egg-info/PKG-INFO
7
+ src/unidecompiler_cli.egg-info/SOURCES.txt
8
+ src/unidecompiler_cli.egg-info/dependency_links.txt
9
+ src/unidecompiler_cli.egg-info/entry_points.txt
10
+ src/unidecompiler_cli.egg-info/requires.txt
11
+ src/unidecompiler_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ unidecompiler = unidecompiler_cli.cli:main
@@ -0,0 +1 @@
1
+ unidecompiler<0.2.0,>=0.1.0
@@ -0,0 +1 @@
1
+ unidecompiler_cli