The OpenTable func now references a prepacked version of the resources.arsc file with a number of entries removed. The current size of this file is 62KB. This could be dropped further by implementing utf8 in string pool during marshal, utf16 encoding exploded the original size by approximately 20%. Another potential improvement is to allow type entries to be packed sparsely which may provide significant savings if not zipping. Change-Id: Ie139c2bdb0e3c5a9212516d18cf627d75774e187 Reviewed-on: https://go-review.googlesource.com/18649 Reviewed-by: David Crawshaw <crawshaw@golang.org>
696 lines
17 KiB
Go
696 lines
17 KiB
Go
// 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
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"unicode/utf16"
|
|
)
|
|
|
|
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.
|
|
type Table struct {
|
|
chunkHeader
|
|
pool *Pool
|
|
pkgs []*Package
|
|
}
|
|
|
|
// OpenSDKTable decodes resources.arsc from sdk platform jar.
|
|
func OpenSDKTable() (*Table, error) {
|
|
bin, err := apiResources()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tbl := new(Table)
|
|
if err := tbl.UnmarshalBinary(bin); err != nil {
|
|
return nil, err
|
|
}
|
|
return tbl, nil
|
|
}
|
|
|
|
// OpenTable decodes the prepacked resources.arsc for the supported sdk platform.
|
|
func OpenTable() (*Table, error) {
|
|
f, err := os.Open(filepath.Join("data", "packed.arsc.gz"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
zr, err := gzip.NewReader(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer zr.Close()
|
|
|
|
var buf bytes.Buffer
|
|
if _, err := io.Copy(&buf, zr); err != nil {
|
|
return nil, err
|
|
}
|
|
tbl := new(Table)
|
|
if err := tbl.UnmarshalBinary(buf.Bytes()); err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|
|
if tbl.typ != ResTable {
|
|
return fmt.Errorf("unexpected resource type %s, want %s", tbl.typ, ResTable)
|
|
}
|
|
|
|
npkgs := btou32(bin[8:])
|
|
tbl.pkgs = make([]*Package, npkgs)
|
|
|
|
buf := bin[tbl.headerByteSize:]
|
|
|
|
tbl.pool = new(Pool)
|
|
if err := tbl.pool.UnmarshalBinary(buf); err != nil {
|
|
return err
|
|
}
|
|
buf = buf[tbl.pool.size():]
|
|
for i := range tbl.pkgs {
|
|
pkg := new(Package)
|
|
if err := pkg.UnmarshalBinary(buf); err != nil {
|
|
return err
|
|
}
|
|
tbl.pkgs[i] = pkg
|
|
buf = buf[pkg.byteSize:]
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tbl *Table) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 12)
|
|
putu16(bin, uint16(ResTable))
|
|
putu16(bin[2:], 12)
|
|
putu32(bin[8:], uint32(len(tbl.pkgs)))
|
|
|
|
if tbl.pool.IsUTF8() {
|
|
tbl.pool.flags ^= UTF8Flag
|
|
defer func() {
|
|
tbl.pool.flags |= UTF8Flag
|
|
}()
|
|
}
|
|
|
|
b, err := tbl.pool.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bin = append(bin, b...)
|
|
|
|
for _, pkg := range tbl.pkgs {
|
|
b, err = pkg.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
putu32(bin[4:], uint32(len(bin)))
|
|
|
|
return bin, nil
|
|
}
|
|
|
|
// Package contains a collection of resource data types.
|
|
type Package struct {
|
|
chunkHeader
|
|
|
|
id uint32
|
|
name string
|
|
|
|
lastPublicType uint32 // last index into typePool that is for public use
|
|
lastPublicKey uint32 // last index into keyPool that is for public use
|
|
|
|
typePool *Pool // type names; e.g. theme
|
|
keyPool *Pool // resource names; e.g. Theme.NoTitleBar
|
|
|
|
specs []*TypeSpec
|
|
}
|
|
|
|
func (pkg *Package) UnmarshalBinary(bin []byte) error {
|
|
if err := (&pkg.chunkHeader).UnmarshalBinary(bin); err != nil {
|
|
return err
|
|
}
|
|
if pkg.typ != ResTablePackage {
|
|
return errWrongType(pkg.typ, ResTablePackage)
|
|
}
|
|
|
|
pkg.id = btou32(bin[8:])
|
|
|
|
var name []uint16
|
|
for i := 0; i < 128; i++ {
|
|
x := btou16(bin[12+i*2:])
|
|
if x == 0 {
|
|
break
|
|
}
|
|
name = append(name, x)
|
|
}
|
|
pkg.name = string(utf16.Decode(name))
|
|
|
|
typeOffset := btou32(bin[268:]) // 0 if inheriting from another package
|
|
pkg.lastPublicType = btou32(bin[272:])
|
|
keyOffset := btou32(bin[276:]) // 0 if inheriting from another package
|
|
pkg.lastPublicKey = btou32(bin[280:])
|
|
|
|
var idOffset uint32 // value determined by either typePool or keyPool below
|
|
|
|
if typeOffset != 0 {
|
|
pkg.typePool = new(Pool)
|
|
if err := pkg.typePool.UnmarshalBinary(bin[typeOffset:]); err != nil {
|
|
return err
|
|
}
|
|
idOffset = typeOffset + pkg.typePool.byteSize
|
|
}
|
|
|
|
if keyOffset != 0 {
|
|
pkg.keyPool = new(Pool)
|
|
if err := pkg.keyPool.UnmarshalBinary(bin[keyOffset:]); err != nil {
|
|
return err
|
|
}
|
|
idOffset = keyOffset + pkg.keyPool.byteSize
|
|
}
|
|
|
|
if idOffset == 0 {
|
|
return nil
|
|
}
|
|
|
|
buf := bin[idOffset:]
|
|
for len(pkg.specs) < len(pkg.typePool.strings) {
|
|
t := ResType(btou16(buf))
|
|
switch t {
|
|
case ResTableTypeSpec:
|
|
spec := new(TypeSpec)
|
|
if err := spec.UnmarshalBinary(buf); err != nil {
|
|
return err
|
|
}
|
|
pkg.specs = append(pkg.specs, spec)
|
|
buf = buf[spec.byteSize:]
|
|
case ResTableType:
|
|
typ := new(Type)
|
|
if err := typ.UnmarshalBinary(buf); err != nil {
|
|
return err
|
|
}
|
|
last := pkg.specs[len(pkg.specs)-1]
|
|
last.types = append(last.types, typ)
|
|
buf = buf[typ.byteSize:]
|
|
default:
|
|
return errWrongType(t, ResTableTypeSpec, ResTableType)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pkg *Package) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 284)
|
|
putu16(bin, uint16(ResTablePackage))
|
|
putu16(bin[2:], 284)
|
|
|
|
putu32(bin[8:], pkg.id)
|
|
p := utf16.Encode([]rune(pkg.name))
|
|
for i, x := range p {
|
|
putu16(bin[12+i*2:], x)
|
|
}
|
|
|
|
if pkg.typePool != nil {
|
|
if pkg.typePool.IsUTF8() {
|
|
pkg.typePool.flags ^= UTF8Flag
|
|
defer func() {
|
|
pkg.typePool.flags |= UTF8Flag
|
|
}()
|
|
}
|
|
|
|
b, err := pkg.typePool.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
putu32(bin[268:], uint32(len(bin)))
|
|
putu32(bin[272:], pkg.lastPublicType)
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
if pkg.keyPool != nil {
|
|
if pkg.keyPool.IsUTF8() {
|
|
pkg.keyPool.flags ^= UTF8Flag
|
|
defer func() {
|
|
pkg.keyPool.flags |= UTF8Flag
|
|
}()
|
|
}
|
|
b, err := pkg.keyPool.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
putu32(bin[276:], uint32(len(bin)))
|
|
putu32(bin[280:], pkg.lastPublicKey)
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
for _, spec := range pkg.specs {
|
|
b, err := spec.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
putu32(bin[4:], uint32(len(bin)))
|
|
return bin, nil
|
|
}
|
|
|
|
// TypeSpec provides a specification for the resources defined by a particular type.
|
|
type TypeSpec struct {
|
|
chunkHeader
|
|
id uint8 // id-1 is name index in Package.typePool
|
|
res0 uint8 // must be 0
|
|
res1 uint16 // must be 0
|
|
entryCount uint32 // number of uint32 entry configuration masks that follow
|
|
|
|
entries []uint32 // entry configuration masks
|
|
types []*Type
|
|
}
|
|
|
|
func (spec *TypeSpec) UnmarshalBinary(bin []byte) error {
|
|
if err := (&spec.chunkHeader).UnmarshalBinary(bin); err != nil {
|
|
return err
|
|
}
|
|
if spec.typ != ResTableTypeSpec {
|
|
return errWrongType(spec.typ, ResTableTypeSpec)
|
|
}
|
|
spec.id = uint8(bin[8])
|
|
spec.res0 = uint8(bin[9])
|
|
spec.res1 = btou16(bin[10:])
|
|
spec.entryCount = btou32(bin[12:])
|
|
|
|
spec.entries = make([]uint32, spec.entryCount)
|
|
for i := range spec.entries {
|
|
spec.entries[i] = btou32(bin[16+i*4:])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (spec *TypeSpec) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 16+len(spec.entries)*4)
|
|
putu16(bin, uint16(ResTableTypeSpec))
|
|
putu16(bin[2:], 16)
|
|
putu32(bin[4:], uint32(len(bin)))
|
|
|
|
bin[8] = byte(spec.id)
|
|
// [9] = 0
|
|
// [10:12] = 0
|
|
putu32(bin[12:], uint32(len(spec.entries)))
|
|
for i, x := range spec.entries {
|
|
putu32(bin[16+i*4:], x)
|
|
}
|
|
|
|
for _, typ := range spec.types {
|
|
b, err := typ.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
return bin, nil
|
|
}
|
|
|
|
// Type provides a collection of entries for a specific device configuration.
|
|
type Type struct {
|
|
chunkHeader
|
|
id uint8
|
|
res0 uint8 // must be 0
|
|
res1 uint16 // must be 0
|
|
entryCount uint32 // number of uint32 entry configuration masks that follow
|
|
entriesStart uint32 // offset from header where Entry data starts
|
|
|
|
// configuration this collection of entries is designed for
|
|
config struct {
|
|
size uint32
|
|
imsi struct {
|
|
mcc uint16 // mobile country code
|
|
mnc uint16 // mobile network code
|
|
}
|
|
locale struct {
|
|
language uint16
|
|
country uint16
|
|
}
|
|
screenType struct {
|
|
orientation uint8
|
|
touchscreen uint8
|
|
density uint16
|
|
}
|
|
input struct {
|
|
keyboard uint8
|
|
navigation uint8
|
|
inputFlags uint8
|
|
inputPad0 uint8
|
|
}
|
|
screenSize struct {
|
|
width uint16
|
|
height uint16
|
|
}
|
|
version struct {
|
|
sdk uint16
|
|
minor uint16 // always 0
|
|
}
|
|
screenConfig struct {
|
|
layout uint8
|
|
uiMode uint8
|
|
smallestWidthDP uint16
|
|
}
|
|
screenSizeDP struct {
|
|
width uint16
|
|
height uint16
|
|
}
|
|
}
|
|
|
|
indices []uint32 // values that map to typePool
|
|
entries []*Entry
|
|
}
|
|
|
|
func (typ *Type) UnmarshalBinary(bin []byte) error {
|
|
if err := (&typ.chunkHeader).UnmarshalBinary(bin); err != nil {
|
|
return err
|
|
}
|
|
if typ.typ != ResTableType {
|
|
return errWrongType(typ.typ, ResTableType)
|
|
}
|
|
|
|
typ.id = uint8(bin[8])
|
|
typ.res0 = uint8(bin[9])
|
|
typ.res1 = btou16(bin[10:])
|
|
typ.entryCount = btou32(bin[12:])
|
|
typ.entriesStart = btou32(bin[16:])
|
|
|
|
if typ.res0 != 0 || typ.res1 != 0 {
|
|
return fmt.Errorf("res0 res1 not zero")
|
|
}
|
|
|
|
typ.config.size = btou32(bin[20:])
|
|
typ.config.imsi.mcc = btou16(bin[24:])
|
|
typ.config.imsi.mnc = btou16(bin[26:])
|
|
typ.config.locale.language = btou16(bin[28:])
|
|
typ.config.locale.country = btou16(bin[30:])
|
|
typ.config.screenType.orientation = uint8(bin[32])
|
|
typ.config.screenType.touchscreen = uint8(bin[33])
|
|
typ.config.screenType.density = btou16(bin[34:])
|
|
typ.config.input.keyboard = uint8(bin[36])
|
|
typ.config.input.navigation = uint8(bin[37])
|
|
typ.config.input.inputFlags = uint8(bin[38])
|
|
typ.config.input.inputPad0 = uint8(bin[39])
|
|
typ.config.screenSize.width = btou16(bin[40:])
|
|
typ.config.screenSize.height = btou16(bin[42:])
|
|
typ.config.version.sdk = btou16(bin[44:])
|
|
typ.config.version.minor = btou16(bin[46:])
|
|
typ.config.screenConfig.layout = uint8(bin[48])
|
|
typ.config.screenConfig.uiMode = uint8(bin[49])
|
|
typ.config.screenConfig.smallestWidthDP = btou16(bin[50:])
|
|
typ.config.screenSizeDP.width = btou16(bin[52:])
|
|
typ.config.screenSizeDP.height = btou16(bin[54:])
|
|
|
|
// fmt.Println("language/country:", u16tos(typ.config.locale.language), u16tos(typ.config.locale.country))
|
|
|
|
buf := bin[typ.headerByteSize:typ.entriesStart]
|
|
for len(buf) > 0 {
|
|
typ.indices = append(typ.indices, btou32(buf))
|
|
buf = buf[4:]
|
|
}
|
|
|
|
if len(typ.indices) != int(typ.entryCount) {
|
|
return fmt.Errorf("indices len[%v] doesn't match entryCount[%v]", len(typ.indices), typ.entryCount)
|
|
}
|
|
typ.entries = make([]*Entry, typ.entryCount)
|
|
|
|
for i, x := range typ.indices {
|
|
if x == NoEntry {
|
|
continue
|
|
}
|
|
nt := &Entry{}
|
|
if err := nt.UnmarshalBinary(bin[typ.entriesStart+x:]); err != nil {
|
|
return err
|
|
}
|
|
typ.entries[i] = nt
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (typ *Type) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 56+len(typ.entries)*4)
|
|
putu16(bin, uint16(ResTableType))
|
|
putu16(bin[2:], 56)
|
|
|
|
bin[8] = byte(typ.id)
|
|
// [9] = 0
|
|
// [10:12] = 0
|
|
putu32(bin[12:], uint32(len(typ.entries)))
|
|
putu32(bin[16:], uint32(56+len(typ.entries)*4))
|
|
|
|
var ntbin []byte
|
|
for i, nt := range typ.entries {
|
|
if nt == nil { // NoEntry
|
|
putu32(bin[56+i*4:], NoEntry)
|
|
continue
|
|
}
|
|
putu32(bin[56+i*4:], uint32(len(ntbin)))
|
|
b, err := nt.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ntbin = append(ntbin, b...)
|
|
}
|
|
bin = append(bin, ntbin...)
|
|
|
|
putu32(bin[4:], uint32(len(bin)))
|
|
return bin, nil
|
|
}
|
|
|
|
// Entry is a resource key typically followed by a value or resource map.
|
|
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
|
|
}
|
|
|
|
func (nt *Entry) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 8)
|
|
putu16(bin, nt.size)
|
|
putu16(bin[2:], nt.flags)
|
|
putu32(bin[4:], uint32(nt.key))
|
|
|
|
if nt.size == 16 {
|
|
bin = append(bin, make([]byte, 8+len(nt.values)*12)...)
|
|
putu32(bin[8:], uint32(nt.parent))
|
|
putu32(bin[12:], uint32(len(nt.values)))
|
|
for i, val := range nt.values {
|
|
b, err := val.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
copy(bin[16+i*12:], b)
|
|
}
|
|
} else {
|
|
b, err := nt.values[0].data.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bin = append(bin, b...)
|
|
}
|
|
|
|
return bin, 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:])
|
|
}
|
|
|
|
func (val *Value) MarshalBinary() ([]byte, error) {
|
|
bin := make([]byte, 12)
|
|
putu32(bin, uint32(val.name))
|
|
b, err := val.data.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
copy(bin[4:], b)
|
|
return bin, nil
|
|
}
|
|
|
|
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
|
|
}
|