use std::io::{SeekFrom, Write};
use byteorder::{ByteOrder, LittleEndian};
use crate::{
decoder::{Decoder, IoBackend},
error::Error,
header::{SECTION_TYPE_SD, SECTION_TYPE_STRING},
sd::Object,
strings::StringSection,
variant::package::{
object::{ObjectHeader, ObjectTable},
Architecture,
Platform,
SECTION_TYPE_OBJECT_TABLE,
SUPPORTED_VERSION
},
Interface,
Result,
SectionHandle
};
const DATA_READ_BUFFER_SIZE: usize = 8192;
pub struct PackageDecoder<'a, TBackend: IoBackend>
{
type_code: [u8; 2],
architecture: Architecture,
platform: Platform,
strings: StringSection,
decoder: &'a mut Decoder<TBackend>,
object_table: SectionHandle
}
fn get_arch_platform_from_code(acode: u8, pcode: u8) -> Result<(Architecture, Platform)>
{
let arch;
let platform;
match acode {
0x0 => arch = Architecture::X86_64,
0x1 => arch = Architecture::Aarch64,
0x2 => arch = Architecture::X86,
0x3 => arch = Architecture::Armv7hl,
0x4 => arch = Architecture::Any,
_ => return Err(Error::Corruption(String::from("Architecture code does not exist")))
}
match pcode {
0x0 => platform = Platform::Linux,
0x1 => platform = Platform::Mac,
0x2 => platform = Platform::Windows,
0x3 => platform = Platform::Android,
0x4 => platform = Platform::Any,
_ => return Err(Error::Corruption(String::from("Platform code does not exist")))
}
return Ok((arch, platform));
}
impl<'a, TBackend: IoBackend> PackageDecoder<'a, TBackend>
{
pub fn read(decoder: &mut Decoder<TBackend>) -> Result<PackageDecoder<TBackend>>
{
if decoder.get_main_header().btype != 'P' as u8 {
return Err(Error::Corruption(format!(
"Unknown variant of BPX: {}",
decoder.get_main_header().btype as char
)));
}
if decoder.get_main_header().version != SUPPORTED_VERSION {
return Err(Error::Unsupported(format!(
"This version of the BPX SDK only supports BPXP version {}, you are trying to decode version {} BPXP",
SUPPORTED_VERSION,
decoder.get_main_header().version
)));
}
let (a, p) = get_arch_platform_from_code(
decoder.get_main_header().type_ext[0],
decoder.get_main_header().type_ext[1]
)?;
let strings = match decoder.find_section_by_type(SECTION_TYPE_STRING) {
Some(v) => v,
None => return Err(Error::Corruption(String::from("Unable to locate strings section")))
};
let object_table = match decoder.find_section_by_type(SECTION_TYPE_OBJECT_TABLE) {
Some(v) => v,
None => return Err(Error::Corruption(String::from("Unable to locate BPXP object table")))
};
return Ok(PackageDecoder {
architecture: a,
platform: p,
strings: StringSection::new(strings),
type_code: [
decoder.get_main_header().type_ext[2],
decoder.get_main_header().type_ext[3]
],
decoder,
object_table
});
}
pub fn get_variant(&self) -> [u8; 2]
{
return self.type_code;
}
pub fn get_architecture(&self) -> Architecture
{
return self.architecture;
}
pub fn get_platform(&self) -> Platform
{
return self.platform;
}
pub fn read_metadata(&mut self) -> Result<Option<Object>>
{
if let Some(handle) = self.decoder.find_section_by_type(SECTION_TYPE_SD) {
let mut data = self.decoder.open_section(handle)?;
let obj = Object::read(&mut data)?;
return Ok(Some(obj));
}
return Ok(None);
}
pub fn read_object_table(&mut self) -> Result<ObjectTable>
{
let mut v = Vec::new();
let count = self.decoder.get_section_header(self.object_table).size / 20;
let object_table = self.decoder.open_section(self.object_table)?;
for _ in 0..count {
let mut buf: [u8; 20] = [0; 20];
if object_table.read(&mut buf)? != 20 {
return Err(Error::Truncation("read object table"));
}
let size = LittleEndian::read_u64(&buf[0..8]);
let name_ptr = LittleEndian::read_u32(&buf[8..12]);
let start = LittleEndian::read_u32(&buf[12..16]);
let offset = LittleEndian::read_u32(&buf[16..20]);
v.push(ObjectHeader {
size,
name: name_ptr,
start,
offset
})
}
return Ok(ObjectTable::new(v));
}
pub fn get_object_name(&mut self, obj: &ObjectHeader) -> Result<&str>
{
return self.strings.get(self.decoder, obj.name);
}
fn load_from_section<TWrite: Write>(
&mut self,
handle: SectionHandle,
offset: u32,
size: u32,
out: &mut TWrite
) -> Result<u32>
{
let mut len = 0;
let mut buf: [u8; DATA_READ_BUFFER_SIZE] = [0; DATA_READ_BUFFER_SIZE];
let data = self.decoder.open_section(handle)?;
data.seek(SeekFrom::Start(offset as u64))?;
while len < size {
let s = std::cmp::min(size - len, DATA_READ_BUFFER_SIZE as u32);
let val = data.read(&mut buf[0..s as usize])?;
len += val as u32;
out.write(&buf[0..val])?;
}
return Ok(len);
}
pub fn unpack_object<TWrite: Write>(&mut self, obj: &ObjectHeader, out: &mut TWrite) -> Result<u64>
{
let mut section_id = obj.start;
let mut offset = obj.offset;
let mut len = obj.size;
while len > 0 {
let handle = match self.decoder.find_section_by_index(section_id) {
Some(i) => i,
None => break
};
let remaining_section_size = self.decoder.get_section_header(handle).size - offset;
let val = self.load_from_section(
handle,
offset,
std::cmp::min(remaining_section_size as u64, len) as u32,
out
)?;
len -= val as u64;
offset = 0;
section_id += 1;
}
return Ok(obj.size);
}
}