use std::{
fmt::Display,
io::{Cursor, Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
};
use crate::{
align,
defs::{HeaderSizeInstance, HeaderSizeStatic, ResChunk, ResTableRef, ResType, ResTypeValue},
res_value::ResValue,
stream::{
NewResultCtx, Readable, ReadableNoOptions, StreamError, StreamResult, VecReadable,
VecWritable, Writeable, WriteableNoOptions,
},
string_pool::{ResStringPoolRef, StringPoolHandler},
traits::Mergeable,
};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ResTableTypeSpec {
pub id: u8,
pub types_count: u16,
pub config_masks: Vec<u32>,
}
impl Readable for ResTableTypeSpec {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let id = u8::read_no_opts(reader).add_context(|| "read id for ResTableTypeSpec")?;
let res0 = u8::read_no_opts(reader).add_context(|| "read res0 for ResTableTypeSpec")?;
if res0 != 0 {
return Err(StreamError::new_string_context(
format!("invalid res0 {res0}, expected 0"),
reader.stream_position()?,
"validate res0 for ResTableTypeSpec",
));
}
let types_count =
u16::read_no_opts(reader).add_context(|| "read types_count for ResTableTypeSpec")?;
let entry_count =
u32::read_no_opts(reader).add_context(|| "read entry_count for ResTableTypeSpec")?;
let config_masks = <Vec<u32>>::read_vec(reader, entry_count as usize)
.add_context(|| "read config_masks for ResTableTypeSpec")?;
Ok(Self {
id,
types_count,
config_masks,
})
}
}
impl Writeable for ResTableTypeSpec {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.id
.write_no_opts(writer)
.add_context(|| "write id for ResTableTypeSpec")?;
let res0: u8 = 0;
res0.write_no_opts(writer)
.add_context(|| "write res0 for ResTableTypeSpec")?;
self.types_count
.write_no_opts(writer)
.add_context(|| "write types_count for ResTableTypeSpec")?;
let entry_count: u32 = self.config_masks.len() as u32;
entry_count
.write_no_opts(writer)
.add_context(|| "write entry_count for ResTableTypeSpec")?;
self.config_masks
.write_vec(writer)
.add_context(|| "write config_masks for ResTableTypeSpec")
}
}
impl HeaderSizeStatic for ResTableTypeSpec {
fn header_size() -> usize {
8
}
}
#[derive(Debug, PartialEq, Default, Copy, Clone)]
pub struct ResTableTypeFlags {
pub flags: u8,
}
impl Readable for ResTableTypeFlags {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
flags: u8::read_no_opts(reader).add_context(|| "read flags for ResTableTypeFlags")?,
})
}
}
impl Writeable for ResTableTypeFlags {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.flags
.write_no_opts(writer)
.add_context(|| "write flags for ResTableTypeFlags")
}
}
#[derive(Debug, PartialEq, Clone, Default, Hash, Eq, Copy)]
pub struct ResTableConfig {
pub imsi: Option<u32>,
pub locale: Option<u32>,
pub screen_type: Option<u32>,
pub input: Option<u32>,
pub screen_size: Option<u32>,
pub version: Option<u32>,
pub screen_config: Option<u32>,
pub screen_size_dp: Option<u32>,
pub locale_script: Option<FixedString<4>>,
pub locale_variant: Option<FixedString<8>>,
pub screen_config_2: Option<u32>,
pub locale_script_was_computed: Option<bool>,
pub locale_numbering_system: Option<FixedString<8>>,
}
impl Writeable for ResTableConfig {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let sp = writer.stream_position()?;
let size: u32 = self.get_size() as u32;
size.write_no_opts(writer)?;
if let Some(imsi) = self.imsi {
imsi.write_no_opts(writer)?;
}
if let Some(locale) = self.locale {
locale.write_no_opts(writer)?;
}
if let Some(screen_type) = self.screen_type {
screen_type.write_no_opts(writer)?;
}
if let Some(input) = self.input {
input.write_no_opts(writer)?;
}
if let Some(screen_size) = self.screen_size {
screen_size.write_no_opts(writer)?;
}
if let Some(version) = self.version {
version.write_no_opts(writer)?;
}
if let Some(screen_config) = self.screen_config {
screen_config.write_no_opts(writer)?;
}
if let Some(screen_size_dp) = self.screen_size_dp {
screen_size_dp.write_no_opts(writer)?;
}
if let Some(locale_script) = self.locale_script {
locale_script.write_no_opts(writer)?;
}
if let Some(locale_variant) = self.locale_variant {
locale_variant.write_no_opts(writer)?;
}
if let Some(screen_config_2) = self.screen_config_2 {
screen_config_2.write_no_opts(writer)?;
}
if let Some(locale_script_was_computed) = self.locale_script_was_computed {
locale_script_was_computed.write_no_opts(writer)?;
}
if let Some(locale_numbering_system) = self.locale_numbering_system {
locale_numbering_system.write_no_opts(writer)?;
}
let remaining = writer.stream_position()? - sp;
if size as u64 > remaining {
for _ in 0..(size as u64 - remaining) {
0u8.write_no_opts(writer)?;
}
}
Ok(())
}
}
impl Readable for ResTableConfig {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let start_offset = reader.stream_position()?;
let size = u32::read_no_opts(reader)?;
let imsi = if size >= 8 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let locale = if size >= 12 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let screen_type = if size >= 16 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let input = if size >= 20 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let screen_size = if size >= 24 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let version = if size >= 28 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let screen_config = if size >= 32 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let screen_size_dp = if size >= 36 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let locale_script = if size >= 40 {
Some(<FixedString<4>>::read_no_opts(reader)?)
} else {
None
};
let locale_variant = if size >= 48 {
Some(<FixedString<8>>::read_no_opts(reader)?)
} else {
None
};
let screen_config_2 = if size >= 52 {
Some(u32::read_no_opts(reader)?)
} else {
None
};
let locale_script_was_computed = if size >= 53 {
Some(bool::read_no_opts(reader)?)
} else {
None
};
let locale_numbering_system = if size >= 61 {
Some(<FixedString<8>>::read_no_opts(reader)?)
} else {
None
};
reader.seek(SeekFrom::Start(start_offset + size as u64))?;
Ok(Self {
imsi,
locale,
screen_type,
input,
screen_size,
version,
screen_config,
screen_size_dp,
locale_script,
locale_variant,
screen_config_2,
locale_script_was_computed,
locale_numbering_system,
})
}
}
impl ResTableConfig {
pub fn get_size(&self) -> usize {
let mut size = 4;
if self.imsi.is_none() {
return size;
}
size += 4;
if self.locale.is_none() {
return size;
}
size += 4;
if self.screen_type.is_none() {
return size;
}
size += 4;
if self.input.is_none() {
return size;
}
size += 4;
if self.screen_size.is_none() {
return size;
}
size += 4;
if self.version.is_none() {
return size;
}
size += 4;
if self.screen_config.is_none() {
return size;
}
size += 4;
if self.screen_size_dp.is_none() {
return size;
}
size += 4;
if self.locale_script.is_none() {
return size;
}
size += 4;
if self.locale_variant.is_none() {
return size;
}
size += 8;
if self.screen_config_2.is_none() {
return size;
}
size += 4;
if self.locale_script_was_computed.is_none() {
return size;
}
size += 1;
if self.locale_numbering_system.is_none() {
return size;
}
size += 8;
size = align(size as u64, 4) as usize;
size
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct FixedString<const N: usize> {
data: [u8; N],
}
impl<const N: usize> Readable for FixedString<N> {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
data: <[u8; N]>::read_no_opts(reader)
.add_context(|| format!("reading data for FixedString<{N}>"))?,
})
}
}
impl<const N: usize> Writeable for FixedString<N> {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.data
.write_no_opts(writer)
.add_context(|| format!("write data for FixedString<{N}>"))
}
}
impl<const N: usize> Display for FixedString<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.data))
}
}
#[derive(Debug)]
pub struct InvalidLength {
pub expected: usize,
pub got: usize,
}
impl<const N: usize> TryFrom<String> for FixedString<N> {
type Error = InvalidLength;
fn try_from(value: String) -> Result<Self, InvalidLength> {
let bytes = value.into_bytes();
if bytes.len() != N {
return Err(InvalidLength {
expected: N,
got: bytes.len(),
});
}
Ok(Self {
data: bytes.try_into().unwrap(),
})
}
}
impl ResTableTypeFlags {
pub fn sparse(&self) -> bool {
(self.flags & 0x01) != 0
}
pub fn with_sparse(mut self) -> Self {
self.flags |= 0x01;
self
}
pub fn offset_16(&self) -> bool {
(self.flags & 0x02) != 0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableType {
pub id: u8,
pub flags: ResTableTypeFlags,
pub config: ResTableConfig,
pub entries: Vec<(usize, Option<ResTableEntry>)>, }
impl Writeable for ResTableType {
type Args = ();
fn write<W: Write + Seek>(mut self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let header_size = self.header_size() + ResChunk::header_size();
let header_offset = ResChunk::get_header_offset(writer.stream_position()?);
let sparse = self.flags.sparse();
self.id
.write_no_opts(writer)
.add_context(|| "write id for ResTableType")?;
self.flags
.write_no_opts(writer)
.add_context(|| "write flags for ResTableType")?;
let reserved: u16 = 0;
reserved
.write_no_opts(writer)
.add_context(|| "write reserved for ResTableType")?;
let pos = writer.stream_position()?;
let entry_indicies = calculate_entry_indicies(&self.entries, sparse).map_err(|e| {
StreamError::new_string_context(e, pos, "calculate entry_indicies for ResTableType")
})?;
let entry_count: u32 = entry_indicies.len() as u32;
entry_count
.write_no_opts(writer)
.add_context(|| "write entry_count for ResTableType")?;
self.entries.sort_by_key(|e| e.0);
let entries_start: u32 = calc_entries_start(&self.config, entry_indicies.len());
entries_start
.write_no_opts(writer)
.add_context(|| "write entries_start for ResTableType")?;
self.config
.write_no_opts(writer)
.add_context(|| "write config for ResTableType")?;
writer.seek(SeekFrom::Start(header_offset + header_size as u64))?;
entry_indicies
.clone()
.write_no_opts(writer)
.add_context(|| "write entry_indicies for ResTableType")?;
writer.seek(SeekFrom::Start(header_offset + entries_start as u64))?;
self.entries
.write(writer, entry_indicies)
.add_context(|| "write entries for ResTableType")?;
Ok(())
}
}
impl Readable for ResTableType {
type Args = usize;
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
let header_offset = ResChunk::get_header_offset(reader.stream_position()?);
let id = u8::read_no_opts(reader).add_context(|| "read id for ResTableType")?;
let flags = ResTableTypeFlags::read_no_opts(reader)
.add_context(|| "read flags for ResTableType")?;
let reserved =
u16::read_no_opts(reader).add_context(|| "read reserved for ResTableType")?;
if reserved != 0 {
return Err(StreamError::new_string_context(
format!("invalid reserved value: {reserved}, expected 0"),
reader.stream_position()?,
"validate reserved for ResTableType",
));
}
let entry_count =
u32::read_no_opts(reader).add_context(|| "read entry_count for ResTableType")?;
let entries_start =
u32::read_no_opts(reader).add_context(|| "read entries_start for ResTableType")?;
let config =
ResTableConfig::read_no_opts(reader).add_context(|| "read config for ResTableType")?;
reader.seek(SeekFrom::Start(header_offset + args as u64))?;
let entry_indicies =
ResTableTypeEntryIndicies::read(reader, (entry_count as usize, flags.sparse()))
.add_context(|| "read entry_indicies for ResTableType")?;
reader.seek(SeekFrom::Start(header_offset + entries_start as u64))?;
let entries = <Vec<(usize, Option<ResTableEntry>)>>::read(reader, entry_indicies)
.add_context(|| "read entries for ResTableType")?;
Ok(Self {
id,
flags,
config,
entries,
})
}
}
impl Writeable for Vec<(usize, Option<ResTableEntry>)> {
type Args = ResTableTypeEntryIndicies;
fn write<W: Write + Seek>(self, writer: &mut W, args: Self::Args) -> StreamResult<()> {
let sp = writer.stream_position()?;
match args {
ResTableTypeEntryIndicies::NoSparse(items) => {
for (i, ind) in items.into_iter().enumerate() {
if ind == 0xffffffff {
continue;
}
let mut found = false;
writer.seek(SeekFrom::Start(sp + ind as u64))?;
for item in &self {
if item.0 == i {
if let Some(ref item) = item.1 {
item.clone().write_no_opts(writer)?;
}
found = true;
break;
}
}
if !found {
return Err(StreamError::new_string_context(
format!("could not find entry with id: {i}"),
writer.stream_position()?,
"writing sparse entries",
));
}
}
}
ResTableTypeEntryIndicies::Sparse(items) => {
for val in items {
let new_pos = sp + (val.offset as u64) * 4;
writer.seek(SeekFrom::Start(new_pos))?;
let mut found = false;
for item in &self {
if item.0 == val.idx as usize {
if let Some(ref item) = item.1 {
item.clone().write_no_opts(writer)?;
found = true;
break;
} else {
return Err(StreamError::new_string_context(
InvalidEntry::InvalidEntry,
writer.stream_position()?,
format!("entry cannot be None in sparse mode!"),
));
}
}
}
if !found {
return Err(StreamError::new_string_context(
format!("could not find entry with id: {}", val.idx),
writer.stream_position()?,
"writing sparse entries",
));
}
}
}
}
Ok(())
}
}
impl HeaderSizeInstance for ResTableType {
fn header_size(&self) -> usize {
1 + 1 + 2 + 4 + 4 + self.config.get_size()
}
}
impl ResTableType {
pub fn get_entry(&self, id: usize) -> Option<&ResTableEntry> {
for entry in &self.entries {
if entry.0 == id {
return entry.1.as_ref();
}
}
None
}
pub fn get_entry_mut(&mut self, id: usize) -> Option<&mut ResTableEntry> {
if !self.flags.sparse() {
let entry = self.entries.get_mut(id)?;
if entry.0 != id {
return None;
}
entry.1.as_mut()
} else {
for entry in self.entries.iter_mut() {
if entry.0 == id {
return entry.1.as_mut();
}
}
None
}
}
}
#[derive(Debug)]
pub enum InvalidEntry {
InvalidID { expected_id: usize, got_id: usize },
InvalidEntry,
}
impl Display for InvalidEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidID {
expected_id,
got_id,
} => write!(
f,
"invalid entry, expected entry id {expected_id}, got {got_id}"
),
Self::InvalidEntry => write!(f, "invalid entry, entry cannot be None"),
}
}
}
fn calculate_entry_indicies(
entries: &Vec<(usize, Option<ResTableEntry>)>,
sparse: bool,
) -> Result<ResTableTypeEntryIndicies, InvalidEntry> {
let mut pos: usize = 0;
Ok(match sparse {
true => {
let mut indicies: Vec<ResTableSparseTypeEntry> = Vec::new();
for (id, entry) in entries {
if entry.is_none() {
return Err(InvalidEntry::InvalidEntry);
}
let size = (entry.as_ref())
.ok_or(InvalidEntry::InvalidEntry)?
.get_size();
indicies.push(ResTableSparseTypeEntry {
idx: (*id) as u16,
offset: pos as u16,
});
pos += size / 4;
}
ResTableTypeEntryIndicies::Sparse(indicies)
}
false => {
let mut indicies: Vec<u32> = Vec::new();
let max_index = entries.iter().map(|v| v.0).max();
if let Some(max) = max_index {
for ind in 0..=max {
let mut found = false;
for (id, entry) in entries.iter() {
if *id == ind {
if let Some(entry) = entry {
indicies.push(pos as u32);
pos += entry.get_size();
} else {
indicies.push(0xffffffff);
}
found = true;
break;
}
}
if !found {
indicies.push(0xffffffff);
}
}
}
ResTableTypeEntryIndicies::NoSparse(indicies)
}
})
}
impl Readable for Vec<(usize, Option<ResTableEntry>)> {
type Args = ResTableTypeEntryIndicies;
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
let start_pos = reader.stream_position()?;
match args {
ResTableTypeEntryIndicies::NoSparse(indcs) => {
let mut data: Vec<(usize, Option<ResTableEntry>)> = Vec::with_capacity(indcs.len());
for (i, offset) in indcs.into_iter().enumerate() {
if offset == 0xffffffff {
data.push((i, None));
} else {
reader.seek(SeekFrom::Start(start_pos + offset as u64))?;
data.push((
i,
Some(ResTableEntry::read_no_opts(reader).add_context(
|| "read entry for ResTableTypeEntryIndicies::NoSparse",
)?),
));
}
}
Ok(data)
}
ResTableTypeEntryIndicies::Sparse(sp) => {
let mut data: Vec<(usize, Option<ResTableEntry>)> = Vec::with_capacity(sp.len());
for v in sp {
reader.seek(SeekFrom::Start(start_pos + (v.offset as u64) * 4))?;
data.push((
v.idx as usize,
Some(
ResTableEntry::read_no_opts(reader).add_context(
|| "read entry for ResTableTypeEntryIndicies::Sparse",
)?,
),
));
}
Ok(data)
}
}
}
}
#[derive(Debug, PartialEq, Clone, Copy, Default)]
pub struct ResTableEntryFlags(u16);
impl Readable for ResTableEntryFlags {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self(
u16::read_no_opts(reader).add_context(|| "read flags for ResTableEntryFlags")?,
))
}
}
impl Writeable for ResTableEntryFlags {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.0
.write_no_opts(writer)
.add_context(|| "write flags for ResTableEntryFlags")
}
}
impl ResTableEntryFlags {
pub fn complex(&self) -> bool {
self.0 & 0x1 != 0
}
pub fn public(&self) -> bool {
self.0 & 0x2 != 0
}
pub fn weak(&self) -> bool {
self.0 & 0x4 != 0
}
pub fn compact(&self) -> bool {
self.0 & 0x8 != 0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableEntry {
pub flags: ResTableEntryFlags,
pub data: ResTableEntryValue,
}
impl Readable for ResTableEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let _header_size =
u16::read_no_opts(reader).add_context(|| "read header_size for ResTableEntry")?;
let flags = ResTableEntryFlags::read_no_opts(reader)
.add_context(|| "read flags for ResTableEntry")?;
let data = ResTableEntryValue::read(reader, flags)
.add_context(|| "read data for ResTableEntryValue")?;
Ok(Self { flags, data })
}
}
impl Writeable for ResTableEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let header_size: u16 = self.header_size() as u16;
header_size
.write_no_opts(writer)
.add_context(|| "write header_size for ResTableEntry")?;
self.flags
.write_no_opts(writer)
.add_context(|| "write flags for ResTableEntry")?;
self.data
.write_no_opts(writer)
.add_context(|| "write data for ResTableEntry")
}
}
impl HeaderSizeInstance for ResTableEntry {
fn header_size(&self) -> usize {
4 + self.data.header_size()
}
}
impl ResTableEntry {
pub fn get_size(&self) -> usize {
4 + self.data.get_size()
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ResTableEntryValue {
ResValue(ResTableResValueEntry),
Map(ResTableMapEntry),
Compact(u32), }
impl HeaderSizeInstance for ResTableEntryValue {
fn header_size(&self) -> usize {
match self {
ResTableEntryValue::Compact(_) => 0,
ResTableEntryValue::ResValue(_) => 4,
ResTableEntryValue::Map(_) => 12,
}
}
}
impl ResTableEntryValue {
pub fn is_compact(&self) -> bool {
matches!(self, ResTableEntryValue::Compact(_))
}
pub fn is_complex(&self) -> bool {
matches!(self, ResTableEntryValue::Map(_))
}
pub fn get_size(&self) -> usize {
match self {
ResTableEntryValue::ResValue(r) => r.get_size(),
ResTableEntryValue::Map(m) => m.get_size(),
ResTableEntryValue::Compact(_) => 0x4,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableMapEntry {
pub key: ResStringPoolRef,
pub parent: ResTableRef,
pub map: Vec<ResTableMap>,
}
impl Readable for ResTableMapEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let key = ResStringPoolRef::read_no_opts(reader)
.add_context(|| "read key for ResTableMapEntry")?;
let parent =
ResTableRef::read_no_opts(reader).add_context(|| "read parent for ResTableMapEntry")?;
let count = u32::read_no_opts(reader).add_context(|| "read count for ResTableMapEntry")?;
let map = <Vec<ResTableMap>>::read_vec(reader, count as usize)
.add_context(|| "read map for ResTableMapEntry")?;
Ok(Self { key, parent, map })
}
}
impl Writeable for ResTableMapEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.key
.write_no_opts(writer)
.add_context(|| "write key for ResTableMapEntry")?;
self.parent
.write_no_opts(writer)
.add_context(|| "write parent for ResTableMapEntry")?;
let count: u32 = self.map.len() as u32;
count
.write_no_opts(writer)
.add_context(|| "write count for ResTableMapEntry")?;
self.map
.write_vec(writer)
.add_context(|| "write map for ResTableMapEntry")
}
}
impl ResTableMapEntry {
pub fn get_size(&self) -> usize {
4 + 4 + 4 + self.map.len() * (4 + 8)
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct ResTableResValueEntry {
pub key: ResStringPoolRef,
pub data: ResValue,
}
impl Readable for ResTableResValueEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
key: ResStringPoolRef::read_no_opts(reader)
.add_context(|| "read key for ResTableResValueEntry")?,
data: ResValue::read_no_opts(reader)
.add_context(|| "read data for ResTableValueEntry")?,
})
}
}
impl Writeable for ResTableResValueEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.key
.write_no_opts(writer)
.add_context(|| "write key for ResTableResValueEntry")?;
self.data
.write_no_opts(writer)
.add_context(|| "write data for ResTableResValueEntry")
}
}
impl ResTableResValueEntry {
pub fn get_size(&self) -> usize {
4 + 8
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct ResTableMap {
pub name: ResTableRef,
pub value: ResValue,
}
impl Readable for ResTableMap {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
name: ResTableRef::read_no_opts(reader).add_context(|| "read name for ResTableMap")?,
value: ResValue::read_no_opts(reader).add_context(|| "read value for ResTableMap")?,
})
}
}
impl Writeable for ResTableMap {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.name
.write_no_opts(writer)
.add_context(|| "write name for ResTableMap")?;
self.value
.write_no_opts(writer)
.add_context(|| "write value for ResTableMap")
}
}
impl Readable for ResTableEntryValue {
type Args = ResTableEntryFlags;
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
if args.compact() {
Ok(ResTableEntryValue::Compact(
u32::read_no_opts(reader)
.add_context(|| "read entries for ResTableEntryValue::Compact")?,
))
} else if args.complex() {
Ok(ResTableEntryValue::Map(
ResTableMapEntry::read_no_opts(reader)
.add_context(|| "read entries for ResTableEntryValue::Map")?,
))
} else {
Ok(ResTableEntryValue::ResValue(
ResTableResValueEntry::read_no_opts(reader)
.add_context(|| "read entries for ResTableEntryValue::ResValue")?,
))
}
}
}
impl Writeable for ResTableEntryValue {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
match self {
Self::ResValue(v) => v
.write_no_opts(writer)
.add_context(|| "write value for ResTableEntryValue::ResValue"),
Self::Map(v) => v
.write_no_opts(writer)
.add_context(|| "write value for ResTableEntryValue::Map"),
Self::Compact(v) => v
.write_no_opts(writer)
.add_context(|| "write value for ResTableEntryValue::Compact"),
}
}
}
fn calc_entries_start(config: &ResTableConfig, total_entries: usize) -> u32 {
8 + 1 + 1 + 2 + 4 + 4 + config.get_size() as u32 + (total_entries as u32 * 4)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ResTableSparseTypeEntry {
pub idx: u16,
pub offset: u16,
}
impl Readable for ResTableSparseTypeEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
idx: u16::read_no_opts(reader)
.add_context(|| "read idx for ResTableSparseTypeEntry")?,
offset: u16::read_no_opts(reader)
.add_context(|| "read offset for ResTableSparseTypeEntry")?,
})
}
}
impl Writeable for ResTableSparseTypeEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.idx
.write_no_opts(writer)
.add_context(|| "write idx for ResTableSparseTypeEntry")?;
self.offset
.write_no_opts(writer)
.add_context(|| "write offset for ResTableSparseTypeEntry")
}
}
impl ResTableSparseTypeEntry {
pub fn get_size() -> usize {
2 + 2
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResTableTypeEntryIndicies {
NoSparse(Vec<u32>),
Sparse(Vec<ResTableSparseTypeEntry>),
}
impl Writeable for ResTableTypeEntryIndicies {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
match self {
Self::Sparse(e) => e
.write_vec(writer)
.add_context(|| "write entries for ResTableTypeEntryIndicies::Sparse"),
Self::NoSparse(e) => e
.write_vec(writer)
.add_context(|| "write entries for ResTableTypeEntryIndicies::NoSparse"),
}
}
}
impl Readable for ResTableTypeEntryIndicies {
type Args = (usize, bool);
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
if args.1 {
Ok(ResTableTypeEntryIndicies::Sparse(
<Vec<ResTableSparseTypeEntry>>::read_vec(reader, args.0)
.add_context(|| "read entries for ResTableTypeEntryIndicies::Sparse")?,
))
} else {
Ok(ResTableTypeEntryIndicies::NoSparse(
<Vec<u32>>::read_vec(reader, args.0)
.add_context(|| "read entries for RestableTypeEntryIndicies::NoSparse")?,
))
}
}
}
impl ResTableTypeEntryIndicies {
pub fn is_sparse(&self) -> bool {
match self {
ResTableTypeEntryIndicies::Sparse(_) => true,
ResTableTypeEntryIndicies::NoSparse(_) => false,
}
}
pub fn len(&self) -> usize {
match self {
ResTableTypeEntryIndicies::NoSparse(ent) => ent.len(),
ResTableTypeEntryIndicies::Sparse(ent) => ent.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTablePackage {
pub id: u32,
pub name: String,
pub last_public_type: u32,
pub last_public_key: u32,
pub type_id_offset: u32,
pub string_pool_type: StringPoolHandler,
pub string_pool_key: StringPoolHandler,
pub chunks: Vec<ResChunk>,
}
macro_rules! mut_ref_remove {
($mut:ident, $ref:ident, $remove:ident, $type:ty, $variant:ident) => {
pub fn $mut(&mut self) -> Option<&mut $type> {
self.chunks.iter_mut().find_map(|v| {
if let ResTypeValue::$variant(ref mut lib) = v.data {
Some(lib)
} else {
None
}
})
}
pub fn $ref(&self) -> Option<&$type> {
self.chunks.iter().find_map(|v| {
if let ResTypeValue::$variant(ref lib) = v.data {
Some(lib)
} else {
None
}
})
}
pub fn $remove(&mut self) -> Option<$type> {
let pos = self
.chunks
.iter()
.position(|v| matches!(v.data, ResTypeValue::$variant(_)))?;
if let ResTypeValue::$variant(lib) = self.chunks.remove(pos).data {
Some(lib)
} else {
None
}
}
};
}
macro_rules! mut_ref_removes {
($mut:ident, $ref:ident, $remove:ident, $remove_all:ident, $type:ty, $variant:ident) => {
pub fn $mut(&mut self) -> Vec<&mut $type> {
self.chunks
.iter_mut()
.filter_map(|v| {
if let ResTypeValue::$variant(ref mut lib) = v.data {
Some(lib)
} else {
None
}
})
.collect()
}
pub fn $ref(&self) -> Vec<&$type> {
self.chunks
.iter()
.filter_map(|v| {
if let ResTypeValue::$variant(ref lib) = v.data {
Some(lib)
} else {
None
}
})
.collect()
}
pub fn $remove(&mut self, id: u8) -> Option<$type> {
let pos = self.chunks.iter().position(|v| {
if let ResTypeValue::$variant(o) = &v.data {
o.id == id
} else {
false
}
})?;
if let ResTypeValue::$variant(lib) = self.chunks.remove(pos).data {
Some(lib)
} else {
None
}
}
pub fn $remove_all(&mut self) -> Vec<$type> {
let poses = self
.chunks
.iter()
.enumerate()
.filter_map(|(i, v)| {
if let ResTypeValue::$variant(_) = &v.data {
Some(i)
} else {
None
}
})
.collect::<Vec<usize>>();
let mut data = Vec::new();
for pos in poses.iter().rev() {
if let ResTypeValue::$variant(lib) = self.chunks.remove(*pos).data {
data.push(lib);
}
}
data
}
};
}
impl ResTablePackage {
mut_ref_remove!(
library_mut,
library_ref,
remove_library,
ResTableLib,
TableLibrary
);
mut_ref_remove!(
type_spec_mut,
type_spec_ref,
remove_type_spec,
ResTableTypeSpec,
TableSpec
);
mut_ref_removes!(
table_type_mut,
table_type_ref,
remove_table_type,
remove_table_types,
ResTableType,
TableType
);
}
impl Mergeable for ResTablePackage {
type Returns = ();
fn merge(&mut self, mut other: Self) {
let slib = self.library_mut();
if let Some(slib) = slib {
let olib = other.remove_library();
if let Some(olib) = olib {
slib.entries.extend(olib.entries);
}
}
let s = self.type_spec_mut();
if let Some(s) = s {
let o = other.remove_type_spec();
if let Some(o) = o {
s.config_masks.extend(o.config_masks);
}
}
self.string_pool_key.merge(other.string_pool_key);
}
}
impl Readable for ResTablePackage {
type Args = usize;
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
let header_offset = ResChunk::get_header_offset(reader.stream_position()?);
let id = u32::read_no_opts(reader).add_context(|| "read id for ResTablePackage")?;
let name = read_utf16_fixed_null_string(reader, 128)
.add_context(|| "read name for ResTablePackage")?;
let type_strings =
u32::read_no_opts(reader).add_context(|| "read type_strings for ResTablePackage")?;
let last_public_type = u32::read_no_opts(reader)
.add_context(|| "read last_public_type for ResTablePackage")?;
let key_strings =
u32::read_no_opts(reader).add_context(|| "read key_strings for ResTablePackage")?;
let last_public_key =
u32::read_no_opts(reader).add_context(|| "read last_public_key for ResTablePackage")?;
let type_id_offset =
u32::read_no_opts(reader).add_context(|| "read type_id_offset for ResTablePackage")?;
reader.seek(SeekFrom::Start(header_offset + type_strings as u64))?;
let string_pool_type = parse_string_pool(reader)
.add_context(|| "read string_pool_type for ResTablePackage")?;
reader.seek(SeekFrom::Start(header_offset + key_strings as u64))?;
let string_pool_key =
parse_string_pool(reader).add_context(|| "read string_pool_key for ResTablePackage")?;
let size = args as u64 - (reader.stream_position()? - header_offset);
let chunks = <Vec<ResChunk>>::read(reader, size)
.add_context(|| "read chunks for ResTablePackage")?;
Ok(Self {
id,
name,
last_public_type,
last_public_key,
type_id_offset,
string_pool_type,
string_pool_key,
chunks,
})
}
}
impl HeaderSizeStatic for ResTablePackage {
fn header_size() -> usize {
280
}
}
impl ResTablePackage {
pub fn resolve_ref(&self, reference: ResTableRef) -> Option<&ResTableEntry> {
for chunk in &self.chunks {
if let ResTypeValue::TableType(table_type) = &chunk.data {
if table_type.id != reference.type_index {
continue;
}
return table_type.get_entry(reference.entry_index as usize);
}
}
None
}
pub fn resolve_ref_mut(&mut self, reference: ResTableRef) -> Option<&mut ResTableEntry> {
for chunk in self.chunks.iter_mut() {
if let ResTypeValue::TableType(table_type) = &mut chunk.data {
if table_type.id != reference.type_index {
continue;
}
return table_type.get_entry_mut(reference.entry_index as usize);
}
}
None
}
}
impl Writeable for ResTablePackage {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let header_offset = ResChunk::get_header_offset(writer.stream_position()?);
self.id
.write_no_opts(writer)
.add_context(|| "write id for ResTablePackage")?;
write_utf16_fixed_null_string(writer, &self.name, 128)
.add_context(|| "write name for ResTablePackage")?;
let type_strings_pos_pos = writer.stream_position()?;
writer.seek_relative(4)?;
self.last_public_type
.write_no_opts(writer)
.add_context(|| "write last_public_type for ResTablePackage")?;
let key_strings_pos_pos = writer.stream_position()?;
writer.seek_relative(4)?;
self.last_public_key
.write_no_opts(writer)
.add_context(|| "write last_public_key for ResTablePackage")?;
self.type_id_offset
.write_no_opts(writer)
.add_context(|| "write type_id_offset for ResTablePackage")?;
let type_string_pos = writer.stream_position()?;
writer.seek(std::io::SeekFrom::Start(type_strings_pos_pos))?;
let type_strings_pos2: u32 = (type_string_pos - header_offset) as u32;
type_strings_pos2
.write_no_opts(writer)
.add_context(|| "write type_strings_pos for ResTablePackage")?;
writer.seek(std::io::SeekFrom::Start(type_string_pos))?;
write_string_pool(writer, self.string_pool_type)
.add_context(|| "write string_pool_type for ResTablePackage")?;
let key_string_pos = writer.stream_position()?;
writer.seek(std::io::SeekFrom::Start(key_strings_pos_pos))?;
let key_strings_pos2: u32 = (key_string_pos - header_offset) as u32;
key_strings_pos2
.write_no_opts(writer)
.add_context(|| "write key_strings_pos for ResTablePackage")?;
writer.seek(std::io::SeekFrom::Start(key_string_pos))?;
write_string_pool(writer, self.string_pool_key)
.add_context(|| "write string_pool_key for ResTablePackage")?;
self.chunks
.write_vec(writer)
.add_context(|| "write chunks for ResTablePackage")?;
Ok(())
}
}
#[derive(Debug)]
pub struct InvalidStringPoolResType;
impl Display for InvalidStringPoolResType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "expected string pool got something else")
}
}
fn parse_string_pool<R: Read + Seek>(reader: &mut R) -> StreamResult<StringPoolHandler> {
let start = reader.stream_position()?;
let chunk =
ResChunk::read_no_opts(reader).add_context(|| "read chunk for parse_string_pool")?;
if let ResTypeValue::StringPool(sp) = chunk.data {
return Ok(sp.into());
}
let res_type: ResType = (&chunk.data).into();
Err(StreamError::new_string_context(
format!("invalid res type: {res_type}, expected StringPool"),
start,
"validate chunk for parse_string_pool",
))
}
fn write_string_pool<W: Write + Seek>(
writer: &mut W,
string_pool: StringPoolHandler,
) -> StreamResult<()> {
let chunk = ResChunk {
data: ResTypeValue::StringPool(string_pool.string_pool),
};
chunk
.write_no_opts(writer)
.add_context(|| "write string pool chunk for write_string_pool")
}
fn read_utf16_fixed_null_string<R: Read + Seek>(
reader: &mut R,
length: usize,
) -> StreamResult<String> {
let mut data: Vec<u16> = Vec::new();
let start = reader.stream_position()?;
let end = start + (length as u64) * 2;
for _ in 0..length {
let val = <u16>::read_no_opts(reader)
.add_context(|| "read utf16 char for read_utf16_fixed_null_string")?;
if val == 0 {
reader.seek(std::io::SeekFrom::Start(end))?;
break;
}
data.push(val);
}
String::from_utf16(data.as_slice()).map_err(|e| {
StreamError::new_string_context(e, start, "decode utf16 for read_utf16_fixed_null_string")
})
}
#[derive(Debug)]
pub struct PackageNameError(usize);
impl Display for PackageNameError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"package name was too long, expected a length of less than 128, got a length of {}",
self.0
)
}
}
fn write_utf16_fixed_null_string<W: Write + Seek>(
writer: &mut W,
string: &str,
length: usize,
) -> StreamResult<()> {
let mut data: Vec<u16> = string.encode_utf16().collect();
if data.len() >= length {
return Err(StreamError::new_string_context(
format!("invalid data length {}, expected {}", data.len(), length),
writer.stream_position()?,
"validate data length for write_utf16_fixed_null_string",
));
}
data.resize(length, 0);
data.write_vec(writer)
.add_context(|| "write encoded utf16 data for write_utf16_fixed_null_string")?;
Ok(())
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTable {
pub string_pool: StringPoolHandler,
pub packages: Packages,
}
impl ResTable {
pub fn resolve(&self, reference: ResTableRef) -> Option<&ResTableEntry> {
self.packages.resolve(reference)
}
}
impl Readable for ResTable {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let package_count =
u32::read_no_opts(reader).add_context(|| "read package_count for ResTable")?;
let string_pool =
parse_string_pool(reader).add_context(|| "read string_pool for ResTable")?;
let packages = Packages::read(reader, package_count as usize)
.add_context(|| "read packages for ResTable")?;
Ok(Self {
string_pool,
packages,
})
}
}
impl Writeable for ResTable {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let package_count: u32 = self.packages.packages.len() as u32;
package_count
.write_no_opts(writer)
.add_context(|| "write package_count for ResTable")?;
write_string_pool(writer, self.string_pool)
.add_context(|| "write string_pool for ResTable")?;
self.packages
.write_no_opts(writer)
.add_context(|| "write packages for ResTable")
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Packages {
packages: Vec<ResChunk>,
}
impl Mergeable for Packages {
type Returns = ();
fn merge(&mut self, other: Self) {
for opackage in other.into_packages() {
let spackage = self.get_by_id_mut(opackage.id);
if let Some(spackage) = spackage {
spackage.merge(opackage);
} else {
self.add_package(opackage);
}
}
}
}
impl Readable for Packages {
type Args = usize;
fn read<R: Read + Seek>(reader: &mut R, args: Self::Args) -> StreamResult<Self> {
Ok(Self {
packages: <Vec<ResChunk>>::read_vec(reader, args)
.add_context(|| "read packages for Packages")?,
})
}
}
impl Writeable for Packages {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.packages
.write_vec(writer)
.add_context(|| "write packages for Packages")
}
}
impl Packages {
pub fn new(packages: Vec<ResTablePackage>) -> Self {
Self {
packages: packages
.into_iter()
.map(|v| ResChunk {
data: ResTypeValue::TablePackage(v),
})
.collect(),
}
}
pub fn get(&self, index: usize) -> Option<&ResTablePackage> {
let item = self.packages.get(index)?;
if let ResTypeValue::TablePackage(pkg) = &item.data {
return Some(pkg);
}
None
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut ResTablePackage> {
let item = self.packages.get_mut(index)?;
if let ResTypeValue::TablePackage(pkg) = &mut item.data {
return Some(pkg);
}
None
}
pub fn into_packages(self) -> Vec<ResTablePackage> {
self.packages
.into_iter()
.filter_map(|v| {
if let ResTypeValue::TablePackage(pkg) = v.data {
Some(pkg)
} else {
None
}
})
.collect()
}
pub fn as_packages(&self) -> Vec<&ResTablePackage> {
self.packages
.iter()
.filter_map(|v| {
if let ResTypeValue::TablePackage(ref pkg) = v.data {
Some(pkg)
} else {
None
}
})
.collect()
}
pub fn get_by_id_mut(&mut self, id: u32) -> Option<&mut ResTablePackage> {
for package in &mut self.packages {
if let ResTypeValue::TablePackage(pkg) = &mut package.data {
if pkg.id == id {
return Some(pkg);
}
}
}
None
}
pub fn add_package(&mut self, package: ResTablePackage) {
self.packages.push(ResChunk {
data: ResTypeValue::TablePackage(package),
});
}
pub fn get_by_id(&self, id: u32) -> Option<&ResTablePackage> {
for package in &self.packages {
if let ResTypeValue::TablePackage(pkg) = &package.data {
if pkg.id == id {
return Some(pkg);
}
}
}
None
}
pub fn first(&self) -> Option<&ResTablePackage> {
self.get(0)
}
pub fn first_mut(&mut self) -> Option<&mut ResTablePackage> {
self.get_mut(0)
}
pub fn resolve(&self, reference: ResTableRef) -> Option<&ResTableEntry> {
self.get_by_id(reference.package_index as u32)?
.resolve_ref(reference)
}
}
impl HeaderSizeStatic for ResTable {
fn header_size() -> usize {
4
}
}
#[derive(Debug)]
pub enum ResTableReadError {
ReadFile(std::io::Error, PathBuf),
Parse(StreamError),
}
impl Display for ResTableReadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ResTableReadError::ReadFile(error, path_buf) => format!(
"failed to read file: {} due to {error}",
path_buf.to_string_lossy()
),
ResTableReadError::Parse(stream_error) => stream_error.to_string(),
}
)
}
}
impl ResTable {
pub fn from_path(path: &Path) -> Result<Self, ResTableReadError> {
Self::read_all(
&mut std::fs::File::open(path)
.map_err(|e| ResTableReadError::ReadFile(e, path.to_path_buf()))?,
)
.map_err(|e| ResTableReadError::Parse(e))
}
pub fn read_all<R: Seek + Read>(reader: &mut R) -> StreamResult<Self> {
let pos = reader.stream_position()?;
let header = ResChunk::read_no_opts(reader).add_context(|| "read chunk for ResTable")?;
if let ResTypeValue::Table(table) = header.data {
return Ok(table);
}
let res_type: ResType = (&header.data).into();
Err(StreamError::new_string_context(
format!("invalid res_type: {res_type}, expected ResTable"),
pos,
"validate read chunk for ResTable",
))
}
pub fn write_all<W: Seek + Write>(self, writer: &mut W) -> StreamResult<()> {
let header = ResChunk {
data: ResTypeValue::Table(self),
};
header
.write_no_opts(writer)
.add_context(|| "write chunk for ResTable")
}
}
#[derive(Debug)]
pub struct WriteARSCError(pub std::io::Error);
impl Display for WriteARSCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl From<std::io::Error> for WriteARSCError {
fn from(value: std::io::Error) -> Self {
Self(value)
}
}
impl TryFrom<&[u8]> for ResTable {
type Error = StreamError;
fn try_from(value: &[u8]) -> Result<Self, StreamError> {
let mut stream = Cursor::new(value);
let val = ResTable::read_all(&mut stream)?;
Ok(val)
}
}
impl TryFrom<Vec<u8>> for ResTable {
type Error = StreamError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Self::read_all(&mut Cursor::new(value))
}
}
impl TryFrom<ResTable> for Vec<u8> {
type Error = StreamError;
fn try_from(value: ResTable) -> Result<Self, StreamError> {
let mut stream = Cursor::new(Vec::new());
value.write_all(&mut stream)?;
Ok(stream.into_inner())
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableLib {
pub entries: Vec<ResTableLibEntry>,
}
impl Readable for ResTableLib {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let count = u32::read_no_opts(reader).add_context(|| "read count for ResTableLib")?;
let entries = <Vec<ResTableLibEntry>>::read_vec(reader, count as usize)
.add_context(|| "read entries for ResTableLib")?;
Ok(Self { entries })
}
}
impl Writeable for ResTableLib {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let count: u32 = self.entries.len() as u32;
count
.write_no_opts(writer)
.add_context(|| "write count for ResTableLib")?;
self.entries
.write_vec(writer)
.add_context(|| "write entries for ResTableLib")
}
}
impl HeaderSizeStatic for ResTableLib {
fn header_size() -> usize {
4
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableLibEntry {
pub package_id: u32,
pub package_name: String,
}
impl Readable for ResTableLibEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
package_id: u32::read_no_opts(reader)
.add_context(|| "read package_id for ResTableLibEntry")?,
package_name: read_utf16_fixed_null_string(reader, 128)
.add_context(|| "read package_name for ResTableLibEntry")?,
})
}
}
impl Writeable for ResTableLibEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.package_id
.write_no_opts(writer)
.add_context(|| "write package_id for ResTableLibEntry")?;
write_utf16_fixed_null_string(writer, &self.package_name, 128)
.add_context(|| "write package_name for ResTableLibEntry")
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ResTableStagedAlias {
pub entries: Vec<ResTableStagedAliasEntry>,
}
impl Readable for ResTableStagedAlias {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let count =
u32::read_no_opts(reader).add_context(|| "read count for ResTableStagedAlias")?;
let entries = <Vec<ResTableStagedAliasEntry>>::read_vec(reader, count as usize)
.add_context(|| "read entries for ResTableStagedAlias")?;
Ok(Self { entries })
}
}
impl Writeable for ResTableStagedAlias {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
let count: u32 = self.entries.len() as u32;
count
.write_no_opts(writer)
.add_context(|| "write count for ResTableStagedAlias")?;
self.entries
.write_vec(writer)
.add_context(|| "write entries for ResTableStagedAlias")
}
}
impl HeaderSizeStatic for ResTableStagedAlias {
fn header_size() -> usize {
4
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct ResTableStagedAliasEntry {
pub staged_res_id: u32,
pub finalized_res_id: u32,
}
impl Readable for ResTableStagedAliasEntry {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
staged_res_id: u32::read_no_opts(reader)
.add_context(|| "read staged_res_id for ResTableStagedAliasEntry")?,
finalized_res_id: u32::read_no_opts(reader)
.add_context(|| "read finalized_res_id for ResTableStagedAliasEntry")?,
})
}
}
impl Writeable for ResTableStagedAliasEntry {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.staged_res_id
.write_no_opts(writer)
.add_context(|| "write staged_res_id for ResTableStagedAliasEntry")?;
self.finalized_res_id
.write_no_opts(writer)
.add_context(|| "write finalized_res_id for ResTableStagedAliasEntry")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResTableOverlayable {
pub name: String,
pub actor: String,
}
impl Readable for ResTableOverlayable {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
name: read_utf16_fixed_null_string(reader, 256)
.add_context(|| "read name for ResTableOverlayable")?,
actor: read_utf16_fixed_null_string(reader, 256)
.add_context(|| "read actor for ResTableOverlayable")?,
})
}
}
impl Writeable for ResTableOverlayable {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
write_utf16_fixed_null_string(writer, &self.name, 256)
.add_context(|| "write name for ResTableOverlayable")?;
write_utf16_fixed_null_string(writer, &self.actor, 256)
.add_context(|| "write actor for ResTableOverlayable")
}
}
impl HeaderSizeStatic for ResTableOverlayable {
fn header_size() -> usize {
1024
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct PolicyFlags {
pub flags: u32,
}
impl Readable for PolicyFlags {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
Ok(Self {
flags: u32::read_no_opts(reader).add_context(|| "read flags for PolicyFlags")?,
})
}
}
impl Writeable for PolicyFlags {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.flags
.write_no_opts(writer)
.add_context(|| "write flags for PolicyFlags")
}
}
impl PolicyFlags {
pub fn public(&self) -> bool {
self.flags & 1 != 0
}
pub fn system_partition(&self) -> bool {
self.flags & 2 != 0
}
pub fn vendor_partition(&self) -> bool {
self.flags & 4 != 0
}
pub fn product_partition(&self) -> bool {
self.flags & 8 != 0
}
pub fn signature(&self) -> bool {
self.flags & 0x10 != 0
}
pub fn odm_parition(&self) -> bool {
self.flags & 0x20 != 0
}
pub fn oem_partition(&self) -> bool {
self.flags & 0x40 != 0
}
pub fn actor_signature(&self) -> bool {
self.flags & 0x80 != 0
}
pub fn config_signature(&self) -> bool {
self.flags & 0x100 != 0
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResTableOverlayablePolicy {
pub policy_flags: PolicyFlags,
pub entries: Vec<ResTableRef>,
}
impl HeaderSizeStatic for ResTableOverlayablePolicy {
fn header_size() -> usize {
8
}
}
impl Readable for ResTableOverlayablePolicy {
type Args = ();
fn read<R: Read + Seek>(reader: &mut R, _args: Self::Args) -> StreamResult<Self> {
let policy_flags = PolicyFlags::read_no_opts(reader)
.add_context(|| "read policy_flags for ResTableOverlayablePolicy")?;
let entry_count = u32::read_no_opts(reader)
.add_context(|| "read entry_count for ResTableOverlayablePolicy")?;
let entries = <Vec<ResTableRef>>::read_vec(reader, entry_count as usize)
.add_context(|| "read entries for ResTableOverlayablePolicy")?;
Ok(Self {
policy_flags,
entries,
})
}
}
impl Writeable for ResTableOverlayablePolicy {
type Args = ();
fn write<W: Write + Seek>(self, writer: &mut W, _args: Self::Args) -> StreamResult<()> {
self.policy_flags
.write_no_opts(writer)
.add_context(|| "write policy flags for ResTableOverlayablePolicy")?;
let entry_count: u32 = self.entries.len() as u32;
entry_count
.write_no_opts(writer)
.add_context(|| "write entry_count for ResTableOverlayablePolicy")?;
self.entries
.write_vec(writer)
.add_context(|| "write entries for ResTableOverlayablePolicy")
}
}
impl Mergeable for ResTable {
type Returns = ();
fn merge(&mut self, other: Self) {
self.string_pool.merge(other.string_pool);
self.packages.merge(other.packages);
}
}