From 1aef1f73d9ea84536887d8b4113c31055327113f Mon Sep 17 00:00:00 2001 From: Quaternions Date: Mon, 8 Jan 2024 15:09:12 -0800 Subject: [PATCH] minor refactor to add/respect some cli options --- src/main.rs | 80 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index 51ff62c..998758a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,13 @@ struct Cli{ #[arg(long)] cookie_file:Option, + #[arg(long)] + no_models:Option, + #[arg(long)] + no_scripts:Option, + #[arg(long)] + no_template:Option, + #[arg(long)] asset_id:Option, @@ -90,11 +97,21 @@ async fn main()->AResult<()>{ }; match cli.command{ - Commands::DownloadHistory=>download_history(cookie.unwrap(),cli.asset_id.unwrap()).await, + Commands::DownloadHistory=>download_history(DownloadHistoryConfig{ + output_folder:cli.output.unwrap(), + cookie:cookie.unwrap(), + asset_id:cli.asset_id.unwrap(), + }).await, Commands::Download=>download_list(cookie.unwrap(),vec![(cli.asset_id.unwrap(),cli.output.unwrap())]).await, Commands::Upload=>upload_list(cookie.unwrap(),cli.group,vec![(cli.asset_id.unwrap(),cli.output.unwrap())]).await, Commands::Compile=>compile(cli.input.unwrap(),cli.output.unwrap()), - Commands::Decompile=>decompile(cli.input.unwrap(),cli.output.unwrap()), + Commands::Decompile=>decompile(DecompileConfig{ + input_file:cli.input.unwrap(), + output_folder:cli.output.unwrap(), + write_template:!cli.no_template.unwrap_or(false), + write_models:!cli.no_models.unwrap_or(false), + write_scripts:!cli.no_scripts.unwrap_or(false), + }), } } @@ -216,15 +233,21 @@ async fn download_list(cookie:String,asset_id_file_map:AssetIDFileMap)->AResult< Ok(()) } -async fn download_history(cookie:String,asset_id:AssetID)->AResult<()>{ +struct DownloadHistoryConfig{ + output_folder:std::path::PathBuf, + cookie:String, + asset_id:AssetID, +} + +async fn download_history(config:DownloadHistoryConfig)->AResult<()>{ let client=reqwest::Client::new(); - let asset_id_string=asset_id.to_string(); + let asset_id_string=config.asset_id.to_string(); //poll paged list of all asset versions let mut cursor:Option=None; let mut asset_list=Vec::new(); loop{ - let mut url=reqwest::Url::parse(format!("https://develop.roblox.com/v1/assets/{}/saved-versions",asset_id).as_str())?; + let mut url=reqwest::Url::parse(format!("https://develop.roblox.com/v1/assets/{}/saved-versions",config.asset_id).as_str())?; //url borrow scope { let mut query=url.query_pairs_mut();//borrow here @@ -238,7 +261,7 @@ async fn download_history(cookie:String,asset_id:AssetID)->AResult<()>{ } println!("page url={}",url); let resp=client.get(url) - .header("Cookie",cookie.clone()) + .header("Cookie",config.cookie.clone()) .send().await?; match resp.json::().await{ Ok(mut page)=>{ @@ -252,7 +275,7 @@ async fn download_history(cookie:String,asset_id:AssetID)->AResult<()>{ } } asset_list.sort_by(|a,b|a.assetVersionNumber.cmp(&b.assetVersionNumber)); - let mut path=std::path::PathBuf::new(); + let mut path=config.output_folder.clone(); path.set_file_name("versions.json"); tokio::fs::write(path,serde_json::to_string(&asset_list)?).await?; @@ -260,8 +283,9 @@ async fn download_history(cookie:String,asset_id:AssetID)->AResult<()>{ futures::stream::iter(asset_list) .map(|asset_version|{ let client=&client; - let cookie=cookie.as_str(); + let cookie=config.cookie.as_str(); let asset_id_str=asset_id_string.as_str(); + let output_folder=config.output_folder.clone(); async move{ let mut url=reqwest::Url::parse("https://assetdelivery.roblox.com/v1/asset/")?; //url borrow scope @@ -282,8 +306,8 @@ async fn download_history(cookie:String,asset_id:AssetID)->AResult<()>{ continue; } - let mut path=std::path::PathBuf::new(); - path.set_file_name(format!("{}_v{}.rbxl",asset_id,asset_version.assetVersionNumber)); + let mut path=output_folder; + path.set_file_name(format!("{}_v{}.rbxl",config.asset_id,asset_version.assetVersionNumber)); result=Ok((path,resp.bytes().await?)); break; } @@ -387,11 +411,14 @@ fn sanitize<'a>(s:&'a str)->std::borrow::Cow<'a,str>{ lazy_regex::regex!(r"[^a-zA-Z0-9._-]").replace_all(s,"_") } -fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeNode)->AResult<()>{ +fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeNode,write_models:bool,write_scripts:bool)->AResult<()>{ file.push(sanitize(node.name.as_str()).as_ref()); match node.class{ Class::Folder=>(), Class::ModuleScript|Class::LocalScript|Class::Script=>{ + if !write_scripts{ + return Ok(()) + } assert!(file.set_extension("lua"),"could not set extension"); assert!(dom.get_by_ref(node.referent).is_some_and(|item|{ //TODO: delete disabled scripts @@ -401,6 +428,9 @@ fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeN }),"no string property or file failed to write"); }, Class::Model=>{ + if !write_models{ + return Ok(()) + } assert!(file.set_extension("rbxmx")); let output=std::io::BufWriter::new(std::fs::File::create(file)?); rbx_xml::to_writer_default(output,dom,&[node.referent])?; @@ -409,7 +439,15 @@ fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeN Ok(()) } -fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AResult<()>{ +struct DecompileConfig{ + input_file:std::path::PathBuf, + output_folder:std::path::PathBuf, + write_template:bool, + write_models:bool, + write_scripts:bool, +} + +fn decompile(config:DecompileConfig)->AResult<()>{ //rules: //Class Script|LocalScript|ModuleScript->$Name.lua //Class Model->$Name.rbxmx @@ -417,7 +455,7 @@ fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AR //Everything else goes into template.rbxlx //read file - let mut input=std::io::BufReader::new(std::fs::File::open(input_file)?); + let mut input=std::io::BufReader::new(std::fs::File::open(config.input_file)?); let mut dom=load_dom(&mut input)?; let mut tree_refs=std::collections::HashMap::new(); @@ -510,8 +548,8 @@ fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AR //generate folders, models, and scripts //delete models and scripts from dom - { - let mut folder=output_folder.clone(); + + let mut folder=config.output_folder.clone(); let mut stack=vec![WriteStackInstruction::Node(tree_refs.get(&dom.root_ref()).unwrap())]; while let Some(instruction)=stack.pop(){ match instruction{ @@ -548,10 +586,10 @@ fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AR std::fs::write(file,serde_json::to_string(&properties)?)? } //write item in subfolder - write_item(&dom,subfolder,node)?; + write_item(&dom,subfolder,node,config.write_models,config.write_scripts)?; }else{ //write item - write_item(&dom,folder.clone(),node)?; + write_item(&dom,folder.clone(),node,config.write_models,config.write_scripts)?; } //queue item to be deleted from dom after child objects are handled (stack is popped from the back) match node.class{ @@ -570,10 +608,10 @@ fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AR }, } } - } + //write what remains in template.rbxlx - { - let mut file=output_folder.clone(); + if config.write_template{ + let mut file=config.output_folder.clone(); file.push("template"); assert!(file.set_extension("rbxlx")); let output=std::io::BufWriter::new(std::fs::File::create(file)?); @@ -585,4 +623,4 @@ fn decompile(input_file:std::path::PathBuf,output_folder:std::path::PathBuf)->AR fn compile(_folder:std::path::PathBuf,_file:std::path::PathBuf)->AResult<()>{ Ok(()) -} \ No newline at end of file +}