use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::common::StructureError;
use crate::structures::gif::{parse_gif_extension, parse_gif_header, parse_gif_image_descriptor};
pub fn gif_extractor() -> Extractor {
Extractor {
do_not_recurse: true,
utility: ExtractorType::Internal(extract_gif_image),
..Default::default()
}
}
pub fn extract_gif_image(
file_data: &[u8],
offset: usize,
output_directory: Option<&String>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "image.gif";
let mut result = ExtractionResult {
..Default::default()
};
if let Ok(gif_header) = parse_gif_header(&file_data[offset..]) {
if let Some(gif_image_data) = file_data.get(offset + gif_header.size..) {
if let Some(gif_data_size) = get_gif_data_size(gif_image_data) {
result.size = Some(gif_header.size + gif_data_size);
result.success = true;
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success =
chroot.carve_file(OUTFILE_NAME, file_data, offset, result.size.unwrap());
}
}
}
}
result
}
fn get_gif_data_size(gif_data: &[u8]) -> Option<usize> {
const EXTENSION: u8 = 0x21;
const TERMINATOR: u8 = 0x3B;
const IMAGE_DESCRIPTOR: u8 = 0x2C;
let mut next_offset: usize = 0;
let mut previous_offset = None;
let available_data = gif_data.len();
while is_offset_safe(available_data, next_offset, previous_offset) {
let block_size: Result<usize, StructureError>;
match gif_data.get(next_offset) {
None => break,
Some(block_type) => {
if *block_type == IMAGE_DESCRIPTOR {
block_size = parse_gif_image_descriptor(&gif_data[next_offset..]);
} else if *block_type == EXTENSION {
block_size = parse_gif_extension(&gif_data[next_offset..]);
} else if *block_type == TERMINATOR {
return Some(next_offset + 1);
} else {
break;
}
}
}
match block_size {
Err(_) => break,
Ok(this_block_size) => {
previous_offset = Some(next_offset);
next_offset += this_block_size;
}
}
}
None
}