1#![deny(missing_docs)]
2
3use super::{
4 AstVisitor,
5 emit_context::{EmitContext, IndentStyle},
6};
7use crate::decompiler::ast::{AstKind, AstVisitable};
8use crate::decompiler::ast::{
9 array_access::ArrayAccessNode, array_kind::ArrayKind, array_node::ArrayNode,
10 control_flow::ControlFlowType, expr::ExprKind, phi::PhiNode, phi_array::PhiArrayNode,
11};
12use crate::decompiler::ast::{assignment::AssignmentNode, statement::StatementKind};
13use crate::decompiler::ast::{
14 bin_op::{BinOpType, BinaryOperationNode},
15 func_call::FunctionCallNode,
16};
17use crate::decompiler::ast::{block::BlockNode, ptr::P};
18use crate::decompiler::ast::{control_flow::ControlFlowNode, unary_op::UnaryOperationNode};
19use crate::decompiler::ast::{function::FunctionNode, literal::LiteralNode};
20use crate::decompiler::ast::{member_access::MemberAccessNode, ret::ReturnNode};
21use crate::{decompiler::ast::identifier::IdentifierNode, utils::escape_string};
22
23pub struct Gs2Emitter {
28 context: EmitContext,
30}
31
32impl Gs2Emitter {
33 pub fn new(context: EmitContext) -> Self {
35 Self { context }
36 }
37
38 fn merge_comments(&self, comments: Vec<Vec<String>>) -> Vec<String> {
43 comments.into_iter().flatten().collect()
44 }
45
46 fn emit_indent(&self) -> String {
48 " ".repeat(self.context.indent)
49 }
50}
51
52pub struct AstOutput {
54 pub node: String,
56 pub comments: Vec<String>,
58}
59
60impl AstVisitor for Gs2Emitter {
61 type Output = AstOutput;
62
63 fn visit_node(&mut self, node: &AstKind) -> AstOutput {
65 match node {
66 AstKind::Expression(expr) => expr.accept(self),
67 AstKind::Statement(stmt) => stmt.accept(self),
68 AstKind::Function(func) => func.accept(self),
69 AstKind::Block(block) => block.accept(self),
70 AstKind::ControlFlow(control_flow) => control_flow.accept(self),
71 }
72 }
73
74 fn visit_statement(&mut self, node: &StatementKind) -> AstOutput {
76 self.context = self.context.with_expr_root(true);
77 let stmt_str = match node {
78 StatementKind::Assignment(assignment) => assignment.accept(self),
79 StatementKind::Return(ret) => ret.accept(self),
80 StatementKind::VirtualBranch(vbranch) => vbranch.accept(self),
81 };
82 AstOutput {
83 node: format!("{};", stmt_str.node),
84 comments: stmt_str.comments,
85 }
86 }
87
88 fn visit_assignment(&mut self, stmt_node: &P<AssignmentNode>) -> AstOutput {
90 let base_comments = stmt_node.metadata().comments().clone();
91 let lhs_str = stmt_node.lhs.accept(self);
93
94 if let ExprKind::BinOp(bin_op_node) = stmt_node.rhs.clone() {
96 let lhs_in_rhs = bin_op_node.lhs == stmt_node.lhs.clone();
97 if lhs_in_rhs {
98 match bin_op_node.op_type {
99 BinOpType::Add => {
100 if let ExprKind::Literal(lit) = bin_op_node.rhs.clone() {
101 if let LiteralNode::Number(num) = lit.as_ref() {
102 if *num == 1 {
103 return AstOutput {
104 node: format!("{}++", lhs_str.node),
105 comments: self.merge_comments(vec![
106 base_comments.clone(),
107 lhs_str.comments.clone(),
108 ]),
109 };
110 } else {
111 let rhs_str = bin_op_node.rhs.accept(self);
112 return AstOutput {
113 node: format!("{} += {}", lhs_str.node, rhs_str.node),
114 comments: self.merge_comments(vec![
115 base_comments.clone(),
116 lhs_str.comments.clone(),
117 rhs_str.comments,
118 ]),
119 };
120 }
121 }
122 }
123 }
124 BinOpType::Sub => {
125 if let ExprKind::Literal(lit) = bin_op_node.rhs.clone() {
126 if let LiteralNode::Number(num) = lit.as_ref() {
127 if *num == 1 {
128 return AstOutput {
129 node: format!("{}--", lhs_str.node),
130 comments: self.merge_comments(vec![
131 base_comments.clone(),
132 lhs_str.comments.clone(),
133 ]),
134 };
135 } else {
136 let rhs_str = bin_op_node.rhs.accept(self);
137 return AstOutput {
138 node: format!("{} -= {}", lhs_str.node, rhs_str.node),
139 comments: self.merge_comments(vec![
140 base_comments.clone(),
141 lhs_str.comments.clone(),
142 rhs_str.comments,
143 ]),
144 };
145 }
146 }
147 }
148 }
149 _ => {}
150 }
151 }
152 }
153
154 let prev_context = self.context;
156 self.context = self.context.with_expr_root(true);
157 let rhs_str = stmt_node.rhs.accept(self);
158 self.context = prev_context;
159 AstOutput {
160 node: format!("{} = {}", lhs_str.node, rhs_str.node),
161 comments: self.merge_comments(vec![base_comments, lhs_str.comments, rhs_str.comments]),
162 }
163 }
164
165 fn visit_virtual_branch(
167 &mut self,
168 node: &P<crate::decompiler::ast::vbranch::VirtualBranchNode>,
169 ) -> Self::Output {
170 let mut s = String::new();
172 s.push_str("goto ");
173 s.push_str(&node.branch().to_string());
174
175 AstOutput {
176 node: s,
177 comments: node.metadata().comments().clone(),
178 }
179 }
180
181 fn visit_expr(&mut self, node: &ExprKind) -> AstOutput {
183 match node {
184 ExprKind::Literal(literal) => literal.accept(self),
185 ExprKind::BinOp(bin_op) => bin_op.accept(self),
186 ExprKind::UnaryOp(unary_op) => unary_op.accept(self),
187 ExprKind::FunctionCall(func_call) => func_call.accept(self),
188 ExprKind::Array(array) => array.accept(self),
189 ExprKind::New(new_node) => new_node.accept(self),
190 ExprKind::NewArray(new_array) => new_array.accept(self),
191 ExprKind::MemberAccess(member_access) => member_access.accept(self),
192 ExprKind::Identifier(identifier) => identifier.accept(self),
193 ExprKind::ArrayAccess(array_access) => array_access.accept(self),
194 ExprKind::Phi(phi) => phi.accept(self),
195 ExprKind::Range(range) => range.accept(self),
196 }
197 }
198
199 fn visit_array(&mut self, node: &ArrayKind) -> AstOutput {
201 match node {
202 ArrayKind::MergedArray(array) => self.visit_array_node(array),
203 ArrayKind::PhiArray(phi_array) => self.visit_phi_array(phi_array),
204 }
205 }
206
207 fn visit_array_node(&mut self, node: &P<ArrayNode>) -> AstOutput {
209 let mut s = String::new();
210 let mut comments = node.metadata().comments().clone();
211 s.push('{');
212 for (i, elem) in node.elements.iter().enumerate() {
213 let elem_out = elem.accept(self);
214 s.push_str(&elem_out.node);
215 comments.extend(elem_out.comments);
216 if i < node.elements.len() - 1 {
217 s.push_str(", ");
218 }
219 }
220 s.push('}');
221 AstOutput { node: s, comments }
222 }
223
224 fn visit_phi_array(&mut self, node: &P<PhiArrayNode>) -> AstOutput {
226 let mut s = String::new();
228 let mut comments = node.metadata().comments().clone();
229 s.push('{');
230 for (i, elem) in node.elements.iter().enumerate() {
231 let elem_out = elem.accept(self);
232 s.push_str(&elem_out.node);
233 comments.extend(elem_out.comments);
234 if i < node.elements.len() - 1 {
235 s.push_str(", ");
236 }
237 }
238 s.push_str(", ...");
239 let phi_out = node.phi.accept(self);
240 s.push_str(&phi_out.node);
241 comments.extend(phi_out.comments);
242 s.push('}');
243 AstOutput { node: s, comments }
244 }
245
246 fn visit_array_access(&mut self, node: &P<ArrayAccessNode>) -> AstOutput {
248 let array_str = node.arr.accept(self);
249 let index_str = node.index.accept(self);
250 AstOutput {
251 node: format!("{}[{}]", array_str.node, index_str.node),
252 comments: self.merge_comments(vec![
253 node.metadata().comments().clone(),
254 array_str.comments,
255 index_str.comments,
256 ]),
257 }
258 }
259
260 fn visit_bin_op(&mut self, node: &P<BinaryOperationNode>) -> AstOutput {
262 let base_comments = node.metadata().comments().clone();
263 let prev_context = self.context;
264 self.context = self.context.with_expr_root(false);
265 let lhs_str = node.lhs.accept(self);
266 let rhs_str = node.rhs.accept(self);
267 self.context = prev_context;
268 let op_str = node.op_type.to_string();
269 if self.context.expr_root {
270 AstOutput {
271 node: format!("{} {} {}", lhs_str.node, op_str, rhs_str.node),
272 comments: self.merge_comments(vec![
273 base_comments,
274 lhs_str.comments,
275 rhs_str.comments,
276 ]),
277 }
278 } else {
279 AstOutput {
280 node: format!("({} {} {})", lhs_str.node, op_str, rhs_str.node),
281 comments: self.merge_comments(vec![
282 base_comments,
283 lhs_str.comments,
284 rhs_str.comments,
285 ]),
286 }
287 }
288 }
289
290 fn visit_unary_op(&mut self, node: &P<UnaryOperationNode>) -> AstOutput {
292 let base_comments = node.metadata().comments().clone();
293 let prev_context = self.context;
294 self.context = self.context.with_expr_root(false);
295 let operand_str = node.operand.accept(self);
296 self.context = prev_context;
297 let op_str = node.op_type.to_string();
298 if self.context.expr_root {
299 AstOutput {
300 node: format!("{}{}", op_str, operand_str.node),
301 comments: self.merge_comments(vec![
302 base_comments,
303 node.metadata().comments().clone(),
304 operand_str.comments,
305 ]),
306 }
307 } else {
308 AstOutput {
309 node: format!("({}{})", op_str, operand_str.node),
310 comments: self.merge_comments(vec![
311 base_comments,
312 node.metadata().comments().clone(),
313 operand_str.comments,
314 ]),
315 }
316 }
317 }
318
319 fn visit_identifier(&mut self, node: &P<IdentifierNode>) -> AstOutput {
321 let mut s = node.id().clone();
322 if self.context.include_ssa_versions {
323 if let Some(ssa_version) = node.ssa_version {
324 s.push_str(&format!("#{}", ssa_version));
325 }
326 }
327 AstOutput {
328 node: s,
329 comments: node.metadata().comments().clone(),
330 }
331 }
332
333 fn visit_literal(&mut self, node: &P<LiteralNode>) -> AstOutput {
335 match node.as_ref() {
336 LiteralNode::String(s) => {
337 let escaped = escape_string(s);
338 AstOutput {
339 node: format!("\"{}\"", escaped),
340 comments: node.metadata().comments().clone(),
341 }
342 }
343 LiteralNode::Number(n) => {
344 if self.context.format_number_hex {
345 AstOutput {
346 node: format!("0x{:x}", n),
347 comments: node.metadata().comments().clone(),
348 }
349 } else {
350 AstOutput {
351 node: n.to_string(),
352 comments: node.metadata().comments().clone(),
353 }
354 }
355 }
356 LiteralNode::Float(f) => AstOutput {
357 node: f.to_string(),
358 comments: node.metadata().comments().clone(),
359 },
360 LiteralNode::Boolean(b) => AstOutput {
361 node: b.to_string(),
362 comments: node.metadata().comments().clone(),
363 },
364 LiteralNode::Null => AstOutput {
365 node: "null".to_string(),
366 comments: node.metadata().comments().clone(),
367 },
368 }
369 }
370
371 fn visit_member_access(&mut self, node: &P<MemberAccessNode>) -> AstOutput {
373 let lhs_str = node.lhs.accept(self);
374 let rhs_str = node.rhs.accept(self);
375 AstOutput {
376 node: format!("{}.{}", lhs_str.node, rhs_str.node),
377 comments: self.merge_comments(vec![
378 node.metadata().comments().clone(),
379 lhs_str.comments,
380 rhs_str.comments,
381 ]),
382 }
383 }
384
385 fn visit_function_call(&mut self, node: &P<FunctionCallNode>) -> AstOutput {
387 match &node.arguments {
389 ArrayKind::MergedArray(array_node) => {
390 let elements = &array_node.elements;
393 if elements.is_empty() {
394 return AstOutput {
396 node: "fn()".to_string(),
397 comments: Vec::new(),
398 };
399 }
400
401 let name_out = elements[0].accept(self);
403 let mut s = String::new();
404 s.push_str(name_out.node.as_str());
405 s.push('(');
406
407 let mut arg_comments = Vec::new();
409 let arg_outputs: Vec<AstOutput> = elements
411 .iter()
412 .skip(1)
413 .map(|arg| arg.accept(self))
414 .collect();
415 for (i, arg_out) in arg_outputs.iter().enumerate() {
416 s.push_str(&arg_out.node);
417 arg_comments.extend(arg_out.comments.clone());
418 if i < arg_outputs.len() - 1 {
419 s.push_str(", ");
420 }
421 }
422 s.push(')');
423
424 let comments = self.merge_comments(vec![
426 node.metadata().comments().clone(),
427 name_out.comments,
428 arg_comments,
429 ]);
430 AstOutput { node: s, comments }
431 }
432 ArrayKind::PhiArray(phi_array_node) => {
433 let call_out = phi_array_node.accept(self);
437 let s = format!("phi_fn_call({})", call_out.node);
438 AstOutput {
439 node: s,
440 comments: self.merge_comments(vec![
441 node.metadata().comments().clone(),
442 call_out.comments,
443 ]),
444 }
445 }
446 }
447 }
448
449 fn visit_function(&mut self, node: &P<FunctionNode>) -> AstOutput {
451 let mut comments = node.metadata().comments().clone();
452
453 if node.name().is_none() {
455 let mut s = String::new();
456 for stmt in node.body().instructions.iter() {
457 let stmt_out = stmt.accept(self);
458 for comment in stmt_out.comments.iter() {
460 s.push_str(&self.emit_indent());
461 s.push_str("// ");
462 s.push_str(comment);
463 s.push('\n');
464 }
465 s.push_str(&stmt_out.node);
466 s.push('\n');
467 }
468 return AstOutput { node: s, comments };
469 }
470
471 let name = node.name().as_ref().unwrap();
473 let mut s = String::new();
474 s.push_str(&format!("function {}(", name));
475
476 match &node.params() {
478 ArrayKind::MergedArray(array_node) => {
479 let params = &array_node.elements;
481 for (i, param) in params.iter().enumerate() {
482 let param_out = param.accept(self);
483 comments.extend(param_out.comments);
484 s.push_str(¶m_out.node);
485 if i < params.len() - 1 {
486 s.push_str(", ");
487 }
488 }
489 }
490 ArrayKind::PhiArray(phi_array_node) => {
491 let phi_out = phi_array_node.accept(self);
493 s.push_str(&format!("phi_params({})", phi_out.node));
494 comments.extend(phi_out.comments);
495 }
496 }
497 s.push(')');
498
499 let block_output = node.body().accept(self);
501 s.push_str(&block_output.node);
502
503 AstOutput {
504 node: s,
505 comments: self.merge_comments(vec![comments, block_output.comments]),
506 }
507 }
508
509 fn visit_return(&mut self, node: &P<ReturnNode>) -> AstOutput {
511 let child = node.ret.accept(self);
512 let mut s = String::new();
513 s.push_str("return ");
514 s.push_str(&child.node);
515 AstOutput {
516 node: s,
517 comments: self.merge_comments(vec![node.metadata().comments().clone(), child.comments]),
518 }
519 }
520
521 fn visit_block(&mut self, node: &P<BlockNode>) -> AstOutput {
523 let mut s = String::new();
524 if self.context.indent_style == IndentStyle::Allman {
525 s.push('\n');
526 s.push_str(&self.emit_indent());
527 s.push_str("{\n");
528 } else {
529 s.push_str(" {\n");
530 }
531 let old_context = self.context;
532 self.context = self.context.with_indent();
533 if !node.instructions.is_empty() {
534 for stmt in node.instructions.iter() {
535 let stmt_out = stmt.accept(self);
536 for comment in stmt_out.comments.iter() {
538 s.push_str(&self.emit_indent());
539 s.push_str("// ");
540 s.push_str(comment);
541 s.push('\n');
542 }
543 s.push_str(&self.emit_indent());
545 s.push_str(&stmt_out.node);
546 s.push('\n');
547 }
548 }
549 self.context = old_context;
550 s.push_str(&self.emit_indent());
551 s.push('}');
552 AstOutput {
553 node: s,
554 comments: node.metadata().comments().clone(),
555 }
556 }
557
558 fn visit_control_flow(&mut self, node: &P<ControlFlowNode>) -> AstOutput {
560 let mut s = String::new();
561 let mut base_comments = node.metadata().comments().clone();
562 let name = match node.ty() {
563 ControlFlowType::If => "if",
564 ControlFlowType::Else => "else",
565 ControlFlowType::ElseIf => "else if",
566 ControlFlowType::With => "with",
567 ControlFlowType::While => "while",
568 ControlFlowType::For => "for",
569 ControlFlowType::DoWhile => "do",
570 };
571 if *node.ty() == ControlFlowType::DoWhile {
572 s.push_str(name);
573 let body_out = node.body().accept(self);
574 s.push(' ');
575 s.push_str(&body_out.node);
576 s.push_str(" while (");
577 if let Some(condition) = node.condition() {
578 let condition_out = condition.accept(self);
579 s.push_str(&condition_out.node);
580 base_comments.extend(condition_out.comments.clone());
581 }
582 s.push_str(");");
583 AstOutput {
584 node: s,
585 comments: self.merge_comments(vec![base_comments, body_out.comments]),
586 }
587 } else {
588 s.push_str(name);
589 if let Some(condition) = node.condition() {
590 let condition_out = condition.accept(self);
591 s.push_str(" (");
592 s.push_str(&condition_out.node);
593 s.push_str(") ");
594 base_comments.extend(condition_out.comments.clone());
595 }
596 let body_out = node.body().accept(self);
597 s.push_str(&body_out.node);
598 AstOutput {
599 node: s,
600 comments: self.merge_comments(vec![base_comments, body_out.comments]),
601 }
602 }
603 }
604
605 fn visit_phi(&mut self, node: &P<PhiNode>) -> AstOutput {
607 let mut s = String::new();
608 s.push_str("phi<idx=");
609 s.push_str(&node.idx().to_string());
610 s.push_str(", regions=(");
611 for (i, region) in node.regions().iter().enumerate() {
612 s.push_str(®ion.0.to_string());
613 if i < node.regions().len() - 1 {
614 s.push_str(", ");
615 }
616 }
617 s.push_str(")>");
618 AstOutput {
619 node: s,
620 comments: node.metadata().comments().clone(),
621 }
622 }
623
624 fn visit_new(&mut self, node: &P<crate::decompiler::ast::new::NewNode>) -> AstOutput {
626 let type_out = node.new_type.accept(self);
627 let arg_out = node.arg.accept(self);
628 AstOutput {
630 node: format!("new {}({})", type_out.node, arg_out.node),
631 comments: self.merge_comments(vec![
632 node.metadata().comments().clone(),
633 type_out.comments,
634 arg_out.comments,
635 ]),
636 }
637 }
638
639 fn visit_new_array(
641 &mut self,
642 node: &P<crate::decompiler::ast::new_array::NewArrayNode>,
643 ) -> AstOutput {
644 let arg_out = node.arg.accept(self);
645 AstOutput {
646 node: format!("new [{}]", arg_out.node),
647 comments: self
648 .merge_comments(vec![node.metadata().comments().clone(), arg_out.comments]),
649 }
650 }
651
652 fn visit_range(&mut self, node: &P<crate::decompiler::ast::range::RangeNode>) -> AstOutput {
654 let start_out = node.start.accept(self);
655 let end_out = node.end.accept(self);
656 AstOutput {
657 node: format!("<{}, {}>", start_out.node, end_out.node),
658 comments: self.merge_comments(vec![
659 node.metadata().comments().clone(),
660 start_out.comments,
661 end_out.comments,
662 ]),
663 }
664 }
665}