use anyhow::Result;
use byteorder::{LittleEndian, WriteBytesExt};
use encoding_rs::SHIFT_JIS;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::io::{Cursor, Seek, SeekFrom, Write};
pub struct SerializationState {
raw_text: Vec<u8>,
written_text_offsets: HashMap<String, usize>,
raw_pointers: Vec<u32>,
raw_labels: Vec<u32>,
}
impl SerializationState {
pub fn new() -> Self {
SerializationState {
raw_text: Vec::new(),
written_text_offsets: HashMap::new(),
raw_pointers: Vec::new(),
raw_labels: Vec::new(),
}
}
pub fn assemble(&self, data: &Vec<u8>) -> Result<Vec<u8>> {
let mut output: Vec<u8> = Vec::new();
let file_size = data.len()
+ self.raw_pointers.len() * 4
+ (self.raw_labels.len() / 2) * 8
+ self.raw_text.len()
+ 0x20;
output.write_u32::<LittleEndian>(file_size as u32)?;
output.write_u32::<LittleEndian>(data.len() as u32)?;
output.write_u32::<LittleEndian>(self.raw_pointers.len() as u32)?;
output.write_u32::<LittleEndian>((self.raw_labels.len() / 2) as u32)?;
output.write_u64::<LittleEndian>(0)?;
output.write_u64::<LittleEndian>(0)?;
output.write_all(data)?;
for ptr in &self.raw_pointers {
output.write_u32::<LittleEndian>(*ptr)?;
}
for ptr in &self.raw_labels {
output.write_u32::<LittleEndian>(*ptr)?;
}
output.write_all(&self.raw_text)?;
Ok(output)
}
pub fn add_text(&mut self, text: &str) -> u32 {
if self.written_text_offsets.contains_key(text) {
return *self.written_text_offsets.get(text).unwrap() as u32;
}
let offset = self.raw_text.len();
let (result, _enc, _errors) = SHIFT_JIS.encode(text);
let bytes = result.to_vec();
for byte in bytes {
self.raw_text.push(byte);
}
self.raw_text.push(0);
self.written_text_offsets.insert(text.into(), offset);
offset as u32
}
pub fn load_internal_pointers(
&mut self,
data: &mut Vec<u8>,
internal_pointers: &HashMap<usize, usize>,
) -> Result<()> {
let mut cursor = Cursor::new(data);
let mut pointers: Vec<(&usize, &usize)> = internal_pointers.iter().collect();
pointers.sort_by(|a, b| a.0.cmp(b.0));
for pointer_pair in pointers {
cursor.seek(SeekFrom::Start(*pointer_pair.0 as u64))?;
cursor.write_u32::<LittleEndian>(*pointer_pair.1 as u32)?;
self.raw_pointers.push(*pointer_pair.0 as u32);
}
Ok(())
}
pub fn load_labels(&mut self, labels: &HashMap<usize, Vec<String>>) {
let mut pointers: Vec<(&usize, &Vec<String>)> = labels.iter().collect();
pointers.sort_by(|a, b| a.0.cmp(b.0));
for pointer_pair in pointers {
for string in pointer_pair.1 {
let text_offset = self.add_text(string);
self.raw_labels.push(*pointer_pair.0 as u32);
self.raw_labels.push(text_offset);
}
}
}
pub fn load_text_pointers(
&mut self,
data: &mut Vec<u8>,
text_pointers: &HashMap<usize, String>,
label_start: u32,
) -> Result<()> {
let mut cursor = Cursor::new(data);
let mut pointers: Vec<(&usize, &String)> = text_pointers.iter().collect();
pointers.sort_by(|a, b| a.0.cmp(b.0));
let mut ptr_data_pairs: LinkedHashMap<usize, Vec<u32>> = LinkedHashMap::new();
for pointer_pair in pointers {
let text_offset = self.add_text(pointer_pair.1);
let text_address = label_start + text_offset;
if !ptr_data_pairs.contains_key(&(text_address as usize)) {
ptr_data_pairs.insert(text_address as usize, Vec::new());
}
let bucket = ptr_data_pairs.get_mut(&(text_address as usize)).unwrap();
bucket.push(*pointer_pair.0 as u32);
cursor.seek(SeekFrom::Start(*pointer_pair.0 as u64))?;
cursor.write_u32::<LittleEndian>(text_address as u32)?;
}
for mut ptr_data_pair in ptr_data_pairs {
ptr_data_pair.1.sort();
for ptr in ptr_data_pair.1 {
self.raw_pointers.push(ptr);
}
}
Ok(())
}
}