github.com/shranet/mobile@v0.0.0-20200814083559-5702cdcd481b/internal/binres/table.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package binres
     6  
     7  import (
     8  	"bytes"
     9  	"compress/gzip"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"strings"
    14  	"unicode/utf16"
    15  )
    16  
    17  const NoEntry = 0xFFFFFFFF
    18  
    19  // TableRef uniquely identifies entries within a resource table.
    20  type TableRef uint32
    21  
    22  // Resolve returns the Entry of TableRef in the given table.
    23  //
    24  // A TableRef is structured as follows:
    25  //  0xpptteeee
    26  //  pp: package index
    27  //  tt: type spec index in package
    28  //  eeee: entry index in type spec
    29  //
    30  // The package and type spec values start at 1 for the first item,
    31  // to help catch cases where they have not been supplied.
    32  func (ref TableRef) Resolve(tbl *Table) (*Entry, error) {
    33  	pkg := tbl.pkgs[uint8(ref>>24)-1]
    34  	spec := pkg.specs[uint8(ref>>16)-1]
    35  	idx := uint16(ref)
    36  
    37  	for _, typ := range spec.types {
    38  		if idx < uint16(len(typ.entries)) {
    39  			nt := typ.entries[idx]
    40  			if nt == nil {
    41  				return nil, errors.New("nil entry match")
    42  			}
    43  			return nt, nil
    44  		}
    45  	}
    46  
    47  	return nil, errors.New("failed to resolve table reference")
    48  }
    49  
    50  // Table is a container for packaged resources. Resource values within a package
    51  // are obtained through pool while resource names and identifiers are obtained
    52  // through each package's type and key pools respectively.
    53  type Table struct {
    54  	chunkHeader
    55  	pool *Pool
    56  	pkgs []*Package
    57  }
    58  
    59  // NewMipmapTable returns a resource table initialized for a single xxxhdpi mipmap resource
    60  // and the path to write resource data to.
    61  func NewMipmapTable(pkgname string) (*Table, string) {
    62  	pkg := &Package{id: 127, name: pkgname, typePool: &Pool{}, keyPool: &Pool{}}
    63  
    64  	attr := pkg.typePool.ref("attr")
    65  	mipmap := pkg.typePool.ref("mipmap")
    66  	icon := pkg.keyPool.ref("icon")
    67  
    68  	nt := &Entry{values: []*Value{{data: &Data{Type: DataString}}}}
    69  	typ := &Type{id: 2, indices: []uint32{0}, entries: []*Entry{nt}}
    70  	typ.config.screenType.density = 640
    71  	typ.config.version.sdk = 4
    72  
    73  	pkg.specs = append(pkg.specs,
    74  		&TypeSpec{
    75  			id: uint8(attr) + 1, //1,
    76  		},
    77  		&TypeSpec{
    78  			id:         uint8(mipmap) + 1, //2,
    79  			entryCount: 1,
    80  			entries:    []uint32{uint32(icon)}, // {0}
    81  			types:      []*Type{typ},
    82  		})
    83  
    84  	pkg.lastPublicType = uint32(len(pkg.typePool.strings)) // 2
    85  	pkg.lastPublicKey = uint32(len(pkg.keyPool.strings))   // 1
    86  
    87  	name := "res/mipmap-xxxhdpi-v4/icon.png"
    88  	tbl := &Table{pool: &Pool{}, pkgs: []*Package{pkg}}
    89  	tbl.pool.ref(name)
    90  	return tbl, name
    91  }
    92  
    93  // OpenSDKTable decodes resources.arsc from sdk platform jar.
    94  func OpenSDKTable() (*Table, error) {
    95  	bin, err := apiResources()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	tbl := new(Table)
   100  	if err := tbl.UnmarshalBinary(bin); err != nil {
   101  		return nil, err
   102  	}
   103  	return tbl, nil
   104  }
   105  
   106  // OpenTable decodes the prepacked resources.arsc for the supported sdk platform.
   107  func OpenTable() (*Table, error) {
   108  	zr, err := gzip.NewReader(bytes.NewReader(arsc))
   109  	if err != nil {
   110  		return nil, fmt.Errorf("gzip: %v", err)
   111  	}
   112  	defer zr.Close()
   113  
   114  	var buf bytes.Buffer
   115  	if _, err := io.Copy(&buf, zr); err != nil {
   116  		return nil, fmt.Errorf("io: %v", err)
   117  	}
   118  	tbl := new(Table)
   119  	if err := tbl.UnmarshalBinary(buf.Bytes()); err != nil {
   120  		return nil, err
   121  	}
   122  	return tbl, nil
   123  }
   124  
   125  // SpecByName parses the spec name from an entry string if necessary and returns
   126  // the Package and TypeSpec associated with that name along with their respective
   127  // indices.
   128  //
   129  // For example:
   130  //  tbl.SpecByName("@android:style/Theme.NoTitleBar")
   131  //  tbl.SpecByName("style")
   132  // Both locate the spec by name "style".
   133  func (tbl *Table) SpecByName(name string) (int, *Package, int, *TypeSpec, error) {
   134  	n := strings.TrimPrefix(name, "@android:")
   135  	n = strings.Split(n, "/")[0]
   136  	for pp, pkg := range tbl.pkgs {
   137  		for tt, spec := range pkg.specs {
   138  			if n == pkg.typePool.strings[spec.id-1] {
   139  				return pp, pkg, tt, spec, nil
   140  			}
   141  		}
   142  	}
   143  	return 0, nil, 0, nil, fmt.Errorf("spec by name not found: %q", name)
   144  }
   145  
   146  // RefByName returns the TableRef by a given name. The ref may be used to resolve the
   147  // associated Entry and is used for the generation of binary manifest files.
   148  func (tbl *Table) RefByName(name string) (TableRef, error) {
   149  	pp, pkg, tt, spec, err := tbl.SpecByName(name)
   150  	if err != nil {
   151  		return 0, err
   152  	}
   153  
   154  	q := strings.Split(name, "/")
   155  	if len(q) != 2 {
   156  		return 0, fmt.Errorf("invalid entry format, missing forward-slash: %q", name)
   157  	}
   158  	n := q[1]
   159  
   160  	for _, t := range spec.types {
   161  		for eeee, nt := range t.entries {
   162  			if nt == nil { // NoEntry
   163  				continue
   164  			}
   165  			if n == pkg.keyPool.strings[nt.key] {
   166  				return TableRef(uint32(eeee) | uint32(tt+1)<<16 | uint32(pp+1)<<24), nil
   167  			}
   168  		}
   169  	}
   170  	return 0, fmt.Errorf("failed to find table ref by %q", name)
   171  }
   172  
   173  func (tbl *Table) UnmarshalBinary(bin []byte) error {
   174  	if err := (&tbl.chunkHeader).UnmarshalBinary(bin); err != nil {
   175  		return err
   176  	}
   177  	if tbl.typ != ResTable {
   178  		return fmt.Errorf("unexpected resource type %s, want %s", tbl.typ, ResTable)
   179  	}
   180  
   181  	npkgs := btou32(bin[8:])
   182  	tbl.pkgs = make([]*Package, npkgs)
   183  
   184  	buf := bin[tbl.headerByteSize:]
   185  
   186  	tbl.pool = new(Pool)
   187  	if err := tbl.pool.UnmarshalBinary(buf); err != nil {
   188  		return err
   189  	}
   190  	buf = buf[tbl.pool.size():]
   191  	for i := range tbl.pkgs {
   192  		pkg := new(Package)
   193  		if err := pkg.UnmarshalBinary(buf); err != nil {
   194  			return err
   195  		}
   196  		tbl.pkgs[i] = pkg
   197  		buf = buf[pkg.byteSize:]
   198  	}
   199  
   200  	return nil
   201  }
   202  
   203  func (tbl *Table) MarshalBinary() ([]byte, error) {
   204  	bin := make([]byte, 12)
   205  	putu16(bin, uint16(ResTable))
   206  	putu16(bin[2:], 12)
   207  	putu32(bin[8:], uint32(len(tbl.pkgs)))
   208  
   209  	if tbl.pool.IsUTF8() {
   210  		tbl.pool.flags ^= UTF8Flag
   211  		defer func() {
   212  			tbl.pool.flags |= UTF8Flag
   213  		}()
   214  	}
   215  
   216  	b, err := tbl.pool.MarshalBinary()
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	bin = append(bin, b...)
   221  
   222  	for _, pkg := range tbl.pkgs {
   223  		b, err = pkg.MarshalBinary()
   224  		if err != nil {
   225  			return nil, err
   226  		}
   227  		bin = append(bin, b...)
   228  	}
   229  
   230  	putu32(bin[4:], uint32(len(bin)))
   231  
   232  	return bin, nil
   233  }
   234  
   235  // Package contains a collection of resource data types.
   236  type Package struct {
   237  	chunkHeader
   238  
   239  	id   uint32
   240  	name string
   241  
   242  	lastPublicType uint32 // last index into typePool that is for public use
   243  	lastPublicKey  uint32 // last index into keyPool that is for public use
   244  
   245  	typePool *Pool // type names; e.g. theme
   246  	keyPool  *Pool // resource names; e.g. Theme.NoTitleBar
   247  
   248  	specs []*TypeSpec
   249  }
   250  
   251  func (pkg *Package) UnmarshalBinary(bin []byte) error {
   252  	if err := (&pkg.chunkHeader).UnmarshalBinary(bin); err != nil {
   253  		return err
   254  	}
   255  	if pkg.typ != ResTablePackage {
   256  		return errWrongType(pkg.typ, ResTablePackage)
   257  	}
   258  
   259  	pkg.id = btou32(bin[8:])
   260  
   261  	var name []uint16
   262  	for i := 0; i < 128; i++ {
   263  		x := btou16(bin[12+i*2:])
   264  		if x == 0 {
   265  			break
   266  		}
   267  		name = append(name, x)
   268  	}
   269  	pkg.name = string(utf16.Decode(name))
   270  
   271  	typeOffset := btou32(bin[268:]) // 0 if inheriting from another package
   272  	pkg.lastPublicType = btou32(bin[272:])
   273  	keyOffset := btou32(bin[276:]) // 0 if inheriting from another package
   274  	pkg.lastPublicKey = btou32(bin[280:])
   275  
   276  	var idOffset uint32 // value determined by either typePool or keyPool below
   277  
   278  	if typeOffset != 0 {
   279  		pkg.typePool = new(Pool)
   280  		if err := pkg.typePool.UnmarshalBinary(bin[typeOffset:]); err != nil {
   281  			return err
   282  		}
   283  		idOffset = typeOffset + pkg.typePool.byteSize
   284  	}
   285  
   286  	if keyOffset != 0 {
   287  		pkg.keyPool = new(Pool)
   288  		if err := pkg.keyPool.UnmarshalBinary(bin[keyOffset:]); err != nil {
   289  			return err
   290  		}
   291  		idOffset = keyOffset + pkg.keyPool.byteSize
   292  	}
   293  
   294  	if idOffset == 0 {
   295  		return nil
   296  	}
   297  
   298  	buf := bin[idOffset:]
   299  	for len(buf) > 0 {
   300  		t := ResType(btou16(buf))
   301  		switch t {
   302  		case ResTableTypeSpec:
   303  			spec := new(TypeSpec)
   304  			if err := spec.UnmarshalBinary(buf); err != nil {
   305  				return err
   306  			}
   307  			pkg.specs = append(pkg.specs, spec)
   308  			buf = buf[spec.byteSize:]
   309  		case ResTableType:
   310  			typ := new(Type)
   311  			if err := typ.UnmarshalBinary(buf); err != nil {
   312  				return err
   313  			}
   314  			last := pkg.specs[len(pkg.specs)-1]
   315  			last.types = append(last.types, typ)
   316  			buf = buf[typ.byteSize:]
   317  		default:
   318  			return errWrongType(t, ResTableTypeSpec, ResTableType)
   319  		}
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  func (pkg *Package) MarshalBinary() ([]byte, error) {
   326  	// Package header size is determined by C++ struct ResTable_package
   327  	// see frameworks/base/include/ResourceTypes.h
   328  	bin := make([]byte, 288)
   329  	putu16(bin, uint16(ResTablePackage))
   330  	putu16(bin[2:], 288)
   331  
   332  	putu32(bin[8:], pkg.id)
   333  	p := utf16.Encode([]rune(pkg.name))
   334  	for i, x := range p {
   335  		putu16(bin[12+i*2:], x)
   336  	}
   337  
   338  	if pkg.typePool != nil {
   339  		if pkg.typePool.IsUTF8() {
   340  			pkg.typePool.flags ^= UTF8Flag
   341  			defer func() {
   342  				pkg.typePool.flags |= UTF8Flag
   343  			}()
   344  		}
   345  
   346  		b, err := pkg.typePool.MarshalBinary()
   347  		if err != nil {
   348  			return nil, err
   349  		}
   350  		putu32(bin[268:], uint32(len(bin)))
   351  		putu32(bin[272:], pkg.lastPublicType)
   352  		bin = append(bin, b...)
   353  	}
   354  
   355  	if pkg.keyPool != nil {
   356  		if pkg.keyPool.IsUTF8() {
   357  			pkg.keyPool.flags ^= UTF8Flag
   358  			defer func() {
   359  				pkg.keyPool.flags |= UTF8Flag
   360  			}()
   361  		}
   362  		b, err := pkg.keyPool.MarshalBinary()
   363  		if err != nil {
   364  			return nil, err
   365  		}
   366  		putu32(bin[276:], uint32(len(bin)))
   367  		putu32(bin[280:], pkg.lastPublicKey)
   368  		bin = append(bin, b...)
   369  	}
   370  
   371  	for _, spec := range pkg.specs {
   372  		b, err := spec.MarshalBinary()
   373  		if err != nil {
   374  			return nil, err
   375  		}
   376  		bin = append(bin, b...)
   377  	}
   378  
   379  	putu32(bin[4:], uint32(len(bin)))
   380  	return bin, nil
   381  }
   382  
   383  // TypeSpec provides a specification for the resources defined by a particular type.
   384  type TypeSpec struct {
   385  	chunkHeader
   386  	id         uint8  // id-1 is name index in Package.typePool
   387  	res0       uint8  // must be 0
   388  	res1       uint16 // must be 0
   389  	entryCount uint32 // number of uint32 entry configuration masks that follow
   390  
   391  	entries []uint32 // entry configuration masks
   392  	types   []*Type
   393  }
   394  
   395  func (spec *TypeSpec) UnmarshalBinary(bin []byte) error {
   396  	if err := (&spec.chunkHeader).UnmarshalBinary(bin); err != nil {
   397  		return err
   398  	}
   399  	if spec.typ != ResTableTypeSpec {
   400  		return errWrongType(spec.typ, ResTableTypeSpec)
   401  	}
   402  	spec.id = uint8(bin[8])
   403  	spec.res0 = uint8(bin[9])
   404  	spec.res1 = btou16(bin[10:])
   405  	spec.entryCount = btou32(bin[12:])
   406  
   407  	spec.entries = make([]uint32, spec.entryCount)
   408  	for i := range spec.entries {
   409  		spec.entries[i] = btou32(bin[16+i*4:])
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  func (spec *TypeSpec) MarshalBinary() ([]byte, error) {
   416  	bin := make([]byte, 16+len(spec.entries)*4)
   417  	putu16(bin, uint16(ResTableTypeSpec))
   418  	putu16(bin[2:], 16)
   419  	putu32(bin[4:], uint32(len(bin)))
   420  
   421  	bin[8] = byte(spec.id)
   422  	// [9] = 0
   423  	// [10:12] = 0
   424  	putu32(bin[12:], uint32(len(spec.entries)))
   425  	for i, x := range spec.entries {
   426  		putu32(bin[16+i*4:], x)
   427  	}
   428  
   429  	for _, typ := range spec.types {
   430  		b, err := typ.MarshalBinary()
   431  		if err != nil {
   432  			return nil, err
   433  		}
   434  		bin = append(bin, b...)
   435  	}
   436  
   437  	return bin, nil
   438  }
   439  
   440  // Type provides a collection of entries for a specific device configuration.
   441  type Type struct {
   442  	chunkHeader
   443  	id           uint8
   444  	res0         uint8  // must be 0
   445  	res1         uint16 // must be 0
   446  	entryCount   uint32 // number of uint32 entry configuration masks that follow
   447  	entriesStart uint32 // offset from header where Entry data starts
   448  
   449  	// configuration this collection of entries is designed for
   450  	config struct {
   451  		size uint32
   452  		imsi struct {
   453  			mcc uint16 // mobile country code
   454  			mnc uint16 // mobile network code
   455  		}
   456  		locale struct {
   457  			language uint16
   458  			country  uint16
   459  		}
   460  		screenType struct {
   461  			orientation uint8
   462  			touchscreen uint8
   463  			density     uint16
   464  		}
   465  		input struct {
   466  			keyboard   uint8
   467  			navigation uint8
   468  			inputFlags uint8
   469  			inputPad0  uint8
   470  		}
   471  		screenSize struct {
   472  			width  uint16
   473  			height uint16
   474  		}
   475  		version struct {
   476  			sdk   uint16
   477  			minor uint16 // always 0
   478  		}
   479  		screenConfig struct {
   480  			layout          uint8
   481  			uiMode          uint8
   482  			smallestWidthDP uint16
   483  		}
   484  		screenSizeDP struct {
   485  			width  uint16
   486  			height uint16
   487  		}
   488  	}
   489  
   490  	indices []uint32 // values that map to typePool
   491  	entries []*Entry
   492  }
   493  
   494  func (typ *Type) UnmarshalBinary(bin []byte) error {
   495  	if err := (&typ.chunkHeader).UnmarshalBinary(bin); err != nil {
   496  		return err
   497  	}
   498  	if typ.typ != ResTableType {
   499  		return errWrongType(typ.typ, ResTableType)
   500  	}
   501  
   502  	typ.id = uint8(bin[8])
   503  	typ.res0 = uint8(bin[9])
   504  	typ.res1 = btou16(bin[10:])
   505  	typ.entryCount = btou32(bin[12:])
   506  	typ.entriesStart = btou32(bin[16:])
   507  
   508  	if typ.res0 != 0 || typ.res1 != 0 {
   509  		return fmt.Errorf("res0 res1 not zero")
   510  	}
   511  
   512  	typ.config.size = btou32(bin[20:])
   513  	typ.config.imsi.mcc = btou16(bin[24:])
   514  	typ.config.imsi.mnc = btou16(bin[26:])
   515  	typ.config.locale.language = btou16(bin[28:])
   516  	typ.config.locale.country = btou16(bin[30:])
   517  	typ.config.screenType.orientation = uint8(bin[32])
   518  	typ.config.screenType.touchscreen = uint8(bin[33])
   519  	typ.config.screenType.density = btou16(bin[34:])
   520  	typ.config.input.keyboard = uint8(bin[36])
   521  	typ.config.input.navigation = uint8(bin[37])
   522  	typ.config.input.inputFlags = uint8(bin[38])
   523  	typ.config.input.inputPad0 = uint8(bin[39])
   524  	typ.config.screenSize.width = btou16(bin[40:])
   525  	typ.config.screenSize.height = btou16(bin[42:])
   526  	typ.config.version.sdk = btou16(bin[44:])
   527  	typ.config.version.minor = btou16(bin[46:])
   528  	typ.config.screenConfig.layout = uint8(bin[48])
   529  	typ.config.screenConfig.uiMode = uint8(bin[49])
   530  	typ.config.screenConfig.smallestWidthDP = btou16(bin[50:])
   531  	typ.config.screenSizeDP.width = btou16(bin[52:])
   532  	typ.config.screenSizeDP.height = btou16(bin[54:])
   533  
   534  	// fmt.Println("language/country:", u16tos(typ.config.locale.language), u16tos(typ.config.locale.country))
   535  
   536  	buf := bin[typ.headerByteSize:typ.entriesStart]
   537  	for len(buf) > 0 {
   538  		typ.indices = append(typ.indices, btou32(buf))
   539  		buf = buf[4:]
   540  	}
   541  
   542  	if len(typ.indices) != int(typ.entryCount) {
   543  		return fmt.Errorf("indices len[%v] doesn't match entryCount[%v]", len(typ.indices), typ.entryCount)
   544  	}
   545  	typ.entries = make([]*Entry, typ.entryCount)
   546  
   547  	for i, x := range typ.indices {
   548  		if x == NoEntry {
   549  			continue
   550  		}
   551  		nt := &Entry{}
   552  		if err := nt.UnmarshalBinary(bin[typ.entriesStart+x:]); err != nil {
   553  			return err
   554  		}
   555  		typ.entries[i] = nt
   556  	}
   557  
   558  	return nil
   559  }
   560  
   561  func (typ *Type) MarshalBinary() ([]byte, error) {
   562  	bin := make([]byte, 56+len(typ.entries)*4)
   563  	putu16(bin, uint16(ResTableType))
   564  	putu16(bin[2:], 56)
   565  
   566  	bin[8] = byte(typ.id)
   567  	// [9] = 0
   568  	// [10:12] = 0
   569  	putu32(bin[12:], uint32(len(typ.entries)))
   570  	putu32(bin[16:], uint32(56+len(typ.entries)*4))
   571  
   572  	// assure typ.config.size is always written as 52; extended configuration beyond supported
   573  	// API level is not supported by this marshal implementation but will be forward-compatible.
   574  	putu32(bin[20:], 52)
   575  
   576  	putu16(bin[24:], typ.config.imsi.mcc)
   577  	putu16(bin[26:], typ.config.imsi.mnc)
   578  	putu16(bin[28:], typ.config.locale.language)
   579  	putu16(bin[30:], typ.config.locale.country)
   580  	bin[32] = typ.config.screenType.orientation
   581  	bin[33] = typ.config.screenType.touchscreen
   582  	putu16(bin[34:], typ.config.screenType.density)
   583  	bin[36] = typ.config.input.keyboard
   584  	bin[37] = typ.config.input.navigation
   585  	bin[38] = typ.config.input.inputFlags
   586  	bin[39] = typ.config.input.inputPad0
   587  	putu16(bin[40:], typ.config.screenSize.width)
   588  	putu16(bin[42:], typ.config.screenSize.height)
   589  	putu16(bin[44:], typ.config.version.sdk)
   590  	putu16(bin[46:], typ.config.version.minor)
   591  	bin[48] = typ.config.screenConfig.layout
   592  	bin[49] = typ.config.screenConfig.uiMode
   593  	putu16(bin[50:], typ.config.screenConfig.smallestWidthDP)
   594  	putu16(bin[52:], typ.config.screenSizeDP.width)
   595  	putu16(bin[54:], typ.config.screenSizeDP.height)
   596  
   597  	var ntbin []byte
   598  	for i, nt := range typ.entries {
   599  		if nt == nil { // NoEntry
   600  			putu32(bin[56+i*4:], NoEntry)
   601  			continue
   602  		}
   603  		putu32(bin[56+i*4:], uint32(len(ntbin)))
   604  		b, err := nt.MarshalBinary()
   605  		if err != nil {
   606  			return nil, err
   607  		}
   608  		ntbin = append(ntbin, b...)
   609  	}
   610  	bin = append(bin, ntbin...)
   611  
   612  	putu32(bin[4:], uint32(len(bin)))
   613  	return bin, nil
   614  }
   615  
   616  // Entry is a resource key typically followed by a value or resource map.
   617  type Entry struct {
   618  	size  uint16
   619  	flags uint16
   620  	key   PoolRef // ref into key pool
   621  
   622  	// only filled if this is a map entry; when size is 16
   623  	parent TableRef // id of parent mapping or zero if none
   624  	count  uint32   // name and value pairs that follow for FlagComplex
   625  
   626  	values []*Value
   627  }
   628  
   629  func (nt *Entry) UnmarshalBinary(bin []byte) error {
   630  	nt.size = btou16(bin)
   631  	nt.flags = btou16(bin[2:])
   632  	nt.key = PoolRef(btou32(bin[4:]))
   633  
   634  	if nt.size == 16 {
   635  		nt.parent = TableRef(btou32(bin[8:]))
   636  		nt.count = btou32(bin[12:])
   637  		nt.values = make([]*Value, nt.count)
   638  		for i := range nt.values {
   639  			val := &Value{}
   640  			if err := val.UnmarshalBinary(bin[16+i*12:]); err != nil {
   641  				return err
   642  			}
   643  			nt.values[i] = val
   644  		}
   645  	} else {
   646  		data := &Data{}
   647  		if err := data.UnmarshalBinary(bin[8:]); err != nil {
   648  			return err
   649  		}
   650  		// TODO boxing data not strictly correct as binary repr isn't of Value.
   651  		nt.values = append(nt.values, &Value{0, data})
   652  	}
   653  
   654  	return nil
   655  }
   656  
   657  func (nt *Entry) MarshalBinary() ([]byte, error) {
   658  	bin := make([]byte, 8)
   659  	sz := nt.size
   660  	if sz == 0 {
   661  		sz = 8
   662  	}
   663  	putu16(bin, sz)
   664  	putu16(bin[2:], nt.flags)
   665  	putu32(bin[4:], uint32(nt.key))
   666  
   667  	if sz == 16 {
   668  		bin = append(bin, make([]byte, 8+len(nt.values)*12)...)
   669  		putu32(bin[8:], uint32(nt.parent))
   670  		putu32(bin[12:], uint32(len(nt.values)))
   671  		for i, val := range nt.values {
   672  			b, err := val.MarshalBinary()
   673  			if err != nil {
   674  				return nil, err
   675  			}
   676  			copy(bin[16+i*12:], b)
   677  		}
   678  	} else {
   679  		b, err := nt.values[0].data.MarshalBinary()
   680  		if err != nil {
   681  			return nil, err
   682  		}
   683  		bin = append(bin, b...)
   684  	}
   685  
   686  	return bin, nil
   687  }
   688  
   689  type Value struct {
   690  	name TableRef
   691  	data *Data
   692  }
   693  
   694  func (val *Value) UnmarshalBinary(bin []byte) error {
   695  	val.name = TableRef(btou32(bin))
   696  	val.data = &Data{}
   697  	return val.data.UnmarshalBinary(bin[4:])
   698  }
   699  
   700  func (val *Value) MarshalBinary() ([]byte, error) {
   701  	bin := make([]byte, 12)
   702  	putu32(bin, uint32(val.name))
   703  	b, err := val.data.MarshalBinary()
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  	copy(bin[4:], b)
   708  	return bin, nil
   709  }
   710  
   711  type DataType uint8
   712  
   713  // explicitly defined for clarity and resolvability with apt source
   714  const (
   715  	DataNull             DataType = 0x00 // either 0 or 1 for resource undefined or empty
   716  	DataReference        DataType = 0x01 // ResTable_ref, a reference to another resource table entry
   717  	DataAttribute        DataType = 0x02 // attribute resource identifier
   718  	DataString           DataType = 0x03 // index into the containing resource table's global value string pool
   719  	DataFloat            DataType = 0x04 // single-precision floating point number
   720  	DataDimension        DataType = 0x05 // complex number encoding a dimension value, such as "100in"
   721  	DataFraction         DataType = 0x06 // complex number encoding a fraction of a container
   722  	DataDynamicReference DataType = 0x07 // dynamic ResTable_ref, which needs to be resolved before it can be used like a TYPE_REFERENCE.
   723  	DataIntDec           DataType = 0x10 // raw integer value of the form n..n
   724  	DataIntHex           DataType = 0x11 // raw integer value of the form 0xn..n
   725  	DataIntBool          DataType = 0x12 // either 0 or 1, for input "false" or "true"
   726  	DataIntColorARGB8    DataType = 0x1c // raw integer value of the form #aarrggbb
   727  	DataIntColorRGB8     DataType = 0x1d // raw integer value of the form #rrggbb
   728  	DataIntColorARGB4    DataType = 0x1e // raw integer value of the form #argb
   729  	DataIntColorRGB4     DataType = 0x1f // raw integer value of the form #rgb
   730  )
   731  
   732  type Data struct {
   733  	ByteSize uint16
   734  	Res0     uint8 // always 0, useful for debugging bad read offsets
   735  	Type     DataType
   736  	Value    uint32
   737  }
   738  
   739  func (d *Data) UnmarshalBinary(bin []byte) error {
   740  	d.ByteSize = btou16(bin)
   741  	d.Res0 = uint8(bin[2])
   742  	d.Type = DataType(bin[3])
   743  	d.Value = btou32(bin[4:])
   744  	return nil
   745  }
   746  
   747  func (d *Data) MarshalBinary() ([]byte, error) {
   748  	bin := make([]byte, 8)
   749  	putu16(bin, 8)
   750  	bin[2] = byte(d.Res0)
   751  	bin[3] = byte(d.Type)
   752  	putu32(bin[4:], d.Value)
   753  	return bin, nil
   754  }