diff --git a/internal/binres/binres.go b/internal/binres/binres.go
index 682c378..a796efa 100644
--- a/internal/binres/binres.go
+++ b/internal/binres/binres.go
@@ -63,14 +63,7 @@ func errWrongType(have ResType, want ...ResType) error {
type ResType uint16
func (t ResType) IsSupported() bool {
- // explicit for clarity
- return t == ResStringPool || t == ResXML ||
- t == ResXMLStartNamespace || t == ResXMLEndNamespace ||
- t == ResXMLStartElement || t == ResXMLEndElement ||
- t == ResXMLCharData ||
- t == ResXMLResourceMap ||
- t == ResTable || t == ResTablePackage ||
- t == ResTableTypeSpec || t == ResTableType
+ return t != ResNull
}
// explicitly defined for clarity and resolvability with apt source
@@ -235,7 +228,7 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
Space: "",
Local: "platformBuildVersionName",
},
- Value: "4.0.3",
+ Value: "4.0.4-1406430",
})
}
@@ -267,6 +260,7 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
el.attrs = append(el.attrs, nattr)
if attr.Name.Space == "" {
+ nattr.NS = NoEntry
// TODO it's unclear how to query these
switch attr.Name.Local {
case "platformBuildVersionCode":
@@ -306,6 +300,7 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
// TODO identify 0x3e, in bootstrap.xml this is the native lib name
nattr.RawValue = pool.ref(attr.Value)
nattr.TypedValue.Type = DataString
+ nattr.TypedValue.Value = uint32(nattr.RawValue)
case DataIntBool, DataType(0x08):
nattr.TypedValue.Type = DataIntBool
switch attr.Value {
@@ -398,13 +393,30 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
}
}
case xml.EndElement:
+ if tkn.Name.Local == "manifest" {
+ bx.Namespace.end = &Namespace{
+ NodeHeader: NodeHeader{
+ LineNumber: uint32(line),
+ Comment: NoEntry,
+ },
+ prefix: 0,
+ uri: 0,
+ }
+ }
n := len(bx.stack)
var el *Element
el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
if el.end != nil {
return nil, fmt.Errorf("element end already exists")
}
- el.end = new(ElementEnd)
+ el.end = &ElementEnd{
+ NodeHeader: NodeHeader{
+ LineNumber: uint32(line),
+ Comment: NoEntry,
+ },
+ NS: el.NS,
+ Name: el.Name,
+ }
case xml.Comment, xml.ProcInst:
// discard
default:
@@ -424,6 +436,9 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
var arecurse func(*Element)
arecurse = func(el *Element) {
for _, attr := range el.attrs {
+ if attr.NS == NoEntry {
+ continue
+ }
if attr.NS.Resolve(pool) == androidSchema {
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
}
@@ -448,7 +463,7 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
var brecurse func(*Element)
brecurse = func(el *Element) {
for _, attr := range el.attrs {
- if attr.NS.Resolve(pool) == "" {
+ if attr.NS == NoEntry {
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
}
}
@@ -458,7 +473,7 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
for _, attr := range el.attrs {
if attr.RawValue != NoEntry {
bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool))
- } else if attr.NS.Resolve(pool) == "" {
+ } else if attr.NS == NoEntry {
bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value))
}
}
@@ -502,8 +517,10 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
resolve = func(el *Element) {
if el.NS != NoEntry {
el.NS = bx.Pool.ref(el.NS.Resolve(pool))
+ el.end.NS = el.NS
}
el.Name = bx.Pool.ref(el.Name.Resolve(pool))
+ el.end.Name = el.Name
for _, attr := range el.attrs {
if attr.NS != NoEntry {
attr.NS = bx.Pool.ref(attr.NS.Resolve(pool))
@@ -511,6 +528,9 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
attr.Name = bx.Pool.ref(attr.Name.Resolve(pool))
if attr.RawValue != NoEntry {
attr.RawValue = bx.Pool.ref(attr.RawValue.Resolve(pool))
+ if attr.TypedValue.Type == DataString {
+ attr.TypedValue.Value = uint32(attr.RawValue)
+ }
}
}
for _, child := range el.Children {
@@ -524,8 +544,8 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
var asort func(*Element)
asort = func(el *Element) {
sort.Sort(byType(el.attrs))
- // sort.Sort(byName(el.attrs))
sort.Sort(byNamespace(el.attrs))
+ sort.Sort(byName(el.attrs))
for _, child := range el.Children {
asort(child)
}
@@ -534,6 +554,17 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
asort(el)
}
+ for i, s := range bx.Pool.strings {
+ switch s {
+ case androidSchema:
+ bx.Namespace.uri = PoolRef(i)
+ bx.Namespace.end.uri = PoolRef(i)
+ case "android":
+ bx.Namespace.prefix = PoolRef(i)
+ bx.Namespace.end.prefix = PoolRef(i)
+ }
+ }
+
return bx, nil
}
@@ -629,6 +660,9 @@ func (bx *XML) kind(t ResType) (unmarshaler, error) {
}
func (bx *XML) MarshalBinary() ([]byte, error) {
+ bx.typ = ResXML
+ bx.headerByteSize = 8
+
var (
bin, b []byte
err error
@@ -669,6 +703,7 @@ func (bx *XML) MarshalBinary() ([]byte, error) {
}
bin = append(bin, b...)
+ putu32(bin[4:], uint32(len(bin)))
return bin, nil
}
@@ -788,7 +823,9 @@ type byName []*Attribute
func (a byName) Len() int { return len(a) }
func (a byName) Less(i, j int) bool {
- return a[i].TypedValue.Type == a[j].TypedValue.Type && a[i].Name < a[j].Name
+ return (a[i].TypedValue.Type == DataString || a[i].TypedValue.Type == DataIntDec) &&
+ (a[j].TypedValue.Type == DataString || a[j].TypedValue.Type == DataIntDec) &&
+ a[i].Name < a[j].Name
}
func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/internal/binres/binres_test.go b/internal/binres/binres_test.go
index aed4817..bfd2b51 100644
--- a/internal/binres/binres_test.go
+++ b/internal/binres/binres_test.go
@@ -13,7 +13,6 @@ import (
"math"
"os"
"path/filepath"
- "reflect"
"strings"
"testing"
)
@@ -53,6 +52,53 @@ func printrecurse(t *testing.T, pl *Pool, el *Element, ws string) {
}
}
+func compareBytes(a, b []byte) error {
+ if bytes.Equal(a, b) {
+ return nil
+ }
+ buf := new(bytes.Buffer)
+ x, y := len(a), len(b)
+ if x != y {
+ fmt.Fprintf(buf, "byte length does not match, have %v, want %v\n", y, x)
+ }
+ if x > y {
+ x, y = y, x
+ }
+ mismatch := false
+ for i := 0; i < x; i++ {
+ if mismatch = a[i] != b[i]; mismatch {
+ fmt.Fprintf(buf, "first byte mismatch at %v\n", i)
+ break
+ }
+ }
+ if mismatch {
+ // print out a reasonable amount of data to help identify issues
+ truncate := x > 3300
+ if truncate {
+ x = 3300
+ }
+ buf.WriteString(" HAVE WANT\n")
+ for i := 0; i < x; i += 4 {
+ he, we := 4, 4
+ if i+he >= x {
+ he = x - i
+ }
+ if i+we >= y {
+ we = y - i
+ }
+ notequal := ""
+ if !bytes.Equal(b[i:i+he], a[i:i+we]) {
+ notequal = "***"
+ }
+ fmt.Fprintf(buf, "%3v | % X % X %s\n", i, b[i:i+he], a[i:i+we], notequal)
+ }
+ if truncate {
+ fmt.Fprint(buf, "... output truncated.\n")
+ }
+ }
+ return fmt.Errorf(buf.String())
+}
+
func TestBootstrap(t *testing.T) {
bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
if err != nil {
@@ -143,7 +189,6 @@ func TestEncode(t *testing.T) {
t.Fatal(err)
}
- //
bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
if err != nil {
log.Fatal(err)
@@ -161,17 +206,75 @@ func TestEncode(t *testing.T) {
t.Error(err)
}
- if !reflect.DeepEqual(bxml.Namespace, bx.Namespace) {
- t.Errorf("Namespace doesn't match, have %+v, want %+v", bx.Namespace, bxml.Namespace)
+ if err := compareNamespaces(bx.Namespace, bxml.Namespace); err != nil {
+ t.Error(err)
}
- if !reflect.DeepEqual(bxml.Children, bx.Children) {
- t.Error("Children don't match")
+ if err := compareElements(bx, bxml); err != nil {
+ t.Fatal(err)
}
- for _, el := range bx.Children {
- printrecurse(t, bx.Pool, el, "")
+ have, err := bx.MarshalBinary()
+ if err != nil {
+ t.Fatal(err)
}
+ if err := compareBytes(bin, have); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func compareElements(have, want *XML) error {
+ h, w := have.iterElements(), want.iterElements()
+ buf := new(bytes.Buffer)
+ for {
+ a, b := <-h, <-w
+ if a == nil || b == nil {
+ break
+ }
+ if a.NS != b.NS ||
+ a.Name != b.Name {
+ return fmt.Errorf("elements don't match, have %+v, want %+v", a, b)
+ }
+ if a.end.NS != b.end.NS ||
+ a.end.Name != b.end.Name {
+ return fmt.Errorf("element ends don't match, have %+v, want %+v", a.end, b.end)
+ }
+ if len(a.attrs) != len(b.attrs) {
+ return fmt.Errorf("element attribute lengths don't match, have %v, want %v", len(a.attrs), len(b.attrs))
+ }
+
+ for i, attr := range a.attrs {
+ bttr := b.attrs[i]
+ if attr.NS != bttr.NS ||
+ attr.Name != bttr.Name ||
+ attr.RawValue != bttr.RawValue ||
+ attr.TypedValue.Type != bttr.TypedValue.Type ||
+ attr.TypedValue.Value != bttr.TypedValue.Value {
+ fmt.Fprintf(buf, "attrs don't match\nhave: %+v\nwant: %+v\n", attr, bttr)
+ }
+ }
+ if buf.Len() > 0 {
+ buf.WriteString("-------------\n")
+ }
+ }
+ if buf.Len() > 0 {
+ return fmt.Errorf(buf.String())
+ }
+ return nil
+}
+
+func compareNamespaces(have, want *Namespace) error {
+ if have == nil || want == nil ||
+ have.LineNumber != want.LineNumber ||
+ have.Comment != want.Comment ||
+ have.prefix != want.prefix ||
+ have.uri != want.uri {
+ return fmt.Errorf("namespaces don't match, have %+v, want %+v", have, want)
+ }
+ if have.end != nil || want.end != nil {
+ return compareNamespaces(have.end, want.end)
+ }
+ return nil
}
func rtou(a []TableRef) (b []uint32) {
diff --git a/internal/binres/node.go b/internal/binres/node.go
index 5a4853a..4918251 100644
--- a/internal/binres/node.go
+++ b/internal/binres/node.go
@@ -52,7 +52,15 @@ func (ns *Namespace) UnmarshalBinary(bin []byte) error {
}
func (ns *Namespace) MarshalBinary() ([]byte, error) {
- bin := make([]byte, 24)
+ if ns.end == nil {
+ ns.typ = ResXMLEndNamespace
+ } else {
+ ns.typ = ResXMLStartNamespace
+ }
+ ns.headerByteSize = 16
+ ns.byteSize = 24
+
+ bin := make([]byte, ns.byteSize)
b, err := ns.NodeHeader.MarshalBinary()
if err != nil {
return nil, err
@@ -88,6 +96,7 @@ func (el *Element) UnmarshalBinary(buf []byte) error {
buf = buf[el.headerByteSize:]
el.NS = PoolRef(btou32(buf))
el.Name = PoolRef(btou32(buf[4:]))
+
el.AttributeStart = btou16(buf[8:])
el.AttributeSize = btou16(buf[10:])
el.AttributeCount = btou16(buf[12:])
@@ -110,7 +119,17 @@ func (el *Element) UnmarshalBinary(buf []byte) error {
}
func (el *Element) MarshalBinary() ([]byte, error) {
- bin := make([]byte, 16+20+len(el.attrs)*int(el.AttributeSize))
+ el.typ = ResXMLStartElement
+ el.headerByteSize = 16
+ el.AttributeSize = 20
+ el.AttributeStart = 20
+ el.AttributeCount = uint16(len(el.attrs))
+ el.IdIndex = 0
+ el.ClassIndex = 0
+ el.StyleIndex = 0
+ el.byteSize = uint32(el.headerByteSize) + uint32(el.AttributeStart) + uint32(len(el.attrs)*int(el.AttributeSize))
+
+ bin := make([]byte, el.byteSize)
b, err := el.NodeHeader.MarshalBinary()
if err != nil {
return nil, err
@@ -154,6 +173,10 @@ func (el *ElementEnd) UnmarshalBinary(bin []byte) error {
}
func (el *ElementEnd) MarshalBinary() ([]byte, error) {
+ el.typ = ResXMLEndElement
+ el.headerByteSize = 16
+ el.byteSize = 24
+
bin := make([]byte, 24)
b, err := el.NodeHeader.MarshalBinary()
if err != nil {
@@ -209,7 +232,11 @@ func (cdt *CharData) UnmarshalBinary(bin []byte) error {
}
func (cdt *CharData) MarshalBinary() ([]byte, error) {
- bin := make([]byte, 28)
+ cdt.typ = ResXMLCharData
+ cdt.headerByteSize = 16
+ cdt.byteSize = 28
+
+ bin := make([]byte, cdt.byteSize)
b, err := cdt.NodeHeader.MarshalBinary()
if err != nil {
return nil, err
diff --git a/internal/binres/pool.go b/internal/binres/pool.go
index 84cb04c..bb9e89d 100644
--- a/internal/binres/pool.go
+++ b/internal/binres/pool.go
@@ -215,6 +215,7 @@ func (pl *Pool) MarshalBinary() ([]byte, error) {
putu32(buf, x)
buf = buf[4:]
}
+
for _, x := range strs {
putu16(buf, x)
buf = buf[2:]
@@ -263,7 +264,10 @@ func (m *Map) UnmarshalBinary(bin []byte) error {
}
func (m *Map) MarshalBinary() ([]byte, error) {
- bin := make([]byte, 8+len(m.rs)*4)
+ m.typ = ResXMLResourceMap
+ m.headerByteSize = 8
+ m.byteSize = uint32(m.headerByteSize) + uint32(len(m.rs)*4)
+ bin := make([]byte, m.byteSize)
b, err := m.chunkHeader.MarshalBinary()
if err != nil {
return nil, err
diff --git a/internal/binres/table.go b/internal/binres/table.go
index 280757f..be43e02 100644
--- a/internal/binres/table.go
+++ b/internal/binres/table.go
@@ -687,7 +687,7 @@ func (d *Data) UnmarshalBinary(bin []byte) error {
func (d *Data) MarshalBinary() ([]byte, error) {
bin := make([]byte, 8)
- putu16(bin, d.ByteSize)
+ putu16(bin, 8)
bin[2] = byte(d.Res0)
bin[3] = byte(d.Type)
putu32(bin[4:], d.Value)
diff --git a/internal/binres/testdata/bootstrap.bin b/internal/binres/testdata/bootstrap.bin
index 9aa0635..00a3f1d 100644
Binary files a/internal/binres/testdata/bootstrap.bin and b/internal/binres/testdata/bootstrap.bin differ
diff --git a/internal/binres/testdata/bootstrap.xml b/internal/binres/testdata/bootstrap.xml
index 680e76b..27989bb 100644
--- a/internal/binres/testdata/bootstrap.xml
+++ b/internal/binres/testdata/bootstrap.xml
@@ -11,7 +11,7 @@ license that can be found in the LICENSE file.
android:versionName=""
xmlns:tools="http://schemas.android.com/tools">
-
+
@@ -21,7 +21,7 @@ license that can be found in the LICENSE file.
android:hasCode="false"
foo="bar"
android:debuggable="true"
- baz="bar"
+ baz="bar"
tools:strict="label">