NCPrePatcher 0.2.0
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 +7 -0
- data/LICENSE.txt +674 -0
- data/README.md +66 -0
- data/example/README.md +3 -0
- data/example/disasm.rb +34 -0
- data/exe/ncpp +4 -0
- data/lib/ncpp/commands.rb +903 -0
- data/lib/ncpp/interpreter.rb +919 -0
- data/lib/ncpp/parser.rb +249 -0
- data/lib/ncpp/types.rb +68 -0
- data/lib/ncpp/utils.rb +700 -0
- data/lib/ncpp/version.rb +4 -0
- data/lib/ncpp.rb +478 -0
- data/lib/nitro/nitro.dll +0 -0
- data/lib/nitro/nitro.rb +440 -0
- data/lib/unarm/unarm.dll +0 -0
- data/lib/unarm/unarm.rb +836 -0
- metadata +91 -0
data/lib/nitro/nitro.rb
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
require 'ffi'
|
|
2
|
+
|
|
3
|
+
module NitroBind
|
|
4
|
+
extend FFI::Library
|
|
5
|
+
ffi_lib [
|
|
6
|
+
File.expand_path("nitro", __dir__),
|
|
7
|
+
File.expand_path("nitro.dylib", __dir__),
|
|
8
|
+
File.expand_path("nitro.so", __dir__),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
typedef :pointer, :rom_handle
|
|
12
|
+
typedef :pointer, :header_handle
|
|
13
|
+
typedef :pointer, :codebin_handle
|
|
14
|
+
typedef :pointer, :ovte_handle
|
|
15
|
+
typedef :pointer, :module_params_handle
|
|
16
|
+
|
|
17
|
+
attach_function :nitroRom_alloc, [], :rom_handle
|
|
18
|
+
attach_function :nitroRom_release, [:rom_handle], :void
|
|
19
|
+
attach_function :nitroRom_load, [:rom_handle, :string], :bool
|
|
20
|
+
attach_function :nitroRom_getSize, [:rom_handle], :size_t
|
|
21
|
+
attach_function :nitroRom_getHeader, [:rom_handle], :header_handle
|
|
22
|
+
attach_function :nitroRom_getFile, [:rom_handle, :uint32], :pointer
|
|
23
|
+
attach_function :nitroRom_getFileSize, [:rom_handle, :uint32], :uint32
|
|
24
|
+
attach_function :nitroRom_loadArm9, [:rom_handle], :codebin_handle
|
|
25
|
+
attach_function :nitroRom_loadArm7, [:rom_handle], :codebin_handle
|
|
26
|
+
attach_function :nitroRom_loadOverlay, [:rom_handle, :uint32], :codebin_handle
|
|
27
|
+
attach_function :nitroRom_getOverlayCount, [:rom_handle], :uint32
|
|
28
|
+
attach_function :nitroRom_getArm9OvT, [:rom_handle], :ovte_handle
|
|
29
|
+
|
|
30
|
+
attach_function :headerBin_alloc, [], :header_handle
|
|
31
|
+
attach_function :headerBin_release, [:header_handle], :void
|
|
32
|
+
attach_function :headerBin_load, [:header_handle, :string], :bool
|
|
33
|
+
attach_function :headerBin_getGameTitle, [:header_handle], :string
|
|
34
|
+
attach_function :headerBin_getGameCode, [:header_handle], :string
|
|
35
|
+
attach_function :headerBin_getMakerCode, [:header_handle], :string
|
|
36
|
+
attach_function :headerBin_getArm9AutoLoadHookOffset, [:header_handle], :uint32
|
|
37
|
+
attach_function :headerBin_getArm7AutoLoadHookOffset, [:header_handle], :uint32
|
|
38
|
+
attach_function :headerBin_getArm9EntryAddress, [:header_handle], :uint32
|
|
39
|
+
attach_function :headerBin_getArm7EntryAddress, [:header_handle], :uint32
|
|
40
|
+
attach_function :headerBin_getArm9RamAddress, [:header_handle], :uint32
|
|
41
|
+
attach_function :headerBin_getArm7RamAddress, [:header_handle], :uint32
|
|
42
|
+
attach_function :headerBin_getArm9OvTSize, [:header_handle], :uint32
|
|
43
|
+
|
|
44
|
+
attach_function :codeBin_read64, [:codebin_handle, :uint32], :uint64
|
|
45
|
+
attach_function :codeBin_read32, [:codebin_handle, :uint32], :uint32
|
|
46
|
+
attach_function :codeBin_read16, [:codebin_handle, :uint32], :uint16
|
|
47
|
+
attach_function :codeBin_read8, [:codebin_handle, :uint32], :uint8
|
|
48
|
+
attach_function :codeBin_readCString, [:codebin_handle, :uint32], :string
|
|
49
|
+
attach_function :codeBin_getSize, [:codebin_handle], :uint32
|
|
50
|
+
attach_function :codeBin_getStartAddress, [:codebin_handle], :uint32
|
|
51
|
+
|
|
52
|
+
attach_function :armBin_alloc, [], :codebin_handle
|
|
53
|
+
attach_function :armBin_release, [:codebin_handle], :void
|
|
54
|
+
attach_function :armBin_load, [:codebin_handle, :string, :uint32, :uint32, :uint32, :bool], :bool
|
|
55
|
+
attach_function :armBin_getEntryPointAddress, [:codebin_handle], :uint32
|
|
56
|
+
attach_function :armBin_getModuleParams, [:codebin_handle], :module_params_handle
|
|
57
|
+
attach_function :armBin_sanityCheckAddress, [:codebin_handle, :uint32], :bool
|
|
58
|
+
|
|
59
|
+
attach_function :overlayBin_alloc, [], :codebin_handle
|
|
60
|
+
attach_function :overlayBin_release, [:codebin_handle], :void
|
|
61
|
+
attach_function :overlayBin_load, [:codebin_handle, :string, :uint32, :bool, :int32], :bool
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
module Nitro
|
|
67
|
+
extend NitroBind
|
|
68
|
+
|
|
69
|
+
class OvtEntry < FFI::Struct
|
|
70
|
+
layout :overlay_id, :uint32,
|
|
71
|
+
:ram_address, :uint32,
|
|
72
|
+
:ram_size, :uint32,
|
|
73
|
+
:bss_size, :uint32,
|
|
74
|
+
:sinit_start, :uint32,
|
|
75
|
+
:sinit_end, :uint32,
|
|
76
|
+
:file_id, :uint32,
|
|
77
|
+
:comp_field, :uint32
|
|
78
|
+
|
|
79
|
+
def id
|
|
80
|
+
self[:overlay_id]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def ram_addr
|
|
84
|
+
self[:ram_address]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def sinit_bounds
|
|
88
|
+
self[:sinit_start]..self[:sinit_end]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def fid
|
|
92
|
+
self[:file_id]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def compressed_size
|
|
96
|
+
(self[:comp_field] >> 8) & 0xffffff # 24 bits
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def is_compressed?
|
|
100
|
+
(self[:comp_field] & 0xff) == 1 # 8 bits
|
|
101
|
+
end
|
|
102
|
+
alias_method :compressed?, :is_compressed?
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class OvtBin
|
|
107
|
+
include NitroBind
|
|
108
|
+
|
|
109
|
+
attr_reader :size, :entry_count
|
|
110
|
+
|
|
111
|
+
def initialize(args = {})
|
|
112
|
+
if args.has_key? :file_path
|
|
113
|
+
bin = File.binread(args[:file_path])
|
|
114
|
+
@size = bin.bytesize
|
|
115
|
+
@ptr = FFI::MemoryPointer.new(:uint8, @size)
|
|
116
|
+
@ptr.put_bytes(0, bin)
|
|
117
|
+
|
|
118
|
+
elsif args.has_key?(:ptr) && args.has_key?(:size)
|
|
119
|
+
@ptr = args[:ptr]
|
|
120
|
+
@size = args[:size]
|
|
121
|
+
@entry_count = @size / OvtEntry.size
|
|
122
|
+
else
|
|
123
|
+
raise ArgumentError
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def get_entry(id)
|
|
128
|
+
raise IndexError if id > @entry_count-1
|
|
129
|
+
OvtEntry.new(@ptr + id*OvtEntry.size)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class CodeBin
|
|
135
|
+
include NitroBind
|
|
136
|
+
|
|
137
|
+
def read64(addr)
|
|
138
|
+
codeBin_read64(@ptr, addr)
|
|
139
|
+
end
|
|
140
|
+
alias_method :read_dword, :read64
|
|
141
|
+
|
|
142
|
+
def read32(addr)
|
|
143
|
+
codeBin_read32(@ptr, addr)
|
|
144
|
+
end
|
|
145
|
+
alias_method :read_word, :read32
|
|
146
|
+
|
|
147
|
+
def read16(addr)
|
|
148
|
+
codeBin_read16(@ptr, addr)
|
|
149
|
+
end
|
|
150
|
+
alias_method :read_hword, :read16
|
|
151
|
+
|
|
152
|
+
def read8(addr)
|
|
153
|
+
codeBin_read8(@ptr, addr)
|
|
154
|
+
end
|
|
155
|
+
alias_method :read_byte, :read8
|
|
156
|
+
|
|
157
|
+
def size
|
|
158
|
+
codeBin_getSize(@ptr)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def start_address
|
|
162
|
+
codeBin_getStartAddress(@ptr)
|
|
163
|
+
end
|
|
164
|
+
alias_method :start_addr, :start_address
|
|
165
|
+
|
|
166
|
+
def end_address
|
|
167
|
+
start_address + size
|
|
168
|
+
end
|
|
169
|
+
alias_method :end_addr, :end_address
|
|
170
|
+
|
|
171
|
+
def bounds
|
|
172
|
+
start_addr..end_addr
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def read(range = bounds, step = 4)
|
|
176
|
+
raise ArgumentError, 'step must be 1, 2, 4, or 8 (bytes)' unless [1,2,4,8].include? step
|
|
177
|
+
raise ArgumentError, 'range must be a Range' unless range.is_a? Range
|
|
178
|
+
|
|
179
|
+
clamped = Range.new([range.begin || start_addr, start_addr].max, [range.end || end_addr, end_addr].min)
|
|
180
|
+
|
|
181
|
+
clamped.step(step).map { |addr| [send(:"read#{step * 8}", addr), addr] }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def each_word(range = bounds)
|
|
185
|
+
read(range).each do |word, addr|
|
|
186
|
+
yield word, addr
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def each_dword(range = bounds)
|
|
191
|
+
read(range,8).each do |dword, addr|
|
|
192
|
+
yield dword, addr
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def each_hword(range = bounds)
|
|
197
|
+
read(range,2).each do |hword, addr|
|
|
198
|
+
yield hword, addr
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def each_byte(range = bounds)
|
|
203
|
+
read(range,1).each do |byte, addr|
|
|
204
|
+
yield byte, addr
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def each_char(range = bounds)
|
|
209
|
+
each_byte(range) do |char, addr|
|
|
210
|
+
yield char.chr, addr
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def read_cstring(start_addr)
|
|
215
|
+
codeBin_readCString(@ptr, start_addr)
|
|
216
|
+
end
|
|
217
|
+
alias_method :read_cstr, :read_cstring
|
|
218
|
+
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class ArmBin < CodeBin
|
|
222
|
+
include NitroBind
|
|
223
|
+
|
|
224
|
+
attr_reader :module_params
|
|
225
|
+
|
|
226
|
+
class ModuleParams < FFI::Struct
|
|
227
|
+
layout :autoload_list_start, :uint32,
|
|
228
|
+
:autoload_list_end, :uint32,
|
|
229
|
+
:autoload_start, :uint32,
|
|
230
|
+
:static_bss_start, :uint32,
|
|
231
|
+
:static_bss_end, :uint32,
|
|
232
|
+
:comp_static_end, :uint32,
|
|
233
|
+
:sdk_version_id, :uint32,
|
|
234
|
+
:nitro_code_be, :uint32,
|
|
235
|
+
:nitro_code_le, :uint32
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def initialize(args = {})
|
|
239
|
+
if args.has_key? :file_path
|
|
240
|
+
@ptr = FFI::AutoPointer.new(armBin_alloc, method(:armBin_release))
|
|
241
|
+
if not File.exist? args[:file_path]
|
|
242
|
+
puts "Error: #{args[:file_path]} does not exist"
|
|
243
|
+
raise 'ArmBin initialization failed'
|
|
244
|
+
end
|
|
245
|
+
armBin_load(@ptr, args[:file_path], args[:entry_addr], args[:ram_addr], args[:auto_load_hook_offset],
|
|
246
|
+
args[:is_arm9] || true)
|
|
247
|
+
|
|
248
|
+
elsif args.has_key?(:ptr) && args[:ptr].is_a?(FFI::AutoPointer)
|
|
249
|
+
@ptr = args[:ptr]
|
|
250
|
+
|
|
251
|
+
else
|
|
252
|
+
raise ArgumentError, 'ArmBin must be initialized with a file or a pointer'
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
@module_params = ModuleParams.new(armBin_getModuleParams(@ptr))
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def entry_point_address
|
|
259
|
+
armBin_getEntryPointAddress(@ptr)
|
|
260
|
+
end
|
|
261
|
+
alias_method :entry_addr, :entry_point_address
|
|
262
|
+
alias_method :entry_point_addr, :entry_point_address
|
|
263
|
+
|
|
264
|
+
def sane_address?(addr)
|
|
265
|
+
armBin_sanityCheckAddress(@ptr, addr)
|
|
266
|
+
end
|
|
267
|
+
alias_method :sane_addr?, :sane_address?
|
|
268
|
+
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
class OverlayBin < CodeBin
|
|
272
|
+
include NitroBind
|
|
273
|
+
|
|
274
|
+
attr_reader :id
|
|
275
|
+
|
|
276
|
+
def initialize(id, args = {})
|
|
277
|
+
@id = id
|
|
278
|
+
if [:file_path, :ram_addr, :is_compressed].all? { |k| args.key?(k) }
|
|
279
|
+
@ptr = FFI::AutoPointer.new(overlayBin_alloc, method(:overlayBin_release))
|
|
280
|
+
if !File.exist? args[:file_path]
|
|
281
|
+
puts "Error: #{args[:file_path]} does not exist"
|
|
282
|
+
raise "OverlayBin initialization failed"
|
|
283
|
+
end
|
|
284
|
+
overlayBin_load(@ptr, args[:file_path], args[:ram_addr], args[:is_compressed], @id)
|
|
285
|
+
|
|
286
|
+
elsif args.has_key?(:ptr) && args[:ptr].is_a?(FFI::AutoPointer)
|
|
287
|
+
@ptr = args[:ptr]
|
|
288
|
+
|
|
289
|
+
else
|
|
290
|
+
raise ArgumentError, 'OverlayBin must be initialized with a file or a pointer'
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
class HeaderBin
|
|
297
|
+
include NitroBind
|
|
298
|
+
|
|
299
|
+
def initialize(arg)
|
|
300
|
+
if arg.is_a? String
|
|
301
|
+
@ptr = FFI::AutoPointer.new(headerBin_alloc, method(:headerBin_release))
|
|
302
|
+
if not File.exist? arg
|
|
303
|
+
puts "Error: #{arg} does not exist"
|
|
304
|
+
raise "HeaderBin initialization failed"
|
|
305
|
+
end
|
|
306
|
+
headerBin_load(@ptr, arg)
|
|
307
|
+
elsif arg.is_a? FFI::Pointer
|
|
308
|
+
@ptr = arg
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def game_title
|
|
313
|
+
headerBin_getGameTitle(@ptr)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def game_code
|
|
317
|
+
headerBin_getGameCode(@ptr)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def maker_code
|
|
321
|
+
headerBin_getMakerCode(@ptr)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def arm9_auto_load_hook_offset
|
|
325
|
+
headerBin_getArm9AutoLoadHookOffset(@ptr)
|
|
326
|
+
end
|
|
327
|
+
alias_method :arm9_auto_load_hook_ofs, :arm9_auto_load_hook_offset
|
|
328
|
+
|
|
329
|
+
def arm7_auto_load_hook_offset
|
|
330
|
+
headerBin_getArm7AutoLoadHookOffset(@ptr)
|
|
331
|
+
end
|
|
332
|
+
alias_method :arm7_auto_load_hook_ofs, :arm7_auto_load_hook_offset
|
|
333
|
+
|
|
334
|
+
def arm9_entry_address
|
|
335
|
+
headerBin_getArm9EntryAddress(@ptr)
|
|
336
|
+
end
|
|
337
|
+
alias_method :arm9_entry_addr, :arm9_entry_address
|
|
338
|
+
|
|
339
|
+
def arm7_entry_address
|
|
340
|
+
headerBin_getArm7EntryAddress(@ptr)
|
|
341
|
+
end
|
|
342
|
+
alias_method :arm7_entry_addr, :arm7_entry_address
|
|
343
|
+
|
|
344
|
+
def arm9_ram_address
|
|
345
|
+
headerBin_getArm9RamAddress(@ptr)
|
|
346
|
+
end
|
|
347
|
+
alias_method :arm9_ram_addr, :arm9_ram_address
|
|
348
|
+
|
|
349
|
+
def arm7_ram_address
|
|
350
|
+
headerBin_getArm7RamAddress(@ptr)
|
|
351
|
+
end
|
|
352
|
+
alias_method :arm7_ram_addr, :arm7_ram_address
|
|
353
|
+
|
|
354
|
+
def arm9_ovt_size
|
|
355
|
+
headerBin_getArm9OvTSize(@ptr)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
class Rom
|
|
361
|
+
include NitroBind
|
|
362
|
+
|
|
363
|
+
attr_reader :header, :arm9, :arm7, :overlays, :overlay_count, :overlay_table
|
|
364
|
+
|
|
365
|
+
alias_method :ov_count, :overlay_count
|
|
366
|
+
alias_method :ov_table, :overlay_table
|
|
367
|
+
alias_method :ovt, :overlay_table
|
|
368
|
+
|
|
369
|
+
def initialize(file_path)
|
|
370
|
+
@ptr = FFI::AutoPointer.new(nitroRom_alloc, method(:nitroRom_release))
|
|
371
|
+
|
|
372
|
+
# Check whether file exists here because if C++ throws an exception we get a segfault
|
|
373
|
+
if !File.exist?(file_path)
|
|
374
|
+
puts "Error: #{file_path} does not exist"
|
|
375
|
+
raise "Rom initialization failed"
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
nitroRom_load(@ptr, file_path)
|
|
379
|
+
@header = HeaderBin.new(nitroRom_getHeader(@ptr))
|
|
380
|
+
@arm9 = ArmBin.new(ptr: FFI::AutoPointer.new(nitroRom_loadArm9(@ptr), method(:armBin_release)))
|
|
381
|
+
@arm7 = ArmBin.new(ptr: FFI::AutoPointer.new(nitroRom_loadArm7(@ptr), method(:armBin_release)))
|
|
382
|
+
@overlay_count = nitroRom_getOverlayCount(@ptr)
|
|
383
|
+
@overlays = Array.new(@overlay_count)
|
|
384
|
+
@overlay_table = OvtBin.new(ptr: nitroRom_getArm9OvT(@ptr), size: @header.arm9_ovt_size)
|
|
385
|
+
define_ov_accessors
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def size
|
|
389
|
+
nitroRom_getSize(@ptr)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def get_file(id)
|
|
393
|
+
nitroRom_getFile(id)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def get_file_size(id)
|
|
397
|
+
nitroRom_getFileSize(id)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def nitro_sdk_version
|
|
401
|
+
@arm9.module_params[:sdk_version_id]
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def load_overlay(id)
|
|
405
|
+
raise IndexError if id > @overlay_count-1
|
|
406
|
+
ov_ptr = nitroRom_loadOverlay(@ptr, id)
|
|
407
|
+
raise "Failed to load overlay #{id}." if !ov_ptr
|
|
408
|
+
@overlays[id] = OverlayBin.new(id, ptr: FFI::AutoPointer.new(ov_ptr, method(:overlayBin_release)))
|
|
409
|
+
end
|
|
410
|
+
alias_method :load_ov, :load_overlay
|
|
411
|
+
|
|
412
|
+
def get_overlay(id)
|
|
413
|
+
raise IndexError if id > @overlay_count-1
|
|
414
|
+
load_overlay(id) if @overlays[id].nil?
|
|
415
|
+
@overlays[id]
|
|
416
|
+
end
|
|
417
|
+
alias_method :get_ov, :get_overlay
|
|
418
|
+
|
|
419
|
+
def each_overlay
|
|
420
|
+
@overlay_count.times do |i|
|
|
421
|
+
yield get_overlay(i), i
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
alias_method :each_ov, :each_overlay
|
|
425
|
+
|
|
426
|
+
private
|
|
427
|
+
def define_ov_accessors
|
|
428
|
+
(0..@overlay_count-1).each do |id|
|
|
429
|
+
self.class.define_method(:"overlay#{id}") do
|
|
430
|
+
get_overlay(id)
|
|
431
|
+
end
|
|
432
|
+
self.class.define_method(:"ov#{id}") do
|
|
433
|
+
get_overlay(id)
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
end
|
data/lib/unarm/unarm.dll
ADDED
|
Binary file
|