Lots of changes, expand to read
- Add notes folder with MDBook documentation (the NOTES.md file was getting kind of large) - Add rz_analyze.py, does the same a r2_analyze.py just with Rizin instead of radare2 so the project can be loaded in Cutter (*and* it's faster) - Add Scrap.rzdb, Rizin database for the Scrap.exe executable - Add Scrapper_rs, Rust version of .packed extractor and repacker - replace helplib.txt with helplib.md - add Py_Docs folder which contains generated documentation for the binary python modules built into Scrap.exe
This commit is contained in:
		
							parent
							
								
									43c01e81d2
								
							
						
					
					
						commit
						7afdfb5869
					
				
					 50 changed files with 483086 additions and 1709 deletions
				
			
		
							
								
								
									
										4
									
								
								Scrapper_rs/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Scrapper_rs/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| target | ||||
| ext | ||||
| .history | ||||
| Cargo.lock | ||||
							
								
								
									
										12
									
								
								Scrapper_rs/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Scrapper_rs/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| [package] | ||||
| name = "scrapper" | ||||
| version = "0.1.0" | ||||
| authors = ["Daniel Seiller <earthnuker@gmail.com>"] | ||||
| edition = "2018" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| byteorder = "1.4.2" | ||||
| encoding = "0.2.33" | ||||
| structopt = {version="0.3.21",features = [ "paw" ]} | ||||
							
								
								
									
										202
									
								
								Scrapper_rs/src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								Scrapper_rs/src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,202 @@ | |||
| use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; | ||||
| use encoding::{all::WINDOWS_1252, EncoderTrap}; | ||||
| use encoding::{DecoderTrap, Encoding}; | ||||
| use std::{ | ||||
|     collections::VecDeque, | ||||
|     convert::TryInto, | ||||
|     fs::{self, File}, | ||||
|     io::Seek, | ||||
|     path::PathBuf, | ||||
| }; | ||||
| 
 | ||||
| use std::{fs::create_dir_all, io::SeekFrom}; | ||||
| use structopt::{StructOpt, paw}; | ||||
| use std::{ | ||||
|     io::{BufReader, BufWriter, Read, Write}, | ||||
|     path::Path, | ||||
| }; | ||||
| 
 | ||||
| #[derive(StructOpt)] | ||||
| #[structopt(about = "Scrapland .packed packer and unpacker")] | ||||
| enum Args { | ||||
|     /// Unpack .packed file
 | ||||
|     Unpack { | ||||
|         destination_folder: PathBuf, | ||||
|         packed_files: Vec<PathBuf>, | ||||
|     }, | ||||
|     /// Repack .packed file
 | ||||
|     Repack { | ||||
|         input_folder: PathBuf, | ||||
|         destination_folder: PathBuf | ||||
|     } | ||||
| } | ||||
| #[derive(Debug, PartialEq, Eq)] | ||||
| struct FileEntry { | ||||
|     path: String, | ||||
|     offset: u32, | ||||
|     size: u32, | ||||
| } | ||||
| #[derive(Debug, PartialEq, Eq)] | ||||
| struct Packed { | ||||
|     path: PathBuf, | ||||
|     ext_path: Option<PathBuf>, | ||||
|     magic: [u8; 4], | ||||
|     version: [u8; 4], | ||||
|     files: Vec<FileEntry>, | ||||
| } | ||||
| 
 | ||||
| impl Packed { | ||||
|     fn from_file(filename: &PathBuf) -> std::io::Result<Packed> { | ||||
|         let mut fh = BufReader::new(File::open(filename)?); | ||||
|         let magic: [u8; 4] = [fh.read_u8()?, fh.read_u8()?, fh.read_u8()?, fh.read_u8()?]; | ||||
|         let version: [u8; 4] = [fh.read_u8()?, fh.read_u8()?, fh.read_u8()?, fh.read_u8()?]; | ||||
|         println!("Magic: {:?}", magic); | ||||
|         println!("Version: {:?}", version); | ||||
|         let num_files = fh.read_u32::<LittleEndian>()?; | ||||
|         println!("{} files", num_files); | ||||
|         let mut files: Vec<FileEntry> = Vec::new(); | ||||
|         for _ in 0..num_files { | ||||
|             let name_len = fh.read_u32::<LittleEndian>()?; | ||||
|             let mut name = vec![0; name_len as usize]; | ||||
|             fh.read_exact(&mut name)?; | ||||
|             let size = fh.read_u32::<LittleEndian>()?; | ||||
|             let offset = fh.read_u32::<LittleEndian>()?; | ||||
|             let path = WINDOWS_1252.decode(&name, DecoderTrap::Strict).unwrap(); | ||||
|             files.push(FileEntry { path, offset, size }); | ||||
|         } | ||||
|         Ok(Packed { | ||||
|             path: filename.to_owned(), | ||||
|             magic, | ||||
|             version, | ||||
|             files, | ||||
|             ext_path: None, | ||||
|         }) | ||||
|     } | ||||
|     fn size(&self) -> (u64, u64) { | ||||
|         let mut header_size: u64 = 4 * 3; // Magic+Version+Num files
 | ||||
|         let mut data_size: u64 = 0; | ||||
|         for entry in &self.files { | ||||
|             header_size += 4 * 3; // Name length, Offset, Size
 | ||||
|             let path = WINDOWS_1252 | ||||
|                 .encode(&entry.path, EncoderTrap::Strict) | ||||
|                 .unwrap(); | ||||
|             let hs: u64 = path.len().try_into().unwrap(); | ||||
|             header_size += hs; | ||||
|             data_size += entry.size as u64; | ||||
|         } | ||||
|         (header_size, data_size) | ||||
|     } | ||||
| 
 | ||||
|     fn from_folder(folder: PathBuf) -> std::io::Result<Self> { | ||||
|         let mut queue = VecDeque::new(); | ||||
|         queue.push_back(folder.clone()); | ||||
|         let mut header = Packed { | ||||
|             path: PathBuf::new(), | ||||
|             magic: [0x42, 0x46, 0x50, 0x4B], // BFPK
 | ||||
|             version: [0x00, 0x00, 0x00, 0x00], | ||||
|             files: vec![], | ||||
|             ext_path: Some(folder.clone()), | ||||
|         }; | ||||
|         while let Some(dir) = queue.pop_front() { | ||||
|             for entry in fs::read_dir(dir)? { | ||||
|                 let entry = entry?; | ||||
|                 let path = entry.path(); | ||||
|                 if path.is_dir() { | ||||
|                     queue.push_back(path); | ||||
|                 } else { | ||||
|                     let size = path.metadata().unwrap().len().try_into().unwrap(); | ||||
|                     header.files.push(FileEntry { | ||||
|                         path: path | ||||
|                             .strip_prefix(folder.clone()) | ||||
|                             .unwrap() | ||||
|                             .to_string_lossy() | ||||
|                             .replace("\\", "/"), | ||||
|                         offset: 0, | ||||
|                         size, | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         let mut offset = header.size().0.try_into().unwrap(); | ||||
|         for entry in header.files.iter_mut() { | ||||
|             entry.offset = offset; | ||||
|             offset += entry.size; | ||||
|         } | ||||
|         Ok(header) | ||||
|     } | ||||
| 
 | ||||
|     fn write(&self, out_path: &PathBuf) -> std::io::Result<()> { | ||||
|         let base_path = self.ext_path.clone().unwrap(); | ||||
|         let mut outfile = BufWriter::new(File::create(out_path)?); | ||||
|         outfile.write_all(&self.magic)?; | ||||
|         outfile.write_all(&self.version)?; | ||||
|         outfile.write_u32::<LittleEndian>(self.files.len() as u32)?; | ||||
|         println!("Building header"); | ||||
|         for entry in &self.files { | ||||
|             let path = WINDOWS_1252 | ||||
|                 .encode(&entry.path, EncoderTrap::Strict) | ||||
|                 .unwrap(); | ||||
|             outfile.write_u32::<LittleEndian>(path.len().try_into().unwrap())?; | ||||
|             outfile.write_all(&path)?; | ||||
|             outfile.write_u32::<LittleEndian>(entry.size)?; | ||||
|             outfile.write_u32::<LittleEndian>(entry.offset)?; | ||||
|         } | ||||
|         let total = self.files.len(); | ||||
|         for (n, entry) in self.files.iter().enumerate() { | ||||
|             println!( | ||||
|                 "[{}/{}] Writing: {} (offset: {}, size: {})", | ||||
|                 n + 1, | ||||
|                 total, | ||||
|                 entry.path, | ||||
|                 entry.offset, | ||||
|                 entry.size | ||||
|             ); | ||||
|             let mut fh = BufReader::new(File::open(base_path.join(&entry.path))?); | ||||
|             std::io::copy(&mut fh, &mut outfile)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn extract(&mut self, ext_folder: &PathBuf) -> std::io::Result<()> { | ||||
|         let total = self.files.len(); | ||||
|         let ext_folder = ext_folder.join(&Path::new(&self.path).file_name().unwrap()); | ||||
|         println!("Extracting to {}", ext_folder.to_string_lossy()); | ||||
|         let mut fh = File::open(self.path.clone())?; | ||||
|         for (n, entry) in self.files.iter().enumerate() { | ||||
|             println!( | ||||
|                 "[{}/{}] Extracting: {} (offset: {}, size: {})", | ||||
|                 n + 1, | ||||
|                 total, | ||||
|                 entry.path, | ||||
|                 entry.offset, | ||||
|                 entry.size | ||||
|             ); | ||||
|             fh.seek(SeekFrom::Start(entry.offset.try_into().unwrap()))?; | ||||
|             let mut data = vec![0; entry.size.try_into().unwrap()]; | ||||
|             let path: PathBuf = PathBuf::from(&entry.path); | ||||
|             let file = Path::new(&ext_folder).join(path); | ||||
|             fh.read_exact(&mut data)?; | ||||
|             create_dir_all(file.parent().unwrap())?; | ||||
|             let mut fh = BufWriter::new(File::create(file)?); | ||||
|             fh.write_all(&data)?; | ||||
|         } | ||||
|         self.ext_path = Some(ext_folder); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[paw::main] | ||||
| fn main(args: Args) -> std::io::Result<()> { | ||||
|     match args { | ||||
|         Args::Unpack { packed_files, destination_folder } => { | ||||
|             for packed_file in &packed_files { | ||||
|                 let mut pkd = Packed::from_file(&packed_file)?; | ||||
|                 pkd.extract(&destination_folder)?; | ||||
|             } | ||||
|         } | ||||
|         Args::Repack { input_folder, destination_folder } => { | ||||
|             Packed::from_folder(input_folder)?.write(&destination_folder)?; | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue