123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- package android_binary
- import (
- "bytes"
- "encoding/binary"
- "encoding/xml"
- "fmt"
- "io"
- "reflect"
- )
- // XMLFile is an XML file expressed in binary format.
- type XMLFile struct {
- stringPool *ResStringPool
- resourceMap []uint32
- notPrecessedNS map[ResStringPoolRef]ResStringPoolRef
- namespaces map[ResStringPoolRef]ResStringPoolRef
- xmlBuffer bytes.Buffer
- }
- // ResXMLTreeNode is basic XML tree node.
- type ResXMLTreeNode struct {
- Header ResChunkHeader
- LineNumber uint32
- Comment ResStringPoolRef
- }
- // ResXMLTreeNamespaceExt is extended XML tree node for namespace start/end nodes.
- type ResXMLTreeNamespaceExt struct {
- Prefix ResStringPoolRef
- URI ResStringPoolRef
- }
- // ResXMLTreeAttrExt is extended XML tree node for start tags -- includes attribute.
- type ResXMLTreeAttrExt struct {
- NS ResStringPoolRef
- Name ResStringPoolRef
- AttributeStart uint16
- AttributeSize uint16
- AttributeCount uint16
- IDIndex uint16
- ClassIndex uint16
- StyleIndex uint16
- }
- // ResXMLTreeAttribute is an attribute of start tags.
- type ResXMLTreeAttribute struct {
- NS ResStringPoolRef
- Name ResStringPoolRef
- RawValue ResStringPoolRef
- TypedValue ResValue
- }
- // ResXMLTreeEndElementExt is extended XML tree node for element start/end nodes.
- type ResXMLTreeEndElementExt struct {
- NS ResStringPoolRef
- Name ResStringPoolRef
- }
- // NewXMLFile returns a new XMLFile.
- func NewXMLFile(r io.ReaderAt) (*XMLFile, error) {
- f := new(XMLFile)
- sr := io.NewSectionReader(r, 0, 1<<63-1)
- fmt.Fprintf(&f.xmlBuffer, xml.Header)
- header := new(ResChunkHeader)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return nil, err
- }
- offset := int64(header.HeaderSize)
- for offset < int64(header.Size) {
- chunkHeader, err := f.readChunk(r, offset)
- if err != nil {
- return nil, err
- }
- offset += int64(chunkHeader.Size)
- }
- return f, nil
- }
- // Reader returns a reader of XML file expressed in text format.
- func (f *XMLFile) Reader() *bytes.Reader {
- return bytes.NewReader(f.xmlBuffer.Bytes())
- }
- // Decode decodes XML file and stores the result in the value pointed to by v.
- // To resolve the resource references, Decode also stores default TableFile and ResTableConfig in the value pointed to by v.
- func (f *XMLFile) Decode(v any, table *TableFile, config *ResTableConfig) error {
- decoder := xml.NewDecoder(f.Reader())
- if err := decoder.Decode(v); err != nil {
- return err
- }
- inject(reflect.ValueOf(v), table, config)
- return nil
- }
- func (f *XMLFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) {
- sr := io.NewSectionReader(r, offset, 1<<63-1-offset)
- chunkHeader := &ResChunkHeader{}
- if _, err := sr.Seek(0, io.SeekStart); err != nil {
- return nil, err
- }
- if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
- return nil, err
- }
- var err error
- if _, err := sr.Seek(0, io.SeekStart); err != nil {
- return nil, err
- }
- switch chunkHeader.Type {
- case ResStringPoolChunkType:
- f.stringPool, err = readStringPool(sr)
- case ResXMLStartNamespaceType:
- err = f.readStartNamespace(sr)
- case ResXMLEndNamespaceType:
- err = f.readEndNamespace(sr)
- case ResXMLStartElementType:
- err = f.readStartElement(sr)
- case ResXMLEndElementType:
- err = f.readEndElement(sr)
- }
- if err != nil {
- return nil, err
- }
- return chunkHeader, nil
- }
- // GetString returns a string referenced by ref.
- func (f *XMLFile) GetString(ref ResStringPoolRef) string {
- return f.stringPool.GetString(ref)
- }
- func (f *XMLFile) readStartNamespace(sr *io.SectionReader) error {
- header := new(ResXMLTreeNode)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return err
- }
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return err
- }
- namespace := new(ResXMLTreeNamespaceExt)
- if err := binary.Read(sr, binary.LittleEndian, namespace); err != nil {
- return err
- }
- if f.notPrecessedNS == nil {
- f.notPrecessedNS = make(map[ResStringPoolRef]ResStringPoolRef)
- }
- f.notPrecessedNS[namespace.URI] = namespace.Prefix
- if f.namespaces == nil {
- f.namespaces = make(map[ResStringPoolRef]ResStringPoolRef)
- }
- f.namespaces[namespace.URI] = namespace.Prefix
- return nil
- }
- func (f *XMLFile) readEndNamespace(sr *io.SectionReader) error {
- header := new(ResXMLTreeNode)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return err
- }
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return err
- }
- namespace := new(ResXMLTreeNamespaceExt)
- if err := binary.Read(sr, binary.LittleEndian, namespace); err != nil {
- return err
- }
- delete(f.namespaces, namespace.URI)
- return nil
- }
- func (f *XMLFile) addNamespacePrefix(ns, name ResStringPoolRef) string {
- if ns != NilResStringPoolRef {
- prefix := f.GetString(f.namespaces[ns])
- return fmt.Sprintf("%s:%s", prefix, f.GetString(name))
- }
- return f.GetString(name)
- }
- func (f *XMLFile) readStartElement(sr *io.SectionReader) error {
- header := new(ResXMLTreeNode)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return err
- }
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return err
- }
- ext := new(ResXMLTreeAttrExt)
- if err := binary.Read(sr, binary.LittleEndian, ext); err != nil {
- return nil
- }
- fmt.Fprintf(&f.xmlBuffer, "<%s", f.addNamespacePrefix(ext.NS, ext.Name))
- // output XML namespaces
- if f.notPrecessedNS != nil {
- for uri, prefix := range f.notPrecessedNS {
- fmt.Fprintf(&f.xmlBuffer, " xmlns:%s=\"", f.GetString(prefix))
- xml.Escape(&f.xmlBuffer, []byte(f.GetString(uri)))
- fmt.Fprint(&f.xmlBuffer, "\"")
- }
- f.notPrecessedNS = nil
- }
- // process attributes
- offset := int64(ext.AttributeStart + header.Header.HeaderSize)
- for i := 0; i < int(ext.AttributeCount); i++ {
- if _, err := sr.Seek(offset, io.SeekStart); err != nil {
- return err
- }
- attr := new(ResXMLTreeAttribute)
- binary.Read(sr, binary.LittleEndian, attr)
- var value string
- if attr.RawValue != NilResStringPoolRef {
- value = f.GetString(attr.RawValue)
- } else {
- data := attr.TypedValue.Data
- switch attr.TypedValue.DataType {
- case TypeNull:
- value = ""
- case TypeReference:
- value = fmt.Sprintf("@0x%08X", data)
- case TypeIntDec:
- value = fmt.Sprintf("%d", data)
- case TypeIntHex:
- value = fmt.Sprintf("0x%08X", data)
- case TypeIntBoolean:
- if data != 0 {
- value = "true"
- } else {
- value = "false"
- }
- default:
- value = fmt.Sprintf("@0x%08X", data)
- }
- }
- fmt.Fprintf(&f.xmlBuffer, " %s=\"", f.addNamespacePrefix(attr.NS, attr.Name))
- xml.Escape(&f.xmlBuffer, []byte(value))
- fmt.Fprint(&f.xmlBuffer, "\"")
- offset += int64(ext.AttributeSize)
- }
- fmt.Fprint(&f.xmlBuffer, ">")
- return nil
- }
- func (f *XMLFile) readEndElement(sr *io.SectionReader) error {
- header := new(ResXMLTreeNode)
- if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
- return err
- }
- if _, err := sr.Seek(int64(header.Header.HeaderSize), io.SeekStart); err != nil {
- return err
- }
- ext := new(ResXMLTreeEndElementExt)
- if err := binary.Read(sr, binary.LittleEndian, ext); err != nil {
- return err
- }
- fmt.Fprintf(&f.xmlBuffer, "</%s>", f.addNamespacePrefix(ext.NS, ext.Name))
- return nil
- }
|