local R4u= memory.read_u32_le
local R4s= memory.read_s32_le
local R2u= memory.read_u16_le
local R2s= memory.read_s16_le
local R1u= memory.read_u8
local ROMmin,ROMmax= 0x08000000, 0x08000000+memory.getmemorydomainsize("ROM")
local cnv= gui.createcanvas(480,320)
--for k,v in pairs(cnv) do print(k,type(v)) end
--*****************************************************************************
local function FetchAddrDomainGBA(a)
--*****************************************************************************
--I am furious at the design away from the bus. It used to exist! Why remove?
--I do not want to code in removing offsets to pointers every time I read one.
--This function was made because I insist on full pointers. Has only what I know.
if (a >= 0x02000000) and (a < (0x02000000+memory.getmemorydomainsize("EWRAM"))) then
return a-0x02000000, "EWRAM"
elseif (a >= 0x03000000) and (a < (0x03000000+memory.getmemorydomainsize("IWRAM"))) then
return a-0x03000000, "IWRAM"
elseif (a >= 0x08000000) and (a < (0x08000000+memory.getmemorydomainsize("ROM"))) then
return a-0x08000000, "ROM"
else
error(string.format("Unknown address %08X", a),2)
end
end
local Maps= {}
--*****************************************************************************
local function LoadMaps()
--*****************************************************************************
--This function tracks down every stage, every segment in each stage, every
--block in each segment, and every tile in each block. Then it stitches
--together the tiles as it would fit in the block.
for Stage= 0, 16 do
local StageMaps= {} --Construct new table
local BlockSetPtr= R4u(0x3CD86C + 4*Stage,"ROM")
local SegmentBase= R4u(0x3CD8B0 + 4*Stage,"ROM")
local Segment= 0
while true do
local SegmentPtr= R4u(FetchAddrDomainGBA(SegmentBase + Segment*4))
if (SegmentPtr < ROMmin) or (SegmentPtr >= ROMmax) then break end
local SegX, SegY= R2s(FetchAddrDomainGBA(SegmentPtr)), R2s(FetchAddrDomainGBA(SegmentPtr+2))
SegmentPtr= SegmentPtr+4
local SegmentMap= {}
for y= 0, SegY-1 do
for x= 0, SegX-1 do
local BlockIndex= R2u(FetchAddrDomainGBA(SegmentPtr + (y*SegX + x)*2))
local BlockPtr= R4u(FetchAddrDomainGBA(BlockSetPtr + 4*BlockIndex))
for yy= 0, 31 do
for xx= 0, 31 do
local i= xx + x*32 + yy*SegX*32 + y*SegX*1024
SegmentMap[i]= R1u(FetchAddrDomainGBA(BlockPtr + 2 + xx + 32*yy))
end
end
end
end
StageMaps[Segment]= SegmentMap
Segment= Segment+1
end
Maps[Stage]= StageMaps --Stash what we've got!
print("Loaded Stage " .. Stage)
end
end
LoadMaps()
local Stage, Segment, SegX, SegY, CamX, CamY
--*****************************************************************************
local function UpdateStats()
--*****************************************************************************
Stage= R4u(0x152C) --Which stage we're playing
Segment= R4s(0x1530) --What part in that stage
SegX,SegY= R4s(0x153C),R4s(0x1540) --Size of that part
CamX,CamY= R4s(0x14F0),R4s(0x14F4) --Camera location
end
--*****************************************************************************
local function SafishRect(x,y,w,h,bc,fc)
--*****************************************************************************
if x < 0 then return end
if y < 0 then return end
if x+w > 479 then return end
if y+h > 319 then return end
cnv.DrawRectangle(x,y,w,h,bc,fc)
end
local TileClrs= {
[0]=0xFF000080,0xFF808080,0xFF801000,0xFF000000, --Air,Floor,Spike,Solid
0xFF000000,0xFF008080,0xFF808040,0xFF808040, --nil,Wj,Sslope /
0xFF808040,0xFF808040,0xFF808000,0xFF808000, --Sslope \,magnet
0xFF808040,0xFF808040,0xFF808040,0xFF808040, --Gslope /
0xFF808040,0xFF808040,0xFF808040,0xFF808040, --Gslope \
0xFF006030,0xFF008000,0xFF006030,0xFF800080, --Ladder, sPlatform
0xFF800080,0xFF800080,0xFF800080,0xFF800080, --sPlatform, Capsule
0xFF800080,0xFF000000,0xFF000000,0xFF408080, --Capsule,nil,nil,AS end
[0x60]=0xFF800080, [0x61]=0xFF800080, [0x62]=0xFF800080, [0x63]=0xFF800080,
[0x64]=0xFF800080, [0x65]=0xFF800080, [0x66]=0xFF800080, [0x67]=0xFF800080,
[0x68]=0xFF800080, [0x69]=0xFF800080, [0x6A]=0xFF800080, [0x6B]=0xFF800080,
[0x6C]=0xFF800080, [0x6D]=0xFF800080, [0x6E]=0xFF800080, [0x6F]=0xFF800080,
[0x70]=0xFF800080, [0x71]=0xFF800080, [0x72]=0xFF800080, [0x73]=0xFF800080,
[0x74]=0xFF800080, [0x75]=0xFF800080, [0x76]=0xFF800080
}
local OffsetX,OffsetY= 120,80
--*****************************************************************************
local function CanvasTerrain()
--*****************************************************************************
if Stage > 16 then return end
local Area= Maps[Stage][Segment]; if not Area then return end
local Left= math.max(
math.floor((CamX-OffsetX)/8), --Left edge
0 --Segment left
)
local Top= math.max(
math.floor((CamY-OffsetY)/8), --Top edge
0 --Segment top
)
local Right= math.min(
math.floor((CamX-OffsetX+480)/8), --Right edge
SegX*32 - 1 --Segment right
)
local Bottom= math.min(
math.floor((CamY-OffsetY+320)/8), --Bottom edge
SegY*32 - 1 --Segment bottom
)
for Y= Top, Bottom do
local DispY= Y*8 + OffsetY - CamY
for X= Left, Right do
local DispX= X*8 + OffsetX - CamX
local tile= Area[X + Y*SegX*32]
cnv.DrawRectangle(
DispX,DispY,7,7,TileClrs[tile],TileClrs[tile]
)
end
end
end
--*****************************************************************************
local function CanvasBorder()
--*****************************************************************************
cnv.DrawRectangle(OffsetX,OffsetY,239,159,0x40FFFFFF)
end
--*****************************************************************************
local function CanvasPlayer()
--*****************************************************************************
local addr= 0x1DE4 + 0x74*1
local Spr= R2s(addr+0x08,"IWRAM")
local pX, pY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local BoxAddr= 0x087B64 + 0x10*Spr
local Left= R4u(BoxAddr+ 8,"ROM")
local Right= R4u(BoxAddr+12,"ROM")
local Up= R4u(BoxAddr+ 0,"ROM")
local Down= R4u(BoxAddr+ 4,"ROM")
SafishRect(pX+Left+OffsetX,pY+Up+OffsetY,Right,Down,0xFFFFFFFF,0x8000FFFF)
end
--*****************************************************************************
local function CanvasBullet()
--*****************************************************************************
local bID= R4u(0x15E4,"IWRAM")
for i= 11, 29 do --Player projectiles zone
local addr= 0x1DE4 + 0x74*i
local bSpr= R2s(addr+0x08,"IWRAM") --Sprite
if (bSpr ~= 0) and (bID < 9) then
local bX, bY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local HitBoxData
if bID == 0 then
HitBoxData= R4u(0x06EFDC + bSpr*4,"ROM")
--No error catch. Try not to run this with absurdly high Sprite ID.
else
local bPtr= R4u(0x07E36C + bID*4,"ROM")
HitBoxData= bPtr + 0x10*bSpr
end
local Left= R4u(FetchAddrDomainGBA(HitBoxData+ 8))
local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))
local Up= R4u(FetchAddrDomainGBA(HitBoxData+ 0))
local Down= R4u(FetchAddrDomainGBA(HitBoxData+ 4))
SafishRect(bX+Left+OffsetX,bY+Up+OffsetY,Right,Down,0xFF00FFFF,0x8000FF00)
end
end
end
local eFill= {
0x60FF0000,0x60FF4000,0x60FF8000,0x60FFC000,0x60FFFF00
}
--*****************************************************************************
local function MakeEnemyColor(addr)
--*****************************************************************************
--Colored based on HP.
local HP= R2s(addr+0x2C)
local border= 0xFFFFFF00
local fill= eFill[HP]
if HP <= 0 then border= 0xFFFF00FF; fill= 0x60FF00FF end
return border,fill or 0x6080FF00
end
--*****************************************************************************
local function CanvasEnemy()
--*****************************************************************************
if Stage > 16 then return end
local StagePtr= R4u(0x3CD5C4 + 4*Stage,"ROM")
for i= 30, 59 do --Enemies occupy these slots only
local addr= 0x1DE4 + 0x74*i
local eSpr, eID= R2s(addr+0x08,"IWRAM"), R2u(addr+0x0A,"IWRAM") --Sprite, ID
if (eSpr ~= 0) and (eID ~= 0) and (eID < 31) then
local eX, eY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local IsItem= (R2s(addr+0x3C,"IWRAM") == 2)
local EnemyPtr
if IsItem then
EnemyPtr= R4u(0x06885C + eID*4,"ROM")
else
EnemyPtr= R4u(FetchAddrDomainGBA(StagePtr+eID*4))
end
local HitBoxData= EnemyPtr + 0x10*eSpr
local Left= R4u(FetchAddrDomainGBA(HitBoxData+ 8))
local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))
local Up= R4u(FetchAddrDomainGBA(HitBoxData+ 0))
local Down= R4u(FetchAddrDomainGBA(HitBoxData+ 4))
if (Right ~= 0) and (Down ~= 0) then
local border,fill= MakeEnemyColor(addr)
if IsItem then border,fill= 0xFF00FF00, 0x600000FF end
SafishRect(eX+Left+OffsetX,eY+Up+OffsetY,Right,Down,bClr,fClr)
else
SafishRect(eX+OffsetX,eY+OffsetY,3,3,0xFFFF8000)
end
end
end
end
--*****************************************************************************
local function CanvasObjects()
--*****************************************************************************
CanvasPlayer()
CanvasBullet()
CanvasEnemy()
end
--*****************************************************************************
local function HandleCanvas()
--*****************************************************************************
cnv.Clear(0xFF404040)
UpdateStats()
CanvasTerrain()
CanvasBorder()
CanvasObjects()
cnv.Refresh()
end
--[[
local ScaleDiv= 2
local OffsetX= 0
local OffsetY= 0
--*****************************************************************************
local function Rescale(v)
--*****************************************************************************
ScaleDiv= v
OffsetX= (240 - math.floor(240/v))/2
OffsetY= (160 - math.floor(160/v))/2
end
Rescale(ScaleDiv)
--*****************************************************************************
local function DrawRescaledBorder()
--*****************************************************************************
if ScaleDiv <= 1 then return end
gui.drawRectangle(
OffsetX,
OffsetY,
math.floor(240/ScaleDiv),
math.floor(160/ScaleDiv),
0x80C0C0C0 --Yay, border color
)
end
--*****************************************************************************
local function DrawScaledHitbox(x1,y1,x2,y2,fc,bc)
--*****************************************************************************
if x1 == x2 then return end
x1= x1/ScaleDiv+OffsetX
y1= y1/ScaleDiv+OffsetY
x2= x2/ScaleDiv+OffsetX
y2= y2/ScaleDiv+OffsetY
gui.drawBox(x1,y1,x2,y2,fc,bc)
end
local dbox= DrawScaledHitbox
--*****************************************************************************
local function PlayerHitbox()
--*****************************************************************************
local addr= 0x1DE4 + 0x74*1
local Spr= R2s(addr+0x08,"IWRAM")
local pX, pY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local BoxAddr= 0x087B64 + 0x10*Spr
local Left= R4u(BoxAddr+ 8,"ROM")
local Right= R4u(BoxAddr+12,"ROM")+Left
local Up= R4u(BoxAddr+ 0,"ROM")
local Down= R4u(BoxAddr+ 4,"ROM")+Up
dbox(pX+Left,pY+Up,pX+Right,pY+Down)
end
--*****************************************************************************
local function BulletHitboxes()
--*****************************************************************************
--I'm hating these things.
for i= 10, 29 do --Player projectiles zone
local addr= 0x1DE4 + 0x74*i
local bSpr, bID= R2s(addr+0x08,"IWRAM"), R2u(0x15E4,"IWRAM") --Sprite, ID
if (bSpr ~= 0) and (bID < 9) then
local bX, bY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local HitBoxData
if bID == 0 then
HitBoxData= R4u(0x06EFDC + bSpr*4,"ROM")
--No error catch. Try not to run this with absurdly high Sprite ID.
else
local bPtr= R4u(0x07E36C + bID*4,"ROM")
HitBoxData= bPtr + 0x10*bSpr
end
local Left= R4u(FetchAddrDomainGBA(HitBoxData+ 8))
local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))+Left
local Up= R4u(FetchAddrDomainGBA(HitBoxData+ 0))
local Down= R4u(FetchAddrDomainGBA(HitBoxData+ 4))+Up
dbox(bX+Left,bY+Up,bX+Right,bY+Down)
end
end
end
--*****************************************************************************
local function EnemyHitboxes()
--*****************************************************************************
--Found a grail. Not sure if it's holy.
local Stage= R4u(0x152C,"IWRAM"); if Stage > 16 then return end
local StagePtr= R4u(0x3CD5C4 + 4*Stage,"ROM")
for i= 30, 59 do --Enemies occupy these slots only
local addr= 0x1DE4 + 0x74*i
local eSpr, eID= R2s(addr+0x08,"IWRAM"), R2u(addr+0x0A,"IWRAM") --Sprite, ID
if (eSpr ~= 0) and (eID ~= 0) and (eID < 31) then
local eX, eY= R4s(addr+0x00,"IWRAM"), R4s(addr+0x04,"IWRAM")
local EnemyPtr= R4u(FetchAddrDomainGBA(StagePtr+eID*4))
local HitBoxData= EnemyPtr + 0x10*eSpr
local Left= R4u(FetchAddrDomainGBA(HitBoxData+ 8))
local Right= R4u(FetchAddrDomainGBA(HitBoxData+12))+Left
local Up= R4u(FetchAddrDomainGBA(HitBoxData+ 0))
local Down= R4u(FetchAddrDomainGBA(HitBoxData+ 4))+Up
dbox(eX+Left,eY+Up,eX+Right,eY+Down)
end
end
end
]]--
--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
HandleCanvas()
-- Fn()
-- DrawRescaledBorder()
-- EnemyHitboxes()
-- BulletHitboxes()
-- PlayerHitbox()
emu.frameadvance()
end