#![allow(dead_code)]
use crate::{
chunks::{
chunk_header::ChunkHeader,
chunk_types::ChunkType,
},
errors::AxmlError
};
use std::io::{
Read,
Cursor,
};
use byteorder::{
LittleEndian,
ReadBytesExt
};
#[derive(Debug)]
pub struct StringPool {
header: ChunkHeader,
string_count: u32,
style_count: u32,
is_sorted: bool,
is_utf8: bool,
strings_start: u32,
styles_start: u32,
strings_offsets: Vec<u32>,
styles_offsets: Vec<u32>,
strings: Vec<String>,
styles: Vec<StringPoolSpan>,
}
impl StringPool {
pub fn from_buff(axml_buff: &mut Cursor<Vec<u8>>,
global_strings: &mut Vec<String>) -> Result<Self, AxmlError> {
let initial_offset = axml_buff.position() - 2;
axml_buff.set_position(initial_offset);
let initial_offset = initial_offset as u32;
let header = ChunkHeader::from_buff(axml_buff, ChunkType::ResStringPoolType)?;
let string_count = axml_buff.read_u32::<LittleEndian>()?;
let style_count = axml_buff.read_u32::<LittleEndian>()?;
let flags = axml_buff.read_u32::<LittleEndian>()?;
let is_sorted = (flags & (1<<0)) != 0;
let is_utf8 = (flags & (1<<8)) != 0;
let strings_start = axml_buff.read_u32::<LittleEndian>()?;
let styles_start = axml_buff.read_u32::<LittleEndian>()?;
let mut strings_offsets = Vec::new();
for _ in 0..string_count {
let offset = axml_buff.read_u32::<LittleEndian>()?;
strings_offsets.push(offset);
}
let mut styles_offsets = Vec::new();
for _ in 0..style_count {
let offset = axml_buff.read_u32::<LittleEndian>()?;
styles_offsets.push(offset);
}
for offset in strings_offsets.iter() {
let current_start = (initial_offset + strings_start + offset) as u64;
axml_buff.set_position(current_start);
let decoded_string: String;
if is_utf8 {
let mut first_byte_char_count = axml_buff.read_u8()? as u16;
let _utf8_char_count = if (first_byte_char_count & 0x80) != 0 {
first_byte_char_count &= 0x7F; (first_byte_char_count << 8) | (axml_buff.read_u8()? as u16)
} else {
first_byte_char_count
};
let mut first_byte_byte_len = axml_buff.read_u8()? as u16;
let byte_len: u16 = if (first_byte_byte_len & 0x80) != 0 {
first_byte_byte_len &= 0x7F; (first_byte_byte_len << 8) | (axml_buff.read_u8()? as u16)
} else {
first_byte_byte_len
};
let mut str_buff = Vec::with_capacity(byte_len as usize);
let mut chunk = axml_buff.take(byte_len as u64);
chunk.read_to_end(&mut str_buff)?;
decoded_string = String::from_utf8(str_buff)?;
axml_buff.read_u8()?; } else { let char_count = axml_buff.read_u16::<LittleEndian>()?; let actual_decoded_string = if char_count > 0 {
let mut str_chars = Vec::with_capacity(char_count as usize);
for _ in 0..char_count {
str_chars.push(axml_buff.read_u16::<LittleEndian>()?);
}
std::char::decode_utf16(str_chars.into_iter())
.collect::<Result<String, _>>()?
} else {
String::new()
};
axml_buff.read_u16::<LittleEndian>()?; decoded_string = actual_decoded_string;
}
global_strings.push(decoded_string);
}
let mut styles = Vec::new();
for offset in styles_offsets.iter() {
let current_start = (initial_offset + strings_start + offset) as u64;
axml_buff.set_position(current_start);
let string_pool_ref = StringPoolRef {
index: axml_buff.read_u32::<LittleEndian>()?
};
let first_char = axml_buff.read_u32::<LittleEndian>()?;
let last_char = axml_buff.read_u32::<LittleEndian>()?;
styles.push(StringPoolSpan {
name: string_pool_ref,
first_char,
last_char
});
}
Ok(StringPool {
header,
string_count,
style_count,
is_sorted,
is_utf8,
strings_start,
styles_start,
strings_offsets,
styles_offsets,
strings: global_strings.to_vec(),
styles
})
}
}
#[derive(Debug)]
struct StringPoolRef {
index: u32,
}
#[derive(Debug)]
struct StringPoolSpan {
name: StringPoolRef,
first_char: u32,
last_char: u32,
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{ Cursor, Write };
use byteorder::{LittleEndian, WriteBytesExt};
fn create_test_buffer() -> Cursor<Vec<u8>> {
let mut buf = Vec::new();
buf.write_u16::<LittleEndian>(0x0001).unwrap(); buf.write_u16::<LittleEndian>(8).unwrap(); buf.write_u32::<LittleEndian>(128).unwrap();
buf.write_u32::<LittleEndian>(2).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(1).unwrap(); buf.write_u32::<LittleEndian>(36).unwrap(); buf.write_u32::<LittleEndian>(20).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(14).unwrap();
buf.write_u16::<LittleEndian>(5).unwrap(); buf.write_u16::<LittleEndian>(0x0048).unwrap(); buf.write_u16::<LittleEndian>(0x0065).unwrap(); buf.write_u16::<LittleEndian>(0x006C).unwrap(); buf.write_u16::<LittleEndian>(0x006C).unwrap(); buf.write_u16::<LittleEndian>(0x006F).unwrap(); buf.write_u16::<LittleEndian>(0x0000).unwrap();
buf.write_u16::<LittleEndian>(5).unwrap(); buf.write_u16::<LittleEndian>(0x0057).unwrap(); buf.write_u16::<LittleEndian>(0x006F).unwrap(); buf.write_u16::<LittleEndian>(0x0072).unwrap(); buf.write_u16::<LittleEndian>(0x006C).unwrap(); buf.write_u16::<LittleEndian>(0x0064).unwrap(); buf.write_u16::<LittleEndian>(0x0000).unwrap();
Cursor::new(buf)
}
#[test]
fn test_string_pool_parse_utf16() {
let mut buffer = create_test_buffer();
buffer.read_u16::<LittleEndian>().unwrap();
let mut global_strings = Vec::new();
let string_pool = StringPool::from_buff(&mut buffer, &mut global_strings).unwrap();
assert_eq!(string_pool.strings.len(), 2);
assert_eq!(string_pool.strings[0], "Hello");
assert_eq!(string_pool.strings[1], "World");
}
#[test]
fn test_string_pool_flags() {
let mut buffer = create_test_buffer();
buffer.read_u16::<LittleEndian>().unwrap();
let mut global_strings = Vec::new();
let string_pool = StringPool::from_buff(&mut buffer, &mut global_strings).unwrap();
assert!(string_pool.is_sorted);
assert!(!string_pool.is_utf8);
}
#[test]
fn test_empty_pool() {
let mut buf = Vec::new();
buf.write_u16::<LittleEndian>(0x0001).unwrap(); buf.write_u16::<LittleEndian>(8).unwrap(); buf.write_u32::<LittleEndian>(128).unwrap();
buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(32).unwrap(); buf.write_u32::<LittleEndian>(20).unwrap();
let mut buffer = Cursor::new(buf);
buffer.read_u16::<LittleEndian>().unwrap();
let mut global_strings = Vec::new();
let string_pool = StringPool::from_buff(&mut buffer, &mut global_strings).unwrap();
assert_eq!(string_pool.strings.len(), 0);
}
#[test]
fn test_utf8_string_parsing() {
let mut buf = Vec::new();
buf.write_u16::<LittleEndian>(0x0001).unwrap(); buf.write_u16::<LittleEndian>(8).unwrap(); buf.write_u32::<LittleEndian>(128).unwrap();
buf.write_u32::<LittleEndian>(1).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap(); buf.write_u32::<LittleEndian>(256).unwrap(); buf.write_u32::<LittleEndian>(32).unwrap(); buf.write_u32::<LittleEndian>(20).unwrap(); buf.write_u32::<LittleEndian>(0).unwrap();
buf.write_u8(0x05).unwrap(); buf.write_u8(0x05).unwrap(); buf.write_all(b"Hello").unwrap(); buf.write_u8(0x00).unwrap();
let mut buffer = Cursor::new(buf);
buffer.read_u16::<LittleEndian>().unwrap();
let mut global_strings = Vec::new();
let string_pool = StringPool::from_buff(&mut buffer, &mut global_strings).unwrap();
assert_eq!(string_pool.strings.len(), 1);
assert_eq!(string_pool.strings[0], "Hello");
}
#[test]
fn test_long_utf8_string_parsing() {
let mut buf = Vec::new();
let long_string = "A".repeat(150);
let string_char_count = 150u16; let string_byte_len = 150u16;
let chunk_type_res_string_pool: u16 = 0x0001;
let string_count_val: u32 = 1;
let style_count_val: u32 = 0;
let flags_val: u32 = 256;
let res_string_pool_specific_fields_size: u32 = 5 * 4; let string_offsets_array_size: u32 = string_count_val * 4;
let single_string_entry_size: u32 = 2 + 2 + string_byte_len as u32 + 1;
let styles_start_val: u32 = 0;
let chunk_data_size_val: u32 = res_string_pool_specific_fields_size + string_offsets_array_size + single_string_entry_size;
buf.write_u16::<LittleEndian>(chunk_type_res_string_pool).unwrap(); buf.write_u16::<LittleEndian>(8).unwrap(); buf.write_u32::<LittleEndian>(chunk_data_size_val).unwrap();
buf.write_u32::<LittleEndian>(string_count_val).unwrap(); buf.write_u32::<LittleEndian>(style_count_val).unwrap(); buf.write_u32::<LittleEndian>(flags_val).unwrap(); let strings_start_field_val: u32 = 8 + res_string_pool_specific_fields_size + string_offsets_array_size; buf.write_u32::<LittleEndian>(strings_start_field_val).unwrap(); buf.write_u32::<LittleEndian>(styles_start_val).unwrap();
buf.write_u32::<LittleEndian>(0).unwrap();
buf.write_u8(0x80 | ((string_char_count >> 8) & 0x7F) as u8).unwrap();
buf.write_u8((string_char_count & 0xFF) as u8).unwrap();
buf.write_u8(0x80 | ((string_byte_len >> 8) & 0x7F) as u8).unwrap();
buf.write_u8((string_byte_len & 0xFF) as u8).unwrap();
buf.write_all(long_string.as_bytes()).unwrap();
buf.write_u8(0x00).unwrap();
let mut buffer = Cursor::new(buf);
buffer.read_u16::<LittleEndian>().unwrap();
let mut global_strings = Vec::new();
let string_pool = StringPool::from_buff(&mut buffer, &mut global_strings).unwrap();
assert_eq!(string_pool.strings.len(), 1);
assert_eq!(string_pool.strings[0], long_string);
assert_eq!(string_pool.strings[0].len(), 150);
assert_eq!(string_pool.string_count, 1);
assert!(string_pool.is_utf8);
}
}