use std::{ fs::{self}, io::{Cursor, Read}, path::PathBuf, }; const BIN_DATA: &[u8] = include_bytes!("../data.bin"); // 4bytes const LENGTH: usize = 4; const IDENTIFIER_LENGTH: usize = 8; const MD5_LENGTH: usize = 32; const BUF_SIZE: usize = 4096; pub(crate) struct BinaryData { pub md5_code: &'static [u8], // compressed gzip data pub raw: &'static [u8], pub path: String, } pub(crate) struct BinaryReader { pub files: Vec, pub exe: String, } impl Default for BinaryReader { fn default() -> Self { let (files, exe) = BinaryReader::read(); Self { files, exe } } } impl BinaryData { fn decompress(&self) -> Vec { let cursor = Cursor::new(self.raw); let mut decoder = brotli::Decompressor::new(cursor, BUF_SIZE); let mut buf = Vec::new(); decoder.read_to_end(&mut buf).unwrap(); buf } pub fn write_to_file(&self, prefix: &PathBuf) { let p = prefix.join(&self.path); if let Some(parent) = p.parent() { if !parent.exists() { let _ = fs::create_dir_all(parent); } } if p.exists() { // check md5 let f = fs::read(p.clone()).unwrap(); let digest = format!("{:x}", md5::compute(&f)); let md5_record = String::from_utf8_lossy(self.md5_code); if digest == md5_record { // same, skip this file println!("skip {}", &self.path); return; } else { println!("writing {}", p.display()); println!("{} -> {}", md5_record, digest) } } let _ = fs::write(p, self.decompress()); } } impl BinaryReader { fn read() -> (Vec, String) { let mut base: usize = 0; let mut parsed = vec![]; assert!(BIN_DATA.len() > IDENTIFIER_LENGTH, "bin data invalid!"); let mut iden = String::from_utf8_lossy(&BIN_DATA[base..base + IDENTIFIER_LENGTH]); if iden != "rustdesk" { panic!("bin file is not vaild!"); } base += IDENTIFIER_LENGTH; loop { iden = String::from_utf8_lossy(&BIN_DATA[base..base + IDENTIFIER_LENGTH]); if iden == "rustdesk" { base += IDENTIFIER_LENGTH; break; } // start reading let mut offset = 0; let path_length = u32::from_be_bytes([ BIN_DATA[base + offset], BIN_DATA[base + offset + 1], BIN_DATA[base + offset + 2], BIN_DATA[base + offset + 3], ]) as usize; offset += LENGTH; let path = String::from_utf8_lossy(&BIN_DATA[base + offset..base + offset + path_length]) .to_string(); offset += path_length; // file sz let file_length = u32::from_be_bytes([ BIN_DATA[base + offset], BIN_DATA[base + offset + 1], BIN_DATA[base + offset + 2], BIN_DATA[base + offset + 3], ]) as usize; offset += LENGTH; let raw = &BIN_DATA[base + offset..base + offset + file_length]; offset += file_length; // md5 let md5 = &BIN_DATA[base + offset..base + offset + MD5_LENGTH]; offset += MD5_LENGTH; parsed.push(BinaryData { md5_code: md5, raw: raw, path: path, }); base += offset; } // executable let executable = String::from_utf8_lossy(&BIN_DATA[base..]).to_string(); (parsed, executable) } #[cfg(unix)] pub fn configure_permission(&self, prefix: &PathBuf) { use std::os::unix::prelude::PermissionsExt; let exe_path = prefix.join(&self.exe); if exe_path.exists() { let f = File::open(exe_path).unwrap(); let meta = f.metadata().unwrap(); let mut permissions = meta.permissions(); permissions.set_mode(0o755); f.set_permissions(permissions).unwrap(); } } }