NCPrePatcher 0.2.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,66 +1,69 @@
1
- # NCPrePatcher
2
-
3
- A preprocessor for [NCPatcher](https://github.com/TheGameratorT/NCPatcher) with access to your NDS ROM and a disassembler.
4
-
5
- ## Installation
6
-
7
- #### Requirements
8
-
9
- - [Ruby (≥ 3.4 required)](https://www.ruby-lang.org/en/downloads/)
10
-
11
- Go to Releases, download the .gem file for your platform, then open up the command line where it was downloaded to and enter:
12
- ```console
13
- gem install NCPrePatcher-[version]-[platform].gem
14
- ```
15
-
16
- The `ncpp` command should now be available at your command line (a system reboot may be required).
17
-
18
- ## Usage
19
-
20
- NCPrePatcher can be used as a Ruby library or, as it was built for, as a preprocessor alongside NCPatcher.
21
-
22
- For the former, simply `require` it as you would with any gem:
23
- ```ruby
24
- require ncpp
25
- ```
26
-
27
- For the latter, navigate to an existing project using NCPatcher (where an `ncpatcher.json` file is present), and run:
28
- ```console
29
- ncpp
30
- ```
31
- Follow the directions given, and it will be installed into your project. Subsequently, running `ncpp` manually will no longer be required; being added as a pre-build command in `ncpatcher.json`, it will run when NCPatcher does.
32
-
33
- For examples of usage as a preprocessor, see [ncpp-demos](https://github.com/pete420griff/ncpp-demos).
34
-
35
- To view what else NCPrePatcher can do, run:
36
- ```console
37
- ncpp --help
38
- ```
39
-
40
- ## Building
41
-
42
- > [!NOTE]
43
- > This is an alternative to installing NCPrePatcher via the methods described in [the installation guide](#installation)
44
-
45
- #### Requirements
46
-
47
- - Ruby
48
- - CMake and a modern C++ compiler
49
- - Rust and Cargo
50
-
51
- To build the required native libraries, go to /ext/ and run:
52
- ```console
53
- ruby build.rb
54
- ```
55
-
56
- Then go back to the base directory and run:
57
- ```console
58
- gem build ncpp.gemspec
59
- ```
60
-
61
- ## Credits
62
-
63
- - Code from NCPatcher used by the **nitro** library
64
- - [unarm](https://github.com/AetiasHax/unarm) used for disassembling
65
- - [Ruby-FFI](https://github.com/ffi/ffi) used for binding the above libraries to Ruby
1
+ # NCPrePatcher
2
+
3
+ A preprocessor for [NCPatcher](https://github.com/TheGameratorT/NCPatcher) with access to your NDS ROM, a disassembler, an assembler, and an emulator.
4
+
5
+ ## Installation
6
+
7
+ #### Requirements
8
+
9
+ - [Ruby (≥ 3.4 required)](https://www.ruby-lang.org/en/downloads/)
10
+
11
+ From the command line, enter:
12
+ ```console
13
+ gem install NCPrePatcher
14
+ ```
15
+
16
+ The `ncpp` command should now be available at your command line (a system reboot may be required).
17
+
18
+ ## Usage
19
+
20
+ NCPrePatcher can be used as a Ruby library or, as it was built for, as a preprocessor alongside NCPatcher.
21
+
22
+ For the former, simply `require` it as you would with any gem:
23
+ ```ruby
24
+ require ncpp
25
+ ```
26
+
27
+ For the latter, navigate to an existing project using NCPatcher (where an `ncpatcher.json` file is present), and run:
28
+ ```console
29
+ ncpp
30
+ ```
31
+ Follow the directions given, and it will be installed into your project. Subsequently, running `ncpp` manually will no longer be required; being added as a pre-build command in `ncpatcher.json`, it will run when NCPatcher does.
32
+
33
+ For examples of usage as a preprocessor, see [ncpp-demos](https://github.com/pete420griff/ncpp-demos).
34
+
35
+ To view what else NCPrePatcher can do, run:
36
+ ```console
37
+ ncpp --help
38
+ ```
39
+
40
+ ## Building
41
+
42
+ > [!NOTE]
43
+ > This is an alternative to installing NCPrePatcher via the methods described in [the installation guide](#installation)
44
+
45
+ #### Requirements
46
+
47
+ - Ruby
48
+ - CMake and a modern C++ compiler
49
+ - Rust and Cargo
50
+ - vcpkg
51
+
52
+ To build the native libraries, go to /ext/ and run:
53
+ ```console
54
+ ruby build.rb
55
+ ```
56
+
57
+ Finally, go back to the base directory and run:
58
+ ```console
59
+ gem build ncpp.gemspec
60
+ ```
61
+
62
+ ## Credits
63
+
64
+ - Code from NCPatcher used by the **nitro** library
65
+ - [unarm](https://github.com/AetiasHax/unarm) used for disassembling
66
+ - [Keystone](https://github.com/keystone-engine/keystone/tree/master) used for assembling
67
+ - [Unicorn](https://github.com/unicorn-engine/unicorn/tree/master) used for emulating
68
+ - [Ruby-FFI](https://github.com/ffi/ffi) used for binding the above libraries to Ruby
66
69
  - [Parslet](https://github.com/kschiess/parslet) used for parsing the DSL
data/example/README.md CHANGED
@@ -1,3 +1,3 @@
1
- # Examples
2
-
3
- This folder contains examples of using NCPrePatcher as a **Ruby library**.
1
+ # Examples
2
+
3
+ This folder contains examples of using NCPrePatcher as a **Ruby library**.
data/example/disasm.rb CHANGED
@@ -1,34 +1,30 @@
1
- require 'ncpp'
2
-
3
- start_time = Time.now
4
-
5
- puts 'Loading rom...'
6
- rom = Nitro::Rom.new('Ratatouille.nds')
7
-
8
- NCPP.show_rom_info(rom)
9
-
10
- puts "Ov0 sinit start: " + rom.ovt.get_entry(0)[:sinit_start].to_hex
11
- puts "Ov1 sinit start: " + rom.ovt.get_entry(1)[:sinit_start].to_hex
12
- puts "Ov2 sinit start: " + rom.ovt.get_entry(2)[:sinit_start].to_hex
13
-
14
- # puts 'Loading symbols...'
15
- # Unarm.load_symbols9('symbols9.x')
16
-
17
- f = File.open('rat-disasm.txt', 'w')
18
-
19
- puts 'Disassembling arm9...'
20
-
21
- # Here we treat every word in the binary as if it's an arm instruction; this is of course not reality
22
- rom.arm9.each_ins {|ins| f.puts "#{ins.addr.to_hex}: #{ins.str}" }
23
-
24
- f.puts
25
-
26
- rom.each_overlay do |ov, id|
27
- puts "Disassembling overlay#{id}"
28
- ov.each_ins {|ins| f.puts "#{ins.addr.to_hex}: #{ins.str}" }
29
- f.puts
30
- end
31
-
32
- f.close
33
-
34
- puts "Done! Took #{Time.now - start_time} seconds."
1
+ require 'ncpp'
2
+
3
+ start_time = Time.now
4
+
5
+ puts 'Loading rom...'
6
+ rom = Nitro::Rom.new('speeeedrun2.nds')
7
+
8
+ NCPP.show_rom_info(rom)
9
+
10
+ # puts 'Loading symbols...'
11
+ Unarm.load_symbols9('../symbols9.x')
12
+
13
+ f = File.open('speedrun-disasm2.txt', 'w')
14
+
15
+ puts 'Disassembling arm9...'
16
+
17
+ # Here we treat every word in the binary as if it's an arm instruction; this is of course not reality
18
+ rom.arm9.each_ins {|ins| f.puts "#{ins.addr.to_hex}: #{ins.raw.to_hex} #{ins.str}" }
19
+
20
+ f.puts
21
+
22
+ rom.each_overlay do |ov, id|
23
+ puts "Disassembling overlay#{id}"
24
+ ov.each_ins {|ins| f.puts "#{ins.addr.to_hex}: #{ins.raw.to_hex} #{ins.str}" }
25
+ f.puts
26
+ end
27
+
28
+ f.close
29
+
30
+ puts "Done! Took #{Time.now - start_time} seconds."
@@ -0,0 +1,7 @@
1
+ # For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.rb]
2
+
3
+ module Keystone
4
+ KS_ERR_ASM_ARM_INVALIDOPERAND = 512
5
+ KS_ERR_ASM_ARM_MISSINGFEATURE = 513
6
+ KS_ERR_ASM_ARM_MNEMONICFAIL = 514
7
+ end
@@ -0,0 +1,76 @@
1
+ require 'ffi'
2
+
3
+ require_relative 'arm_const'
4
+ require_relative 'keystone_const'
5
+ require_relative 'version'
6
+
7
+ module KeystoneBind
8
+ extend FFI::Library
9
+ ffi_lib [
10
+ File.expand_path("keystone", __dir__),
11
+ File.expand_path("keystone.dylib", __dir__),
12
+ File.expand_path("keystone.so", __dir__),
13
+ ]
14
+
15
+ typedef :pointer, :ks_engine
16
+ typedef :pointer, :ks_engine_handle
17
+ typedef :uint, :ks_arch
18
+ typedef :uint, :ks_err
19
+ typedef :uint, :ks_opt_type
20
+
21
+ attach_function :ks_version, [:pointer, :pointer], :uint
22
+ attach_function :ks_arch_supported, [:ks_arch], :bool
23
+ attach_function :ks_open, [:ks_arch, :int, :ks_engine_handle], :ks_err
24
+ attach_function :ks_close, [:ks_engine], :ks_err
25
+ attach_function :ks_errno, [:ks_engine], :ks_err
26
+ attach_function :ks_strerror, [:ks_err], :string
27
+ attach_function :ks_option, [:ks_engine, :ks_opt_type, :size_t], :ks_err
28
+ attach_function :ks_asm, [:ks_engine, :string, :uint64, :pointer, :pointer, :pointer], :int
29
+ attach_function :ks_free, [:pointer], :void
30
+ end
31
+
32
+ module Keystone
33
+ extend KeystoneBind
34
+
35
+ def self.major_version
36
+ ks_version(nil, nil)
37
+ end
38
+
39
+ def self.arch_supported?(ks_arch)
40
+ ks_arch_supported(ks_arch)
41
+ end
42
+
43
+ class Assembler
44
+ include KeystoneBind
45
+
46
+ def initialize(arch: KS_ARCH_ARM, mode: KS_MODE_ARM)
47
+
48
+ FFI::MemoryPointer.new(:pointer,1) do |ptr|
49
+ safe_call(:ks_open, arch, mode, ptr)
50
+ @engine = FFI::AutoPointer.new(ptr.read_pointer, method(:ks_close))
51
+ end
52
+ end
53
+
54
+ def assemble(asm_str, addr: 0)
55
+ encoding_ptr = FFI::MemoryPointer.new(:pointer)
56
+ encoding_size = FFI::MemoryPointer.new(:size_t)
57
+ stat_count = FFI::MemoryPointer.new(:size_t)
58
+ safe_call(:ks_asm, @engine, asm_str, addr, encoding_ptr, encoding_size, stat_count)
59
+ encoded_bytes = encoding_ptr.read_pointer.read_array_of_uint8(encoding_size.read_uint)
60
+ ks_free(encoding_ptr.read_pointer)
61
+ encoded_bytes.pack('C*')
62
+ end
63
+
64
+ def set_option(ks_opt_type, value)
65
+ safe_call(:ks_option, @engine, ks_opt_type, value)
66
+ end
67
+
68
+ private
69
+ def safe_call(meth_sym, *args)
70
+ err = send(meth_sym, *args)
71
+ raise "Error from #{meth_sym}: #{ks_strerror(err)}" if err != KS_ERR_OK
72
+ end
73
+ end
74
+ end
75
+
76
+ Ks = Keystone
@@ -0,0 +1,97 @@
1
+ # For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.rb]
2
+
3
+ module Keystone
4
+ KS_API_MAJOR = 0
5
+ KS_API_MINOR = 9
6
+ KS_VERSION_MAJOR = 0
7
+ KS_VERSION_MINOR = 9
8
+ KS_VERSION_EXTRA = 2
9
+ KS_ARCH_ARM = 1
10
+ KS_ARCH_ARM64 = 2
11
+ KS_ARCH_MIPS = 3
12
+ KS_ARCH_X86 = 4
13
+ KS_ARCH_PPC = 5
14
+ KS_ARCH_SPARC = 6
15
+ KS_ARCH_SYSTEMZ = 7
16
+ KS_ARCH_HEXAGON = 8
17
+ KS_ARCH_EVM = 9
18
+ KS_ARCH_RISCV = 10
19
+ KS_ARCH_MAX = 11
20
+ KS_MODE_LITTLE_ENDIAN = 0
21
+ KS_MODE_BIG_ENDIAN = 1073741824
22
+ KS_MODE_ARM = 1
23
+ KS_MODE_THUMB = 16
24
+ KS_MODE_V8 = 64
25
+ KS_MODE_MICRO = 16
26
+ KS_MODE_MIPS3 = 32
27
+ KS_MODE_MIPS32R6 = 64
28
+ KS_MODE_MIPS32 = 4
29
+ KS_MODE_MIPS64 = 8
30
+ KS_MODE_16 = 2
31
+ KS_MODE_32 = 4
32
+ KS_MODE_64 = 8
33
+ KS_MODE_PPC32 = 4
34
+ KS_MODE_PPC64 = 8
35
+ KS_MODE_QPX = 16
36
+ KS_MODE_RISCV32 = 4
37
+ KS_MODE_RISCV64 = 8
38
+ KS_MODE_SPARC32 = 4
39
+ KS_MODE_SPARC64 = 8
40
+ KS_MODE_V9 = 16
41
+ KS_ERR_ASM = 128
42
+ KS_ERR_ASM_ARCH = 512
43
+ KS_ERR_OK = 0
44
+ KS_ERR_NOMEM = 1
45
+ KS_ERR_ARCH = 2
46
+ KS_ERR_HANDLE = 3
47
+ KS_ERR_MODE = 4
48
+ KS_ERR_VERSION = 5
49
+ KS_ERR_OPT_INVALID = 6
50
+ KS_ERR_ASM_EXPR_TOKEN = 128
51
+ KS_ERR_ASM_DIRECTIVE_VALUE_RANGE = 129
52
+ KS_ERR_ASM_DIRECTIVE_ID = 130
53
+ KS_ERR_ASM_DIRECTIVE_TOKEN = 131
54
+ KS_ERR_ASM_DIRECTIVE_STR = 132
55
+ KS_ERR_ASM_DIRECTIVE_COMMA = 133
56
+ KS_ERR_ASM_DIRECTIVE_RELOC_NAME = 134
57
+ KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN = 135
58
+ KS_ERR_ASM_DIRECTIVE_FPOINT = 136
59
+ KS_ERR_ASM_DIRECTIVE_UNKNOWN = 137
60
+ KS_ERR_ASM_DIRECTIVE_EQU = 138
61
+ KS_ERR_ASM_DIRECTIVE_INVALID = 139
62
+ KS_ERR_ASM_VARIANT_INVALID = 140
63
+ KS_ERR_ASM_EXPR_BRACKET = 141
64
+ KS_ERR_ASM_SYMBOL_MODIFIER = 142
65
+ KS_ERR_ASM_SYMBOL_REDEFINED = 143
66
+ KS_ERR_ASM_SYMBOL_MISSING = 144
67
+ KS_ERR_ASM_RPAREN = 145
68
+ KS_ERR_ASM_STAT_TOKEN = 146
69
+ KS_ERR_ASM_UNSUPPORTED = 147
70
+ KS_ERR_ASM_MACRO_TOKEN = 148
71
+ KS_ERR_ASM_MACRO_PAREN = 149
72
+ KS_ERR_ASM_MACRO_EQU = 150
73
+ KS_ERR_ASM_MACRO_ARGS = 151
74
+ KS_ERR_ASM_MACRO_LEVELS_EXCEED = 152
75
+ KS_ERR_ASM_MACRO_STR = 153
76
+ KS_ERR_ASM_MACRO_INVALID = 154
77
+ KS_ERR_ASM_ESC_BACKSLASH = 155
78
+ KS_ERR_ASM_ESC_OCTAL = 156
79
+ KS_ERR_ASM_ESC_SEQUENCE = 157
80
+ KS_ERR_ASM_ESC_STR = 158
81
+ KS_ERR_ASM_TOKEN_INVALID = 159
82
+ KS_ERR_ASM_INSN_UNSUPPORTED = 160
83
+ KS_ERR_ASM_FIXUP_INVALID = 161
84
+ KS_ERR_ASM_LABEL_INVALID = 162
85
+ KS_ERR_ASM_FRAGMENT_INVALID = 163
86
+ KS_ERR_ASM_INVALIDOPERAND = 512
87
+ KS_ERR_ASM_MISSINGFEATURE = 513
88
+ KS_ERR_ASM_MNEMONICFAIL = 514
89
+ KS_OPT_SYNTAX = 1
90
+ KS_OPT_SYM_RESOLVER = 2
91
+ KS_OPT_SYNTAX_INTEL = 1
92
+ KS_OPT_SYNTAX_ATT = 2
93
+ KS_OPT_SYNTAX_NASM = 4
94
+ KS_OPT_SYNTAX_MASM = 8
95
+ KS_OPT_SYNTAX_GAS = 16
96
+ KS_OPT_SYNTAX_RADIX16 = 32
97
+ end
@@ -0,0 +1,3 @@
1
+ module KeystoneEngine
2
+ VERSION = '0.9.2'
3
+ end
data/lib/ncpp/commands.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'types.rb'
4
- require_relative 'version.rb'
3
+ require_relative 'types'
4
+ require_relative 'version'
5
5
 
6
6
  module NCPP
7
7
 
@@ -206,6 +206,9 @@ module NCPP
206
206
  hex: ->(i) { Utils.integer_check(i,'hex'); i.to_hex }.returns(String)
207
207
  .describe('Returns a hexadecimal representation of the given Integer.'),
208
208
 
209
+ ord: ->(s) { s.ord }.returns(Integer)
210
+ .describe('Gets the Integer ordinal of the first character in the given String.'),
211
+
209
212
  string: ->(x) { String(x) }.returns(String)
210
213
  .describe('Gets the given argument as a String.'),
211
214
 
@@ -587,6 +590,7 @@ module NCPP
587
590
  exp: ->(n) { Math.exp(n) }.returns(Float).describe('Gets e raised to the power of the given number.'),
588
591
  log: ->(n) { Math.log(n) }.returns(Float).describe('Gets the base logarithm of the given number.'),
589
592
  sqrt: ->(n) { Math.sqrt(n) }.returns(Float).describe('Gets the square root of the given number.'),
593
+ round: ->(f, n_digits=0) { f.round(n_digits) }.returns(Numeric),
590
594
  clamp: ->(n, min,max=nil) { n.clamp(min,max) }.returns(Numeric)
591
595
  .describe('Clamps number between given min and max values.'),
592
596
 
@@ -644,7 +648,7 @@ module NCPP
644
648
  "the original ASM with the immediate swapped to the value given."),
645
649
 
646
650
  repl_array: ->(loc, ov, dtype, arr) { Utils.gen_repl_array(loc,ov,dtype,arr) }.returns(String),
647
-
651
+
648
652
  repl_u64_array: ->(loc,ov_or_arr,arr=nil) { Utils.gen_repl_type_array(loc, ov_or_arr, :u64, arr) }.returns(String),
649
653
  repl_s64_array: ->(loc,ov_or_arr,arr=nil) { Utils.gen_repl_type_array(loc, ov_or_arr, :s64, arr) }.returns(String),
650
654
  repl_u32_array: ->(loc,ov_or_arr,arr=nil) { Utils.gen_repl_type_array(loc, ov_or_arr, :u32, arr) }.returns(String),
@@ -677,11 +681,15 @@ module NCPP
677
681
  get_signed_byte: ->(addr,ov=nil) { Utils.get_signed_byte(addr,ov) }.returns(Integer),
678
682
  get_cstring: ->(addr,ov=nil) { Utils.get_cstring(addr,ov) }.returns(String),
679
683
  get_array: ->(addr,ov,e_type_id,e_count=1) { Utils.get_array(addr,ov,e_type_id,e_count) }.returns(Array),
684
+ get_chars: ->(addr,ov, char_count) { Utils.get_array(addr,ov,Utils::DTYPE_IDS[:u8],char_count).map { it.chr } }
685
+ .returns(Array),
680
686
 
681
687
  get_c_array: ->(addr,ov,e_type_id,e_count=1) {
682
688
  Utils.to_c_array(Utils.get_array(addr,ov,e_type_id,e_count))
683
689
  }.returns(Array),
684
690
 
691
+ get_byte_str: ->(loc,ov,size) { Utils.get_byte_str(loc,ov,size) }.returns(String),
692
+
685
693
  find_first_branch_to: ->(branch_dest, start_loc,start_ov=nil) {
686
694
  Utils.find_branch_to(branch_dest, start_loc,start_ov)
687
695
  }.returns(Integer),
@@ -772,6 +780,14 @@ module NCPP
772
780
  s16: ->(n) { n.signed(16) }.returns(Integer).describe('Gets the given number as a signed 16-bit Integer.'),
773
781
  u8: ->(n) { n.unsigned(8) }.returns(Integer).describe('Gets the given number as an unsigned 8-bit Integer.'),
774
782
  s8: ->(n) { n.signed(8) }.returns(Integer).describe('Gets the given number as a signed 8-bit Integer.'),
783
+ char: ->(n) { n.unsigned(8).chr }.returns(String).describe('Gets the given number as an ASCII character.'),
784
+
785
+ sizeof: ->(dtype) { Utils::DTYPES[dtype][:size] }.returns(Integer),
786
+
787
+ f32: ->(n) { [n].pack('g').unpack('L>') }.returns(Integer),
788
+ f64: ->(n) { [n].pack('G').unpack('Q>') }.returns(Integer),
789
+ from_f32: ->(n) { [n].pack('L>').unpack('g')[0] }.returns(Float),
790
+ from_f64: ->(n) { [n].pack('Q>').unpack('G')[0] }.returns(Float),
775
791
 
776
792
  from_fx_deg: ->(fx_num) {
777
793
  Float(fx_num) / 0x10000 * 360
@@ -794,7 +810,36 @@ module NCPP
794
810
  from_gx_rgb: ->(n) { [(n >> 0) & 31, (n >> 5) & 31, (n >> 10) & 31] }.returns(Array)
795
811
  .describe('Unpacks the given RGB x1B5G5R5 value as an Array ([R,G,B]).'),
796
812
  from_gx_rgba: ->(n) { [(n >> 0) & 31, (n >> 5) & 31, (n >> 10) & 31, (n >> 15) & 1] }.returns(Array)
797
- .describe('Unpacks the given packed RGB A1B5G5R5 value as an Array ([R,G,B,A]).')
813
+ .describe('Unpacks the given packed RGB A1B5G5R5 value as an Array ([R,G,B,A]).'),
814
+
815
+ pack_u64_array: ->(arr) { arr.pack('Q*') }.returns(String),
816
+ pack_u32_array: ->(arr) { arr.pack('L*') }.returns(String),
817
+ pack_u16_array: ->(arr) { arr.pack('S*') }.returns(String),
818
+ pack_u8_array: ->(arr) { arr.pack('C*') }.returns(String),
819
+ pack_s64_array: ->(arr) { arr.pack('q*') }.returns(String),
820
+ pack_s32_array: ->(arr) { arr.pack('l*') }.returns(String),
821
+ pack_s16_array: ->(arr) { arr.pack('s*') }.returns(String),
822
+ pack_s8_array: ->(arr) { arr.pack('c*') }.returns(String),
823
+
824
+ unpack_u64_array: ->(byte_str) { byte_str.unpack('Q*') }.returns(Array),
825
+ unpack_u32_array: ->(byte_str) { byte_str.unpack('L*') }.returns(Array),
826
+ unpack_u16_array: ->(byte_str) { byte_str.unpack('S*') }.returns(Array),
827
+ unpack_u8_array: ->(byte_str) { byte_str.unpack('C*') }.returns(Array),
828
+ unpack_s64_array: ->(byte_str) { byte_str.unpack('q*') }.returns(Array),
829
+ unpack_s32_array: ->(byte_str) { byte_str.unpack('l*') }.returns(Array),
830
+ unpack_s16_array: ->(byte_str) { byte_str.unpack('s*') }.returns(Array),
831
+ unpack_s8_array: ->(byte_str) { byte_str.unpack('c*') }.returns(Array),
832
+
833
+ emulate_func: ->(loc,ov,*args) { Utils.emulate_func(loc,ov,*args) }.returns(Object).impure,
834
+ emu_get_reg: ->(reg_s) { $emu.read_reg(reg_s.to_sym) }.returns(Integer).impure,
835
+ emu_set_reg: ->(reg_s,val) { $emu.write_reg(reg_s.to_sym,val) }.impure,
836
+ emu_get_mem: ->(loc,size) { Utils.emu_get_mem(loc,size) }.returns(String).impure,
837
+ emu_set_mem: ->(loc,byte_str) { Utils.emu_set_mem(loc,byte_str) }.impure,
838
+ emu_load_ov: ->(ov_id) { $emu.load_overlay(ov_id) }.impure,
839
+ emu_reset: -> { $emu = Uc::Emu.new(); $emu.load_arm9 }.impure,
840
+
841
+ assemble_arm: ->(asm,addr=0) { Utils.assemble_arm(asm,addr:addr) }.returns(Integer),
842
+ assemble_thumb: ->(asm,addr=0) { Utils.assemble_thumb(asm,addr:addr) }.returns(Integer),
798
843
 
799
844
  },
800
845
 
@@ -839,6 +884,7 @@ module NCPP
839
884
  get_s8: :get_signed_byte,
840
885
  get_cstr: :get_cstring,
841
886
  disasm_ins: :disasm_arm_ins,
887
+ disasm: :disasm_arm_ins,
842
888
  disasm_hex_seq: :disasm_arm_hex_seq,
843
889
  disasm_hex_str: :disasm_arm_hex_seq,
844
890
  disasm_arm_hex_str: :disasm_arm_hex_seq,
@@ -876,7 +922,34 @@ module NCPP
876
922
  is_addr_in_ov: :is_address_in_overlay,
877
923
  is_addr_in_arm9: :is_address_in_arm9,
878
924
  is_addr_in_arm7: :is_address_in_arm7,
879
- find_hex_seq: :find_hex_bytes
925
+ find_hex_seq: :find_hex_bytes,
926
+ pack_u64_arr: :pack_u64_array,
927
+ pack_u32_arr: :pack_u32_array,
928
+ pack_u16_arr: :pack_u16_array,
929
+ pack_u8_arr: :pack_u8_array,
930
+ pack_s64_arr: :pack_s64_array,
931
+ pack_s32_arr: :pack_s32_array,
932
+ pack_s16_arr: :pack_s16_array,
933
+ pack_s8_arr: :pack_s8_array,
934
+ unpack_u64_arr: :unpack_u64_array,
935
+ unpack_u32_arr: :unpack_u32_array,
936
+ unpack_u16_arr: :unpack_u16_array,
937
+ unpack_u8_arr: :unpack_u8_array,
938
+ unpack_s64_arr: :unpack_s64_array,
939
+ unpack_s32_arr: :unpack_s32_array,
940
+ unpack_s16_arr: :unpack_s16_array,
941
+ unpack_s8_arr: :unpack_s8_array,
942
+ emulate_function: :emulate_func,
943
+ emu_call_func: :emulate_func,
944
+ emu_get_register: :emu_get_reg,
945
+ emu_set_register: :emu_set_reg,
946
+ emu_get_memory: :emu_get_mem,
947
+ emu_set_memory: :emu_set_mem,
948
+ emu_load_overlay: :emu_load_ov,
949
+ assemble: :assemble_arm,
950
+ assemble_ins: :assemble_arm,
951
+ assemble_arm_ins: :assemble_arm,
952
+ assemble_thumb_ins: :assemble_thumb
880
953
  }).freeze
881
954
 
882
955
 
@@ -1,5 +1,5 @@
1
- require_relative 'parser.rb'
2
- require_relative 'commands.rb'
1
+ require_relative 'parser'
2
+ require_relative 'commands'
3
3
 
4
4
  require 'fileutils'
5
5
 
@@ -170,7 +170,8 @@ module NCPP
170
170
  def: :define,
171
171
  desc: :describe,
172
172
  get_cmd_names: :get_command_names,
173
- get_var_names: :get_variable_names
173
+ get_var_names: :get_variable_names,
174
+ clear_cache: :invalidate_cache
174
175
  }).freeze
175
176
  end
176
177
 
data/lib/ncpp/types.rb CHANGED
@@ -1,4 +1,4 @@
1
- require_relative 'utils.rb'
1
+ require_relative 'utils'
2
2
 
3
3
  module NCPP
4
4
 
data/lib/ncpp/utils.rb CHANGED
@@ -1,5 +1,6 @@
1
- require_relative '../nitro/nitro.rb'
2
- require_relative '../unarm/unarm.rb'
1
+ require_relative '../nitro/nitro'
2
+ require_relative '../unarm/unarm'
3
+ require_relative '../unicorn/unicorn'
3
4
 
4
5
  require 'did_you_mean/jaro_winkler'
5
6
  require 'did_you_mean/levenshtein'
@@ -265,6 +266,11 @@ module NCPP
265
266
  end
266
267
  end
267
268
 
269
+ def self.get_byte_str(loc, ov, size)
270
+ addr, _ov, code_bin = resolve_code_loc(loc, ov)
271
+ code_bin.get_sect_ptr(addr,size).read_array_of_uint8(size).pack('C*')
272
+ end
273
+
268
274
  def self.find_branch_to(branch_dest, start_loc, start_ov=nil, from_func: false, find_all: false)
269
275
  start_addr, _ov, code_bin = resolve_code_loc(start_loc, start_ov)
270
276
  if branch_dest.is_a? Array
@@ -423,6 +429,40 @@ module NCPP
423
429
  gen_repl_array(addr, ov, DTYPE_IDS[:u8], [new_hex_str].pack('H*').unpack('C*'))
424
430
  end
425
431
 
432
+ def self.emulate_func(func_loc, ov, *args)
433
+ addr, ov, code_bin = resolve_code_loc(func_loc,ov)
434
+ $emu.load_overlay(ov) if !ov.nil? && ov >= 0
435
+ $emu.call_func(addr, *args)
436
+ end
437
+
438
+ def self.emu_get_mem(loc, size)
439
+ addr, ov = resolve_loc(loc)
440
+ $emu.load_overlay(ov) if !ov.nil? && ov >= 0
441
+ $emu.read_mem(addr, size)
442
+ end
443
+
444
+ def self.emu_set_mem(loc, byte_str)
445
+ addr, ov = resolve_loc(loc)
446
+ $emu.load_overlay(ov) if !ov.nil? && ov >= 0
447
+ $emu.write_mem(addr, byte_str)
448
+ end
449
+
450
+ def self.assemble_arm(asm, addr: 0)
451
+ if asm.is_a? Array
452
+ asm.map.with_index {|ins,idx| $arm_assembler.assemble(ins,addr+4*idx).unpack('L')[0] }
453
+ else
454
+ $arm_assembler.assemble(asm).unpack('L')[0]
455
+ end
456
+ end
457
+
458
+ def self.assemble_thumb(asm, addr: 0)
459
+ if asm.is_a? Array
460
+ asm.map.with_index {|ins,idx| $thumb_assembler.assemble(ins,addr+2*idx).unpack('S')[0] }
461
+ else
462
+ $thumb_assembler.assemble(asm).unpack('S')[0]
463
+ end
464
+ end
465
+
426
466
  class << self
427
467
  alias_method :get_u64, :get_dword
428
468
  alias_method :get_u32, :get_word
@@ -596,7 +636,7 @@ class Nitro::CodeBin
596
636
 
597
637
  instructions << ins
598
638
 
599
- if target = ins.target_addr
639
+ if target = ins.target_addr # if target addr not nil
600
640
  pool[target] ||= Unarm::Data.new(read_word(target), addr: target, loc: get_loc)
601
641
  end
602
642
 
@@ -698,3 +738,44 @@ class Nitro::CodeBin
698
738
  end
699
739
 
700
740
  end
741
+
742
+ #
743
+ # Various utility methods tying Unicorn to the Unarm and Nitro modules
744
+ #
745
+ class Unicorn::Emulator
746
+
747
+ def load_arm9
748
+ arm = $rom.arm9
749
+ main_added = false
750
+ arm.autoload_entries.sort_by {|e| e[:address] }.each do |e|
751
+ if e[:address] > arm.end_addr # DTCM found
752
+ add_region(Uc::Region.new(e[:address], 16*1024))
753
+ next
754
+ end
755
+ if !main_added && e[:address] > arm.start_addr # Main section found
756
+ size = e[:address] - arm.start_addr
757
+ add_section(Uc::Sect.new(arm.start_addr, arm.get_sect_ptr(arm.start_addr,size), size))
758
+ main_added = true
759
+ end
760
+ next if e[:address]+e[:size] <= e[:address]
761
+ ptr = arm.get_sect_ptr(e[:address],e[:size])
762
+ add_section(Uc::Sect.new(e[:address], ptr, e[:size]))
763
+ end
764
+ end
765
+
766
+ def load_overlay(ov_id)
767
+ ov = $rom.get_overlay(ov_id)
768
+ add_section(Uc::Sect.new(ov.start_addr, ov.get_sect_ptr(ov.start_addr, ov.size), ov.size))
769
+ end
770
+ alias_method :load_ov, :load_overlay
771
+
772
+ def call_func(addr, *args)
773
+ raise "Calling functions with more than 4 args isn't supported yet" if args.length > 4
774
+ args.each_with_index do |arg,i|
775
+ send(:"write_r#{i}", arg)
776
+ end
777
+ run(from: addr, to: read_lr, timeout_ms: 5000)
778
+ read_r0
779
+ end
780
+
781
+ end