github.com/goki/mobile@v0.0.0-20230707090321-193544ec5700/internal/binres/binres.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 //go:generate go run genarsc.go 6 //go:generate stringer -output binres_string.go -type ResType,DataType 7 8 // Package binres implements encoding and decoding of android binary resources. 9 // 10 // Binary resource structs support unmarshalling the binary output of aapt. 11 // Implementations of marshalling for each struct must produce the exact input 12 // sent to unmarshalling. This allows tests to validate each struct representation 13 // of the binary format as follows: 14 // 15 // - unmarshal the output of aapt 16 // - marshal the struct representation 17 // - perform byte-to-byte comparison with aapt output per chunk header and body 18 // 19 // This process should strive to make structs idiomatic to make parsing xml text 20 // into structs trivial. 21 // 22 // Once the struct representation is validated, tests for parsing xml text 23 // into structs can become self-referential as the following holds true: 24 // 25 // - the unmarshalled input of aapt output is the only valid target 26 // - the unmarshalled input of xml text may be compared to the unmarshalled 27 // input of aapt output to identify errors, e.g. text-trims, wrong flags, etc 28 // 29 // This provides validation, byte-for-byte, for producing binary xml resources. 30 // 31 // It should be made clear that unmarshalling binary resources is currently only 32 // in scope for proving that the BinaryMarshaler works correctly. Any other use 33 // is currently out of scope. 34 // 35 // A simple view of binary xml document structure: 36 // 37 // XML 38 // Pool 39 // Map 40 // Namespace 41 // [...node] 42 // 43 // Additional resources: 44 // https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h 45 // https://justanapplication.wordpress.com/2011/09/13/ (a series of articles, increment date) 46 package binres 47 48 import ( 49 "encoding" 50 "encoding/binary" 51 "encoding/xml" 52 "fmt" 53 "io" 54 "sort" 55 "strconv" 56 "strings" 57 "unicode" 58 ) 59 60 func errWrongType(have ResType, want ...ResType) error { 61 return fmt.Errorf("wrong resource type %s, want one of %v", have, want) 62 } 63 64 type ResType uint16 65 66 func (t ResType) IsSupported() bool { 67 return t != ResNull 68 } 69 70 // explicitly defined for clarity and resolvability with apt source 71 const ( 72 ResNull ResType = 0x0000 73 ResStringPool ResType = 0x0001 74 ResTable ResType = 0x0002 75 ResXML ResType = 0x0003 76 77 ResXMLStartNamespace ResType = 0x0100 78 ResXMLEndNamespace ResType = 0x0101 79 ResXMLStartElement ResType = 0x0102 80 ResXMLEndElement ResType = 0x0103 81 ResXMLCharData ResType = 0x0104 82 83 ResXMLResourceMap ResType = 0x0180 84 85 ResTablePackage ResType = 0x0200 86 ResTableType ResType = 0x0201 87 ResTableTypeSpec ResType = 0x0202 88 ResTableLibrary ResType = 0x0203 89 ResTableOverlayable ResType = 0x0204 90 ResTableOverlayablePolicy ResType = 0x0205 91 ResTableStagedAlias ResType = 0x0206 92 ) 93 94 var ( 95 btou16 = binary.LittleEndian.Uint16 96 btou32 = binary.LittleEndian.Uint32 97 putu16 = binary.LittleEndian.PutUint16 98 putu32 = binary.LittleEndian.PutUint32 99 ) 100 101 // unmarshaler wraps BinaryUnmarshaler to provide byte size of decoded chunks. 102 type unmarshaler interface { 103 encoding.BinaryUnmarshaler 104 105 // size returns the byte size unmarshalled after a call to 106 // UnmarshalBinary, or otherwise zero. 107 size() int 108 } 109 110 // chunkHeader appears at the front of every data chunk in a resource. 111 type chunkHeader struct { 112 // Type of data that follows this header. 113 typ ResType 114 115 // Advance slice index by this value to find its associated data, if any. 116 headerByteSize uint16 117 118 // This is the header size plus the size of any data associated with the chunk. 119 // Advance slice index by this value to completely skip its contents, including 120 // any child chunks. If this value is the same as headerByteSize, there is 121 // no data associated with the chunk. 122 byteSize uint32 123 } 124 125 // size implements unmarshaler. 126 func (hdr chunkHeader) size() int { return int(hdr.byteSize) } 127 128 func (hdr *chunkHeader) UnmarshalBinary(bin []byte) error { 129 hdr.typ = ResType(btou16(bin)) 130 if !hdr.typ.IsSupported() { 131 return fmt.Errorf("%s not supported", hdr.typ) 132 } 133 hdr.headerByteSize = btou16(bin[2:]) 134 hdr.byteSize = btou32(bin[4:]) 135 if len(bin) < int(hdr.byteSize) { 136 return fmt.Errorf("too few bytes to unmarshal chunk body, have %v, need at-least %v", len(bin), hdr.byteSize) 137 } 138 return nil 139 } 140 141 func (hdr chunkHeader) MarshalBinary() ([]byte, error) { 142 if !hdr.typ.IsSupported() { 143 return nil, fmt.Errorf("%s not supported", hdr.typ) 144 } 145 146 bin := make([]byte, 8) 147 putu16(bin, uint16(hdr.typ)) 148 putu16(bin[2:], hdr.headerByteSize) 149 putu32(bin[4:], hdr.byteSize) 150 return bin, nil 151 } 152 153 type XML struct { 154 chunkHeader 155 156 Pool *Pool 157 Map *Map 158 159 Namespace *Namespace 160 Children []*Element 161 162 // tmp field used when unmarshalling binary 163 stack []*Element 164 } 165 166 // RawValueByName returns the original raw string value of first matching element attribute, or error if not exists. 167 // Given <manifest package="VAL" ...> then RawValueByName("manifest", xml.Name{Local: "package"}) returns "VAL". 168 func (bx *XML) RawValueByName(elname string, attrname xml.Name) (string, error) { 169 elref, err := bx.Pool.RefByName(elname) 170 if err != nil { 171 return "", err 172 } 173 nref, err := bx.Pool.RefByName(attrname.Local) 174 if err != nil { 175 return "", err 176 } 177 nsref := PoolRef(NoEntry) 178 if attrname.Space != "" { 179 nsref, err = bx.Pool.RefByName(attrname.Space) 180 if err != nil { 181 return "", err 182 } 183 } 184 185 for el := range bx.iterElements() { 186 if el.Name == elref { 187 for _, attr := range el.attrs { 188 // TODO enforce TypedValue DataString constraint? 189 if nsref == attr.NS && nref == attr.Name { 190 return bx.Pool.strings[int(attr.RawValue)], nil 191 } 192 } 193 } 194 } 195 return "", fmt.Errorf("no matching element %q for attribute %+v found", elname, attrname) 196 } 197 198 const ( 199 androidSchema = "http://schemas.android.com/apk/res/android" 200 toolsSchema = "http://schemas.android.com/tools" 201 ) 202 203 // skipSynthesize is set true for tests to avoid synthesis of additional nodes and attributes. 204 var skipSynthesize bool 205 206 // UnmarshalXML decodes an AndroidManifest.xml document returning type XML 207 // containing decoded resources with the given minimum and target Android SDK / API version. 208 func UnmarshalXML(r io.Reader, withIcon bool, minSdkVersion, targetSdkVersion int) (*XML, error) { 209 tbl, err := OpenTable() 210 if err != nil { 211 return nil, err 212 } 213 214 lr := &lineReader{r: r} 215 dec := xml.NewDecoder(lr) 216 bx := new(XML) 217 218 // temporary pool to resolve real poolref later 219 pool := new(Pool) 220 221 type ltoken struct { 222 xml.Token 223 line int 224 } 225 var q []ltoken 226 227 for { 228 line := lr.line(dec.InputOffset()) 229 tkn, err := dec.Token() 230 if err != nil { 231 if err == io.EOF { 232 break 233 } 234 return nil, err 235 } 236 tkn = xml.CopyToken(tkn) 237 switch tkn := tkn.(type) { 238 case xml.StartElement: 239 switch tkn.Name.Local { 240 default: 241 q = append(q, ltoken{tkn, line}) 242 case "uses-sdk": 243 return nil, fmt.Errorf("manual declaration of uses-sdk in AndroidManifest.xml not supported") 244 case "manifest": 245 // synthesize additional attributes and nodes for use during encode. 246 tkn.Attr = append(tkn.Attr, xml.Attr{ 247 Name: xml.Name{ 248 Space: "", 249 Local: "platformBuildVersionCode", 250 }, 251 Value: strconv.Itoa(targetSdkVersion), 252 }, 253 xml.Attr{ 254 Name: xml.Name{ 255 Space: "", 256 Local: "platformBuildVersionName", 257 }, 258 Value: "4.1.2-1425332", 259 }, 260 ) 261 262 q = append(q, ltoken{tkn, line}) 263 264 if !skipSynthesize { 265 s := xml.StartElement{ 266 Name: xml.Name{ 267 Space: "", 268 Local: "uses-sdk", 269 }, 270 Attr: []xml.Attr{ 271 { 272 Name: xml.Name{ 273 Space: androidSchema, 274 Local: "minSdkVersion", 275 }, 276 Value: strconv.Itoa(minSdkVersion), 277 }, 278 { 279 Name: xml.Name{ 280 Space: androidSchema, 281 Local: "targetSdkVersion", 282 }, 283 Value: strconv.Itoa(targetSdkVersion), 284 }, 285 }, 286 } 287 e := xml.EndElement{Name: xml.Name{Local: "uses-sdk"}} 288 289 q = append(q, ltoken{s, line}, ltoken{e, line}) 290 } 291 case "application": 292 if !skipSynthesize { 293 for _, attr := range tkn.Attr { 294 if attr.Name.Space == androidSchema && attr.Name.Local == "icon" { 295 return nil, fmt.Errorf("manual declaration of android:icon in AndroidManifest.xml not supported") 296 } 297 } 298 if withIcon { 299 tkn.Attr = append(tkn.Attr, 300 xml.Attr{ 301 Name: xml.Name{ 302 Space: androidSchema, 303 Local: "icon", 304 }, 305 Value: "@mipmap/icon", 306 }) 307 } 308 } 309 q = append(q, ltoken{tkn, line}) 310 case "activity", "intent-filter", "action", "category", "service", "meta-data": 311 // need android:exported="true" for activities in android sdk version 31 and above (still not working so testing with other things also set to exported) 312 if !skipSynthesize && targetSdkVersion >= 31 { 313 tkn.Attr = append(tkn.Attr, 314 xml.Attr{ 315 Name: xml.Name{ 316 Space: androidSchema, 317 Local: "exported", 318 }, 319 Value: "true", 320 }, 321 ) 322 } 323 q = append(q, ltoken{tkn, line}) 324 } 325 default: 326 q = append(q, ltoken{tkn, line}) 327 } 328 } 329 330 for _, ltkn := range q { 331 tkn, line := ltkn.Token, ltkn.line 332 switch tkn := tkn.(type) { 333 case xml.StartElement: 334 el := &Element{ 335 NodeHeader: NodeHeader{ 336 LineNumber: uint32(line), 337 Comment: 0xFFFFFFFF, 338 }, 339 NS: NoEntry, 340 Name: pool.ref(tkn.Name.Local), 341 } 342 if len(bx.stack) == 0 { 343 bx.Children = append(bx.Children, el) 344 } else { 345 n := len(bx.stack) 346 var p *Element 347 p, bx.stack = bx.stack[n-1], bx.stack[:n-1] 348 p.Children = append(p.Children, el) 349 bx.stack = append(bx.stack, p) 350 } 351 bx.stack = append(bx.stack, el) 352 353 for _, attr := range tkn.Attr { 354 if (attr.Name.Space == "xmlns" && attr.Name.Local == "tools") || attr.Name.Space == toolsSchema { 355 continue // TODO can tbl be queried for schemas to determine validity instead? 356 } 357 358 if attr.Name.Space == "xmlns" && attr.Name.Local == "android" { 359 if bx.Namespace != nil { 360 return nil, fmt.Errorf("multiple declarations of xmlns:android encountered") 361 } 362 bx.Namespace = &Namespace{ 363 NodeHeader: NodeHeader{ 364 LineNumber: uint32(line), 365 Comment: NoEntry, 366 }, 367 prefix: 0, 368 uri: 0, 369 } 370 continue 371 } 372 373 nattr := &Attribute{ 374 NS: pool.ref(attr.Name.Space), 375 Name: pool.ref(attr.Name.Local), 376 RawValue: NoEntry, 377 } 378 el.attrs = append(el.attrs, nattr) 379 380 if attr.Name.Space == "" { 381 nattr.NS = NoEntry 382 // TODO it's unclear how to query these 383 switch attr.Name.Local { 384 case "platformBuildVersionCode": 385 nattr.TypedValue.Type = DataIntDec 386 i, err := strconv.Atoi(attr.Value) 387 if err != nil { 388 return nil, err 389 } 390 nattr.TypedValue.Value = uint32(i) 391 default: // "package", "platformBuildVersionName", and any invalid 392 nattr.RawValue = pool.ref(attr.Value) 393 nattr.TypedValue.Type = DataString 394 } 395 } else { 396 // get type spec and value data type 397 ref, err := tbl.RefByName("attr/" + attr.Name.Local) 398 if err != nil { 399 return nil, err 400 } 401 nt, err := ref.Resolve(tbl) 402 if err != nil { 403 return nil, err 404 } 405 if len(nt.values) == 0 { 406 panic("encountered empty values slice") 407 } 408 409 if len(nt.values) == 1 { 410 val := nt.values[0] 411 if val.data.Type != DataIntDec { 412 panic("TODO only know how to handle DataIntDec type here") 413 } 414 415 t := DataType(val.data.Value) 416 switch t { 417 case DataString, DataAttribute, DataType(0x3e): 418 // TODO identify 0x3e, in bootstrap.xml this is the native lib name 419 nattr.RawValue = pool.ref(attr.Value) 420 nattr.TypedValue.Type = DataString 421 nattr.TypedValue.Value = uint32(nattr.RawValue) 422 case DataIntBool, DataType(0x08): 423 nattr.TypedValue.Type = DataIntBool 424 switch attr.Value { 425 case "true": 426 nattr.TypedValue.Value = 0xFFFFFFFF 427 case "false": 428 nattr.TypedValue.Value = 0 429 default: 430 return nil, fmt.Errorf("invalid bool value %q", attr.Value) 431 } 432 case DataIntDec, DataFloat, DataFraction: 433 // TODO DataFraction needs it's own case statement. minSdkVersion identifies as DataFraction 434 // but has accepted input in the past such as android:minSdkVersion="L" 435 // Other use-cases for DataFraction are currently unknown as applicable to manifest generation 436 // but this provides minimum support for writing out minSdkVersion="15" correctly. 437 nattr.TypedValue.Type = DataIntDec 438 i, err := strconv.Atoi(attr.Value) 439 if err != nil { 440 return nil, err 441 } 442 nattr.TypedValue.Value = uint32(i) 443 case DataReference: 444 nattr.TypedValue.Type = DataReference 445 dref, err := tbl.RefByName(attr.Value) 446 if err != nil { 447 if strings.HasPrefix(attr.Value, "@mipmap") { 448 // firstDrawableId is a TableRef matching first entry of mipmap spec initialized by NewMipmapTable. 449 // 7f is default package, 02 is mipmap spec, 0000 is first entry; e.g. R.drawable.icon 450 // TODO resource table should generate ids as required. 451 const firstDrawableId = 0x7f020000 452 nattr.TypedValue.Value = firstDrawableId 453 continue 454 } 455 return nil, err 456 } 457 nattr.TypedValue.Value = uint32(dref) 458 default: 459 return nil, fmt.Errorf("unhandled data type %0#2x: %s", uint8(t), t) 460 } 461 } else { 462 // 0x01000000 is an unknown ref that doesn't point to anything, typically 463 // located at the start of entry value lists, peek at last value to determine type. 464 t := nt.values[len(nt.values)-1].data.Type 465 switch t { 466 case DataIntDec: 467 for _, val := range nt.values { 468 if val.name == 0x01000000 { 469 continue 470 } 471 nr, err := val.name.Resolve(tbl) 472 if err != nil { 473 return nil, err 474 } 475 if attr.Value == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref 476 nattr.TypedValue = *val.data 477 break 478 } 479 } 480 case DataIntHex: 481 nattr.TypedValue.Type = t 482 for _, x := range strings.Split(attr.Value, "|") { 483 for _, val := range nt.values { 484 if val.name == 0x01000000 { 485 continue 486 } 487 nr, err := val.name.Resolve(tbl) 488 if err != nil { 489 return nil, err 490 } 491 if x == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref 492 nattr.TypedValue.Value |= val.data.Value 493 break 494 } 495 } 496 } 497 default: 498 return nil, fmt.Errorf("unhandled data type for configuration %0#2x: %s", uint8(t), t) 499 } 500 } 501 } 502 } 503 case xml.CharData: 504 if s := poolTrim(string(tkn)); s != "" { 505 cdt := &CharData{ 506 NodeHeader: NodeHeader{ 507 LineNumber: uint32(line), 508 Comment: NoEntry, 509 }, 510 RawData: pool.ref(s), 511 } 512 el := bx.stack[len(bx.stack)-1] 513 if el.head == nil { 514 el.head = cdt 515 } else if el.tail == nil { 516 el.tail = cdt 517 } else { 518 return nil, fmt.Errorf("element head and tail already contain chardata") 519 } 520 } 521 case xml.EndElement: 522 if tkn.Name.Local == "manifest" { 523 bx.Namespace.end = &Namespace{ 524 NodeHeader: NodeHeader{ 525 LineNumber: uint32(line), 526 Comment: NoEntry, 527 }, 528 prefix: 0, 529 uri: 0, 530 } 531 } 532 n := len(bx.stack) 533 var el *Element 534 el, bx.stack = bx.stack[n-1], bx.stack[:n-1] 535 if el.end != nil { 536 return nil, fmt.Errorf("element end already exists") 537 } 538 el.end = &ElementEnd{ 539 NodeHeader: NodeHeader{ 540 LineNumber: uint32(line), 541 Comment: NoEntry, 542 }, 543 NS: el.NS, 544 Name: el.Name, 545 } 546 case xml.Comment, xml.ProcInst: 547 // discard 548 default: 549 panic(fmt.Errorf("unhandled token type: %T %+v", tkn, tkn)) 550 } 551 } 552 553 // pools appear to be sorted as follows: 554 // * attribute names prefixed with android: 555 // * "android", [schema-url], [empty-string] 556 // * for each node: 557 // * attribute names with no prefix 558 // * node name 559 // * attribute value if data type of name is DataString, DataAttribute, or 0x3e (an unknown) 560 bx.Pool = new(Pool) 561 562 var arecurse func(*Element) 563 arecurse = func(el *Element) { 564 for _, attr := range el.attrs { 565 if attr.NS == NoEntry { 566 continue 567 } 568 if attr.NS.Resolve(pool) == androidSchema { 569 bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool)) 570 } 571 } 572 for _, child := range el.Children { 573 arecurse(child) 574 } 575 } 576 for _, el := range bx.Children { 577 arecurse(el) 578 } 579 580 // TODO encoding/xml does not enforce namespace prefix and manifest encoding in aapt 581 // appears to ignore all other prefixes. Inserting this manually is not strictly correct 582 // for the general case, but the effort to do otherwise currently offers nothing. 583 bx.Pool.strings = append(bx.Pool.strings, "android", androidSchema) 584 585 // there always appears to be an empty string located after schema, even if one is 586 // not present in manifest. 587 bx.Pool.strings = append(bx.Pool.strings, "") 588 589 var brecurse func(*Element) 590 brecurse = func(el *Element) { 591 for _, attr := range el.attrs { 592 if attr.NS == NoEntry { 593 bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool)) 594 } 595 } 596 597 bx.Pool.strings = append(bx.Pool.strings, el.Name.Resolve(pool)) 598 599 for _, attr := range el.attrs { 600 if attr.RawValue != NoEntry { 601 bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool)) 602 } else if attr.NS == NoEntry { 603 bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value)) 604 } 605 } 606 607 if el.head != nil { 608 bx.Pool.strings = append(bx.Pool.strings, el.head.RawData.Resolve(pool)) 609 } 610 if el.tail != nil { 611 bx.Pool.strings = append(bx.Pool.strings, el.tail.RawData.Resolve(pool)) 612 } 613 614 for _, child := range el.Children { 615 brecurse(child) 616 } 617 } 618 for _, el := range bx.Children { 619 brecurse(el) 620 } 621 622 // do not eliminate duplicates until the entire slice has been composed. 623 // consider <activity android:label="label" .../> 624 // all attribute names come first followed by values; in such a case, the value "label" 625 // would be a reference to the same "android:label" in the string pool which will occur 626 // within the beginning of the pool where other attr names are located. 627 bx.Pool.strings = asSet(bx.Pool.strings) 628 629 // TODO consider cases of multiple declarations of the same attr name that should return error 630 // before ever reaching this point. 631 bx.Map = new(Map) 632 for _, s := range bx.Pool.strings { 633 ref, err := tbl.RefByName("attr/" + s) 634 if err != nil { 635 break // break after first non-ref as all strings after are also non-refs. 636 } 637 bx.Map.rs = append(bx.Map.rs, ref) 638 } 639 640 // resolve tmp pool refs to final pool refs 641 // TODO drop this in favor of sort directly on Table 642 var resolve func(el *Element) 643 resolve = func(el *Element) { 644 if el.NS != NoEntry { 645 el.NS = bx.Pool.ref(el.NS.Resolve(pool)) 646 el.end.NS = el.NS 647 } 648 el.Name = bx.Pool.ref(el.Name.Resolve(pool)) 649 el.end.Name = el.Name 650 for _, attr := range el.attrs { 651 if attr.NS != NoEntry { 652 attr.NS = bx.Pool.ref(attr.NS.Resolve(pool)) 653 } 654 attr.Name = bx.Pool.ref(attr.Name.Resolve(pool)) 655 if attr.RawValue != NoEntry { 656 attr.RawValue = bx.Pool.ref(attr.RawValue.Resolve(pool)) 657 if attr.TypedValue.Type == DataString { 658 attr.TypedValue.Value = uint32(attr.RawValue) 659 } 660 } 661 } 662 for _, child := range el.Children { 663 resolve(child) 664 } 665 } 666 for _, el := range bx.Children { 667 resolve(el) 668 } 669 670 var asort func(*Element) 671 asort = func(el *Element) { 672 sort.Sort(byType(el.attrs)) 673 sort.Sort(byNamespace(el.attrs)) 674 sort.Sort(byName(el.attrs)) 675 for _, child := range el.Children { 676 asort(child) 677 } 678 } 679 for _, el := range bx.Children { 680 asort(el) 681 } 682 683 for i, s := range bx.Pool.strings { 684 switch s { 685 case androidSchema: 686 bx.Namespace.uri = PoolRef(i) 687 bx.Namespace.end.uri = PoolRef(i) 688 case "android": 689 bx.Namespace.prefix = PoolRef(i) 690 bx.Namespace.end.prefix = PoolRef(i) 691 } 692 } 693 694 return bx, nil 695 } 696 697 // UnmarshalBinary decodes all resource chunks in buf returning any error encountered. 698 func (bx *XML) UnmarshalBinary(buf []byte) error { 699 if err := (&bx.chunkHeader).UnmarshalBinary(buf); err != nil { 700 return err 701 } 702 buf = buf[8:] 703 for len(buf) > 0 { 704 k, err := bx.unmarshalBinaryKind(buf) 705 if err != nil { 706 return err 707 } 708 buf = buf[k.size():] 709 } 710 return nil 711 } 712 713 // unmarshalBinaryKind decodes and stores the first resource chunk of bin. 714 // It returns the unmarshaler interface and any error encountered. 715 // If k.size() < len(bin), subsequent chunks can be decoded at bin[k.size():]. 716 func (bx *XML) unmarshalBinaryKind(bin []byte) (k unmarshaler, err error) { 717 k, err = bx.kind(ResType(btou16(bin))) 718 if err != nil { 719 return nil, err 720 } 721 if err = k.UnmarshalBinary(bin); err != nil { 722 return nil, err 723 } 724 return k, nil 725 } 726 727 func (bx *XML) kind(t ResType) (unmarshaler, error) { 728 switch t { 729 case ResStringPool: 730 if bx.Pool != nil { 731 return nil, fmt.Errorf("pool already exists") 732 } 733 bx.Pool = new(Pool) 734 return bx.Pool, nil 735 case ResXMLResourceMap: 736 if bx.Map != nil { 737 return nil, fmt.Errorf("resource map already exists") 738 } 739 bx.Map = new(Map) 740 return bx.Map, nil 741 case ResXMLStartNamespace: 742 if bx.Namespace != nil { 743 return nil, fmt.Errorf("namespace start already exists") 744 } 745 bx.Namespace = new(Namespace) 746 return bx.Namespace, nil 747 case ResXMLEndNamespace: 748 if bx.Namespace.end != nil { 749 return nil, fmt.Errorf("namespace end already exists") 750 } 751 bx.Namespace.end = new(Namespace) 752 return bx.Namespace.end, nil 753 case ResXMLStartElement: 754 el := new(Element) 755 if len(bx.stack) == 0 { 756 bx.Children = append(bx.Children, el) 757 } else { 758 n := len(bx.stack) 759 var p *Element 760 p, bx.stack = bx.stack[n-1], bx.stack[:n-1] 761 p.Children = append(p.Children, el) 762 bx.stack = append(bx.stack, p) 763 } 764 bx.stack = append(bx.stack, el) 765 return el, nil 766 case ResXMLEndElement: 767 n := len(bx.stack) 768 var el *Element 769 el, bx.stack = bx.stack[n-1], bx.stack[:n-1] 770 if el.end != nil { 771 return nil, fmt.Errorf("element end already exists") 772 } 773 el.end = new(ElementEnd) 774 return el.end, nil 775 case ResXMLCharData: // TODO assure correctness 776 cdt := new(CharData) 777 el := bx.stack[len(bx.stack)-1] 778 if el.head == nil { 779 el.head = cdt 780 } else if el.tail == nil { 781 el.tail = cdt 782 } else { 783 return nil, fmt.Errorf("element head and tail already contain chardata") 784 } 785 return cdt, nil 786 default: 787 return nil, fmt.Errorf("unexpected type %s", t) 788 } 789 } 790 791 func (bx *XML) MarshalBinary() ([]byte, error) { 792 bx.typ = ResXML 793 bx.headerByteSize = 8 794 795 var ( 796 bin, b []byte 797 err error 798 ) 799 b, err = bx.chunkHeader.MarshalBinary() 800 if err != nil { 801 return nil, err 802 } 803 bin = append(bin, b...) 804 805 b, err = bx.Pool.MarshalBinary() 806 if err != nil { 807 return nil, err 808 } 809 bin = append(bin, b...) 810 811 b, err = bx.Map.MarshalBinary() 812 if err != nil { 813 return nil, err 814 } 815 bin = append(bin, b...) 816 817 b, err = bx.Namespace.MarshalBinary() 818 if err != nil { 819 return nil, err 820 } 821 bin = append(bin, b...) 822 823 for _, child := range bx.Children { 824 if err := marshalRecurse(child, &bin); err != nil { 825 return nil, err 826 } 827 } 828 829 b, err = bx.Namespace.end.MarshalBinary() 830 if err != nil { 831 return nil, err 832 } 833 bin = append(bin, b...) 834 835 putu32(bin[4:], uint32(len(bin))) 836 return bin, nil 837 } 838 839 func marshalRecurse(el *Element, bin *[]byte) error { 840 b, err := el.MarshalBinary() 841 if err != nil { 842 return err 843 } 844 *bin = append(*bin, b...) 845 846 if el.head != nil { 847 b, err := el.head.MarshalBinary() 848 if err != nil { 849 return err 850 } 851 *bin = append(*bin, b...) 852 } 853 854 for _, child := range el.Children { 855 if err := marshalRecurse(child, bin); err != nil { 856 return err 857 } 858 } 859 860 b, err = el.end.MarshalBinary() 861 if err != nil { 862 return err 863 } 864 *bin = append(*bin, b...) 865 866 return nil 867 } 868 869 func (bx *XML) iterElements() <-chan *Element { 870 ch := make(chan *Element, 1) 871 go func() { 872 for _, el := range bx.Children { 873 iterElementsRecurse(el, ch) 874 } 875 close(ch) 876 }() 877 return ch 878 } 879 880 func iterElementsRecurse(el *Element, ch chan *Element) { 881 ch <- el 882 for _, e := range el.Children { 883 iterElementsRecurse(e, ch) 884 } 885 } 886 887 // asSet returns a set from a slice of strings. 888 func asSet(xs []string) []string { 889 m := make(map[string]bool) 890 fo := xs[:0] 891 for _, x := range xs { 892 if !m[x] { 893 m[x] = true 894 fo = append(fo, x) 895 } 896 } 897 return fo 898 } 899 900 // poolTrim trims all but immediately surrounding space. 901 // \n\t\tfoobar\n\t\t becomes \tfoobar\n 902 func poolTrim(s string) string { 903 var start, end int 904 for i, r := range s { 905 if !unicode.IsSpace(r) { 906 if i != 0 { 907 start = i - 1 // preserve preceding space 908 } 909 break 910 } 911 } 912 913 for i := len(s) - 1; i >= 0; i-- { 914 r := rune(s[i]) 915 if !unicode.IsSpace(r) { 916 if i != len(s)-1 { 917 end = i + 2 918 } 919 break 920 } 921 } 922 923 if start == 0 && end == 0 { 924 return "" // every char was a space 925 } 926 927 return s[start:end] 928 } 929 930 // byNamespace sorts attributes based on string pool position of namespace. 931 // Given that "android" always proceeds "" in the pool, this results in the 932 // correct ordering of attributes. 933 type byNamespace []*Attribute 934 935 func (a byNamespace) Len() int { return len(a) } 936 func (a byNamespace) Less(i, j int) bool { 937 return a[i].NS < a[j].NS 938 } 939 func (a byNamespace) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 940 941 // byType sorts attributes by the uint8 value of the type. 942 type byType []*Attribute 943 944 func (a byType) Len() int { return len(a) } 945 func (a byType) Less(i, j int) bool { 946 return a[i].TypedValue.Type < a[j].TypedValue.Type 947 } 948 func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 949 950 // byName sorts attributes that have matching types based on string pool position of name. 951 type byName []*Attribute 952 953 func (a byName) Len() int { return len(a) } 954 func (a byName) Less(i, j int) bool { 955 return (a[i].TypedValue.Type == DataString || a[i].TypedValue.Type == DataIntDec) && 956 (a[j].TypedValue.Type == DataString || a[j].TypedValue.Type == DataIntDec) && 957 a[i].Name < a[j].Name 958 } 959 func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 960 961 type lineReader struct { 962 off int64 963 lines []int64 964 r io.Reader 965 } 966 967 func (r *lineReader) Read(p []byte) (n int, err error) { 968 n, err = r.r.Read(p) 969 for i := 0; i < n; i++ { 970 if p[i] == '\n' { 971 r.lines = append(r.lines, r.off+int64(i)) 972 } 973 } 974 r.off += int64(n) 975 return n, err 976 } 977 978 func (r *lineReader) line(pos int64) int { 979 return sort.Search(len(r.lines), func(i int) bool { 980 return pos < r.lines[i] 981 }) + 1 982 }