class Android::Resource::ResStringPool

Constants

SORTED_FLAG
UTF8_FLAG

Attributes

strings[R]

Private Class Methods

utf16_len(data) click to toggle source

@note refer to /frameworks/base/libs/androidfw/ResourceTypes.cpp

static inline size_t decodeLength(const char16_t** str)

@param [String] data parse target @return[Integer, Integer] string length and parsed length

# File lib/android/resource.rb, line 186
def self.utf16_len(data)
  first, second = data.unpack('vv')
  if (first & 0x8000) != 0
    return (((first & 0x7FFF) << 16) + second), 4
  else
    return first, 2
  end
end
utf16_str_len(str) click to toggle source
# File lib/android/resource.rb, line 195
def self.utf16_str_len(str)
  [str.size].pack('v')
end
utf8_len(data) click to toggle source

@note refer to /frameworks/base/libs/androidfw/ResourceTypes.cpp

static inline size_t decodeLength(const uint8_t** str)

@param [String] data parse target @return[Integer, Integer] string length and parsed length

# File lib/android/resource.rb, line 174
def self.utf8_len(data)
  first, second = data.unpack('CC')
  if (first & 0x80) != 0
    return (((first & 0x7F) << 8) + second), 2
  else
    return first, 1
  end
end

Public Instance Methods

add_string(str) click to toggle source
# File lib/android/resource.rb, line 62
def add_string(str)
  raise UnsupportedStringFormatError, 'Adding strings in UTF-8 format is not supported yet' if utf8_string_format?

  @data_io = StringIO.new(@data, 'r+b')

  increment_string_count
  bytes_added = insert_string(str)
  increment_string_start_offset
  update_chunk_size(bytes_added)

  @data_io.close
  [@string_count - 1, bytes_added]
end
utf8_string_format?() click to toggle source
# File lib/android/resource.rb, line 76
def utf8_string_format?
  (@flags & UTF8_FLAG != 0)
end

Private Instance Methods

increment_string_count() click to toggle source
# File lib/android/resource.rb, line 107
def increment_string_count
  string_count_offset = @offset + 8
  @string_count = @data[string_count_offset, 4].unpack1('V') + 1
  @data_io.pos = string_count_offset
  @data_io.write([@string_count].pack('V'))
end
increment_string_start_offset() click to toggle source
# File lib/android/resource.rb, line 154
def increment_string_start_offset
  string_start_offset = @offset + 20
  @string_start = @data[string_start_offset, 4].unpack1('V') + 4

  @data_io.pos = string_start_offset
  @data_io.write([@string_start].pack('V'))
end
insert_string(str) click to toggle source

Inserts the string into the string data section and updates the string index. @return [Integer] number of bytes added to the string pool chunk

# File lib/android/resource.rb, line 116
def insert_string(str)
  bytes = str.codepoints << 0
  # To keep the alignment we need to pad the new string we're inserting.
  # In total, we're adding the string bytes + 2 bytes string length + 4 bytes string index.
  padding = (4 - (bytes.size * 2 + 2 + 4) % 4) % 4
  padding_bytes = [0] * padding
  next_string_offset = new_string_offset

  string_bytes = ResStringPool.utf16_str_len(str.codepoints) + bytes.pack('v*') + padding_bytes.pack('C*')

  # Write string data into the string data section.
  @data.insert(next_string_offset, string_bytes)
  # Insert new string index entry. The offset needs to be relative to the start of the string data section.
  @data.insert(last_string_index_offset + 4, [next_string_offset - (@offset + @string_start)].pack('V'))

  # We added the bytes of the string itself + a new string index entry
  string_bytes.size + 4
end
last_string_index_offset() click to toggle source
# File lib/android/resource.rb, line 135
def last_string_index_offset
  # The last entry in the string index section is the 4 bytes right before the start
  # of the string-data section (string_start).
  @offset + @string_start - 4
end
new_string_offset() click to toggle source

Calculates the offset at which to insert new string data. @return [Integer] offset of the end of the current string data section

# File lib/android/resource.rb, line 143
def new_string_offset
  last_string_index = @data[last_string_index_offset, 4].unpack1('V')
  offset = @offset + @string_start + last_string_index

  u16len, o16 = ResStringPool.utf16_len(@data[offset, 4])
  # To insert a new string at the end of the string section, we need to start at the current
  # last string entry, and add o16 (number of length bytes), u16len * 2(number of string bytes),
  # and 2 bytes for the terminating null-bytes.
  offset + o16 + u16len * 2 + 2
end
parse() click to toggle source
Calls superclass method Android::Resource::ChunkHeader#parse
# File lib/android/resource.rb, line 81
def parse
  super
  @string_count = read_int32
  @style_count = read_int32
  @flags = read_int32
  @string_start = read_int32
  @style_start = read_int32
  @strings = []
  @string_count.times do
    offset = @offset + @string_start + read_int32
    if utf8_string_format?
      # read length twice(utf16 length and utf8 length)
      #  const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
      u16len, o16 = ResStringPool.utf8_len(@data[offset, 2])
      u8len, o8 = ResStringPool.utf8_len(@data[offset+o16, 2])
      str = @data[offset+o16+o8, u8len]
      @strings << str.force_encoding(Encoding::UTF_8)
    else
      u16len, o16 = ResStringPool.utf16_len(@data[offset, 4])
      str = @data[offset+o16, u16len*2]
      str.force_encoding(Encoding::UTF_16LE)
      @strings << str.encode(Encoding::UTF_8)
    end
  end
end
update_chunk_size(bytes_added) click to toggle source
# File lib/android/resource.rb, line 162
def update_chunk_size(bytes_added)
  size_offset = @offset + 4
  @size = @data[size_offset, 4].unpack1('V') + bytes_added

  @data_io.pos = size_offset
  @data_io.write([@size].pack('V'))
end