├── .gitignore ├── .travis.yml ├── testdata ├── helloworld.apk └── helloworld.ipa ├── vendor ├── github.com │ ├── shogo82148 │ │ └── androidbinary │ │ │ ├── const_go17.go │ │ │ ├── const_fallback.go │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── apk │ │ │ ├── apkxml.go │ │ │ └── apk.go │ │ │ ├── common.go │ │ │ ├── xml.go │ │ │ └── table.go │ ├── DHowett │ │ └── go-plist │ │ │ ├── zerocopy_appengine.go │ │ │ ├── fuzz.go │ │ │ ├── doc.go │ │ │ ├── zerocopy.go │ │ │ ├── util.go │ │ │ ├── README.md │ │ │ ├── bplist.go │ │ │ ├── must.go │ │ │ ├── text_tables.go │ │ │ ├── plist.go │ │ │ ├── plist_types.go │ │ │ ├── LICENSE │ │ │ ├── encode.go │ │ │ ├── decode.go │ │ │ ├── xml_generator.go │ │ │ ├── typeinfo.go │ │ │ ├── xml_parser.go │ │ │ ├── text_generator.go │ │ │ ├── marshal.go │ │ │ ├── bplist_generator.go │ │ │ ├── unmarshal.go │ │ │ ├── bplist_parser.go │ │ │ └── text_parser.go │ ├── pkg │ │ └── errors │ │ │ ├── appveyor.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── stack.go │ │ │ └── errors.go │ └── andrianbdn │ │ └── iospng │ │ ├── README.md │ │ ├── LICENSE │ │ └── iospng.go └── vendor.json ├── README.md ├── LICENSE ├── parser_test.go └── parser.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.x 4 | script: 5 | - go test -v ./... 6 | -------------------------------------------------------------------------------- /testdata/helloworld.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiethen/ipapk/HEAD/testdata/helloworld.apk -------------------------------------------------------------------------------- /testdata/helloworld.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kiethen/ipapk/HEAD/testdata/helloworld.ipa -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/const_go17.go: -------------------------------------------------------------------------------- 1 | // +build go17 2 | 3 | package androidbinary 4 | 5 | import "io" 6 | 7 | const seekStart = io.SeekStart 8 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/const_fallback.go: -------------------------------------------------------------------------------- 1 | // +build !go17 2 | 3 | package androidbinary 4 | 5 | import "os" 6 | 7 | const seekStart = os.SEEK_SET 8 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/zerocopy_appengine.go: -------------------------------------------------------------------------------- 1 | // +build appengine 2 | 3 | package plist 4 | 5 | func zeroCopy8BitString(buf []byte, off int, len int) string { 6 | return string(buf[off : off+len]) 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package plist 4 | 5 | import ( 6 | "bytes" 7 | ) 8 | 9 | func Fuzz(data []byte) int { 10 | buf := bytes.NewReader(data) 11 | 12 | var obj interface{} 13 | if err := NewDecoder(buf).Decode(&obj); err != nil { 14 | return 0 15 | } 16 | return 1 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/doc.go: -------------------------------------------------------------------------------- 1 | // Package plist implements encoding and decoding of Apple's "property list" format. 2 | // Property lists come in three sorts: plain text (GNUStep and OpenStep), XML and binary. 3 | // plist supports all of them. 4 | // The mapping between property list and Go objects is described in the documentation for the Marshal and Unmarshal functions. 5 | package plist 6 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/zerocopy.go: -------------------------------------------------------------------------------- 1 | // +build !appengine 2 | 3 | package plist 4 | 5 | import ( 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | func zeroCopy8BitString(buf []byte, off int, len int) string { 11 | if len == 0 { 12 | return "" 13 | } 14 | 15 | var s string 16 | hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) 17 | hdr.Data = uintptr(unsafe.Pointer(&buf[off])) 18 | hdr.Len = len 19 | return s 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipapk 2 | ipa or apk parser written in golang, aims to extract app information 3 | 4 | [![Build Status](https://travis-ci.org/phinexdaz/ipapk.svg?branch=master)](https://travis-ci.org/phinexdaz/ipapk) 5 | 6 | ## INSTALL 7 | $ go get github.com/phinexdaz/ipapk 8 | 9 | ## USAGE 10 | ```go 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "github.com/phinexdaz/ipapk" 16 | ) 17 | 18 | func main() { 19 | apk, _ := ipapk.NewAppParser("test.apk") 20 | fmt.Println(apk) 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/util.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import "io" 4 | 5 | type countedWriter struct { 6 | io.Writer 7 | nbytes int 8 | } 9 | 10 | func (w *countedWriter) Write(p []byte) (int, error) { 11 | n, err := w.Writer.Write(p) 12 | w.nbytes += n 13 | return n, err 14 | } 15 | 16 | func (w *countedWriter) BytesWritten() int { 17 | return w.nbytes 18 | } 19 | 20 | func unsignedGetBase(s string) (string, int) { 21 | if len(s) > 1 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 22 | return s[2:], 16 23 | } 24 | return s, 10 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/README.md: -------------------------------------------------------------------------------- 1 | # plist - A pure Go property list transcoder [![coverage report](https://gitlab.howett.net/DHowett/plist/badges/master/coverage.svg)](https://gitlab.howett.net/DHowett/plist/commits/master) 2 | ## INSTALL 3 | ``` 4 | $ go get howett.net/plist 5 | ``` 6 | 7 | ## FEATURES 8 | * Supports encoding/decoding property lists (Apple XML, Apple Binary, OpenStep and GNUStep) from/to arbitrary Go types 9 | 10 | ## USE 11 | ```go 12 | package main 13 | import ( 14 | "howett.net/plist" 15 | "os" 16 | ) 17 | func main() { 18 | encoder := plist.NewEncoder(os.Stdout) 19 | encoder.Encode(map[string]string{"hello": "world"}) 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/bplist.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | type bplistTrailer struct { 4 | Unused [5]uint8 5 | SortVersion uint8 6 | OffsetIntSize uint8 7 | ObjectRefSize uint8 8 | NumObjects uint64 9 | TopObject uint64 10 | OffsetTableOffset uint64 11 | } 12 | 13 | const ( 14 | bpTagNull uint8 = 0x00 15 | bpTagBoolFalse = 0x08 16 | bpTagBoolTrue = 0x09 17 | bpTagInteger = 0x10 18 | bpTagReal = 0x20 19 | bpTagDate = 0x30 20 | bpTagData = 0x40 21 | bpTagASCIIString = 0x50 22 | bpTagUTF16String = 0x60 23 | bpTagUID = 0x80 24 | bpTagArray = 0xA0 25 | bpTagDictionary = 0xD0 26 | ) 27 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\pkg\errors 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off 33 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/must.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "io" 5 | "strconv" 6 | ) 7 | 8 | type mustWriter struct { 9 | io.Writer 10 | } 11 | 12 | func (w mustWriter) Write(p []byte) (int, error) { 13 | n, err := w.Writer.Write(p) 14 | if err != nil { 15 | panic(err) 16 | } 17 | return n, nil 18 | } 19 | 20 | func mustParseInt(str string, base, bits int) int64 { 21 | i, err := strconv.ParseInt(str, base, bits) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return i 26 | } 27 | 28 | func mustParseUint(str string, base, bits int) uint64 { 29 | i, err := strconv.ParseUint(str, base, bits) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return i 34 | } 35 | 36 | func mustParseFloat(str string, bits int) float64 { 37 | i, err := strconv.ParseFloat(str, bits) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return i 42 | } 43 | 44 | func mustParseBool(str string) bool { 45 | i, err := strconv.ParseBool(str) 46 | if err != nil { 47 | panic(err) 48 | } 49 | return i 50 | } 51 | -------------------------------------------------------------------------------- /vendor/github.com/andrianbdn/iospng/README.md: -------------------------------------------------------------------------------- 1 | # iOS PNG File Normalizer 2 | 3 | This package reverts optimizations that are done by Xcode for PNG files when packaging iOS apps: 4 | 5 | - Removes CgBI chunks 6 | - Fixes compressed IDAT chunks 7 | - removes alpha pre-multiply 8 | 9 | The package does similar things like ipin.py or xcrun -sdk iphoneos pngcrush -revert-iphone-optimizations 10 | 11 | ## Installation 12 | 13 | 14 | The import path for the package is *github.com/andrianbdn/iospng*. 15 | 16 | To install it, run: 17 | 18 | go get github.com/andrianbdn/iospng 19 | 20 | 21 | ## Usage 22 | 23 | #### func PngRevertOptimization 24 | 25 | 26 | ```go 27 | func PngRevertOptimization(reader io.Reader, writer io.Writer) error 28 | ``` 29 | 30 | This function actually does everything: reads PNG from reader and in case it is iOS-optimized, reverts optimization. 31 | Function does not change data if PNG does not have CgBI chunk. 32 | 33 | 34 | ## See also 35 | 36 | - [CgBI file format](http://iphonedevwiki.net/index.php/CgBI_file_format) 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 phinexdaz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/andrianbdn/iospng/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2017 Andrian Budantsov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ichinose Shogo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/text_tables.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | type characterSet [4]uint64 4 | 5 | func (s *characterSet) Contains(ch rune) bool { 6 | return ch >= 0 && ch <= 255 && s.ContainsByte(byte(ch)) 7 | } 8 | 9 | func (s *characterSet) ContainsByte(ch byte) bool { 10 | return (s[ch/64]&(1<<(ch%64)) > 0) 11 | } 12 | 13 | // Bitmap of characters that must be inside a quoted string 14 | // when written to an old-style property list 15 | // Low bits represent lower characters, and each uint64 represents 64 characters. 16 | var gsQuotable = characterSet{ 17 | 0x78001385ffffffff, 18 | 0xa800000138000000, 19 | 0xffffffffffffffff, 20 | 0xffffffffffffffff, 21 | } 22 | 23 | // 7f instead of 3f in the top line: CFOldStylePlist.c says . is valid, but they quote it. 24 | var osQuotable = characterSet{ 25 | 0xf4007f6fffffffff, 26 | 0xf8000001f8000001, 27 | 0xffffffffffffffff, 28 | 0xffffffffffffffff, 29 | } 30 | 31 | var whitespace = characterSet{ 32 | 0x0000000100003f00, 33 | 0x0000000000000000, 34 | 0x0000000000000000, 35 | 0x0000000000000000, 36 | } 37 | 38 | var newlineCharacterSet = characterSet{ 39 | 0x0000000000002400, 40 | 0x0000000000000000, 41 | 0x0000000000000000, 42 | 0x0000000000000000, 43 | } 44 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "oYHVIFpLyGN6pVjljblPR+LS30I=", 7 | "path": "github.com/DHowett/go-plist", 8 | "revision": "84d08b7bddb35583bb3153b811c6cc52bb306f52", 9 | "revisionTime": "2018-02-14T08:12:42Z" 10 | }, 11 | { 12 | "checksumSHA1": "dwoXLBBJwlFDbqbYwSGcaQZvEYU=", 13 | "path": "github.com/andrianbdn/iospng", 14 | "revision": "eb5ebcdb1909fde8a38cac3df227c5717f7577df", 15 | "revisionTime": "2018-06-29T16:15:40Z" 16 | }, 17 | { 18 | "checksumSHA1": "ljd3FhYRJ91cLZz3wsH9BQQ2JbA=", 19 | "path": "github.com/pkg/errors", 20 | "revision": "816c9085562cd7ee03e7f8188a1cfd942858cded", 21 | "revisionTime": "2018-03-11T21:45:15Z" 22 | }, 23 | { 24 | "checksumSHA1": "CkhNiFyfIGR9ctRM4rEexItcblU=", 25 | "path": "github.com/shogo82148/androidbinary", 26 | "revision": "f2570763f91ec28d97aa329a78bfb2b93eb41822", 27 | "revisionTime": "2017-11-25T03:40:40Z" 28 | }, 29 | { 30 | "checksumSHA1": "RXqSQ/p78DgPX/UxRF8fjJPcMdg=", 31 | "path": "github.com/shogo82148/androidbinary/apk", 32 | "revision": "f2570763f91ec28d97aa329a78bfb2b93eb41822", 33 | "revisionTime": "2017-11-25T03:40:40Z" 34 | } 35 | ], 36 | "rootPath": "github.com/phinexdaz/ipapk" 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/README.md: -------------------------------------------------------------------------------- 1 | androidbinary 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/shogo82148/androidbinary.svg?branch=master)](https://travis-ci.org/shogo82148/androidbinary) 5 | [![GoDoc](https://godoc.org/github.com/shogo82148/androidbinary?status.svg)](https://godoc.org/github.com/shogo82148/androidbinary) 6 | 7 | Android binary file parser 8 | 9 | ## High Level API 10 | 11 | ### Parse APK files 12 | 13 | ``` go 14 | package main 15 | 16 | import ( 17 | "github.com/shogo82148/androidbinary/apk" 18 | ) 19 | 20 | func main() { 21 | pkg, _ := apk.OpenFile("your-android-app.apk") 22 | defer pkg.Close() 23 | 24 | icon, _ := apk.Icon(nil) // returns the icon of APK as image.Image 25 | pkgName := pkg.PackageName() // returns the pakcage name 26 | } 27 | ``` 28 | 29 | ## Low Lebel API 30 | 31 | ### Parse XML binary 32 | 33 | ``` go 34 | package main 35 | 36 | import ( 37 | "encoding/xml" 38 | 39 | "github.com/shogo82148/androidbinary" 40 | "github.com/shogo82148/androidbinary/apk" 41 | ) 42 | 43 | func main() { 44 | f, _ := os.Open("AndroidManifest.xml") 45 | xml, _ := androidbinary.NewXMLFile(f) 46 | reader := xml.Reader() 47 | 48 | // read XML from reader 49 | var manifest apk.Manifest 50 | data, _ := ioutil.ReadAll(reader) 51 | xml.Unmarshal(data, &manifest) 52 | } 53 | ``` 54 | 55 | ### Parse Resource files 56 | 57 | ``` go 58 | package main 59 | 60 | import ( 61 | "fmt" 62 | "github.com/shogo82148/androidbinary" 63 | ) 64 | 65 | func main() { 66 | f, _ := os.Open("resources.arsc") 67 | rsc, _ := androidbinary.NewTableFile(f) 68 | resorce, _ := rsc.GetResource(androidbinary.ResID(0xCAFEBABE), nil) 69 | fmt.Println(resource) 70 | } 71 | ``` 72 | 73 | ## License 74 | 75 | This software is released under the MIT License, see LICENSE. 76 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/plist.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // Property list format constants 8 | const ( 9 | // Used by Decoder to represent an invalid property list. 10 | InvalidFormat int = 0 11 | 12 | // Used to indicate total abandon with regards to Encoder's output format. 13 | AutomaticFormat = 0 14 | 15 | XMLFormat = 1 16 | BinaryFormat = 2 17 | OpenStepFormat = 3 18 | GNUStepFormat = 4 19 | ) 20 | 21 | var FormatNames = map[int]string{ 22 | InvalidFormat: "unknown/invalid", 23 | XMLFormat: "XML", 24 | BinaryFormat: "Binary", 25 | OpenStepFormat: "OpenStep", 26 | GNUStepFormat: "GNUStep", 27 | } 28 | 29 | type unknownTypeError struct { 30 | typ reflect.Type 31 | } 32 | 33 | func (u *unknownTypeError) Error() string { 34 | return "plist: can't marshal value of type " + u.typ.String() 35 | } 36 | 37 | type invalidPlistError struct { 38 | format string 39 | err error 40 | } 41 | 42 | func (e invalidPlistError) Error() string { 43 | s := "plist: invalid " + e.format + " property list" 44 | if e.err != nil { 45 | s += ": " + e.err.Error() 46 | } 47 | return s 48 | } 49 | 50 | type plistParseError struct { 51 | format string 52 | err error 53 | } 54 | 55 | func (e plistParseError) Error() string { 56 | s := "plist: error parsing " + e.format + " property list" 57 | if e.err != nil { 58 | s += ": " + e.err.Error() 59 | } 60 | return s 61 | } 62 | 63 | // A UID represents a unique object identifier. UIDs are serialized in a manner distinct from 64 | // that of integers. 65 | // 66 | // UIDs cannot be serialized in OpenStepFormat or GNUStepFormat property lists. 67 | type UID uint64 68 | 69 | // Marshaler is the interface implemented by types that can marshal themselves into valid 70 | // property list objects. The returned value is marshaled in place of the original value 71 | // implementing Marshaler 72 | // 73 | // If an error is returned by MarshalPlist, marshaling stops and the error is returned. 74 | type Marshaler interface { 75 | MarshalPlist() (interface{}, error) 76 | } 77 | 78 | // Unmarshaler is the interface implemented by types that can unmarshal themselves from 79 | // property list objects. The UnmarshalPlist method receives a function that may 80 | // be called to unmarshal the original property list value into a field or variable. 81 | // 82 | // It is safe to call the unmarshal function more than once. 83 | type Unmarshaler interface { 84 | UnmarshalPlist(unmarshal func(interface{}) error) error 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/README.md: -------------------------------------------------------------------------------- 1 | # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) 2 | 3 | Package errors provides simple error handling primitives. 4 | 5 | `go get github.com/pkg/errors` 6 | 7 | The traditional error handling idiom in Go is roughly akin to 8 | ```go 9 | if err != nil { 10 | return err 11 | } 12 | ``` 13 | which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. 14 | 15 | ## Adding context to an error 16 | 17 | The errors.Wrap function returns a new error that adds context to the original error. For example 18 | ```go 19 | _, err := ioutil.ReadAll(r) 20 | if err != nil { 21 | return errors.Wrap(err, "read failed") 22 | } 23 | ``` 24 | ## Retrieving the cause of an error 25 | 26 | Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. 27 | ```go 28 | type causer interface { 29 | Cause() error 30 | } 31 | ``` 32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: 33 | ```go 34 | switch err := errors.Cause(err).(type) { 35 | case *MyError: 36 | // handle specifically 37 | default: 38 | // unknown error 39 | } 40 | ``` 41 | 42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). 43 | 44 | ## Contributing 45 | 46 | We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. 47 | 48 | Before proposing a change, please discuss your change by raising an issue. 49 | 50 | ## License 51 | 52 | BSD-2-Clause 53 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/plist_types.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "hash/crc32" 5 | "sort" 6 | "time" 7 | ) 8 | 9 | type cfValue interface { 10 | typeName() string 11 | hash() interface{} 12 | } 13 | 14 | type cfDictionary struct { 15 | keys sort.StringSlice 16 | values []cfValue 17 | } 18 | 19 | func (*cfDictionary) typeName() string { 20 | return "dictionary" 21 | } 22 | 23 | func (p *cfDictionary) hash() interface{} { 24 | return p 25 | } 26 | 27 | func (p *cfDictionary) Len() int { 28 | return len(p.keys) 29 | } 30 | 31 | func (p *cfDictionary) Less(i, j int) bool { 32 | return p.keys.Less(i, j) 33 | } 34 | 35 | func (p *cfDictionary) Swap(i, j int) { 36 | p.keys.Swap(i, j) 37 | p.values[i], p.values[j] = p.values[j], p.values[i] 38 | } 39 | 40 | func (p *cfDictionary) sort() { 41 | sort.Sort(p) 42 | } 43 | 44 | type cfArray struct { 45 | values []cfValue 46 | } 47 | 48 | func (*cfArray) typeName() string { 49 | return "array" 50 | } 51 | 52 | func (p *cfArray) hash() interface{} { 53 | return p 54 | } 55 | 56 | type cfString string 57 | 58 | func (cfString) typeName() string { 59 | return "string" 60 | } 61 | 62 | func (p cfString) hash() interface{} { 63 | return string(p) 64 | } 65 | 66 | type cfNumber struct { 67 | signed bool 68 | value uint64 69 | } 70 | 71 | func (*cfNumber) typeName() string { 72 | return "integer" 73 | } 74 | 75 | func (p *cfNumber) hash() interface{} { 76 | if p.signed { 77 | return int64(p.value) 78 | } 79 | return p.value 80 | } 81 | 82 | type cfReal struct { 83 | wide bool 84 | value float64 85 | } 86 | 87 | func (cfReal) typeName() string { 88 | return "real" 89 | } 90 | 91 | func (p *cfReal) hash() interface{} { 92 | if p.wide { 93 | return p.value 94 | } 95 | return float32(p.value) 96 | } 97 | 98 | type cfBoolean bool 99 | 100 | func (cfBoolean) typeName() string { 101 | return "boolean" 102 | } 103 | 104 | func (p cfBoolean) hash() interface{} { 105 | return bool(p) 106 | } 107 | 108 | type cfUID UID 109 | 110 | func (cfUID) typeName() string { 111 | return "UID" 112 | } 113 | 114 | func (p cfUID) hash() interface{} { 115 | return p 116 | } 117 | 118 | type cfData []byte 119 | 120 | func (cfData) typeName() string { 121 | return "data" 122 | } 123 | 124 | func (p cfData) hash() interface{} { 125 | // Data are uniqued by their checksums. 126 | // Todo: Look at calculating this only once and storing it somewhere; 127 | // crc32 is fairly quick, however. 128 | return crc32.ChecksumIEEE([]byte(p)) 129 | } 130 | 131 | type cfDate time.Time 132 | 133 | func (cfDate) typeName() string { 134 | return "date" 135 | } 136 | 137 | func (p cfDate) hash() interface{} { 138 | return time.Time(p) 139 | } 140 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Dustin L. Howett. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | The views and conclusions contained in the software and documentation are those 24 | of the authors and should not be interpreted as representing official policies, 25 | either expressed or implied, of the FreeBSD Project. 26 | 27 | -------------------------------------------------------------------------------- 28 | Parts of this package were made available under the license covering 29 | the Go language and all attended core libraries. That license follows. 30 | -------------------------------------------------------------------------------- 31 | 32 | Copyright (c) 2012 The Go Authors. All rights reserved. 33 | 34 | Redistribution and use in source and binary forms, with or without 35 | modification, are permitted provided that the following conditions are 36 | met: 37 | 38 | * Redistributions of source code must retain the above copyright 39 | notice, this list of conditions and the following disclaimer. 40 | * Redistributions in binary form must reproduce the above 41 | copyright notice, this list of conditions and the following disclaimer 42 | in the documentation and/or other materials provided with the 43 | distribution. 44 | * Neither the name of Google Inc. nor the names of its 45 | contributors may be used to endorse or promote products derived from 46 | this software without specific prior written permission. 47 | 48 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 49 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 50 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 51 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 52 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 53 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 54 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 55 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 56 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 57 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 58 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/apk/apkxml.go: -------------------------------------------------------------------------------- 1 | package apk 2 | 3 | // Instrumentation is an application instrumentation code. 4 | type Instrumentation struct { 5 | Name string `xml:"name,attr"` 6 | Target string `xml:"targetPackage,attr"` 7 | HandleProfiling bool `xml:"handleProfiling,attr"` 8 | FunctionalTest bool `xml:"functionalTest,attr"` 9 | } 10 | 11 | // ActivityAction is an action of an activity. 12 | type ActivityAction struct { 13 | Name string `xml:"name,attr"` 14 | } 15 | 16 | // ActivityCategory is a category of an activity. 17 | type ActivityCategory struct { 18 | Name string `xml:"name,attr"` 19 | } 20 | 21 | // ActivityIntentFilter is an intent filter of an activity. 22 | type ActivityIntentFilter struct { 23 | Action ActivityAction `xml:"action"` 24 | Category ActivityCategory `xml:"category"` 25 | } 26 | 27 | // AppActivity is an activity in an application. 28 | type AppActivity struct { 29 | Theme string `xml:"theme,attr"` 30 | Name string `xml:"name,attr"` 31 | Label string `xml:"label,attr"` 32 | IntentFilter []ActivityIntentFilter `xml:"intent-filter"` 33 | } 34 | 35 | // Application is an application in an APK. 36 | type Application struct { 37 | AllowTaskReparenting bool `xml:"allowTaskReparenting,attr"` 38 | AllowBackup bool `xml:"allowBackup,attr"` 39 | BackupAgent string `xml:"backupAgent,attr"` 40 | Debuggable bool `xml:"debuggable,attr"` 41 | Description string `xml:"description,attr"` 42 | Enabled bool `xml:"enabled,attr"` 43 | HasCode bool `xml:"hasCode,attr"` 44 | HardwareAccelerated bool `xml:"hardwareAccelerated,attr"` 45 | Icon string `xml:"icon,attr"` 46 | KillAfterRestore bool `xml:"killAfterRestore,attr"` 47 | LargeHeap bool `xml:"largeHeap,attr"` 48 | Label string `xml:"label,attr"` 49 | Logo int `xml:"logo,attr"` 50 | ManageSpaceActivity string `xml:"manageSpaceActivity,attr"` 51 | Name string `xml:"name,attr"` 52 | Permission string `xml:"permission,attr"` 53 | Persistent bool `xml:"persistent,attr"` 54 | Process string `xml:"process,attr"` 55 | RestoreAnyVersion bool `xml:"restoreAnyVersion,attr"` 56 | RequiredAccountType string `xml:"requiredAccountType,attr"` 57 | RestrictedAccountType string `xml:"restrictedAccountType,attr"` 58 | SupportsRtl bool `xml:"supportsRtl,attr"` 59 | TaskAffinity string `xml:"taskAffinity,attr"` 60 | TestOnly bool `xml:"testOnly,attr"` 61 | Theme string `xml:"theme,attr"` 62 | UIOptions string `xml:"uiOptions,attr"` 63 | VMSafeMode bool `xml:"vmSafeMode,attr"` 64 | Activity []AppActivity `xml:"activity"` 65 | } 66 | 67 | // UsesSDK is target SDK version. 68 | type UsesSDK struct { 69 | Min int `xml:"minSdkVersion,attr"` 70 | Target int `xml:"targetSdkVersion,attr"` 71 | Max int `xml:"maxSdkVersion,attr"` 72 | } 73 | 74 | // Manifest is a manifest of an APK. 75 | type Manifest struct { 76 | Package string `xml:"package,attr"` 77 | VersionCode int `xml:"versionCode,attr"` 78 | VersionName string `xml:"versionName,attr"` 79 | App Application `xml:"application"` 80 | Instrument Instrumentation `xml:"instrumentation"` 81 | SDK UsesSDK `xml:"uses-sdk"` 82 | } 83 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/stack.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | // Frame represents a program counter inside a stack frame. 12 | type Frame uintptr 13 | 14 | // pc returns the program counter for this frame; 15 | // multiple frames may have the same PC value. 16 | func (f Frame) pc() uintptr { return uintptr(f) - 1 } 17 | 18 | // file returns the full path to the file that contains the 19 | // function for this Frame's pc. 20 | func (f Frame) file() string { 21 | fn := runtime.FuncForPC(f.pc()) 22 | if fn == nil { 23 | return "unknown" 24 | } 25 | file, _ := fn.FileLine(f.pc()) 26 | return file 27 | } 28 | 29 | // line returns the line number of source code of the 30 | // function for this Frame's pc. 31 | func (f Frame) line() int { 32 | fn := runtime.FuncForPC(f.pc()) 33 | if fn == nil { 34 | return 0 35 | } 36 | _, line := fn.FileLine(f.pc()) 37 | return line 38 | } 39 | 40 | // Format formats the frame according to the fmt.Formatter interface. 41 | // 42 | // %s source file 43 | // %d source line 44 | // %n function name 45 | // %v equivalent to %s:%d 46 | // 47 | // Format accepts flags that alter the printing of some verbs, as follows: 48 | // 49 | // %+s function name and path of source file relative to the compile time 50 | // GOPATH separated by \n\t (\n\t) 51 | // %+v equivalent to %+s:%d 52 | func (f Frame) Format(s fmt.State, verb rune) { 53 | switch verb { 54 | case 's': 55 | switch { 56 | case s.Flag('+'): 57 | pc := f.pc() 58 | fn := runtime.FuncForPC(pc) 59 | if fn == nil { 60 | io.WriteString(s, "unknown") 61 | } else { 62 | file, _ := fn.FileLine(pc) 63 | fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) 64 | } 65 | default: 66 | io.WriteString(s, path.Base(f.file())) 67 | } 68 | case 'd': 69 | fmt.Fprintf(s, "%d", f.line()) 70 | case 'n': 71 | name := runtime.FuncForPC(f.pc()).Name() 72 | io.WriteString(s, funcname(name)) 73 | case 'v': 74 | f.Format(s, 's') 75 | io.WriteString(s, ":") 76 | f.Format(s, 'd') 77 | } 78 | } 79 | 80 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). 81 | type StackTrace []Frame 82 | 83 | // Format formats the stack of Frames according to the fmt.Formatter interface. 84 | // 85 | // %s lists source files for each Frame in the stack 86 | // %v lists the source file and line number for each Frame in the stack 87 | // 88 | // Format accepts flags that alter the printing of some verbs, as follows: 89 | // 90 | // %+v Prints filename, function, and line number for each Frame in the stack. 91 | func (st StackTrace) Format(s fmt.State, verb rune) { 92 | switch verb { 93 | case 'v': 94 | switch { 95 | case s.Flag('+'): 96 | for _, f := range st { 97 | fmt.Fprintf(s, "\n%+v", f) 98 | } 99 | case s.Flag('#'): 100 | fmt.Fprintf(s, "%#v", []Frame(st)) 101 | default: 102 | fmt.Fprintf(s, "%v", []Frame(st)) 103 | } 104 | case 's': 105 | fmt.Fprintf(s, "%s", []Frame(st)) 106 | } 107 | } 108 | 109 | // stack represents a stack of program counters. 110 | type stack []uintptr 111 | 112 | func (s *stack) Format(st fmt.State, verb rune) { 113 | switch verb { 114 | case 'v': 115 | switch { 116 | case st.Flag('+'): 117 | for _, pc := range *s { 118 | f := Frame(pc) 119 | fmt.Fprintf(st, "\n%+v", f) 120 | } 121 | } 122 | } 123 | } 124 | 125 | func (s *stack) StackTrace() StackTrace { 126 | f := make([]Frame, len(*s)) 127 | for i := 0; i < len(f); i++ { 128 | f[i] = Frame((*s)[i]) 129 | } 130 | return f 131 | } 132 | 133 | func callers() *stack { 134 | const depth = 32 135 | var pcs [depth]uintptr 136 | n := runtime.Callers(3, pcs[:]) 137 | var st stack = pcs[0:n] 138 | return &st 139 | } 140 | 141 | // funcname removes the path prefix component of a function's name reported by func.Name(). 142 | func funcname(name string) string { 143 | i := strings.LastIndex(name, "/") 144 | name = name[i+1:] 145 | i = strings.Index(name, ".") 146 | return name[i+1:] 147 | } 148 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package ipapk 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "image/png" 7 | "os" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func getAppZipReader(filename string) (*zip.Reader, error) { 13 | file, err := os.Open(filename) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | stat, err := file.Stat() 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | reader, err := zip.NewReader(file, stat.Size()) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return reader, nil 28 | } 29 | 30 | func getAndroidManifest() (*zip.File, error) { 31 | reader, err := getAppZipReader("testdata/helloworld.apk") 32 | if err != nil { 33 | return nil, err 34 | } 35 | var xmlFile *zip.File 36 | for _, f := range reader.File { 37 | if f.Name == "AndroidManifest.xml" { 38 | xmlFile = f 39 | break 40 | } 41 | } 42 | return xmlFile, nil 43 | } 44 | 45 | func TestParseAndroidManifest(t *testing.T) { 46 | xmlFile, err := getAndroidManifest() 47 | if err != nil { 48 | t.Errorf("got %v want no error", err) 49 | } 50 | manifest, err := parseAndroidManifest(xmlFile) 51 | if err != nil { 52 | t.Errorf("got %v want no error", err) 53 | } 54 | if manifest.Package != "com.example.helloworld" { 55 | t.Errorf("got %v want %v", manifest.Package, "com.example.helloworld") 56 | } 57 | if manifest.VersionName != "1.0" { 58 | t.Errorf("got %v want %v", manifest.VersionName, "1.0") 59 | } 60 | if manifest.VersionCode != "1" { 61 | t.Errorf("got %v want %v", manifest.VersionCode, "1") 62 | } 63 | } 64 | 65 | func TestParseApkFile(t *testing.T) { 66 | xmlFile, err := getAndroidManifest() 67 | if err != nil { 68 | t.Errorf("got %v want no error", err) 69 | } 70 | apk, err := parseApkFile(xmlFile) 71 | if err != nil { 72 | t.Errorf("got %v want no error", err) 73 | } 74 | if apk.BundleId != "com.example.helloworld" { 75 | t.Errorf("got %v want %v", apk.BundleId, "com.example.helloworld") 76 | } 77 | if apk.Version != "1.0" { 78 | t.Errorf("got %v want %v", apk.Version, "1.0") 79 | } 80 | if apk.Build != "1" { 81 | t.Errorf("got %v want %v", apk.Build, "1") 82 | } 83 | } 84 | 85 | func TestParseApkIconAndLabel(t *testing.T) { 86 | icon, label, err := parseApkIconAndLabel("testdata/helloworld.apk") 87 | if err != nil { 88 | t.Errorf("got %v want no error", err) 89 | } 90 | buf := new(bytes.Buffer) 91 | if err := png.Encode(buf, icon); err != nil { 92 | t.Errorf("got %v want no error", err) 93 | } 94 | if len(buf.Bytes()) != 10223 { 95 | t.Errorf("got %v want %v", len(buf.Bytes()), 10223) 96 | } 97 | if label != "HelloWorld" { 98 | t.Errorf("got %v want %v", label, "HelloWorld") 99 | } 100 | } 101 | 102 | func getIosPlist() (*zip.File, error) { 103 | reader, err := getAppZipReader("testdata/helloworld.ipa") 104 | if err != nil { 105 | return nil, err 106 | } 107 | var plistFile *zip.File 108 | for _, f := range reader.File { 109 | if reInfoPlist.MatchString(f.Name) { 110 | plistFile = f 111 | break 112 | } 113 | } 114 | return plistFile, nil 115 | } 116 | 117 | func TestParseIpaFile(t *testing.T) { 118 | plistFile, err := getIosPlist() 119 | if err != nil { 120 | t.Errorf("got %v want no error", err) 121 | } 122 | ipa, err := parseIpaFile(plistFile) 123 | if err != nil { 124 | t.Errorf("got %v want no error", err) 125 | } 126 | if ipa.BundleId != "com.kthcorp.helloworld" { 127 | t.Errorf("got %v want %v", ipa.BundleId, "com.kthcorp.helloworld") 128 | } 129 | if ipa.Version != "1.0" { 130 | t.Errorf("got %v want %v", ipa.Version, "1.0") 131 | } 132 | if ipa.Build != "1.0" { 133 | t.Errorf("got %v want %v", ipa.Build, "1.0") 134 | } 135 | } 136 | 137 | func TestParseIpaIcon(t *testing.T) { 138 | reader, err := getAppZipReader("testdata/helloworld.ipa") 139 | if err != nil { 140 | t.Errorf("got %v want no error", err) 141 | } 142 | var iconFile *zip.File 143 | for _, f := range reader.File { 144 | if strings.Contains(f.Name, "AppIcon60x60") { 145 | iconFile = f 146 | break 147 | } 148 | } 149 | if _, err := parseIpaIcon(iconFile); err != ErrNoIcon { 150 | t.Errorf("got %v want %v", err, ErrNoIcon) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/encode.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "reflect" 8 | "runtime" 9 | ) 10 | 11 | type generator interface { 12 | generateDocument(cfValue) 13 | Indent(string) 14 | } 15 | 16 | // An Encoder writes a property list to an output stream. 17 | type Encoder struct { 18 | writer io.Writer 19 | format int 20 | 21 | indent string 22 | } 23 | 24 | // Encode writes the property list encoding of v to the stream. 25 | func (p *Encoder) Encode(v interface{}) (err error) { 26 | defer func() { 27 | if r := recover(); r != nil { 28 | if _, ok := r.(runtime.Error); ok { 29 | panic(r) 30 | } 31 | err = r.(error) 32 | } 33 | }() 34 | 35 | pval := p.marshal(reflect.ValueOf(v)) 36 | if pval == nil { 37 | panic(errors.New("plist: no root element to encode")) 38 | } 39 | 40 | var g generator 41 | switch p.format { 42 | case XMLFormat: 43 | g = newXMLPlistGenerator(p.writer) 44 | case BinaryFormat, AutomaticFormat: 45 | g = newBplistGenerator(p.writer) 46 | case OpenStepFormat, GNUStepFormat: 47 | g = newTextPlistGenerator(p.writer, p.format) 48 | } 49 | g.Indent(p.indent) 50 | g.generateDocument(pval) 51 | return 52 | } 53 | 54 | // Indent turns on pretty-printing for the XML and Text property list formats. 55 | // Each element begins on a new line and is preceded by one or more copies of indent according to its nesting depth. 56 | func (p *Encoder) Indent(indent string) { 57 | p.indent = indent 58 | } 59 | 60 | // NewEncoder returns an Encoder that writes an XML property list to w. 61 | func NewEncoder(w io.Writer) *Encoder { 62 | return NewEncoderForFormat(w, XMLFormat) 63 | } 64 | 65 | // NewEncoderForFormat returns an Encoder that writes a property list to w in the specified format. 66 | // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat). 67 | func NewEncoderForFormat(w io.Writer, format int) *Encoder { 68 | return &Encoder{ 69 | writer: w, 70 | format: format, 71 | } 72 | } 73 | 74 | // NewBinaryEncoder returns an Encoder that writes a binary property list to w. 75 | func NewBinaryEncoder(w io.Writer) *Encoder { 76 | return NewEncoderForFormat(w, BinaryFormat) 77 | } 78 | 79 | // Marshal returns the property list encoding of v in the specified format. 80 | // 81 | // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat). 82 | // 83 | // Marshal traverses the value v recursively. 84 | // Any nil values encountered, other than the root, will be silently discarded as 85 | // the property list format bears no representation for nil values. 86 | // 87 | // Strings, integers of varying size, floats and booleans are encoded unchanged. 88 | // Strings bearing non-ASCII runes will be encoded differently depending upon the property list format: 89 | // UTF-8 for XML property lists and UTF-16 for binary property lists. 90 | // 91 | // Slice and Array values are encoded as property list arrays, except for 92 | // []byte values, which are encoded as data. 93 | // 94 | // Map values encode as dictionaries. The map's key type must be string; there is no provision for encoding non-string dictionary keys. 95 | // 96 | // Struct values are encoded as dictionaries, with only exported fields being serialized. Struct field encoding may be influenced with the use of tags. 97 | // The tag format is: 98 | // 99 | // `plist:"[,flags...]"` 100 | // 101 | // The following flags are supported: 102 | // 103 | // omitempty Only include the field if it is not set to the zero value for its type. 104 | // 105 | // If the key is "-", the field is ignored. 106 | // 107 | // Anonymous struct fields are encoded as if their exported fields were exposed via the outer struct. 108 | // 109 | // Pointer values encode as the value pointed to. 110 | // 111 | // Channel, complex and function values cannot be encoded. Any attempt to do so causes Marshal to return an error. 112 | func Marshal(v interface{}, format int) ([]byte, error) { 113 | return MarshalIndent(v, format, "") 114 | } 115 | 116 | // MarshalIndent works like Marshal, but each property list element 117 | // begins on a new line and is preceded by one or more copies of indent according to its nesting depth. 118 | func MarshalIndent(v interface{}, format int, indent string) ([]byte, error) { 119 | buf := &bytes.Buffer{} 120 | enc := NewEncoderForFormat(buf, format) 121 | enc.Indent(indent) 122 | if err := enc.Encode(v); err != nil { 123 | return nil, err 124 | } 125 | return buf.Bytes(), nil 126 | } 127 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/decode.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "reflect" 7 | "runtime" 8 | ) 9 | 10 | type parser interface { 11 | parseDocument() (cfValue, error) 12 | } 13 | 14 | // A Decoder reads a property list from an input stream. 15 | type Decoder struct { 16 | // the format of the most-recently-decoded property list 17 | Format int 18 | 19 | reader io.ReadSeeker 20 | lax bool 21 | } 22 | 23 | // Decode works like Unmarshal, except it reads the decoder stream to find property list elements. 24 | // 25 | // After Decoding, the Decoder's Format field will be set to one of the plist format constants. 26 | func (p *Decoder) Decode(v interface{}) (err error) { 27 | defer func() { 28 | if r := recover(); r != nil { 29 | if _, ok := r.(runtime.Error); ok { 30 | panic(r) 31 | } 32 | err = r.(error) 33 | } 34 | }() 35 | 36 | header := make([]byte, 6) 37 | p.reader.Read(header) 38 | p.reader.Seek(0, 0) 39 | 40 | var parser parser 41 | var pval cfValue 42 | if bytes.Equal(header, []byte("bplist")) { 43 | parser = newBplistParser(p.reader) 44 | pval, err = parser.parseDocument() 45 | if err != nil { 46 | // Had a bplist header, but still got an error: we have to die here. 47 | return err 48 | } 49 | p.Format = BinaryFormat 50 | } else { 51 | parser = newXMLPlistParser(p.reader) 52 | pval, err = parser.parseDocument() 53 | if _, ok := err.(invalidPlistError); ok { 54 | // Rewind: the XML parser might have exhausted the file. 55 | p.reader.Seek(0, 0) 56 | // We don't use parser here because we want the textPlistParser type 57 | tp := newTextPlistParser(p.reader) 58 | pval, err = tp.parseDocument() 59 | if err != nil { 60 | return err 61 | } 62 | p.Format = tp.format 63 | if p.Format == OpenStepFormat { 64 | // OpenStep property lists can only store strings, 65 | // so we have to turn on lax mode here for the unmarshal step later. 66 | p.lax = true 67 | } 68 | } else { 69 | if err != nil { 70 | return err 71 | } 72 | p.Format = XMLFormat 73 | } 74 | } 75 | 76 | p.unmarshal(pval, reflect.ValueOf(v)) 77 | return 78 | } 79 | 80 | // NewDecoder returns a Decoder that reads property list elements from a stream reader, r. 81 | // NewDecoder requires a Seekable stream for the purposes of file type detection. 82 | func NewDecoder(r io.ReadSeeker) *Decoder { 83 | return &Decoder{Format: InvalidFormat, reader: r, lax: false} 84 | } 85 | 86 | // Unmarshal parses a property list document and stores the result in the value pointed to by v. 87 | // 88 | // Unmarshal uses the inverse of the type encodings that Marshal uses, allocating heap-borne types as necessary. 89 | // 90 | // When given a nil pointer, Unmarshal allocates a new value for it to point to. 91 | // 92 | // To decode property list values into an interface value, Unmarshal decodes the property list into the concrete value contained 93 | // in the interface value. If the interface value is nil, Unmarshal stores one of the following in the interface value: 94 | // 95 | // string, bool, uint64, float64 96 | // plist.UID for "CoreFoundation Keyed Archiver UIDs" (convertible to uint64) 97 | // []byte, for plist data 98 | // []interface{}, for plist arrays 99 | // map[string]interface{}, for plist dictionaries 100 | // 101 | // If a property list value is not appropriate for a given value type, Unmarshal aborts immediately and returns an error. 102 | // 103 | // As Go does not support 128-bit types, and we don't want to pretend we're giving the user integer types (as opposed to 104 | // secretly passing them structs), Unmarshal will drop the high 64 bits of any 128-bit integers encoded in binary property lists. 105 | // (This is important because CoreFoundation serializes some large 64-bit values as 128-bit values with an empty high half.) 106 | // 107 | // When Unmarshal encounters an OpenStep property list, it will enter a relaxed parsing mode: OpenStep property lists can only store 108 | // plain old data as strings, so we will attempt to recover integer, floating-point, boolean and date values wherever they are necessary. 109 | // (for example, if Unmarshal attempts to unmarshal an OpenStep property list into a time.Time, it will try to parse the string it 110 | // receives as a time.) 111 | // 112 | // Unmarshal returns the detected property list format and an error, if any. 113 | func Unmarshal(data []byte, v interface{}) (format int, err error) { 114 | r := bytes.NewReader(data) 115 | dec := NewDecoder(r) 116 | err = dec.Decode(v) 117 | format = dec.Format 118 | return 119 | } 120 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/xml_generator.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "encoding/xml" 7 | "io" 8 | "math" 9 | "strconv" 10 | "time" 11 | ) 12 | 13 | const ( 14 | xmlHEADER string = `` + "\n" 15 | xmlDOCTYPE = `` + "\n" 16 | xmlArrayTag = "array" 17 | xmlDataTag = "data" 18 | xmlDateTag = "date" 19 | xmlDictTag = "dict" 20 | xmlFalseTag = "false" 21 | xmlIntegerTag = "integer" 22 | xmlKeyTag = "key" 23 | xmlPlistTag = "plist" 24 | xmlRealTag = "real" 25 | xmlStringTag = "string" 26 | xmlTrueTag = "true" 27 | 28 | // magic value used in the XML encoding of UIDs 29 | // (stored as a dictionary mapping CF$UID->integer) 30 | xmlCFUIDMagic = "CF$UID" 31 | ) 32 | 33 | func formatXMLFloat(f float64) string { 34 | switch { 35 | case math.IsInf(f, 1): 36 | return "inf" 37 | case math.IsInf(f, -1): 38 | return "-inf" 39 | case math.IsNaN(f): 40 | return "nan" 41 | } 42 | return strconv.FormatFloat(f, 'g', -1, 64) 43 | } 44 | 45 | type xmlPlistGenerator struct { 46 | *bufio.Writer 47 | 48 | indent string 49 | depth int 50 | putNewline bool 51 | } 52 | 53 | func (p *xmlPlistGenerator) generateDocument(root cfValue) { 54 | p.WriteString(xmlHEADER) 55 | p.WriteString(xmlDOCTYPE) 56 | 57 | p.openTag(`plist version="1.0"`) 58 | p.writePlistValue(root) 59 | p.closeTag(xmlPlistTag) 60 | p.Flush() 61 | } 62 | 63 | func (p *xmlPlistGenerator) openTag(n string) { 64 | p.writeIndent(1) 65 | p.WriteByte('<') 66 | p.WriteString(n) 67 | p.WriteByte('>') 68 | } 69 | 70 | func (p *xmlPlistGenerator) closeTag(n string) { 71 | p.writeIndent(-1) 72 | p.WriteString("') 75 | } 76 | 77 | func (p *xmlPlistGenerator) element(n string, v string) { 78 | p.writeIndent(0) 79 | if len(v) == 0 { 80 | p.WriteByte('<') 81 | p.WriteString(n) 82 | p.WriteString("/>") 83 | } else { 84 | p.WriteByte('<') 85 | p.WriteString(n) 86 | p.WriteByte('>') 87 | 88 | err := xml.EscapeText(p.Writer, []byte(v)) 89 | if err != nil { 90 | panic(err) 91 | } 92 | 93 | p.WriteString("') 96 | } 97 | } 98 | 99 | func (p *xmlPlistGenerator) writeDictionary(dict *cfDictionary) { 100 | dict.sort() 101 | p.openTag(xmlDictTag) 102 | for i, k := range dict.keys { 103 | p.element(xmlKeyTag, k) 104 | p.writePlistValue(dict.values[i]) 105 | } 106 | p.closeTag(xmlDictTag) 107 | } 108 | 109 | func (p *xmlPlistGenerator) writeArray(a *cfArray) { 110 | p.openTag(xmlArrayTag) 111 | for _, v := range a.values { 112 | p.writePlistValue(v) 113 | } 114 | p.closeTag(xmlArrayTag) 115 | } 116 | 117 | func (p *xmlPlistGenerator) writePlistValue(pval cfValue) { 118 | if pval == nil { 119 | return 120 | } 121 | 122 | switch pval := pval.(type) { 123 | case cfString: 124 | p.element(xmlStringTag, string(pval)) 125 | case *cfNumber: 126 | if pval.signed { 127 | p.element(xmlIntegerTag, strconv.FormatInt(int64(pval.value), 10)) 128 | } else { 129 | p.element(xmlIntegerTag, strconv.FormatUint(pval.value, 10)) 130 | } 131 | case *cfReal: 132 | p.element(xmlRealTag, formatXMLFloat(pval.value)) 133 | case cfBoolean: 134 | if bool(pval) { 135 | p.element(xmlTrueTag, "") 136 | } else { 137 | p.element(xmlFalseTag, "") 138 | } 139 | case cfData: 140 | p.element(xmlDataTag, base64.StdEncoding.EncodeToString([]byte(pval))) 141 | case cfDate: 142 | p.element(xmlDateTag, time.Time(pval).In(time.UTC).Format(time.RFC3339)) 143 | case *cfDictionary: 144 | p.writeDictionary(pval) 145 | case *cfArray: 146 | p.writeArray(pval) 147 | case cfUID: 148 | p.openTag(xmlDictTag) 149 | p.element(xmlKeyTag, xmlCFUIDMagic) 150 | p.element(xmlIntegerTag, strconv.FormatUint(uint64(pval), 10)) 151 | p.closeTag(xmlDictTag) 152 | } 153 | } 154 | 155 | func (p *xmlPlistGenerator) writeIndent(delta int) { 156 | if len(p.indent) == 0 { 157 | return 158 | } 159 | 160 | if delta < 0 { 161 | p.depth-- 162 | } 163 | 164 | if p.putNewline { 165 | // from encoding/xml/marshal.go; it seems to be intended 166 | // to suppress the first newline. 167 | p.WriteByte('\n') 168 | } else { 169 | p.putNewline = true 170 | } 171 | for i := 0; i < p.depth; i++ { 172 | p.WriteString(p.indent) 173 | } 174 | if delta > 0 { 175 | p.depth++ 176 | } 177 | } 178 | 179 | func (p *xmlPlistGenerator) Indent(i string) { 180 | p.indent = i 181 | } 182 | 183 | func newXMLPlistGenerator(w io.Writer) *xmlPlistGenerator { 184 | return &xmlPlistGenerator{Writer: bufio.NewWriter(w)} 185 | } 186 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/apk/apk.go: -------------------------------------------------------------------------------- 1 | package apk 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "encoding/xml" 7 | "fmt" 8 | "image" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "strconv" 13 | 14 | "github.com/pkg/errors" 15 | "github.com/shogo82148/androidbinary" 16 | ) 17 | 18 | // Apk is an application package file for android. 19 | type Apk struct { 20 | f *os.File 21 | zipreader *zip.Reader 22 | manifest Manifest 23 | table *androidbinary.TableFile 24 | } 25 | 26 | // OpenFile will open the file specified by filename and return Apk 27 | func OpenFile(filename string) (apk *Apk, err error) { 28 | f, err := os.Open(filename) 29 | if err != nil { 30 | return nil, err 31 | } 32 | defer func() { 33 | if err != nil { 34 | f.Close() 35 | } 36 | }() 37 | fi, err := f.Stat() 38 | if err != nil { 39 | return nil, err 40 | } 41 | apk, err = OpenZipReader(f, fi.Size()) 42 | if err != nil { 43 | return nil, err 44 | } 45 | apk.f = f 46 | return 47 | } 48 | 49 | // OpenZipReader has same arguments like zip.NewReader 50 | func OpenZipReader(r io.ReaderAt, size int64) (*Apk, error) { 51 | zipreader, err := zip.NewReader(r, size) 52 | if err != nil { 53 | return nil, err 54 | } 55 | apk := &Apk{ 56 | zipreader: zipreader, 57 | } 58 | if err = apk.parseManifest(); err != nil { 59 | return nil, errors.Wrap(err, "parse-manifest") 60 | } 61 | if err = apk.parseResources(); err != nil { 62 | return nil, err 63 | } 64 | return apk, nil 65 | } 66 | 67 | // Close is avaliable only if apk is created with OpenFile 68 | func (k *Apk) Close() error { 69 | if k.f == nil { 70 | return nil 71 | } 72 | return k.f.Close() 73 | } 74 | 75 | // Icon returns the icon image of the APK. 76 | func (k *Apk) Icon(resConfig *androidbinary.ResTableConfig) (image.Image, error) { 77 | iconPath := k.getResource(k.manifest.App.Icon, resConfig) 78 | if androidbinary.IsResID(iconPath) { 79 | return nil, errors.New("unable to convert icon-id to icon path") 80 | } 81 | imgData, err := k.readZipFile(iconPath) 82 | if err != nil { 83 | return nil, err 84 | } 85 | m, _, err := image.Decode(bytes.NewReader(imgData)) 86 | return m, err 87 | } 88 | 89 | // Label returns the label of the APK. 90 | func (k *Apk) Label(resConfig *androidbinary.ResTableConfig) (s string, err error) { 91 | s = k.getResource(k.manifest.App.Label, resConfig) 92 | if androidbinary.IsResID(s) { 93 | err = errors.New("unable to convert label-id to string") 94 | } 95 | return 96 | } 97 | 98 | // Manifest returns the manifest of the APK. 99 | func (k *Apk) Manifest() Manifest { 100 | return k.manifest 101 | } 102 | 103 | // PackageName returns the package name of the APK. 104 | func (k *Apk) PackageName() string { 105 | return k.manifest.Package 106 | } 107 | 108 | // MainAcitivty returns the name of the main activity. 109 | func (k *Apk) MainAcitivty() (activity string, err error) { 110 | for _, act := range k.manifest.App.Activity { 111 | for _, intent := range act.IntentFilter { 112 | if intent.Action.Name == "android.intent.action.MAIN" && 113 | intent.Category.Name == "android.intent.category.LAUNCHER" { 114 | return act.Name, nil 115 | } 116 | } 117 | } 118 | return "", errors.New("No main activity found") 119 | } 120 | 121 | func (k *Apk) parseManifest() error { 122 | xmlData, err := k.readZipFile("AndroidManifest.xml") 123 | if err != nil { 124 | return errors.Wrap(err, "read-manifest.xml") 125 | } 126 | xmlfile, err := androidbinary.NewXMLFile(bytes.NewReader(xmlData)) 127 | if err != nil { 128 | return errors.Wrap(err, "parse-xml") 129 | } 130 | reader := xmlfile.Reader() 131 | data, err := ioutil.ReadAll(reader) 132 | if err != nil { 133 | return err 134 | } 135 | return xml.Unmarshal(data, &k.manifest) 136 | } 137 | 138 | func (k *Apk) parseResources() (err error) { 139 | resData, err := k.readZipFile("resources.arsc") 140 | if err != nil { 141 | return 142 | } 143 | k.table, err = androidbinary.NewTableFile(bytes.NewReader(resData)) 144 | return 145 | } 146 | 147 | func (k *Apk) getResource(id string, resConfig *androidbinary.ResTableConfig) string { 148 | resID, err := androidbinary.ParseResID(id) 149 | if err != nil { 150 | return id 151 | } 152 | val, err := k.table.GetResource(resID, resConfig) 153 | if err != nil { 154 | return id 155 | } 156 | return fmt.Sprintf("%s", val) 157 | } 158 | 159 | func (k *Apk) readZipFile(name string) (data []byte, err error) { 160 | buf := bytes.NewBuffer(nil) 161 | for _, file := range k.zipreader.File { 162 | if file.Name != name { 163 | continue 164 | } 165 | rc, er := file.Open() 166 | if er != nil { 167 | err = er 168 | return 169 | } 170 | defer rc.Close() 171 | _, err = io.Copy(buf, rc) 172 | if err != nil { 173 | return 174 | } 175 | return buf.Bytes(), nil 176 | } 177 | return nil, fmt.Errorf("File %s not found", strconv.Quote(name)) 178 | } 179 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/typeinfo.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "sync" 7 | ) 8 | 9 | // typeInfo holds details for the plist representation of a type. 10 | type typeInfo struct { 11 | fields []fieldInfo 12 | } 13 | 14 | // fieldInfo holds details for the plist representation of a single field. 15 | type fieldInfo struct { 16 | idx []int 17 | name string 18 | omitEmpty bool 19 | } 20 | 21 | var tinfoMap = make(map[reflect.Type]*typeInfo) 22 | var tinfoLock sync.RWMutex 23 | 24 | // getTypeInfo returns the typeInfo structure with details necessary 25 | // for marshalling and unmarshalling typ. 26 | func getTypeInfo(typ reflect.Type) (*typeInfo, error) { 27 | tinfoLock.RLock() 28 | tinfo, ok := tinfoMap[typ] 29 | tinfoLock.RUnlock() 30 | if ok { 31 | return tinfo, nil 32 | } 33 | tinfo = &typeInfo{} 34 | if typ.Kind() == reflect.Struct { 35 | n := typ.NumField() 36 | for i := 0; i < n; i++ { 37 | f := typ.Field(i) 38 | if f.PkgPath != "" || f.Tag.Get("plist") == "-" { 39 | continue // Private field 40 | } 41 | 42 | // For embedded structs, embed its fields. 43 | if f.Anonymous { 44 | t := f.Type 45 | if t.Kind() == reflect.Ptr { 46 | t = t.Elem() 47 | } 48 | if t.Kind() == reflect.Struct { 49 | inner, err := getTypeInfo(t) 50 | if err != nil { 51 | return nil, err 52 | } 53 | for _, finfo := range inner.fields { 54 | finfo.idx = append([]int{i}, finfo.idx...) 55 | if err := addFieldInfo(typ, tinfo, &finfo); err != nil { 56 | return nil, err 57 | } 58 | } 59 | continue 60 | } 61 | } 62 | 63 | finfo, err := structFieldInfo(typ, &f) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | // Add the field if it doesn't conflict with other fields. 69 | if err := addFieldInfo(typ, tinfo, finfo); err != nil { 70 | return nil, err 71 | } 72 | } 73 | } 74 | tinfoLock.Lock() 75 | tinfoMap[typ] = tinfo 76 | tinfoLock.Unlock() 77 | return tinfo, nil 78 | } 79 | 80 | // structFieldInfo builds and returns a fieldInfo for f. 81 | func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) { 82 | finfo := &fieldInfo{idx: f.Index} 83 | 84 | // Split the tag from the xml namespace if necessary. 85 | tag := f.Tag.Get("plist") 86 | 87 | // Parse flags. 88 | tokens := strings.Split(tag, ",") 89 | tag = tokens[0] 90 | if len(tokens) > 1 { 91 | tag = tokens[0] 92 | for _, flag := range tokens[1:] { 93 | switch flag { 94 | case "omitempty": 95 | finfo.omitEmpty = true 96 | } 97 | } 98 | } 99 | 100 | if tag == "" { 101 | // If the name part of the tag is completely empty, 102 | // use the field name 103 | finfo.name = f.Name 104 | return finfo, nil 105 | } 106 | 107 | finfo.name = tag 108 | return finfo, nil 109 | } 110 | 111 | // addFieldInfo adds finfo to tinfo.fields if there are no 112 | // conflicts, or if conflicts arise from previous fields that were 113 | // obtained from deeper embedded structures than finfo. In the latter 114 | // case, the conflicting entries are dropped. 115 | // A conflict occurs when the path (parent + name) to a field is 116 | // itself a prefix of another path, or when two paths match exactly. 117 | // It is okay for field paths to share a common, shorter prefix. 118 | func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error { 119 | var conflicts []int 120 | // First, figure all conflicts. Most working code will have none. 121 | for i := range tinfo.fields { 122 | oldf := &tinfo.fields[i] 123 | if newf.name == oldf.name { 124 | conflicts = append(conflicts, i) 125 | } 126 | } 127 | 128 | // Without conflicts, add the new field and return. 129 | if conflicts == nil { 130 | tinfo.fields = append(tinfo.fields, *newf) 131 | return nil 132 | } 133 | 134 | // If any conflict is shallower, ignore the new field. 135 | // This matches the Go field resolution on embedding. 136 | for _, i := range conflicts { 137 | if len(tinfo.fields[i].idx) < len(newf.idx) { 138 | return nil 139 | } 140 | } 141 | 142 | // Otherwise, the new field is shallower, and thus takes precedence, 143 | // so drop the conflicting fields from tinfo and append the new one. 144 | for c := len(conflicts) - 1; c >= 0; c-- { 145 | i := conflicts[c] 146 | copy(tinfo.fields[i:], tinfo.fields[i+1:]) 147 | tinfo.fields = tinfo.fields[:len(tinfo.fields)-1] 148 | } 149 | tinfo.fields = append(tinfo.fields, *newf) 150 | return nil 151 | } 152 | 153 | // value returns v's field value corresponding to finfo. 154 | // It's equivalent to v.FieldByIndex(finfo.idx), but initializes 155 | // and dereferences pointers as necessary. 156 | func (finfo *fieldInfo) value(v reflect.Value) reflect.Value { 157 | for i, x := range finfo.idx { 158 | if i > 0 { 159 | t := v.Type() 160 | if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { 161 | if v.IsNil() { 162 | v.Set(reflect.New(v.Type().Elem())) 163 | } 164 | v = v.Elem() 165 | } 166 | } 167 | v = v.Field(x) 168 | } 169 | return v 170 | } 171 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package ipapk 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "encoding/xml" 7 | "errors" 8 | "image" 9 | "image/png" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strings" 15 | 16 | "github.com/DHowett/go-plist" 17 | "github.com/andrianbdn/iospng" 18 | "github.com/shogo82148/androidbinary" 19 | "github.com/shogo82148/androidbinary/apk" 20 | ) 21 | 22 | var ( 23 | reInfoPlist = regexp.MustCompile(`Payload/[^/]+/Info\.plist`) 24 | ErrNoIcon = errors.New("icon not found") 25 | ) 26 | 27 | const ( 28 | iosExt = ".ipa" 29 | androidExt = ".apk" 30 | ) 31 | 32 | type AppInfo struct { 33 | Name string 34 | BundleId string 35 | Version string 36 | Build string 37 | Icon image.Image 38 | Size int64 39 | } 40 | 41 | type androidManifest struct { 42 | Package string `xml:"package,attr"` 43 | VersionName string `xml:"versionName,attr"` 44 | VersionCode string `xml:"versionCode,attr"` 45 | } 46 | 47 | type iosPlist struct { 48 | CFBundleName string `plist:"CFBundleName"` 49 | CFBundleDisplayName string `plist:"CFBundleDisplayName"` 50 | CFBundleVersion string `plist:"CFBundleVersion"` 51 | CFBundleShortVersion string `plist:"CFBundleShortVersionString"` 52 | CFBundleIdentifier string `plist:"CFBundleIdentifier"` 53 | } 54 | 55 | func NewAppParser(name string) (*AppInfo, error) { 56 | file, err := os.Open(name) 57 | if err != nil { 58 | return nil, err 59 | } 60 | defer file.Close() 61 | 62 | stat, err := file.Stat() 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | reader, err := zip.NewReader(file, stat.Size()) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | var xmlFile, plistFile, iosIconFile *zip.File 73 | for _, f := range reader.File { 74 | switch { 75 | case f.Name == "AndroidManifest.xml": 76 | xmlFile = f 77 | case reInfoPlist.MatchString(f.Name): 78 | plistFile = f 79 | case strings.Contains(f.Name, "AppIcon60x60"): 80 | iosIconFile = f 81 | } 82 | } 83 | 84 | ext := filepath.Ext(stat.Name()) 85 | 86 | if ext == androidExt { 87 | info, err := parseApkFile(xmlFile) 88 | icon, label, err := parseApkIconAndLabel(name) 89 | info.Name = label 90 | info.Icon = icon 91 | info.Size = stat.Size() 92 | return info, err 93 | } 94 | 95 | if ext == iosExt { 96 | info, err := parseIpaFile(plistFile) 97 | icon, err := parseIpaIcon(iosIconFile) 98 | info.Icon = icon 99 | info.Size = stat.Size() 100 | return info, err 101 | } 102 | 103 | return nil, errors.New("unknown platform") 104 | } 105 | 106 | func parseAndroidManifest(xmlFile *zip.File) (*androidManifest, error) { 107 | rc, err := xmlFile.Open() 108 | if err != nil { 109 | return nil, err 110 | } 111 | defer rc.Close() 112 | 113 | buf, err := ioutil.ReadAll(rc) 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | xmlContent, err := androidbinary.NewXMLFile(bytes.NewReader(buf)) 119 | if err != nil { 120 | return nil, err 121 | } 122 | 123 | manifest := new(androidManifest) 124 | decoder := xml.NewDecoder(xmlContent.Reader()) 125 | if err := decoder.Decode(manifest); err != nil { 126 | return nil, err 127 | } 128 | return manifest, nil 129 | } 130 | 131 | func parseApkFile(xmlFile *zip.File) (*AppInfo, error) { 132 | if xmlFile == nil { 133 | return nil, errors.New("AndroidManifest.xml not found") 134 | } 135 | 136 | manifest, err := parseAndroidManifest(xmlFile) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | info := new(AppInfo) 142 | info.BundleId = manifest.Package 143 | info.Version = manifest.VersionName 144 | info.Build = manifest.VersionCode 145 | 146 | return info, nil 147 | } 148 | 149 | func parseApkIconAndLabel(name string) (image.Image, string, error) { 150 | pkg, err := apk.OpenFile(name) 151 | if err != nil { 152 | return nil, "", err 153 | } 154 | defer pkg.Close() 155 | 156 | icon, _ := pkg.Icon(&androidbinary.ResTableConfig{ 157 | Density: 720, 158 | }) 159 | if icon == nil { 160 | return nil, "", ErrNoIcon 161 | } 162 | 163 | label, _ := pkg.Label(nil) 164 | 165 | return icon, label, nil 166 | } 167 | 168 | func parseIpaFile(plistFile *zip.File) (*AppInfo, error) { 169 | if plistFile == nil { 170 | return nil, errors.New("info.plist not found") 171 | } 172 | 173 | rc, err := plistFile.Open() 174 | if err != nil { 175 | return nil, err 176 | } 177 | defer rc.Close() 178 | 179 | buf, err := ioutil.ReadAll(rc) 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | p := new(iosPlist) 185 | decoder := plist.NewDecoder(bytes.NewReader(buf)) 186 | if err := decoder.Decode(p); err != nil { 187 | return nil, err 188 | } 189 | 190 | info := new(AppInfo) 191 | if p.CFBundleDisplayName == "" { 192 | info.Name = p.CFBundleName 193 | } else { 194 | info.Name = p.CFBundleDisplayName 195 | } 196 | info.BundleId = p.CFBundleIdentifier 197 | info.Version = p.CFBundleShortVersion 198 | info.Build = p.CFBundleVersion 199 | 200 | return info, nil 201 | } 202 | 203 | func parseIpaIcon(iconFile *zip.File) (image.Image, error) { 204 | if iconFile == nil { 205 | return nil, ErrNoIcon 206 | } 207 | 208 | rc, err := iconFile.Open() 209 | if err != nil { 210 | return nil, err 211 | } 212 | defer rc.Close() 213 | 214 | var w bytes.Buffer 215 | iospng.PngRevertOptimization(rc, &w) 216 | 217 | return png.Decode(bytes.NewReader(w.Bytes())) 218 | } 219 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/xml_parser.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/xml" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "runtime" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | type xmlPlistParser struct { 15 | reader io.Reader 16 | xmlDecoder *xml.Decoder 17 | whitespaceReplacer *strings.Replacer 18 | ntags int 19 | } 20 | 21 | func (p *xmlPlistParser) parseDocument() (pval cfValue, parseError error) { 22 | defer func() { 23 | if r := recover(); r != nil { 24 | if _, ok := r.(runtime.Error); ok { 25 | panic(r) 26 | } 27 | if _, ok := r.(invalidPlistError); ok { 28 | parseError = r.(error) 29 | } else { 30 | // Wrap all non-invalid-plist errors. 31 | parseError = plistParseError{"XML", r.(error)} 32 | } 33 | } 34 | }() 35 | for { 36 | if token, err := p.xmlDecoder.Token(); err == nil { 37 | if element, ok := token.(xml.StartElement); ok { 38 | pval = p.parseXMLElement(element) 39 | if p.ntags == 0 { 40 | panic(invalidPlistError{"XML", errors.New("no elements encountered")}) 41 | } 42 | return 43 | } 44 | } else { 45 | // The first XML parse turned out to be invalid: 46 | // we do not have an XML property list. 47 | panic(invalidPlistError{"XML", err}) 48 | } 49 | } 50 | } 51 | 52 | func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) cfValue { 53 | var charData xml.CharData 54 | switch element.Name.Local { 55 | case "plist": 56 | p.ntags++ 57 | for { 58 | token, err := p.xmlDecoder.Token() 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | if el, ok := token.(xml.EndElement); ok && el.Name.Local == "plist" { 64 | break 65 | } 66 | 67 | if el, ok := token.(xml.StartElement); ok { 68 | return p.parseXMLElement(el) 69 | } 70 | } 71 | return nil 72 | case "string": 73 | p.ntags++ 74 | err := p.xmlDecoder.DecodeElement(&charData, &element) 75 | if err != nil { 76 | panic(err) 77 | } 78 | 79 | return cfString(charData) 80 | case "integer": 81 | p.ntags++ 82 | err := p.xmlDecoder.DecodeElement(&charData, &element) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | s := string(charData) 88 | if len(s) == 0 { 89 | panic(errors.New("invalid empty ")) 90 | } 91 | 92 | if s[0] == '-' { 93 | s, base := unsignedGetBase(s[1:]) 94 | n := mustParseInt("-"+s, base, 64) 95 | return &cfNumber{signed: true, value: uint64(n)} 96 | } else { 97 | s, base := unsignedGetBase(s) 98 | n := mustParseUint(s, base, 64) 99 | return &cfNumber{signed: false, value: n} 100 | } 101 | case "real": 102 | p.ntags++ 103 | err := p.xmlDecoder.DecodeElement(&charData, &element) 104 | if err != nil { 105 | panic(err) 106 | } 107 | 108 | n := mustParseFloat(string(charData), 64) 109 | return &cfReal{wide: true, value: n} 110 | case "true", "false": 111 | p.ntags++ 112 | p.xmlDecoder.Skip() 113 | 114 | b := element.Name.Local == "true" 115 | return cfBoolean(b) 116 | case "date": 117 | p.ntags++ 118 | err := p.xmlDecoder.DecodeElement(&charData, &element) 119 | if err != nil { 120 | panic(err) 121 | } 122 | 123 | t, err := time.ParseInLocation(time.RFC3339, string(charData), time.UTC) 124 | if err != nil { 125 | panic(err) 126 | } 127 | 128 | return cfDate(t) 129 | case "data": 130 | p.ntags++ 131 | err := p.xmlDecoder.DecodeElement(&charData, &element) 132 | if err != nil { 133 | panic(err) 134 | } 135 | 136 | str := p.whitespaceReplacer.Replace(string(charData)) 137 | 138 | l := base64.StdEncoding.DecodedLen(len(str)) 139 | bytes := make([]uint8, l) 140 | l, err = base64.StdEncoding.Decode(bytes, []byte(str)) 141 | if err != nil { 142 | panic(err) 143 | } 144 | 145 | return cfData(bytes[:l]) 146 | case "dict": 147 | p.ntags++ 148 | var key *string 149 | keys := make([]string, 0, 32) 150 | values := make([]cfValue, 0, 32) 151 | for { 152 | token, err := p.xmlDecoder.Token() 153 | if err != nil { 154 | panic(err) 155 | } 156 | 157 | if el, ok := token.(xml.EndElement); ok && el.Name.Local == "dict" { 158 | if key != nil { 159 | panic(errors.New("missing value in dictionary")) 160 | } 161 | break 162 | } 163 | 164 | if el, ok := token.(xml.StartElement); ok { 165 | if el.Name.Local == "key" { 166 | var k string 167 | p.xmlDecoder.DecodeElement(&k, &el) 168 | key = &k 169 | } else { 170 | if key == nil { 171 | panic(errors.New("missing key in dictionary")) 172 | } 173 | keys = append(keys, *key) 174 | values = append(values, p.parseXMLElement(el)) 175 | key = nil 176 | } 177 | } 178 | } 179 | 180 | if len(keys) == 1 && keys[0] == "CF$UID" && len(values) == 1 { 181 | if integer, ok := values[0].(*cfNumber); ok { 182 | return cfUID(integer.value) 183 | } 184 | } 185 | 186 | return &cfDictionary{keys: keys, values: values} 187 | case "array": 188 | p.ntags++ 189 | values := make([]cfValue, 0, 10) 190 | for { 191 | token, err := p.xmlDecoder.Token() 192 | if err != nil { 193 | panic(err) 194 | } 195 | 196 | if el, ok := token.(xml.EndElement); ok && el.Name.Local == "array" { 197 | break 198 | } 199 | 200 | if el, ok := token.(xml.StartElement); ok { 201 | values = append(values, p.parseXMLElement(el)) 202 | } 203 | } 204 | return &cfArray{values} 205 | } 206 | err := fmt.Errorf("encountered unknown element %s", element.Name.Local) 207 | if p.ntags == 0 { 208 | // If out first XML tag is invalid, it might be an openstep data element, ala or <0101> 209 | panic(invalidPlistError{"XML", err}) 210 | } 211 | panic(err) 212 | } 213 | 214 | func newXMLPlistParser(r io.Reader) *xmlPlistParser { 215 | return &xmlPlistParser{r, xml.NewDecoder(r), strings.NewReplacer("\t", "", "\n", "", " ", "", "\r", ""), 0} 216 | } 217 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/text_generator.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding/hex" 5 | "io" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | type textPlistGenerator struct { 11 | writer io.Writer 12 | format int 13 | 14 | quotableTable *characterSet 15 | 16 | indent string 17 | depth int 18 | 19 | dictKvDelimiter, dictEntryDelimiter, arrayDelimiter []byte 20 | } 21 | 22 | var ( 23 | textPlistTimeLayout = "2006-01-02 15:04:05 -0700" 24 | padding = "0000" 25 | ) 26 | 27 | func (p *textPlistGenerator) generateDocument(pval cfValue) { 28 | p.writePlistValue(pval) 29 | } 30 | 31 | func (p *textPlistGenerator) plistQuotedString(str string) string { 32 | if str == "" { 33 | return `""` 34 | } 35 | s := "" 36 | quot := false 37 | for _, r := range str { 38 | if r > 0xFF { 39 | quot = true 40 | s += `\U` 41 | us := strconv.FormatInt(int64(r), 16) 42 | s += padding[len(us):] 43 | s += us 44 | } else if r > 0x7F { 45 | quot = true 46 | s += `\` 47 | us := strconv.FormatInt(int64(r), 8) 48 | s += padding[1+len(us):] 49 | s += us 50 | } else { 51 | c := uint8(r) 52 | if p.quotableTable.ContainsByte(c) { 53 | quot = true 54 | } 55 | 56 | switch c { 57 | case '\a': 58 | s += `\a` 59 | case '\b': 60 | s += `\b` 61 | case '\v': 62 | s += `\v` 63 | case '\f': 64 | s += `\f` 65 | case '\\': 66 | s += `\\` 67 | case '"': 68 | s += `\"` 69 | case '\t', '\r', '\n': 70 | fallthrough 71 | default: 72 | s += string(c) 73 | } 74 | } 75 | } 76 | if quot { 77 | s = `"` + s + `"` 78 | } 79 | return s 80 | } 81 | 82 | func (p *textPlistGenerator) deltaIndent(depthDelta int) { 83 | if depthDelta < 0 { 84 | p.depth-- 85 | } else if depthDelta > 0 { 86 | p.depth++ 87 | } 88 | } 89 | 90 | func (p *textPlistGenerator) writeIndent() { 91 | if len(p.indent) == 0 { 92 | return 93 | } 94 | if len(p.indent) > 0 { 95 | p.writer.Write([]byte("\n")) 96 | for i := 0; i < p.depth; i++ { 97 | io.WriteString(p.writer, p.indent) 98 | } 99 | } 100 | } 101 | 102 | func (p *textPlistGenerator) writePlistValue(pval cfValue) { 103 | if pval == nil { 104 | return 105 | } 106 | 107 | switch pval := pval.(type) { 108 | case *cfDictionary: 109 | pval.sort() 110 | p.writer.Write([]byte(`{`)) 111 | p.deltaIndent(1) 112 | for i, k := range pval.keys { 113 | p.writeIndent() 114 | io.WriteString(p.writer, p.plistQuotedString(k)) 115 | p.writer.Write(p.dictKvDelimiter) 116 | p.writePlistValue(pval.values[i]) 117 | p.writer.Write(p.dictEntryDelimiter) 118 | } 119 | p.deltaIndent(-1) 120 | p.writeIndent() 121 | p.writer.Write([]byte(`}`)) 122 | case *cfArray: 123 | p.writer.Write([]byte(`(`)) 124 | p.deltaIndent(1) 125 | for _, v := range pval.values { 126 | p.writeIndent() 127 | p.writePlistValue(v) 128 | p.writer.Write(p.arrayDelimiter) 129 | } 130 | p.deltaIndent(-1) 131 | p.writeIndent() 132 | p.writer.Write([]byte(`)`)) 133 | case cfString: 134 | io.WriteString(p.writer, p.plistQuotedString(string(pval))) 135 | case *cfNumber: 136 | if p.format == GNUStepFormat { 137 | p.writer.Write([]byte(`<*I`)) 138 | } 139 | if pval.signed { 140 | io.WriteString(p.writer, strconv.FormatInt(int64(pval.value), 10)) 141 | } else { 142 | io.WriteString(p.writer, strconv.FormatUint(pval.value, 10)) 143 | } 144 | if p.format == GNUStepFormat { 145 | p.writer.Write([]byte(`>`)) 146 | } 147 | case *cfReal: 148 | if p.format == GNUStepFormat { 149 | p.writer.Write([]byte(`<*R`)) 150 | } 151 | // GNUstep does not differentiate between 32/64-bit floats. 152 | io.WriteString(p.writer, strconv.FormatFloat(pval.value, 'g', -1, 64)) 153 | if p.format == GNUStepFormat { 154 | p.writer.Write([]byte(`>`)) 155 | } 156 | case cfBoolean: 157 | if p.format == GNUStepFormat { 158 | if pval { 159 | p.writer.Write([]byte(`<*BY>`)) 160 | } else { 161 | p.writer.Write([]byte(`<*BN>`)) 162 | } 163 | } else { 164 | if pval { 165 | p.writer.Write([]byte(`1`)) 166 | } else { 167 | p.writer.Write([]byte(`0`)) 168 | } 169 | } 170 | case cfData: 171 | var hexencoded [9]byte 172 | var l int 173 | var asc = 9 174 | hexencoded[8] = ' ' 175 | 176 | p.writer.Write([]byte(`<`)) 177 | b := []byte(pval) 178 | for i := 0; i < len(b); i += 4 { 179 | l = i + 4 180 | if l >= len(b) { 181 | l = len(b) 182 | // We no longer need the space - or the rest of the buffer. 183 | // (we used >= above to get this part without another conditional :P) 184 | asc = (l - i) * 2 185 | } 186 | // Fill the buffer (only up to 8 characters, to preserve the space we implicitly include 187 | // at the end of every encode) 188 | hex.Encode(hexencoded[:8], b[i:l]) 189 | io.WriteString(p.writer, string(hexencoded[:asc])) 190 | } 191 | p.writer.Write([]byte(`>`)) 192 | case cfDate: 193 | if p.format == GNUStepFormat { 194 | p.writer.Write([]byte(`<*D`)) 195 | io.WriteString(p.writer, time.Time(pval).In(time.UTC).Format(textPlistTimeLayout)) 196 | p.writer.Write([]byte(`>`)) 197 | } else { 198 | io.WriteString(p.writer, p.plistQuotedString(time.Time(pval).In(time.UTC).Format(textPlistTimeLayout))) 199 | } 200 | } 201 | } 202 | 203 | func (p *textPlistGenerator) Indent(i string) { 204 | p.indent = i 205 | if i == "" { 206 | p.dictKvDelimiter = []byte(`=`) 207 | } else { 208 | // For pretty-printing 209 | p.dictKvDelimiter = []byte(` = `) 210 | } 211 | } 212 | 213 | func newTextPlistGenerator(w io.Writer, format int) *textPlistGenerator { 214 | table := &osQuotable 215 | if format == GNUStepFormat { 216 | table = &gsQuotable 217 | } 218 | return &textPlistGenerator{ 219 | writer: mustWriter{w}, 220 | format: format, 221 | quotableTable: table, 222 | dictKvDelimiter: []byte(`=`), 223 | arrayDelimiter: []byte(`,`), 224 | dictEntryDelimiter: []byte(`;`), 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/marshal.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func isEmptyValue(v reflect.Value) bool { 10 | switch v.Kind() { 11 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 12 | return v.Len() == 0 13 | case reflect.Bool: 14 | return !v.Bool() 15 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 16 | return v.Int() == 0 17 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 18 | return v.Uint() == 0 19 | case reflect.Float32, reflect.Float64: 20 | return v.Float() == 0 21 | case reflect.Interface, reflect.Ptr: 22 | return v.IsNil() 23 | } 24 | return false 25 | } 26 | 27 | var ( 28 | plistMarshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() 29 | textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() 30 | timeType = reflect.TypeOf((*time.Time)(nil)).Elem() 31 | ) 32 | 33 | func implementsInterface(val reflect.Value, interfaceType reflect.Type) (interface{}, bool) { 34 | if val.CanInterface() && val.Type().Implements(interfaceType) { 35 | return val.Interface(), true 36 | } 37 | 38 | if val.CanAddr() { 39 | pv := val.Addr() 40 | if pv.CanInterface() && pv.Type().Implements(interfaceType) { 41 | return pv.Interface(), true 42 | } 43 | } 44 | return nil, false 45 | } 46 | 47 | func (p *Encoder) marshalPlistInterface(marshalable Marshaler) cfValue { 48 | value, err := marshalable.MarshalPlist() 49 | if err != nil { 50 | panic(err) 51 | } 52 | return p.marshal(reflect.ValueOf(value)) 53 | } 54 | 55 | // marshalTextInterface marshals a TextMarshaler to a plist string. 56 | func (p *Encoder) marshalTextInterface(marshalable encoding.TextMarshaler) cfValue { 57 | s, err := marshalable.MarshalText() 58 | if err != nil { 59 | panic(err) 60 | } 61 | return cfString(s) 62 | } 63 | 64 | // marshalStruct marshals a reflected struct value to a plist dictionary 65 | func (p *Encoder) marshalStruct(typ reflect.Type, val reflect.Value) cfValue { 66 | tinfo, _ := getTypeInfo(typ) 67 | 68 | dict := &cfDictionary{ 69 | keys: make([]string, 0, len(tinfo.fields)), 70 | values: make([]cfValue, 0, len(tinfo.fields)), 71 | } 72 | for _, finfo := range tinfo.fields { 73 | value := finfo.value(val) 74 | if !value.IsValid() || finfo.omitEmpty && isEmptyValue(value) { 75 | continue 76 | } 77 | dict.keys = append(dict.keys, finfo.name) 78 | dict.values = append(dict.values, p.marshal(value)) 79 | } 80 | 81 | return dict 82 | } 83 | 84 | func (p *Encoder) marshalTime(val reflect.Value) cfValue { 85 | time := val.Interface().(time.Time) 86 | return cfDate(time) 87 | } 88 | 89 | func (p *Encoder) marshal(val reflect.Value) cfValue { 90 | if !val.IsValid() { 91 | return nil 92 | } 93 | 94 | if receiver, can := implementsInterface(val, plistMarshalerType); can { 95 | return p.marshalPlistInterface(receiver.(Marshaler)) 96 | } 97 | 98 | // time.Time implements TextMarshaler, but we need to store it in RFC3339 99 | if val.Type() == timeType { 100 | return p.marshalTime(val) 101 | } 102 | if val.Kind() == reflect.Ptr || (val.Kind() == reflect.Interface && val.NumMethod() == 0) { 103 | ival := val.Elem() 104 | if ival.IsValid() && ival.Type() == timeType { 105 | return p.marshalTime(ival) 106 | } 107 | } 108 | 109 | // Check for text marshaler. 110 | if receiver, can := implementsInterface(val, textMarshalerType); can { 111 | return p.marshalTextInterface(receiver.(encoding.TextMarshaler)) 112 | } 113 | 114 | // Descend into pointers or interfaces 115 | if val.Kind() == reflect.Ptr || (val.Kind() == reflect.Interface && val.NumMethod() == 0) { 116 | val = val.Elem() 117 | } 118 | 119 | // We got this far and still may have an invalid anything or nil ptr/interface 120 | if !val.IsValid() || ((val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface) && val.IsNil()) { 121 | return nil 122 | } 123 | 124 | typ := val.Type() 125 | 126 | if typ == uidType { 127 | return cfUID(val.Uint()) 128 | } 129 | 130 | if val.Kind() == reflect.Struct { 131 | return p.marshalStruct(typ, val) 132 | } 133 | 134 | switch val.Kind() { 135 | case reflect.String: 136 | return cfString(val.String()) 137 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 138 | return &cfNumber{signed: true, value: uint64(val.Int())} 139 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 140 | return &cfNumber{signed: false, value: val.Uint()} 141 | case reflect.Float32: 142 | return &cfReal{wide: false, value: val.Float()} 143 | case reflect.Float64: 144 | return &cfReal{wide: true, value: val.Float()} 145 | case reflect.Bool: 146 | return cfBoolean(val.Bool()) 147 | case reflect.Slice, reflect.Array: 148 | if typ.Elem().Kind() == reflect.Uint8 { 149 | bytes := []byte(nil) 150 | if val.CanAddr() { 151 | bytes = val.Bytes() 152 | } else { 153 | bytes = make([]byte, val.Len()) 154 | reflect.Copy(reflect.ValueOf(bytes), val) 155 | } 156 | return cfData(bytes) 157 | } else { 158 | values := make([]cfValue, val.Len()) 159 | for i, length := 0, val.Len(); i < length; i++ { 160 | if subpval := p.marshal(val.Index(i)); subpval != nil { 161 | values[i] = subpval 162 | } 163 | } 164 | return &cfArray{values} 165 | } 166 | case reflect.Map: 167 | if typ.Key().Kind() != reflect.String { 168 | panic(&unknownTypeError{typ}) 169 | } 170 | 171 | l := val.Len() 172 | dict := &cfDictionary{ 173 | keys: make([]string, 0, l), 174 | values: make([]cfValue, 0, l), 175 | } 176 | for _, keyv := range val.MapKeys() { 177 | if subpval := p.marshal(val.MapIndex(keyv)); subpval != nil { 178 | dict.keys = append(dict.keys, keyv.String()) 179 | dict.values = append(dict.values, subpval) 180 | } 181 | } 182 | return dict 183 | default: 184 | panic(&unknownTypeError{typ}) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /vendor/github.com/andrianbdn/iospng/iospng.go: -------------------------------------------------------------------------------- 1 | package iospng 2 | 3 | import ( 4 | "bytes" 5 | "compress/zlib" 6 | "encoding/binary" 7 | "errors" 8 | "hash/crc32" 9 | "io" 10 | "io/ioutil" 11 | ) 12 | 13 | var ( 14 | ErrPngHeader = errors.New("Not a Png") 15 | ErrImageData = errors.New("Unexpected amount of image data") 16 | ) 17 | 18 | type pngChunk struct { 19 | chunkLength, chunkCRC uint32 20 | chunkType, chunkData []byte 21 | } 22 | 23 | func decodePngData(data []byte) ([]byte, error) { 24 | var zbuf bytes.Buffer 25 | zbuf.Write([]byte{0x78, 0x1}) // looks like a good zlib header 26 | zbuf.Write(data) 27 | zbuf.Write([]byte{0, 0, 0, 0}) // don't know CRC, will get zlib.ErrChecksum 28 | 29 | reader, err := zlib.NewReader(&zbuf) 30 | if err != nil { 31 | return nil, err 32 | } 33 | defer reader.Close() 34 | 35 | dat, err := ioutil.ReadAll(reader) 36 | 37 | if err != zlib.ErrChecksum { 38 | return nil, err 39 | } 40 | return dat, nil 41 | } 42 | 43 | func (p *pngChunk) write(writer io.Writer, needCrc bool) error { 44 | if needCrc { 45 | crc := crc32.NewIEEE() 46 | crc.Write(p.chunkType) 47 | crc.Write(p.chunkData) 48 | p.chunkCRC = crc.Sum32() 49 | } 50 | 51 | chunkLength := uint32(len(p.chunkData)) 52 | err := binary.Write(writer, binary.BigEndian, &chunkLength) 53 | if err != nil { 54 | return err 55 | } 56 | _, err = writer.Write(p.chunkType) 57 | if err != nil { 58 | return err 59 | } 60 | _, err = writer.Write(p.chunkData) 61 | if err != nil { 62 | return err 63 | } 64 | err = binary.Write(writer, binary.BigEndian, &p.chunkCRC) 65 | if err != nil { 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | func (p *pngChunk) read(reader io.Reader) error { 72 | 73 | if err := binary.Read(reader, binary.BigEndian, &p.chunkLength); err != nil { 74 | return err 75 | } 76 | 77 | p.chunkType = make([]byte, 4) 78 | 79 | if _, err := io.ReadFull(reader, p.chunkType); err != nil { 80 | return err 81 | } 82 | 83 | p.chunkData = make([]byte, p.chunkLength) 84 | 85 | if _, err := io.ReadFull(reader, p.chunkData); err != nil { 86 | return err 87 | } 88 | 89 | if err := binary.Read(reader, binary.BigEndian, &p.chunkCRC); err != nil { 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | 96 | func (p *pngChunk) is(kind string) bool { 97 | return string(p.chunkType) == kind 98 | } 99 | 100 | func unsafeImageFix(w, h int, raw []byte) { 101 | for y := 0; y < h; y++ { 102 | for x := 0; x < w; x++ { 103 | // we expect this PNG data 104 | // to be 4 bytes per pixel 105 | // 1st byte in each row is filter 106 | row := y*w*4 + y 107 | col := x*4 + 1 108 | 109 | b := raw[row+col+0] 110 | g := raw[row+col+1] 111 | r := raw[row+col+2] 112 | a := raw[row+col+3] 113 | 114 | // de-multiplying 115 | r = uint8(float64(r) * 255 / float64(a)) 116 | g = uint8(float64(g) * 255 / float64(a)) 117 | b = uint8(float64(b) * 255 / float64(a)) 118 | 119 | raw[row+col+0] = r 120 | raw[row+col+1] = g 121 | raw[row+col+2] = b 122 | 123 | } 124 | } 125 | } 126 | 127 | type interlacedAdam7 struct { 128 | xF, yF, xO, yO int 129 | } 130 | 131 | var adam7factoroffset = []interlacedAdam7{ 132 | {8, 8, 0, 0}, 133 | {8, 8, 4, 0}, 134 | {4, 8, 0, 4}, 135 | {4, 4, 2, 0}, 136 | {2, 4, 0, 2}, 137 | {2, 2, 1, 0}, 138 | {1, 2, 0, 1}, 139 | } 140 | 141 | func rawImageFix(w, h int, interlaced bool, raw []byte) error { 142 | if interlaced { 143 | 144 | total := 0 145 | for pass := 0; pass < 7; pass++ { 146 | p := adam7factoroffset[pass] 147 | 148 | wp := (w - p.xO + p.xF - 1) / p.xF 149 | hp := (h - p.yO + p.yF - 1) / p.yF 150 | psize := wp*hp*4 + hp 151 | 152 | if total+psize > len(raw) { 153 | return ErrImageData 154 | } 155 | 156 | unsafeImageFix(wp, hp, raw[total:]) 157 | total = total + psize 158 | } 159 | 160 | return nil 161 | 162 | } else { 163 | if len(raw) != w*h*4+h { 164 | return ErrImageData 165 | } 166 | 167 | unsafeImageFix(w, h, raw) 168 | return nil 169 | } 170 | } 171 | 172 | // This function actually does everything: 173 | // reads PNG from reader and in case it is iOS-optimized, 174 | // reverts optimization. 175 | // 176 | // Function does not change data if PNG does not have CgBI chunk. 177 | func PngRevertOptimization(reader io.Reader, writer io.Writer) error { 178 | header := make([]byte, 8) 179 | if _, err := io.ReadFull(reader, header); err != nil { 180 | return errors.New("Read error" + err.Error()) 181 | } 182 | 183 | if bytes.Compare([]byte("\x89PNG\r\n\x1a\n"), header) != 0 { 184 | return ErrPngHeader 185 | } 186 | 187 | writer.Write(header) 188 | 189 | var interlaced bool 190 | var w, h int 191 | var datbuf bytes.Buffer 192 | optimized := false 193 | 194 | for { 195 | var chunk pngChunk 196 | if err := chunk.read(reader); err != nil { 197 | return err 198 | } 199 | 200 | switch { 201 | 202 | case chunk.is("IHDR"): 203 | w = int(binary.BigEndian.Uint32(chunk.chunkData[:4])) 204 | h = int(binary.BigEndian.Uint32(chunk.chunkData[4:8])) 205 | interlaced = chunk.chunkData[12] == 1 206 | 207 | case chunk.is("CgBI"): 208 | optimized = true 209 | continue 210 | 211 | case chunk.is("IDAT"): 212 | if optimized { 213 | datbuf.Write(chunk.chunkData) 214 | continue 215 | } 216 | 217 | case chunk.is("IEND"): 218 | if optimized { 219 | raw, err := decodePngData(datbuf.Bytes()) 220 | if err != nil { 221 | return err 222 | } 223 | 224 | if err = rawImageFix(w, h, interlaced, raw); err != nil { 225 | return err 226 | } 227 | 228 | var zdatbuf bytes.Buffer 229 | zwrite := zlib.NewWriter(&zdatbuf) 230 | zwrite.Write(raw) 231 | zwrite.Close() 232 | 233 | chunk.chunkType = []byte("IDAT") 234 | chunk.chunkData = zdatbuf.Bytes() 235 | err = chunk.write(writer, true) 236 | 237 | chunk.chunkType = []byte("IEND") 238 | chunk.chunkData = []byte{} 239 | err = chunk.write(writer, true) 240 | 241 | return nil 242 | } else { 243 | return chunk.write(writer, false) 244 | } 245 | 246 | } 247 | 248 | if err := chunk.write(writer, false); err != nil { 249 | return err 250 | } 251 | 252 | } 253 | 254 | return nil 255 | } 256 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/common.go: -------------------------------------------------------------------------------- 1 | package androidbinary 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "unicode/utf16" 8 | ) 9 | 10 | // ChunkType is a type of a resource chunk. 11 | type ChunkType uint16 12 | 13 | // Chunk types. 14 | const ( 15 | ResNullChunkType ChunkType = 0x0000 16 | ResStringPoolChunkType ChunkType = 0x0001 17 | ResTableChunkType ChunkType = 0x0002 18 | ResXMLChunkType ChunkType = 0x0003 19 | 20 | // Chunk types in RES_XML_TYPE 21 | ResXMLFirstChunkType ChunkType = 0x0100 22 | ResXMLStartNamespaceType ChunkType = 0x0100 23 | ResXMLEndNamespaceType ChunkType = 0x0101 24 | ResXMLStartElementType ChunkType = 0x0102 25 | ResXMLEndElementType ChunkType = 0x0103 26 | ResXMLCDataType ChunkType = 0x0104 27 | ResXMLLastChunkType ChunkType = 0x017f 28 | 29 | // This contains a uint32_t array mapping strings in the string 30 | // pool back to resource identifiers. It is optional. 31 | ResXMLResourceMapType ChunkType = 0x0180 32 | 33 | // Chunk types in RES_TABLE_TYPE 34 | ResTablePackageType ChunkType = 0x0200 35 | ResTableTypeType ChunkType = 0x0201 36 | ResTableTypeSpecType ChunkType = 0x0202 37 | ) 38 | 39 | // ResChunkHeader is a header of a resource chunk. 40 | type ResChunkHeader struct { 41 | Type ChunkType 42 | HeaderSize uint16 43 | Size uint32 44 | } 45 | 46 | // Flags are flags for string pool header. 47 | type Flags uint32 48 | 49 | // the values of Flags. 50 | const ( 51 | SortedFlag Flags = 1 << 0 52 | UTF8Flag Flags = 1 << 8 53 | ) 54 | 55 | // ResStringPoolHeader is a chunk header of string pool. 56 | type ResStringPoolHeader struct { 57 | Header ResChunkHeader 58 | StringCount uint32 59 | StyleCount uint32 60 | Flags Flags 61 | StringStart uint32 62 | StylesStart uint32 63 | } 64 | 65 | // ResStringPoolSpan is a span of style information associated with 66 | // a string in the pool. 67 | type ResStringPoolSpan struct { 68 | FirstChar, LastChar uint32 69 | } 70 | 71 | // ResStringPool is a string pool resrouce. 72 | type ResStringPool struct { 73 | Header ResStringPoolHeader 74 | Strings []string 75 | Styles []ResStringPoolSpan 76 | } 77 | 78 | // NilResStringPoolRef is nil reference for string pool. 79 | const NilResStringPoolRef = ResStringPoolRef(0xFFFFFFFF) 80 | 81 | // ResStringPoolRef is a type representing a reference to a string. 82 | type ResStringPoolRef uint32 83 | 84 | // DataType is a type of the data value. 85 | type DataType uint8 86 | 87 | // The constants for DataType 88 | const ( 89 | TypeNull DataType = 0x00 90 | TypeReference DataType = 0x01 91 | TypeAttribute DataType = 0x02 92 | TypeString DataType = 0x03 93 | TypeFloat DataType = 0x04 94 | TypeDemention DataType = 0x05 95 | TypeFraction DataType = 0x06 96 | TypeFirstInt DataType = 0x10 97 | TypeIntDec DataType = 0x10 98 | TypeIntHex DataType = 0x11 99 | TypeIntBoolean DataType = 0x12 100 | TypeFirstColorInt DataType = 0x1c 101 | TypeIntColorARGB8 DataType = 0x1c 102 | TypeIntColorRGB8 DataType = 0x1d 103 | TypeIntColorARGB4 DataType = 0x1e 104 | TypeIntColorRGB4 DataType = 0x1f 105 | TypeLastColorInt DataType = 0x1f 106 | TypeLastInt DataType = 0x1f 107 | ) 108 | 109 | // ResValue is a representation of a value in a resource 110 | type ResValue struct { 111 | Size uint16 112 | Res0 uint8 113 | DataType DataType 114 | Data uint32 115 | } 116 | 117 | // GetString returns a string referenced by ref. 118 | func (pool *ResStringPool) GetString(ref ResStringPoolRef) string { 119 | return pool.Strings[int(ref)] 120 | } 121 | 122 | func readStringPool(sr *io.SectionReader) (*ResStringPool, error) { 123 | sp := new(ResStringPool) 124 | if err := binary.Read(sr, binary.LittleEndian, &sp.Header); err != nil { 125 | return nil, err 126 | } 127 | 128 | stringStarts := make([]uint32, sp.Header.StringCount) 129 | if err := binary.Read(sr, binary.LittleEndian, stringStarts); err != nil { 130 | return nil, err 131 | } 132 | 133 | styleStarts := make([]uint32, sp.Header.StyleCount) 134 | if err := binary.Read(sr, binary.LittleEndian, styleStarts); err != nil { 135 | return nil, err 136 | } 137 | 138 | sp.Strings = make([]string, sp.Header.StringCount) 139 | for i, start := range stringStarts { 140 | var str string 141 | var err error 142 | if _, err := sr.Seek(int64(sp.Header.StringStart+start), seekStart); err != nil { 143 | return nil, err 144 | } 145 | if (sp.Header.Flags & UTF8Flag) == 0 { 146 | str, err = readUTF16(sr) 147 | } else { 148 | str, err = readUTF8(sr) 149 | } 150 | if err != nil { 151 | return nil, err 152 | } 153 | sp.Strings[i] = str 154 | } 155 | 156 | sp.Styles = make([]ResStringPoolSpan, sp.Header.StyleCount) 157 | for i, start := range styleStarts { 158 | if _, err := sr.Seek(int64(sp.Header.StylesStart+start), seekStart); err != nil { 159 | return nil, err 160 | } 161 | if err := binary.Read(sr, binary.LittleEndian, &sp.Styles[i]); err != nil { 162 | return nil, err 163 | } 164 | } 165 | 166 | return sp, nil 167 | } 168 | 169 | func readUTF16(sr *io.SectionReader) (string, error) { 170 | // read lenth of string 171 | size, err := readUTF16length(sr) 172 | if err != nil { 173 | return "", err 174 | } 175 | 176 | // read string value 177 | buf := make([]uint16, size) 178 | if err := binary.Read(sr, binary.LittleEndian, buf); err != nil { 179 | return "", err 180 | } 181 | return string(utf16.Decode(buf)), nil 182 | } 183 | 184 | func readUTF16length(sr *io.SectionReader) (int, error) { 185 | var size int 186 | var first, second uint16 187 | if err := binary.Read(sr, binary.LittleEndian, &first); err != nil { 188 | return 0, err 189 | } 190 | if (first & 0x8000) != 0 { 191 | if err := binary.Read(sr, binary.LittleEndian, &second); err != nil { 192 | return 0, err 193 | } 194 | size = (int(first&0x7FFF) << 16) + int(second) 195 | } else { 196 | size = int(first) 197 | } 198 | return size, nil 199 | } 200 | 201 | func readUTF8(sr *io.SectionReader) (string, error) { 202 | // skip utf16 length 203 | _, err := readUTF8length(sr) 204 | if err != nil { 205 | return "", err 206 | } 207 | 208 | // read lenth of string 209 | size, err := readUTF8length(sr) 210 | if err != nil { 211 | return "", err 212 | } 213 | 214 | buf := make([]uint8, size) 215 | if err := binary.Read(sr, binary.LittleEndian, buf); err != nil { 216 | return "", err 217 | } 218 | return string(buf), nil 219 | } 220 | 221 | func readUTF8length(sr *io.SectionReader) (int, error) { 222 | var size int 223 | var first, second uint8 224 | if err := binary.Read(sr, binary.LittleEndian, &first); err != nil { 225 | return 0, err 226 | } 227 | if (first & 0x80) != 0 { 228 | if err := binary.Read(sr, binary.LittleEndian, &second); err != nil { 229 | return 0, err 230 | } 231 | size = (int(first&0x7F) << 8) + int(second) 232 | } else { 233 | size = int(first) 234 | } 235 | return size, nil 236 | } 237 | 238 | func newZeroFilledReader(r io.Reader, actual int64, expected int64) (io.Reader, error) { 239 | if actual >= expected { 240 | // no need to fill 241 | return r, nil 242 | } 243 | 244 | // read `actual' bytes from r, and 245 | buf := new(bytes.Buffer) 246 | if _, err := io.CopyN(buf, r, actual); err != nil { 247 | return nil, err 248 | } 249 | 250 | // fill zero until `expected' bytes 251 | for i := actual; i < expected; i++ { 252 | if err := buf.WriteByte(0x00); err != nil { 253 | return nil, err 254 | } 255 | } 256 | 257 | return buf, nil 258 | } 259 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/xml.go: -------------------------------------------------------------------------------- 1 | package androidbinary 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/xml" 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | // XMLFile is an XML file expressed in binary format. 12 | type XMLFile struct { 13 | stringPool *ResStringPool 14 | resourceMap []uint32 15 | notPrecessedNS map[ResStringPoolRef]ResStringPoolRef 16 | namespaces map[ResStringPoolRef]ResStringPoolRef 17 | xmlBuffer bytes.Buffer 18 | } 19 | 20 | // ResXMLTreeNode is basic XML tree node. 21 | type ResXMLTreeNode struct { 22 | Header ResChunkHeader 23 | LineNumber uint32 24 | Comment ResStringPoolRef 25 | } 26 | 27 | // ResXMLTreeNamespaceExt is extended XML tree node for namespace start/end nodes. 28 | type ResXMLTreeNamespaceExt struct { 29 | Prefix ResStringPoolRef 30 | URI ResStringPoolRef 31 | } 32 | 33 | // ResXMLTreeAttrExt is extended XML tree node for start tags -- includes attribute. 34 | type ResXMLTreeAttrExt struct { 35 | NS ResStringPoolRef 36 | Name ResStringPoolRef 37 | AttributeStart uint16 38 | AttributeSize uint16 39 | AttributeCount uint16 40 | IDIndex uint16 41 | ClassIndex uint16 42 | StyleIndex uint16 43 | } 44 | 45 | // ResXMLTreeAttribute is an attribute of start tags. 46 | type ResXMLTreeAttribute struct { 47 | NS ResStringPoolRef 48 | Name ResStringPoolRef 49 | RawValue ResStringPoolRef 50 | TypedValue ResValue 51 | } 52 | 53 | // ResXMLTreeEndElementExt is extended XML tree node for element start/end nodes. 54 | type ResXMLTreeEndElementExt struct { 55 | NS ResStringPoolRef 56 | Name ResStringPoolRef 57 | } 58 | 59 | // NewXMLFile returns a new XMLFile. 60 | func NewXMLFile(r io.ReaderAt) (*XMLFile, error) { 61 | f := new(XMLFile) 62 | sr := io.NewSectionReader(r, 0, 1<<63-1) 63 | 64 | fmt.Fprintf(&f.xmlBuffer, xml.Header) 65 | 66 | header := new(ResChunkHeader) 67 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 68 | return nil, err 69 | } 70 | offset := int64(header.HeaderSize) 71 | for offset < int64(header.Size) { 72 | chunkHeader, err := f.readChunk(r, offset) 73 | if err != nil { 74 | return nil, err 75 | } 76 | offset += int64(chunkHeader.Size) 77 | } 78 | return f, nil 79 | } 80 | 81 | // Reader returns a reader of XML file expressed in text format. 82 | func (f *XMLFile) Reader() *bytes.Reader { 83 | return bytes.NewReader(f.xmlBuffer.Bytes()) 84 | } 85 | 86 | func (f *XMLFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) { 87 | sr := io.NewSectionReader(r, offset, 1<<63-1-offset) 88 | chunkHeader := &ResChunkHeader{} 89 | if _, err := sr.Seek(0, seekStart); err != nil { 90 | return nil, err 91 | } 92 | if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { 93 | return nil, err 94 | } 95 | 96 | var err error 97 | if _, err := sr.Seek(0, seekStart); err != nil { 98 | return nil, err 99 | } 100 | switch chunkHeader.Type { 101 | case ResStringPoolChunkType: 102 | f.stringPool, err = readStringPool(sr) 103 | case ResXMLStartNamespaceType: 104 | err = f.readStartNamespace(sr) 105 | case ResXMLEndNamespaceType: 106 | err = f.readEndNamespace(sr) 107 | case ResXMLStartElementType: 108 | err = f.readStartElement(sr) 109 | case ResXMLEndElementType: 110 | err = f.readEndElement(sr) 111 | } 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | return chunkHeader, nil 117 | } 118 | 119 | // GetString returns a string referenced by ref. 120 | func (f *XMLFile) GetString(ref ResStringPoolRef) string { 121 | return f.stringPool.GetString(ref) 122 | } 123 | 124 | func (f *XMLFile) readStartNamespace(sr *io.SectionReader) error { 125 | header := new(ResXMLTreeNode) 126 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 127 | return err 128 | } 129 | 130 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 131 | return err 132 | } 133 | namespace := new(ResXMLTreeNamespaceExt) 134 | if err := binary.Read(sr, binary.LittleEndian, namespace); err != nil { 135 | return err 136 | } 137 | 138 | if f.notPrecessedNS == nil { 139 | f.notPrecessedNS = make(map[ResStringPoolRef]ResStringPoolRef) 140 | } 141 | f.notPrecessedNS[namespace.URI] = namespace.Prefix 142 | 143 | if f.namespaces == nil { 144 | f.namespaces = make(map[ResStringPoolRef]ResStringPoolRef) 145 | } 146 | f.namespaces[namespace.URI] = namespace.Prefix 147 | 148 | return nil 149 | } 150 | 151 | func (f *XMLFile) readEndNamespace(sr *io.SectionReader) error { 152 | header := new(ResXMLTreeNode) 153 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 154 | return err 155 | } 156 | 157 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 158 | return err 159 | } 160 | namespace := new(ResXMLTreeNamespaceExt) 161 | if err := binary.Read(sr, binary.LittleEndian, namespace); err != nil { 162 | return err 163 | } 164 | delete(f.namespaces, namespace.URI) 165 | return nil 166 | } 167 | 168 | func (f *XMLFile) addNamespacePrefix(ns, name ResStringPoolRef) string { 169 | if ns != NilResStringPoolRef { 170 | prefix := f.GetString(f.namespaces[ns]) 171 | return fmt.Sprintf("%s:%s", prefix, f.GetString(name)) 172 | } 173 | return f.GetString(name) 174 | } 175 | 176 | func (f *XMLFile) readStartElement(sr *io.SectionReader) error { 177 | header := new(ResXMLTreeNode) 178 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 179 | return err 180 | } 181 | 182 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 183 | return err 184 | } 185 | ext := new(ResXMLTreeAttrExt) 186 | if err := binary.Read(sr, binary.LittleEndian, ext); err != nil { 187 | return nil 188 | } 189 | 190 | fmt.Fprintf(&f.xmlBuffer, "<%s", f.addNamespacePrefix(ext.NS, ext.Name)) 191 | 192 | // output XML namespaces 193 | if f.notPrecessedNS != nil { 194 | for uri, prefix := range f.notPrecessedNS { 195 | fmt.Fprintf(&f.xmlBuffer, " xmlns:%s=\"", f.GetString(prefix)) 196 | xml.Escape(&f.xmlBuffer, []byte(f.GetString(uri))) 197 | fmt.Fprint(&f.xmlBuffer, "\"") 198 | } 199 | f.notPrecessedNS = nil 200 | } 201 | 202 | // process attributes 203 | offset := int64(ext.AttributeStart + header.Header.HeaderSize) 204 | for i := 0; i < int(ext.AttributeCount); i++ { 205 | if _, err := sr.Seek(offset, seekStart); err != nil { 206 | return err 207 | } 208 | attr := new(ResXMLTreeAttribute) 209 | binary.Read(sr, binary.LittleEndian, attr) 210 | 211 | var value string 212 | if attr.RawValue != NilResStringPoolRef { 213 | value = f.GetString(attr.RawValue) 214 | } else { 215 | data := attr.TypedValue.Data 216 | switch attr.TypedValue.DataType { 217 | case TypeNull: 218 | value = "" 219 | case TypeReference: 220 | value = fmt.Sprintf("@0x%08X", data) 221 | case TypeIntDec: 222 | value = fmt.Sprintf("%d", data) 223 | case TypeIntHex: 224 | value = fmt.Sprintf("0x%08X", data) 225 | case TypeIntBoolean: 226 | if data != 0 { 227 | value = "true" 228 | } else { 229 | value = "false" 230 | } 231 | default: 232 | value = fmt.Sprintf("@0x%08X", data) 233 | } 234 | } 235 | 236 | fmt.Fprintf(&f.xmlBuffer, " %s=\"", f.addNamespacePrefix(attr.NS, attr.Name)) 237 | xml.Escape(&f.xmlBuffer, []byte(value)) 238 | fmt.Fprint(&f.xmlBuffer, "\"") 239 | offset += int64(ext.AttributeSize) 240 | } 241 | fmt.Fprint(&f.xmlBuffer, ">") 242 | return nil 243 | } 244 | 245 | func (f *XMLFile) readEndElement(sr *io.SectionReader) error { 246 | header := new(ResXMLTreeNode) 247 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 248 | return err 249 | } 250 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 251 | return err 252 | } 253 | ext := new(ResXMLTreeEndElementExt) 254 | if err := binary.Read(sr, binary.LittleEndian, ext); err != nil { 255 | return err 256 | } 257 | fmt.Fprintf(&f.xmlBuffer, "", f.addNamespacePrefix(ext.NS, ext.Name)) 258 | return nil 259 | } 260 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors provides simple error handling primitives. 2 | // 3 | // The traditional error handling idiom in Go is roughly akin to 4 | // 5 | // if err != nil { 6 | // return err 7 | // } 8 | // 9 | // which applied recursively up the call stack results in error reports 10 | // without context or debugging information. The errors package allows 11 | // programmers to add context to the failure path in their code in a way 12 | // that does not destroy the original value of the error. 13 | // 14 | // Adding context to an error 15 | // 16 | // The errors.Wrap function returns a new error that adds context to the 17 | // original error by recording a stack trace at the point Wrap is called, 18 | // and the supplied message. For example 19 | // 20 | // _, err := ioutil.ReadAll(r) 21 | // if err != nil { 22 | // return errors.Wrap(err, "read failed") 23 | // } 24 | // 25 | // If additional control is required the errors.WithStack and errors.WithMessage 26 | // functions destructure errors.Wrap into its component operations of annotating 27 | // an error with a stack trace and an a message, respectively. 28 | // 29 | // Retrieving the cause of an error 30 | // 31 | // Using errors.Wrap constructs a stack of errors, adding context to the 32 | // preceding error. Depending on the nature of the error it may be necessary 33 | // to reverse the operation of errors.Wrap to retrieve the original error 34 | // for inspection. Any error value which implements this interface 35 | // 36 | // type causer interface { 37 | // Cause() error 38 | // } 39 | // 40 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve 41 | // the topmost error which does not implement causer, which is assumed to be 42 | // the original cause. For example: 43 | // 44 | // switch err := errors.Cause(err).(type) { 45 | // case *MyError: 46 | // // handle specifically 47 | // default: 48 | // // unknown error 49 | // } 50 | // 51 | // causer interface is not exported by this package, but is considered a part 52 | // of stable public API. 53 | // 54 | // Formatted printing of errors 55 | // 56 | // All error values returned from this package implement fmt.Formatter and can 57 | // be formatted by the fmt package. The following verbs are supported 58 | // 59 | // %s print the error. If the error has a Cause it will be 60 | // printed recursively 61 | // %v see %s 62 | // %+v extended format. Each Frame of the error's StackTrace will 63 | // be printed in detail. 64 | // 65 | // Retrieving the stack trace of an error or wrapper 66 | // 67 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are 68 | // invoked. This information can be retrieved with the following interface. 69 | // 70 | // type stackTracer interface { 71 | // StackTrace() errors.StackTrace 72 | // } 73 | // 74 | // Where errors.StackTrace is defined as 75 | // 76 | // type StackTrace []Frame 77 | // 78 | // The Frame type represents a call site in the stack trace. Frame supports 79 | // the fmt.Formatter interface that can be used for printing information about 80 | // the stack trace of this error. For example: 81 | // 82 | // if err, ok := err.(stackTracer); ok { 83 | // for _, f := range err.StackTrace() { 84 | // fmt.Printf("%+s:%d", f) 85 | // } 86 | // } 87 | // 88 | // stackTracer interface is not exported by this package, but is considered a part 89 | // of stable public API. 90 | // 91 | // See the documentation for Frame.Format for more details. 92 | package errors 93 | 94 | import ( 95 | "fmt" 96 | "io" 97 | ) 98 | 99 | // New returns an error with the supplied message. 100 | // New also records the stack trace at the point it was called. 101 | func New(message string) error { 102 | return &fundamental{ 103 | msg: message, 104 | stack: callers(), 105 | } 106 | } 107 | 108 | // Errorf formats according to a format specifier and returns the string 109 | // as a value that satisfies error. 110 | // Errorf also records the stack trace at the point it was called. 111 | func Errorf(format string, args ...interface{}) error { 112 | return &fundamental{ 113 | msg: fmt.Sprintf(format, args...), 114 | stack: callers(), 115 | } 116 | } 117 | 118 | // fundamental is an error that has a message and a stack, but no caller. 119 | type fundamental struct { 120 | msg string 121 | *stack 122 | } 123 | 124 | func (f *fundamental) Error() string { return f.msg } 125 | 126 | func (f *fundamental) Format(s fmt.State, verb rune) { 127 | switch verb { 128 | case 'v': 129 | if s.Flag('+') { 130 | io.WriteString(s, f.msg) 131 | f.stack.Format(s, verb) 132 | return 133 | } 134 | fallthrough 135 | case 's': 136 | io.WriteString(s, f.msg) 137 | case 'q': 138 | fmt.Fprintf(s, "%q", f.msg) 139 | } 140 | } 141 | 142 | // WithStack annotates err with a stack trace at the point WithStack was called. 143 | // If err is nil, WithStack returns nil. 144 | func WithStack(err error) error { 145 | if err == nil { 146 | return nil 147 | } 148 | return &withStack{ 149 | err, 150 | callers(), 151 | } 152 | } 153 | 154 | type withStack struct { 155 | error 156 | *stack 157 | } 158 | 159 | func (w *withStack) Cause() error { return w.error } 160 | 161 | func (w *withStack) Format(s fmt.State, verb rune) { 162 | switch verb { 163 | case 'v': 164 | if s.Flag('+') { 165 | fmt.Fprintf(s, "%+v", w.Cause()) 166 | w.stack.Format(s, verb) 167 | return 168 | } 169 | fallthrough 170 | case 's': 171 | io.WriteString(s, w.Error()) 172 | case 'q': 173 | fmt.Fprintf(s, "%q", w.Error()) 174 | } 175 | } 176 | 177 | // Wrap returns an error annotating err with a stack trace 178 | // at the point Wrap is called, and the supplied message. 179 | // If err is nil, Wrap returns nil. 180 | func Wrap(err error, message string) error { 181 | if err == nil { 182 | return nil 183 | } 184 | err = &withMessage{ 185 | cause: err, 186 | msg: message, 187 | } 188 | return &withStack{ 189 | err, 190 | callers(), 191 | } 192 | } 193 | 194 | // Wrapf returns an error annotating err with a stack trace 195 | // at the point Wrapf is call, and the format specifier. 196 | // If err is nil, Wrapf returns nil. 197 | func Wrapf(err error, format string, args ...interface{}) error { 198 | if err == nil { 199 | return nil 200 | } 201 | err = &withMessage{ 202 | cause: err, 203 | msg: fmt.Sprintf(format, args...), 204 | } 205 | return &withStack{ 206 | err, 207 | callers(), 208 | } 209 | } 210 | 211 | // WithMessage annotates err with a new message. 212 | // If err is nil, WithMessage returns nil. 213 | func WithMessage(err error, message string) error { 214 | if err == nil { 215 | return nil 216 | } 217 | return &withMessage{ 218 | cause: err, 219 | msg: message, 220 | } 221 | } 222 | 223 | type withMessage struct { 224 | cause error 225 | msg string 226 | } 227 | 228 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } 229 | func (w *withMessage) Cause() error { return w.cause } 230 | 231 | func (w *withMessage) Format(s fmt.State, verb rune) { 232 | switch verb { 233 | case 'v': 234 | if s.Flag('+') { 235 | fmt.Fprintf(s, "%+v\n", w.Cause()) 236 | io.WriteString(s, w.msg) 237 | return 238 | } 239 | fallthrough 240 | case 's', 'q': 241 | io.WriteString(s, w.Error()) 242 | } 243 | } 244 | 245 | // Cause returns the underlying cause of the error, if possible. 246 | // An error value has a cause if it implements the following 247 | // interface: 248 | // 249 | // type causer interface { 250 | // Cause() error 251 | // } 252 | // 253 | // If the error does not implement Cause, the original error will 254 | // be returned. If the error is nil, nil will be returned without further 255 | // investigation. 256 | func Cause(err error) error { 257 | type causer interface { 258 | Cause() error 259 | } 260 | 261 | for err != nil { 262 | cause, ok := err.(causer) 263 | if !ok { 264 | break 265 | } 266 | err = cause.Cause() 267 | } 268 | return err 269 | } 270 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/bplist_generator.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "time" 9 | "unicode/utf16" 10 | ) 11 | 12 | func bplistMinimumIntSize(n uint64) int { 13 | switch { 14 | case n <= uint64(0xff): 15 | return 1 16 | case n <= uint64(0xffff): 17 | return 2 18 | case n <= uint64(0xffffffff): 19 | return 4 20 | default: 21 | return 8 22 | } 23 | } 24 | 25 | func bplistValueShouldUnique(pval cfValue) bool { 26 | switch pval.(type) { 27 | case cfString, *cfNumber, *cfReal, cfDate, cfData: 28 | return true 29 | } 30 | return false 31 | } 32 | 33 | type bplistGenerator struct { 34 | writer *countedWriter 35 | objmap map[interface{}]uint64 // maps pValue.hash()es to object locations 36 | objtable []cfValue 37 | trailer bplistTrailer 38 | } 39 | 40 | func (p *bplistGenerator) flattenPlistValue(pval cfValue) { 41 | key := pval.hash() 42 | if bplistValueShouldUnique(pval) { 43 | if _, ok := p.objmap[key]; ok { 44 | return 45 | } 46 | } 47 | 48 | p.objmap[key] = uint64(len(p.objtable)) 49 | p.objtable = append(p.objtable, pval) 50 | 51 | switch pval := pval.(type) { 52 | case *cfDictionary: 53 | pval.sort() 54 | for _, k := range pval.keys { 55 | p.flattenPlistValue(cfString(k)) 56 | } 57 | for _, v := range pval.values { 58 | p.flattenPlistValue(v) 59 | } 60 | case *cfArray: 61 | for _, v := range pval.values { 62 | p.flattenPlistValue(v) 63 | } 64 | } 65 | } 66 | 67 | func (p *bplistGenerator) indexForPlistValue(pval cfValue) (uint64, bool) { 68 | v, ok := p.objmap[pval.hash()] 69 | return v, ok 70 | } 71 | 72 | func (p *bplistGenerator) generateDocument(root cfValue) { 73 | p.objtable = make([]cfValue, 0, 16) 74 | p.objmap = make(map[interface{}]uint64) 75 | p.flattenPlistValue(root) 76 | 77 | p.trailer.NumObjects = uint64(len(p.objtable)) 78 | p.trailer.ObjectRefSize = uint8(bplistMinimumIntSize(p.trailer.NumObjects)) 79 | 80 | p.writer.Write([]byte("bplist00")) 81 | 82 | offtable := make([]uint64, p.trailer.NumObjects) 83 | for i, pval := range p.objtable { 84 | offtable[i] = uint64(p.writer.BytesWritten()) 85 | p.writePlistValue(pval) 86 | } 87 | 88 | p.trailer.OffsetIntSize = uint8(bplistMinimumIntSize(uint64(p.writer.BytesWritten()))) 89 | p.trailer.TopObject = p.objmap[root.hash()] 90 | p.trailer.OffsetTableOffset = uint64(p.writer.BytesWritten()) 91 | 92 | for _, offset := range offtable { 93 | p.writeSizedInt(offset, int(p.trailer.OffsetIntSize)) 94 | } 95 | 96 | binary.Write(p.writer, binary.BigEndian, p.trailer) 97 | } 98 | 99 | func (p *bplistGenerator) writePlistValue(pval cfValue) { 100 | if pval == nil { 101 | return 102 | } 103 | 104 | switch pval := pval.(type) { 105 | case *cfDictionary: 106 | p.writeDictionaryTag(pval) 107 | case *cfArray: 108 | p.writeArrayTag(pval.values) 109 | case cfString: 110 | p.writeStringTag(string(pval)) 111 | case *cfNumber: 112 | p.writeIntTag(pval.signed, pval.value) 113 | case *cfReal: 114 | if pval.wide { 115 | p.writeRealTag(pval.value, 64) 116 | } else { 117 | p.writeRealTag(pval.value, 32) 118 | } 119 | case cfBoolean: 120 | p.writeBoolTag(bool(pval)) 121 | case cfData: 122 | p.writeDataTag([]byte(pval)) 123 | case cfDate: 124 | p.writeDateTag(time.Time(pval)) 125 | case cfUID: 126 | p.writeUIDTag(UID(pval)) 127 | default: 128 | panic(fmt.Errorf("unknown plist type %t", pval)) 129 | } 130 | } 131 | 132 | func (p *bplistGenerator) writeSizedInt(n uint64, nbytes int) { 133 | var val interface{} 134 | switch nbytes { 135 | case 1: 136 | val = uint8(n) 137 | case 2: 138 | val = uint16(n) 139 | case 4: 140 | val = uint32(n) 141 | case 8: 142 | val = n 143 | default: 144 | panic(errors.New("illegal integer size")) 145 | } 146 | binary.Write(p.writer, binary.BigEndian, val) 147 | } 148 | 149 | func (p *bplistGenerator) writeBoolTag(v bool) { 150 | tag := uint8(bpTagBoolFalse) 151 | if v { 152 | tag = bpTagBoolTrue 153 | } 154 | binary.Write(p.writer, binary.BigEndian, tag) 155 | } 156 | 157 | func (p *bplistGenerator) writeIntTag(signed bool, n uint64) { 158 | var tag uint8 159 | var val interface{} 160 | switch { 161 | case n <= uint64(0xff): 162 | val = uint8(n) 163 | tag = bpTagInteger | 0x0 164 | case n <= uint64(0xffff): 165 | val = uint16(n) 166 | tag = bpTagInteger | 0x1 167 | case n <= uint64(0xffffffff): 168 | val = uint32(n) 169 | tag = bpTagInteger | 0x2 170 | case n > uint64(0x7fffffffffffffff) && !signed: 171 | // 64-bit values are always *signed* in format 00. 172 | // Any unsigned value that doesn't intersect with the signed 173 | // range must be sign-extended and stored as a SInt128 174 | val = n 175 | tag = bpTagInteger | 0x4 176 | default: 177 | val = n 178 | tag = bpTagInteger | 0x3 179 | } 180 | 181 | binary.Write(p.writer, binary.BigEndian, tag) 182 | if tag&0xF == 0x4 { 183 | // SInt128; in the absence of true 128-bit integers in Go, 184 | // we'll just fake the top half. We only got here because 185 | // we had an unsigned 64-bit int that didn't fit, 186 | // so sign extend it with zeroes. 187 | binary.Write(p.writer, binary.BigEndian, uint64(0)) 188 | } 189 | binary.Write(p.writer, binary.BigEndian, val) 190 | } 191 | 192 | func (p *bplistGenerator) writeUIDTag(u UID) { 193 | nbytes := bplistMinimumIntSize(uint64(u)) 194 | tag := uint8(bpTagUID | (nbytes - 1)) 195 | 196 | binary.Write(p.writer, binary.BigEndian, tag) 197 | p.writeSizedInt(uint64(u), nbytes) 198 | } 199 | 200 | func (p *bplistGenerator) writeRealTag(n float64, bits int) { 201 | var tag uint8 = bpTagReal | 0x3 202 | var val interface{} = n 203 | if bits == 32 { 204 | val = float32(n) 205 | tag = bpTagReal | 0x2 206 | } 207 | 208 | binary.Write(p.writer, binary.BigEndian, tag) 209 | binary.Write(p.writer, binary.BigEndian, val) 210 | } 211 | 212 | func (p *bplistGenerator) writeDateTag(t time.Time) { 213 | tag := uint8(bpTagDate) | 0x3 214 | val := float64(t.In(time.UTC).UnixNano()) / float64(time.Second) 215 | val -= 978307200 // Adjust to Apple Epoch 216 | 217 | binary.Write(p.writer, binary.BigEndian, tag) 218 | binary.Write(p.writer, binary.BigEndian, val) 219 | } 220 | 221 | func (p *bplistGenerator) writeCountedTag(tag uint8, count uint64) { 222 | marker := tag 223 | if count >= 0xF { 224 | marker |= 0xF 225 | } else { 226 | marker |= uint8(count) 227 | } 228 | 229 | binary.Write(p.writer, binary.BigEndian, marker) 230 | 231 | if count >= 0xF { 232 | p.writeIntTag(false, count) 233 | } 234 | } 235 | 236 | func (p *bplistGenerator) writeDataTag(data []byte) { 237 | p.writeCountedTag(bpTagData, uint64(len(data))) 238 | binary.Write(p.writer, binary.BigEndian, data) 239 | } 240 | 241 | func (p *bplistGenerator) writeStringTag(str string) { 242 | for _, r := range str { 243 | if r > 0x7F { 244 | utf16Runes := utf16.Encode([]rune(str)) 245 | p.writeCountedTag(bpTagUTF16String, uint64(len(utf16Runes))) 246 | binary.Write(p.writer, binary.BigEndian, utf16Runes) 247 | return 248 | } 249 | } 250 | 251 | p.writeCountedTag(bpTagASCIIString, uint64(len(str))) 252 | binary.Write(p.writer, binary.BigEndian, []byte(str)) 253 | } 254 | 255 | func (p *bplistGenerator) writeDictionaryTag(dict *cfDictionary) { 256 | // assumption: sorted already; flattenPlistValue did this. 257 | cnt := len(dict.keys) 258 | p.writeCountedTag(bpTagDictionary, uint64(cnt)) 259 | vals := make([]uint64, cnt*2) 260 | for i, k := range dict.keys { 261 | // invariant: keys have already been "uniqued" (as PStrings) 262 | keyIdx, ok := p.objmap[cfString(k).hash()] 263 | if !ok { 264 | panic(errors.New("failed to find key " + k + " in object map during serialization")) 265 | } 266 | vals[i] = keyIdx 267 | } 268 | 269 | for i, v := range dict.values { 270 | // invariant: values have already been "uniqued" 271 | objIdx, ok := p.indexForPlistValue(v) 272 | if !ok { 273 | panic(errors.New("failed to find value in object map during serialization")) 274 | } 275 | vals[i+cnt] = objIdx 276 | } 277 | 278 | for _, v := range vals { 279 | p.writeSizedInt(v, int(p.trailer.ObjectRefSize)) 280 | } 281 | } 282 | 283 | func (p *bplistGenerator) writeArrayTag(arr []cfValue) { 284 | p.writeCountedTag(bpTagArray, uint64(len(arr))) 285 | for _, v := range arr { 286 | objIdx, ok := p.indexForPlistValue(v) 287 | if !ok { 288 | panic(errors.New("failed to find value in object map during serialization")) 289 | } 290 | 291 | p.writeSizedInt(objIdx, int(p.trailer.ObjectRefSize)) 292 | } 293 | } 294 | 295 | func (p *bplistGenerator) Indent(i string) { 296 | // There's nothing to indent. 297 | } 298 | 299 | func newBplistGenerator(w io.Writer) *bplistGenerator { 300 | return &bplistGenerator{ 301 | writer: &countedWriter{Writer: mustWriter{w}}, 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/unmarshal.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding" 5 | "fmt" 6 | "reflect" 7 | "runtime" 8 | "time" 9 | ) 10 | 11 | type incompatibleDecodeTypeError struct { 12 | dest reflect.Type 13 | src string // type name (from cfValue) 14 | } 15 | 16 | func (u *incompatibleDecodeTypeError) Error() string { 17 | return fmt.Sprintf("plist: type mismatch: tried to decode plist type `%v' into value of type `%v'", u.src, u.dest) 18 | } 19 | 20 | var ( 21 | plistUnmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() 22 | textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 23 | uidType = reflect.TypeOf(UID(0)) 24 | ) 25 | 26 | func isEmptyInterface(v reflect.Value) bool { 27 | return v.Kind() == reflect.Interface && v.NumMethod() == 0 28 | } 29 | 30 | func (p *Decoder) unmarshalPlistInterface(pval cfValue, unmarshalable Unmarshaler) { 31 | err := unmarshalable.UnmarshalPlist(func(i interface{}) (err error) { 32 | defer func() { 33 | if r := recover(); r != nil { 34 | if _, ok := r.(runtime.Error); ok { 35 | panic(r) 36 | } 37 | err = r.(error) 38 | } 39 | }() 40 | p.unmarshal(pval, reflect.ValueOf(i)) 41 | return 42 | }) 43 | 44 | if err != nil { 45 | panic(err) 46 | } 47 | } 48 | 49 | func (p *Decoder) unmarshalTextInterface(pval cfString, unmarshalable encoding.TextUnmarshaler) { 50 | err := unmarshalable.UnmarshalText([]byte(pval)) 51 | if err != nil { 52 | panic(err) 53 | } 54 | } 55 | 56 | func (p *Decoder) unmarshalTime(pval cfDate, val reflect.Value) { 57 | val.Set(reflect.ValueOf(time.Time(pval))) 58 | } 59 | 60 | func (p *Decoder) unmarshalLaxString(s string, val reflect.Value) { 61 | switch val.Kind() { 62 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 63 | i := mustParseInt(s, 10, 64) 64 | val.SetInt(i) 65 | return 66 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 67 | i := mustParseUint(s, 10, 64) 68 | val.SetUint(i) 69 | return 70 | case reflect.Float32, reflect.Float64: 71 | f := mustParseFloat(s, 64) 72 | val.SetFloat(f) 73 | return 74 | case reflect.Bool: 75 | b := mustParseBool(s) 76 | val.SetBool(b) 77 | return 78 | case reflect.Struct: 79 | if val.Type() == timeType { 80 | t, err := time.Parse(textPlistTimeLayout, s) 81 | if err != nil { 82 | panic(err) 83 | } 84 | val.Set(reflect.ValueOf(t.In(time.UTC))) 85 | return 86 | } 87 | fallthrough 88 | default: 89 | panic(&incompatibleDecodeTypeError{val.Type(), "string"}) 90 | } 91 | } 92 | 93 | func (p *Decoder) unmarshal(pval cfValue, val reflect.Value) { 94 | if pval == nil { 95 | return 96 | } 97 | 98 | if val.Kind() == reflect.Ptr { 99 | if val.IsNil() { 100 | val.Set(reflect.New(val.Type().Elem())) 101 | } 102 | val = val.Elem() 103 | } 104 | 105 | if isEmptyInterface(val) { 106 | v := p.valueInterface(pval) 107 | val.Set(reflect.ValueOf(v)) 108 | return 109 | } 110 | 111 | incompatibleTypeError := &incompatibleDecodeTypeError{val.Type(), pval.typeName()} 112 | 113 | // time.Time implements TextMarshaler, but we need to parse it as RFC3339 114 | if date, ok := pval.(cfDate); ok { 115 | if val.Type() == timeType { 116 | p.unmarshalTime(date, val) 117 | return 118 | } 119 | panic(incompatibleTypeError) 120 | } 121 | 122 | if receiver, can := implementsInterface(val, plistUnmarshalerType); can { 123 | p.unmarshalPlistInterface(pval, receiver.(Unmarshaler)) 124 | return 125 | } 126 | 127 | if val.Type() != timeType { 128 | if receiver, can := implementsInterface(val, textUnmarshalerType); can { 129 | if str, ok := pval.(cfString); ok { 130 | p.unmarshalTextInterface(str, receiver.(encoding.TextUnmarshaler)) 131 | } else { 132 | panic(incompatibleTypeError) 133 | } 134 | return 135 | } 136 | } 137 | 138 | typ := val.Type() 139 | 140 | switch pval := pval.(type) { 141 | case cfString: 142 | if val.Kind() == reflect.String { 143 | val.SetString(string(pval)) 144 | return 145 | } 146 | if p.lax { 147 | p.unmarshalLaxString(string(pval), val) 148 | return 149 | } 150 | 151 | panic(incompatibleTypeError) 152 | case *cfNumber: 153 | switch val.Kind() { 154 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 155 | val.SetInt(int64(pval.value)) 156 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 157 | val.SetUint(pval.value) 158 | default: 159 | panic(incompatibleTypeError) 160 | } 161 | case *cfReal: 162 | if val.Kind() == reflect.Float32 || val.Kind() == reflect.Float64 { 163 | // TODO: Consider warning on a downcast (storing a 64-bit value in a 32-bit reflect) 164 | val.SetFloat(pval.value) 165 | } else { 166 | panic(incompatibleTypeError) 167 | } 168 | case cfBoolean: 169 | if val.Kind() == reflect.Bool { 170 | val.SetBool(bool(pval)) 171 | } else { 172 | panic(incompatibleTypeError) 173 | } 174 | case cfData: 175 | if val.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { 176 | val.SetBytes([]byte(pval)) 177 | } else { 178 | panic(incompatibleTypeError) 179 | } 180 | case cfUID: 181 | if val.Type() == uidType { 182 | val.SetUint(uint64(pval)) 183 | } else { 184 | switch val.Kind() { 185 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 186 | val.SetInt(int64(pval)) 187 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 188 | val.SetUint(uint64(pval)) 189 | default: 190 | panic(incompatibleTypeError) 191 | } 192 | } 193 | case *cfArray: 194 | p.unmarshalArray(pval, val) 195 | case *cfDictionary: 196 | p.unmarshalDictionary(pval, val) 197 | } 198 | } 199 | 200 | func (p *Decoder) unmarshalArray(a *cfArray, val reflect.Value) { 201 | var n int 202 | if val.Kind() == reflect.Slice { 203 | // Slice of element values. 204 | // Grow slice. 205 | cnt := len(a.values) + val.Len() 206 | if cnt >= val.Cap() { 207 | ncap := 2 * cnt 208 | if ncap < 4 { 209 | ncap = 4 210 | } 211 | new := reflect.MakeSlice(val.Type(), val.Len(), ncap) 212 | reflect.Copy(new, val) 213 | val.Set(new) 214 | } 215 | n = val.Len() 216 | val.SetLen(cnt) 217 | } else if val.Kind() == reflect.Array { 218 | if len(a.values) > val.Cap() { 219 | panic(fmt.Errorf("plist: attempted to unmarshal %d values into an array of size %d", len(a.values), val.Cap())) 220 | } 221 | } else { 222 | panic(&incompatibleDecodeTypeError{val.Type(), a.typeName()}) 223 | } 224 | 225 | // Recur to read element into slice. 226 | for _, sval := range a.values { 227 | p.unmarshal(sval, val.Index(n)) 228 | n++ 229 | } 230 | return 231 | } 232 | 233 | func (p *Decoder) unmarshalDictionary(dict *cfDictionary, val reflect.Value) { 234 | typ := val.Type() 235 | switch val.Kind() { 236 | case reflect.Struct: 237 | tinfo, err := getTypeInfo(typ) 238 | if err != nil { 239 | panic(err) 240 | } 241 | 242 | entries := make(map[string]cfValue, len(dict.keys)) 243 | for i, k := range dict.keys { 244 | sval := dict.values[i] 245 | entries[k] = sval 246 | } 247 | 248 | for _, finfo := range tinfo.fields { 249 | p.unmarshal(entries[finfo.name], finfo.value(val)) 250 | } 251 | case reflect.Map: 252 | if val.IsNil() { 253 | val.Set(reflect.MakeMap(typ)) 254 | } 255 | 256 | for i, k := range dict.keys { 257 | sval := dict.values[i] 258 | 259 | keyv := reflect.ValueOf(k).Convert(typ.Key()) 260 | mapElem := val.MapIndex(keyv) 261 | if !mapElem.IsValid() { 262 | mapElem = reflect.New(typ.Elem()).Elem() 263 | } 264 | 265 | p.unmarshal(sval, mapElem) 266 | val.SetMapIndex(keyv, mapElem) 267 | } 268 | default: 269 | panic(&incompatibleDecodeTypeError{typ, dict.typeName()}) 270 | } 271 | } 272 | 273 | /* *Interface is modelled after encoding/json */ 274 | func (p *Decoder) valueInterface(pval cfValue) interface{} { 275 | switch pval := pval.(type) { 276 | case cfString: 277 | return string(pval) 278 | case *cfNumber: 279 | if pval.signed { 280 | return int64(pval.value) 281 | } 282 | return pval.value 283 | case *cfReal: 284 | if pval.wide { 285 | return pval.value 286 | } else { 287 | return float32(pval.value) 288 | } 289 | case cfBoolean: 290 | return bool(pval) 291 | case *cfArray: 292 | return p.arrayInterface(pval) 293 | case *cfDictionary: 294 | return p.dictionaryInterface(pval) 295 | case cfData: 296 | return []byte(pval) 297 | case cfDate: 298 | return time.Time(pval) 299 | case cfUID: 300 | return UID(pval) 301 | } 302 | return nil 303 | } 304 | 305 | func (p *Decoder) arrayInterface(a *cfArray) []interface{} { 306 | out := make([]interface{}, len(a.values)) 307 | for i, subv := range a.values { 308 | out[i] = p.valueInterface(subv) 309 | } 310 | return out 311 | } 312 | 313 | func (p *Decoder) dictionaryInterface(dict *cfDictionary) map[string]interface{} { 314 | out := make(map[string]interface{}) 315 | for i, k := range dict.keys { 316 | subv := dict.values[i] 317 | out[k] = p.valueInterface(subv) 318 | } 319 | return out 320 | } 321 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/bplist_parser.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "math" 11 | "runtime" 12 | "time" 13 | "unicode/utf16" 14 | ) 15 | 16 | const ( 17 | signedHighBits = 0xFFFFFFFFFFFFFFFF 18 | ) 19 | 20 | type offset uint64 21 | 22 | type bplistParser struct { 23 | buffer []byte 24 | 25 | reader io.ReadSeeker 26 | version int 27 | objects []cfValue // object ID to object 28 | trailer bplistTrailer 29 | trailerOffset uint64 30 | 31 | containerStack []offset // slice of object offsets; manipulated during container deserialization 32 | } 33 | 34 | func (p *bplistParser) validateDocumentTrailer() { 35 | if p.trailer.OffsetTableOffset >= p.trailerOffset { 36 | panic(fmt.Errorf("offset table beyond beginning of trailer (0x%x, trailer@0x%x)", p.trailer.OffsetTableOffset, p.trailerOffset)) 37 | } 38 | 39 | if p.trailer.OffsetTableOffset < 9 { 40 | panic(fmt.Errorf("offset table begins inside header (0x%x)", p.trailer.OffsetTableOffset)) 41 | } 42 | 43 | if p.trailerOffset > (p.trailer.NumObjects*uint64(p.trailer.OffsetIntSize))+p.trailer.OffsetTableOffset { 44 | panic(errors.New("garbage between offset table and trailer")) 45 | } 46 | 47 | if p.trailer.OffsetTableOffset+(uint64(p.trailer.OffsetIntSize)*p.trailer.NumObjects) > p.trailerOffset { 48 | panic(errors.New("offset table isn't long enough to address every object")) 49 | } 50 | 51 | maxObjectRef := uint64(1) << (8 * p.trailer.ObjectRefSize) 52 | if p.trailer.NumObjects > maxObjectRef { 53 | panic(fmt.Errorf("more objects (%v) than object ref size (%v bytes) can support", p.trailer.NumObjects, p.trailer.ObjectRefSize)) 54 | } 55 | 56 | if p.trailer.OffsetIntSize < uint8(8) && (uint64(1)<<(8*p.trailer.OffsetIntSize)) <= p.trailer.OffsetTableOffset { 57 | panic(errors.New("offset size isn't big enough to address entire file")) 58 | } 59 | 60 | if p.trailer.TopObject >= p.trailer.NumObjects { 61 | panic(fmt.Errorf("top object #%d is out of range (only %d exist)", p.trailer.TopObject, p.trailer.NumObjects)) 62 | } 63 | } 64 | 65 | func (p *bplistParser) parseDocument() (pval cfValue, parseError error) { 66 | defer func() { 67 | if r := recover(); r != nil { 68 | if _, ok := r.(runtime.Error); ok { 69 | panic(r) 70 | } 71 | 72 | parseError = plistParseError{"binary", r.(error)} 73 | } 74 | }() 75 | 76 | p.buffer, _ = ioutil.ReadAll(p.reader) 77 | 78 | l := len(p.buffer) 79 | if l < 40 { 80 | panic(errors.New("not enough data")) 81 | } 82 | 83 | if !bytes.Equal(p.buffer[0:6], []byte{'b', 'p', 'l', 'i', 's', 't'}) { 84 | panic(errors.New("incomprehensible magic")) 85 | } 86 | 87 | p.version = int(((p.buffer[6] - '0') * 10) + (p.buffer[7] - '0')) 88 | 89 | if p.version > 1 { 90 | panic(fmt.Errorf("unexpected version %d", p.version)) 91 | } 92 | 93 | p.trailerOffset = uint64(l - 32) 94 | p.trailer = bplistTrailer{ 95 | SortVersion: p.buffer[p.trailerOffset+5], 96 | OffsetIntSize: p.buffer[p.trailerOffset+6], 97 | ObjectRefSize: p.buffer[p.trailerOffset+7], 98 | NumObjects: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+8:]), 99 | TopObject: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+16:]), 100 | OffsetTableOffset: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+24:]), 101 | } 102 | 103 | p.validateDocumentTrailer() 104 | 105 | // INVARIANTS: 106 | // - Entire offset table is before trailer 107 | // - Offset table begins after header 108 | // - Offset table can address entire document 109 | // - Object IDs are big enough to support the number of objects in this plist 110 | // - Top object is in range 111 | 112 | p.objects = make([]cfValue, p.trailer.NumObjects) 113 | 114 | pval = p.objectAtIndex(p.trailer.TopObject) 115 | return 116 | } 117 | 118 | // parseSizedInteger returns a 128-bit integer as low64, high64 119 | func (p *bplistParser) parseSizedInteger(off offset, nbytes int) (lo uint64, hi uint64, newOffset offset) { 120 | // Per comments in CoreFoundation, format version 00 requires that all 121 | // 1, 2 or 4-byte integers be interpreted as unsigned. 8-byte integers are 122 | // signed (always?) and therefore must be sign extended here. 123 | // negative 1, 2, or 4-byte integers are always emitted as 64-bit. 124 | switch nbytes { 125 | case 1: 126 | lo, hi = uint64(p.buffer[off]), 0 127 | case 2: 128 | lo, hi = uint64(binary.BigEndian.Uint16(p.buffer[off:])), 0 129 | case 4: 130 | lo, hi = uint64(binary.BigEndian.Uint32(p.buffer[off:])), 0 131 | case 8: 132 | lo = binary.BigEndian.Uint64(p.buffer[off:]) 133 | if p.buffer[off]&0x80 != 0 { 134 | // sign extend if lo is signed 135 | hi = signedHighBits 136 | } 137 | case 16: 138 | lo, hi = binary.BigEndian.Uint64(p.buffer[off+8:]), binary.BigEndian.Uint64(p.buffer[off:]) 139 | default: 140 | panic(errors.New("illegal integer size")) 141 | } 142 | newOffset = off + offset(nbytes) 143 | return 144 | } 145 | 146 | func (p *bplistParser) parseObjectRefAtOffset(off offset) (uint64, offset) { 147 | oid, _, next := p.parseSizedInteger(off, int(p.trailer.ObjectRefSize)) 148 | return oid, next 149 | } 150 | 151 | func (p *bplistParser) parseOffsetAtOffset(off offset) (offset, offset) { 152 | parsedOffset, _, next := p.parseSizedInteger(off, int(p.trailer.OffsetIntSize)) 153 | return offset(parsedOffset), next 154 | } 155 | 156 | func (p *bplistParser) objectAtIndex(index uint64) cfValue { 157 | if index >= p.trailer.NumObjects { 158 | panic(fmt.Errorf("invalid object#%d (max %d)", index, p.trailer.NumObjects)) 159 | } 160 | 161 | if pval := p.objects[index]; pval != nil { 162 | return pval 163 | } 164 | 165 | off, _ := p.parseOffsetAtOffset(offset(p.trailer.OffsetTableOffset + (index * uint64(p.trailer.OffsetIntSize)))) 166 | if off > offset(p.trailer.OffsetTableOffset-1) { 167 | panic(fmt.Errorf("object#%d starts beyond beginning of object table (0x%x, table@0x%x)", index, off, p.trailer.OffsetTableOffset)) 168 | } 169 | 170 | pval := p.parseTagAtOffset(off) 171 | p.objects[index] = pval 172 | return pval 173 | 174 | } 175 | 176 | func (p *bplistParser) pushNestedObject(off offset) { 177 | for _, v := range p.containerStack { 178 | if v == off { 179 | p.panicNestedObject(off) 180 | } 181 | } 182 | p.containerStack = append(p.containerStack, off) 183 | } 184 | 185 | func (p *bplistParser) panicNestedObject(off offset) { 186 | ids := "" 187 | for _, v := range p.containerStack { 188 | ids += fmt.Sprintf("0x%x > ", v) 189 | } 190 | 191 | // %s0x%d: ids above ends with " > " 192 | panic(fmt.Errorf("self-referential collection@0x%x (%s0x%x) cannot be deserialized", off, ids, off)) 193 | } 194 | 195 | func (p *bplistParser) popNestedObject() { 196 | p.containerStack = p.containerStack[:len(p.containerStack)-1] 197 | } 198 | 199 | func (p *bplistParser) parseTagAtOffset(off offset) cfValue { 200 | tag := p.buffer[off] 201 | 202 | switch tag & 0xF0 { 203 | case bpTagNull: 204 | switch tag & 0x0F { 205 | case bpTagBoolTrue, bpTagBoolFalse: 206 | return cfBoolean(tag == bpTagBoolTrue) 207 | } 208 | case bpTagInteger: 209 | lo, hi, _ := p.parseIntegerAtOffset(off) 210 | return &cfNumber{ 211 | signed: hi == signedHighBits, // a signed integer is stored as a 128-bit integer with the top 64 bits set 212 | value: lo, 213 | } 214 | case bpTagReal: 215 | nbytes := 1 << (tag & 0x0F) 216 | switch nbytes { 217 | case 4: 218 | bits := binary.BigEndian.Uint32(p.buffer[off+1:]) 219 | return &cfReal{wide: false, value: float64(math.Float32frombits(bits))} 220 | case 8: 221 | bits := binary.BigEndian.Uint64(p.buffer[off+1:]) 222 | return &cfReal{wide: true, value: math.Float64frombits(bits)} 223 | } 224 | panic(errors.New("illegal float size")) 225 | case bpTagDate: 226 | bits := binary.BigEndian.Uint64(p.buffer[off+1:]) 227 | val := math.Float64frombits(bits) 228 | 229 | // Apple Epoch is 20110101000000Z 230 | // Adjust for UNIX Time 231 | val += 978307200 232 | 233 | sec, fsec := math.Modf(val) 234 | time := time.Unix(int64(sec), int64(fsec*float64(time.Second))).In(time.UTC) 235 | return cfDate(time) 236 | case bpTagData: 237 | data := p.parseDataAtOffset(off) 238 | return cfData(data) 239 | case bpTagASCIIString: 240 | str := p.parseASCIIStringAtOffset(off) 241 | return cfString(str) 242 | case bpTagUTF16String: 243 | str := p.parseUTF16StringAtOffset(off) 244 | return cfString(str) 245 | case bpTagUID: // Somehow different than int: low half is nbytes - 1 instead of log2(nbytes) 246 | lo, _, _ := p.parseSizedInteger(off+1, int(tag&0xF)+1) 247 | return cfUID(lo) 248 | case bpTagDictionary: 249 | return p.parseDictionaryAtOffset(off) 250 | case bpTagArray: 251 | return p.parseArrayAtOffset(off) 252 | } 253 | panic(fmt.Errorf("unexpected atom 0x%2.02x at offset 0x%x", tag, off)) 254 | } 255 | 256 | func (p *bplistParser) parseIntegerAtOffset(off offset) (uint64, uint64, offset) { 257 | tag := p.buffer[off] 258 | return p.parseSizedInteger(off+1, 1<<(tag&0xF)) 259 | } 260 | 261 | func (p *bplistParser) countForTagAtOffset(off offset) (uint64, offset) { 262 | tag := p.buffer[off] 263 | cnt := uint64(tag & 0x0F) 264 | if cnt == 0xF { 265 | cnt, _, off = p.parseIntegerAtOffset(off + 1) 266 | return cnt, off 267 | } 268 | return cnt, off + 1 269 | } 270 | 271 | func (p *bplistParser) parseDataAtOffset(off offset) []byte { 272 | len, start := p.countForTagAtOffset(off) 273 | if start+offset(len) > offset(p.trailer.OffsetTableOffset) { 274 | panic(fmt.Errorf("data@0x%x too long (%v bytes, max is %v)", off, len, p.trailer.OffsetTableOffset-uint64(start))) 275 | } 276 | return p.buffer[start : start+offset(len)] 277 | } 278 | 279 | func (p *bplistParser) parseASCIIStringAtOffset(off offset) string { 280 | len, start := p.countForTagAtOffset(off) 281 | if start+offset(len) > offset(p.trailer.OffsetTableOffset) { 282 | panic(fmt.Errorf("ascii string@0x%x too long (%v bytes, max is %v)", off, len, p.trailer.OffsetTableOffset-uint64(start))) 283 | } 284 | 285 | return zeroCopy8BitString(p.buffer, int(start), int(len)) 286 | } 287 | 288 | func (p *bplistParser) parseUTF16StringAtOffset(off offset) string { 289 | len, start := p.countForTagAtOffset(off) 290 | bytes := len * 2 291 | if start+offset(bytes) > offset(p.trailer.OffsetTableOffset) { 292 | panic(fmt.Errorf("utf16 string@0x%x too long (%v bytes, max is %v)", off, bytes, p.trailer.OffsetTableOffset-uint64(start))) 293 | } 294 | 295 | u16s := make([]uint16, len) 296 | for i := offset(0); i < offset(len); i++ { 297 | u16s[i] = binary.BigEndian.Uint16(p.buffer[start+(i*2):]) 298 | } 299 | runes := utf16.Decode(u16s) 300 | return string(runes) 301 | } 302 | 303 | func (p *bplistParser) parseObjectListAtOffset(off offset, count uint64) []cfValue { 304 | if off+offset(count*uint64(p.trailer.ObjectRefSize)) > offset(p.trailer.OffsetTableOffset) { 305 | panic(fmt.Errorf("list@0x%x length (%v) puts its end beyond the offset table at 0x%x", off, count, p.trailer.OffsetTableOffset)) 306 | } 307 | objects := make([]cfValue, count) 308 | 309 | next := off 310 | var oid uint64 311 | for i := uint64(0); i < count; i++ { 312 | oid, next = p.parseObjectRefAtOffset(next) 313 | objects[i] = p.objectAtIndex(oid) 314 | } 315 | 316 | return objects 317 | } 318 | 319 | func (p *bplistParser) parseDictionaryAtOffset(off offset) *cfDictionary { 320 | p.pushNestedObject(off) 321 | defer p.popNestedObject() 322 | 323 | // a dictionary is an object list of [key key key val val val] 324 | cnt, start := p.countForTagAtOffset(off) 325 | objects := p.parseObjectListAtOffset(start, cnt*2) 326 | 327 | keys := make([]string, cnt) 328 | for i := uint64(0); i < cnt; i++ { 329 | if str, ok := objects[i].(cfString); ok { 330 | keys[i] = string(str) 331 | } else { 332 | panic(fmt.Errorf("dictionary@0x%x contains non-string key at index %d", off, i)) 333 | } 334 | } 335 | 336 | return &cfDictionary{ 337 | keys: keys, 338 | values: objects[cnt:], 339 | } 340 | } 341 | 342 | func (p *bplistParser) parseArrayAtOffset(off offset) *cfArray { 343 | p.pushNestedObject(off) 344 | defer p.popNestedObject() 345 | 346 | // an array is just an object list 347 | cnt, start := p.countForTagAtOffset(off) 348 | return &cfArray{p.parseObjectListAtOffset(start, cnt)} 349 | } 350 | 351 | func newBplistParser(r io.ReadSeeker) *bplistParser { 352 | return &bplistParser{reader: r} 353 | } 354 | -------------------------------------------------------------------------------- /vendor/github.com/DHowett/go-plist/text_parser.go: -------------------------------------------------------------------------------- 1 | package plist 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "runtime" 10 | "strings" 11 | "time" 12 | "unicode/utf16" 13 | "unicode/utf8" 14 | ) 15 | 16 | type textPlistParser struct { 17 | reader io.Reader 18 | format int 19 | 20 | input string 21 | start int 22 | pos int 23 | width int 24 | } 25 | 26 | func convertU16(buffer []byte, bo binary.ByteOrder) (string, error) { 27 | if len(buffer)%2 != 0 { 28 | return "", errors.New("truncated utf16") 29 | } 30 | 31 | tmp := make([]uint16, len(buffer)/2) 32 | for i := 0; i < len(buffer); i += 2 { 33 | tmp[i/2] = bo.Uint16(buffer[i : i+2]) 34 | } 35 | return string(utf16.Decode(tmp)), nil 36 | } 37 | 38 | func guessEncodingAndConvert(buffer []byte) (string, error) { 39 | if len(buffer) >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF { 40 | // UTF-8 BOM 41 | return zeroCopy8BitString(buffer, 3, len(buffer)-3), nil 42 | } else if len(buffer) >= 2 { 43 | // UTF-16 guesses 44 | 45 | switch { 46 | // stream is big-endian (BOM is FE FF or head is 00 XX) 47 | case (buffer[0] == 0xFE && buffer[1] == 0xFF): 48 | return convertU16(buffer[2:], binary.BigEndian) 49 | case (buffer[0] == 0 && buffer[1] != 0): 50 | return convertU16(buffer, binary.BigEndian) 51 | 52 | // stream is little-endian (BOM is FE FF or head is XX 00) 53 | case (buffer[0] == 0xFF && buffer[1] == 0xFE): 54 | return convertU16(buffer[2:], binary.LittleEndian) 55 | case (buffer[0] != 0 && buffer[1] == 0): 56 | return convertU16(buffer, binary.LittleEndian) 57 | } 58 | } 59 | 60 | // fallback: assume ASCII (not great!) 61 | return zeroCopy8BitString(buffer, 0, len(buffer)), nil 62 | } 63 | 64 | func (p *textPlistParser) parseDocument() (pval cfValue, parseError error) { 65 | defer func() { 66 | if r := recover(); r != nil { 67 | if _, ok := r.(runtime.Error); ok { 68 | panic(r) 69 | } 70 | // Wrap all non-invalid-plist errors. 71 | parseError = plistParseError{"text", r.(error)} 72 | } 73 | }() 74 | 75 | buffer, err := ioutil.ReadAll(p.reader) 76 | if err != nil { 77 | panic(err) 78 | } 79 | 80 | p.input, err = guessEncodingAndConvert(buffer) 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | val := p.parsePlistValue() 86 | 87 | p.skipWhitespaceAndComments() 88 | if p.peek() != eof { 89 | if _, ok := val.(cfString); !ok { 90 | p.error("garbage after end of document") 91 | } 92 | 93 | p.start = 0 94 | p.pos = 0 95 | val = p.parseDictionary(true) 96 | } 97 | 98 | pval = val 99 | 100 | return 101 | } 102 | 103 | const eof rune = -1 104 | 105 | func (p *textPlistParser) error(e string, args ...interface{}) { 106 | line := strings.Count(p.input[:p.pos], "\n") 107 | char := p.pos - strings.LastIndex(p.input[:p.pos], "\n") - 1 108 | panic(fmt.Errorf("%s at line %d character %d", fmt.Sprintf(e, args...), line, char)) 109 | } 110 | 111 | func (p *textPlistParser) next() rune { 112 | if int(p.pos) >= len(p.input) { 113 | p.width = 0 114 | return eof 115 | } 116 | r, w := utf8.DecodeRuneInString(p.input[p.pos:]) 117 | p.width = w 118 | p.pos += p.width 119 | return r 120 | } 121 | 122 | func (p *textPlistParser) backup() { 123 | p.pos -= p.width 124 | } 125 | 126 | func (p *textPlistParser) peek() rune { 127 | r := p.next() 128 | p.backup() 129 | return r 130 | } 131 | 132 | func (p *textPlistParser) emit() string { 133 | s := p.input[p.start:p.pos] 134 | p.start = p.pos 135 | return s 136 | } 137 | 138 | func (p *textPlistParser) ignore() { 139 | p.start = p.pos 140 | } 141 | 142 | func (p *textPlistParser) empty() bool { 143 | return p.start == p.pos 144 | } 145 | 146 | func (p *textPlistParser) scanUntil(ch rune) { 147 | if x := strings.IndexRune(p.input[p.pos:], ch); x >= 0 { 148 | p.pos += x 149 | return 150 | } 151 | p.pos = len(p.input) 152 | } 153 | 154 | func (p *textPlistParser) scanUntilAny(chs string) { 155 | if x := strings.IndexAny(p.input[p.pos:], chs); x >= 0 { 156 | p.pos += x 157 | return 158 | } 159 | p.pos = len(p.input) 160 | } 161 | 162 | func (p *textPlistParser) scanCharactersInSet(ch *characterSet) { 163 | for ch.Contains(p.next()) { 164 | } 165 | p.backup() 166 | } 167 | 168 | func (p *textPlistParser) scanCharactersNotInSet(ch *characterSet) { 169 | var r rune 170 | for { 171 | r = p.next() 172 | if r == eof || ch.Contains(r) { 173 | break 174 | } 175 | } 176 | p.backup() 177 | } 178 | 179 | func (p *textPlistParser) skipWhitespaceAndComments() { 180 | for { 181 | p.scanCharactersInSet(&whitespace) 182 | if strings.HasPrefix(p.input[p.pos:], "//") { 183 | p.scanCharactersNotInSet(&newlineCharacterSet) 184 | } else if strings.HasPrefix(p.input[p.pos:], "/*") { 185 | if x := strings.Index(p.input[p.pos:], "*/"); x >= 0 { 186 | p.pos += x + 2 // skip the */ as well 187 | continue // consume more whitespace 188 | } else { 189 | p.error("unexpected eof in block comment") 190 | } 191 | } else { 192 | break 193 | } 194 | } 195 | p.ignore() 196 | } 197 | 198 | func (p *textPlistParser) parseOctalDigits(max int) uint64 { 199 | var val uint64 200 | 201 | for i := 0; i < max; i++ { 202 | r := p.next() 203 | 204 | if r >= '0' && r <= '7' { 205 | val <<= 3 206 | val |= uint64((r - '0')) 207 | } else { 208 | p.backup() 209 | break 210 | } 211 | } 212 | return val 213 | } 214 | 215 | func (p *textPlistParser) parseHexDigits(max int) uint64 { 216 | var val uint64 217 | 218 | for i := 0; i < max; i++ { 219 | r := p.next() 220 | 221 | if r >= 'a' && r <= 'f' { 222 | val <<= 4 223 | val |= 10 + uint64((r - 'a')) 224 | } else if r >= 'A' && r <= 'F' { 225 | val <<= 4 226 | val |= 10 + uint64((r - 'A')) 227 | } else if r >= '0' && r <= '9' { 228 | val <<= 4 229 | val |= uint64((r - '0')) 230 | } else { 231 | p.backup() 232 | break 233 | } 234 | } 235 | return val 236 | } 237 | 238 | // the \ has already been consumed 239 | func (p *textPlistParser) parseEscape() string { 240 | var s string 241 | switch p.next() { 242 | case 'a': 243 | s = "\a" 244 | case 'b': 245 | s = "\b" 246 | case 'v': 247 | s = "\v" 248 | case 'f': 249 | s = "\f" 250 | case 't': 251 | s = "\t" 252 | case 'r': 253 | s = "\r" 254 | case 'n': 255 | s = "\n" 256 | case '\\': 257 | s = `\` 258 | case '"': 259 | s = `"` 260 | case 'x': 261 | s = string(rune(p.parseHexDigits(2))) 262 | case 'u', 'U': 263 | s = string(rune(p.parseHexDigits(4))) 264 | case '0', '1', '2', '3', '4', '5', '6', '7': 265 | p.backup() // we've already consumed one of the digits 266 | s = string(rune(p.parseOctalDigits(3))) 267 | default: 268 | p.backup() // everything else should be accepted 269 | } 270 | p.ignore() // skip the entire escape sequence 271 | return s 272 | } 273 | 274 | // the " has already been consumed 275 | func (p *textPlistParser) parseQuotedString() cfString { 276 | p.ignore() // ignore the " 277 | 278 | slowPath := false 279 | s := "" 280 | 281 | for { 282 | p.scanUntilAny(`"\`) 283 | switch p.peek() { 284 | case eof: 285 | p.error("unexpected eof in quoted string") 286 | case '"': 287 | section := p.emit() 288 | p.pos++ // skip " 289 | if !slowPath { 290 | return cfString(section) 291 | } else { 292 | s += section 293 | return cfString(s) 294 | } 295 | case '\\': 296 | slowPath = true 297 | s += p.emit() 298 | p.next() // consume \ 299 | s += p.parseEscape() 300 | } 301 | } 302 | } 303 | 304 | func (p *textPlistParser) parseUnquotedString() cfString { 305 | p.scanCharactersNotInSet(&gsQuotable) 306 | s := p.emit() 307 | if s == "" { 308 | p.error("invalid unquoted string (found an unquoted character that should be quoted?)") 309 | } 310 | 311 | return cfString(s) 312 | } 313 | 314 | // the { has already been consumed 315 | func (p *textPlistParser) parseDictionary(ignoreEof bool) *cfDictionary { 316 | //p.ignore() // ignore the { 317 | var keypv cfValue 318 | keys := make([]string, 0, 32) 319 | values := make([]cfValue, 0, 32) 320 | outer: 321 | for { 322 | p.skipWhitespaceAndComments() 323 | 324 | switch p.next() { 325 | case eof: 326 | if !ignoreEof { 327 | p.error("unexpected eof in dictionary") 328 | } 329 | fallthrough 330 | case '}': 331 | break outer 332 | case '"': 333 | keypv = p.parseQuotedString() 334 | default: 335 | p.backup() 336 | keypv = p.parseUnquotedString() 337 | } 338 | 339 | // INVARIANT: key can't be nil; parseQuoted and parseUnquoted 340 | // will panic out before they return nil. 341 | 342 | p.skipWhitespaceAndComments() 343 | 344 | var val cfValue 345 | n := p.next() 346 | if n == ';' { 347 | val = keypv 348 | } else if n == '=' { 349 | // whitespace is consumed within 350 | val = p.parsePlistValue() 351 | 352 | p.skipWhitespaceAndComments() 353 | 354 | if p.next() != ';' { 355 | p.error("missing ; in dictionary") 356 | } 357 | } else { 358 | p.error("missing = in dictionary") 359 | } 360 | 361 | keys = append(keys, string(keypv.(cfString))) 362 | values = append(values, val) 363 | } 364 | 365 | return &cfDictionary{keys: keys, values: values} 366 | } 367 | 368 | // the ( has already been consumed 369 | func (p *textPlistParser) parseArray() *cfArray { 370 | //p.ignore() // ignore the ( 371 | values := make([]cfValue, 0, 32) 372 | outer: 373 | for { 374 | p.skipWhitespaceAndComments() 375 | 376 | switch p.next() { 377 | case eof: 378 | p.error("unexpected eof in array") 379 | case ')': 380 | break outer // done here 381 | case ',': 382 | continue // restart; ,) is valid and we don't want to blow it 383 | default: 384 | p.backup() 385 | } 386 | 387 | pval := p.parsePlistValue() // whitespace is consumed within 388 | if str, ok := pval.(cfString); ok && string(str) == "" { 389 | // Empty strings in arrays are apparently skipped? 390 | // TODO: Figure out why this was implemented. 391 | continue 392 | } 393 | values = append(values, pval) 394 | } 395 | return &cfArray{values} 396 | } 397 | 398 | // the <* have already been consumed 399 | func (p *textPlistParser) parseGNUStepValue() cfValue { 400 | typ := p.next() 401 | p.ignore() 402 | p.scanUntil('>') 403 | 404 | if typ == eof || typ == '>' || p.empty() || p.peek() == eof { 405 | p.error("invalid GNUStep extended value") 406 | } 407 | 408 | v := p.emit() 409 | p.next() // consume the > 410 | 411 | switch typ { 412 | case 'I': 413 | if v[0] == '-' { 414 | n := mustParseInt(v, 10, 64) 415 | return &cfNumber{signed: true, value: uint64(n)} 416 | } else { 417 | n := mustParseUint(v, 10, 64) 418 | return &cfNumber{signed: false, value: n} 419 | } 420 | case 'R': 421 | n := mustParseFloat(v, 64) 422 | return &cfReal{wide: true, value: n} // TODO(DH) 32/64 423 | case 'B': 424 | b := v[0] == 'Y' 425 | return cfBoolean(b) 426 | case 'D': 427 | t, err := time.Parse(textPlistTimeLayout, v) 428 | if err != nil { 429 | p.error(err.Error()) 430 | } 431 | 432 | return cfDate(t.In(time.UTC)) 433 | } 434 | p.error("invalid GNUStep type " + string(typ)) 435 | return nil 436 | } 437 | 438 | // The < has already been consumed 439 | func (p *textPlistParser) parseHexData() cfData { 440 | buf := make([]byte, 256) 441 | i := 0 442 | c := 0 443 | 444 | for { 445 | r := p.next() 446 | switch r { 447 | case eof: 448 | p.error("unexpected eof in data") 449 | case '>': 450 | if c&1 == 1 { 451 | p.error("uneven number of hex digits in data") 452 | } 453 | p.ignore() 454 | return cfData(buf[:i]) 455 | case ' ', '\t', '\n', '\r', '\u2028', '\u2029': // more lax than apple here: skip spaces 456 | continue 457 | } 458 | 459 | buf[i] <<= 4 460 | if r >= 'a' && r <= 'f' { 461 | buf[i] |= 10 + byte((r - 'a')) 462 | } else if r >= 'A' && r <= 'F' { 463 | buf[i] |= 10 + byte((r - 'A')) 464 | } else if r >= '0' && r <= '9' { 465 | buf[i] |= byte((r - '0')) 466 | } else { 467 | p.error("unexpected hex digit `%c'", r) 468 | } 469 | 470 | c++ 471 | if c&1 == 0 { 472 | i++ 473 | if i >= len(buf) { 474 | realloc := make([]byte, len(buf)*2) 475 | copy(realloc, buf) 476 | buf = realloc 477 | } 478 | } 479 | } 480 | } 481 | 482 | func (p *textPlistParser) parsePlistValue() cfValue { 483 | for { 484 | p.skipWhitespaceAndComments() 485 | 486 | switch p.next() { 487 | case eof: 488 | return &cfDictionary{} 489 | case '<': 490 | if p.next() == '*' { 491 | p.format = GNUStepFormat 492 | return p.parseGNUStepValue() 493 | } 494 | 495 | p.backup() 496 | return p.parseHexData() 497 | case '"': 498 | return p.parseQuotedString() 499 | case '{': 500 | return p.parseDictionary(false) 501 | case '(': 502 | return p.parseArray() 503 | default: 504 | p.backup() 505 | return p.parseUnquotedString() 506 | } 507 | } 508 | } 509 | 510 | func newTextPlistParser(r io.Reader) *textPlistParser { 511 | return &textPlistParser{ 512 | reader: r, 513 | format: OpenStepFormat, 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /vendor/github.com/shogo82148/androidbinary/table.go: -------------------------------------------------------------------------------- 1 | package androidbinary 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "strconv" 8 | "strings" 9 | "unsafe" 10 | ) 11 | 12 | // ResID is ID for resources. 13 | type ResID uint32 14 | 15 | // TableFile is a resrouce table file. 16 | type TableFile struct { 17 | stringPool *ResStringPool 18 | tablePackages map[uint32]*TablePackage 19 | } 20 | 21 | // ResTableHeader is a header of TableFile. 22 | type ResTableHeader struct { 23 | Header ResChunkHeader 24 | PackageCount uint32 25 | } 26 | 27 | // ResTablePackage is a header of table packages. 28 | type ResTablePackage struct { 29 | Header ResChunkHeader 30 | ID uint32 31 | Name [128]uint16 32 | TypeStrings uint32 33 | LastPublicType uint32 34 | KeyStrings uint32 35 | LastPublicKey uint32 36 | } 37 | 38 | // TablePackage is a table package. 39 | type TablePackage struct { 40 | Header ResTablePackage 41 | TypeStrings *ResStringPool 42 | KeyStrings *ResStringPool 43 | TableTypes []*TableType 44 | } 45 | 46 | // ResTableType is a type of a table. 47 | type ResTableType struct { 48 | Header ResChunkHeader 49 | ID uint8 50 | Res0 uint8 51 | Res1 uint16 52 | EntryCount uint32 53 | EntriesStart uint32 54 | Config ResTableConfig 55 | } 56 | 57 | // ScreenLayout describes screen layout. 58 | type ScreenLayout uint8 59 | 60 | // ScreenLayout bits 61 | const ( 62 | MaskScreenSize ScreenLayout = 0x0f 63 | ScreenSizeAny ScreenLayout = 0x01 64 | ScreenSizeSmall ScreenLayout = 0x02 65 | ScreenSizeNormal ScreenLayout = 0x03 66 | ScreenSizeLarge ScreenLayout = 0x04 67 | ScreenSizeXLarge ScreenLayout = 0x05 68 | 69 | MaskScreenLong ScreenLayout = 0x30 70 | ShiftScreenLong = 4 71 | ScreenLongAny ScreenLayout = 0x00 72 | ScreenLongNo ScreenLayout = 0x10 73 | ScreenLongYes ScreenLayout = 0x20 74 | 75 | MaskLayoutDir ScreenLayout = 0xC0 76 | ShiftLayoutDir = 6 77 | LayoutDirAny ScreenLayout = 0x00 78 | LayoutDirLTR ScreenLayout = 0x40 79 | LayoutDirRTL ScreenLayout = 0x80 80 | ) 81 | 82 | // UIMode describes UI mode. 83 | type UIMode uint8 84 | 85 | // UIMode bits 86 | const ( 87 | MaskUIModeType UIMode = 0x0f 88 | UIModeTypeAny UIMode = 0x01 89 | UIModeTypeNormal UIMode = 0x02 90 | UIModeTypeDesk UIMode = 0x03 91 | UIModeTypeCar UIMode = 0x04 92 | 93 | MaskUIModeNight UIMode = 0x30 94 | ShiftUIModeNight = 4 95 | UIModeNightAny UIMode = 0x00 96 | UIModeNightNo UIMode = 0x10 97 | UIModeNightYes UIMode = 0x20 98 | ) 99 | 100 | // InputFlags are input flags. 101 | type InputFlags uint8 102 | 103 | // input flags 104 | const ( 105 | MaskKeysHidden InputFlags = 0x03 106 | KeysHiddenAny InputFlags = 0x00 107 | KeysHiddenNo InputFlags = 0x01 108 | KeysHiddenYes InputFlags = 0x02 109 | KeysHiddenSoft InputFlags = 0x03 110 | 111 | MaskNavHidden InputFlags = 0x0c 112 | NavHiddenAny InputFlags = 0x00 113 | NavHiddenNo InputFlags = 0x04 114 | NavHiddenYes InputFlags = 0x08 115 | ) 116 | 117 | // ResTableConfig is a configuration of a table. 118 | type ResTableConfig struct { 119 | Size uint32 120 | // imsi 121 | Mcc uint16 122 | Mnc uint16 123 | 124 | // locale 125 | Language [2]uint8 126 | Country [2]uint8 127 | 128 | // screen type 129 | Orientation uint8 130 | Touchscreen uint8 131 | Density uint16 132 | 133 | // inout 134 | Keyboard uint8 135 | Navigation uint8 136 | InputFlags InputFlags 137 | InputPad0 uint8 138 | 139 | // screen size 140 | ScreenWidth uint16 141 | ScreenHeight uint16 142 | 143 | // version 144 | SDKVersion uint16 145 | MinorVersion uint16 146 | 147 | // screen config 148 | ScreenLayout ScreenLayout 149 | UIMode UIMode 150 | SmallestScreenWidthDp uint16 151 | 152 | // screen size dp 153 | ScreenWidthDp uint16 154 | ScreenHeightDp uint16 155 | } 156 | 157 | // TableType is a collection of resource entries for a particular resource data type. 158 | type TableType struct { 159 | Header *ResTableType 160 | Entries []TableEntry 161 | } 162 | 163 | // ResTableEntry is the beginning of information about an entry in the resource table. 164 | type ResTableEntry struct { 165 | Size uint16 166 | Flags uint16 167 | Key ResStringPoolRef 168 | } 169 | 170 | // TableEntry is a entry in a recource table. 171 | type TableEntry struct { 172 | Key *ResTableEntry 173 | Value *ResValue 174 | Flags uint32 175 | } 176 | 177 | // ResTableTypeSpec is specification of the resources defined by a particular type. 178 | type ResTableTypeSpec struct { 179 | Header ResChunkHeader 180 | ID uint8 181 | Res0 uint8 182 | Res1 uint16 183 | EntryCount uint32 184 | } 185 | 186 | // IsResID returns whether s is ResId. 187 | func IsResID(s string) bool { 188 | return strings.HasPrefix(s, "@0x") 189 | } 190 | 191 | // ParseResID parses ResId. 192 | func ParseResID(s string) (ResID, error) { 193 | if !IsResID(s) { 194 | return 0, fmt.Errorf("androidbinary: %s is not ResID", s) 195 | } 196 | id, err := strconv.ParseUint(s[3:], 16, 32) 197 | if err != nil { 198 | return 0, err 199 | } 200 | return ResID(id), nil 201 | } 202 | 203 | func (id ResID) String() string { 204 | return fmt.Sprintf("@0x%08X", uint32(id)) 205 | } 206 | 207 | // Package returns the package index of id. 208 | func (id ResID) Package() uint32 { 209 | return uint32(id) >> 24 210 | } 211 | 212 | // Type returns the type index of id. 213 | func (id ResID) Type() int { 214 | return (int(id) >> 16) & 0xFF 215 | } 216 | 217 | // Entry returns the entry index of id. 218 | func (id ResID) Entry() int { 219 | return int(id) & 0xFFFF 220 | } 221 | 222 | // NewTableFile returns new TableFile. 223 | func NewTableFile(r io.ReaderAt) (*TableFile, error) { 224 | f := new(TableFile) 225 | sr := io.NewSectionReader(r, 0, 1<<63-1) 226 | 227 | header := new(ResTableHeader) 228 | binary.Read(sr, binary.LittleEndian, header) 229 | f.tablePackages = make(map[uint32]*TablePackage) 230 | 231 | offset := int64(header.Header.HeaderSize) 232 | for offset < int64(header.Header.Size) { 233 | chunkHeader, err := f.readChunk(sr, offset) 234 | if err != nil { 235 | return nil, err 236 | } 237 | offset += int64(chunkHeader.Size) 238 | } 239 | return f, nil 240 | } 241 | 242 | func (f *TableFile) findPackage(id uint32) *TablePackage { 243 | if f == nil { 244 | return nil 245 | } 246 | return f.tablePackages[id] 247 | } 248 | 249 | func (p *TablePackage) findEntry(typeIndex, entryIndex int, config *ResTableConfig) TableEntry { 250 | var best *TableType 251 | for _, t := range p.TableTypes { 252 | switch { 253 | case int(t.Header.ID) != typeIndex: 254 | // nothing to do 255 | case !t.Header.Config.Match(config): 256 | // nothing to do 257 | case entryIndex >= len(t.Entries): 258 | // nothing to do 259 | case t.Entries[entryIndex].Value == nil: 260 | // nothing to do 261 | case best == nil || t.Header.Config.IsBetterThan(&best.Header.Config, config): 262 | best = t 263 | } 264 | } 265 | if best == nil || entryIndex >= len(best.Entries) { 266 | return TableEntry{} 267 | } 268 | return best.Entries[entryIndex] 269 | } 270 | 271 | // GetResource returns a resrouce referenced by id. 272 | func (f *TableFile) GetResource(id ResID, config *ResTableConfig) (interface{}, error) { 273 | p := f.findPackage(id.Package()) 274 | if p == nil { 275 | return nil, fmt.Errorf("androidbinary: package 0x%02X not found", id.Package()) 276 | } 277 | e := p.findEntry(id.Type(), id.Entry(), config) 278 | v := e.Value 279 | if v == nil { 280 | return nil, fmt.Errorf("androidbinary: entry 0x%04X not found", id.Entry()) 281 | } 282 | switch v.DataType { 283 | case TypeNull: 284 | return nil, nil 285 | case TypeString: 286 | return f.GetString(ResStringPoolRef(v.Data)), nil 287 | case TypeIntDec: 288 | return v.Data, nil 289 | case TypeIntHex: 290 | return v.Data, nil 291 | case TypeIntBoolean: 292 | return v.Data != 0, nil 293 | } 294 | return v.Data, nil 295 | } 296 | 297 | // GetString returns a string referenced by ref. 298 | func (f *TableFile) GetString(ref ResStringPoolRef) string { 299 | return f.stringPool.GetString(ref) 300 | } 301 | 302 | func (f *TableFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) { 303 | sr := io.NewSectionReader(r, offset, 1<<63-1-offset) 304 | chunkHeader := &ResChunkHeader{} 305 | if _, err := sr.Seek(0, seekStart); err != nil { 306 | return nil, err 307 | } 308 | if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { 309 | return nil, err 310 | } 311 | 312 | var err error 313 | if _, err := sr.Seek(0, seekStart); err != nil { 314 | return nil, err 315 | } 316 | switch chunkHeader.Type { 317 | case ResStringPoolChunkType: 318 | f.stringPool, err = readStringPool(sr) 319 | case ResTablePackageType: 320 | var tablePackage *TablePackage 321 | tablePackage, err = readTablePackage(sr) 322 | f.tablePackages[tablePackage.Header.ID] = tablePackage 323 | } 324 | if err != nil { 325 | return nil, err 326 | } 327 | 328 | return chunkHeader, nil 329 | } 330 | 331 | func readTablePackage(sr *io.SectionReader) (*TablePackage, error) { 332 | tablePackage := new(TablePackage) 333 | header := new(ResTablePackage) 334 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 335 | return nil, err 336 | } 337 | tablePackage.Header = *header 338 | 339 | srTypes := io.NewSectionReader(sr, int64(header.TypeStrings), int64(header.Header.Size-header.TypeStrings)) 340 | if typeStrings, err := readStringPool(srTypes); err == nil { 341 | tablePackage.TypeStrings = typeStrings 342 | } else { 343 | return nil, err 344 | } 345 | 346 | srKeys := io.NewSectionReader(sr, int64(header.KeyStrings), int64(header.Header.Size-header.KeyStrings)) 347 | if keyStrings, err := readStringPool(srKeys); err == nil { 348 | tablePackage.KeyStrings = keyStrings 349 | } else { 350 | return nil, err 351 | } 352 | 353 | offset := int64(header.Header.HeaderSize) 354 | for offset < int64(header.Header.Size) { 355 | chunkHeader := &ResChunkHeader{} 356 | if _, err := sr.Seek(offset, seekStart); err != nil { 357 | return nil, err 358 | } 359 | if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil { 360 | return nil, err 361 | } 362 | 363 | var err error 364 | chunkReader := io.NewSectionReader(sr, offset, int64(chunkHeader.Size)) 365 | if _, err := sr.Seek(offset, seekStart); err != nil { 366 | return nil, err 367 | } 368 | switch chunkHeader.Type { 369 | case ResTableTypeType: 370 | var tableType *TableType 371 | tableType, err = readTableType(chunkHeader, chunkReader) 372 | tablePackage.TableTypes = append(tablePackage.TableTypes, tableType) 373 | case ResTableTypeSpecType: 374 | _, err = readTableTypeSpec(chunkReader) 375 | } 376 | if err != nil { 377 | return nil, err 378 | } 379 | offset += int64(chunkHeader.Size) 380 | } 381 | 382 | return tablePackage, nil 383 | } 384 | 385 | func readTableType(chunkHeader *ResChunkHeader, sr *io.SectionReader) (*TableType, error) { 386 | // TableType header may be omitted 387 | header := new(ResTableType) 388 | if _, err := sr.Seek(0, seekStart); err != nil { 389 | return nil, err 390 | } 391 | buf, err := newZeroFilledReader(sr, int64(chunkHeader.HeaderSize), int64(unsafe.Sizeof(*header))) 392 | if err != nil { 393 | return nil, err 394 | } 395 | if err := binary.Read(buf, binary.LittleEndian, header); err != nil { 396 | return nil, err 397 | } 398 | 399 | entryIndexes := make([]uint32, header.EntryCount) 400 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 401 | return nil, err 402 | } 403 | if err := binary.Read(sr, binary.LittleEndian, entryIndexes); err != nil { 404 | return nil, err 405 | } 406 | 407 | entries := make([]TableEntry, header.EntryCount) 408 | for i, index := range entryIndexes { 409 | if index == 0xFFFFFFFF { 410 | continue 411 | } 412 | if _, err := sr.Seek(int64(header.EntriesStart+index), seekStart); err != nil { 413 | return nil, err 414 | } 415 | var key ResTableEntry 416 | binary.Read(sr, binary.LittleEndian, &key) 417 | entries[i].Key = &key 418 | 419 | var val ResValue 420 | binary.Read(sr, binary.LittleEndian, &val) 421 | entries[i].Value = &val 422 | } 423 | return &TableType{ 424 | header, 425 | entries, 426 | }, nil 427 | } 428 | 429 | func readTableTypeSpec(sr *io.SectionReader) ([]uint32, error) { 430 | header := new(ResTableTypeSpec) 431 | if err := binary.Read(sr, binary.LittleEndian, header); err != nil { 432 | return nil, err 433 | } 434 | 435 | flags := make([]uint32, header.EntryCount) 436 | if _, err := sr.Seek(int64(header.Header.HeaderSize), seekStart); err != nil { 437 | return nil, err 438 | } 439 | if err := binary.Read(sr, binary.LittleEndian, flags); err != nil { 440 | return nil, err 441 | } 442 | return flags, nil 443 | } 444 | 445 | // IsMoreSpecificThan returns true if c is more specific than o. 446 | func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool { 447 | // nil ResTableConfig is never more specific than any ResTableConfig 448 | if c == nil { 449 | return false 450 | } 451 | if o == nil { 452 | return false 453 | } 454 | 455 | // imsi 456 | if c.Mcc != o.Mcc { 457 | if c.Mcc == 0 { 458 | return false 459 | } 460 | if o.Mnc == 0 { 461 | return true 462 | } 463 | } 464 | if c.Mnc != o.Mnc { 465 | if c.Mnc == 0 { 466 | return false 467 | } 468 | if o.Mnc == 0 { 469 | return true 470 | } 471 | } 472 | 473 | // locale 474 | if diff := c.IsLocaleMoreSpecificThan(o); diff < 0 { 475 | return false 476 | } else if diff > 0 { 477 | return true 478 | } 479 | 480 | // screen layout 481 | if c.ScreenLayout != 0 || o.ScreenLayout != 0 { 482 | if ((c.ScreenLayout ^ o.ScreenLayout) & MaskLayoutDir) != 0 { 483 | if (c.ScreenLayout & MaskLayoutDir) == 0 { 484 | return false 485 | } 486 | if (o.ScreenLayout & MaskLayoutDir) == 0 { 487 | return true 488 | } 489 | } 490 | } 491 | 492 | // smallest screen width dp 493 | if c.SmallestScreenWidthDp != 0 || o.SmallestScreenWidthDp != 0 { 494 | if c.SmallestScreenWidthDp != o.SmallestScreenWidthDp { 495 | if c.SmallestScreenWidthDp == 0 { 496 | return false 497 | } 498 | if o.SmallestScreenWidthDp == 0 { 499 | return true 500 | } 501 | } 502 | } 503 | 504 | // screen size dp 505 | if c.ScreenWidthDp != 0 || o.ScreenWidthDp != 0 || 506 | c.ScreenHeightDp != 0 || o.ScreenHeightDp != 0 { 507 | if c.ScreenWidthDp != o.ScreenWidthDp { 508 | if c.ScreenWidthDp == 0 { 509 | return false 510 | } 511 | if o.ScreenWidthDp == 0 { 512 | return true 513 | } 514 | } 515 | if c.ScreenHeightDp != o.ScreenHeightDp { 516 | if c.ScreenHeightDp == 0 { 517 | return false 518 | } 519 | if o.ScreenHeightDp == 0 { 520 | return true 521 | } 522 | } 523 | } 524 | 525 | // screen layout 526 | if c.ScreenLayout != 0 || o.ScreenLayout != 0 { 527 | if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenSize) != 0 { 528 | if (c.ScreenLayout & MaskScreenSize) == 0 { 529 | return false 530 | } 531 | if (o.ScreenLayout & MaskScreenSize) == 0 { 532 | return true 533 | } 534 | } 535 | if ((c.ScreenLayout ^ o.ScreenLayout) & MaskScreenLong) != 0 { 536 | if (c.ScreenLayout & MaskScreenLong) == 0 { 537 | return false 538 | } 539 | if (o.ScreenLayout & MaskScreenLong) == 0 { 540 | return true 541 | } 542 | } 543 | } 544 | 545 | // orientation 546 | if c.Orientation != o.Orientation { 547 | if c.Orientation == 0 { 548 | return false 549 | } 550 | if o.Orientation == 0 { 551 | return true 552 | } 553 | } 554 | 555 | // uimode 556 | if c.UIMode != 0 || o.UIMode != 0 { 557 | diff := c.UIMode ^ o.UIMode 558 | if (diff & MaskUIModeType) != 0 { 559 | if (c.UIMode & MaskUIModeType) == 0 { 560 | return false 561 | } 562 | if (o.UIMode & MaskUIModeType) == 0 { 563 | return true 564 | } 565 | } 566 | if (diff & MaskUIModeNight) != 0 { 567 | if (c.UIMode & MaskUIModeNight) == 0 { 568 | return false 569 | } 570 | if (o.UIMode & MaskUIModeNight) == 0 { 571 | return true 572 | } 573 | } 574 | } 575 | 576 | // touchscreen 577 | if c.Touchscreen != o.Touchscreen { 578 | if c.Touchscreen == 0 { 579 | return false 580 | } 581 | if o.Touchscreen == 0 { 582 | return true 583 | } 584 | } 585 | 586 | // input 587 | if c.InputFlags != 0 || o.InputFlags != 0 { 588 | myKeysHidden := c.InputFlags & MaskKeysHidden 589 | oKeysHidden := o.InputFlags & MaskKeysHidden 590 | if (myKeysHidden ^ oKeysHidden) != 0 { 591 | if myKeysHidden == 0 { 592 | return false 593 | } 594 | if oKeysHidden == 0 { 595 | return true 596 | } 597 | } 598 | myNavHidden := c.InputFlags & MaskNavHidden 599 | oNavHidden := o.InputFlags & MaskNavHidden 600 | if (myNavHidden ^ oNavHidden) != 0 { 601 | if myNavHidden == 0 { 602 | return false 603 | } 604 | if oNavHidden == 0 { 605 | return true 606 | } 607 | } 608 | } 609 | 610 | if c.Keyboard != o.Keyboard { 611 | if c.Keyboard == 0 { 612 | return false 613 | } 614 | if o.Keyboard == 0 { 615 | return true 616 | } 617 | } 618 | 619 | if c.Navigation != o.Navigation { 620 | if c.Navigation == 0 { 621 | return false 622 | } 623 | if o.Navigation == 0 { 624 | return true 625 | } 626 | } 627 | 628 | // screen size 629 | if c.ScreenWidth != 0 || o.ScreenWidth != 0 || 630 | c.ScreenHeight != 0 || o.ScreenHeight != 0 { 631 | if c.ScreenWidth != o.ScreenWidth { 632 | if c.ScreenWidth == 0 { 633 | return false 634 | } 635 | if o.ScreenWidth == 0 { 636 | return true 637 | } 638 | } 639 | if c.ScreenHeight != o.ScreenHeight { 640 | if c.ScreenHeight == 0 { 641 | return false 642 | } 643 | if o.ScreenHeight == 0 { 644 | return true 645 | } 646 | } 647 | } 648 | 649 | //version 650 | if c.SDKVersion != o.SDKVersion { 651 | if c.SDKVersion == 0 { 652 | return false 653 | } 654 | if o.SDKVersion == 0 { 655 | return true 656 | } 657 | } 658 | if c.MinorVersion != o.MinorVersion { 659 | if c.MinorVersion == 0 { 660 | return false 661 | } 662 | if o.MinorVersion == 0 { 663 | return true 664 | } 665 | } 666 | 667 | return false 668 | } 669 | 670 | // IsBetterThan returns true if c is better than o for the r configuration. 671 | func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool { 672 | if r == nil { 673 | return c.IsMoreSpecificThan(o) 674 | } 675 | 676 | // nil ResTableConfig is never better than any ResTableConfig 677 | if c == nil { 678 | return false 679 | } 680 | if o == nil { 681 | return false 682 | } 683 | 684 | // imsi 685 | if c.Mcc != 0 || c.Mnc != 0 || o.Mcc != 0 || o.Mnc != 0 { 686 | if c.Mcc != o.Mcc && r.Mcc != 0 { 687 | return c.Mcc != 0 688 | } 689 | if c.Mnc != o.Mnc && r.Mnc != 0 { 690 | return c.Mnc != 0 691 | } 692 | } 693 | 694 | // locale 695 | if c.IsLocaleBetterThan(o, r) { 696 | return true 697 | } 698 | 699 | // screen layout 700 | if c.ScreenLayout != 0 || o.ScreenLayout != 0 { 701 | myLayoutdir := c.ScreenLayout & MaskLayoutDir 702 | oLayoutdir := o.ScreenLayout & MaskLayoutDir 703 | if (myLayoutdir^oLayoutdir) != 0 && (r.ScreenLayout&MaskLayoutDir) != 0 { 704 | return myLayoutdir > oLayoutdir 705 | } 706 | } 707 | 708 | // smallest screen width dp 709 | if c.SmallestScreenWidthDp != 0 || o.SmallestScreenWidthDp != 0 { 710 | if c.SmallestScreenWidthDp != o.SmallestScreenWidthDp { 711 | return c.SmallestScreenWidthDp > o.SmallestScreenWidthDp 712 | } 713 | } 714 | 715 | // screen size dp 716 | if c.ScreenWidthDp != 0 || c.ScreenHeightDp != 0 || o.ScreenWidthDp != 0 || o.ScreenHeightDp != 0 { 717 | myDelta := 0 718 | otherDelta := 0 719 | if r.ScreenWidthDp != 0 { 720 | myDelta += int(r.ScreenWidthDp) - int(c.ScreenWidthDp) 721 | otherDelta += int(r.ScreenWidthDp) - int(o.ScreenWidthDp) 722 | } 723 | if r.ScreenHeightDp != 0 { 724 | myDelta += int(r.ScreenHeightDp) - int(c.ScreenHeightDp) 725 | otherDelta += int(r.ScreenHeightDp) - int(o.ScreenHeightDp) 726 | } 727 | if myDelta != otherDelta { 728 | return myDelta < otherDelta 729 | } 730 | } 731 | 732 | // screen layout 733 | if c.ScreenLayout != 0 || o.ScreenLayout != 0 { 734 | mySL := c.ScreenLayout & MaskScreenSize 735 | oSL := o.ScreenLayout & MaskScreenSize 736 | if (mySL^oSL) != 0 && (r.ScreenLayout&MaskScreenSize) != 0 { 737 | fixedMySL := mySL 738 | fixedOSL := oSL 739 | if (r.ScreenLayout & MaskScreenSize) >= ScreenSizeNormal { 740 | if fixedMySL == 0 { 741 | fixedMySL = ScreenSizeNormal 742 | } 743 | if fixedOSL == 0 { 744 | fixedOSL = ScreenSizeNormal 745 | } 746 | } 747 | if fixedMySL == fixedOSL { 748 | return mySL != 0 749 | } 750 | return fixedMySL > fixedOSL 751 | } 752 | 753 | if ((c.ScreenLayout^o.ScreenLayout)&MaskScreenLong) != 0 && 754 | (r.ScreenLayout&MaskScreenLong) != 0 { 755 | return (c.ScreenLayout & MaskScreenLong) != 0 756 | } 757 | } 758 | 759 | // orientation 760 | if c.Orientation != o.Orientation && r.Orientation != 0 { 761 | return c.Orientation != 0 762 | } 763 | 764 | // uimode 765 | if c.UIMode != 0 || o.UIMode != 0 { 766 | diff := c.UIMode ^ o.UIMode 767 | if (diff&MaskUIModeType) != 0 && (r.UIMode&MaskUIModeType) != 0 { 768 | return (c.UIMode & MaskUIModeType) != 0 769 | } 770 | if (diff&MaskUIModeNight) != 0 && (r.UIMode&MaskUIModeNight) != 0 { 771 | return (c.UIMode & MaskUIModeNight) != 0 772 | } 773 | } 774 | 775 | // screen type 776 | if c.Density != o.Density { 777 | h := int(c.Density) 778 | if h == 0 { 779 | h = 160 780 | } 781 | l := int(o.Density) 782 | if l == 0 { 783 | l = 160 784 | } 785 | blmBigger := true 786 | if l > h { 787 | h, l = l, h 788 | blmBigger = false 789 | } 790 | 791 | reqValue := int(r.Density) 792 | if reqValue == 0 { 793 | reqValue = 160 794 | } 795 | if reqValue >= h { 796 | return blmBigger 797 | } 798 | if l >= reqValue { 799 | return !blmBigger 800 | } 801 | if (2*l-reqValue)*h > reqValue*reqValue { 802 | return !blmBigger 803 | } 804 | return blmBigger 805 | } 806 | if c.Touchscreen != o.Touchscreen && r.Touchscreen != 0 { 807 | return c.Touchscreen != 0 808 | } 809 | 810 | // input 811 | if c.InputFlags != 0 || o.InputFlags != 0 { 812 | myKeysHidden := c.InputFlags & MaskKeysHidden 813 | oKeysHidden := o.InputFlags & MaskKeysHidden 814 | reqKeysHidden := r.InputFlags & MaskKeysHidden 815 | if myKeysHidden != oKeysHidden && reqKeysHidden != 0 { 816 | switch { 817 | case myKeysHidden == 0: 818 | return false 819 | case oKeysHidden == 0: 820 | return true 821 | case reqKeysHidden == myKeysHidden: 822 | return true 823 | case reqKeysHidden == oKeysHidden: 824 | return false 825 | } 826 | } 827 | myNavHidden := c.InputFlags & MaskNavHidden 828 | oNavHidden := o.InputFlags & MaskNavHidden 829 | reqNavHidden := r.InputFlags & MaskNavHidden 830 | if myNavHidden != oNavHidden && reqNavHidden != 0 { 831 | switch { 832 | case myNavHidden == 0: 833 | return false 834 | case oNavHidden == 0: 835 | return true 836 | } 837 | } 838 | } 839 | if c.Keyboard != o.Keyboard && r.Keyboard != 0 { 840 | return c.Keyboard != 0 841 | } 842 | if c.Navigation != o.Navigation && r.Navigation != 0 { 843 | return c.Navigation != 0 844 | } 845 | 846 | // screen size 847 | if c.ScreenWidth != 0 || c.ScreenHeight != 0 || o.ScreenWidth != 0 || o.ScreenHeight != 0 { 848 | myDelta := 0 849 | otherDelta := 0 850 | if r.ScreenWidth != 0 { 851 | myDelta += int(r.ScreenWidth) - int(c.ScreenWidth) 852 | otherDelta += int(r.ScreenWidth) - int(o.ScreenWidth) 853 | } 854 | if r.ScreenHeight != 0 { 855 | myDelta += int(r.ScreenHeight) - int(c.ScreenHeight) 856 | otherDelta += int(r.ScreenHeight) - int(o.ScreenHeight) 857 | } 858 | if myDelta != otherDelta { 859 | return myDelta < otherDelta 860 | } 861 | } 862 | 863 | // version 864 | if c.SDKVersion != 0 || o.MinorVersion != 0 { 865 | if c.SDKVersion != o.SDKVersion && r.SDKVersion != 0 { 866 | return c.SDKVersion > o.SDKVersion 867 | } 868 | if c.MinorVersion != o.MinorVersion && r.MinorVersion != 0 { 869 | return c.MinorVersion != 0 870 | } 871 | } 872 | 873 | return false 874 | } 875 | 876 | // IsLocaleMoreSpecificThan a positive integer if this config is more specific than o, 877 | // a negative integer if |o| is more specific 878 | // and 0 if they're equally specific. 879 | func (c *ResTableConfig) IsLocaleMoreSpecificThan(o *ResTableConfig) int { 880 | if (c.Language != [2]uint8{} || c.Country != [2]uint8{}) || (o.Language != [2]uint8{} || o.Country != [2]uint8{}) { 881 | if c.Language != o.Language { 882 | if c.Language == [2]uint8{} { 883 | return -1 884 | } 885 | if o.Language == [2]uint8{} { 886 | return 1 887 | } 888 | } 889 | 890 | if c.Country != o.Country { 891 | if c.Country == [2]uint8{} { 892 | return -1 893 | } 894 | if o.Country == [2]uint8{} { 895 | return 1 896 | } 897 | } 898 | } 899 | return 0 900 | } 901 | 902 | // IsLocaleBetterThan returns true if c is a better locale match than o for the r configuration. 903 | func (c *ResTableConfig) IsLocaleBetterThan(o *ResTableConfig, r *ResTableConfig) bool { 904 | if r.Language == [2]uint8{} && r.Country == [2]uint8{} { 905 | // The request doesn't have a locale, so no resource is better 906 | // than the other. 907 | return false 908 | } 909 | 910 | if c.Language == [2]uint8{} && c.Country == [2]uint8{} && o.Language == [2]uint8{} && o.Country == [2]uint8{} { 911 | // The locales parts of both resources are empty, so no one is better 912 | // than the other. 913 | return false 914 | } 915 | 916 | if c.Language != o.Language { 917 | // The languages of the two resources are not the same. 918 | 919 | // the US English resource have traditionally lived for most apps. 920 | if r.Language == [2]uint8{'e', 'n'} { 921 | if r.Country == [2]uint8{'U', 'S'} { 922 | if c.Language != [2]uint8{} { 923 | return c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'} 924 | } 925 | return !(c.Country == [2]uint8{} || c.Country == [2]uint8{'U', 'S'}) 926 | } 927 | } 928 | return c.Language != [2]uint8{} 929 | } 930 | 931 | if c.Country != o.Country { 932 | return c.Country != [2]uint8{} 933 | } 934 | 935 | return false 936 | } 937 | 938 | // Match returns true if c can be considered a match for the parameters in settings. 939 | func (c *ResTableConfig) Match(settings *ResTableConfig) bool { 940 | // nil ResTableConfig always matches. 941 | if settings == nil { 942 | return true 943 | } else if c == nil { 944 | return *settings == ResTableConfig{} 945 | } 946 | 947 | // match imsi 948 | if settings.Mcc == 0 { 949 | if c.Mcc != 0 { 950 | return false 951 | } 952 | } else { 953 | if c.Mcc != 0 && c.Mcc != settings.Mcc { 954 | return false 955 | } 956 | } 957 | if settings.Mnc == 0 { 958 | if c.Mnc != 0 { 959 | return false 960 | } 961 | } else { 962 | if c.Mnc != 0 && c.Mnc != settings.Mnc { 963 | return false 964 | } 965 | } 966 | 967 | // match locale 968 | if c.Language != [2]uint8{0, 0} { 969 | // Don't consider country and variants when deciding matches. 970 | // If two configs differ only in their country and variant, 971 | // they can be weeded out in the isMoreSpecificThan test. 972 | if c.Language != settings.Language { 973 | return false 974 | } 975 | 976 | if c.Country != [2]uint8{0, 0} { 977 | if c.Country != settings.Country { 978 | return false 979 | } 980 | } 981 | } 982 | 983 | // screen layout 984 | layoutDir := c.ScreenLayout & MaskLayoutDir 985 | setLayoutDir := settings.ScreenLayout & MaskLayoutDir 986 | if layoutDir != 0 && layoutDir != setLayoutDir { 987 | return false 988 | } 989 | 990 | screenSize := c.ScreenLayout & MaskScreenSize 991 | setScreenSize := settings.ScreenLayout & MaskScreenSize 992 | if screenSize != 0 && screenSize > setScreenSize { 993 | return false 994 | } 995 | 996 | screenLong := c.ScreenLayout & MaskScreenLong 997 | setScreenLong := settings.ScreenLayout & MaskScreenLong 998 | if screenLong != 0 && screenLong != setScreenLong { 999 | return false 1000 | } 1001 | 1002 | // ui mode 1003 | uiModeType := c.UIMode & MaskUIModeType 1004 | setUIModeType := settings.UIMode & MaskUIModeType 1005 | if uiModeType != 0 && uiModeType != setUIModeType { 1006 | return false 1007 | } 1008 | 1009 | uiModeNight := c.UIMode & MaskUIModeNight 1010 | setUIModeNight := settings.UIMode & MaskUIModeNight 1011 | if uiModeNight != 0 && uiModeNight != setUIModeNight { 1012 | return false 1013 | } 1014 | 1015 | // smallest screen width dp 1016 | if c.SmallestScreenWidthDp != 0 && 1017 | c.SmallestScreenWidthDp > settings.SmallestScreenWidthDp { 1018 | return false 1019 | } 1020 | 1021 | // screen size dp 1022 | if c.ScreenWidthDp != 0 && 1023 | c.ScreenWidthDp > settings.ScreenWidthDp { 1024 | return false 1025 | } 1026 | if c.ScreenHeightDp != 0 && 1027 | c.ScreenHeightDp > settings.ScreenHeightDp { 1028 | return false 1029 | } 1030 | 1031 | // screen type 1032 | if c.Orientation != 0 && c.Orientation != settings.Orientation { 1033 | return false 1034 | } 1035 | if c.Touchscreen != 0 && c.Touchscreen != settings.Touchscreen { 1036 | return false 1037 | } 1038 | 1039 | // input 1040 | if c.InputFlags != 0 { 1041 | myKeysHidden := c.InputFlags & MaskKeysHidden 1042 | oKeysHidden := settings.InputFlags & MaskKeysHidden 1043 | if myKeysHidden != 0 && myKeysHidden != oKeysHidden { 1044 | if myKeysHidden != KeysHiddenNo || oKeysHidden != KeysHiddenSoft { 1045 | return false 1046 | } 1047 | } 1048 | myNavHidden := c.InputFlags & MaskNavHidden 1049 | oNavHidden := settings.InputFlags & MaskNavHidden 1050 | if myNavHidden != 0 && myNavHidden != oNavHidden { 1051 | return false 1052 | } 1053 | } 1054 | if c.Keyboard != 0 && c.Keyboard != settings.Keyboard { 1055 | return false 1056 | } 1057 | if c.Navigation != 0 && c.Navigation != settings.Navigation { 1058 | return false 1059 | } 1060 | 1061 | // screen size 1062 | if c.ScreenWidth != 0 && 1063 | c.ScreenWidth > settings.ScreenWidth { 1064 | return false 1065 | } 1066 | if c.ScreenHeight != 0 && 1067 | c.ScreenHeight > settings.ScreenHeight { 1068 | return false 1069 | } 1070 | 1071 | // version 1072 | if settings.SDKVersion != 0 && c.SDKVersion != 0 && 1073 | c.SDKVersion > settings.SDKVersion { 1074 | return false 1075 | } 1076 | if settings.MinorVersion != 0 && c.MinorVersion != 0 && 1077 | c.MinorVersion != settings.MinorVersion { 1078 | return false 1079 | } 1080 | 1081 | return true 1082 | } 1083 | 1084 | // Locale returns the locale of the configuration. 1085 | func (c *ResTableConfig) Locale() string { 1086 | if c.Language[0] == 0 { 1087 | return "" 1088 | } 1089 | if c.Country[0] == 0 { 1090 | return fmt.Sprintf("%c%c", c.Language[0], c.Language[1]) 1091 | } 1092 | return fmt.Sprintf("%c%c-%c%c", c.Language[0], c.Language[1], c.Country[0], c.Country[1]) 1093 | } 1094 | --------------------------------------------------------------------------------