fantastic 4 DTC7 script.
-- HOWTO:
-- For the level you want to highlight specific IDs
-- Create a line like this:
-- interesting_blocks[<level>] = { <block>[, <block>, ...] }
-- e.g.
-- interesting_blocks[2] = { 0x79, 0x1b }
local interesting_blocks = {}
interesting_blocks[1] = { 0x47 }
interesting_blocks[2] = { 0x79, 0x1b }
interesting_blocks[3] = { 0x47 }
interesting_blocks[4] = { 0x79 }
interesting_blocks[5] = { 0x38 }
interesting_blocks[6] = { 0x38 }
local level_section_table = {}
-- address when mapped, number of sections
-- everything is in bank 4
level_section_table[01] = { 0xbc36, 2 }
level_section_table[02] = { 0xbc56, 2 }
level_section_table[03] = { 0xbc76, 3 }
level_section_table[04] = { 0xbca6, 3 }
level_section_table[05] = { 0xbcc6, 2 }
level_section_table[06] = { 0xbce6, 5 }
level_section_table[07] = { 0xbd16, 2 }
level_section_table[08] = { 0xbd36, 2 }
level_section_table[09] = { 0xbd56, 8 }
level_section_table[10] = { 0xbdb6, 3 }
level_section_table[11] = { 0xbdd6, 2 }
level_section_table[12] = { 0xbdf6, 23 }
-- BizHawk only!
local EAL = {}
do
local mem = nil
local ROM = nil
end
-- EAL Memory namespace functions
EAL.mem = {}
do
function EAL.mem.read_u8(addr)
return memory.read_u8(addr)
end
function EAL.mem.read_s8(addr)
return memory.read_s8(addr)
end
function EAL.mem.read_u16_be(addr)
return memory.read_u16_be(addr)
end
function EAL.mem.read_s16_be(addr)
return memory.read_s16_be(addr)
end
function EAL.mem.read_s16_be_scatter(hi, lo)
local val = memory.read_u8(hi)
val = bit.lshift(val, 8)
val = bit.bor(val, memory.read_u8(lo))
if val > 0x7FFF then
return val - 0x10000
else
return val
end
end
function EAL.mem.read_u16_le(addr)
return memory.read_u16_le(addr)
end
end
EAL.ROM = {}
do
-- `addr` is the address as seen from the system bus
local function get_addr_in_bank(bank, addr)
local bank_addr = addr - 0x8000
if addr > 0xC000 then -- high region
bank_addr = bank_addr - 0x4000
end
return bank_addr
end
-- `addr` is the address as seen from the system bus
local function get_offset_in_prg(bank, addr)
local bank_addr = get_addr_in_bank(bank, addr)
local offset = (bank * 0x4000) + bank_addr
return offset
end
-- Usage note: `addr` is the address as seen from the system bus
function EAL.ROM.read_u8(bank, addr)
local offset = get_offset_in_prg(bank, addr)
return memory.read_u8(offset, "PRG ROM")
end
-- Usage note: `addr` is the address as seen from the system bus
function EAL.ROM.read_u16_be(bank, addr)
local offset = get_offset_in_prg(bank, addr)
return memory.read_u16_be(offset, "PRG ROM")
end
function EAL.ROM.read_u16_le(bank, addr)
local offset = get_offset_in_prg(bank, addr)
return memory.read_u16_le(offset, "PRG ROM")
end
end
function get_screen_x_coordinate()
return EAL.mem.read_u16_le(0x00CC)
end
function get_position()
local onscreen_x = EAL.mem.read_u8(0x0091)
local x = onscreen_x + get_screen_x_coordinate()
local subx = EAL.mem.read_u8(0x0092)
local y = EAL.mem.read_u8(0x0095)
return x, subx, y
end
function get_level_info()
local level = EAL.mem.read_u8(0x0051)
local section = EAL.mem.read_u8(0x0052)
return level, section
end
function get_level_length(level)
local bank = 4
local highest = 0
for i=0,(level_section_table[level][2] - 1) do
local max = EAL.ROM.read_u8(bank, level_section_table[level][1] + (i * 16) + 5)
if max > highest then
highest = max
end
end
return highest
end
function get_block_id_addr(level)
local bank = 7
local lvl_bank = EAL.ROM.read_u8(bank, 0xC5AD + level)
local offset_table_addr = EAL.ROM.read_u16_le(bank, 0xD102 + (level * 2)) + 3
local data_table_addr = EAL.ROM.read_u16_le(bank, 0xCD9F + (level * 2))
return lvl_bank, offset_table_addr, data_table_addr
end
local canvas = nil
local canvas_width = 0
local canvas_height = 0
function do_block_id_canvas()
canvas = nil -- Hoping for the garbage collection to not leak too much
local level, section = get_level_info()
if level < 1 or level > 12 then
return
end
local level_length = get_level_length(level)
local screenx = get_screen_x_coordinate()
local bank, offsets_addr, data_addr = get_block_id_addr(level)
if interesting_blocks[level] == nil then
return
end
canvas_width = level_length * 256 / 4
canvas_height = (13 * 16 / 4) + 10 -- allocate 10 pixels for the "progress bar"
canvas = gui.createcanvas(canvas_width, canvas_height) -- 4 pixels for each block
canvas.SetDefaultForegroundColor("black")
canvas.SetDefaultBackgroundColor("white")
canvas.SetTitle("Level ID Map")
-- offsets are multiplied by 208
for x=0,level_length do
local offset = EAL.ROM.read_u8(bank, offsets_addr + x) * 208 --13*16
for i=0,15 do -- 1 -> 16
for y=0,12 do -- 1 -> 13
local id = EAL.ROM.read_u8(bank, data_addr + offset + i + (y * 16))
for k,v in ipairs(interesting_blocks[level]) do
if id == v then
local dx = ((x * 64) + (i * 4))
local dy = (y * 4)
canvas.DrawRectangle(dx, dy, 4, 4, "red", "red")
end
end
end
end
end
end
local previous_x = 0
function do_level_progress()
if canvas == nil then -- if we end up called before a level
return
end
local level, section = get_level_info()
if level < 1 or level > 12 then
return
end
local x, subx, y = get_position()
x = math.floor(x / 16)
-- Clearing the canvas is expensive, only draw when there has been a significant movement
if x == previous_x then
return
end
-- draw one black and one white bar
canvas.DrawRectangle(0, canvas_height-10, x*4, 10, "black", "black")
canvas.DrawRectangle(x*4, canvas_height-10, canvas_width-(x*4), 10, "white", "white")
canvas.Refresh()
previous_x = x
end
local id0 = nil
local id1 = nil
local id2 = nil
local events = {}
function do_exit()
--event.unregisterbyid(id0)
--event.unregisterbyid(id1)
--event.unregisterbyid(id2)
end
id0 = event.onmemorywrite(do_block_id_canvas, 0x0051)
id1 = event.onmemorywrite(do_level_progress, 0x0091)
id2 = event.onexit(do_exit)
do_block_id_canvas() -- Just in case script is started mid-level
do_level_progress() -- Just in case script is started mid-level
while true do
emu.frameadvance()
end