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 CanvasSpawns()
--*****************************************************************************
--[[Spawn list offsets:
+00,2 - X position
+02,2 - Y position
+04,2 - Does it exist (?)
+06,2 - Enemy to spawn (?)
+08,2 -
+0A,2 -
+0C,4 - ? What, there's some apparently random numbers here...
+10,2 -
]]--
if Stage > 16 then return end --Just in case
local ptr= R4u(0x3CCF68+4*Stage,"ROM") --Getting stage pointer
ptr= R4u(FetchAddrDomainGBA(ptr+4*Segment)) --Getting Segment pointer of stage
local Xpos= R2s(FetchAddrDomainGBA(ptr))
local SpIndex= 0
while Xpos ~= -1 do --For whatever reason, this offset is double-burdened as a magic end marker
if R2s(FetchAddrDomainGBA(ptr+4)) ~= 0 then --Apparently it's 1 if it exists
local Ypos= R2s(FetchAddrDomainGBA(ptr+2))
local clr= 0xFFFFFFFF --White
if R1u(0x7C24+SpIndex,"IWRAM") ~= 0 then clr= 0xFFC08080 end --Reddish
Xpos= Xpos - CamX + OffsetX
Ypos= Ypos - CamY + OffsetY
SafishRect(Xpos-3,Ypos-3,2,2,clr)
SafishRect(Xpos-3,Ypos+1,2,2,clr)
SafishRect(Xpos+1,Ypos-3,2,2,clr)
SafishRect(Xpos+1,Ypos+1,2,2,clr)
end
SpIndex= SpIndex+1; if SpIndex >= 32 then return end --Something's wrong. Don't crash script.
ptr= ptr+18
Xpos= R2s(FetchAddrDomainGBA(ptr))
end
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,"IWRAM")
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 bClr,fClr= MakeEnemyColor(addr)
if IsItem then bClr,fClr= 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()
CanvasSpawns()
CanvasObjects()
cnv.Refresh()
end
--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
HandleCanvas()
-- Fn()
-- DrawRescaledBorder()
-- EnemyHitboxes()
-- BulletHitboxes()
-- PlayerHitbox()
emu.frameadvance()
end