diff --git a/src/main.rs b/src/main.rs index 48e116e..7ae4bd0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,8 @@ struct Cli{ no_scripts:Option, #[arg(long)] no_template:Option, + #[arg(long)] + style:Option, //git options #[arg(long)] @@ -61,6 +63,13 @@ enum Commands{ DownloadAndDecompileHistoryIntoGit, } +#[derive(Clone,Copy)] +enum DecompileStyle{ + Rox, + Rojo, + RoxRojo, +} + #[derive(Args)] struct PathBufList{ paths:Vec @@ -108,6 +117,21 @@ async fn main()->AResult<()>{ None=>None, }; + let decompile_style=match cli.style.as_deref(){ + Some("rox") + |Some("Rox")=>Some(DecompileStyle::Rox), + Some("rojo") + |Some("Rojo")=>Some(DecompileStyle::Rojo), + Some("rox-rojo") + |Some("rojo-rox") + |Some("roxrojo") + |Some("rojorox") + |Some("RoxRojo") + |Some("RojoRox")=>Some(DecompileStyle::RoxRojo), + None=>None, + _=>return Err(anyhow::Error::msg("Invalid style")), + }; + match cli.command{ Commands::DownloadHistory=>download_history(DownloadHistoryConfig{ output_folder:cli.output.unwrap(), @@ -118,6 +142,7 @@ async fn main()->AResult<()>{ 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(DecompileConfig{ + style:decompile_style.unwrap(), input_file:cli.input.unwrap(), output_folder:cli.output.unwrap(), write_template:!cli.no_template.unwrap_or(false), @@ -129,6 +154,7 @@ async fn main()->AResult<()>{ git_committer_email:cli.git_committer_email.unwrap(), input_folder:cli.input.unwrap(), output_folder:cli.output.unwrap(), + style:decompile_style.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), @@ -139,6 +165,7 @@ async fn main()->AResult<()>{ cookie:cookie.unwrap(), asset_id:cli.asset_id.unwrap(), output_folder:cli.output.unwrap(), + style:decompile_style.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), @@ -427,23 +454,34 @@ enum WriteStackInstruction<'a>{ Destroy(Ref), } -#[derive(Default,serde::Deserialize,serde::Serialize)] -#[allow(nonstandard_style,dead_code)] +#[derive(Default)] struct PropertiesOverride{ - //Name:Option, - ClassName:Option, + name:Option, + class_name:Option, } impl PropertiesOverride{ fn is_some(&self)->bool{ - self.ClassName.is_some() + self.name.is_some() + ||self.class_name.is_some() } } +impl std::fmt::Display for PropertiesOverride{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + if let Some(name)=self.name.as_deref(){ + writeln!(f,"--! Properties.Name=\"{}\"",name)?; + } + if let Some(class_name)=self.class_name.as_deref(){ + writeln!(f,"--! Properties.ClassName=\"{}\"",class_name)?; + } + Ok(()) + } +} 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,node_name_override:String,write_models:bool,write_scripts:bool)->AResult<()>{ +fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeNode,node_name_override:String,mut properties:PropertiesOverride,style:DecompileStyle,write_models:bool,write_scripts:bool)->AResult<()>{ file.push(sanitize(node_name_override.as_str()).as_ref()); match node.class{ Class::Folder=>(), @@ -451,11 +489,40 @@ fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeN if !write_scripts{ return Ok(()) } - assert!(file.set_extension("lua"),"could not set extension"); + + //set extension + match style{ + DecompileStyle::Rox=>assert!(file.set_extension("lua"),"could not set extension"), + DecompileStyle::RoxRojo|DecompileStyle::Rojo=>{ + match properties.class_name.as_deref(){ + Some("LocalScript")=>{ + file.set_extension("client.lua"); + properties.class_name=None; + }, + Some("Script")=>{ + file.set_extension("server.lua"); + properties.class_name=None; + }, + // Some("ModuleScript")=>{ + // file.set_extension("module"); + // properties.class_name=None; + // }, + None=>assert!(file.set_extension("lua"),"could not set extension"), + Some(other)=>return Err(anyhow::Error::msg(format!("Attempt to write a {} as a script",other))), + } + } + } + if let Some(item)=dom.get_by_ref(node.referent){ //TODO: delete disabled scripts if let Some(rbx_dom_weak::types::Variant::String(source))=item.properties.get("Source"){ - std::fs::write(file,source)?; + if properties.is_some(){ + //rox style + let source=properties.to_string()+source.as_str(); + std::fs::write(file,source)?; + }else{ + std::fs::write(file,source)?; + } } } }, @@ -574,6 +641,7 @@ fn generate_decompiled_context(input:R)->AResult{ } struct WriteConfig{ + style:DecompileStyle, output_folder:std::path::PathBuf, write_template:bool, write_models:bool, @@ -593,44 +661,41 @@ async fn write_files(config:WriteConfig,mut context:DecompiledContext)->AResult< WriteStackInstruction::PopFolder=>assert!(folder.pop(),"weirdness"), WriteStackInstruction::Destroy(referent)=>destroy_queue.push(referent), WriteStackInstruction::Node(node,name_count)=>{ - //properties.json to override class or other simple properties + //track properties that must be overriden to compile folder structure back into a place file let mut properties=PropertiesOverride::default(); let has_children=node.children.len()!=0; match node.class{ Class::Folder=>(), - Class::ModuleScript=>{ - //.lua files are ModuleScript by default - if has_children{ - properties.ClassName=Some("ModuleScript".to_string()) - } - }, - Class::LocalScript=>properties.ClassName=Some("LocalScript".to_string()), - Class::Script=>properties.ClassName=Some("Script".to_string()), + Class::ModuleScript=>(),//.lua files are ModuleScript by default + Class::LocalScript=>properties.class_name=Some("LocalScript".to_string()), + Class::Script=>properties.class_name=Some("Script".to_string()), Class::Model=>(), } let name_override=if 0name_override.clone(), + DecompileStyle::Rojo=>"init".to_owned(), + }; + //write item in subfolder - write_queue.push((subfolder,node,name_override.clone())); + write_queue.push((subfolder,node,name_final,properties,config.style)); }else{ //write item - write_queue.push((folder.clone(),node,name_override.clone())); + write_queue.push((folder.clone(),node,name_override.clone(),properties,config.style)); } //queue item to be deleted from dom after child objects are handled (stack is popped from the back) match node.class{ @@ -657,8 +722,8 @@ async fn write_files(config:WriteConfig,mut context:DecompiledContext)->AResult< let dom=&context.dom; let write_models=config.write_models; let write_scripts=config.write_scripts; - let results:Vec>=rayon::iter::ParallelIterator::collect(rayon::iter::ParallelIterator::map(rayon::iter::IntoParallelIterator::into_par_iter(write_queue),|(write_path,node,node_name_override)|{ - write_item(&dom,write_path,node,node_name_override,write_models,write_scripts) + let results:Vec>=rayon::iter::ParallelIterator::collect(rayon::iter::ParallelIterator::map(rayon::iter::IntoParallelIterator::into_par_iter(write_queue),|(write_path,node,node_name_override,properties,style)|{ + write_item(&dom,write_path,node,node_name_override,properties,style,write_models,write_scripts) })); for result in results{ result?; @@ -683,6 +748,7 @@ async fn write_files(config:WriteConfig,mut context:DecompiledContext)->AResult< } struct DecompileConfig{ + style:DecompileStyle, input_file:std::path::PathBuf, output_folder:std::path::PathBuf, write_template:bool, @@ -703,6 +769,7 @@ async fn decompile(config:DecompileConfig)->AResult<()>{ //generate folders, models, and scripts //delete models and scripts from dom write_files(WriteConfig{ + style:config.style, output_folder:config.output_folder, write_template:config.write_template, write_models:config.write_models, @@ -716,6 +783,7 @@ struct WriteCommitConfig{ git_committer_name:String, git_committer_email:String, output_folder:std::path::PathBuf, + style:DecompileStyle, write_template:bool, write_models:bool, write_scripts:bool, @@ -745,6 +813,7 @@ async fn write_commit(config:WriteCommitConfig,b:ResultAResult<()>{ match write_commit(WriteCommitConfig{ git_committer_name:config.git_committer_name.clone(), git_committer_email:config.git_committer_email.clone(), + style:config.style, output_folder:config.output_folder.clone(), write_template:config.write_template, write_models:config.write_models, @@ -851,6 +922,7 @@ struct DownloadAndDecompileHistoryConfig{ asset_id:AssetID, git_committer_name:String, git_committer_email:String, + style:DecompileStyle, output_folder:std::path::PathBuf, write_template:bool, write_models:bool, @@ -884,6 +956,7 @@ async fn download_and_decompile_history_into_git(config:DownloadAndDecompileHist .buffered(CONCURRENT_DECODE) .for_each(|join_handle_result|async{ match write_commit(WriteCommitConfig{ + style:config.style, git_committer_name:config.git_committer_name.clone(), git_committer_email:config.git_committer_email.clone(), output_folder:config.output_folder.clone(),