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