use std::fs::File;
use std::io::prelude::*;
use std::io::Result;
use std::io::{Error, ErrorKind};
use std::string::FromUtf8Error;
use lz4_flex::{frame};
use lz4_flex::frame::FrameDecoder;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_write_test() {
println!("Read write test");
println!();
println!("Hello from the PRTGN Development Team");
println!();
let filename ="test.prtgn".to_string();
let text = "Hello from PRTGN Encoding!".to_string();
let og_txt = text.clone();
write(filename.clone(), text).unwrap();
let decoded = read(filename.clone()).unwrap().to_string();
println!("Original : {:?}", og_txt);
println!("Decoded : {:?}", decoded);
assert_eq!(decoded, og_txt);
println!("Test passed!");
println!();
println!("------------------------------------------------------------");
println!();
std::fs::remove_file(filename).unwrap();
}
#[test]
fn read_write_test_compressed() {
println!("Read write test with compression");
println!();
println!("Hello from the PRTGN Development Team");
println!();
let filename ="test_compressed.prtgn".to_string();
let text = "Hello from PRTGN Encoding. Now with lossless compression!".to_string();
let og_txt = text.clone();
compress_write(filename.clone(), text).unwrap();
let decoded = read(filename.clone()).unwrap().to_string();
println!("Original : {:?}", og_txt);
println!("Decoded : {:?}", decoded);
assert_eq!(decoded, og_txt);
println!("Test passed!");
println!();
println!("------------------------------------------------------------");
println!();
std::fs::remove_file(filename).unwrap();
}
}
const XOR_KEY: u8 = 0x66;
const FILE_HEADER: &[u8] = b"Encoded with PRTGN | https://github.com/PRTGN-Development-Team | Version 0.3.0 | \x66 ";
const FILE_HEADER_COMPRESSED: &[u8] = b"Encoded with PRTGN, compressed with lz4_flex | https://github.com/PRTGN-Development-Team | Version 0.3.0 | \x66 ";
pub fn write(filename: String, text: String) -> Result<()> {
let mut file = File::create(filename)?;
file.write_all(FILE_HEADER)?;
let encoded_bytes: Vec<u8> = text.as_bytes().iter().map(|byte| byte ^ XOR_KEY).collect();
file.write_all(&encoded_bytes)?;
Ok(())
}
pub fn compress_write(filename: String, text: String) -> Result<()> {
let mut file = frame::FrameEncoder::new(File::create(filename)?);
file.write_all(FILE_HEADER_COMPRESSED)?;
let encoded_bytes: Vec<u8> = text.as_bytes().iter().map(|byte| byte ^ XOR_KEY).collect();
file.write_all(&encoded_bytes)?;
file.finish()?;
Ok(())
}
pub fn read(filename: String) -> Result<String> {
let mut file = File::open(filename.clone())?;
let mut header_buffer = vec![0u8; FILE_HEADER.len()];
file.read_exact(&mut header_buffer)?;
let mut outer_decompressed: Vec<u8> = Vec::new();
let version = if header_buffer.starts_with(FILE_HEADER) {
"newest"
} else if header_buffer.starts_with(b"Encoded with PRTGN | https://github.com/PRTGN-Development-Team | Version 0.2.0 | \x66 ") {
"0.2.0"
} else if header_buffer.starts_with(b"Encoded with PRTGN | https://github.com/PRTGN-Development-Team \x66 ") {
"0.1.3"
} else if header_buffer.starts_with(b"Encoded with PRTGN | https://github.com/PRTGN-Development-Team\x01\xFF\x00 ") {
"0.1.2"
} else if header_buffer.starts_with(b"Encoded with PRTGN | https://github.com/PRTGN-Development-Team\x01\xFF\x00") {
"0.1.1"
} else {
let temp = File::open(filename.clone())?;
let mut decoder = FrameDecoder::new(temp);
decoder.read_to_end(&mut outer_decompressed)?;
if outer_decompressed.starts_with(FILE_HEADER_COMPRESSED) {
"newest.24601"
} else {
return Err(Error::new(ErrorKind::InvalidData, "Not a valid PRTGN encoded file."));
}
};
println!("Detected version: {}", version);
if version != "newest" && version != "newest.24601" {
println!("The version of encoding is outdated. Automatically rewriting the file after read.");
println!("---------------------------------------------");
print!("");
};
let mut file_buffer = Vec::new();
file.read_to_end(&mut file_buffer)?;
let decoded_byte: Vec<u8> = if version == "newest" {
file_buffer.iter().map(|byte| byte ^ XOR_KEY).collect()
} else if version == "0.1.3" {
file_buffer.iter().map(|byte| byte ^ 0x66).collect()
} else if version == "0.1.2" {
file_buffer.iter().map(|byte| byte ^ 0xA3).collect()
} else if version == "0.1.1" {
file_buffer.iter().map(|byte| byte ^ 0xA3).collect()
} else if version == "newest.24601" {
outer_decompressed[FILE_HEADER_COMPRESSED.len()..]
.iter()
.map(|byte| byte ^ XOR_KEY)
.collect()
} else {
println!("No decoding for you. Old sport.");
unreachable!()
};
String::from_utf8(decoded_byte)
.map_err(|e: FromUtf8Error| Error::new(ErrorKind::InvalidData, e.to_string()))
}