├── .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 | [](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 [](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 | [](https://travis-ci.org/shogo82148/androidbinary)
5 | [](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 [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](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("")
73 | p.WriteString(n)
74 | p.WriteByte('>')
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("")
94 | p.WriteString(n)
95 | p.WriteByte('>')
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, "%s>", 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 |
--------------------------------------------------------------------------------