# README


## GENERAL INFO


  Project: Library to parse and modify AXML files (Rust implementation)
  Author: Benoît Forgette alias MadSquirrel
  License: GNU General Public License v3.0 and later

## GOAL

  pyaxml-rs is a full Rust rewrite of PyAxml to parse and write AXML (Android XML)
  binary files. It exposes the same functionality as the original Python library
  through three interfaces:

  - A native Rust library crate (`pyaxml_rs`) for use in Rust projects.
  - A Python package (`pyaxml`) built with [maturin](https://github.com/PyO3/maturin)
    and [PyO3](https://github.com/PyO3/pyo3), providing the same API as the original
    `pyaxml` Python package.
  - Two CLI entry-points with identical commands:
    - **`pyaxml-rs`**: native Rust binary, no Python required.

  **Runtime dependencies (minimal by design):**
  - `quick-xml`: XML parsing for the `xml2axml` direction
  - `zip`: APK/ZIP file support in the CLI
  - `pyo3`: (optional, only when building the Python extension)

PyAxml is a Rust tool with python wrapper to play with AXML (Android XML) files.
This tool is useful to decode and encode AXML files and manipulate AXML files
over a [Protocol Buffers](https://protobuf.dev/) object.
It is designed to work with Python 3 only.
Some examples of the use of the tool is provided with the project:
- copymanifest.py that allow to decode and reencode the original file
- replace_activity_name.py to change the name an activity and replace by an
other

If you want to see more example you can dig more on the project
[apkpatcher](https://gitlab.com/MadSquirrels/mobile/apkpatcher) that use this
library to add some useful permission and inject a library inside the target
application.

## BUILD & INSTALL

### Prerequisites

  - Rust toolchain (`rustup`): https://rustup.rs/
  - Python 3.8+ with a virtual environment (for the Python extension)

### Build the Python extension

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop           # debug build (fast)
maturin develop --release # release build
```

This installs both the `pyaxml` Python package and the `pyaxml-rs` CLI entry-point.

### Build the Rust CLI binary

```bash
cargo build --release
# Binary at: target/release/pyaxml-rs
```

### Install the Rust CLI system-wide

```bash
cargo install --path .
```

Or use the Makefile:

```bash
make build     # compile release binary
make install   # cargo install
```

## CLI USAGE

```
pyaxml-rs [-h] [-i INPUT] [-o OUTPUT] [-p PATH] [-v]
          [--stringblocks-file STRINGBLOCKS_FILE]
          {axml2xml,xml2axml,arsc2xml,axml2proto}
```

### Commands

| Command | Description |
|---------|-------------|
| `axml2xml` | Decode a binary AXML file (or AndroidManifest.xml from an APK) to plain XML |
| `xml2axml` | Encode a plain XML file back to binary AXML |
| `arsc2xml` | Parse a resources.arsc file to XML grouped by locale |
| `arsc2proto` | Decode a resources.arsc file to protobuf text format |
| `axml2proto` | Decode a binary AXML file to protobuf text format |

### Options

| Flag | Description |
|------|-------------|
| `-i FILE` / `--input FILE` | Input file (AXML, XML, or APK/ZIP) |
| `-o FILE` / `--output FILE` | Output file (default: stdout) |
| `-p PATH` / `--path PATH` | Path of a specific file inside a ZIP/APK |
| `-v` / `--version` | Print version |
| `--stringblocks-file FILE` | Export or import the string pool as JSON (`axml2xml` / `xml2axml`) |
| `--pretty` | Pretty-print protobuf output (`arsc2proto` / `axml2proto`) |

### Examples

```bash
# Decode AndroidManifest.xml from a binary AXML file
pyaxml-rs axml2xml -i AndroidManifest.xml

# Decode from an APK
pyaxml-rs axml2xml -i app.apk -o manifest.xml

# Decode a specific file from an APK
pyaxml-rs axml2xml -i app.apk -p res/xml/network_security_config.xml

# Encode XML back to binary AXML
pyaxml-rs xml2axml -i manifest.xml -o AndroidManifest.xml

# Dump the intermediate protobuf text representation
pyaxml-rs axml2proto -i AndroidManifest.xml

# Export string pool alongside the XML
pyaxml-rs axml2xml -i AndroidManifest.xml --stringblocks-file strings.json

# Re-encode using a previously exported string pool
pyaxml-rs xml2axml -i manifest.xml -o out.axml --stringblocks-file strings.json

# Same commands work with the Python CLI
pyaxml axml2xml -i AndroidManifest.xml
```

## PYTHON API

```python
import pyaxml

# Parse binary AXML
with open("AndroidManifest.xml", "rb") as f:
    data = f.read()

axml = pyaxml.AXML.from_axml(data)

# Convert to an XML Element (lxml or stdlib ET)
element = axml.to_xml()

# Re-pack to binary
binary = axml.pack()

# Build from XML
axml2 = pyaxml.AXML()
axml2.from_xml(element)
binary2 = axml2.pack()

# String pool access
print(axml.string_count())
print(axml.get_string(0))

# Parse binary ARSC
arsc = pyaxml.ARSC.from_axml(data)
xml_str = arsc.list_packages()

# Auto-detect AXML vs ARSC
obj = pyaxml.AXMLGuess.from_axml(data)
```

## TESTS

```bash
# Rust integration tests
cargo test

# Python tests (requires maturin develop first)
python3 -m pytest tests-py/ -v
```

Or use the Makefile:

```bash
make test       # both Rust and Python tests
make test-rs    # Rust only
make test-py    # Python only
```

## MAKEFILE TARGETS

| Target | Description |
|--------|-------------|
| `make build` | Compile the release binary (`pyaxml-rs`) |
| `make develop` | Build and install the Python extension (maturin develop) |
| `make lint` | Run all linters (Rust + Python) |
| `make test` | Run all tests (Rust + Python) |
| `make test-rs` | Run Rust integration tests only |
| `make test-py` | Run Python tests only |
| `make install` | Install `pyaxml-rs` binary via cargo install |
| `make bump VERSION=x.y.z` | Bump the version in Cargo.toml |
| `make clean` | Remove build artefacts |
| `make help` | Show available targets |
