# CLI reference
[English] | [简体中文](zh-CN/cli.md)
Everything `ddc --help` tells you, in more detail: invocation modes,
every option's semantics, every subcommand's output format and behavior.
The binary's help is the quick card; this is the manual.
Two invocation modes:
```
ddc [OPTIONS] ... [OUTPUT] # full decompile
ddc [ARGS...] # progressive analysis (query)
```
## Language
Messages (help, errors, summaries, table headers) localize automatically:
`DDC_LANG` (explicit `zh`/`en`) overrides `LC_ALL` > `LC_MESSAGES` > `LANG`;
any `zh*` value selects Chinese, everything else falls back to English.
```bash
DDC_LANG=zh ddc --help # 中文帮助
LANG=zh_CN.UTF-8 ddc -V # also Chinese
DDC_LANG=en ddc -V # forced English even under a zh locale
```
## Full decompile
`ddc [OPTIONS] ... [OUTPUT]`
**Inputs** (multiple merge into one class pool; duplicate classes are
deduplicated, first definition wins):
- `.dex` — versions 035–041
- `.apk` / `.jar` / `.zip` — every `classes.dex`, `classes2.dex`, … entry,
merged in numeric order
- `.xapk` / `.apks` / `.apkm` — a zip of APKs: every inner APK's dexes
merge, base first; labels are three-level (`container!base.apk!
classes.dex`)
- a directory — scanned recursively for all of the above
**Output** — the last positional argument or `-o`:
- `` — output root, package structure preserved; default
`-out/` next to the input
- `` — a single class only (single-class input or `-c`)
- `-` — stdout, classes separated by `// ===== class =====` lines, in pool
order (forces one thread)
Positional-argument disambiguation: with no `-o` and two or more
positionals, a **last** argument that isn't input-shaped (no dex-bearing
extension, not an existing file, not a directory containing dex files) is
the output — so `ddc app.apk out/` writes into the pre-created `out/`,
while `ddc a.dex dump/` treats a real dex directory as an input.
| Option | Semantics |
|---|---|
| `-o, --output ` | output location: dir / `file.java` / `-` |
| `-c, --class FQCN` | decompile only this class (dotted or slashed; nested/anonymous/local classes of it come along) |
| `-l, --list` | list class names and exit |
| `-t, --threads ` | worker count (default: CPU count; stdout forces one for pool order) |
| `--no-comments` | omit the provenance header |
| `-v, --verbose` | per-dex stats and slow classes on stderr |
| `-h, --help` / `-V, --version` | help / name+version+homepage |
`--opt=value` and `-o=path` forms both work. Bare `ddc help` / `ddc
version` do the same as `-h` / `-V`.
**Provenance header** (unless `--no-comments`):
```java
// Decompiled by https://github.com/ejfkdev/ddc 1.2.3
// From: app!classes3.dex (DEX 038)
// Source file: Foo.java
```
`From:` names the input file, the dex image, and the DEX version — the
fastest clue for which image a class lives in (jadx's `loaded from:`
pattern). Headers carry no timestamps, so two runs diff cleanly; a few
variable IDs may still differ between runs (std HashMap seed), with
identical semantics.
**Exit codes**: `0` success; `1` some classes failed (e.g. a
pathological-CFG timeout); `2` usage error (the error, then the full
help). A one-line summary goes to stderr when a run finishes: `ddc:
wrote 98348 file(s) to out/, 1 failed in 6.13s`.
## Progressive-analysis subcommands
All of them accept `-d/--dex NAME` (repeatable; entry-name substring —
the filter runs before parsing, so `getclass --dex classes20` parses
exactly one image) and most accept `-o FILE` to write the result.
Queries never enter the lift/structure/render pipeline; stdout stays
clean (timing prints only with `-o`).
### Get oriented
- **`ddc info `** — one command, the whole picture. A context
header first (when a manifest exists): app label — an `@0x…` ref is
resolved through a minimal resources.arsc walk, literals print
as-is — package, `versionName (versionCode)`, custom Application
class, launcher activity, `uses-sdk` bounds, file size and MD5 (the
label lookup mirrors the manifest's base-first container rule). Then
the per-dex table: one row per image with version, class, method,
field, string counts, plus a total row. Bare `.dex` inputs skip the
header (no manifest) and print the table only.
- **`ddc listclasses [pattern]`** — class names (internal
`com/foo/Bar` form); pattern is a case-insensitive substring.
- **`ddc manifest [--component C] [-o FILE]`** — decodes the
binary AndroidManifest.xml to text XML. `--component` filters to one
element kind: `launcher` (the MAIN/LAUNCHER activity), `activity`,
`service`, `receiver`, `provider`, `permission`, `activity-alias`,
`application`. Raw `.axml` files are accepted directly. In XAPK/APKS
containers the base APK's manifest is used.
- **`ddc mainactivity `** — package, custom Application class (if
any), and the launcher activity, resolved through relative-name rules
(`.MainActivity` → package-prefixed; bare word → package + word) and
activity-alias `targetActivity`; then verified against the dex images
(the defining image is reported).
- **`ddc res [entry] [-o FILE]`** — without an entry: every archive
entry (method, compressed size; XAPK inner APKs flattened into
`apk!name` labels). With an entry: dumps it — binary XML (first chunk
`0x0003`) decodes through the AXML decoder, text prints as-is, binary
content is saved via `-o` (or the error tells you to). Entry matching:
exact name first, then a unique substring.
### Find things
- **`ddc strings [-f TEXT] [--with-locations]`** — the string
table (one row per string). `-f` filters by substring;
`--with-locations` walks every method's const-string sites and adds a
`used-by` column mapping each hit to its owner methods.
- **`ddc findrefs [--class
FQCN] [--fuzzy-class] [-o FILE]`** — every reference to a string
literal / type / method call site / field access. Output is columnar
with a header (`dex kind class method refs`), **one row per method**:
multiple hits aggregate into `refs` (`; `-separated, deduped); `kind`
is the first hit's instruction. Match semantics: queries are
case-insensitive substrings; `--class` defaults to exact (dots,
slashes and `L…;` descriptor forms all normalize) and `--fuzzy-class`
widens it to substring.
- **`ddc callers NAME [FQCN]`** — who invokes method NAME (the
findrefs method machinery, scoped to one class optionally).
- **`ddc members [NAME] [--class FQCN] [--fuzzy-class]
[--method|--field]`** — method/field name search over the method and
field id tables; `--method` / `--field` restrict the kind.
### Understand structure
- **`ddc hierarchy FQCN`** — the class's lineage: `class` /
`extends` / `implements` forward, `sub` / `impl` for every class that
extends or implements it. Works across images (name-matched when the
parent lives in another dex).
- **`ddc largest [-n N]`** — top-N methods by instruction count
(find the monsters; default 20).
- **`ddc disasm FQCN[.method]`** — raw bytecode of a class or
one method: one line per instruction (`pc opcode mnemonic`). The
target resolves as a whole-string class first, then splits at the last
dot — so both `org.foo.Cells.t1` (a class) and `Greeter.greet` (a
method) work.
### Decompile surgically
- **`ddc getclass FQCN [-o FILE] [--dex NAME]`** — one class
with its nested/anonymous/local classes, through the full pipeline.
Ambiguous names (defined in several images) get a warning listing the
images; the defining image is registered first so the pool resolves
from it.
- **`ddc getmethod FQCN[.method] [-o FILE]`** — one method,
sliced out of the decompiled class: provenance header + package line +
every matching overload, dedented. A miss lists the class's method
names. A bare class name falls back to the whole class.
- **`ddc pkg PACKAGE [-o DIR] [-t N] [--app]`** — decompile a
whole package subtree through the full pipeline (default output:
`-pkg/` next to the input). `""` or `.`
means the root (default package included). `--app` takes the package
from the manifest — and when that package has no classes (Telegram:
manifest says `org.telegram.messenger.web`, code lives in
`org.telegram.messenger`) it retries with the launcher class's
package, which is where an app's own code clusters.
## Output quality passes
Four passes clean the decompiled body, all on by default:
- **jadx-style local names** — a synthetic `v12`/`p3` never survives
when a better name exists: a Kotlin
`Intrinsics.checkNotNullParameter(x, "name")` names `x` from the
message string (the compiler wrote the real parameter name into the
check); the single consistent defining call (`getFoo() → foo`,
`new File(…) → file`); jadx's type-alias table (`str/cls/it/…`)
with the lowercased class simple name as fallback. Collisions take
`2, 3, …`; debug-info names are never touched.
- **Kotlin null-check elision** — statement-position
`Intrinsics.checkNotNull…` calls are runtime assertions; they are
dropped after the naming pass harvested their strings (lark:
59,106 → 9).
- **Synthetic-accessor inlining** — `access$NNN` static bridges inline
at call sites when the body is an identity, a field getter, or a
method forwarder (the d8 APM trace wrappers around the core are
tolerated). Only STATIC+SYNTHETIC callees qualify. lark: 43% of
9,214 call sites.
- **IntDef constant rendering** — see below.
## Platform symbols
ddc renders IntDef/LongDef literal arguments as their constant names
out of the box (`setVisibility(8)` → `android.view.View.GONE`). The
domain table (android-37) lives in the repo as a READABLE,
diffable text file — `crates/ddc-cli/src/platform_symbols.txt`, one
domain per line — which build.rs raw-DEFLATEs into the binary (72KB).
Regenerate with `scripts/gen-platform-symbols.sh [platform-dir]` and
commit the .txt. The table is exact-match — combined flag values stay
numeric — and version-independent in effect: methods missing from the
baked API level simply stay numeric. Startup cost is under 5ms.
`--symbols ` (e.g.
`~/Library/Android/sdk/platforms/android-37.0`, needs `android.jar`
and `data/annotations.zip`) rebuilds the table from that platform for
this invocation, overriding the built-in — full decompile and
subcommands alike.
## Exit codes for subcommands
Usage errors (missing arguments, unknown options, bad `--dex`) print the
error plus the full help and exit `2`. Query misses (class/method/entry
not found) are also `2` but carry the specific miss in the error —
`getmethod` lists the available methods, `--dex` the available images.