DecompileStyle

This commit is contained in:
Quaternions 2024-01-12 11:24:03 -08:00
parent 2260d9a565
commit 10f1ac313c

View File

@ -33,6 +33,8 @@ struct Cli{
no_scripts:Option<bool>, no_scripts:Option<bool>,
#[arg(long)] #[arg(long)]
no_template:Option<bool>, no_template:Option<bool>,
#[arg(long)]
style:Option<String>,
//git options //git options
#[arg(long)] #[arg(long)]
@ -61,6 +63,13 @@ enum Commands{
DownloadAndDecompileHistoryIntoGit, DownloadAndDecompileHistoryIntoGit,
} }
#[derive(Clone,Copy)]
enum DecompileStyle{
Rox,
Rojo,
RoxRojo,
}
#[derive(Args)] #[derive(Args)]
struct PathBufList{ struct PathBufList{
paths:Vec<std::path::PathBuf> paths:Vec<std::path::PathBuf>
@ -108,6 +117,21 @@ async fn main()->AResult<()>{
None=>None, 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{ match cli.command{
Commands::DownloadHistory=>download_history(DownloadHistoryConfig{ Commands::DownloadHistory=>download_history(DownloadHistoryConfig{
output_folder:cli.output.unwrap(), 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::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::Compile=>compile(cli.input.unwrap(),cli.output.unwrap()),
Commands::Decompile=>decompile(DecompileConfig{ Commands::Decompile=>decompile(DecompileConfig{
style:decompile_style.unwrap(),
input_file:cli.input.unwrap(), input_file:cli.input.unwrap(),
output_folder:cli.output.unwrap(), output_folder:cli.output.unwrap(),
write_template:!cli.no_template.unwrap_or(false), write_template:!cli.no_template.unwrap_or(false),
@ -129,6 +154,7 @@ async fn main()->AResult<()>{
git_committer_email:cli.git_committer_email.unwrap(), git_committer_email:cli.git_committer_email.unwrap(),
input_folder:cli.input.unwrap(), input_folder:cli.input.unwrap(),
output_folder:cli.output.unwrap(), output_folder:cli.output.unwrap(),
style:decompile_style.unwrap(),
write_template:!cli.no_template.unwrap_or(false), write_template:!cli.no_template.unwrap_or(false),
write_models:!cli.no_models.unwrap_or(false), write_models:!cli.no_models.unwrap_or(false),
write_scripts:!cli.no_scripts.unwrap_or(false), write_scripts:!cli.no_scripts.unwrap_or(false),
@ -139,6 +165,7 @@ async fn main()->AResult<()>{
cookie:cookie.unwrap(), cookie:cookie.unwrap(),
asset_id:cli.asset_id.unwrap(), asset_id:cli.asset_id.unwrap(),
output_folder:cli.output.unwrap(), output_folder:cli.output.unwrap(),
style:decompile_style.unwrap(),
write_template:!cli.no_template.unwrap_or(false), write_template:!cli.no_template.unwrap_or(false),
write_models:!cli.no_models.unwrap_or(false), write_models:!cli.no_models.unwrap_or(false),
write_scripts:!cli.no_scripts.unwrap_or(false), write_scripts:!cli.no_scripts.unwrap_or(false),
@ -427,23 +454,34 @@ enum WriteStackInstruction<'a>{
Destroy(Ref), Destroy(Ref),
} }
#[derive(Default,serde::Deserialize,serde::Serialize)] #[derive(Default)]
#[allow(nonstandard_style,dead_code)]
struct PropertiesOverride{ struct PropertiesOverride{
//Name:Option<String>, name:Option<String>,
ClassName:Option<String>, class_name:Option<String>,
} }
impl PropertiesOverride{ impl PropertiesOverride{
fn is_some(&self)->bool{ 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>{ fn sanitize<'a>(s:&'a str)->std::borrow::Cow<'a,str>{
lazy_regex::regex!(r"[^a-zA-Z0-9._-]").replace_all(s,"_") 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()); file.push(sanitize(node_name_override.as_str()).as_ref());
match node.class{ match node.class{
Class::Folder=>(), Class::Folder=>(),
@ -451,11 +489,40 @@ fn write_item(dom:&rbx_dom_weak::WeakDom,mut file:std::path::PathBuf,node:&TreeN
if !write_scripts{ if !write_scripts{
return Ok(()) 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){ if let Some(item)=dom.get_by_ref(node.referent){
//TODO: delete disabled scripts //TODO: delete disabled scripts
if let Some(rbx_dom_weak::types::Variant::String(source))=item.properties.get("Source"){ 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<R:Read>(input:R)->AResult<DecompiledContext>{
} }
struct WriteConfig{ struct WriteConfig{
style:DecompileStyle,
output_folder:std::path::PathBuf, output_folder:std::path::PathBuf,
write_template:bool, write_template:bool,
write_models: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::PopFolder=>assert!(folder.pop(),"weirdness"),
WriteStackInstruction::Destroy(referent)=>destroy_queue.push(referent), WriteStackInstruction::Destroy(referent)=>destroy_queue.push(referent),
WriteStackInstruction::Node(node,name_count)=>{ 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 mut properties=PropertiesOverride::default();
let has_children=node.children.len()!=0; let has_children=node.children.len()!=0;
match node.class{ match node.class{
Class::Folder=>(), Class::Folder=>(),
Class::ModuleScript=>{ Class::ModuleScript=>(),//.lua files are ModuleScript by default
//.lua files are ModuleScript by default Class::LocalScript=>properties.class_name=Some("LocalScript".to_string()),
if has_children{ Class::Script=>properties.class_name=Some("Script".to_string()),
properties.ClassName=Some("ModuleScript".to_string())
}
},
Class::LocalScript=>properties.ClassName=Some("LocalScript".to_string()),
Class::Script=>properties.ClassName=Some("Script".to_string()),
Class::Model=>(), Class::Model=>(),
} }
let name_override=if 0<name_count{ let name_override=if 0<name_count{
properties.name=Some(node.name.clone());
format!("{}_{}",node.name,name_count) format!("{}_{}",node.name,name_count)
}else{ }else{
node.name.clone() node.name.clone()
}; };
if has_children||properties.is_some(){
if has_children{
//push temp subfolder //push temp subfolder
let mut subfolder=folder.clone(); let mut subfolder=folder.clone();
subfolder.push(sanitize(name_override.as_str()).as_ref()); subfolder.push(sanitize(name_override.as_str()).as_ref());
//make folder //make folder
tokio::fs::create_dir(subfolder.clone()).await?; tokio::fs::create_dir(subfolder.clone()).await?;
//write properties
if properties.is_some(){ let name_final=match config.style{
let mut file=subfolder.clone(); DecompileStyle::Rox
file.push("properties"); |DecompileStyle::RoxRojo=>name_override.clone(),
assert!(file.set_extension("json"),"could not set extension"); DecompileStyle::Rojo=>"init".to_owned(),
tokio::fs::write(file,serde_json::to_string(&properties)?).await?; };
}
//write item in subfolder //write item in subfolder
write_queue.push((subfolder,node,name_override.clone())); write_queue.push((subfolder,node,name_final,properties,config.style));
}else{ }else{
//write item //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) //queue item to be deleted from dom after child objects are handled (stack is popped from the back)
match node.class{ match node.class{
@ -657,8 +722,8 @@ async fn write_files(config:WriteConfig,mut context:DecompiledContext)->AResult<
let dom=&context.dom; let dom=&context.dom;
let write_models=config.write_models; let write_models=config.write_models;
let write_scripts=config.write_scripts; let write_scripts=config.write_scripts;
let results:Vec<AResult<()>>=rayon::iter::ParallelIterator::collect(rayon::iter::ParallelIterator::map(rayon::iter::IntoParallelIterator::into_par_iter(write_queue),|(write_path,node,node_name_override)|{ let results:Vec<AResult<()>>=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,write_models,write_scripts) write_item(&dom,write_path,node,node_name_override,properties,style,write_models,write_scripts)
})); }));
for result in results{ for result in results{
result?; result?;
@ -683,6 +748,7 @@ async fn write_files(config:WriteConfig,mut context:DecompiledContext)->AResult<
} }
struct DecompileConfig{ struct DecompileConfig{
style:DecompileStyle,
input_file:std::path::PathBuf, input_file:std::path::PathBuf,
output_folder:std::path::PathBuf, output_folder:std::path::PathBuf,
write_template:bool, write_template:bool,
@ -703,6 +769,7 @@ async fn decompile(config:DecompileConfig)->AResult<()>{
//generate folders, models, and scripts //generate folders, models, and scripts
//delete models and scripts from dom //delete models and scripts from dom
write_files(WriteConfig{ write_files(WriteConfig{
style:config.style,
output_folder:config.output_folder, output_folder:config.output_folder,
write_template:config.write_template, write_template:config.write_template,
write_models:config.write_models, write_models:config.write_models,
@ -716,6 +783,7 @@ struct WriteCommitConfig{
git_committer_name:String, git_committer_name:String,
git_committer_email:String, git_committer_email:String,
output_folder:std::path::PathBuf, output_folder:std::path::PathBuf,
style:DecompileStyle,
write_template:bool, write_template:bool,
write_models:bool, write_models:bool,
write_scripts:bool, write_scripts:bool,
@ -745,6 +813,7 @@ async fn write_commit(config:WriteCommitConfig,b:Result<AResult<(AssetVersion,De
//write files //write files
write_files(WriteConfig{ write_files(WriteConfig{
style:config.style,
output_folder:config.output_folder.clone(), output_folder:config.output_folder.clone(),
write_template:config.write_template, write_template:config.write_template,
write_models:config.write_models, write_models:config.write_models,
@ -804,6 +873,7 @@ struct DecompileHistoryConfig{
git_committer_name:String, git_committer_name:String,
git_committer_email:String, git_committer_email:String,
input_folder:std::path::PathBuf, input_folder:std::path::PathBuf,
style:DecompileStyle,
output_folder:std::path::PathBuf, output_folder:std::path::PathBuf,
write_template:bool, write_template:bool,
write_models:bool, write_models:bool,
@ -834,6 +904,7 @@ async fn decompile_history_into_git(config:DecompileHistoryConfig)->AResult<()>{
match write_commit(WriteCommitConfig{ match write_commit(WriteCommitConfig{
git_committer_name:config.git_committer_name.clone(), git_committer_name:config.git_committer_name.clone(),
git_committer_email:config.git_committer_email.clone(), git_committer_email:config.git_committer_email.clone(),
style:config.style,
output_folder:config.output_folder.clone(), output_folder:config.output_folder.clone(),
write_template:config.write_template, write_template:config.write_template,
write_models:config.write_models, write_models:config.write_models,
@ -851,6 +922,7 @@ struct DownloadAndDecompileHistoryConfig{
asset_id:AssetID, asset_id:AssetID,
git_committer_name:String, git_committer_name:String,
git_committer_email:String, git_committer_email:String,
style:DecompileStyle,
output_folder:std::path::PathBuf, output_folder:std::path::PathBuf,
write_template:bool, write_template:bool,
write_models:bool, write_models:bool,
@ -884,6 +956,7 @@ async fn download_and_decompile_history_into_git(config:DownloadAndDecompileHist
.buffered(CONCURRENT_DECODE) .buffered(CONCURRENT_DECODE)
.for_each(|join_handle_result|async{ .for_each(|join_handle_result|async{
match write_commit(WriteCommitConfig{ match write_commit(WriteCommitConfig{
style:config.style,
git_committer_name:config.git_committer_name.clone(), git_committer_name:config.git_committer_name.clone(),
git_committer_email:config.git_committer_email.clone(), git_committer_email:config.git_committer_email.clone(),
output_folder:config.output_folder.clone(), output_folder:config.output_folder.clone(),