1use anyhow::{bail, Result};
2use clap::Parser;
3use inst::Inst;
4use rfvp::script::{opcode::Opcode, parser::Nls};
5use serde::{de::value, Deserialize, Serialize};
6use std::{
7 cell::RefCell,
8 collections::{BTreeMap, BTreeSet},
9 path::{Path, PathBuf},
10 rc::Rc,
11};
12
13use inst::*;
14use utils::*;
15
16mod inst;
17mod utils;
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct FVPProject {
21 config_file: PathBuf,
22 disassembly_file: PathBuf,
23}
24
25impl FVPProject {
26 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
27 let config_file = PathBuf::from(path.as_ref());
28 let config_str = std::fs::read_to_string(config_file)?;
29 let config: FVPProject = toml::from_str(&config_str)?;
30 Ok(config)
31 }
32}
33
34#[derive(Debug, Serialize, Deserialize)]
35pub struct SyscallEntry {
36 id: u32,
37 name: String,
38 args_count: u8,
39}
40
41#[derive(Debug, Serialize, Deserialize)]
42pub struct ProjectConfig {
43 entry_point: u32,
44 non_volatile_global_count: u16,
45 volatile_global_count: u16,
46 game_mode: u8,
47 game_mode_reserved: u8,
49 game_title: String,
50 syscalls: Vec<SyscallEntry>,
51 custom_syscall_count: u16,
52}
53
54impl ProjectConfig {
55 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
56 let config_file = PathBuf::from(path.as_ref());
57 let config_str = std::fs::read_to_string(config_file)?;
58 let config: ProjectConfig = serde_yaml::from_str(&config_str)?;
59 Ok(config)
60 }
61
62 pub fn put_u8(value: u8, buffer: &mut Vec<u8>) {
63 buffer.push(value);
64 }
65
66 pub fn put_u16_le(value: u16, buffer: &mut Vec<u8>) {
67 buffer.push((value & 0xff) as u8);
68 buffer.push(((value >> 8) & 0xff) as u8);
69 }
70
71 pub fn put_u32_le(value: u32, buffer: &mut Vec<u8>) {
72 buffer.push((value & 0xff) as u8);
73 buffer.push(((value >> 8) & 0xff) as u8);
74 buffer.push(((value >> 16) & 0xff) as u8);
75 buffer.push(((value >> 24) & 0xff) as u8);
76 }
77
78 fn string_to_blob(content: &str, nls: Nls) -> Vec<u8> {
79 let mut content_bytes = match nls {
81 Nls::GBK => encoding_rs::GBK.encode(content).0.to_vec(),
82 Nls::ShiftJIS => encoding_rs::SHIFT_JIS.encode(content).0.to_vec(),
83 Nls::UTF8 => content.as_bytes().to_vec(),
84 };
85
86 if !content_bytes.ends_with(&[0]) {
87 content_bytes.push(0);
88 }
89
90 content_bytes
91 }
92
93 fn serialize_to_binary(&mut self, nls: Nls) -> Result<Vec<u8>> {
94 let mut data = Vec::new();
95 Self::put_u32_le(self.entry_point, &mut data);
96 Self::put_u16_le(self.non_volatile_global_count, &mut data);
97 Self::put_u16_le(self.volatile_global_count, &mut data);
98 Self::put_u8(self.game_mode, &mut data);
99 Self::put_u8(self.game_mode_reserved, &mut data);
100
101 let game_title = Self::string_to_blob(&self.game_title, nls.clone());
102 let game_title_len = game_title.len() as u8;
103 Self::put_u8(game_title_len, &mut data);
104 data.extend_from_slice(&game_title);
105
106 Self::put_u16_le(self.syscalls.len() as u16, &mut data);
107 self.syscalls.sort_by_key(|x| x.id);
108 for syscall in &self.syscalls {
109 Self::put_u8(syscall.args_count, &mut data);
110 let syscall_name = Self::string_to_blob(&syscall.name, nls.clone());
111 let syscall_name_len = syscall_name.len() as u8;
112 Self::put_u8(syscall_name_len, &mut data);
113 data.extend_from_slice(&syscall_name);
114 }
115
116 if self.custom_syscall_count > 0 {
117 bail!("custom syscall not supported");
118 }
119
120 Self::put_u16_le(self.custom_syscall_count, &mut data);
121
122 Ok(data)
123 }
124
125 pub fn link(&mut self, entry_point: u32, nls: Nls) -> Result<Vec<u8>> {
126 self.entry_point = entry_point;
127 self.serialize_to_binary(nls)
128 }
129}
130
131pub struct Assembler {
132 project: FVPProject,
133 config: ProjectConfig,
134 functions: Vec<Function>,
135 nls: Nls,
136
137 code_section: Vec<u8>,
138}
139
140pub enum InstSet {
141 Nop(NopInst),
142 InitStack(InitStackInst),
143 Call(CallInst),
144 Syscall(SyscallInst),
145 Ret(RetInst),
146 RetV(RetVInst),
147 Jmp(JmpInst),
148 Jz(JzInst),
149 PushNil(PushNilInst),
150 PushTrue(PushTrueInst),
151 PushI32(PushI32Inst),
152 PushI16(PushI16Inst),
153 PushI8(PushI8Inst),
154 PushF32(PushF32Inst),
155 PushString(PushStringInst),
156 PushGlobal(PushGlobalInst),
157 PushStack(PushStackInst),
158 PushGlobalTable(PushGlobalTableInst),
159 PushLocalTable(PushLocalTableInst),
160 PushTop(PushTopInst),
161 PushReturn(PushReturnInst),
162 PopGlobal(PopGlobalInst),
163 PopStack(PopStackInst),
164 PopGlobalTable(PopGlobalTableInst),
165 PopLocalTable(PopLocalTableInst),
166 Neg(NegInst),
167 Add(AddInst),
168 Sub(SubInst),
169 Mul(MulInst),
170 Div(DivInst),
171 Mod(ModInst),
172 BitTest(BitTestInst),
173 And(AndInst),
174 Or(OrInst),
175 SetE(SetEInst),
176 SetNE(SetNEInst),
177 SetG(SetGInst),
178 SetLE(SetLEInst),
179 SetL(SetLInst),
180 SetGE(SetGEInst),
181}
182
183impl InstSet {
184 pub fn set_address(&mut self, address: u32) {
185 match self {
186 InstSet::Nop(inst) => inst.set_address(address),
187 InstSet::InitStack(inst) => inst.set_address(address),
188 InstSet::Call(inst) => inst.set_address(address),
189 InstSet::Syscall(inst) => inst.set_address(address),
190 InstSet::Ret(inst) => inst.set_address(address),
191 InstSet::RetV(inst) => inst.set_address(address),
192 InstSet::Jmp(inst) => inst.set_address(address),
193 InstSet::Jz(inst) => inst.set_address(address),
194 InstSet::PushNil(inst) => inst.set_address(address),
195 InstSet::PushTrue(inst) => inst.set_address(address),
196 InstSet::PushI32(inst) => inst.set_address(address),
197 InstSet::PushI16(inst) => inst.set_address(address),
198 InstSet::PushI8(inst) => inst.set_address(address),
199 InstSet::PushF32(inst) => inst.set_address(address),
200 InstSet::PushString(inst) => inst.set_address(address),
201 InstSet::PushGlobal(inst) => inst.set_address(address),
202 InstSet::PushStack(inst) => inst.set_address(address),
203 InstSet::PushGlobalTable(inst) => inst.set_address(address),
204 InstSet::PushLocalTable(inst) => inst.set_address(address),
205 InstSet::PushTop(inst) => inst.set_address(address),
206 InstSet::PushReturn(inst) => inst.set_address(address),
207 InstSet::PopGlobal(inst) => inst.set_address(address),
208 InstSet::PopStack(inst) => inst.set_address(address),
209 InstSet::PopGlobalTable(inst) => inst.set_address(address),
210 InstSet::PopLocalTable(inst) => inst.set_address(address),
211 InstSet::Neg(inst) => inst.set_address(address),
212 InstSet::Add(inst) => inst.set_address(address),
213 InstSet::Sub(inst) => inst.set_address(address),
214 InstSet::Mul(inst) => inst.set_address(address),
215 InstSet::Div(inst) => inst.set_address(address),
216 InstSet::Mod(inst) => inst.set_address(address),
217 InstSet::BitTest(inst) => inst.set_address(address),
218 InstSet::And(inst) => inst.set_address(address),
219 InstSet::Or(inst) => inst.set_address(address),
220 InstSet::SetE(inst) => inst.set_address(address),
221 InstSet::SetNE(inst) => inst.set_address(address),
222 InstSet::SetG(inst) => inst.set_address(address),
223 InstSet::SetLE(inst) => inst.set_address(address),
224 InstSet::SetL(inst) => inst.set_address(address),
225 InstSet::SetGE(inst) => inst.set_address(address),
226 }
227 }
228
229 pub fn get_address(&self) -> u32 {
230 match self {
231 InstSet::Nop(inst) => inst.address(),
232 InstSet::InitStack(inst) => inst.address(),
233 InstSet::Call(inst) => inst.address(),
234 InstSet::Syscall(inst) => inst.address(),
235 InstSet::Ret(inst) => inst.address(),
236 InstSet::RetV(inst) => inst.address(),
237 InstSet::Jmp(inst) => inst.address(),
238 InstSet::Jz(inst) => inst.address(),
239 InstSet::PushNil(inst) => inst.address(),
240 InstSet::PushTrue(inst) => inst.address(),
241 InstSet::PushI32(inst) => inst.address(),
242 InstSet::PushI16(inst) => inst.address(),
243 InstSet::PushI8(inst) => inst.address(),
244 InstSet::PushF32(inst) => inst.address(),
245 InstSet::PushString(inst) => inst.address(),
246 InstSet::PushGlobal(inst) => inst.address(),
247 InstSet::PushStack(inst) => inst.address(),
248 InstSet::PushGlobalTable(inst) => inst.address(),
249 InstSet::PushLocalTable(inst) => inst.address(),
250 InstSet::PushTop(inst) => inst.address(),
251 InstSet::PushReturn(inst) => inst.address(),
252 InstSet::PopGlobal(inst) => inst.address(),
253 InstSet::PopStack(inst) => inst.address(),
254 InstSet::PopGlobalTable(inst) => inst.address(),
255 InstSet::PopLocalTable(inst) => inst.address(),
256 InstSet::Neg(inst) => inst.address(),
257 InstSet::Add(inst) => inst.address(),
258 InstSet::Sub(inst) => inst.address(),
259 InstSet::Mul(inst) => inst.address(),
260 InstSet::Div(inst) => inst.address(),
261 InstSet::Mod(inst) => inst.address(),
262 InstSet::BitTest(inst) => inst.address(),
263 InstSet::And(inst) => inst.address(),
264 InstSet::Or(inst) => inst.address(),
265 InstSet::SetE(inst) => inst.address(),
266 InstSet::SetNE(inst) => inst.address(),
267 InstSet::SetG(inst) => inst.address(),
268 InstSet::SetLE(inst) => inst.address(),
269 InstSet::SetL(inst) => inst.address(),
270 InstSet::SetGE(inst) => inst.address(),
271 }
272 }
273
274 pub fn size(&self) -> u32 {
275 match self {
276 InstSet::Nop(inst) => inst.size(),
277 InstSet::InitStack(inst) => inst.size(),
278 InstSet::Call(inst) => inst.size(),
279 InstSet::Syscall(inst) => inst.size(),
280 InstSet::Ret(inst) => inst.size(),
281 InstSet::RetV(inst) => inst.size(),
282 InstSet::Jmp(inst) => inst.size(),
283 InstSet::Jz(inst) => inst.size(),
284 InstSet::PushNil(inst) => inst.size(),
285 InstSet::PushTrue(inst) => inst.size(),
286 InstSet::PushI32(inst) => inst.size(),
287 InstSet::PushI16(inst) => inst.size(),
288 InstSet::PushI8(inst) => inst.size(),
289 InstSet::PushF32(inst) => inst.size(),
290 InstSet::PushString(inst) => inst.size(),
291 InstSet::PushGlobal(inst) => inst.size(),
292 InstSet::PushStack(inst) => inst.size(),
293 InstSet::PushGlobalTable(inst) => inst.size(),
294 InstSet::PushLocalTable(inst) => inst.size(),
295 InstSet::PushTop(inst) => inst.size(),
296 InstSet::PushReturn(inst) => inst.size(),
297 InstSet::PopGlobal(inst) => inst.size(),
298 InstSet::PopStack(inst) => inst.size(),
299 InstSet::PopGlobalTable(inst) => inst.size(),
300 InstSet::PopLocalTable(inst) => inst.size(),
301 InstSet::Neg(inst) => inst.size(),
302 InstSet::Add(inst) => inst.size(),
303 InstSet::Sub(inst) => inst.size(),
304 InstSet::Mul(inst) => inst.size(),
305 InstSet::Div(inst) => inst.size(),
306 InstSet::Mod(inst) => inst.size(),
307 InstSet::BitTest(inst) => inst.size(),
308 InstSet::And(inst) => inst.size(),
309 InstSet::Or(inst) => inst.size(),
310 InstSet::SetE(inst) => inst.size(),
311 InstSet::SetNE(inst) => inst.size(),
312 InstSet::SetG(inst) => inst.size(),
313 InstSet::SetLE(inst) => inst.size(),
314 InstSet::SetL(inst) => inst.size(),
315 InstSet::SetGE(inst) => inst.size(),
316 }
317 }
318
319 pub fn serialize_to_binary(&self) -> Vec<u8> {
320 match self {
321 InstSet::Nop(inst) => inst.serialize_to_binary(),
322 InstSet::InitStack(inst) => inst.serialize_to_binary(),
323 InstSet::Call(inst) => inst.serialize_to_binary(),
324 InstSet::Syscall(inst) => inst.serialize_to_binary(),
325 InstSet::Ret(inst) => inst.serialize_to_binary(),
326 InstSet::RetV(inst) => inst.serialize_to_binary(),
327 InstSet::Jmp(inst) => inst.serialize_to_binary(),
328 InstSet::Jz(inst) => inst.serialize_to_binary(),
329 InstSet::PushNil(inst) => inst.serialize_to_binary(),
330 InstSet::PushTrue(inst) => inst.serialize_to_binary(),
331 InstSet::PushI32(inst) => inst.serialize_to_binary(),
332 InstSet::PushI16(inst) => inst.serialize_to_binary(),
333 InstSet::PushI8(inst) => inst.serialize_to_binary(),
334 InstSet::PushF32(inst) => inst.serialize_to_binary(),
335 InstSet::PushString(inst) => inst.serialize_to_binary(),
336 InstSet::PushGlobal(inst) => inst.serialize_to_binary(),
337 InstSet::PushStack(inst) => inst.serialize_to_binary(),
338 InstSet::PushGlobalTable(inst) => inst.serialize_to_binary(),
339 InstSet::PushLocalTable(inst) => inst.serialize_to_binary(),
340 InstSet::PushTop(inst) => inst.serialize_to_binary(),
341 InstSet::PushReturn(inst) => inst.serialize_to_binary(),
342 InstSet::PopGlobal(inst) => inst.serialize_to_binary(),
343 InstSet::PopStack(inst) => inst.serialize_to_binary(),
344 InstSet::PopGlobalTable(inst) => inst.serialize_to_binary(),
345 InstSet::PopLocalTable(inst) => inst.serialize_to_binary(),
346 InstSet::Neg(inst) => inst.serialize_to_binary(),
347 InstSet::Add(inst) => inst.serialize_to_binary(),
348 InstSet::Sub(inst) => inst.serialize_to_binary(),
349 InstSet::Mul(inst) => inst.serialize_to_binary(),
350 InstSet::Div(inst) => inst.serialize_to_binary(),
351 InstSet::Mod(inst) => inst.serialize_to_binary(),
352 InstSet::BitTest(inst) => inst.serialize_to_binary(),
353 InstSet::And(inst) => inst.serialize_to_binary(),
354 InstSet::Or(inst) => inst.serialize_to_binary(),
355 InstSet::SetE(inst) => inst.serialize_to_binary(),
356 InstSet::SetNE(inst) => inst.serialize_to_binary(),
357 InstSet::SetG(inst) => inst.serialize_to_binary(),
358 InstSet::SetLE(inst) => inst.serialize_to_binary(),
359 InstSet::SetL(inst) => inst.serialize_to_binary(),
360 InstSet::SetGE(inst) => inst.serialize_to_binary(),
361 }
362 }
363}
364
365impl Assembler {
366 pub fn new(project_dir: impl AsRef<Path>, nls: Nls) -> Result<Self> {
367 let proj_path = project_dir.as_ref().join("project.toml");
368
369 let project = FVPProject::new(proj_path)?;
370 let disassembly_path = project_dir.as_ref().join(&project.disassembly_file);
371 let config_path = project_dir.as_ref().join(&project.config_file);
372 let config = ProjectConfig::new(config_path)?;
373 let functions = std::fs::read_to_string(disassembly_path)?;
374 let functions: Vec<Function> = serde_yaml::from_str(&functions)?;
375
376 Ok(Self {
377 project,
378 config,
379 functions,
380 nls,
381
382 code_section: Vec::new(),
383 })
384 }
385
386 fn inst2_to_inst(
387 inst: &Inst2,
388 nls: &Nls,
389 syscall_table: &BTreeMap<String, u32>,
390 ) -> Result<InstSet> {
391 let opcode = inst.get_opcode()?;
392 let wrapped_inst = match opcode {
393 Opcode::Nop => InstSet::Nop(to_nop(inst)?),
394 Opcode::InitStack => InstSet::InitStack(to_init_stack(inst)?),
395 Opcode::Call => InstSet::Call(to_call(inst)?),
396 Opcode::Syscall => InstSet::Syscall(to_syscall(inst, syscall_table)?),
397 Opcode::Ret => InstSet::Ret(to_ret(inst)?),
398 Opcode::RetV => InstSet::RetV(to_ret_v(inst)?),
399 Opcode::Jmp => InstSet::Jmp(to_jmp(inst)?),
400 Opcode::Jz => InstSet::Jz(to_jz(inst)?),
401 Opcode::PushNil => InstSet::PushNil(to_push_nil(inst)?),
402 Opcode::PushTrue => InstSet::PushTrue(to_push_true(inst)?),
403 Opcode::PushI32 => InstSet::PushI32(to_push_i32(inst)?),
404 Opcode::PushI16 => InstSet::PushI16(to_push_i16(inst)?),
405 Opcode::PushI8 => InstSet::PushI8(to_push_i8(inst)?),
406 Opcode::PushF32 => InstSet::PushF32(to_push_f32(inst)?),
407 Opcode::PushString => InstSet::PushString(to_push_string(inst, nls.clone())?),
408 Opcode::PushGlobal => InstSet::PushGlobal(to_push_global(inst)?),
409 Opcode::PushStack => InstSet::PushStack(to_push_stack(inst)?),
410 Opcode::PushGlobalTable => InstSet::PushGlobalTable(to_push_global_table(inst)?),
411 Opcode::PushLocalTable => InstSet::PushLocalTable(to_push_local_table(inst)?),
412 Opcode::PushTop => InstSet::PushTop(to_push_top(inst)?),
413 Opcode::PushReturn => InstSet::PushReturn(to_push_return(inst)?),
414 Opcode::PopGlobal => InstSet::PopGlobal(to_pop_global(inst)?),
415 Opcode::PopStack => InstSet::PopStack(to_pop_stack(inst)?),
416 Opcode::PopGlobalTable => InstSet::PopGlobalTable(to_pop_global_table(inst)?),
417 Opcode::PopLocalTable => InstSet::PopLocalTable(to_pop_local_table(inst)?),
418 Opcode::Neg => InstSet::Neg(to_neg(inst)?),
419 Opcode::Add => InstSet::Add(to_add(inst)?),
420 Opcode::Sub => InstSet::Sub(to_sub(inst)?),
421 Opcode::Mul => InstSet::Mul(to_mul(inst)?),
422 Opcode::Div => InstSet::Div(to_div(inst)?),
423 Opcode::Mod => InstSet::Mod(to_mod(inst)?),
424 Opcode::BitTest => InstSet::BitTest(to_bit_test(inst)?),
425 Opcode::And => InstSet::And(to_and(inst)?),
426 Opcode::Or => InstSet::Or(to_or(inst)?),
427 Opcode::SetE => InstSet::SetE(to_set_e(inst)?),
428 Opcode::SetNE => InstSet::SetNE(to_set_ne(inst)?),
429 Opcode::SetG => InstSet::SetG(to_set_g(inst)?),
430 Opcode::SetLE => InstSet::SetLE(to_set_le(inst)?),
431 Opcode::SetL => InstSet::SetL(to_set_l(inst)?),
432 Opcode::SetGE => InstSet::SetGE(to_set_ge(inst)?),
433 };
434
435 Ok(wrapped_inst)
436 }
437
438 fn compile(&mut self, old_entry_point: u32) -> Result<u32> {
439 let mut func_entry_addrs = BTreeSet::new();
440 let mut threadstart_sites = Vec::new();
441
442 let mut map = BTreeMap::new();
443 for func in &self.functions {
444 func_entry_addrs.insert(func.address());
445 for inst in func.get_insts() {
446 let addr = inst.get_address();
447
448 if inst.get_opcode()? == Opcode::Syscall {
451 if let Some(name) = inst.operands().first() {
452 if name.eq_ignore_ascii_case("ThreadStart") {
453 threadstart_sites.push(addr);
454 }
455 }
456 }
457 map.insert(addr, inst);
458 }
459 }
460
461 let mut syscall_table = BTreeMap::new();
463 for entry in self.config.syscalls.iter() {
464 syscall_table.insert(entry.name.clone(), entry.id);
465 }
466 let mut insts = BTreeMap::new();
467 let mut old_order = Vec::new();
468 let mut cursor = 4u32;
469 for (addr, inst) in map {
470 let mut wrapped_inst = Self::inst2_to_inst(inst, &self.nls, &syscall_table)?;
471 wrapped_inst.set_address(cursor);
472 let size = wrapped_inst.size();
473 let wrapped_inst = Rc::new(RefCell::new(wrapped_inst));
474 insts.insert(addr, wrapped_inst);
475 old_order.push(addr);
476 cursor += size;
477 }
478 let entry_point = insts
479 .get(&old_entry_point)
480 .ok_or_else(|| anyhow::anyhow!("entry point not found"))?
481 .borrow()
482 .get_address();
483
484 for (_, inst) in &insts {
486 let inst = &mut *inst.borrow_mut();
487 match inst {
488 InstSet::Jmp(inst) => {
489 let old_target = inst.get_old_target();
490 let target_inst = insts
491 .get(&old_target)
492 .ok_or_else(|| anyhow::anyhow!(format!("target not found: {}", old_target)))?;
493 inst.set_target(target_inst.borrow().get_address());
494 }
495 InstSet::Jz(inst) => {
496 let old_target = inst.get_old_target();
497 let target_inst = insts
498 .get(&old_target)
499 .ok_or_else(|| anyhow::anyhow!(format!("target not found: {}", old_target)))?;
500 inst.set_target(target_inst.borrow().get_address());
501 }
502 InstSet::Call(inst) => {
503 let old_target = inst.get_old_func_target();
504 let target_inst = insts
505 .get(&old_target)
506 .ok_or_else(|| anyhow::anyhow!(format!("target not found: {}", old_target)))?;
507 inst.set_func_target(target_inst.borrow().get_address());
508 }
509 _ => {}
510 }
511 }
512
513 if !threadstart_sites.is_empty() {
515 let mut index_by_old = BTreeMap::new();
516 for (i, a) in old_order.iter().enumerate() {
517 index_by_old.insert(*a, i);
518 }
519
520 for ts_old_addr in threadstart_sites {
521 let idx = *index_by_old
522 .get(&ts_old_addr)
523 .ok_or_else(|| anyhow::anyhow!(format!("ThreadStart site not found: {ts_old_addr}")))?;
524
525 let mut candidates: Vec<(u32, u32)> = Vec::new();
527 for back in 1..=8usize {
528 if idx < back {
529 break;
530 }
531 let prev_old = old_order[idx - back];
532 let inst_ref = insts
533 .get(&prev_old)
534 .ok_or_else(|| anyhow::anyhow!(format!("inst missing at {prev_old}")))?;
535 match &*inst_ref.borrow() {
536 InstSet::PushI32(p) => {
537 let v = p.get_value();
538 if v >= 0 {
539 let u = v as u32;
540 if func_entry_addrs.contains(&u) {
541 candidates.push((prev_old, u));
542 }
543 }
544 }
545 InstSet::PushI16(p) => {
546 let v = p.get_value() as i32;
547 if v >= 0 {
548 let u = v as u32;
549 if func_entry_addrs.contains(&u) {
550 candidates.push((prev_old, u));
551 }
552 }
553 }
554 _ => {}
555 }
556 }
557
558 if candidates.is_empty() {
559 bail!(
560 "ThreadStart at old PC {}: could not find a preceding push of a function entry address",
561 ts_old_addr
562 );
563 }
564 if candidates.len() != 1 {
565 bail!(
566 "ThreadStart at old PC {}: ambiguous code-pointer candidates: {:?}",
567 ts_old_addr,
568 candidates
569 );
570 }
571
572 let (push_old_addr, target_old_func_addr) = candidates[0];
573 let target_new = insts
574 .get(&target_old_func_addr)
575 .ok_or_else(|| {
576 anyhow::anyhow!(format!(
577 "ThreadStart target not found in inst map: {}",
578 target_old_func_addr
579 ))
580 })?
581 .borrow()
582 .get_address();
583
584 let push_inst = insts
585 .get(&push_old_addr)
586 .ok_or_else(|| anyhow::anyhow!(format!("push inst missing at {push_old_addr}")))?;
587 match &mut *push_inst.borrow_mut() {
588 InstSet::PushI32(p) => p.set_value(target_new as i32),
589 InstSet::PushI16(p) => {
590 if target_new > i16::MAX as u32 {
591 bail!(
592 "ThreadStart relocated target too large for PushI16: {}",
593 target_new
594 );
595 }
596 p.set_value(target_new as i16);
597 }
598 _ => bail!("Internal error: selected candidate is not a push"),
599 }
600 }
601 }
602
603 self.code_section.clear();
605 for (_, inst) in insts {
606 let blob = inst.borrow().serialize_to_binary();
607 self.code_section.extend_from_slice(&blob);
608 }
609
610 Ok(entry_point)
611 }
612
613 fn size(&self) -> u32 {
614 self.code_section.len() as u32
615 }
616
617 fn link(&mut self, new_entry_point: u32) -> Result<Vec<u8>> {
618 let mut data = Vec::new();
619 let header_offset = 4 + self.size();
620
621 ProjectConfig::put_u32_le(header_offset, &mut data);
622 data.extend_from_slice(&self.code_section);
623
624 let header = self.config.link(new_entry_point, self.nls.clone())?;
625 data.extend_from_slice(&header);
626
627 Ok(data)
628 }
629}
630
631fn compile(project_dir: impl AsRef<Path>, output: impl AsRef<Path>, nls: Nls) -> Result<()> {
632 let mut assembler = Assembler::new(project_dir, nls)?;
633 let entry_point = assembler.compile(assembler.config.entry_point)?;
634 let data = assembler.link(entry_point)?;
635 let output_path = output.as_ref();
636 std::fs::write(output_path, data)?;
637
638 Ok(())
639}
640
641#[derive(Parser, Debug)]
642#[command(version, about, long_about = None)]
643struct Args {
644 #[clap(short, long)]
645 project_dir: String,
646 #[clap(short, long)]
647 output: String,
648 #[clap(short, long)]
649 nls: Nls,
650}
651
652fn main() {
653 env_logger::init();
654 let args = Args::parse();
655 if let Err(e) = compile(args.project_dir, args.output, args.nls) {
656 log::error!("Error: {}", e);
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663
664 #[test]
665 fn test_compile() {
666 let input = Path::new(concat!(
667 env!("CARGO_MANIFEST_DIR"),
668 "/../disassembler/testcase/Snow"
669 ));
670 let output = Path::new(concat!(
671 env!("CARGO_MANIFEST_DIR"),
672 "/testcase/Snow_new.bin"
673 ));
674 let nls = Nls::ShiftJIS;
675 compile(input, output, nls.clone()).unwrap();
676 let _parser = rfvp::script::parser::Parser::new(output, nls).unwrap();
677 }
678}