use delegate::delegate;
#[derive(Clone, Debug)]
pub struct SnapshotChunkData {
code: [u8; 4],
data: Vec<u8>
}
#[allow(missing_docs)]
impl SnapshotChunkData {
pub fn code(&self) -> &[u8; 4] {
&(self.code)
}
pub fn size(&self) -> usize {
self.data.len()
}
pub fn size_as_array(&self) -> [u8; 4] {
let mut size = self.size();
let mut array = [0, 0, 0, 0];
for item in &mut array {
*item = (size % 256) as u8;
size /= 256;
}
array
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn add_bytes(&mut self, data: &[u8]) {
self.data.extend_from_slice(data);
}
}
#[derive(Clone, Debug)]
pub struct MemoryChunk {
pub(crate) data: SnapshotChunkData
}
#[allow(missing_docs)]
impl MemoryChunk {
delegate! {
to self.data {
pub fn code(&self) -> &[u8; 4];
pub fn size(&self) -> usize;
pub fn size_as_array(&self) -> [u8; 4];
pub fn data(&self) -> &[u8];
}
}
pub fn print_info(&self) {
println!(
"\t* Address: 0x{:X}\n\t* Size: 0x{:X}",
self.abstract_address(),
self.uncrunched_memory().len()
);
}
pub fn from(code: [u8; 4], data: Vec<u8>) -> Self {
assert!(code[0] == b'M');
assert!(code[1] == b'E');
assert!(code[2] == b'M');
assert!(
code[3] == b'0'
|| code[3] == b'1'
|| code[3] == b'2'
|| code[3] == b'3'
|| code[3] == b'4'
|| code[3] == b'5'
|| code[3] == b'6'
|| code[3] == b'7'
|| code[3] == b'8'
);
Self {
data: SnapshotChunkData { code, data }
}
}
pub fn build(code: [u8; 4], data: &[u8], compressed: bool) -> Self {
assert_eq!(data.len(), 64 * 1024);
let mut res = Vec::new();
if !compressed {
assert_eq!(data.len(), 64 * 1024);
res.extend(data);
assert_eq!(res.len(), data.len());
res.resize(0x100000, 0);
Self::from(code, res)
}
else {
let mut previous = None;
let mut count = 0;
let mut rle = |previous_value, count| {
if count == 1 {
if previous_value == 0xE5 {
res.push(0xE5);
res.push(0x00);
}
else {
res.push(previous_value);
}
}
else if count == 2 && previous_value != 0xE5 {
res.push(previous_value);
res.push(previous_value);
}
else {
res.push(0xE5);
res.push(count);
res.push(previous_value);
}
};
for current in data.iter() {
let current = *current;
match previous {
None => {
previous.replace(current);
count = 1;
}
Some(previous_value) => {
if previous_value != current || count == 255 {
rle(previous_value, count);
previous.replace(current);
count = 1;
}
else {
count += 1;
}
}
} } if count > 0 {
rle(previous.unwrap(), count);
}
if res.len() >= 65536 {
return Self::from(code, data.to_vec());
}
let chunk = Self::from(code, res.clone());
{
let produced = chunk.uncrunched_memory();
assert_eq!(&data, &produced);
}
chunk
}
}
pub fn uncrunched_memory(&self) -> Vec<u8> {
if !self.is_crunched() {
return self.data.data.clone();
}
let mut content = Vec::new();
let mut idx = 0;
let data = &self.data.data;
let mut read_byte = move || {
if idx == self.data.data.len() {
None
}
else {
let byte = data[idx];
idx += 1;
Some(byte)
}
};
while let Some(byte) = read_byte() {
match byte {
0xE5 => {
let amount = read_byte().unwrap();
if amount == 0 {
content.push(0xE5)
}
else {
let val = read_byte().unwrap();
content.reserve(content.len() + amount as usize);
for _idx in 0..amount {
content.push(val);
}
}
}
val => {
content.push(val);
}
}
}
assert_eq!(content.len(), 64 * 1024);
content
}
pub fn abstract_address(&self) -> usize {
let nb = (self.data.code[3] - b'0') as usize;
nb * 0x10000
}
pub fn is_crunched(&self) -> bool {
self.data.data.len() != 64 * 1024
}
}
#[derive(Clone, Debug)]
pub struct WinapeBreakPointChunk {
data: SnapshotChunkData
}
impl WinapeBreakPointChunk {
delegate! {
to self.data {
pub fn code(&self) -> &[u8; 4];
pub fn size(&self) -> usize;
pub fn size_as_array(&self) -> [u8; 4];
pub fn data(&self) -> &[u8];
pub fn add_bytes(&mut self, data: &[u8]);
}
}
pub fn from(code: [u8; 4], content: Vec<u8>) -> Self {
assert_eq!(code[0], b'B');
assert_eq!(code[1], b'R');
assert_eq!(code[2], b'K');
assert_eq!(code[3], b'S');
Self {
data: SnapshotChunkData {
code,
data: content
}
}
}
pub fn add_breakpoint_raw(&mut self, raw: &[u8]) {
assert!(raw.len() == 5);
self.add_bytes(raw);
}
pub fn nb_breakpoints(&self) -> usize {
self.size() / 5
}
}
#[derive(Clone, Debug)]
pub struct UnknownChunk {
data: SnapshotChunkData
}
impl UnknownChunk {
delegate! {
to self.data {
pub fn code(&self) -> &[u8; 4];
pub fn size(&self) -> usize;
pub fn size_as_array(&self) -> [u8; 4];
pub fn data(&self) -> &[u8];
}
}
pub fn from(code: [u8; 4], data: Vec<u8>) -> Self {
Self {
data: SnapshotChunkData { code, data }
}
}
}
#[derive(Clone, Debug)]
pub enum SnapshotChunk {
Memory(MemoryChunk),
WinapeBreakPoint(WinapeBreakPointChunk),
Unknown(UnknownChunk)
}
#[allow(missing_docs)]
impl SnapshotChunk {
pub fn print_info(&self) {
println!(
"- Chunk: {}{}{}{}",
self.code()[0] as char,
self.code()[1] as char,
self.code()[2] as char,
self.code()[3] as char,
);
if self.is_memory_chunk() {
self.memory_chunk().unwrap().print_info();
}
}
pub fn is_memory_chunk(&self) -> bool {
self.memory_chunk().is_some()
}
pub fn memory_chunk(&self) -> Option<&MemoryChunk> {
match self {
SnapshotChunk::Memory(ref mem) => Some(mem),
_ => None
}
}
pub fn code(&self) -> &[u8; 4] {
match self {
SnapshotChunk::Memory(chunk) => chunk.code(),
SnapshotChunk::Unknown(chunk) => chunk.code(),
SnapshotChunk::WinapeBreakPoint(chunk) => chunk.code()
}
}
pub fn size(&self) -> usize {
match self {
SnapshotChunk::Memory(chunk) => chunk.size(),
SnapshotChunk::WinapeBreakPoint(chunk) => chunk.size(),
SnapshotChunk::Unknown(chunk) => chunk.size()
}
}
pub fn size_as_array(&self) -> [u8; 4] {
match self {
SnapshotChunk::Memory(chunk) => chunk.size_as_array(),
SnapshotChunk::WinapeBreakPoint(ref chunk) => chunk.size_as_array(),
SnapshotChunk::Unknown(chunk) => chunk.size_as_array()
}
}
pub fn data(&self) -> &[u8] {
match self {
SnapshotChunk::Memory(chunk) => chunk.data(),
SnapshotChunk::WinapeBreakPoint(chunk) => chunk.data(),
SnapshotChunk::Unknown(chunk) => chunk.data()
}
}
}
impl From<MemoryChunk> for SnapshotChunk {
fn from(chunk: MemoryChunk) -> Self {
SnapshotChunk::Memory(chunk)
}
}
impl From<WinapeBreakPointChunk> for SnapshotChunk {
fn from(chunk: WinapeBreakPointChunk) -> Self {
SnapshotChunk::WinapeBreakPoint(chunk)
}
}
impl From<UnknownChunk> for SnapshotChunk {
fn from(chunk: UnknownChunk) -> Self {
SnapshotChunk::Unknown(chunk)
}
}