resand 0.3.0

Read and write ARSC and AXML binary files used for Android Resources
Documentation
# Resand

Resand (RESesource ANDroid) is a library for android resource reading and writing. It can read and
write AXML (ResXML) and ARSC (ResTable) files.

The purpose of this crate is that I wanted to be able to change the app name and package name of
an apk without including a large dependency such as apktool, or aapt, or any other similar external
tool.

At the moment it cannot change resource data into XML and XML into resource data, but I am working
on adding that, so at the moment all modifications to resource data have to be done directly in Rust.

There is not much documentation yet as this was mainly something I needed for one of my other
projects.

The data structures in the code are based off of the
[Android Source Code](https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h)

## Usage

```sh
cargo add resand
```

The following examples use .unwrap(), but obviously you should handle errors properly.

Basic reading and writing of and xml tree:

```rust
use resand::xmltree::XMLTree;
use std::fs::File;

let mut file = File::open("./AndroidManifest.xml").unwrap();
let tree = XMLTree::read(&mut file).unwrap();

dbg!(&tree);

let mut output = File::create("./NewAndroidManifest.xml").unwrap();

tree.write(&mut output).unwrap();
```

Basic reading and writing of a resource table:

```rust
use resand::table::ResTable
use std::fs::File;

let mut file = File::open("./resources.arsc").unwrap();
let table = ResTable::read_all(&mut file).unwrap();

dbg!(&table);

let mut output = File::create("./new_resources.arsc").unwrap();

table.write_all(&mut output).unwrap();
```

Setting the app name:

```rust
let xml = XMLTree::read(...);
let mut table = ResTable::read_all(...)

let application = xml
    .root
    .get_elements(&["manifest", "application"], &xml.string_pool)
    .pop()
    .unwrap();

let attr = application
    .get_attribute("label", &xml.string_pool).unwrap();

if let ResValueType::Reference(attr) = attr.typed_value.data {
    let package = table.packages.first_mut().unwrap();
    let entry = package
        .resolve_ref_mut(attr).unwrap();
    if let ResTableEntryValue::ResValue(value) = &mut entry.data {
        value
            .data
            .write_string("New Name".to_string(), &mut table.string_pool);
    }
}

table.write_all(...)
```

Setting the package name:

```rust

let mut xml = (...)
let mut table = (...)

let package = table.packages.first_mut().unwrap();
package.name = "new.package.name".to_string();

let root_element = xml
    .root
    .get_elements_mut(&["manifest"], &xml.string_pool)
    .pop().unwrap();

let attr = root_element
    .get_attribute_mut("package", &xml.string_pool).unwrap();

attr.write_string("new.package.name".to_string(), &mut xml.string_pool);

// you might need to modify other parts of the manifest if they also refer to the package name

xml.write(...);
table.write_all(...);

```

### Using the derive feature

```shell
cargo add resand --features derive
```

#### Setting the package name

```rust
use std::path::Path;

use resand::{
    manifest::AndroidManifest,
    traits::{FromNode, IntoNode},
    xmltree::XMLTree,
};

fn main() {
    let tree = XMLTree::from_path(Path::new(
        "path/to/AndroidManifest.xml",
    ))
    .unwrap();
    let mut manifest = AndroidManifest::from_tree(tree).unwrap();

    manifest.package = "com.foo.bar".to_string();

    let mut output_buf = std::io::Cursor::new(Vec::new());

    let tree = manifest.into_tree();

    tree.write(&mut output_buf).unwrap();
}
```

#### Creating a new binary XML file

Target:

```xml
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>
```

Code:

```rust
fn main() {
use resand::traits::IntoNode;
use resand_derive::{FromNode, IntoNode};

#[derive(Debug, IntoNode, FromNode)]
pub struct Certificate {
    #[resand(attr)]
    pub src: String,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct TrustAnchor {
    #[resand(child, many)]
    pub certificates: Vec<Certificate>,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct DebugOverride {
    #[resand(child, name = "trust-anchors")] // you can rename stuff like this
    pub trust_anchors: TrustAnchor,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct BaseConfig {
    #[resand(attr)]
    pub cleartext_traffic_permitted: bool,
    #[resand(child)]
    pub trust_anchors: TrustAnchor,
}

#[derive(Debug, IntoNode, FromNode)]
#[resand(name = "network-security-config")]
pub struct NetworkSecurityConfig {
    #[resand(child)]
    pub debug_overrides: DebugOverride,
    #[resand(child)]
    pub base_config: BaseConfig,
}

fn main() {
    let tree = NetworkSecurityConfig {
        debug_overrides: DebugOverride {
            trust_anchors: TrustAnchor {
                certificates: vec![Certificate {
                    src: "user".to_string(),
                }],
            },
        },
        base_config: BaseConfig {
            cleartext_traffic_permitted: true,
            trust_anchors: TrustAnchor {
                certificates: vec![
                    Certificate {
                        src: "system".to_string(),
                    },
                    Certificate {
                        src: "user".to_string(),
                    },
                ],
            },
        },
    }
    .into_tree();

    let mut file = std::fs::File::create("path/to/network_security_config.xml").unwrap();
    tree.write(&mut file).unwrap();
}
```

## License

Resand is licensed under the Apache-2.0 license which can be read [here](./LICENSE)