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 }