--MtEdit lsnes v2 (PROTOTYPE)
--For lsnes rr1 delta17 epsilon3
--FatRatKnight
--[[
Checklist:
[X] - gamepad
[X] - gamepad16
[X] - multitap
[X] - multitap16
[?] - mouse
[ ] - superscope
[ ] - justifier
[ ] - justifiers
[ ] - Allow a way to control analog stuff
[ ] - Record/apply reset
[ ] - Insert/delete frames
[ ] - Subframe control
[ ] - Macros
[ ] - Rewind
[ ] - Sanity versus unusual circumstances (Like new -> movie)
]]--
--#############################################################################
-- Key bindings
local inp_MouseUp= "i"
local inp_MouseDn= "k"
local inp_MouseLf= "j"
local inp_MouseRt= "l"
local cmd_SelectPlayer= "p"
--#############################################################################
-- Anything other than key bindings
local Tc= { -- Tile colors to display button pressed state.
-- Released Pressed
[0]= 0xFF2000, 0x00FF00, -- Saturated, normal
0xFF8080, 0xA0FFA0, -- Light, lag indicator
0xC05040, 0x40C040, -- Desat., oversnoop indicator (no changing inputs)
0xFFFF00, 0xFFFF00 -- Yellow=changing oversnoop
}
-- Beyond this point is probably a good idea not to change.
-- Unless, of course, you know what you're doing.
--#############################################################################
--#############################################################################
-- General purpose functions and utilities
local function NullFN() end -- Take a wild guess. If you said nothing, you win
GapVals= {top=0,bottom=0,left=0,right=0}
--*****************************************************************************
function RequestGap(side,value)
--*****************************************************************************
-- For on_paint or on_video only. Messy stuff might happen otherwise.
-- Adds to a number for the next time gaps are to be drawn.
-- Returns an x or y position, based on side:
-- top: y position of total requested top gaps
-- bottom: y position of total bottom gaps + Y resolution
-- left: x position of total requested left gaps
-- right: x position of total right gaps + X resolution
-- These values returned *EXCLUDE* the latest requested gap.
-- You should be able to add them yourself - You requested that gap!
-- Feel free to call this with a value of zero if you want the stored value.
-- Although, why not just look at GapVals.whatever or GapVals["whatever"]?
-- Defensive sanity
if not GapVals[side] then
error("Bad argument #1",2)
end
if type(value) ~= "number" then
error("Argument #2 must be number",2)
end
value= math.floor(value) -- Trim off fractional portion.
if value < 0 then value= value end -- Negative gaps? No.
-- Now do complicated calculations!
local LastGap= GapVals[side]
GapVals[side]= GapVals[side]+value
return LastGap
end
--*****************************************************************************
function ApplyGaps()
--*****************************************************************************
-- on_paint or on_video only.
-- Please call at the very end of an on_paint or on_video callback.
-- Makes use of the overtaking property of gap functions.
-- Will also reset GapVals as they are used.
gui.top_gap( GapVals.top) ;GapVals.top =0
gui.bottom_gap(GapVals.bottom);GapVals.bottom=0
gui.right_gap( GapVals.right) ;GapVals.right =0
gui.left_gap( GapVals.left) ;GapVals.left =0
end
-- Key press stuff.
local KeyPress,KeyRelease= {},{}
--*****************************************************************************
local function RegisterKeyPress(key,fn)
--*****************************************************************************
local OldFn= KeyPress[key]
KeyPress[key]= fn
input.keyhook(key,type(fn or KeyRelease[key]) == "function")
return OldFn
end
--*****************************************************************************
local function RegisterKeyRelease(key,fn)
--*****************************************************************************
local OldFn= KeyRelease[key]
KeyRelease[key]= fn
input.keyhook(key,type(fn or KeyPress[key]) == "function")
return OldFn
end
--*****************************************************************************
local function AltKeyhook(s,t)
--*****************************************************************************
if KeyPress[s] and (t.last_rawval == 1) then
KeyPress[s](s,t)
elseif KeyRelease[s] and (t.last_rawval == 0) then
KeyRelease[s](s,t)
end
end
--#############################################################################
--#############################################################################
-- Global
local PlSel= 0 -- Which player is the focus? Zero to seven.
local cPort= 1 -- Which port is PlSel on? ... I need this all the time. Eep.
local Ports= {input.port_type(1),input.port_type(2)}
local cf= movie.currentframe()
local ResX, ResY= 512,448 -- May want to dynamically read the resolution.
local MaxPlayers= {
gamepad =1,
gamepad16 =1,
multitap =4,
multitap16=4,
mouse =1,
superscope=1,
justifier =1,
justifiers=2,
none =0
}
--local ListSize = math.floor((ResY+1)/8)
--local ListOffset= math.floor(ListSize/2)
--local ListWidth = PortWidths[Ports[1]] or 0
local Immediate = {[0]=0,0,0,0,0,0,0,0} --[player]; For immediate injection.
local BtnState = {0,0}
local FrameList = {} --[player][frame]
local SubframeList= {} --[player][frame][subframe]
local ListRead = {} --[player] How we'll read stored input
for pl= 0, 7 do
FrameList[pl]= {}
SubframeList[pl]= {}
ListRead[pl]=0x000000FFFFFF
end
--*****************************************************************************
local TranslateIndex= {}
--*****************************************************************************
-- Get the proper bit position.
-------------------------------------------------------------------------------
function TranslateIndex.gamepad(i) return bit.value(i) end
function TranslateIndex.mouse(i) return bit.value(i+16) end
function TranslateIndex.superscope(i) return 0 end -- Not supported, yet
function TranslateIndex.justifier(i) return 0 end -- Not supported, yet
function TranslateIndex.none(i) return 0 end -- This shouldn't happen
TranslateIndex.gamepad16 = TranslateIndex.gamepad
TranslateIndex.multitap = TranslateIndex.gamepad
TranslateIndex.multitap16= TranslateIndex.gamepad
TranslateIndex.justifiers= TranslateIndex.justifier
--Structure:
--gamepad,gamepad16,multitap,multitap16:
-- 0xccccbbbbaaaa
-- a = Pressed or not pressed
-- b = Lag indicator.
-- c = Oversnoop indicator. Are subframes needed?
--mouse:
-- 0xcbaYYXX
-- X = X coordinate (8th bit indicates positive/negative: 1 is negative)
-- Y = Y coordinate (Same deal with X coordinate)
-- a = Pressed or not pressed; For X,Y coordinates, record as 1 if non-zero.
-- b = Lag inicator.
-- c = Oversnoop indicator. Are subframes needed?
--superscope,justifier,justifiers:
-- Undefined. To be defined when someone codes it up.
--system:
-- Just one number. Call it [V]
-- If negative, no reset takes place
-- Otherwise, request reset after [V] cycles.
-- Not sure if there's any other data to pick up.
--none:
-- Empty set. Do not record or overwrite.
--[[
--*****************************************************************************
local function GetFrame(port,frame,player,subframe)
--*****************************************************************************
-- Returns a number representing the controller input.
-- These numbers are the base storage for MtEdit.
end
--*****************************************************************************
local function GetFrameFromlsnes(port,frame,player,subframe)
--*****************************************************************************
-- Returns a number, stitched together from lsnes own storage.
-- Returns nil if the frame doesn't exist.
if (frame <= 0) or (frame > movie.framecount()) then return nil end
end
]]--
--#############################################################################
--#############################################################################
-- Display
--Icons
--*****************************************************************************
local function Dbmp7x7(n,clr)
--*****************************************************************************
-- Converts a number into a monochrome 7x7 BITMAP.
-- Enough space in a double for this to happen. So I use it.
-- However, readability suffers greatly due to this.
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, -- DBITMAP object.
i%7, -- x
math.floor(i/7), -- y
c -- color
)
end
return bitmap -- Be sure to hand out what I just made.
end
-------------------------------------------------------------------------------
local Icons7x7= {
[0]=0x0FBFE3C78FFBE, -- 0
0x1FFF98306CF1C, -- 1
0x1FF98638C73BE, -- 2
0x0FB3F079C33BE, -- 3
0x0C187FFECD9B3, -- 4
0x0FB1E07E0FFFF, -- 5
0x0FB1E77E0C1BE, -- 6
0x0186186183FFF, -- 7
0x0FB1E37D8F1BE, -- 8 Too similar to B. I'm going to change it soon...
0x10205EE7879BE, -- 9 ... This one looks stupid
0x18FFFFC78DF1C, -- A
0x0FF1E37F8F1BF, -- B
0x0FBF83060FFBE, -- C
0x07D9E3C78D99F, -- D
0x1FC1837E0C1FF, -- E
0x00C1BF7E0FFFF, -- F
0x000000FE00000, -- -
0x020408FE20408, -- +
--B
0x060C1830F3343, -- Y
0x198CB4A484486, -- Select
0x0994A848894F6, -- Start
0x18DB3E3870408, -- Up
0x02041C38F9B63, -- Down
0x10383C3EF3840, -- Left
0x00439EF878381, -- Right
--A
0x10F33C30F3343, -- X
0x1FFF83060C183, -- L
0x18F1BF7F8F1BF, -- R
}
-------------------------------------------------------------------------------
-- Make white tiles.
for x= 0, #Icons7x7 do
Icons7x7[x]= Dbmp7x7(Icons7x7[x],0xFFFFFF)
end
local EmptyIcon= gui.bitmap_new(7,7,true)
-------------------------------------------------------------------------------
--gamepad16 (and friends)
--BYsS^v<>AXLR0123
local BlankPad= gui.bitmap_new(127,7,true)
-- B Y s S ^ v < > A X L R 0 1 2 3
local PadIcons= {[0]=11,18,19,20,21,22,23,24,10,25,26,27, 0, 1, 2, 3}
for i= 0, 15 do
local IconIndex= PadIcons[i]
--Produce the blank frame.
gui.bitmap_blit(
BlankPad, i*8,0,
Icons7x7[IconIndex],0 ,0, 7,7
)
--Produce the various colored tiles!
PadIcons[i]= {}
for c= 0, #Tc do
PadIcons[i][c]= gui.bitmap_new(7,7,true,Tc[c])
gui.bitmap_blit(
PadIcons[i][c], 0,0,
Icons7x7[IconIndex],0,0, 7,7 , 0xFFFFFF
) -- Use blank tiles like stencil. Should be faster than pset.
end
end
-------------------------------------------------------------------------------
--mouse
--X+7F Y+7F LR
-- Produce blank mouse frame:
local BlankMouse= gui.bitmap_new(95,7,true)
gui.bitmap_blit(BlankMouse, 0,0 , Icons7x7[25],0,0, 7,7) -- X...........
gui.bitmap_blit(BlankMouse,16,0 , Icons7x7[16],0,0, 7,7) -- ..-.........
gui.bitmap_blit(BlankMouse,24,0 , Icons7x7[16],0,0, 7,7) -- ...-........
gui.bitmap_blit(BlankMouse,40,0 , Icons7x7[18],0,0, 7,7) -- .....Y......
gui.bitmap_blit(BlankMouse,56,0 , Icons7x7[16],0,0, 7,7) -- .......-....
gui.bitmap_blit(BlankMouse,64,0 , Icons7x7[16],0,0, 7,7) -- ........-...
gui.bitmap_blit(BlankMouse,80,0 , Icons7x7[26],0,0, 7,7) -- ..........L.
gui.bitmap_blit(BlankMouse,88,0 , Icons7x7[27],0,0, 7,7) -- ...........R
-- Now lets assign a bunch of icons for the mouse!
local MouseIcons= {}
for i= 0, 0xF do MouseIcons[i]= Icons7x7[i] end -- White digits.
MouseIcons[16]= PadIcons[ 9] -- X
MouseIcons[17]= PadIcons[ 1] -- Y
MouseIcons[18]= PadIcons[10] -- L
MouseIcons[19]= PadIcons[11] -- R
MouseIcons[20]= gui.bitmap_new(7,7,true,0x00FFFF) -- Cyan plus
gui.bitmap_blit(MouseIcons[20],0,0 , Icons7x7[17],0,0, 7,7 , 0xFFFFFF)
MouseIcons[21]= gui.bitmap_new(7,7,true,0xFFFF00) -- Yellow minus
gui.bitmap_blit(MouseIcons[21],0,0 , Icons7x7[16],0,0, 7,7 , 0xFFFFFF)
--Wonky indexing, but string indecies make the code too messy.
--#############################################################################
-- Display Frame List (left)
local DisplayLength= 56
local DisplayOffset= math.floor(DisplayLength/2)
local DispList= gui.bitmap_new( 95,447,true)
local DispListEx=gui.bitmap_new(127,447,true)
local TempFrame= gui.bitmap_new(127, 7,true)
local MainList = nil -- Should hold the current display
local MainListWidth= 0
local PortWidths= { -- How wide to make the gap?
gamepad = 95,
gamepad16 =127,
multitap = 95,
multitap16=127,
mouse = 95,
[ 95]= DispList,
[127]= DispListEx
}
--*****************************************************************************
local function BlitGamepad(IN)
--*****************************************************************************
for i= 0, 15 do
local color= bit.extract(IN,i,i+16,i+32)
gui.bitmap_blit(
TempFrame,8*i,0,
PadIcons[i][color],0,0, 7,7
)
end
return TempFrame
end
local MouseTilePos= {[0]=0,40,80,88}
--*****************************************************************************
local function BlitMouse(IN)
--*****************************************************************************
--Blank spaces
gui.bitmap_blit(TempFrame,32,0, EmptyIcon,0,0, 7,7)
gui.bitmap_blit(TempFrame,72,0, EmptyIcon,0,0, 7,7)
--X,Y indicators and Left, Right mouse buttons
for i= 0, 3 do
local color= bit.bor(
bit.extract(IN,i+16), --Pressed/released
bit.extract(IN,nil,i+20), --Lag indicator
bit.extract(IN,nil,nil,i+24)--Oversnoop indicator
)
gui.bitmap_blit(
TempFrame,MouseTilePos[i],0,
MouseIcons[i+16][color],0,0, 7,7
)
end
--Now to blit in change of mouseposition...
for i= 0, 1 do
local v= 8*i
local index= 20 + bit.extract(IN,7+v) --Plus or minus
gui.bitmap_blit(
TempFrame, 8+40*i,0,
MouseIcons[index],0,0, 7,7
)
index= bit.extract(IN,4+v,5+v,6+v) --Upper hex digit
gui.bitmap_blit(
TempFrame,16+40*i,0,
MouseIcons[index],0,0, 7,7
)
index= bit.extract(IN,v,1+v,2+v,3+v) --Lower hex digit
gui.bitmap_blit(
TempFrame,24+40*i,0,
MouseIcons[index],0,0, 7,7
)
end
return TempFrame
end
local BlitFn
local BlankFr
--*****************************************************************************
local function ChooseList(PortType)
--*****************************************************************************
-- Best used on startup and whenever a new player is selected.
-- ... Turn this into a table at some point...
MainListWidth= PortWidths[PortType]
if MainListWidth then
MainList= PortWidths[MainListWidth]
if PortType == "mouse" then
BlitFn= BlitMouse
BlankFr= BlankMouse
else
BlitFn= BlitGamepad
BlankFr= BlankPad
end
return
end
--Either unsupported or this is an empty port. Either way, set up blanks.
MainListWidth= 0
MainList= nil
end
--*****************************************************************************
local function MakeListFrame(i,frame)
--*****************************************************************************
-- Assumes MainList exists. Don't call without ensuring it's there!
local IN= FrameList[PlSel][frame]
local Frame= BlankFr --Confusing names. Sorry! "frame" "Frame"
if IN then Frame= BlitFn(IN) end
gui.bitmap_blit(
MainList,0,i*8 ,
Frame, 0,0 , MainListWidth,7
)
end
--*****************************************************************************
local function MakeListFromScratch()
--*****************************************************************************
--Call on script startup, new player selected, and anytime the frame shifts by
--anything other than +0 or +1.
--Really would love it if ChooseList got the right player first.
--Otherwise, the display
if not MainList then return end -- Don't. Just, don't blit onto nothing!
for i= 0, DisplayLength-1 do
MakeListFrame(i, cf - DisplayOffset + i)
end
end
--*****************************************************************************
local function AdvanceList()
--*****************************************************************************
--Use inside callback on_frame_emulated
--cf (Current Frame) is still on its old value.
--Moves the display up one step, then gets a single new frame.
if not MainList then return end -- No MainList? Then do nothing.
gui.bitmap_blit( -- Dangerous use of blit: source == target
MainList,0,0, -- I'm abusing a quirk for efficiency reasons.
MainList,0,8, MainListWidth,DisplayLength*8-9
)
MakeListFrame(
DisplayLength - 1,
cf - DisplayOffset + DisplayLength
)
MakeListFrame(
DisplayOffset - 1,
cf
)
end
local ReadListColors= {
[0]= 0xFF0000, -- Always return released
0xFFFFFF, -- Read movie
0x0000FF, -- Invert movie
0x00FF00 -- Always return pressed
}
--*****************************************************************************
local MovieReadDots= {}
--*****************************************************************************
-------------------------------------------------------------------------------
function MovieReadDots.GetColor(i)
-------------------------------------------------------------------------------
-- Need this sequence of code in all my ReadDots...
return ReadListColors[bit.extract(ListRead[PlSel],i,i+24)]
end
-------------------------------------------------------------------------------
function MovieReadDots.gamepad(X)
-------------------------------------------------------------------------------
for i= 0, 11 do
local x= X + i*8 + 4
gui.circle(x,ResY+4,4,0,0,MovieReadDots.GetColor(i))
end
end
MovieReadDots.multitap= MovieReadDots.gamepad
-------------------------------------------------------------------------------
function MovieReadDots.gamepad16(X)
-------------------------------------------------------------------------------
for i= 0, 15 do
local x= X + i*8 + 4
gui.circle(x,ResY+4,4,0,0,MovieReadDots.GetColor(i))
end
end
MovieReadDots.multitap16= MovieReadDots.gamepad16
-------------------------------------------------------------------------------
function MovieReadDots.mouse(X)
-------------------------------------------------------------------------------
gui.rectangle(X+ 1,ResY,31,7,0,0,MovieReadDots.GetColor(16))
gui.rectangle(X+ 41,ResY,31,7,0,0,MovieReadDots.GetColor(17))
gui.circle( X+ 84,ResY+4,4 ,0,0,MovieReadDots.GetColor(18))
gui.circle( X+ 92,ResY+4,4 ,0,0,MovieReadDots.GetColor(19))
end
--*****************************************************************************
local function ShowList()
--*****************************************************************************
-- on_paint
-- Just request the gap,
-- and paint our bitmap.
-- Oh, and display some dots indicating movie playback mode.
if not MainList then return end
local X= RequestGap("left",MainListWidth+2)
local Y= DisplayOffset*8-1
gui.rectangle(X-MainListWidth-2,Y,MainListWidth+2,9, 1,0x00FFFF,0)
gui.bitmap_draw(- X - MainListWidth - 1,0,MainList)
MovieReadDots[Ports[cPort]](X - MainListWidth - 2)
--Eh, dot them later...
end
--*****************************************************************************
local function LGui()
--*****************************************************************************
ShowList()
--[[
for i= 0, 23 do
local x= i*8 + 4
gui.circle(x,400,4,0,0,
ReadListColors[bit.extract(ListRead[PlSel],i,i+24)])
end
]]--
end
--#############################################################################
-- Display Immediate Input (bottom)
-- B Y s S ^ v < > A X L R 0 1 2 3
local x_off= {[0]=48,40,24,32, 8, 8, 0,16,56,48, 0,56,16,24,32,40}
local y_off= {[0]=16, 8, 8, 8, 0,16, 8, 8, 8, 0, 0, 0,16,16,16,16}
--*****************************************************************************
local function DrawPad(x,y,IN)
--*****************************************************************************
-- Draws the controller using IN as input at coordinates x,y.
for i= 0, 11 do
local color= bit.extract(IN,i)
gui.bitmap_draw(x+x_off[i],y+y_off[i],PadIcons[i][color])
end
end
--*****************************************************************************
local function DrawPad16(x,y,IN)
--*****************************************************************************
-- Like DrawPad, but with the four extended buttons.
for i= 0, 15 do
local color= bit.extract(IN,i)
gui.bitmap_draw(x+x_off[i],y+y_off[i],PadIcons[i][color])
end
end
local m_sign= {[0]="+","-"}
local m_XY= {[0]="X","Y"}
--*****************************************************************************
local function DrawMouse(x,y,IN)
--*****************************************************************************
-- L and R buttons, as well as X,Y deltas.
gui.bitmap_draw(x+16,y,MouseIcons[18][bit.extract(IN,18)]) -- L
gui.bitmap_draw(x+40,y,MouseIcons[19][bit.extract(IN,19)]) -- R
for i=0,1 do
local b= 8*i -- Bit index
gui.text(x+32*i,y+8,string.format("%s%s%2X",
m_XY[i],
m_sign[bit.extract(IN,b+7)],
bit.band(bit.lrshift(IN,b),0x7F)
))
end
end
--*****************************************************************************
local DrawController= {}
--*****************************************************************************
-- Intermediary functions to display the immediate input.
-------------------------------------------------------------------------------
function DrawController.gamepad(x,y,port)
DrawPad(x,y,Immediate[port*4])
end
-------------------------------------------------------------------------------
function DrawController.gamepad16(x,y,port)
DrawPad16(x,y,Immediate[port*4])
end
-------------------------------------------------------------------------------
function DrawController.multitap(x,y,port)
local P= port*4
for pl= 0,3 do DrawPad(x+pl*64,y,Immediate[pl+P]) end
end
-------------------------------------------------------------------------------
function DrawController.multitap16(x,y,port)
local P= port*4
for pl= 0,3 do DrawPad16(x+pl*64,y,Immediate[pl+P]) end
end
-------------------------------------------------------------------------------
function DrawController.mouse(x,y,port)
DrawMouse(x,y,Immediate[port*4])
end
-------------------------------------------------------------------------------
function DrawController.superscope(x,y,port)
gui.text(x,y,"Not supported!",0xFF2000)
end
-------------------------------------------------------------------------------
function DrawController.justifier(x,y,port)
gui.text(x,y,"Not supported!",0xFF2000)
end
-------------------------------------------------------------------------------
function DrawController.justifiers(x,y,port)
gui.text(x,y,"Not supported!",0xFF2000)
end
-------------------------------------------------------------------------------
function DrawController.none(x,y,port)
gui.text(x,y,"No Controller",0xFFFF00)
end
--*****************************************************************************
local function BGui()
--*****************************************************************************
-- Currently assuming width of 512.
-- Probably not a good idea if the drawing area can be resized.
local Y= ResY+RequestGap("bottom",25)
local x= PlSel*64
gui.rectangle(x,Y,63,25,0x000080,0x000080)
DrawController[Ports[1]]( 0,Y+1,0)
DrawController[Ports[2]](256,Y+1,1)
end
--#############################################################################
--*****************************************************************************
local MarkController= {}
--*****************************************************************************
-------------------------------------------------------------------------------
function MarkController.gamepad(pl)
-------------------------------------------------------------------------------
Immediate[pl]= bit.bor(
bit.band(Immediate[pl],0x00000000FFFF),0x0000FFFF0000
) -- Trim off oversnoop indicator, push in lag indicator
end
MarkController.gamepad16 = MarkController.gamepad
MarkController.multitap = MarkController.gamepad
MarkController.multitap16= MarkController.gamepad
-------------------------------------------------------------------------------
function MarkController.mouse(pl)
-------------------------------------------------------------------------------
Immediate[pl]= bit.bor(
bit.band(Immediate[pl],0x0000000FFFFF),0x000000F00000
)
end
MarkController.superscope= NullFN
MarkController.justifier = NullFN
MarkController.justifiers= NullFN
MarkController.none = NullFN
--*****************************************************************************
local function MarkImmediate()
--*****************************************************************************
for port= 1, 2 do
local pl= (port-1)*4
for p= 0, 3 do
MarkController[Ports[port]](pl+p)
end
end
end
--*****************************************************************************
local function SaveImmediate(frame)
--*****************************************************************************
for port= 1, 2 do
local n= MaxPlayers[Ports[cPort]]-1 -- Don't store non-players.
local pl= (port-1)*4
for p= 0, n do
FrameList[pl+p][frame]= Immediate[pl+p]
end
end
end
--*****************************************************************************
local function LoadImmediate(frame)
--*****************************************************************************
for pl= 0, 7 do
local temp= FrameList[pl][frame] or 0
temp= bit.bor(
bit.band( temp , ListRead[pl] ),
bit.band(bit.bnot(temp),bit.lrshift(ListRead[pl],24))
)
Immediate[pl]= temp
end
--One more thing! Those keys the user has held? Best handle 'em!
Immediate[PlSel]= bit.bxor(Immediate[PlSel],BtnState[cPort])
end
--#############################################################################
--Set up key commands!
-------------------------------------------------------------------------------
RegisterKeyPress(cmd_SelectPlayer,function(s,t)
-------------------------------------------------------------------------------
-- Select the next player! Increment PlSel and refresh the display!
-- Inconsistently selects an empty port. That wasn't intended. Might fix later.
PlSel= (PlSel+1)%8
cPort= math.floor(PlSel/4)+1
if PlSel%4 >= MaxPlayers[Ports[cPort]] then
if (cPort == 1) and (Ports[cPort] ~= "none") then
PlSel= 4; cPort= 2
else
PlSel= 0; cPort= 1
end
end
ChooseList(Ports[cPort])
MakeListFromScratch()
gui.repaint()
end)
--#############################################################################
--Controller get!
--Just to group them together. So I treat "gamepad" and "multitap16" as equal.
local Ptype= {gamepad=1, gamepad16=1, multitap=1, multitap16=1,
mouse=2, superscope=3, justifier=4, justifiers=4, none=0}
--*****************************************************************************
function on_button(p,c,i,t)
--*****************************************************************************
-- Do not check system. Won't happen, to my knowledge, but just in case...
if p == 0 then return end
-- Do not check analog. Might be a good idea in a later version, though.
if t == "analog" then return end
-- Don't allow hold keys to hold after script ends (and avoid message spam).
-- (Though allow the unhold or untype to clear the state for me)
if (t == "hold") or (t == "type") then input.veto_button() end
-- Block any controller of a port aside from the first...
if (c ~= 0) then return end
-- Block control if mismatched control type for PlSel:
if (cPort ~= p) and (Ptype[Ports[1]] ~= Ptype[Ports[2]]) then return end
-- At this point, the first controller of either port 1 or port 2 goes
-- through. If different controller types are hooked up, the mismatched
-- controller feed is blocked; Only first controller of appropriate port goes.
local bitval= TranslateIndex[Ports[p]](i)
if (t == "hold") or (t == "unhold") then -- Switch movie read options
--Subtract "movie mode" by 1. Do this using XORs.
local temp= ListRead[PlSel]
temp= bit.bxor(temp,bitval)
bitval= bit.band(bitval,temp)
ListRead[PlSel]= bit.bxor(temp,bit.lshift(bitval,24))
gui.repaint()
elseif (t == "type") or (t == "untype") then -- Toggle Immediate.
Immediate[PlSel]= bit.bxor(Immediate[PlSel],bitval)
gui.repaint()
--Looks odd; I'm looking if my bit matches the pressed state. If not, do stuff!
elseif (t == "press") == (bit.band(BtnState[cPort],bitval) == 0) then
Immediate[PlSel]= bit.bxor(Immediate[PlSel],bitval)
BtnState[cPort]= bit.bxor(BtnState[cPort] ,bitval)
gui.repaint()
end
end
--#############################################################################
-- Time to inject Immediate to lsnes!
--*****************************************************************************
local InjectInput= {}
--*****************************************************************************
-------------------------------------------------------------------------------
function InjectInput.gamepad(port)
local IN= Immediate[(port-1)*4]
for i= 0, 11 do
input.set2(port,0,i,bit.extract(IN,i))
end
end
-------------------------------------------------------------------------------
function InjectInput.gamepad16(port)
local IN= Immediate[(port-1)*4]
for i= 0, 15 do
input.set2(port,0,i,bit.extract(IN,i))
end
end
-------------------------------------------------------------------------------
function InjectInput.multitap(port)
for c= 0, 3 do
local IN= Immediate[(port-1)*4 + c]
for i= 0, 11 do
input.set2(port,c,i,bit.extract(IN,i))
end
end
end
-------------------------------------------------------------------------------
function InjectInput.multitap16(port)
for c= 0, 3 do
local IN= Immediate[(port-1)*4 + c]
for i= 0, 15 do
input.set2(port,c,i,bit.extract(IN,i))
end
end
end
-------------------------------------------------------------------------------
function InjectInput.mouse(port)
local IN= Immediate[(port-1)*4]
for i= 0, 1 do
local b= i*8
local val= bit.band(bit.lrshift(IN,b),0x7F)
if bit.extract(IN,b+7) ~= 0 then val= -val end
input.set2(port,0,i,val)
end
input.set2(port,0,2,bit.extract(IN,18))
input.set2(port,0,3,bit.extract(IN,19))
end
-------------------------------------------------------------------------------
InjectInput.superscope= NullFN -- Unsupported garbage. Sorry.
InjectInput.justifier= NullFN
InjectInput.justifiers= NullFN
InjectInput.none= NullFN -- This one really should be null.
--*****************************************************************************
function on_input(subframe)
--*****************************************************************************
for port= 1, 2 do
InjectInput[Ports[port]](port)
end
if not subframe then MarkImmediate() end
end
local IndexOffsets= {
gamepad = 16, gamepad16 = 16, multitap = 16, multitap16= 16,
mouse = 4, superscope= 0, justifier = 0, justifiers= 0,
none = 0
}
--*****************************************************************************
local HandleAnalog= {}
--*****************************************************************************
-------------------------------------------------------------------------------
function HandleAnalog.mouse(pl,i,v)
-------------------------------------------------------------------------------
v= math.max(-127,math.min(127,v)) -- Enforce limits!
-- I don't care if lsnes gets out of range, I'm not following.
if v < 0 then v= bit.bor(-v,0x80) end
local mask= bit.bnot(bit.lshift(0xFF,i*8))
Immediate[pl]= bit.bor(
bit.band(Immediate[pl],mask),
bit.lshift(v,i*8)
)
end
HandleAnalog.superscope= NullFN -- Should be handled at some point.
HandleAnalog.justifier = NullFN
HandleAnalog.justifiers= NullFN
HandleAnalog.gamepad = NullFN -- These have no analog to handle.
HandleAnalog.gamepad16 = NullFN
HandleAnalog.multitap = NullFN
HandleAnalog.multitap16= NullFN
HandleAnalog.none = NullFN
--*****************************************************************************
function on_snoop2(p,c,i,v)
--*****************************************************************************
if p == 0 then return end -- Do not handle system. Not this time.
--Run generic bit stuff!
local PortType= Ports[p]
local bitval= TranslateIndex[PortType](i)
local offset= IndexOffsets[PortType]
local pl= (p-1)*4+c
local bitset= 0; if (v ~= 0) then bitset= 1 end
--Start by setting oversnoop, then clear lag.
local temp= Immediate[pl]
local temp2= bit.lshift(bitval,offset)
temp= bit.bor(temp,bit.lshift(bit.band(bit.bnot(temp),temp2),offset))
temp= bit.band(temp,bit.bnot(temp2))
temp= bit.bor(bit.band(temp,bit.bnot(bitval)),bitset*bitval)
Immediate[pl]= temp
--Now call analog functions
HandleAnalog[PortType](pl,i,v)
end
--#############################################################################
--#############################################################################
-- Error protection sanity functions
--*****************************************************************************
local function HandleFrameJump()
--*****************************************************************************
-- Does not update cf - That's up to the calling function.
-- This function can't know what the proper CurrentFrame is anyway.
LoadImmediate(cf)
MakeListFromScratch()
for pl= 0, 7 do ListRead[pl]=0x000000FFFFFF end -- Revert setup.
gui.repaint()
end
--*****************************************************************************
local function Sanity_cf(non_synth)
--*****************************************************************************
local CurrentFrame= movie.currentframe()
if non_synth then CurrentFrame= CurrentFrame+1 end
if CurrentFrame ~= cf then
--Bad news: A frame jump occured, bypassing other forms of detection
--Should only happen under File -> New -> Movie
--Regardless, this should be handled.
end
end
--*****************************************************************************
local function Sanity_Resolution()
--*****************************************************************************
-- TODO: Ensure that the display fits!
ResX,ResY= gui.resolution()
end
--*****************************************************************************
local function Sanity_Controllers()
--*****************************************************************************
-- Ensures the internals are up to date with changes to controller port.
-- Also prints a warning in the message log.
local sane= true
for i= 1,2 do
local temp= input.port_type(i)
sane= sane and (Ports[i] == temp)
Ports[i]= temp
end
if not sane then
print("MtEdit: WARNING! Controller types have changed!")
-- print("MtEdit: Loaded the wrong state? Started a new movie?")
end
end
--#############################################################################
--#############################################################################
-- lsnes stuff and top-level functions
--*****************************************************************************
function on_frame_emulated()
--*****************************************************************************
cf= movie.currentframe() -- Make certain cf exists!
SaveImmediate(cf)
AdvanceList()
LoadImmediate(cf+1)
cf= cf+1
end
--*****************************************************************************
function on_post_load()
--*****************************************************************************
-- Well, uh... Best fix the stuff!
cf= movie.currentframe()
HandleFrameJump()
end
--*****************************************************************************
function on_rewind()
--*****************************************************************************
cf= movie.currentframe() + 1 -- Rewind puts us back to zero.
HandleFrameJump()
end
--*****************************************************************************
function on_paint(non_synth)
--*****************************************************************************
--Mostly just tell the left and bottom sides to paint themselves.
Sanity_cf(non_synth)
Sanity_Resolution()
BGui()
LGui()
ApplyGaps()
end
--#############################################################################
--#############################################################################
--Execute now.
ChooseList(Ports[1])
MakeListFromScratch()
gui.repaint()
--[[
on_paint |Needed to display, well... Anything!
on_frame_emulated|Lets me know when the frame ends.
on_post_load |Important for detecting a jump.
on_rewind |Also important for detecting a jump.
on_input |Required to hijack control. Or else half the fun is gone.
on_snoop2 |For knowing what lsnes did.
on_keyhook |Important to get at commands for this script.
on_button |Very useful for various controls.
on_reset |Commanding and detecting resets would be handy.
on_set_rewind |Unsafe rewinds? Fits this script really, really well!
on_pre_rewind
on_post_rewind
Then there are these callbacks that MtEdit will likely leave alone.
on_video
on_frame
on_startup
on_pre_load
on_err_load
on_pre_save
on_err_save
on_post_save
on_quit
on_readwrite
on_snoop
on_idle
on_timer
]]--