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>,
#[arg(long)]
no_template:Option<bool>,
#[arg(long)]
style:Option<String>,
//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<std::path::PathBuf>
@ -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,15 +454,26 @@ enum WriteStackInstruction<'a>{
Destroy(Ref),
}
#[derive(Default,serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
#[derive(Default)]
struct PropertiesOverride{
//Name:Option<String>,
ClassName:Option<String>,
name:Option<String>,
class_name:Option<String>,
}
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(())
}
}
@ -443,7 +481,7 @@ 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"){
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{
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 0<name_count{
properties.name=Some(node.name.clone());
format!("{}_{}",node.name,name_count)
}else{
node.name.clone()
};
if has_children||properties.is_some(){
if has_children{
//push temp subfolder
let mut subfolder=folder.clone();
subfolder.push(sanitize(name_override.as_str()).as_ref());
//make folder
tokio::fs::create_dir(subfolder.clone()).await?;
//write properties
if properties.is_some(){
let mut file=subfolder.clone();
file.push("properties");
assert!(file.set_extension("json"),"could not set extension");
tokio::fs::write(file,serde_json::to_string(&properties)?).await?;
}
let name_final=match config.style{
DecompileStyle::Rox
|DecompileStyle::RoxRojo=>name_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<AResult<()>>=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<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,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:Result<AResult<(AssetVersion,De
//write files
write_files(WriteConfig{
style:config.style,
output_folder:config.output_folder.clone(),
write_template:config.write_template,
write_models:config.write_models,
@ -804,6 +873,7 @@ struct DecompileHistoryConfig{
git_committer_name:String,
git_committer_email:String,
input_folder:std::path::PathBuf,
style:DecompileStyle,
output_folder:std::path::PathBuf,
write_template:bool,
write_models:bool,
@ -834,6 +904,7 @@ async fn decompile_history_into_git(config:DecompileHistoryConfig)->AResult<()>{
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(),