| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833 |
- // 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"
- "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
- }
- // NewMipmapTable returns a resource table initialized for a single xxxhdpi mipmap resource
- // and the path to write resource data to.
- func NewMipmapTable(pkgname string) (*Table, string) {
- pkg := &Package{id: 127, name: pkgname, typePool: &Pool{}, keyPool: &Pool{}}
- attr := pkg.typePool.ref("attr")
- mipmap := pkg.typePool.ref("mipmap")
- icon := pkg.keyPool.ref("icon")
- nt := &Entry{values: []*Value{{data: &Data{Type: DataString}}}}
- typ := &Type{id: 2, indices: []uint32{0}, entries: []*Entry{nt}}
- typ.config.screenType.density = 640
- typ.config.version.sdk = 4
- pkg.specs = append(pkg.specs,
- &TypeSpec{
- id: uint8(attr) + 1, //1,
- },
- &TypeSpec{
- id: uint8(mipmap) + 1, //2,
- entryCount: 1,
- entries: []uint32{uint32(icon)}, // {0}
- types: []*Type{typ},
- })
- pkg.lastPublicType = uint32(len(pkg.typePool.strings)) // 2
- pkg.lastPublicKey = uint32(len(pkg.keyPool.strings)) // 1
- name := "res/mipmap-xxxhdpi-v4/icon.png"
- tbl := &Table{pool: &Pool{}, pkgs: []*Package{pkg}}
- tbl.pool.ref(name)
- return tbl, name
- }
- // 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) {
- zr, err := gzip.NewReader(bytes.NewReader(arsc))
- if err != nil {
- return nil, fmt.Errorf("gzip: %v", err)
- }
- defer zr.Close()
- var buf bytes.Buffer
- if _, err := io.Copy(&buf, zr); err != nil {
- return nil, fmt.Errorf("io: %v", 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 along with their respective
- // indices.
- //
- // 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
- aliases []*StagedAlias
- 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:pkg.byteSize]
- for len(buf) > 0 {
- 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:]
- case ResTableStagedAlias:
- alias := new(StagedAlias)
- if err := alias.UnmarshalBinary(buf); err != nil {
- return err
- }
- pkg.aliases = append(pkg.aliases, alias)
- buf = buf[alias.byteSize:]
- default:
- return errWrongType(t, ResTableTypeSpec, ResTableType, ResTableStagedAlias)
- }
- }
- return nil
- }
- func (pkg *Package) MarshalBinary() ([]byte, error) {
- // Package header size is determined by C++ struct ResTable_package
- // see frameworks/base/include/ResourceTypes.h
- bin := make([]byte, 288)
- putu16(bin, uint16(ResTablePackage))
- putu16(bin[2:], 288)
- 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 _, alias := range pkg.aliases {
- b, err := alias.MarshalBinary()
- if err != nil {
- return nil, err
- }
- 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]
- if len(buf) != 4*int(typ.entryCount) {
- return fmt.Errorf("index buffer len[%v] doesn't match entryCount[%v]", len(buf), typ.entryCount)
- }
- typ.indices = make([]uint32, typ.entryCount)
- for i := range typ.indices {
- typ.indices[i] = btou32(buf[i*4:])
- }
- 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))
- // assure typ.config.size is always written as 36; extended configuration beyond supported
- // API level is not supported by this marshal implementation but will be forward-compatible.
- putu32(bin[20:], 36)
- putu16(bin[24:], typ.config.imsi.mcc)
- putu16(bin[26:], typ.config.imsi.mnc)
- putu16(bin[28:], typ.config.locale.language)
- putu16(bin[30:], typ.config.locale.country)
- bin[32] = typ.config.screenType.orientation
- bin[33] = typ.config.screenType.touchscreen
- putu16(bin[34:], typ.config.screenType.density)
- bin[36] = typ.config.input.keyboard
- bin[37] = typ.config.input.navigation
- bin[38] = typ.config.input.inputFlags
- bin[39] = typ.config.input.inputPad0
- putu16(bin[40:], typ.config.screenSize.width)
- putu16(bin[42:], typ.config.screenSize.height)
- putu16(bin[44:], typ.config.version.sdk)
- putu16(bin[46:], typ.config.version.minor)
- bin[48] = typ.config.screenConfig.layout
- bin[49] = typ.config.screenConfig.uiMode
- putu16(bin[50:], typ.config.screenConfig.smallestWidthDP)
- putu16(bin[52:], typ.config.screenSizeDP.width)
- putu16(bin[54:], typ.config.screenSizeDP.height)
- 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
- }
- type StagedAliasEntry struct {
- stagedID uint32
- finalizedID uint32
- }
- func (ae *StagedAliasEntry) MarshalBinary() ([]byte, error) {
- bin := make([]byte, 8)
- putu32(bin, ae.stagedID)
- putu32(bin[4:], ae.finalizedID)
- return bin, nil
- }
- func (ae *StagedAliasEntry) UnmarshalBinary(bin []byte) error {
- ae.stagedID = btou32(bin)
- ae.finalizedID = btou32(bin[4:])
- return nil
- }
- type StagedAlias struct {
- chunkHeader
- count uint32
- entries []StagedAliasEntry
- }
- func (a *StagedAlias) UnmarshalBinary(bin []byte) error {
- if err := (&a.chunkHeader).UnmarshalBinary(bin); err != nil {
- return err
- }
- if a.typ != ResTableStagedAlias {
- return errWrongType(a.typ, ResTableStagedAlias)
- }
- a.count = btou32(bin[8:])
- a.entries = make([]StagedAliasEntry, a.count)
- for i := range a.entries {
- if err := a.entries[i].UnmarshalBinary(bin[12+i*8:]); err != nil {
- return err
- }
- }
- return nil
- }
- func (a *StagedAlias) MarshalBinary() ([]byte, error) {
- chunkHeaderBin, err := a.chunkHeader.MarshalBinary()
- if err != nil {
- return nil, err
- }
- countBin := make([]byte, 4)
- putu32(countBin, a.count)
- bin := append(chunkHeaderBin, countBin...)
- for _, entry := range a.entries {
- entryBin, err := entry.MarshalBinary()
- if err != nil {
- return nil, err
- }
- bin = append(bin, entryBin...)
- }
- 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)
- sz := nt.size
- if sz == 0 {
- sz = 8
- }
- putu16(bin, sz)
- putu16(bin[2:], nt.flags)
- putu32(bin[4:], uint32(nt.key))
- if sz == 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, 8)
- bin[2] = byte(d.Res0)
- bin[3] = byte(d.Type)
- putu32(bin[4:], d.Value)
- return bin, nil
- }
|