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 }