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