If you somehow missed this fact from the title I have up there, I have it in nice big words here, just in case.
MtEdit is a TASing lua script designed to hold on to the user's input separate from the emulator and play it back, allowing the user to make modifications as desired, and with a neat display to go with it, showing exactly what you did.
The script has since gone through a complete rewrite in v2. It will auto-detect which controllers are plugged into lsnes when it starts up, and will also respond to lsnes hotkeys for the controllers.
By the way, I'm holding on to all earlier versions I've made. Below is v1, but v2 is linked to above. I prefer that you try the latest stuff.
Download MtEdit_lsnes_v01.luaLanguage: lua
local Players= bit.value(
0,
4,
nil
)
local Controls= {
[0]="g",
"t",
"r",
"u",
"w",
"s",
"a",
"d",
"h",
"y",
"f",
"j"
}
for i= 0, 11 do Controls[Controls[i]]= i end
local cmd_NextPlayer= "l"
local cmd_MtEditRead= "z"
local cmd_InputMode= "x"
local PlSel= 0
local function NullFN() end
local HeldBtns= {}
local LastBtns= {}
local ReadMovie= {}
local ReadMacro= {}
local BtnCtrlA= {}
local BtnCtrlB= {}
local InputList= {}
local ResetList= {}
for Pl= 0, 7 do
HeldBtns[Pl]= 0x0000
LastBtns[Pl]= 0x00000000
ReadMovie[Pl]= 0x0FFF
ReadMacro[Pl]= 0x0000
BtnCtrlA[Pl]= 0x0FFF
BtnCtrlB[Pl]= 0x0000
InputList[Pl]= {}
end
local inp_Top= 0
local inp_Lef= -96
local inp_Off= -27
local ExpectedSize= 56
local inp_Display= gui.bitmap_new(95,ExpectedSize*8-1,true)
local Tiles= {}
local BlankFrame= gui.bitmap_new(95,7,true)
local This_Subframe= 0
local function PlayerLoop(Fn)
for p= 0, 7 do
if bit.extract(Players,p) ~= 0 then
Fn(p)
end
end
end
local function Dbmp7x7(n,clr)
local bitmap= gui.bitmap_new(7,7,true)
for i= 0, 48 do
local c= -1
if bit.extract(n,i) == 1 then c= clr end
gui.bitmap_pset(bitmap,
i%7,
math.floor(i/7),
c
)
end
return bitmap
end
local Icons7x7= {
[0]= 0x0FF1E37F8F1BF,
0x060C1830F3343,
0x198CB4A484486,
0x0994A848894F6,
0x18DB3E3870408,
0x02041C38F9B63,
0x10383C3EF3840,
0x00439EF878381,
0x18FFFFC78DF1C,
0x10F33C30F3343,
0x1FFF83060C183,
0x18F1BF7F8F1BF
}
local BlankTiles= {}
for i= 0, 11 do
BlankTiles[i]= Dbmp7x7(Icons7x7[i],0xFFFFFF)
end
local Tc= {
[0]= 0xFF2000, 0x00FF00,
0xFF8080, 0xA0FFA0,
0xA00000, 0x008000,
0xC04040, 0x40C040,
}
for x= 0, 11 do
Tiles[x]= {}
for c= 0, #Tc do
Tiles[x][c]= gui.bitmap_new(7,7,true,Tc[c])
gui.bitmap_blit(Tiles[x][c],0,0,
BlankTiles[x],0,0,7,7 , 0xFFFFFF)
end
end
for x= 0, 11 do
gui.bitmap_blit(BlankFrame,x*8,0,
BlankTiles[x],0,0,7,7)
end
local x_off= {[0]= 6,5,3,4,1,1,0,2,7,6,0,7}
local y_off= {[0]= 2,1,1,1,0,2,1,1,1,0,0,0}
local ModeNames= {[0]= "Ignore","Normal","Sticky","Hold"}
local ModeClrs= {[0]= 0xFF2000,0xFFFFFF,0x00FF00,0x00FFFF}
local function InputDisplay(Pl)
local Right, Bottom= gui.resolution()
gui.bottom_gap(26)
local BCa, BCb= BtnCtrlA[Pl], BtnCtrlB[Pl]
gui.rectangle(Pl*64,Bottom,64,24,1,0x000080,0x000080)
PlayerLoop(function(p)
local HB= HeldBtns[p]
for i= 0, 11 do
local c= Tiles[i][bit.extract(HB,i)]
gui.bitmap_draw(p*64 + x_off[i]*8,Bottom+1+y_off[i]*8,c)
end
end)
local mode= bit.bor(
bit.extract(BCa,0),
bit.extract(BCb,false,0)
)
gui.text(inp_Lef,Bottom+10,ModeNames[mode],ModeClrs[mode])
gui.text(-10,Bottom+10,PlSel,0x00FFFF)
end
local function ShowList()
gui.left_gap(-inp_Lef+1)
gui.rectangle(inp_Lef-1, inp_Top + inp_Off*(-8) - 1, 97, 9, 1,
0x00FFFF, 0x404040)
gui.bitmap_draw(inp_Lef,inp_Top,inp_Display)
local RM= ReadMovie[PlSel]
for i= 0, 11 do
local clr= 0xFF0000
if bit.extract(RM,i) == 1 then clr= 0x00FF00 end
gui.circle(inp_Lef+3+8*i,451,4,1,clr,clr)
end
end
local function BlitFrame(Pl, y_pos, frame)
local GetFrame= InputList[Pl][frame]
if GetFrame then
for x= 0, 11 do
local tile= Tiles[x][bit.extract(GetFrame,x,x+16)]
gui.bitmap_blit(inp_Display,x*8,y_pos*8,
tile,0,0,7,7)
end
else
gui.bitmap_blit(inp_Display,0,y_pos*8,
BlankFrame,0,0,95,7)
end
end
local function UpdateBox(Pl, frame)
BlitFrame(Pl, ExpectedSize-1,
frame+inp_Off+ExpectedSize-1)
BlitFrame(Pl, -inp_Off - 1,
frame-1)
end
local function RefreshBox(Pl, frame)
local StartFrame= frame + inp_Off
for y= 0, ExpectedSize-1 do
BlitFrame(Pl, y,StartFrame+y)
end
end
RefreshBox(PlSel, movie.currentframe())
local lsnesBGui= InputDisplay
local function GetKeyboardHolds()
local keys= input.raw()
local c= 0
for i= 0, 11 do
local key= keys[Controls[i]]
c= bit.bor(c, bit.lshift(key.last_rawval,i))
end
return c
end
local function UpdateControl(Pl, frame)
PlayerLoop(function(p)
local MovieData= InputList[p][frame] or 0x0000
HeldBtns[p]= bit.band(MovieData,ReadMovie[p])
end)
local KeyData= bit.band(
BtnCtrlA[Pl], bit.bnot(BtnCtrlB[Pl]),
GetKeyboardHolds()
)
HeldBtns[Pl]= bit.bxor(HeldBtns[Pl],KeyData)
end
local function NormalMode(index, KeyState)
HeldBtns[PlSel]= bit.bxor(HeldBtns[PlSel], bit.value(index))
end
local function HoldMode(index, KeyState)
HeldBtns[PlSel]= bit.bxor(HeldBtns[PlSel], bit.lshift(KeyState,index))
end
local lsnesHandleBtn= {
[0]=NullFN,
NormalMode,
HoldMode,
HoldMode
}
KeyPress= {}
KeyPress[cmd_MtEditRead]= function()
if ReadMovie[PlSel] == 0 then
ReadMovie[PlSel]= 0x0FFF
else
ReadMovie[PlSel]= 0
end
end
KeyPress[cmd_InputMode]= function()
if BtnCtrlA[PlSel] ~= 0 then
BtnCtrlA[PlSel]= 0
BtnCtrlB[PlSel]= 0x0FFF
else
BtnCtrlA[PlSel]= 0x0FFF
BtnCtrlB[PlSel]= 0
end
end
KeyPress[cmd_NextPlayer]= function()
local Sanity= PlSel
repeat
PlSel= (PlSel+1)%8
until (bit.extract(Players,PlSel) ~= 0) or (Sanity == PlSel)
RefreshBox(PlSel, movie.currentframe()-1)
end
KeyRelease= {}
function on_paint()
ShowList()
InputDisplay(PlSel)
end
gui.repaint()
function on_keyhook(s,t)
if KeyPress[s] then
if t.last_rawval == 1 then
KeyPress[s]()
gui.repaint()
end
else
local c= Controls[s]
local mode = bit.bor(
bit.extract(BtnCtrlA[PlSel],c),
bit.extract(BtnCtrlB[PlSel],false,c)
)
lsnesHandleBtn[mode](c, t.last_rawval)
gui.repaint()
end
end
for k,v in pairs(KeyPress) do input.keyhook(k,true) end
for k,v in pairs(KeyRelease) do input.keyhook(k,true) end
for i= 0, 11 do input.keyhook(Controls[i],true) end
local function SubframeWatch()
end
function on_frame_emulated()
local frame= movie.currentframe()-1
PlayerLoop(function(p)
InputList[p][frame]= LastBtns[p]
end)
on_idle= nil
UpdateControl(PlSel, movie.currentframe())
This_Subframe= 0
UpdateBox(PlSel,movie.currentframe())
end
local FrameJumped= false
local function FrameJump()
FrameJumped= true
UpdateControl(PlSel, movie.currentframe())
This_Subframe= 0
RefreshBox(PlSel, movie.currentframe()-1)
gui.repaint()
end
on_post_load= FrameJump; on_rewind= FrameJump
function on_input(b)
if (not b) or FrameJumped then
gui.bitmap_blit(inp_Display,0,0,
inp_Display,0,8,95,ExpectedSize*8-9)
if (not b) then
if ResetList[movie.currentframe()] then
input.reset(ResetList[movie.currentframe()])
end
end
This_Subframe= 0
end
FrameJumped= false
PlayerLoop(function(p)
input.seta(p, HeldBtns[p])
local lsnesInput= input.geta(p)
LastBtns[p]= bit.bor(0x0FFF0000, lsnesInput)
end)
This_Subframe= This_Subframe+1
on_idle = SubframeWatch
set_idle_timeout(25000)
end
function on_snoop(port,pad,btn,v)
local Pl= port*4 + pad
if bit.extract(Players, Pl) == 0 then return end
if btn >= 12 then return end
LastBtns[Pl]= bit.bor(bit.band(LastBtns[Pl],bit.bnot(bit.value(btn))),
bit.lshift(v, btn)
)
local LagBit= bit.lshift(0x10000,btn)
LastBtns[Pl]= bit.band(LastBtns[Pl],bit.bnot(LagBit))
end
print("MtEdit loaded. Please enjoy the script.")
gui.subframe_update(false)
on_video= nil
on_frame= nil
on_startup= nil
on_quit= nil
on_reset= nil
on_readwrite= nil
on_pre_load= nil
on_err_load= nil
on_pre_save= nil
on_err_save= nil
on_post_save= nil