91 lines
3.0 KiB
Markdown
91 lines
3.0 KiB
Markdown
# AndroidXML
|
||
|
||
Pure Swift library for parsing binary XML files such as found in Android apk files.
|
||
|
||
|
||
## About
|
||
|
||
Written from scratch and closely following the original C++ source code ([ResourceTypes.h], [ResourceTypes.cpp]).
|
||
Names have been adapted to a more Swiftly syntax.
|
||
Import of `Foundation` is optional which should makes the library cross-platform compatible (though I have not tested other than macOS).
|
||
|
||
Since the AXML format is not documented anywhere – apart from the source code – you cannot rely on the structure to work forever.
|
||
Especially, pay attention to other binary XML formats which have [been spotted](https://www.cclsolutionsgroup.com/post/android-abx-binary-xml) on Android.
|
||
I do not think ABX is currently used for `.apk` files, only system files.
|
||
However, If that should change I would need to make some adjustments to this library.
|
||
|
||
|
||
## Example Usage
|
||
|
||
### Automatic XML String Conversion
|
||
|
||
```swift
|
||
let parser = try AndroidXML(path: "some/AndroidManifest.xml").parseXml()
|
||
print(try parser.xmlString())
|
||
```
|
||
|
||
### Manual XML Parser
|
||
|
||
```swift
|
||
let xml = try! AndroidXML(data: [...]).parseXml()
|
||
try xml.iterElements({ startTag, attributes in
|
||
print("<\(startTag)>")
|
||
for (name, val) in attributes {
|
||
print("\t", name, "=", val.resolve(xml.stringPool))
|
||
}
|
||
}) { endTag in
|
||
print("</\(endTag)>")
|
||
}
|
||
```
|
||
|
||
### Resource Table Lookup
|
||
|
||
```swift
|
||
let lookupId = 0x7F100000
|
||
let xml = try! AndroidXML(path: "some/resources.arsc").parseTable()
|
||
let res = try! xml.getResource(TblTableRef(lookupId))!
|
||
let pkgPool = res.package.stringPool(for: .Keys)!
|
||
for x in res.entries {
|
||
let val = x.entry.value!
|
||
print(pkgPool.getString(x.entry.key), val.resolve(xml.stringPool), x.config.screenType.density)
|
||
}
|
||
```
|
||
|
||
### Manual Table Parsing
|
||
|
||
```swift
|
||
let xml = try AndroidXML(path: "some/resources.arsc").parseTable()
|
||
for pkg in try xml.getPackages() {
|
||
let pool = pkg.stringPool(for: .Keys)!
|
||
try pkg.iterTypes { spec, types in
|
||
//print("----- \(spec.id) ------")
|
||
for type in types {
|
||
try type.iterValues { idx, entry in
|
||
if entry.isComplex {
|
||
//print("::.::.complex", pool.getString(entry.key), entry.valueMap!.parent.asHex)
|
||
for x in entry.valueMap!.entries {
|
||
let _ = x.name.asHex
|
||
let _ = x.value.resolve(xml.stringPool)
|
||
}
|
||
} else {
|
||
let _ = pool.getString(entry.key)
|
||
let _ = entry.value!.resolve(xml.stringPool)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
## Developer Note
|
||
|
||
There are a few places I could not properly test becaues none of my test files triggered the condition.
|
||
For example, handling dynamic attributes and dynamic references.
|
||
I've added some `assert` (ignored in release builds) to trigger as soon as a specific condition is met.
|
||
If you encounter a file, which triggers such a condition, reach out so I can finalize the library.
|
||
|
||
|
||
[ResourceTypes.h]: https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h
|
||
[ResourceTypes.cpp]: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/libs/androidfw/ResourceTypes.cpp
|