binres: method for resolving name as tableref
Change-Id: If31bab76ccb5b03540d86356c74107fe5ec99be6 Reviewed-on: https://go-review.googlesource.com/18490 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
e27aacb64e
commit
f85352c0f7
@ -266,3 +266,26 @@ func TestOpenTable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableRefByName(t *testing.T) {
|
||||
sdkdir := os.Getenv("ANDROID_HOME")
|
||||
if sdkdir == "" {
|
||||
t.Fatal("ANDROID_HOME env var not set")
|
||||
}
|
||||
tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(tbl.pkgs) == 0 {
|
||||
t.Fatal("failed to decode any resource packages")
|
||||
}
|
||||
|
||||
ref, err := tbl.RefByName("@android:style/Theme.NoTitleBar.Fullscreen")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want := uint32(0x01030007); uint32(ref) != want {
|
||||
t.Fatalf("RefByName does not match expected result, have %0#8x, want %0#8x", ref, want)
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binres
|
||||
|
||||
type DataType uint8
|
||||
|
||||
// explicitly defined for clarity and resolvability with apt source
|
||||
const (
|
||||
DataNull DataType = 0x00 // either 0 or 1 for resource undefined or empty
|
||||
DataReference DataType = 0x01 // ResTable_ref, a reference to another resource table entry
|
||||
DataAttribute DataType = 0x02 // attribute resource identifier
|
||||
DataString DataType = 0x03 // index into the containing resource table's global value string pool
|
||||
DataFloat DataType = 0x04 // single-precision floating point number
|
||||
DataDimension DataType = 0x05 // complex number encoding a dimension value, such as "100in"
|
||||
DataFraction DataType = 0x06 // complex number encoding a fraction of a container
|
||||
DataDynamicReference DataType = 0x07 // dynamic ResTable_ref, which needs to be resolved before it can be used like a TYPE_REFERENCE.
|
||||
DataIntDec DataType = 0x10 // raw integer value of the form n..n
|
||||
DataIntHex DataType = 0x11 // raw integer value of the form 0xn..n
|
||||
DataIntBool DataType = 0x12 // either 0 or 1, for input "false" or "true"
|
||||
DataIntColorARGB8 DataType = 0x1c // raw integer value of the form #aarrggbb
|
||||
DataIntColorRGB8 DataType = 0x1d // raw integer value of the form #rrggbb
|
||||
DataIntColorARGB4 DataType = 0x1e // raw integer value of the form #argb
|
||||
DataIntColorRGB4 DataType = 0x1f // raw integer value of the form #rgb
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
ByteSize uint16
|
||||
Res0 uint8 // always 0, useful for debugging bad read offsets
|
||||
Type DataType
|
||||
Value uint32
|
||||
}
|
||||
|
||||
func (d *Data) UnmarshalBinary(bin []byte) error {
|
||||
d.ByteSize = btou16(bin)
|
||||
d.Res0 = uint8(bin[2])
|
||||
d.Type = DataType(bin[3])
|
||||
d.Value = btou32(bin[4:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Data) MarshalBinary() ([]byte, error) {
|
||||
bin := make([]byte, 8)
|
||||
putu16(bin, d.ByteSize)
|
||||
bin[2] = byte(d.Res0)
|
||||
bin[3] = byte(d.Type)
|
||||
putu32(bin[4:], d.Value)
|
||||
return bin, nil
|
||||
}
|
@ -7,8 +7,10 @@ package binres
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
@ -17,11 +19,37 @@ const NoEntry = 0xFFFFFFFF
|
||||
// TableRef uniquely identifies entries within a resource table.
|
||||
type TableRef uint32
|
||||
|
||||
// Resolve returns the Entry of TableRef in the given table.
|
||||
//
|
||||
// A TableRef is structured as follows:
|
||||
// 0xpptteeee
|
||||
// pp: package index
|
||||
// tt: type spec index in package
|
||||
// eeee: entry index in type spec
|
||||
//
|
||||
// The package and type spec values start at 1 for the first item,
|
||||
// to help catch cases where they have not been supplied.
|
||||
func (ref TableRef) Resolve(tbl *Table) (*Entry, error) {
|
||||
pkg := tbl.pkgs[uint8(ref>>24)-1]
|
||||
spec := pkg.specs[uint8(ref>>16)-1]
|
||||
idx := uint16(ref)
|
||||
|
||||
for _, typ := range spec.types {
|
||||
if idx < uint16(len(typ.entries)) {
|
||||
nt := typ.entries[idx]
|
||||
if nt == nil {
|
||||
return nil, errors.New("nil entry match")
|
||||
}
|
||||
return nt, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("failed to resolve table reference")
|
||||
}
|
||||
|
||||
// Table is a container for packaged resources. Resource values within a package
|
||||
// are obtained through pool while resource names and identifiers are obtained
|
||||
// through each package's type and key pools respectively.
|
||||
//
|
||||
// TODO provide method for querying resource types.
|
||||
type Table struct {
|
||||
chunkHeader
|
||||
pool *Pool
|
||||
@ -62,6 +90,53 @@ func OpenTable(path string) (*Table, error) {
|
||||
return tbl, nil
|
||||
}
|
||||
|
||||
// SpecByName parses the spec name from an entry string if necessary and returns
|
||||
// the Package and TypeSpec associated with that name.
|
||||
//
|
||||
// For example:
|
||||
// tbl.SpecByName("@android:style/Theme.NoTitleBar")
|
||||
// tbl.SpecByName("style")
|
||||
// Both locate the spec by name "style".
|
||||
func (tbl *Table) SpecByName(name string) (int, *Package, int, *TypeSpec, error) {
|
||||
n := strings.TrimPrefix(name, "@android:")
|
||||
n = strings.Split(n, "/")[0]
|
||||
for pp, pkg := range tbl.pkgs {
|
||||
for tt, spec := range pkg.specs {
|
||||
if n == pkg.typePool.strings[spec.id-1] {
|
||||
return pp, pkg, tt, spec, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, nil, 0, nil, fmt.Errorf("spec by name not found: %q", name)
|
||||
}
|
||||
|
||||
// RefByName returns the TableRef by a given name. The ref may be used to resolve the
|
||||
// associated Entry and is used for the generation of binary manifest files.
|
||||
func (tbl *Table) RefByName(name string) (TableRef, error) {
|
||||
pp, pkg, tt, spec, err := tbl.SpecByName(name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
q := strings.Split(name, "/")
|
||||
if len(q) != 2 {
|
||||
return 0, fmt.Errorf("invalid entry format, missing forward-slash: %q", name)
|
||||
}
|
||||
n := q[1]
|
||||
|
||||
for _, t := range spec.types {
|
||||
for eeee, nt := range t.entries {
|
||||
if nt == nil { // NoEntry
|
||||
continue
|
||||
}
|
||||
if n == pkg.keyPool.strings[nt.key] {
|
||||
return TableRef(uint32(eeee) | uint32(tt+1)<<16 | uint32(pp+1)<<24), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("failed to find table ref by %q", name)
|
||||
}
|
||||
|
||||
func (tbl *Table) UnmarshalBinary(bin []byte) error {
|
||||
if err := (&tbl.chunkHeader).UnmarshalBinary(bin); err != nil {
|
||||
return err
|
||||
@ -190,7 +265,7 @@ type TypeSpec struct {
|
||||
res1 uint16 // must be 0
|
||||
entryCount uint32 // number of uint32 entry configuration masks that follow
|
||||
|
||||
entries []uint32
|
||||
entries []uint32 // entry configuration masks
|
||||
types []*Type
|
||||
}
|
||||
|
||||
@ -214,6 +289,7 @@ func (spec *TypeSpec) UnmarshalBinary(bin []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type provides a collection of entries for a specific device configuration.
|
||||
type Type struct {
|
||||
chunkHeader
|
||||
id uint8
|
||||
@ -223,7 +299,7 @@ type Type struct {
|
||||
entriesStart uint32 // offset from header where Entry data starts
|
||||
|
||||
// TODO implement and decode Config if strictly necessary
|
||||
// config Config // configuration this collection of entries is designed for
|
||||
// config Config // configuration this collection of entries is designed for (portrait, sw600dp, etc)
|
||||
|
||||
indices []uint32 // values that map to typePool
|
||||
entries []*Entry
|
||||
@ -277,11 +353,94 @@ type Entry struct {
|
||||
size uint16
|
||||
flags uint16
|
||||
key PoolRef // ref into key pool
|
||||
|
||||
// only filled if this is a map entry; when size is 16
|
||||
parent TableRef // id of parent mapping or zero if none
|
||||
count uint32 // name and value pairs that follow for FlagComplex
|
||||
|
||||
values []*Value
|
||||
}
|
||||
|
||||
func (nt *Entry) UnmarshalBinary(bin []byte) error {
|
||||
nt.size = btou16(bin)
|
||||
nt.flags = btou16(bin[2:])
|
||||
nt.key = PoolRef(btou32(bin[4:]))
|
||||
|
||||
if nt.size == 16 {
|
||||
nt.parent = TableRef(btou32(bin[8:]))
|
||||
nt.count = btou32(bin[12:])
|
||||
nt.values = make([]*Value, nt.count)
|
||||
for i := range nt.values {
|
||||
val := &Value{}
|
||||
if err := val.UnmarshalBinary(bin[16+i*12:]); err != nil {
|
||||
return err
|
||||
}
|
||||
nt.values[i] = val
|
||||
}
|
||||
} else {
|
||||
data := &Data{}
|
||||
if err := data.UnmarshalBinary(bin[8:]); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO boxing data not strictly correct as binary repr isn't of Value.
|
||||
nt.values = append(nt.values, &Value{0, data})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
name TableRef
|
||||
data *Data
|
||||
}
|
||||
|
||||
func (val *Value) UnmarshalBinary(bin []byte) error {
|
||||
val.name = TableRef(btou32(bin))
|
||||
val.data = &Data{}
|
||||
return val.data.UnmarshalBinary(bin[4:])
|
||||
}
|
||||
|
||||
type DataType uint8
|
||||
|
||||
// explicitly defined for clarity and resolvability with apt source
|
||||
const (
|
||||
DataNull DataType = 0x00 // either 0 or 1 for resource undefined or empty
|
||||
DataReference DataType = 0x01 // ResTable_ref, a reference to another resource table entry
|
||||
DataAttribute DataType = 0x02 // attribute resource identifier
|
||||
DataString DataType = 0x03 // index into the containing resource table's global value string pool
|
||||
DataFloat DataType = 0x04 // single-precision floating point number
|
||||
DataDimension DataType = 0x05 // complex number encoding a dimension value, such as "100in"
|
||||
DataFraction DataType = 0x06 // complex number encoding a fraction of a container
|
||||
DataDynamicReference DataType = 0x07 // dynamic ResTable_ref, which needs to be resolved before it can be used like a TYPE_REFERENCE.
|
||||
DataIntDec DataType = 0x10 // raw integer value of the form n..n
|
||||
DataIntHex DataType = 0x11 // raw integer value of the form 0xn..n
|
||||
DataIntBool DataType = 0x12 // either 0 or 1, for input "false" or "true"
|
||||
DataIntColorARGB8 DataType = 0x1c // raw integer value of the form #aarrggbb
|
||||
DataIntColorRGB8 DataType = 0x1d // raw integer value of the form #rrggbb
|
||||
DataIntColorARGB4 DataType = 0x1e // raw integer value of the form #argb
|
||||
DataIntColorRGB4 DataType = 0x1f // raw integer value of the form #rgb
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
ByteSize uint16
|
||||
Res0 uint8 // always 0, useful for debugging bad read offsets
|
||||
Type DataType
|
||||
Value uint32
|
||||
}
|
||||
|
||||
func (d *Data) UnmarshalBinary(bin []byte) error {
|
||||
d.ByteSize = btou16(bin)
|
||||
d.Res0 = uint8(bin[2])
|
||||
d.Type = DataType(bin[3])
|
||||
d.Value = btou32(bin[4:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Data) MarshalBinary() ([]byte, error) {
|
||||
bin := make([]byte, 8)
|
||||
putu16(bin, d.ByteSize)
|
||||
bin[2] = byte(d.Res0)
|
||||
bin[3] = byte(d.Type)
|
||||
putu32(bin[4:], d.Value)
|
||||
return bin, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user