base-golang/pkg/android_binary/common.go
2024-07-23 10:23:43 +08:00

259 lines
6.2 KiB
Go

package android_binary
import (
"bytes"
"encoding/binary"
"io"
"unicode/utf16"
)
// ChunkType is a type of a resource chunk.
type ChunkType uint16
// Chunk types.
const (
ResNullChunkType ChunkType = 0x0000
ResStringPoolChunkType ChunkType = 0x0001
ResTableChunkType ChunkType = 0x0002
ResXMLChunkType ChunkType = 0x0003
// Chunk types in RES_XML_TYPE
ResXMLFirstChunkType ChunkType = 0x0100
ResXMLStartNamespaceType ChunkType = 0x0100
ResXMLEndNamespaceType ChunkType = 0x0101
ResXMLStartElementType ChunkType = 0x0102
ResXMLEndElementType ChunkType = 0x0103
ResXMLCDataType ChunkType = 0x0104
ResXMLLastChunkType ChunkType = 0x017f
// This contains a uint32_t array mapping strings in the string
// pool back to resource identifiers. It is optional.
ResXMLResourceMapType ChunkType = 0x0180
// Chunk types in RES_TABLE_TYPE
ResTablePackageType ChunkType = 0x0200
ResTableTypeType ChunkType = 0x0201
ResTableTypeSpecType ChunkType = 0x0202
)
// ResChunkHeader is a header of a resource chunk.
type ResChunkHeader struct {
Type ChunkType
HeaderSize uint16
Size uint32
}
// Flags are flags for string pool header.
type Flags uint32
// the values of Flags.
const (
SortedFlag Flags = 1 << 0
UTF8Flag Flags = 1 << 8
)
// ResStringPoolHeader is a chunk header of string pool.
type ResStringPoolHeader struct {
Header ResChunkHeader
StringCount uint32
StyleCount uint32
Flags Flags
StringStart uint32
StylesStart uint32
}
// ResStringPoolSpan is a span of style information associated with
// a string in the pool.
type ResStringPoolSpan struct {
FirstChar, LastChar uint32
}
// ResStringPool is a string pool resource.
type ResStringPool struct {
Header ResStringPoolHeader
Strings []string
Styles []ResStringPoolSpan
}
// NilResStringPoolRef is nil reference for string pool.
const NilResStringPoolRef = ResStringPoolRef(0xFFFFFFFF)
// ResStringPoolRef is a type representing a reference to a string.
type ResStringPoolRef uint32
// DataType is a type of the data value.
type DataType uint8
// The constants for DataType
const (
TypeNull DataType = 0x00
TypeReference DataType = 0x01
TypeAttribute DataType = 0x02
TypeString DataType = 0x03
TypeFloat DataType = 0x04
TypeDemention DataType = 0x05
TypeFraction DataType = 0x06
TypeFirstInt DataType = 0x10
TypeIntDec DataType = 0x10
TypeIntHex DataType = 0x11
TypeIntBoolean DataType = 0x12
TypeFirstColorInt DataType = 0x1c
TypeIntColorARGB8 DataType = 0x1c
TypeIntColorRGB8 DataType = 0x1d
TypeIntColorARGB4 DataType = 0x1e
TypeIntColorRGB4 DataType = 0x1f
TypeLastColorInt DataType = 0x1f
TypeLastInt DataType = 0x1f
)
// ResValue is a representation of a value in a resource
type ResValue struct {
Size uint16
Res0 uint8
DataType DataType
Data uint32
}
// GetString returns a string referenced by ref.
func (pool *ResStringPool) GetString(ref ResStringPoolRef) string {
return pool.Strings[int(ref)]
}
func readStringPool(sr *io.SectionReader) (*ResStringPool, error) {
sp := new(ResStringPool)
if err := binary.Read(sr, binary.LittleEndian, &sp.Header); err != nil {
return nil, err
}
stringStarts := make([]uint32, sp.Header.StringCount)
if err := binary.Read(sr, binary.LittleEndian, stringStarts); err != nil {
return nil, err
}
styleStarts := make([]uint32, sp.Header.StyleCount)
if err := binary.Read(sr, binary.LittleEndian, styleStarts); err != nil {
return nil, err
}
sp.Strings = make([]string, sp.Header.StringCount)
for i, start := range stringStarts {
var str string
var err error
if _, err := sr.Seek(int64(sp.Header.StringStart+start), io.SeekStart); err != nil {
return nil, err
}
if (sp.Header.Flags & UTF8Flag) == 0 {
str, err = readUTF16(sr)
} else {
str, err = readUTF8(sr)
}
if err != nil {
return nil, err
}
sp.Strings[i] = str
}
sp.Styles = make([]ResStringPoolSpan, sp.Header.StyleCount)
for i, start := range styleStarts {
if _, err := sr.Seek(int64(sp.Header.StylesStart+start), io.SeekStart); err != nil {
return nil, err
}
if err := binary.Read(sr, binary.LittleEndian, &sp.Styles[i]); err != nil {
return nil, err
}
}
return sp, nil
}
func readUTF16(sr *io.SectionReader) (string, error) {
// read length of string
size, err := readUTF16length(sr)
if err != nil {
return "", err
}
// read string value
buf := make([]uint16, size)
if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
return "", err
}
return string(utf16.Decode(buf)), nil
}
func readUTF16length(sr *io.SectionReader) (int, error) {
var size int
var first, second uint16
if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
return 0, err
}
if (first & 0x8000) != 0 {
if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
return 0, err
}
size = (int(first&0x7FFF) << 16) + int(second)
} else {
size = int(first)
}
return size, nil
}
func readUTF8(sr *io.SectionReader) (string, error) {
// skip utf16 length
_, err := readUTF8length(sr)
if err != nil {
return "", err
}
// read lenth of string
size, err := readUTF8length(sr)
if err != nil {
return "", err
}
buf := make([]uint8, size)
if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
return "", err
}
return string(buf), nil
}
func readUTF8length(sr *io.SectionReader) (int, error) {
var size int
var first, second uint8
if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
return 0, err
}
if (first & 0x80) != 0 {
if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
return 0, err
}
size = (int(first&0x7F) << 8) + int(second)
} else {
size = int(first)
}
return size, nil
}
func newZeroFilledReader(r io.Reader, actual int64, expected int64) (io.Reader, error) {
if actual >= expected {
// no need to fill
return r, nil
}
// read `actual' bytes from r, and
buf := new(bytes.Buffer)
if _, err := io.CopyN(buf, r, actual); err != nil {
return nil, err
}
// fill zero until `expected' bytes
for i := actual; i < expected; i++ {
if err := buf.WriteByte(0x00); err != nil {
return nil, err
}
}
return buf, nil
}