2016-01-12 15:54:42 -08:00
|
|
|
require "lib.setup"
|
2015-12-16 18:04:57 -08:00
|
|
|
require "boilerplate"
|
2016-01-13 09:21:24 -08:00
|
|
|
local addrs = require "addrs"
|
2015-12-22 04:56:40 -08:00
|
|
|
require "messages"
|
2016-01-12 15:54:42 -08:00
|
|
|
local assemble = require "lips"
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 17:05:31 -08:00
|
|
|
local injection_points = {
|
|
|
|
['M US10'] = {
|
|
|
|
inject_addr = 0x780000,
|
|
|
|
inject_maxlen = 0x5A800,
|
2016-01-12 06:59:29 -08:00
|
|
|
-- main rendering loop:
|
|
|
|
-- the only other function (that literally just loads and returns)
|
|
|
|
--ow_addr = 0x1749D0,
|
|
|
|
--ow_before = 0x0C05CEC6,
|
|
|
|
-- just after the JALR in the actor(?) rendering loop
|
|
|
|
-- problem is we do processing AFTER it's all rendered
|
|
|
|
-- this causes an extra frame of delay for ingame changes
|
|
|
|
ow_addr = 0x1737C4,
|
|
|
|
ow_before = 0x0C05CD50,
|
2015-12-18 17:05:31 -08:00
|
|
|
},
|
2015-12-22 16:04:06 -08:00
|
|
|
['M JP10'] = {
|
|
|
|
inject_addr = 0x780000,
|
|
|
|
inject_maxlen = 0x5A800,
|
|
|
|
ow_addr = 0x1701A8,
|
|
|
|
ow_before = 0x0C05BCD4,
|
|
|
|
},
|
2015-12-18 17:05:31 -08:00
|
|
|
['O US10'] = {
|
|
|
|
inject_addr = 0x3BC000,
|
|
|
|
inject_maxlen = 0x1E800,
|
2016-01-12 08:52:51 -08:00
|
|
|
--ow_addr = 0x0A19C8,
|
|
|
|
--ow_before = 0x0C0283EE,
|
|
|
|
ow_addr = 0x0A0C3C,
|
|
|
|
ow_before = 0x0C028231,
|
2015-12-18 17:05:31 -08:00
|
|
|
},
|
2015-12-26 19:06:50 -08:00
|
|
|
['O EUDB MQ'] = {
|
|
|
|
inject_addr = 0x700000,
|
|
|
|
inject_maxlen = 0x100000,
|
2016-01-11 16:42:40 -08:00
|
|
|
-- main rendering loop:
|
|
|
|
-- the only other function (that literally just loads and returns)
|
|
|
|
--ow_addr = 0x0C6940,
|
|
|
|
--ow_before = 0x0C03151F,
|
|
|
|
-- first (high-level) function after iterating over actors
|
|
|
|
ow_addr = 0x0C62B8,
|
|
|
|
ow_before = 0x0C031AB1,
|
2015-12-26 19:06:50 -08:00
|
|
|
},
|
2015-12-18 17:05:31 -08:00
|
|
|
}
|
2016-01-10 11:58:16 -08:00
|
|
|
injection_points['O JP10'] = injection_points['O US10']
|
2015-12-18 17:05:31 -08:00
|
|
|
|
2016-01-10 12:37:35 -08:00
|
|
|
local hook = [[
|
|
|
|
[hooked]: 0x%08X
|
|
|
|
// note: this will fail when the hooked function takes args on stack
|
2016-01-10 11:58:16 -08:00
|
|
|
sw ra, -4(sp)
|
|
|
|
sw a0, 0(sp)
|
|
|
|
sw a1, 4(sp)
|
|
|
|
sw a2, 8(sp)
|
|
|
|
sw a3, 12(sp)
|
2015-12-22 17:33:38 -08:00
|
|
|
bal start
|
2016-03-24 23:37:37 -07:00
|
|
|
subi sp, sp, 24
|
|
|
|
lw ra, 20(sp)
|
|
|
|
lw a0, 24(sp)
|
|
|
|
lw a1, 28(sp)
|
|
|
|
lw a2, 32(sp)
|
|
|
|
lw a3, 36(sp)
|
2016-01-10 12:37:35 -08:00
|
|
|
j @hooked
|
2016-03-24 23:37:37 -07:00
|
|
|
addi sp, sp, 24
|
2015-12-18 17:05:31 -08:00
|
|
|
start:
|
|
|
|
]]
|
|
|
|
|
2016-01-13 09:21:24 -08:00
|
|
|
local function inject(fn)
|
2015-12-18 15:18:08 -08:00
|
|
|
local asm_dir = bizstring and 'inject/' or './mm/Lua/inject/'
|
|
|
|
local asm_path = asm_dir..fn
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 17:05:31 -08:00
|
|
|
local point = injection_points[version]
|
|
|
|
if point == nil then
|
2015-12-18 15:18:08 -08:00
|
|
|
print("Sorry, inject.lua is unimplemented for your game version.")
|
|
|
|
return
|
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 17:05:31 -08:00
|
|
|
-- seemingly unused region of memory
|
|
|
|
local inject_addr = point.inject_addr
|
|
|
|
-- how much room we have to work with
|
|
|
|
local inject_maxlen = point.inject_maxlen
|
|
|
|
-- the jal instruction to overwrite with our hook
|
|
|
|
local ow_addr = point.ow_addr
|
|
|
|
-- what its value is normally supposed to be
|
|
|
|
local ow_before = point.ow_before
|
|
|
|
|
2016-01-10 12:37:35 -08:00
|
|
|
local inject_end = inject_addr + inject_maxlen
|
|
|
|
|
2015-12-18 15:18:08 -08:00
|
|
|
-- encode our jal instruction
|
|
|
|
local ow_after = 0x0C000000 + math.floor(inject_addr/4)
|
|
|
|
if R4(ow_addr) ~= ow_before and R4(ow_addr) ~= ow_after then
|
|
|
|
print("Can't inject -- game code is different!")
|
|
|
|
return
|
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 15:18:08 -08:00
|
|
|
-- decode the original address
|
|
|
|
local ow_before_addr = (ow_before % 0x4000000)*4
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2016-01-10 12:37:35 -08:00
|
|
|
-- set up a hook to handle calling our function and the original
|
|
|
|
local hook = hook:format(ow_before_addr)
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-22 04:56:40 -08:00
|
|
|
local inject_bytes = {}
|
2016-01-10 12:37:35 -08:00
|
|
|
local size = 0
|
|
|
|
local cons_pos = inject_addr
|
2015-12-22 06:37:40 -08:00
|
|
|
local function write(pos, line)
|
2016-01-10 12:37:35 -08:00
|
|
|
assert(#line == 2, "that ain't const")
|
2015-12-22 04:56:40 -08:00
|
|
|
dprint(("%08X"):format(pos), line)
|
|
|
|
pos = pos % 0x80000000
|
2016-01-10 12:37:35 -08:00
|
|
|
size = size + 1
|
2016-01-13 11:20:28 -08:00
|
|
|
-- FIXME: doesn't detect .skip/.space directives
|
2016-01-10 12:37:35 -08:00
|
|
|
if pos > cons_pos and (pos < inject_end or cons_pos == pos - 1) then
|
|
|
|
cons_pos = pos
|
|
|
|
end
|
2015-12-22 04:56:40 -08:00
|
|
|
inject_bytes[pos] = tonumber(line, 16)
|
2015-12-18 15:18:08 -08:00
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 15:18:08 -08:00
|
|
|
-- offset assembly labels so they work properly, and assemble!
|
|
|
|
local true_offset = 0x80000000 + inject_addr
|
2016-01-10 12:37:35 -08:00
|
|
|
assemble(hook, write, {unsafe=true, offset=true_offset})
|
|
|
|
assemble(asm_path, write, {unsafe=true, offset=true_offset + size})
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2016-01-10 12:37:35 -08:00
|
|
|
print_deferred()
|
|
|
|
printf("size: %i words", size/4)
|
|
|
|
if cons_pos >= inject_end then
|
2015-12-18 15:18:08 -08:00
|
|
|
print("Assembly too large!")
|
2016-01-10 12:37:35 -08:00
|
|
|
print("The game will probably crash.")
|
2015-12-18 15:18:08 -08:00
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-22 04:56:40 -08:00
|
|
|
for pos, val in pairs(inject_bytes) do
|
|
|
|
W1(pos, val)
|
2015-12-18 15:18:08 -08:00
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 15:18:08 -08:00
|
|
|
-- finally, write our new jump over the original
|
|
|
|
printf('%08X: %08X', ow_addr, ow_after)
|
|
|
|
W4(ow_addr, ow_after)
|
2015-12-16 18:04:57 -08:00
|
|
|
|
2015-12-18 15:18:08 -08:00
|
|
|
-- force code cache to be reloaded
|
|
|
|
if bizstring then
|
|
|
|
local ss_fn = 'inject temp.State'
|
|
|
|
savestate.save(ss_fn)
|
|
|
|
savestate.load(ss_fn)
|
|
|
|
else
|
|
|
|
m64p.reloadCode()
|
|
|
|
end
|
2015-12-16 18:04:57 -08:00
|
|
|
end
|
|
|
|
|
2016-01-10 12:54:16 -08:00
|
|
|
local asms = {
|
|
|
|
['O US10'] = 'spawn oot.asm',
|
|
|
|
['O JP10'] = 'spawn oot.asm',
|
2016-04-07 07:02:27 -07:00
|
|
|
['O EUDB MQ'] = 'widescreen.asm',
|
2016-01-10 12:54:16 -08:00
|
|
|
|
2016-01-18 20:36:51 -08:00
|
|
|
['M US10'] = 'beta.asm',
|
2016-01-10 12:54:16 -08:00
|
|
|
['M JP10'] = 'spawn mm early.asm',
|
|
|
|
}
|
|
|
|
|
|
|
|
local asm = asms[version]
|
|
|
|
if asm then
|
|
|
|
inject(asm)
|
2015-12-18 17:05:31 -08:00
|
|
|
else
|
2016-01-10 12:54:16 -08:00
|
|
|
print('no appropriate assembly found for this game')
|
2015-12-18 17:05:31 -08:00
|
|
|
end
|