use crate::recompiler::parser::DolFile;
use crate::recompiler::decoder::DecodedInstruction;
use crate::recompiler::ghidra::GhidraAnalysis;
use crate::recompiler::analysis::control_flow::ControlFlowAnalyzer;
use crate::recompiler::analysis::data_flow::DataFlowAnalyzer;
use crate::recompiler::analysis::type_inference::TypeInferenceEngine;
use crate::recompiler::codegen::CodeGenerator;
use crate::recompiler::validator::CodeValidator;
use anyhow::Result;
use smallvec::SmallVec;
pub struct RecompilationPipeline;
impl RecompilationPipeline {
#[inline(never)] pub fn recompile(dol_file: &DolFile, output_path: &str) -> Result<()> {
log::info!("Starting recompilation pipeline...");
log::info!("Step 1: Running Ghidra analysis (trying ReOxide first)...");
let ghidra_analysis: GhidraAnalysis = GhidraAnalysis::analyze(
&dol_file.path,
crate::recompiler::ghidra::GhidraBackend::ReOxide, )?;
log::info!("Step 2: Decoding instructions...");
let instructions: Vec<DecodedInstruction> = Self::decode_all_instructions(dol_file)?;
log::info!("Step 3: Building control flow graph...");
let cfg = ControlFlowAnalyzer::build_cfg(&instructions, 0u32)?;
log::info!("Step 4: Performing data flow analysis...");
let def_use_chains = DataFlowAnalyzer::build_def_use_chains(&instructions);
let live_analysis = DataFlowAnalyzer::live_variable_analysis(&cfg);
log::info!("Step 5: Inferring types...");
log::info!("Step 6: Generating Rust code...");
let mut codegen: CodeGenerator = CodeGenerator::new();
let estimated_capacity: usize = ghidra_analysis.functions.len() * 1000usize;
let mut rust_code: String = String::with_capacity(estimated_capacity);
rust_code.push_str("//! Recompiled GameCube game functions\n");
rust_code.push_str("//! Generated by GCRecomp\n\n");
rust_code.push_str("use crate::runtime::context::CpuContext;\n");
rust_code.push_str("use crate::runtime::memory::MemoryManager;\n");
rust_code.push_str("use anyhow::Result;\n\n");
let total_functions: usize = ghidra_analysis.functions.len();
let mut successful_functions: usize = 0usize;
let mut failed_functions: usize = 0usize;
for (idx, func) in ghidra_analysis.functions.iter().enumerate() {
if idx % 10 == 0 || idx == total_functions - 1 {
log::info!(
"Generating code for function {}/{} ({}%) - {}",
idx + 1,
total_functions,
((idx + 1) * 100) / total_functions.max(1),
func.name
);
}
let func_instructions: Vec<DecodedInstruction> = Self::map_instructions_to_function(func, &instructions);
if func_instructions.is_empty() {
log::warn!(
"Function {} at 0x{:08X} has no instructions, skipping",
func.name,
func.address
);
failed_functions += 1;
continue;
}
let func_metadata = crate::recompiler::analysis::FunctionMetadata {
address: func.address,
name: func.name.clone(),
size: func.size,
calling_convention: func.calling_convention.clone(),
parameters: func.parameters.iter().map(|p| {
crate::recompiler::analysis::ParameterInfo {
name: p.name.clone(),
type_info: crate::recompiler::analysis::TypeInfo::Unknown,
register: None,
stack_offset: p.offset.unwrap_or(0),
}
}).collect(),
return_type: None,
local_variables: func.local_variables.iter().map(|v| {
crate::recompiler::analysis::VariableInfo {
name: v.name.clone(),
type_info: crate::recompiler::analysis::TypeInfo::Unknown,
stack_offset: v.offset,
scope_start: 0u32,
scope_end: 0u32,
}
}).collect(),
basic_blocks: vec![],
};
match codegen.generate_function(&func_metadata, &func_instructions) {
Ok(func_code) => {
rust_code.push_str(&func_code);
rust_code.push('\n');
successful_functions += 1;
}
Err(e) => {
log::warn!(
"Failed to generate code for function {} at 0x{:08X}: {}",
func.name,
func.address,
e
);
failed_functions += 1;
rust_code.push_str(&format!(
"// Stub for function {} at 0x{:08X} (generation failed: {})\n",
func.name,
func.address,
e
));
rust_code.push_str(&format!(
"pub fn {}_0x{:08X}(_ctx: &mut CpuContext, _memory: &mut MemoryManager) -> Result<Option<u32>> {{\n",
codegen.sanitize_identifier(&func.name),
func.address
));
rust_code.push_str(" log::warn!(\"Function stub called - not implemented\");\n");
rust_code.push_str(" Ok(None)\n");
rust_code.push_str("}\n\n");
}
}
}
log::info!(
"Code generation complete: {} successful, {} failed out of {} total functions",
successful_functions,
failed_functions,
total_functions
);
rust_code.push_str("\n/// Function dispatcher - calls recompiled functions by address\n");
rust_code.push_str("/// This is generated automatically to handle indirect function calls\n");
rust_code.push_str("pub fn call_function_by_address(\n");
rust_code.push_str(" address: u32,\n");
rust_code.push_str(" ctx: &mut CpuContext,\n");
rust_code.push_str(" memory: &mut MemoryManager,\n");
rust_code.push_str(") -> Result<Option<u32>> {\n");
rust_code.push_str(" // Static function address mapping\n");
rust_code.push_str(" match address {\n");
for func in ghidra_analysis.functions.iter() {
let func_name = if func.name.is_empty() || func.name.starts_with("sub_") {
format!("func_0x{:08X}", func.address)
} else {
format!("{}_{:08X}", codegen.sanitize_identifier(&func.name), func.address)
};
rust_code.push_str(&format!(
" 0x{:08X}u32 => {}(ctx, memory),\n",
func.address,
func_name
));
}
rust_code.push_str(" _ => {\n");
rust_code.push_str(" log::warn!(\"Unknown function address: 0x{:08X}\", address);\n");
rust_code.push_str(" Ok(None)\n");
rust_code.push_str(" }\n");
rust_code.push_str(" }\n");
rust_code.push_str("}\n\n");
log::info!("Step 7: Validating generated code...");
CodeValidator::validate_rust_code(&rust_code)?;
log::info!("Step 8: Writing output to {}...", output_path);
std::fs::write(output_path, rust_code)?;
log::info!("Recompilation complete!");
Ok(())
}
#[inline] fn decode_all_instructions(dol_file: &DolFile) -> Result<Vec<DecodedInstruction>> {
let mut estimated_count: usize = 0usize;
for section in dol_file.text_sections.iter() {
estimated_count = estimated_count.wrapping_add(section.data.len() / 4usize);
}
let mut instructions: Vec<DecodedInstruction> = Vec::with_capacity(estimated_count);
for section in dol_file.text_sections.iter() {
let data: &[u8] = §ion.data;
let section_address: u32 = section.address;
for (chunk_index, chunk) in data.chunks_exact(4usize).enumerate() {
let word: u32 = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let instruction_address: u32 = section_address.wrapping_add((chunk_index * 4usize) as u32);
if let Ok(decoded) = crate::recompiler::decoder::Instruction::decode(word, instruction_address) {
instructions.push(decoded);
}
}
}
Ok(instructions)
}
#[inline] fn map_instructions_to_function(
func: &crate::recompiler::ghidra::FunctionInfo,
instructions: &[DecodedInstruction],
) -> Vec<DecodedInstruction> {
let func_size: u32 = if func.size == 0u32 { 4u32 } else { func.size };
let func_end: u32 = func.address.wrapping_add(func_size);
instructions
.iter()
.filter(|inst| {
func.address <= inst.address && inst.address < func_end
})
.cloned()
.collect()
}
}