From cab407a098c49425bb3540f57b32dbf3d68b5ae7 Mon Sep 17 00:00:00 2001 From: codeskyblue Date: Thu, 1 Jun 2017 17:00:23 +0800 Subject: [PATCH] sync to new androidbinary lib --- Godeps/Godeps.json | 4 +- .../shogo82148/androidbinary/README.md | 54 +- .../shogo82148/androidbinary/apk/apk.go | 27 +- .../shogo82148/androidbinary/apk/apkxml.go | 16 +- .../shogo82148/androidbinary/common.go | 132 +++-- .../androidbinary/const_fallback.go | 7 + .../shogo82148/androidbinary/const_go17.go | 7 + .../shogo82148/androidbinary/table.go | 492 ++++++++++++------ .../shogo82148/androidbinary/xml.go | 68 ++- 9 files changed, 529 insertions(+), 278 deletions(-) create mode 100644 vendor/github.com/shogo82148/androidbinary/const_fallback.go create mode 100644 vendor/github.com/shogo82148/androidbinary/const_go17.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8e043fb..93f86b5 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -76,11 +76,11 @@ }, { "ImportPath": "github.com/shogo82148/androidbinary", - "Rev": "b443b936780e4218d554b88b94b7e5638f4fdc20" + "Rev": "2fb750de4ec6a45853fcc5f85694eab47e1007f9" }, { "ImportPath": "github.com/shogo82148/androidbinary/apk", - "Rev": "b443b936780e4218d554b88b94b7e5638f4fdc20" + "Rev": "2fb750de4ec6a45853fcc5f85694eab47e1007f9" }, { "ImportPath": "golang.org/x/net/html", diff --git a/vendor/github.com/shogo82148/androidbinary/README.md b/vendor/github.com/shogo82148/androidbinary/README.md index 56f9cd2..4f06a98 100644 --- a/vendor/github.com/shogo82148/androidbinary/README.md +++ b/vendor/github.com/shogo82148/androidbinary/README.md @@ -2,25 +2,71 @@ androidbinary ===== [![Build Status](https://travis-ci.org/shogo82148/androidbinary.svg?branch=master)](https://travis-ci.org/shogo82148/androidbinary) +[![GoDoc](https://godoc.org/github.com/shogo82148/androidbinary?status.svg)](https://godoc.org/github.com/shogo82148/androidbinary) Android binary file parser -## Parse XML binary +## High Level API + +### Parse APK files ``` go package main import ( - "fmt" + "github.com/shogo82148/androidbinary/apk" +) + +func main() { + pkg, _ := apk.OpenFile("your-android-app.apk") + defer pkg.Close() + + icon, _ := apk.Icon(nil) // returns the icon of APK as image.Image + pkgName := pkg.PackageName() // returns the pakcage name +} +``` + +## Low Lebel API + +### Parse XML binary + +``` go +package main + +import ( + "encoding/xml" + "github.com/shogo82148/androidbinary" - "os" + "github.com/shogo82148/androidbinary/apk" ) func main() { - f, _ := os.Open("AndroidManifest") + f, _ := os.Open("AndroidManifest.xml") xml, _ := androidbinary.NewXMLFile(f) reader := xml.Reader() + // read XML from reader + var manifest apk.Manifest + data, _ := ioutil.ReadAll(reader) + xml.Unmarshal(data, &manifest) +} +``` + +### Parse Resource files + +``` go +package main + +import ( + "fmt" + "github.com/shogo82148/androidbinary" +) + +func main() { + f, _ := os.Open("resources.arsc") + rsc, _ := androidbinary.NewTableFile(f) + resorce, _ := rsc.GetResource(androidbinary.ResID(0xCAFEBABE), nil) + fmt.Println(resource) } ``` diff --git a/vendor/github.com/shogo82148/androidbinary/apk/apk.go b/vendor/github.com/shogo82148/androidbinary/apk/apk.go index 35f3110..942bedf 100644 --- a/vendor/github.com/shogo82148/androidbinary/apk/apk.go +++ b/vendor/github.com/shogo82148/androidbinary/apk/apk.go @@ -10,17 +10,12 @@ import ( "io/ioutil" "os" "strconv" - "strings" - - _ "image/jpeg" - _ "image/png" "github.com/pkg/errors" "github.com/shogo82148/androidbinary" ) -var DefaultResTableConfig = &androidbinary.ResTableConfig{} - +// Apk is an application package file for android. type Apk struct { f *os.File zipreader *zip.Reader @@ -77,10 +72,10 @@ func (k *Apk) Close() error { return k.f.Close() } -// Icon return icon image +// Icon returns the icon image of the APK. func (k *Apk) Icon(resConfig *androidbinary.ResTableConfig) (image.Image, error) { iconPath := k.getResource(k.manifest.App.Icon, resConfig) - if strings.HasPrefix(iconPath, "@0x") { + if androidbinary.IsResID(iconPath) { return nil, errors.New("unable to convert icon-id to icon path") } imgData, err := k.readZipFile(iconPath) @@ -91,22 +86,26 @@ func (k *Apk) Icon(resConfig *androidbinary.ResTableConfig) (image.Image, error) return m, err } +// Label returns the label of the APK. func (k *Apk) Label(resConfig *androidbinary.ResTableConfig) (s string, err error) { s = k.getResource(k.manifest.App.Label, resConfig) - if strings.HasPrefix(s, "@0x") { + if androidbinary.IsResID(s) { err = errors.New("unable to convert label-id to string") } return } +// Manifest returns the manifest of the APK. func (k *Apk) Manifest() Manifest { return k.manifest } +// PackageName returns the package name of the APK. func (k *Apk) PackageName() string { return k.manifest.Package } +// MainAcitivty returns the name of the main activity. func (k *Apk) MainAcitivty() (activity string, err error) { for _, act := range k.manifest.App.Activity { for _, intent := range act.IntentFilter { @@ -126,7 +125,7 @@ func (k *Apk) parseManifest() error { } xmlfile, err := androidbinary.NewXMLFile(bytes.NewReader(xmlData)) if err != nil { - return errors.Wrap(err, "parse-axml") + return errors.Wrap(err, "parse-xml") } reader := xmlfile.Reader() data, err := ioutil.ReadAll(reader) @@ -146,15 +145,11 @@ func (k *Apk) parseResources() (err error) { } func (k *Apk) getResource(id string, resConfig *androidbinary.ResTableConfig) string { - if resConfig == nil { - resConfig = DefaultResTableConfig - } - var resId uint32 - _, err := fmt.Sscanf(id, "@0x%x", &resId) + resID, err := androidbinary.ParseResID(id) if err != nil { return id } - val, err := k.table.GetResource(androidbinary.ResId(resId), resConfig) + val, err := k.table.GetResource(resID, resConfig) if err != nil { return id } diff --git a/vendor/github.com/shogo82148/androidbinary/apk/apkxml.go b/vendor/github.com/shogo82148/androidbinary/apk/apkxml.go index 17988d5..791dc89 100644 --- a/vendor/github.com/shogo82148/androidbinary/apk/apkxml.go +++ b/vendor/github.com/shogo82148/androidbinary/apk/apkxml.go @@ -1,5 +1,6 @@ package apk +// Instrumentation is an application instrumentation code. type Instrumentation struct { Name string `xml:"name,attr"` Target string `xml:"targetPackage,attr"` @@ -7,19 +8,23 @@ type Instrumentation struct { FunctionalTest bool `xml:"functionalTest,attr"` } +// ActivityAction is an action of an activity. type ActivityAction struct { Name string `xml:"name,attr"` } +// ActivityCategory is a category of an activity. type ActivityCategory struct { Name string `xml:"name,attr"` } +// ActivityIntentFilter is an intent filter of an activity. type ActivityIntentFilter struct { Action ActivityAction `xml:"action"` Category ActivityCategory `xml:"category"` } +// AppActivity is an activity in an application. type AppActivity struct { Theme string `xml:"theme,attr"` Name string `xml:"name,attr"` @@ -27,6 +32,7 @@ type AppActivity struct { IntentFilter []ActivityIntentFilter `xml:"intent-filter"` } +// Application is an application in an APK. type Application struct { AllowTaskReparenting bool `xml:"allowTaskReparenting,attr"` AllowBackup bool `xml:"allowBackup,attr"` @@ -53,22 +59,24 @@ type Application struct { TaskAffinity string `xml:"taskAffinity,attr"` TestOnly bool `xml:"testOnly,attr"` Theme string `xml:"theme,attr"` - UiOptions string `xml:"uiOptions,attr"` - VmSafeMode bool `xml:"vmSafeMode,attr"` + UIOptions string `xml:"uiOptions,attr"` + VMSafeMode bool `xml:"vmSafeMode,attr"` Activity []AppActivity `xml:"activity"` } -type UsesSdk struct { +// UsesSDK is target SDK version. +type UsesSDK struct { Min int `xml:"minSdkVersion,attr"` Target int `xml:"targetSdkVersion,attr"` Max int `xml:"maxSdkVersion,attr"` } +// Manifest is a manifest of an APK. type Manifest struct { Package string `xml:"package,attr"` VersionCode int `xml:"versionCode,attr"` VersionName string `xml:"versionName,attr"` App Application `xml:"application"` Instrument Instrumentation `xml:"instrumentation"` - Sdk UsesSdk `xml:"uses-sdk"` + SDK UsesSDK `xml:"uses-sdk"` } diff --git a/vendor/github.com/shogo82148/androidbinary/common.go b/vendor/github.com/shogo82148/androidbinary/common.go index 6f3bc73..d760158 100644 --- a/vendor/github.com/shogo82148/androidbinary/common.go +++ b/vendor/github.com/shogo82148/androidbinary/common.go @@ -3,93 +3,118 @@ package androidbinary import ( "bytes" "encoding/binary" - "fmt" "io" - "os" "unicode/utf16" ) +// ChunkType is a type of a resource chunk. +type ChunkType uint16 + +// Chunk types. const ( - RES_NULL_TYPE = 0x0000 - RES_STRING_POOL_TYPE = 0x0001 - RES_TABLE_TYPE = 0x0002 - RES_XML_TYPE = 0x0003 + ResNullChunkType ChunkType = 0x0000 + ResStringPoolChunkType ChunkType = 0x0001 + ResTableChunkType ChunkType = 0x0002 + ResXMLChunkType ChunkType = 0x0003 // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100 - RES_XML_START_NAMESPACE_TYPE = 0x0100 - RES_XML_END_NAMESPACE_TYPE = 0x0101 - RES_XML_START_ELEMENT_TYPE = 0x0102 - RES_XML_END_ELEMENT_TYPE = 0x0103 - RES_XML_CDATA_TYPE = 0x0104 - RES_XML_LAST_CHUNK_TYPE = 0x017f + ResXMLFirstChunkType ChunkType = 0x0100 + ResXMLStartNamespaceType ChunkType = 0x0100 + ResXMLEndNamespaceType ChunkType = 0x0101 + ResXMLStartElementType ChunkType = 0x0102 + ResXMLEndElementType ChunkType = 0x0103 + ResXMLCDataType ChunkType = 0x0104 + ResXMLLastChunkType ChunkType = 0x017f // This contains a uint32_t array mapping strings in the string // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180 + ResXMLResourceMapType ChunkType = 0x0180 // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200 - RES_TABLE_TYPE_TYPE = 0x0201 - RES_TABLE_TYPE_SPEC_TYPE = 0x0202 + ResTablePackageType ChunkType = 0x0200 + ResTableTypeType ChunkType = 0x0201 + ResTableTypeSpecType ChunkType = 0x0202 ) +// ResChunkHeader is a header of a resource chunk. type ResChunkHeader struct { - Type uint16 + Type ChunkType HeaderSize uint16 Size uint32 } -const SORTED_FLAG = 1 << 0 -const UTF8_FLAG = 1 << 8 +// Flags are flags for string pool header. +type Flags uint32 +// the values of Flags. +const ( + SortedFlag Flags = 1 << 0 + UTF8Flag Flags = 1 << 8 +) + +// ResStringPoolHeader is a chunk header of string pool. type ResStringPoolHeader struct { Header ResChunkHeader StringCount uint32 StyleCount uint32 - Flags uint32 + Flags Flags StringStart uint32 StylesStart uint32 } +// ResStringPoolSpan is a span of style information associated with +// a string in the pool. +type ResStringPoolSpan struct { + FirstChar, LastChar uint32 +} + +// ResStringPool is a string pool resrouce. type ResStringPool struct { Header ResStringPoolHeader Strings []string - Styles []string + Styles []ResStringPoolSpan } +// NilResStringPoolRef is nil reference for string pool. const NilResStringPoolRef = ResStringPoolRef(0xFFFFFFFF) +// ResStringPoolRef is a type representing a reference to a string. type ResStringPoolRef uint32 +// DataType is a type of the data value. +type DataType uint8 + +// The constants for DataType const ( - TYPE_NULL = 0x00 - TYPE_REFERENCE = 0x01 - TYPE_ATTRIBUTE = 0x02 - TYPE_STRING = 0x03 - TYPE_FLOAT = 0x04 - TYPE_DIMENSION = 0x05 - TYPE_FRACTION = 0x06 - TYPE_FIRST_INT = 0x10 - TYPE_INT_DEC = 0x10 - TYPE_INT_HEX = 0x11 - TYPE_INT_BOOLEAN = 0x12 - TYPE_FIRST_COLOR_INT = 0x1c - TYPE_INT_COLOR_ARGB8 = 0x1c - TYPE_INT_COLOR_RGB8 = 0x1d - TYPE_INT_COLOR_ARGB4 = 0x1e - TYPE_INT_COLOR_RGB4 = 0x1f - TYPE_LAST_COLOR_INT = 0x1f - TYPE_LAST_INT = 0x1f + TypeNull DataType = 0x00 + TypeReference DataType = 0x01 + TypeAttribute DataType = 0x02 + TypeString DataType = 0x03 + TypeFloat DataType = 0x04 + TypeDemention DataType = 0x05 + TypeFraction DataType = 0x06 + TypeFirstInt DataType = 0x10 + TypeIntDec DataType = 0x10 + TypeIntHex DataType = 0x11 + TypeIntBoolean DataType = 0x12 + TypeFirstColorInt DataType = 0x1c + TypeIntColorARGB8 DataType = 0x1c + TypeIntColorRGB8 DataType = 0x1d + TypeIntColorARGB4 DataType = 0x1e + TypeIntColorRGB4 DataType = 0x1f + TypeLastColorInt DataType = 0x1f + TypeLastInt DataType = 0x1f ) +// ResValue is a representation of a value in a resource type ResValue struct { Size uint16 Res0 uint8 - DataType uint8 + DataType DataType Data uint32 } +// GetString returns a string referenced by ref. func (pool *ResStringPool) GetString(ref ResStringPoolRef) string { return pool.Strings[int(ref)] } @@ -114,8 +139,10 @@ func readStringPool(sr *io.SectionReader) (*ResStringPool, error) { for i, start := range stringStarts { var str string var err error - sr.Seek(int64(sp.Header.StringStart+start), os.SEEK_SET) - if (sp.Header.Flags & UTF8_FLAG) == 0 { + if _, err := sr.Seek(int64(sp.Header.StringStart+start), seekStart); err != nil { + return nil, err + } + if (sp.Header.Flags & UTF8Flag) == 0 { str, err = readUTF16(sr) } else { str, err = readUTF8(sr) @@ -126,20 +153,14 @@ func readStringPool(sr *io.SectionReader) (*ResStringPool, error) { sp.Strings[i] = str } - sp.Styles = make([]string, sp.Header.StyleCount) + sp.Styles = make([]ResStringPoolSpan, sp.Header.StyleCount) for i, start := range styleStarts { - var str string - var err error - sr.Seek(int64(sp.Header.StylesStart+start), os.SEEK_SET) - if (sp.Header.Flags & UTF8_FLAG) == 0 { - str, err = readUTF16(sr) - } else { - str, err = readUTF8(sr) + if _, err := sr.Seek(int64(sp.Header.StylesStart+start), seekStart); err != nil { + return nil, err } - if err != nil { + if err := binary.Read(sr, binary.LittleEndian, &sp.Styles[i]); err != nil { return nil, err } - sp.Styles[i] = str } return sp, nil @@ -149,10 +170,7 @@ func readUTF16(sr *io.SectionReader) (string, error) { // read lenth of string size, err := readUTF16length(sr) if err != nil { - return "", nil - } - if size > 2000 { - return "", fmt.Errorf("invalid string length: %d", size) + return "", err } // read string value diff --git a/vendor/github.com/shogo82148/androidbinary/const_fallback.go b/vendor/github.com/shogo82148/androidbinary/const_fallback.go new file mode 100644 index 0000000..1bf4d5f --- /dev/null +++ b/vendor/github.com/shogo82148/androidbinary/const_fallback.go @@ -0,0 +1,7 @@ +// +build !go17 + +package androidbinary + +import "os" + +const seekStart = os.SEEK_SET diff --git a/vendor/github.com/shogo82148/androidbinary/const_go17.go b/vendor/github.com/shogo82148/androidbinary/const_go17.go new file mode 100644 index 0000000..2ea0d24 --- /dev/null +++ b/vendor/github.com/shogo82148/androidbinary/const_go17.go @@ -0,0 +1,7 @@ +// +build go17 + +package androidbinary + +import "io" + +const seekStart = io.SeekStart diff --git a/vendor/github.com/shogo82148/androidbinary/table.go b/vendor/github.com/shogo82148/androidbinary/table.go index 0af0643..0051e0f 100644 --- a/vendor/github.com/shogo82148/androidbinary/table.go +++ b/vendor/github.com/shogo82148/androidbinary/table.go @@ -2,28 +2,32 @@ package androidbinary import ( "encoding/binary" - "errors" "fmt" "io" - "os" + "strconv" + "strings" "unsafe" ) -type ResId uint32 +// ResID is ID for resources. +type ResID uint32 +// TableFile is a resrouce table file. type TableFile struct { stringPool *ResStringPool tablePackages map[uint32]*TablePackage } +// ResTableHeader is a header of TableFile. type ResTableHeader struct { Header ResChunkHeader PackageCount uint32 } +// ResTablePackage is a header of table packages. type ResTablePackage struct { Header ResChunkHeader - Id uint32 + ID uint32 Name [128]uint16 TypeStrings uint32 LastPublicType uint32 @@ -31,6 +35,7 @@ type ResTablePackage struct { LastPublicKey uint32 } +// TablePackage is a table package. type TablePackage struct { Header ResTablePackage TypeStrings *ResStringPool @@ -38,9 +43,10 @@ type TablePackage struct { TableTypes []*TableType } +// ResTableType is a type of a table. type ResTableType struct { Header ResChunkHeader - Id uint8 + ID uint8 Res0 uint8 Res1 uint16 EntryCount uint32 @@ -48,57 +54,67 @@ type ResTableType struct { Config ResTableConfig } +// ScreenLayout describes screen layout. +type ScreenLayout uint8 + // ScreenLayout bits const ( - MASK_SCREENSIZE = 0x0f - SCREENSIZE_ANY = 0x01 - SCREENSIZE_SMALL = 0x02 - SCREENSIZE_NORMAL = 0x03 - SCREENSIZE_LARGE = 0x04 - SCREENSIZE_XLARGE = 0x05 - - MASK_SCREENLONG = 0x30 - SHIFT_SCREENLONG = 4 - SCREENLONG_ANY = 0x00 - SCREENLONG_NO = 0x10 - SCREENLONG_YES = 0x20 - - MASK_LAYOUTDIR = 0xC0 - SHIFT_LAYOUTDIR = 6 - LAYOUTDIR_ANY = 0x00 - LAYOUTDIR_LTR = 0x40 - LAYOUTDIR_RTL = 0x80 + MaskScreenSize ScreenLayout = 0x0f + ScreenSizeAny ScreenLayout = 0x01 + ScreenSizeSmall ScreenLayout = 0x02 + ScreenSizeNormal ScreenLayout = 0x03 + ScreenSizeLarge ScreenLayout = 0x04 + ScreenSizeXLarge ScreenLayout = 0x05 + + MaskScreenLong ScreenLayout = 0x30 + ShiftScreenLong = 4 + ScreenLongAny ScreenLayout = 0x00 + ScreenLongNo ScreenLayout = 0x10 + ScreenLongYes ScreenLayout = 0x20 + + MaskLayoutDir ScreenLayout = 0xC0 + ShiftLayoutDir = 6 + LayoutDirAny ScreenLayout = 0x00 + LayoutDirLTR ScreenLayout = 0x40 + LayoutDirRTL ScreenLayout = 0x80 ) +// UIMode describes UI mode. +type UIMode uint8 + // UIMode bits const ( - MASK_UI_MODE_TYPE = 0x0f - UI_MODE_TYPE_ANY = 0x01 - UI_MODE_TYPE_NORMAL = 0x02 - UI_MODE_TYPE_DESK = 0x03 - UI_MODE_TYPE_CAR = 0x04 - - MASK_UI_MODE_NIGHT = 0x30 - SHIFT_UI_MODE_NIGHT = 4 - UI_MODE_NIGHT_ANY = 0x00 - UI_MODE_NIGHT_NO = 0x10 - UI_MODE_NIGHT_YES = 0x20 + MaskUIModeType UIMode = 0x0f + UIModeTypeAny UIMode = 0x01 + UIModeTypeNormal UIMode = 0x02 + UIModeTypeDesk UIMode = 0x03 + UIModeTypeCar UIMode = 0x04 + + MaskUIModeNight UIMode = 0x30 + ShiftUIModeNight = 4 + UIModeNightAny UIMode = 0x00 + UIModeNightNo UIMode = 0x10 + UIModeNightYes UIMode = 0x20 ) +// InputFlags are input flags. +type InputFlags uint8 + // input flags const ( - MASK_KEYSHIDDEN = 0x03 - KEYSHIDDEN_ANY = 0x00 - KEYSHIDDEN_NO = 0x01 - KEYSHIDDEN_YES = 0x02 - KEYSHIDDEN_SOFT = 0x03 - - MASK_NAVHIDDEN = 0x0c - NAVHIDDEN_ANY = 0x00 - NAVHIDDEN_NO = 0x04 - NAVHIDDEN_YES = 0x08 + MaskKeysHidden InputFlags = 0x03 + KeysHiddenAny InputFlags = 0x00 + KeysHiddenNo InputFlags = 0x01 + KeysHiddenYes InputFlags = 0x02 + KeysHiddenSoft InputFlags = 0x03 + + MaskNavHidden InputFlags = 0x0c + NavHiddenAny InputFlags = 0x00 + NavHiddenNo InputFlags = 0x04 + NavHiddenYes InputFlags = 0x08 ) +// ResTableConfig is a configuration of a table. type ResTableConfig struct { Size uint32 // imsi @@ -117,7 +133,7 @@ type ResTableConfig struct { // inout Keyboard uint8 Navigation uint8 - InputFlags uint8 + InputFlags InputFlags InputPad0 uint8 // screen size @@ -129,8 +145,8 @@ type ResTableConfig struct { MinorVersion uint16 // screen config - ScreenLayout uint8 - UIMode uint8 + ScreenLayout ScreenLayout + UIMode UIMode SmallestScreenWidthDp uint16 // screen size dp @@ -138,43 +154,72 @@ type ResTableConfig struct { ScreenHeightDp uint16 } +// TableType is a collection of resource entries for a particular resource data type. type TableType struct { Header *ResTableType Entries []TableEntry } +// ResTableEntry is the beginning of information about an entry in the resource table. type ResTableEntry struct { Size uint16 Flags uint16 Key ResStringPoolRef } +// TableEntry is a entry in a recource table. type TableEntry struct { Key *ResTableEntry Value *ResValue Flags uint32 } +// ResTableTypeSpec is specification of the resources defined by a particular type. type ResTableTypeSpec struct { Header ResChunkHeader - Id uint8 + ID uint8 Res0 uint8 Res1 uint16 EntryCount uint32 } -func (id ResId) Package() int { +// IsResID returns whether s is ResId. +func IsResID(s string) bool { + return strings.HasPrefix(s, "@0x") +} + +// ParseResID parses ResId. +func ParseResID(s string) (ResID, error) { + if !IsResID(s) { + return 0, fmt.Errorf("androidbinary: %s is not ResID", s) + } + id, err := strconv.ParseUint(s[3:], 16, 32) + if err != nil { + return 0, err + } + return ResID(id), nil +} + +func (id ResID) String() string { + return fmt.Sprintf("@0x%08X", uint32(id)) +} + +// Package returns the package index of id. +func (id ResID) Package() int { return int(id) >> 24 } -func (id ResId) Type() int { +// Type returns the type index of id. +func (id ResID) Type() int { return (int(id) >> 16) & 0xFF } -func (id ResId) Entry() int { +// Entry returns the entry index of id. +func (id ResID) Entry() int { return int(id) & 0xFFFF } +// NewTableFile returns new TableFile. func NewTableFile(r io.ReaderAt) (*TableFile, error) { f := new(TableFile) sr := io.NewSectionReader(r, 0, 1<<63-1) @@ -198,45 +243,55 @@ func (f *TableFile) findPackage(id int) *TablePackage { return f.tablePackages[uint32(id)] } -func (p *TablePackage) findType(id int, config *ResTableConfig) *TableType { +func (p *TablePackage) findEntry(typeIndex, entryIndex int, config *ResTableConfig) TableEntry { var best *TableType for _, t := range p.TableTypes { - if int(t.Header.Id) != id { - continue - } - if !t.Header.Config.Match(config) { - continue - } - if best == nil || t.Header.Config.IsBetterThan(&best.Header.Config, config) { + switch { + case int(t.Header.ID) != typeIndex: + // nothing to do + case !t.Header.Config.Match(config): + // nothing to do + case entryIndex >= len(t.Entries): + // nothing to do + case t.Entries[entryIndex].Value == nil: + // nothing to do + case best == nil || t.Header.Config.IsBetterThan(&best.Header.Config, config): best = t } } - return best + if best == nil || entryIndex >= len(best.Entries) { + return TableEntry{} + } + return best.Entries[entryIndex] } -func (f *TableFile) GetResource(id ResId, config *ResTableConfig) (interface{}, error) { +// GetResource returns a resrouce referenced by id. +func (f *TableFile) GetResource(id ResID, config *ResTableConfig) (interface{}, error) { p := f.findPackage(id.Package()) - t := p.findType(id.Type(), config) - e := t.Entries[id.Entry()] + if p == nil { + return nil, fmt.Errorf("androidbinary: package 0x%02X not found", id.Package()) + } + e := p.findEntry(id.Type(), id.Entry(), config) v := e.Value if v == nil { - return nil, errors.New("get resource error") + return nil, fmt.Errorf("androidbinary: entry 0x%04X not found", id.Entry()) } switch v.DataType { - case TYPE_NULL: + case TypeNull: return nil, nil - case TYPE_STRING: + case TypeString: return f.GetString(ResStringPoolRef(v.Data)), nil - case TYPE_INT_DEC: + case TypeIntDec: return v.Data, nil - case TYPE_INT_HEX: + case TypeIntHex: return v.Data, nil - case TYPE_INT_BOOLEAN: + case TypeIntBoolean: return v.Data != 0, nil } return v.Data, nil } +// GetString returns a string referenced by ref. func (f *TableFile) GetString(ref ResStringPoolRef) string { return f.stringPool.GetString(ref) } @@ -244,20 +299,24 @@ func (f *TableFile) GetString(ref ResStringPoolRef) string { func (f *TableFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) { sr := io.NewSectionReader(r, offset, 1<<63-1-offset) chunkHeader := &ResChunkHeader{} - sr.Seek(0, os.SEEK_SET) + if _, err := sr.Seek(0, seekStart); err != nil { + return nil, err + } if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { return nil, err } var err error - sr.Seek(0, os.SEEK_SET) + if _, err := sr.Seek(0, seekStart); err != nil { + return nil, err + } switch chunkHeader.Type { - case RES_STRING_POOL_TYPE: + case ResStringPoolChunkType: f.stringPool, err = readStringPool(sr) - case RES_TABLE_PACKAGE_TYPE: + case ResTablePackageType: var tablePackage *TablePackage tablePackage, err = readTablePackage(sr) - f.tablePackages[tablePackage.Header.Id] = tablePackage + f.tablePackages[tablePackage.Header.ID] = tablePackage } if err != nil { return nil, err @@ -291,20 +350,24 @@ func readTablePackage(sr *io.SectionReader) (*TablePackage, error) { offset := int64(header.Header.HeaderSize) for offset < int64(header.Header.Size) { chunkHeader := &ResChunkHeader{} - sr.Seek(offset, os.SEEK_SET) + if _, err := sr.Seek(offset, seekStart); err != nil { + return nil, err + } if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { return nil, err } var err error chunkReader := io.NewSectionReader(sr, offset, int64(chunkHeader.Size)) - sr.Seek(offset, os.SEEK_SET) + if _, err := sr.Seek(offset, seekStart); err != nil { + return nil, err + } switch chunkHeader.Type { - case RES_TABLE_TYPE_TYPE: + case ResTableTypeType: var tableType *TableType tableType, err = readTableType(chunkHeader, chunkReader) tablePackage.TableTypes = append(tablePackage.TableTypes, tableType) - case RES_TABLE_TYPE_SPEC_TYPE: + case ResTableTypeSpecType: _, err = readTableTypeSpec(chunkReader) } if err != nil { @@ -319,7 +382,9 @@ func readTablePackage(sr *io.SectionReader) (*TablePackage, error) { func readTableType(chunkHeader *ResChunkHeader, sr *io.SectionReader) (*TableType, error) { // TableType header may be omitted header := new(ResTableType) - sr.Seek(0, os.SEEK_SET) + if _, err := sr.Seek(0, seekStart); err != nil { + return nil, err + } buf, err := newZeroFilledReader(sr, int64(chunkHeader.HeaderSize), int64(unsafe.Sizeof(*header))) if err != nil { return nil, err @@ -329,7 +394,9 @@ func readTableType(chunkHeader *ResChunkHeader, sr *io.SectionReader) (*TableTyp } entryIndexes := make([]uint32, header.EntryCount) - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { + return nil, err + } if err := binary.Read(sr, binary.LittleEndian, entryIndexes); err != nil { return nil, err } @@ -339,7 +406,9 @@ func readTableType(chunkHeader *ResChunkHeader, sr *io.SectionReader) (*TableTyp if index == 0xFFFFFFFF { continue } - sr.Seek(int64(header.EntriesStart+index), os.SEEK_SET) + if _, err := sr.Seek(int64(header.EntriesStart+index), seekStart); err != nil { + return nil, err + } var key ResTableEntry binary.Read(sr, binary.LittleEndian, &key) entries[i].Key = &key @@ -361,14 +430,25 @@ func readTableTypeSpec(sr *io.SectionReader) ([]uint32, error) { } flags := make([]uint32, header.EntryCount) - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { + return nil, err + } if err := binary.Read(sr, binary.LittleEndian, flags); err != nil { return nil, err } return flags, nil } +// IsMoreSpecificThan returns true if c is more specific than o. func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { + // nil ResTableConfig is never more specific than any ResTableConfig + if c == nil { + return false + } + if o == nil { + return false + } + // imsi if c.Mcc != o.Mcc { if c.Mcc == 0 { @@ -388,30 +468,19 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { } // locale - if c.Language[0] != o.Language[0] { - if c.Language[0] == 0 { - return false - } - if o.Language[0] == 0 { - return true - } - } - if c.Country[0] != o.Country[0] { - if c.Country[0] == 0 { - return false - } - if o.Country[0] == 0 { - return true - } + if diff := c.IsLocaleMoreSpecificThan(o); diff < 0 { + return false + } else if diff > 0 { + return true } // screen layout if c.ScreenLayout != 0 || o.ScreenLayout != 0 { - if ((c.ScreenLayout ^ o.ScreenLayout) & MASK_LAYOUTDIR) != 0 { - if (c.ScreenLayout & MASK_LAYOUTDIR) == 0 { + if ((c.ScreenLayout ^ o.ScreenLayout) & MaskLayoutDir) != 0 { + if (c.ScreenLayout & MaskLayoutDir) == 0 { return false } - if (o.ScreenLayout & MASK_LAYOUTDIR) == 0 { + if (o.ScreenLayout & MaskLayoutDir) == 0 { return true } } @@ -452,19 +521,19 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { // screen layout if c.ScreenLayout != 0 || o.ScreenLayout != 0 { - if ((c.ScreenLayout ^ o.ScreenLayout) & MASK_SCREENSIZE) != 0 { - if (c.ScreenLayout & MASK_SCREENSIZE) == 0 { + if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenSize) != 0 { + if (c.ScreenLayout & MaskScreenSize) == 0 { return false } - if (o.ScreenLayout & MASK_SCREENSIZE) == 0 { + if (o.ScreenLayout & MaskScreenSize) == 0 { return true } } - if ((c.ScreenLayout ^ o.ScreenLayout) & MASK_SCREENLONG) != 0 { - if (c.ScreenLayout & MASK_SCREENLONG) == 0 { + if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenLong) != 0 { + if (c.ScreenLayout & MaskScreenLong) == 0 { return false } - if (o.ScreenLayout & MASK_SCREENLONG) == 0 { + if (o.ScreenLayout & MaskScreenLong) == 0 { return true } } @@ -483,19 +552,19 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { // uimode if c.UIMode != 0 || o.UIMode != 0 { diff := c.UIMode ^ o.UIMode - if (diff & MASK_UI_MODE_TYPE) != 0 { - if (c.UIMode & MASK_UI_MODE_TYPE) == 0 { + if (diff & MaskUIModeType) != 0 { + if (c.UIMode & MaskUIModeType) == 0 { return false } - if (o.UIMode & MASK_UI_MODE_TYPE) == 0 { + if (o.UIMode & MaskUIModeType) == 0 { return true } } - if (diff & MASK_UI_MODE_NIGHT) != 0 { - if (c.UIMode & MASK_UI_MODE_NIGHT) == 0 { + if (diff & MaskUIModeNight) != 0 { + if (c.UIMode & MaskUIModeNight) == 0 { return false } - if (o.UIMode & MASK_UI_MODE_NIGHT) == 0 { + if (o.UIMode & MaskUIModeNight) == 0 { return true } } @@ -513,8 +582,8 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { // input if c.InputFlags != 0 || o.InputFlags != 0 { - myKeysHidden := c.InputFlags & MASK_KEYSHIDDEN - oKeysHidden := o.InputFlags & MASK_KEYSHIDDEN + myKeysHidden := c.InputFlags & MaskKeysHidden + oKeysHidden := o.InputFlags & MaskKeysHidden if (myKeysHidden ^ oKeysHidden) != 0 { if myKeysHidden == 0 { return false @@ -523,8 +592,8 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { return true } } - myNavHidden := c.InputFlags & MASK_NAVHIDDEN - oNavHidden := o.InputFlags & MASK_NAVHIDDEN + myNavHidden := c.InputFlags & MaskNavHidden + oNavHidden := o.InputFlags & MaskNavHidden if (myNavHidden ^ oNavHidden) != 0 { if myNavHidden == 0 { return false @@ -595,11 +664,20 @@ func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { return false } +// IsBetterThan returns true if c is better than o for the r configuration. func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool { if r == nil { return c.IsMoreSpecificThan(o) } + // nil ResTableConfig is never better than any ResTableConfig + if c == nil { + return false + } + if o == nil { + return false + } + // imsi if c.Mcc != 0 || c.Mnc != 0 || o.Mcc != 0 || o.Mnc != 0 { if c.Mcc != o.Mcc && r.Mcc != 0 { @@ -611,20 +689,15 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool } // locale - if c.Language[0] != 0 || c.Country[0] != 0 || o.Language[0] != 0 || o.Country[0] != 0 { - if c.Language[0] != o.Language[0] && r.Language[0] != 0 { - return c.Language[0] != 0 - } - if c.Country[0] != o.Country[0] && r.Country[0] != 0 { - return c.Country[0] != 0 - } + if c.IsLocaleBetterThan(o, r) { + return true } // screen layout if c.ScreenLayout != 0 || o.ScreenLayout != 0 { - myLayoutdir := c.ScreenLayout & MASK_LAYOUTDIR - oLayoutdir := o.ScreenLayout & MASK_LAYOUTDIR - if (myLayoutdir^oLayoutdir) != 0 && (r.ScreenLayout&MASK_LAYOUTDIR) != 0 { + myLayoutdir := c.ScreenLayout & MaskLayoutDir + oLayoutdir := o.ScreenLayout & MaskLayoutDir + if (myLayoutdir^oLayoutdir) != 0 && (r.ScreenLayout&MaskLayoutDir) != 0 { return myLayoutdir > oLayoutdir } } @@ -655,29 +728,28 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool // screen layout if c.ScreenLayout != 0 || o.ScreenLayout != 0 { - mySL := c.ScreenLayout & MASK_SCREENSIZE - oSL := o.ScreenLayout & MASK_SCREENSIZE - if (mySL^oSL) != 0 && (r.ScreenLayout&MASK_SCREENSIZE) != 0 { + mySL := c.ScreenLayout & MaskScreenSize + oSL := o.ScreenLayout & MaskScreenSize + if (mySL^oSL) != 0 && (r.ScreenLayout&MaskScreenSize) != 0 { fixedMySL := mySL fixedOSL := oSL - if (r.ScreenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL { + if (r.ScreenLayout & MaskScreenSize) >= ScreenSizeNormal { if fixedMySL == 0 { - fixedMySL = SCREENSIZE_NORMAL + fixedMySL = ScreenSizeNormal } if fixedOSL == 0 { - fixedOSL = SCREENSIZE_NORMAL + fixedOSL = ScreenSizeNormal } } if fixedMySL == fixedOSL { return mySL != 0 - } else { - return fixedMySL > fixedOSL } + return fixedMySL > fixedOSL } - if ((c.ScreenLayout^o.ScreenLayout)&MASK_SCREENLONG) != 0 && - (r.ScreenLayout&MASK_SCREENLONG) != 0 { - return (c.ScreenLayout & MASK_SCREENLONG) != 0 + if ((c.ScreenLayout^o.ScreenLayout)&MaskScreenLong) != 0 && + (r.ScreenLayout&MaskScreenLong) != 0 { + return (c.ScreenLayout & MaskScreenLong) != 0 } } @@ -689,11 +761,11 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool // uimode if c.UIMode != 0 || o.UIMode != 0 { diff := c.UIMode ^ o.UIMode - if (diff&MASK_UI_MODE_TYPE) != 0 && (r.UIMode&MASK_UI_MODE_TYPE) != 0 { - return (c.UIMode & MASK_UI_MODE_TYPE) != 0 + if (diff&MaskUIModeType) != 0 && (r.UIMode&MaskUIModeType) != 0 { + return (c.UIMode & MaskUIModeType) != 0 } - if (diff&MASK_UI_MODE_NIGHT) != 0 && (r.UIMode&MASK_UI_MODE_NIGHT) != 0 { - return (c.UIMode & MASK_UI_MODE_NIGHT) != 0 + if (diff&MaskUIModeNight) != 0 && (r.UIMode&MaskUIModeNight) != 0 { + return (c.UIMode & MaskUIModeNight) != 0 } } @@ -725,9 +797,8 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool } if (2*l-reqValue)*h > reqValue*reqValue { return !blmBigger - } else { - return blmBigger } + return blmBigger } if c.Touchscreen != o.Touchscreen && r.Touchscreen != 0 { return c.Touchscreen != 0 @@ -735,9 +806,9 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool // input if c.InputFlags != 0 || o.InputFlags != 0 { - myKeysHidden := c.InputFlags & MASK_KEYSHIDDEN - oKeysHidden := o.InputFlags & MASK_KEYSHIDDEN - reqKeysHidden := r.InputFlags & MASK_KEYSHIDDEN + myKeysHidden := c.InputFlags & MaskKeysHidden + oKeysHidden := o.InputFlags & MaskKeysHidden + reqKeysHidden := r.InputFlags & MaskKeysHidden if myKeysHidden != oKeysHidden && reqKeysHidden != 0 { switch { case myKeysHidden == 0: @@ -750,9 +821,9 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool return false } } - myNavHidden := c.InputFlags & MASK_NAVHIDDEN - oNavHidden := o.InputFlags & MASK_NAVHIDDEN - reqNavHidden := r.InputFlags & MASK_NAVHIDDEN + myNavHidden := c.InputFlags & MaskNavHidden + oNavHidden := o.InputFlags & MaskNavHidden + reqNavHidden := r.InputFlags & MaskNavHidden if myNavHidden != oNavHidden && reqNavHidden != 0 { switch { case myNavHidden == 0: @@ -787,7 +858,7 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool } // version - if c.SDKVersion != 0 || c.SDKVersion != 0 || o.MinorVersion != 0 || o.MinorVersion != 0 { + if c.SDKVersion != 0 || o.MinorVersion != 0 { if c.SDKVersion != o.SDKVersion && r.SDKVersion != 0 { return c.SDKVersion > o.SDKVersion } @@ -799,7 +870,77 @@ func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool return false } +// IsLocaleMoreSpecificThan a positive integer if this config is more specific than o, +// a negative integer if |o| is more specific +// and 0 if they're equally specific. +func (c *ResTableConfig) IsLocaleMoreSpecificThan(o *ResTableConfig) int { + if (c.Language != [2]uint8{} || c.Country != [2]uint8{}) || (o.Language != [2]uint8{} || o.Country != [2]uint8{}) { + if c.Language != o.Language { + if c.Language == [2]uint8{} { + return -1 + } + if o.Language == [2]uint8{} { + return 1 + } + } + + if c.Country != o.Country { + if c.Country == [2]uint8{} { + return -1 + } + if o.Country == [2]uint8{} { + return 1 + } + } + } + return 0 +} + +// IsLocaleBetterThan returns true if c is a better locale match than o for the r configuration. +func (c *ResTableConfig) IsLocaleBetterThan(o *ResTableConfig, r *ResTableConfig) bool { + if r.Language == [2]uint8{} && r.Country == [2]uint8{} { + // The request doesn't have a locale, so no resource is better + // than the other. + return false + } + + if c.Language == [2]uint8{} && c.Country == [2]uint8{} && o.Language == [2]uint8{} && o.Country == [2]uint8{} { + // The locales parts of both resources are empty, so no one is better + // than the other. + return false + } + + if c.Language != o.Language { + // The languages of the two resources are not the same. + + // the US English resource have traditionally lived for most apps. + if r.Language == [2]uint8{'e', 'n'} { + if r.Country == [2]uint8{'U', 'S'} { + if c.Language != [2]uint8{} { + return c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'} + } + return !(c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'}) + } + } + return c.Language != [2]uint8{} + } + + if c.Country != o.Country { + return c.Country != [2]uint8{} + } + + return false +} + +// Match returns true if c can be considered a match for the parameters in settings. func (c *ResTableConfig) Match(settings *ResTableConfig) bool { + // nil ResTableConfig always matches. + if settings == nil { + return true + } else if c == nil { + return *settings == ResTableConfig{} + } + // match imsi if settings.Mcc == 0 { if c.Mcc != 0 { @@ -821,43 +962,49 @@ func (c *ResTableConfig) Match(settings *ResTableConfig) bool { } // match locale - if settings.Language[0] != 0 && c.Language[0] != 0 && - !(settings.Language[0] == c.Language[0] && settings.Language[1] == c.Language[1]) { - return false - } - if settings.Country[0] != 0 && c.Country[0] != 0 && - !(settings.Country[0] == c.Country[0] && settings.Country[1] == c.Country[1]) { - return false + if c.Language != [2]uint8{0, 0} { + // Don't consider country and variants when deciding matches. + // If two configs differ only in their country and variant, + // they can be weeded out in the isMoreSpecificThan test. + if c.Language != settings.Language { + return false + } + + if c.Country != [2]uint8{0, 0} { + if c.Country != settings.Country { + return false + } + } } // screen layout - layoutDir := c.ScreenLayout & MASK_LAYOUTDIR - setLayoutDir := settings.ScreenLayout & MASK_LAYOUTDIR + layoutDir := c.ScreenLayout & MaskLayoutDir + setLayoutDir := settings.ScreenLayout & MaskLayoutDir if layoutDir != 0 && layoutDir != setLayoutDir { return false } - screenSize := c.ScreenLayout & MASK_SCREENSIZE - setScreenSize := settings.ScreenLayout & MASK_SCREENSIZE + screenSize := c.ScreenLayout & MaskScreenSize + setScreenSize := settings.ScreenLayout & MaskScreenSize if screenSize != 0 && screenSize > setScreenSize { return false } - screenLong := c.ScreenLayout & MASK_SCREENLONG - setScreenLong := settings.ScreenLayout & MASK_SCREENLONG + screenLong := c.ScreenLayout & MaskScreenLong + setScreenLong := settings.ScreenLayout & MaskScreenLong if screenLong != 0 && screenLong != setScreenLong { return false } // ui mode - uiModeType := c.UIMode & MASK_UI_MODE_TYPE - setUIModeType := settings.UIMode & MASK_UI_MODE_TYPE + uiModeType := c.UIMode & MaskUIModeType + setUIModeType := settings.UIMode & MaskUIModeType if uiModeType != 0 && uiModeType != setUIModeType { return false } - uiModeNight := c.UIMode & MASK_UI_MODE_NIGHT - setUIModeNight := settings.UIMode & MASK_UI_MODE_NIGHT + uiModeNight := c.UIMode & MaskUIModeNight + setUIModeNight := settings.UIMode & MaskUIModeNight if uiModeNight != 0 && uiModeNight != setUIModeNight { return false } @@ -888,15 +1035,15 @@ func (c *ResTableConfig) Match(settings *ResTableConfig) bool { // input if c.InputFlags != 0 { - myKeysHidden := c.InputFlags & MASK_KEYSHIDDEN - oKeysHidden := settings.InputFlags & MASK_KEYSHIDDEN + myKeysHidden := c.InputFlags & MaskKeysHidden + oKeysHidden := settings.InputFlags & MaskKeysHidden if myKeysHidden != 0 && myKeysHidden != oKeysHidden { - if myKeysHidden != KEYSHIDDEN_NO || oKeysHidden != KEYSHIDDEN_SOFT { + if myKeysHidden != KeysHiddenNo || oKeysHidden != KeysHiddenSoft { return false } } - myNavHidden := c.InputFlags & MASK_NAVHIDDEN - oNavHidden := settings.InputFlags & MASK_NAVHIDDEN + myNavHidden := c.InputFlags & MaskNavHidden + oNavHidden := settings.InputFlags & MaskNavHidden if myNavHidden != 0 && myNavHidden != oNavHidden { return false } @@ -931,6 +1078,7 @@ func (c *ResTableConfig) Match(settings *ResTableConfig) bool { return true } +// Locale returns the locale of the configuration. func (c *ResTableConfig) Locale() string { if c.Language[0] == 0 { return "" diff --git a/vendor/github.com/shogo82148/androidbinary/xml.go b/vendor/github.com/shogo82148/androidbinary/xml.go index b5aa745..f255bfa 100644 --- a/vendor/github.com/shogo82148/androidbinary/xml.go +++ b/vendor/github.com/shogo82148/androidbinary/xml.go @@ -6,9 +6,9 @@ import ( "encoding/xml" "fmt" "io" - "os" ) +// XMLFile is an XML file expressed in binary format. type XMLFile struct { stringPool *ResStringPool resourceMap []uint32 @@ -17,28 +17,32 @@ type XMLFile struct { 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 + 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 + IDIndex uint16 ClassIndex uint16 StyleIndex uint16 } +// ResXMLTreeAttribute is an attribute of start tags. type ResXMLTreeAttribute struct { NS ResStringPoolRef Name ResStringPoolRef @@ -46,11 +50,13 @@ type ResXMLTreeAttribute struct { 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) @@ -72,6 +78,7 @@ func NewXMLFile(r io.ReaderAt) (*XMLFile, error) { 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()) } @@ -79,23 +86,27 @@ func (f *XMLFile) Reader() *bytes.Reader { func (f *XMLFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) { sr := io.NewSectionReader(r, offset, 1<<63-1-offset) chunkHeader := &ResChunkHeader{} - sr.Seek(0, os.SEEK_SET) + if _, err := sr.Seek(0, seekStart); err != nil { + return nil, err + } if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { return nil, err } var err error - sr.Seek(0, os.SEEK_SET) + if _, err := sr.Seek(0, seekStart); err != nil { + return nil, err + } switch chunkHeader.Type { - case RES_STRING_POOL_TYPE: + case ResStringPoolChunkType: f.stringPool, err = readStringPool(sr) - case RES_XML_START_NAMESPACE_TYPE: + case ResXMLStartNamespaceType: err = f.readStartNamespace(sr) - case RES_XML_END_NAMESPACE_TYPE: + case ResXMLEndNamespaceType: err = f.readEndNamespace(sr) - case RES_XML_START_ELEMENT_TYPE: + case ResXMLStartElementType: err = f.readStartElement(sr) - case RES_XML_END_ELEMENT_TYPE: + case ResXMLEndElementType: err = f.readEndElement(sr) } if err != nil { @@ -105,6 +116,7 @@ func (f *XMLFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error return chunkHeader, nil } +// GetString returns a string referenced by ref. func (f *XMLFile) GetString(ref ResStringPoolRef) string { return f.stringPool.GetString(ref) } @@ -115,7 +127,9 @@ func (f *XMLFile) readStartNamespace(sr *io.SectionReader) error { return err } - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { + return err + } namespace := new(ResXMLTreeNamespaceExt) if err := binary.Read(sr, binary.LittleEndian, namespace); err != nil { return err @@ -124,12 +138,12 @@ func (f *XMLFile) readStartNamespace(sr *io.SectionReader) error { if f.notPrecessedNS == nil { f.notPrecessedNS = make(map[ResStringPoolRef]ResStringPoolRef) } - f.notPrecessedNS[namespace.Uri] = namespace.Prefix + f.notPrecessedNS[namespace.URI] = namespace.Prefix if f.namespaces == nil { f.namespaces = make(map[ResStringPoolRef]ResStringPoolRef) } - f.namespaces[namespace.Uri] = namespace.Prefix + f.namespaces[namespace.URI] = namespace.Prefix return nil } @@ -140,12 +154,14 @@ func (f *XMLFile) readEndNamespace(sr *io.SectionReader) error { return err } - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), 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) + delete(f.namespaces, namespace.URI) return nil } @@ -163,7 +179,9 @@ func (f *XMLFile) readStartElement(sr *io.SectionReader) error { return err } - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { + return err + } ext := new(ResXMLTreeAttrExt) if err := binary.Read(sr, binary.LittleEndian, ext); err != nil { return nil @@ -184,7 +202,9 @@ func (f *XMLFile) readStartElement(sr *io.SectionReader) error { // process attributes offset := int64(ext.AttributeStart + header.Header.HeaderSize) for i := 0; i < int(ext.AttributeCount); i++ { - sr.Seek(offset, os.SEEK_SET) + if _, err := sr.Seek(offset, seekStart); err != nil { + return err + } attr := new(ResXMLTreeAttribute) binary.Read(sr, binary.LittleEndian, attr) @@ -194,15 +214,15 @@ func (f *XMLFile) readStartElement(sr *io.SectionReader) error { } else { data := attr.TypedValue.Data switch attr.TypedValue.DataType { - case TYPE_NULL: + case TypeNull: value = "" - case TYPE_REFERENCE: + case TypeReference: value = fmt.Sprintf("@0x%08X", data) - case TYPE_INT_DEC: + case TypeIntDec: value = fmt.Sprintf("%d", data) - case TYPE_INT_HEX: + case TypeIntHex: value = fmt.Sprintf("0x%08X", data) - case TYPE_INT_BOOLEAN: + case TypeIntBoolean: if data != 0 { value = "true" } else { @@ -227,7 +247,9 @@ func (f *XMLFile) readEndElement(sr *io.SectionReader) error { if err := binary.Read(sr, binary.LittleEndian, header); err != nil { return err } - sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET) + if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { + return err + } ext := new(ResXMLTreeEndElementExt) if err := binary.Read(sr, binary.LittleEndian, ext); err != nil { return err