memory.usememorydomain("IWRAM")
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 R1s= memory.read_s8
local R4x= function(a) return string.format("%X",R4u(a)) end
local R2x= function(a) return string.format("%X",R2u(a)) end
local RAMList2= { --Includes method and color.
-- Address Type Color
{a=0x14F0,m=R4s,c=0xFFFFFFFF}, --Camera X
{a=0x1EA4,m=R2s,c=0xFFFFFF00}, --Flame charge timer
{a=0x15F0,m=R4s,c=0xFFC0C0C0}, --Action lock
{a=0x1E58,m=R2s,c=0xFFFFFFFF}, --Player screen location X
{a=0x17A0,m=R4x,c=0xFFFFFFFF}, --Frame counter
{a=0x1530,m=R4s,c=0xFF00FFFF}, --Segment
{a=0x153C,m=R4s,c=0xFF0080FF}, --Segment width
{a=0x1540,m=R4s,c=0xFF0080FF}, --Segment height
{a=0x15E0,m=R4s,c=0xFFFFFFFF}, --Shot cooldown
{a=0x1608,m=R4s,c=0xFFFFFFFF}, --Charge counter
{a=0x1E6C,m=R2s,c=0xFF00FF00}, --Jump frames (object data: player)
{a=0x16E0,m=R4s,c=0xFFC0C0C0}, --Miniboss invincibility timer
{a=0x16F8,m=R4s,c=0xFFC0C0C0}, --Boss invincibility timer
{a=0x1708,m=R4s,c=0xFFFF8000}, --Boss HP main
{a=0x170C,m=R4s,c=0xFFFF8000}, --Boss HP sub
}
local RAMList= {
0x15E0, --Shot cooldown
0x1608, --Charge counter
0x1530, --Segment counter
0x153C, --Segment width
0x1540 --Segment height
}
--*****************************************************************************
local function PixelTextRight(x,y,t,c1,c2,f)
--*****************************************************************************
--Specify right pixel instead of left pixel to position our text.
--Should be handy in keeping our numbers relatively readable.
x= x - 4*(#tostring(t))
gui.pixelText(x,y,t,c1,c2,f)
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("Unknown address " .. a,2)
end
end
--[[
Weaponry:
Basic (start) : 1 2 3
Flame (FarLeft Top ): 3 5
Whirlwind (MidLeft Top ): 4 4
Dirtspike (MidRightTop ): 4 10
Bubble (FarRightTop ): 3 0
Missile (FarLeft Bottom): 4 6
Star (MidLeft Bottom): 3 6
Spikeball (MidRightBottom): 4 8
Laser (FarRightBottom): 4 10
14F0:Camera X position
14F4:Camera Y position
1500:
1504:
1508:
1510:
1514:
1518:
151C:
1520:
1524:
1528:
152C:Stage selection
1530:Segment
1534:
1538:
153C:Segment width
1540:Segment height
1544:
1548:
154C:
1550:
1554:
1558:
155C:
1560:X position
1564:Y position
1568:
156C:
15C0:Dash ready timer
15C4:Dash ready direction
15C8:Dash ready flag
15D0:Dashing timer
15D4:Dashing flag
15DC:Weapon shot counter? (Laser)
15E0:Shot cooldown
15E4:Weapon select
15F0:Action lock (zero means we're free)
1608:Frames charged
1618:Object invincibility flag
1624:Invincible flag
1628:Invincible timer
1634:Lives
1638:Player HP
163C:Weapon ammo (selection)
1640:Heart tanks
1644:Ammo tanks
1650:Arm upgrade
165C[12,1]:Weapon ammo
1668[12,1]:Have weapons
1674[12,1]:Beat stages
1680:(Looks temporary)
16B4:Bubble barrier durability
16B8:Bubble barrier timer
16E0:Miniboss invincibility timer
16F8:Boss invincibility timer
1708:Boss HP main
170C:Boss HP sub
17A0:Frame counter (used for RNG)
1EA4,2s - Flame charge timer
0807C18C
IWRAM:1DE4[size=0x74][count=60] (2B7C is slot 30, our first enemy)
+00,4s - Screen position X
+04,4s - Screen position Y
+08,2x - Sprite ID (?)
+0A,2x - ID?
+0C,2x -
+0E,2x - Flags
+10,2u - Action Timer
+12,2x - Action ID (?)
+14,4x - ?
+18,2x - Player shot ID
+1A,2x -
+1C,2u - Frames existing
+1E,2x -
+20,4x -
+24,4x -
+28,2u - nth enemy of the current stage
+2A,2x -
+2C,2s - Hit points
+2E,2x -
+30,4x -
+34,4x -
+38,4x -
+3C,4x -
+40,4x - ?
+44,4x -
+48,2s - Damage output
+4A,2x -
+4C,4x -
+50,4x -
+54,4x -
+58,4x - Invincibility frames
+5C,4x -
+60,4x -
+64,4x -
+68,4x -
+6C,4x -
+70,4x - Pointer
]]--
local ObjColors= {
[0]=0xFFC0C0C0, -- 0,Weapon selection icon
0xFF20FF20, -- 1,Player
0xFFC0C0C0, -- 2,Life icon
0xFFC0C0C0, -- 3,HP bar
0xFFC0C0C0, -- 4,Weapon bar
0xFFFFFFFF, -- 5,
0xFFC0C0C0, -- 6,Dust sprite
0xFFC0C0C0, -- 7,Dust sprite
0xFFC0C0C0, -- 8,Dust sprite
0xFFC0C0C0, -- 9,Dust sprite
0xFF00FF00, --10,Charge sprite
0xFFFFFFFF, --11,
0xFFFFFFFF, --12,
0xFFFFFFFF, --13,
0xFFFFFFFF, --14,
0xFFFFFFFF, --15,
0xFFFFFFFF, --16,
0xFFFFFFFF, --17,
0xFFFFFFFF, --18,
0xFFC0C0C0, --19,Player muzzle flash
0xFF00FFFF, --20,Player projectile
0xFF00FFFF, --21,Player projectile
0xFF00FFFF, --22,Player projectile
0xFF00FFFF, --23,Player projectile
0xFF00FFFF, --24,Player projectile
0xFF00FFFF, --25,Player projectile
0xFF00FFFF, --26,Player projectile
0xFF00FFFF, --27,Player projectile
0xFF00FFFF, --28,Player projectile (?)
0xFF00FFFF, --29,Player projectile (?)
0xFFFFFF00, --30,Enemy
0xFFFFFF00, --31,Enemy
0xFFFFFF00, --32,Enemy
0xFFFFFF00, --33,Enemy
0xFFFFFF00, --34,Enemy
0xFFFFFF00, --35,Enemy
0xFFFFFF00, --36,Enemy
0xFFFFFF00, --37,Enemy
0xFFFFFF00, --38,Enemy
0xFFFFFF00, --39,Enemy
0xFFFFFF00, --40,Enemy
0xFFFFFF00, --41,Enemy
0xFFFFFF00, --42,Enemy
0xFFFFFF00, --43,Enemy
0xFFFFFF00, --44,Enemy
0xFFFFFF00, --45,Enemy
0xFFFFFF00, --46,Enemy
0xFFFFFF00, --47,Enemy
0xFFFFFF00, --48,Enemy
0xFFFFFF00, --49,Enemy
0xFFFFC000, --50,
0xFFFFC000, --51,
0xFFFFC000, --52,
0xFFFFC000, --53,
0xFFFFC000, --54,
0xFFFFC000, --55,
0xFFFFC000, --56,
0xFFFFC000, --57,
0xFFFFC000, --58,
0xFFFFC000, --59,
}
--*****************************************************************************
local function DrawBlocks(x,y,a)
--*****************************************************************************
--This idea failed.
--Thought those pointers may have led to something boxy or whatever.
--Instead, we have a mess. But at least it isn't hitting the PANIC.
local PANIC= 0
while R2s(a,"ROM") ~= -1 do
local bX,bY= R1s(a,"ROM"),R1s(a+1,"ROM")
bX= bX*4+x; bY= bY*4+y
gui.drawRectangle(bX,bY,4,4,0x60FF00FF,0x60FFFF00)
a= a+2
PANIC=PANIC+1; if PANIC > 400 then error("PANIC") end --Just in case
end
end
--[[Piece of the failed idea
local ID= R2s(addr+0x08)
local ptr= R4u(addr+0x70)
if (ID ~= 0) and (ptr ~= 0) then
BlockPtr= R4u( FetchAddrDomainGBA(ptr + (ID-1)*4) )
if BlockPtr > 0x08000000 then --Really should be in the ROM.
DrawBlocks(X,Y,BlockPtr-0x08000000)
end
end
]]--
--*****************************************************************************
local function ObjList()
--*****************************************************************************
--Displays some information on objects.
--Well, more like a specific stat.
local ObjCount= 0
for i= 0, 59 do
local addr= 0x1DE4 + 0x74*i
local X= R4s(addr+0x00)
local Y= R4s(addr+0x04)
local clr= ObjColors[i] or 0xFFFF00FF --List really should be complete...
-- if R2s(addr+0x0A) ~= 0 then
gui.pixelText(X,Y ,string.format("%d",R2s(addr+0x2C)),clr)
gui.pixelText(X,Y+ 7,R2x(addr+0x1C),clr)
-- gui.pixelText(X,Y+14,string.format("%04X",addr),clr)
-- end
if R2s(addr+0x0A) ~= 0 then ObjCount= ObjCount+1 end
end
gui.pixelText(10,153,ObjCount)
end
--*****************************************************************************
local function NullFn() end --Do not process.
--*****************************************************************************
--*****************************************************************************
local function DoPlyr(addr,x,y)
--*****************************************************************************
--Player stuff. Potentially useful addresses:
--00,4s - Screen position X
--04,4s - Screen position Y
--08,2x - Sprite ID
--10,2u - Action Timer (?)
gui.pixelText(x,y ,string.format("%d",R2s(addr+0x08)),0xFF00FF20)
gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x10)),0xFF00FF20)
gui.pixelText(x,y+14,(R4u(0x17A0)-1)%4,0xFFFFFFFF) --Frame counter
--gui.pixelText(x,y+21,string.format("%08X",R4u(addr+0x70)),0xFFFFFFFF) --Ptr
end
local Bcount= 0
--*****************************************************************************
local function DoBllt(addr,x,y)
--*****************************************************************************
--Player bullets.
--1C: Frames existed
--48: Damage
if R2s(addr+0x08) == 0 then return end
Bcount= Bcount+1
gui.pixelText(x,y ,string.format("%d",R2s(addr+0x1C)),0xFF00FFFF)
gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x48)),0xFF00FFFF)
-- gui.pixelText(x,y ,string.format("%d",R2s(addr+0x08)),0xFF00FFFF)
-- gui.pixelText(x,y+ 7,string.format("%d",R2s(addr+0x18)),0xFF00FFFF)
-- gui.pixelText(x,y+14,string.format("%08X",R4s(addr+0x70)),0xFF00FFFF)
end
local Ecount= 0
--*****************************************************************************
local function DoOthr(addr,x,y)
--*****************************************************************************
--Enemy data.
--I would like a way to know when things exist.
--I'm guessing it's 0x0A, so I'll do that. Please let me know false negatives.
--Would love a way to recognize who I'm looking at.
--2C - Enemy HP
--58 - Invincibility timer
if R2s(addr+0x0A) == 0 then return end
Ecount= Ecount+1
if R2s(addr+0x3C) == 2 then --Item?
gui.pixelText(x,y ,string.format("%d",R2s(addr+0x1C)),0xFF000000,0xFF00FF00) --Timer
gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x0A)),0xFF000000,0xFF00FF00) --ID
elseif R2s(addr+0x2C) ~= -1 then --Has HP
gui.pixelText(x,y ,string.format("%d",R2s(addr+0x2C)),0xFFFF8000) --HP
gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x58)),0xFFFF8000) --Inv.Timer
-- gui.pixelText(x,y+14,string.format("%04X",addr),0xFFFF8000) --addr
-- gui.pixelText(x,y ,string.format("%d",R2s(addr+0x08)),0xFFFF8000) --Spr
-- gui.pixelText(x,y+7,string.format("%02X",R2s(addr+0x14)),0xFFFF8000) --ID
else --Can't be damaged
gui.pixelText(x,y ,string.format("%d",R2s(addr+0x1C)),0xFF000000,0xFFFF8000) --Timer
gui.pixelText(x,y+7,string.format("%d",R2s(addr+0x08)),0xFF000000,0xFFFF8000) --Sprite
end
end
local ObjTbl= {
[0]=NullFn, DoPlyr, NullFn, NullFn, NullFn,
NullFn, NullFn, NullFn, NullFn, NullFn,
DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
DoBllt, DoBllt, DoBllt, DoBllt, DoBllt,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr,
DoOthr, DoOthr, DoOthr, DoOthr, DoOthr
}
--*****************************************************************************
local function ObjList2()
--*****************************************************************************
--Displays some information on objects.
--Well, more like a specific stat.
Bcount= 0
Ecount= 0
local ObjCount= 0
for i= 0, 59 do
local addr= 0x1DE4 + 0x74*i
local X= R4s(addr+0x00)
local Y= R4s(addr+0x04)
ObjTbl[i](addr,X,Y)
end
PixelTextRight( 9,153,Bcount)
PixelTextRight(19,153,Ecount)
end
-- _h_H_a+A_H_Aa+h_LaHh_AH_*_A*_+_a
local HeartStr= " h H + H +h Hh H + "
local AmmoStr = " a A Aa a A * A* a"
local LifeStr = " L "
local DirStr = " < > <>> < ><<> <<>> <> < >> < >"
--*****************************************************************************
local function ItemDrop()
--*****************************************************************************
gui.pixelText(40,153,HeartStr,0xFFFF8000)
gui.pixelText(40,153,AmmoStr ,0xFF00C0FF,0)
gui.pixelText(39,153,LifeStr ,0xFF00FF00,0)
gui.pixelText(40,146,DirStr ,0xFFC0C0C0)
local RNG= R4u(0x17A0)%0x20
local X= RNG*4 + 40
gui.drawRectangle(X,153,5,6)
end
local DashDir= {[0]=">","<"}
--*****************************************************************************
local function TimerWatch()
--*****************************************************************************
if R4s(0x15C0) ~= 0 then --Dash ready
gui.pixelText( 24,0,DashDir[R4s(0x15C4)] or "?",0xFFFF80FF)--direction
PixelTextRight(23,0, R4s(0x15C0) ,0xFFFF80FF)--timer
end
if R4s(0x15D4) ~= 0 then --Dashing
PixelTextRight(32,0, R4s(0x15D0) ,0xFF80FF80)--timer
end
if R4s(0x1624) ~= 0 then --Player invincible
PixelTextRight(40,0, R4s(0x1628) ,0xFFFF8000)--timer
end
if R4s(0x16B4) ~= 0 then
PixelTextRight(60,0, R4s(0x16B4) ,0xFF0080FF)--Durability
PixelTextRight(70,0, R4s(0x16B8) ,0xFF0080FF)--Timer
end
end
local DashFrames= 0
--*****************************************************************************
local function DashCount()
--*****************************************************************************
if R4s(0x15D4) ~= 0 then DashFrames= DashFrames+1 end
PixelTextRight(32,50,DashFrames,0xFF80FF80)
end
--*****************************************************************************
local function RAMWatch()
--*****************************************************************************
--A convenient way to add important addresses is from the table at the top.
local Y= 153 - (#RAMList)*7
for i= 1, #RAMList do
PixelTextRight(239,Y+i*7,R2s(RAMList[i]))
end
end
--*****************************************************************************
local function RAMWatch2()
--*****************************************************************************
--A convenient way to add important addresses is from the table at the top.
--Additionally, we can specify method and color. Yay, customization!
-- {a=0x1530,m=R4s,c=0xFF00FFFF}
--a = address ; m = method ; c = color
local Y= 153 - (#RAMList2)*7
for i= 1, #RAMList2 do
local stat= RAMList2[i]
PixelTextRight(239,Y+i*7,stat.m(stat.a),stat.c)
end
end
--*****************************************************************************
local function ListVals(ptr,x)
--*****************************************************************************
local i= 0
while R2s(ptr,"ROM") ~= -1 do
gui.pixelText(x ,i*7,string.format("%04X",R2u(ptr ,"ROM")))
-- gui.pixelText(x+12,i*7,R1u(ptr+1,"ROM"))
ptr= ptr+2
i=i+1; if i > 30 then return end --Just in case
end
end
--*****************************************************************************
local function FinalHitboxAttempt()
--*****************************************************************************
--If this fails to call forth inspitation, then hitboxes just aren't happening.
--Those addresses just look suspicious, alright?
local addr= 0x1DE4 + 0x74*1 --Player object
local index= R2s(addr+0x08) --Sprite index
local ptr= R4s(addr+0x70) --Pointer for object
if index == 0 then gui.pixelText(50,0,"Null sprite") --A guess, really
elseif ptr == 0 then gui.pixelText(50,0,"Null pointer") --Not a guess
else
--I don't actually know if the game uses index as is or subtracts an offset.
--If the numbers are used for hitboxery, I want to know.
ptr= ptr + (index-1)*4
ptr= R4s(FetchAddrDomainGBA(ptr)) --Fetching some deeper data
if (ptr >= 0x08000000) and (ptr < 0x08000000+memory.getmemorydomainsize("ROM")) then
ptr= ptr - 0x08000000 --Why is this subtraction a requirement? VBA don't need it.
ListVals(ptr,50)
else
gui.pixelText(50,0,"Bad pointer")
end
end
end
--*****************************************************************************
local function ProgressColors(v)
--*****************************************************************************
if v > 0 then return 0xFF00C0FF end --Forward!
if v < 0 then return 0xFFFFC000 end --Backward!
return 0xFFFF00FF --We held still
end
local Left,Zero,DamageR,DamageL= 0,0,0,0
local cyan= 0xFF00FFFF
local LastX,LastY= 0,0
--*****************************************************************************
local function BasicHUD()
--*****************************************************************************
--General purpose display function. Stick whatever you like here.
local CamX,CamY= R4s(0x14F0),R4s(0x14F4)
local PlScrX,PlScrY= R4s(0x1E58),R4s(0x1E5C)
local PlX,PlY= PlScrX+CamX, PlScrY+CamY
PixelTextRight(239, 0,R4s(0x1560),cyan)
PixelTextRight(239, 7,PlX)
PixelTextRight(239, 14,PlX-LastX,ProgressColors(PlX-LastX))
PixelTextRight(239, 25,R4s(0x1564),cyan)
PixelTextRight(239, 32,PlY)
PixelTextRight(239, 39,PlY-LastY,ProgressColors(PlY-LastY))
local BossHP= (R4s(0x1708)-1)*5 + R4s(0x170C)
PixelTextRight(224,153,BossHP)
--Piece of code to count our movements for pre-dash horizontal stuff.
--[[
local move= PlX-LastX
if move == 1 then DamageR= DamageR+1
elseif move == 0 then Zero= Zero+1
elseif move == -1 then DamageL= DamageL+1
elseif move == -2 then Left= Left+1
end
PixelTextRight(219, 0,DamageR,0xFFFFFF80)
PixelTextRight(219, 7,Zero ,0xFFFFFF00)
PixelTextRight(219, 14,DamageL,0xFFFFC000)
PixelTextRight(219, 21,Left ,0xFFFF4000)
]]--
LastX,LastY= PlX,PlY
end
local Idle= 0
local BossScan
--*****************************************************************************
local function BossRepeat()
--*****************************************************************************
local inv= R4s(0x16F8)
if inv == 0 then
Idle= Idle+1
elseif Idle ~= 0 then
print(string.format("f%d - %d delayed",emu.framecount(),Idle-1))
Idle= 0
end
end
--*****************************************************************************
local function BossFirst()
--*****************************************************************************
local inv= R4s(0x16F8)
if inv ~= 0 then
print(string.format("f%d - First hit",emu.framecount()))
BossScan= BossRepeat
end
end
BossScan= BossFirst
--*****************************************************************************
while true do
--*****************************************************************************
--Our overhead.
memory.usememorydomain("IWRAM")
ObjList2()
ItemDrop()
RAMWatch2()
TimerWatch()
-- DashCount()
BasicHUD()
-- BossScan()
-- FinalHitboxAttempt()
emu.frameadvance()
end