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.
- checksums.yaml +4 -4
- data/LICENSE.txt +674 -674
- data/README.md +68 -65
- data/example/README.md +3 -3
- data/example/disasm.rb +30 -34
- data/lib/keystone/arm_const.rb +7 -0
- data/lib/keystone/keystone.rb +76 -0
- data/lib/keystone/keystone_const.rb +97 -0
- data/lib/keystone/version.rb +3 -0
- data/lib/ncpp/commands.rb +78 -5
- data/lib/ncpp/interpreter.rb +4 -3
- data/lib/ncpp/types.rb +1 -1
- data/lib/ncpp/utils.rb +84 -3
- data/lib/ncpp/version.rb +1 -1
- data/lib/ncpp.rb +14 -20
- data/lib/nitro/nitro.rb +27 -5
- data/lib/unicorn/arm_const.rb +196 -0
- data/lib/unicorn/unicorn.rb +204 -0
- data/lib/unicorn/unicorn_const.rb +152 -0
- data/lib/unicorn/version.rb +3 -0
- metadata +11 -5
- data/lib/nitro/nitro.dll +0 -0
- data/lib/unarm/unarm.dll +0 -0
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
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
#### Requirements
|
|
8
|
-
|
|
9
|
-
- [Ruby (≥ 3.4 required)](https://www.ruby-lang.org/en/downloads/)
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
- [
|
|
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('
|
|
7
|
-
|
|
8
|
-
NCPP.show_rom_info(rom)
|
|
9
|
-
|
|
10
|
-
puts
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
rom.
|
|
23
|
-
|
|
24
|
-
f.puts
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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,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
|
data/lib/ncpp/commands.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'types
|
|
4
|
-
require_relative 'version
|
|
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
|
|
data/lib/ncpp/interpreter.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require_relative 'parser
|
|
2
|
-
require_relative 'commands
|
|
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
data/lib/ncpp/utils.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
require_relative '../nitro/nitro
|
|
2
|
-
require_relative '../unarm/unarm
|
|
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
|