Skip to content

Commit 1891160

Browse files
committed
Initial commit.
1 parent 864ccbb commit 1891160

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Cargo.lock
1212

1313
# MSVC Windows builds of rustc generate these, which store debugging information
1414
*.pdb
15+
.idea/

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "rust-unityextractor"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
argparse = "0.2.2"
10+
flate2 = "1.0.20"
11+
tar = "0.4.36"
12+
tokio = { version = "1.12.0", features = ["full"] }
13+
log = "0.4.14"
14+
simple_logger = "4.1.0"

src/main.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use argparse::{ArgumentParser, StoreTrue, Store};
2+
use flate2::read::GzDecoder;
3+
use std::fs::File;
4+
use std::io::{Read};
5+
use std::path::PathBuf;
6+
use tar::Archive;
7+
use tokio::fs;
8+
use log::{info, LevelFilter};
9+
use simple_logger::SimpleLogger;
10+
use std::collections::HashMap;
11+
12+
#[tokio::main]
13+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
14+
// Parse command-line arguments
15+
let mut verbose = false;
16+
let mut input_path = String::new();
17+
{
18+
let mut parser = ArgumentParser::new();
19+
parser.set_description("Unitypackage extractor");
20+
parser.refer(&mut verbose)
21+
.add_option(&["-v"], StoreTrue, "Verbose mode");
22+
parser.refer(&mut input_path)
23+
.add_argument("input", Store, "Unitypackage (.tar.gz) file")
24+
.required();
25+
parser.parse_args_or_exit();
26+
}
27+
28+
if verbose {
29+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
30+
} else {
31+
SimpleLogger::new().with_level(LevelFilter::Error).init().unwrap();
32+
}
33+
34+
// Open the unitypackage file
35+
let file = File::open(&input_path)?;
36+
let decoder = GzDecoder::new(file);
37+
let mut archive = Archive::new(decoder);
38+
let mut assets: HashMap<PathBuf, Vec<u8>> = HashMap::new();
39+
40+
// Iterate over each entry in the archive
41+
for entry_result in archive.entries()? {
42+
let mut entry = entry_result?;
43+
let path = entry.path()?.to_path_buf();
44+
45+
// If the entry is an 'asset' file, read its content
46+
if path.ends_with("asset") {
47+
let mut asset_data = Vec::new();
48+
entry.read_to_end(&mut asset_data)?;
49+
assets.insert(path.clone(), asset_data);
50+
}
51+
// If the entry is a 'pathname' file, read its content and write the asset
52+
else if path.ends_with("pathname") {
53+
let mut pathname = String::new();
54+
entry.read_to_string(&mut pathname)?;
55+
56+
// Sanitize the pathname
57+
let pathname = pathname.trim().replace("\\", "/");
58+
let target_path = PathBuf::from(&pathname);
59+
60+
// Create directories for the target path
61+
if let Some(parent) = target_path.parent() {
62+
fs::create_dir_all(parent).await?;
63+
}
64+
65+
// Write the asset data to the target path
66+
let asset_path = path.parent().unwrap().join("asset");
67+
if let Some(asset_data) = assets.remove(&asset_path) {
68+
fs::write(&target_path, &asset_data).await?;
69+
70+
info!("Extracted: {}", pathname);
71+
}
72+
}
73+
}
74+
75+
Ok(())
76+
}

0 commit comments

Comments
 (0)