Amaraticando

lsnes - input displayer (from the Pokemon TAS encode)

May 18th, 2016
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 45.04 KB | None | 0 0
  1. --#############################################################################
  2. -- CONFIG:
  3.  
  4. local OPTIONS = {
  5.     -- Hotkeys  (look at the manual to see all the valid keynames)
  6.     -- make sure that the hotkeys below don't conflict with previous bindings
  7.     hotkey_increase_opacity = "equals",  -- to increase the opacity of the text: the '='/'+' key
  8.     hotkey_decrease_opacity = "minus",   -- to decrease the opacity of the text: the '_'/'-' key
  9.    
  10.     -- Script settings
  11.     use_custom_fonts = true,
  12.     use_movie_editor_tool = true,
  13.    
  14.     -- Lateral gaps (initial values)
  15.     left_gap = 128,
  16.     right_gap = 0,--8,
  17.     top_gap = 0,--20,
  18.     bottom_gap = 0,--8,
  19. }
  20.  
  21. -- Colour settings
  22. local COLOUR = {
  23.     transparency = -1,
  24.    
  25.     -- Text
  26.     default_text_opacity = 1.0,
  27.     default_bg_opacity = 0.4,
  28.     text = 0xffffff,
  29.     background = 0x000000,
  30.     halo = 0x000040,
  31.     warning = 0x00ff0000,
  32.     warning_bg = 0x000000ff,
  33.     warning2 = 0xff00ff,
  34.     weak = 0x00a9a9a9,
  35.     very_weak = 0xa0ffffff,
  36.     button_text = 0x00300030,
  37. }
  38.  
  39. -- TODO: make them global later, to export the module
  40. local LSNES = {}
  41. local ROM_INFO = {}
  42. local CONTROLLER = {}
  43. local MOVIE = {}
  44. local draw = {}
  45. local Widgets_context = gui.renderctx.new(512, 478) -- TEST
  46.  
  47. -- Font settings
  48. LSNES.FONT_HEIGHT = 16
  49. LSNES.FONT_WIDTH = 8
  50. CUSTOM_FONTS = {
  51.         [false] = { file = nil, height = LSNES.FONT_HEIGHT, width = LSNES.FONT_WIDTH }, -- this is lsnes default font
  52.        
  53.         snes9xlua =       { file = [[data/snes9xlua.font]],        height = 16, width = 10 },
  54.         snes9xluaclever = { file = [[data/snes9xluaclever.font]],  height = 16, width = 08 }, -- quite pixelated
  55.         snes9xluasmall =  { file = [[data/snes9xluasmall.font]],   height = 09, width = 05 },
  56.         snes9xtext =      { file = [[data/snes9xtext.font]],       height = 11, width = 08 },
  57.         verysmall =       { file = [[data/verysmall.font]],        height = 08, width = 04 }, -- broken, unless for numerals
  58. }
  59.  
  60. -- Others
  61. local INPUT_RAW_VALUE = "value"  -- name of the inner field in input.raw() for values
  62. local SCRIPT_DEBUG_INFO = false
  63. local LSNES_PAUSED_RUNMODES = {pause = true, pause_break = true, corrupt = true, unknown = true}
  64.  
  65.  
  66. -- END OF CONFIG < < < < < < <
  67. --#############################################################################
  68. -- INITIAL STATEMENTS:
  69.  
  70.  
  71. -- Load environment
  72. local bit, gui, input, movie, memory, memory2 = bit, gui, input, movie, memory, memory2
  73. local string, math, table, next, ipairs, pairs, io, os, type = string, math, table, next, ipairs, pairs, io, os, type
  74.  
  75. -- Script verifies whether the emulator is indeed Lsnes - rr2 version / beta23 or higher
  76. if not lsnes_features or not lsnes_features("text-halos") then
  77.     error("This script works in a newer version of lsnes.")
  78. end
  79.  
  80. -- Text/draw.Bg_global_opacity is only changed by the player using the hotkeys
  81. -- Text/Bg_opacity must be used locally inside the functions
  82. draw.Text_global_opacity = COLOUR.default_text_opacity
  83. draw.Bg_global_opacity = COLOUR.default_bg_opacity
  84. draw.Text_local_opacity = 1
  85. draw.Bg_local_opacity = 1
  86.  
  87. -- Verify whether the fonts exist and create the custom fonts' drawing function
  88. draw.font = {}
  89. for font_name, value in pairs(CUSTOM_FONTS) do
  90.     if value.file and not io.open(value.file) then
  91.         print("WARNING:", string.format("./%s is missing.", value.file))
  92.         CUSTOM_FONTS[font_name] = nil
  93.     else
  94.         draw.font[font_name] = font_name and gui.font.load(value.file) or gui.text
  95.     end
  96. end
  97.  
  98. local fmt = string.format
  99.  
  100.  
  101. --#############################################################################
  102. -- SCRIPT UTILITIES:
  103.  
  104.  
  105. -- Variables used in various functions
  106. local Previous = {}
  107. local User_input = {}
  108.  
  109.  
  110. -- unsigned to signed (based in <bits> bits)
  111. local function signed(num, bits)
  112.     local maxval = 1<<(bits - 1)
  113.     if num < maxval then return num else return num - 2*maxval end
  114. end
  115.  
  116.  
  117. -- Transform the binary representation of base into a string
  118. -- For instance, if each bit of a number represents a char of base, then this function verifies what chars are on
  119. local function decode_bits(data, base)
  120.     local i = 1
  121.     local size = base:len()
  122.     local direct_concatenation = size <= 45  -- Performance: I found out that the .. operator is faster for 45 operations or less
  123.     local result
  124.    
  125.     if direct_concatenation then
  126.         result = ""
  127.         for ch in base:gmatch(".") do
  128.             if bit.test(data, size - i) then
  129.                 result = result .. ch
  130.             else
  131.                 result = result .. " "
  132.             end
  133.             i = i + 1
  134.         end
  135.     else
  136.         result = {}
  137.         for ch in base:gmatch(".") do
  138.             if bit.test(data, size-i) then
  139.                 result[i] = ch
  140.             else
  141.                 result[i] = " "
  142.             end
  143.             i = i + 1
  144.         end
  145.         result = table.concat(result)
  146.     end
  147.    
  148.     return result
  149. end
  150.  
  151.  
  152. local function mouse_onregion(x1, y1, x2, y2)
  153.     -- Reads external mouse coordinates
  154.     local mouse_x = User_input.mouse_x
  155.     local mouse_y = User_input.mouse_y
  156.    
  157.     -- From top-left to bottom-right
  158.     if x2 < x1 then
  159.         x1, x2 = x2, x1
  160.     end
  161.     if y2 < y1 then
  162.         y1, y2 = y2, y1
  163.     end
  164.    
  165.     if mouse_x >= x1 and mouse_x <= x2 and  mouse_y >= y1 and mouse_y <= y2 then
  166.         return true
  167.     else
  168.         return false
  169.     end
  170. end
  171.  
  172.  
  173. -- Those 'Keys functions' register presses and releases. Pretty much a copy from the script of player Fat Rat Knight (FRK)
  174. -- http://tasvideos.org/userfiles/info/5481697172299767
  175. Keys = {}
  176. Keys.KeyPress=   {}
  177. Keys.KeyRelease= {}
  178.  
  179. function Keys.registerkeypress(key,fn)
  180. -- key - string. Which key do you wish to bind?
  181. -- fn  - function. To execute on key press. False or nil removes it.
  182. -- Return value: The old function previously assigned to the key.
  183.  
  184.     local OldFn= Keys.KeyPress[key]
  185.     Keys.KeyPress[key]= fn
  186.     --Keys.KeyPress[key]= Keys.KeyPress[key] or {}
  187.     --table.insert(Keys.KeyPress[key], fn)
  188.     input.keyhook(key,type(fn or Keys.KeyRelease[key]) == "function")
  189.     return OldFn
  190. end
  191.  
  192.  
  193. function Keys.registerkeyrelease(key,fn)
  194. -- key - string. Which key do you wish to bind?
  195. -- fn  - function. To execute on key release. False or nil removes it.
  196. -- Return value: The old function previously assigned to the key.
  197.  
  198.     local OldFn= Keys.KeyRelease[key]
  199.     Keys.KeyRelease[key]= fn
  200.     input.keyhook(key,type(fn or Keys.KeyPress[key]) == "function")
  201.     return OldFn
  202. end
  203.  
  204.  
  205. function Keys.altkeyhook(s,t)
  206. -- s,t - input expected is identical to on_keyhook input. Also passed along.
  207. -- You may set by this line: on_keyhook = Keys.altkeyhook
  208. -- Only handles keyboard input. If you need to handle other inputs, you may
  209. -- need to have your own on_keyhook function to handle that, but you can still
  210. -- call this when generic keyboard handling is desired.
  211.  
  212.     if     Keys.KeyPress[s]   and (t[INPUT_RAW_VALUE] == 1) then
  213.         Keys.KeyPress[s](s,t)
  214.     elseif Keys.KeyRelease[s] and (t[INPUT_RAW_VALUE] == 0) then
  215.         Keys.KeyRelease[s](s,t)
  216.     end
  217. end
  218.  
  219.  
  220. -- Stores the raw input in a table for later use. Should be called at the start of paint and timer callbacks
  221. local function read_raw_input()
  222.     for key, inner in pairs(input.raw()) do
  223.         User_input[key] = inner[INPUT_RAW_VALUE]
  224.     end
  225.     User_input.mouse_x = math.floor(User_input.mouse_x)
  226.     User_input.mouse_y = math.floor(User_input.mouse_y)
  227. end
  228.  
  229.  
  230. -- Extensions to the "gui" function, to handle fonts and opacity
  231. draw.Font_name = false
  232.  
  233.  
  234. function draw.opacity(text, bg)
  235.     draw.Text_local_opacity = text or draw.Text_local_opacity
  236.     draw.Bg_local_opacity = bg or draw.Bg_local_opacity
  237.    
  238.     return draw.Text_local_opacity, draw.Bg_local_opacity
  239. end
  240.  
  241.  
  242. function draw.font_width(font)
  243.     font = font or Font  -- TODO: change Font to draw.Font_name ?
  244.     return CUSTOM_FONTS[font] and CUSTOM_FONTS[font].width or LSNES.FONT_WIDTH
  245. end
  246.  
  247.  
  248. function draw.font_height(font)
  249.     font = font or Font
  250.     return CUSTOM_FONTS[font] and CUSTOM_FONTS[font].height or LSNES.FONT_HEIGHT
  251. end
  252.  
  253.  
  254. function LSNES.get_rom_info()
  255.     ROM_INFO.is_loaded = movie.rom_loaded()
  256.     if ROM_INFO.is_loaded then
  257.         -- ROM info
  258.         local movie_info = movie.get_rom_info()
  259.         ROM_INFO.slots = #movie_info
  260.         ROM_INFO.hint = movie_info[1].hint
  261.         ROM_INFO.hash = movie_info[1].sha256
  262.        
  263.         -- Game info
  264.         local game_info = movie.get_game_info()
  265.         ROM_INFO.game_type = game_info.gametype
  266.         ROM_INFO.game_fps = game_info.fps
  267.     else
  268.         -- ROM info
  269.         ROM_INFO.slots = 0
  270.         ROM_INFO.hint = false
  271.         ROM_INFO.hash = false
  272.        
  273.         -- Game info
  274.         ROM_INFO.game_type = false
  275.         ROM_INFO.game_fps = false
  276.     end
  277.    
  278.     ROM_INFO.info_loaded = true
  279.     print"> Read rom info"
  280. end
  281.  
  282.  
  283. function LSNES.get_controller_info()
  284.     local info = CONTROLLER
  285.    
  286.     info.ports = {}
  287.     info.total_ports = 0
  288.     info.total_controllers = 0
  289.     info.total_buttons = 0
  290.     info.complete_input_sequence = ""  -- the sequence of buttons/axis for background in the movie editor
  291.     info.total_width = 0  -- how many horizontal cells are necessary to draw the complete input
  292.     info.button_pcid = {}  -- array that maps the n-th button of the sequence to its port/controller/button index
  293.    
  294.     for port = 0, 2 do  -- SNES
  295.         info.ports[port] = input.port_type(port)
  296.         if not info.ports[port] then break end
  297.         info.total_ports = info.total_ports + 1
  298.     end
  299.    
  300.     for lcid = 1, 8 do  -- SNES
  301.         local port, controller = input.lcid_to_pcid2(lcid)
  302.         local ci = (port and controller) and input.controller_info(port, controller) or nil
  303.        
  304.         if ci then
  305.             info[lcid] = {port = port, controller = controller}
  306.             info[lcid].type = ci.type
  307.             info[lcid].class = ci.class
  308.             info[lcid].classnum = ci.classnum
  309.             info[lcid].button_count = ci.button_count
  310.             info[lcid].symbol_sequence = ""
  311.             info[lcid].controller_width = 0
  312.            
  313.             for button, inner in pairs(ci.buttons) do
  314.                 -- button level
  315.                 info[lcid][button] = {}
  316.                 info[lcid][button].type = inner.type
  317.                 info[lcid][button].name = inner.name
  318.                
  319.                 -- TEST for extra characters
  320.                 if inner.symbol == "↑" then inner.symbol = "^" end
  321.                 if inner.symbol == "↓" then inner.symbol = "v" end
  322.                 if inner.symbol == "←" then inner.symbol = "<" end
  323.                 if inner.symbol == "→" then inner.symbol = ">" end
  324.                 -- END
  325.                
  326.                 info[lcid][button].symbol= inner.symbol
  327.                
  328.                 info[lcid][button].hidden = inner.hidden
  329.                 info[lcid][button].button_width = inner.symbol and 1 or 1  -- TODO: 'or 7' for axis
  330.                
  331.                 -- controller level
  332.                 info[lcid].controller_width = info[lcid].controller_width + info[lcid][button].button_width
  333.                 info[lcid].symbol_sequence = info[lcid].symbol_sequence .. (inner.symbol or " ")  -- TODO: axis: 7 spaces
  334.                
  335.                 -- port level (nothing)
  336.                
  337.                 -- input level
  338.                 info.button_pcid[#info.button_pcid + 1] = {port = port, controller = controller, button = button}
  339.             end
  340.            
  341.             -- input level
  342.             info.total_buttons = info.total_buttons + info[lcid].button_count
  343.             info.total_controllers = info.total_controllers + 1
  344.             info.total_width = info.total_width + info[lcid].controller_width
  345.             info.complete_input_sequence = info.complete_input_sequence .. info[lcid].symbol_sequence
  346.            
  347.         else break
  348.         end
  349.     end
  350.    
  351.     -- debug info
  352.     if SCRIPT_DEBUG_INFO then
  353.         print" - - - - CONTROLLER debug info: - - - - "
  354.         print"Ports:"
  355.         print("total = " .. info.total_ports)
  356.         for a,b in pairs(info.ports) do
  357.             print("", a, b)
  358.         end
  359.        
  360.         print("Controllers:")
  361.         print("total = " .. info.total_controllers)
  362.         for lcid = 1, info.total_controllers do
  363.             local tb = info[lcid]
  364.             print("", lcid .. " :")
  365.             print("", "", "pcid: " .. tb.port .. ", " .. tb.controller)
  366.             print("", "", string.format("class: %s #%d, type: %s", tb.class, tb.classnum, tb.type))
  367.             print("", "", string.format("%d buttons: %s (%d cells)", tb.button_count, tb.symbol_sequence, tb.controller_width))
  368.            
  369.             print("", "", "Buttons:")
  370.             for button, inner in ipairs(tb) do
  371.                 print("", "", string.format("%d: %s (%s), cell width: %d , type: %s, hidden: %s",
  372.                 button, inner.name, inner.symbol or "no symbol", inner.button_width, inner.type, inner.hidden))
  373.             end
  374.         end
  375.        
  376.         print("Some input utilities:")
  377.         print("", string.format("%d buttons, forming: %s", info.total_buttons, info.complete_input_sequence))
  378.         print("Individual button mapping:")
  379.         for a,b in ipairs(info.button_pcid) do
  380.             print("", string.format("%d -> %d, %d, %d", a, b.port, b.controller, b.button))
  381.         end
  382.     end
  383.    
  384.     info.info_loaded = true
  385.     print"> Read controller info"
  386. end
  387.  
  388.  
  389. function LSNES.get_movie_info()
  390.     LSNES.pollcounter = movie.pollcounter(0, 0, 0)
  391.    
  392.     -- DEBUG
  393.     if LSNES.frame_boundary ~= "middle" and LSNES.Runmode == "pause_break" then error"Frame boundary: middle case not accounted!" end
  394.    
  395.     MOVIE.readonly = movie.readonly()
  396.     MOVIE.framecount = movie.framecount()
  397.     MOVIE.subframe_count = movie.get_size()
  398.     MOVIE.lagcount = movie.lagcount()
  399.     MOVIE.rerecords = movie.rerecords()
  400.    
  401.     -- CURRENT
  402.     MOVIE.current_frame = movie.currentframe() + ((LSNES.frame_boundary == "end") and 1 or 0)
  403.     if MOVIE.current_frame == 0 then MOVIE.current_frame = 1 end  -- after the rewind, the currentframe isn't updated to 1
  404.    
  405.     MOVIE.current_poll = (LSNES.frame_boundary ~= "middle") and 1 or LSNES.pollcounter + 1
  406.     -- TODO: this should be incremented after all the buttons have been polled
  407.    
  408.     MOVIE.size_past_frame = LSNES.size_frame(MOVIE.current_frame - 1)  -- glitch: the order of calling size_frame impacts in performance
  409.     MOVIE.size_current_frame = LSNES.size_frame(MOVIE.current_frame)  -- how many subframes of current frames are stored in the movie
  410.     MOVIE.last_frame_started_movie = MOVIE.current_frame - (LSNES.frame_boundary == "middle" and 0 or 1) --test
  411.     if MOVIE.last_frame_started_movie <= MOVIE.framecount then
  412.         MOVIE.current_starting_subframe = movie.current_first_subframe() + 1
  413.         if LSNES.frame_boundary == "end" then
  414.             MOVIE.current_starting_subframe = MOVIE.current_starting_subframe + MOVIE.size_past_frame  -- movie.current_first_subframe() isn't updated
  415.         end                                                                                        -- until the frame boundary is "start"
  416.     else
  417.         MOVIE.current_starting_subframe = MOVIE.subframe_count + (MOVIE.current_frame - MOVIE.framecount)
  418.     end
  419.    
  420.     if MOVIE.size_current_frame == 0 then MOVIE.size_current_frame = 1 end
  421.     MOVIE.current_internal_subframe = (MOVIE.current_poll > MOVIE.size_current_frame) and MOVIE.size_current_frame or MOVIE.current_poll
  422.     MOVIE.current_subframe = MOVIE.current_starting_subframe + MOVIE.current_internal_subframe - 1
  423.     -- for frames with subframes, but not written in the movie
  424.    
  425.     -- PAST SUBFRAME
  426.     MOVIE.frame_of_past_subframe = MOVIE.current_frame - (MOVIE.current_internal_subframe == 1 and 1 or 0)
  427.    
  428.     -- TEST INPUT
  429.     MOVIE.last_input_computed = LSNES.get_input(MOVIE.subframe_count)
  430. end
  431.  
  432.  
  433. function LSNES.debug_movie()
  434.     local x, y = 150, 100
  435.    
  436.     draw.text(x, y, "subframe_update: " .. tostringx(LSNES.subframe_update))
  437.     y = y + 16
  438.     draw.text(x, y, string.format("currentframe: %d, framecount: %d, count_frames: %d",  movie.currentframe(), movie.framecount(),  movie.count_frames()))
  439.     y = y + 16
  440.     draw.text(x, y, string.format("get_size: %d",  movie.get_size()))
  441.     y = y + 16
  442.     draw.text(x, y, "current_first_subframe: " .. movie.current_first_subframe())
  443.     y = y + 16
  444.     draw.text(x, y, "pollcounter: " .. movie.pollcounter(0, 0, 0))
  445.     y = y + 16
  446.     draw.text(x, y, LSNES.frame_boundary)
  447.     y = y + 16
  448.    
  449.     for a, b in pairs(MOVIE) do
  450.         gui.text(x, y, string.format("%s %s", a, tostring(b)), 'yellow', 0x80000000)
  451.         y = y + 16
  452.     end
  453.     --[[
  454.     x = 200
  455.     y = 16
  456.     local colour = {[1] = 0xffff00, [2] = 0x00ff00}
  457.     for controller = 0, 3 do
  458.         for control = 0, 15 do
  459.             if y >= 432 then
  460.                 y = 16
  461.                 x = x + 48
  462.             end
  463.             draw.text(x, y, control .. " " .. movie.pollcounter(0, controller, control), 0xff0000, 0x20000000)
  464.             y = y + 16
  465.         end
  466.     end
  467.     for port = 1, 2 do
  468.         for controller = 0, 3 do
  469.             for control = 0, 15 do
  470.                 if y >= 432 then
  471.                     y = 16
  472.                     x = x + 48
  473.                 end
  474.                 draw.text(x, y, control .. " " .. movie.pollcounter(port, controller, control), colour[(2*port + controller)%2 + 1], 0x20000000)
  475.                 y = y + 16
  476.             end
  477.         end
  478.     end
  479.     --]]
  480. end
  481.  
  482.  
  483. function LSNES.get_screen_info()
  484.     -- Effective script gaps
  485.     LSNES.left_gap = math.max(OPTIONS.left_gap, LSNES.FONT_WIDTH*(CONTROLLER.total_width + 6)) -- for movie editor
  486.     LSNES.right_gap = LSNES.right_gap or OPTIONS.right_gap
  487.     LSNES.top_gap = LSNES.top_gap or OPTIONS.top_gap
  488.     LSNES.bottom_gap = LSNES.bottom_gap or OPTIONS.bottom_gap
  489.    
  490.     -- Advanced configuration: padding dimensions
  491.     LSNES.Padding_left = tonumber(settings.get("left-border"))
  492.     LSNES.Padding_right = tonumber(settings.get("right-border"))
  493.     LSNES.Padding_top = tonumber(settings.get("top-border"))
  494.     LSNES.Padding_bottom = tonumber(settings.get("bottom-border"))
  495.    
  496.     -- Borders' dimensions
  497.     LSNES.Border_left = math.max(LSNES.Padding_left, LSNES.left_gap)  -- for movie editor
  498.     LSNES.Border_right = math.max(LSNES.Padding_right, OPTIONS.right_gap)
  499.     LSNES.Border_top = math.max(LSNES.Padding_top, OPTIONS.top_gap)
  500.     LSNES.Border_bottom = math.max(LSNES.Padding_bottom, OPTIONS.bottom_gap)
  501.    
  502.     LSNES.Buffer_width, LSNES.Buffer_height = gui.resolution()  -- Game area
  503.     if LSNES.Video_callback then  -- The video callback messes with the resolution
  504.         LSNES.Buffer_middle_x, LSNES.Buffer_middle_y = LSNES.Buffer_width, LSNES.Buffer_height
  505.         LSNES.Buffer_width = 2*LSNES.Buffer_width
  506.         LSNES.Buffer_height = 2*LSNES.Buffer_height
  507.     else
  508.         LSNES.Buffer_middle_x, LSNES.Buffer_middle_y = LSNES.Buffer_width//2, LSNES.Buffer_height//2  -- Lua 5.3
  509.     end
  510.    
  511.     LSNES.Screen_width = LSNES.Buffer_width + LSNES.Border_left + LSNES.Border_right  -- Emulator area
  512.     LSNES.Screen_height = LSNES.Buffer_height + LSNES.Border_top + LSNES.Border_bottom
  513.     LSNES.AR_x = 2
  514.     LSNES.AR_y = 2
  515. end
  516.  
  517.  
  518. -- Changes transparency of a color: result is opaque original * transparency level (0.0 to 1.0). Acts like gui.opacity() in Snes9x.
  519. function draw.change_transparency(color, transparency)
  520.     -- Sane transparency
  521.     if transparency >= 1 then return color end  -- no transparency
  522.     if transparency <= 0 then return COLOUR.transparency end    -- total transparency
  523.    
  524.     -- Sane colour
  525.     if color == -1 then return -1 end
  526.    
  527.     local a = color>>24  -- Lua 5.3
  528.     local rgb = color - (a<<24)
  529.     local new_a = 0x100 - math.ceil((0x100 - a)*transparency)
  530.     return (new_a<<24) + rgb
  531. end
  532.  
  533.  
  534. -- Takes a position and dimensions of a rectangle and returns a new position if this rectangle has points outside the screen
  535. local function put_on_screen(x, y, width, height)
  536.     local x_screen, y_screen
  537.     width = width or 0
  538.     height = height or 0
  539.    
  540.     if x < - Border_left then
  541.         x_screen = - Border_left
  542.     elseif x > Buffer_width + Border_right - width then
  543.         x_screen = Buffer_width + Border_right - width
  544.     else
  545.         x_screen = x
  546.     end
  547.    
  548.     if y < - Border_top then
  549.         y_screen = - Border_top
  550.     elseif y > Buffer_height + Border_bottom - height then
  551.         y_screen = Buffer_height + Border_bottom - height
  552.     else
  553.         y_screen = y
  554.     end
  555.    
  556.     return x_screen, y_screen
  557. end
  558.  
  559.  
  560. -- returns the (x, y) position to start the text and its length:
  561. -- number, number, number text_position(x, y, text, font_width, font_height[[[[, always_on_client], always_on_game], ref_x], ref_y])
  562. -- x, y: the coordinates that the refereed point of the text must have
  563. -- text: a string, don't make it bigger than the buffer area width and don't include escape characters
  564. -- font_width, font_height: the sizes of the font
  565. -- always_on_client, always_on_game: boolean
  566. -- ref_x and ref_y: refer to the relative point of the text that must occupy the origin (x,y), from 0% to 100%
  567. --                  for instance, if you want to display the middle of the text in (x, y), then use 0.5, 0.5
  568. function draw.text_position(x, y, text, font_width, font_height, always_on_client, always_on_game, ref_x, ref_y)
  569.     -- Reads external variables
  570.     local border_left     = LSNES.Border_left
  571.     local border_right    = LSNES.Border_right
  572.     local border_top      = LSNES.Border_top
  573.     local border_bottom   = LSNES.Border_bottom
  574.     local buffer_width    = LSNES.Buffer_width
  575.     local buffer_height   = LSNES.Buffer_height
  576.    
  577.     -- text processing
  578.     local text_length = text and string.len(text)*font_width or font_width  -- considering another objects, like bitmaps
  579.    
  580.     -- actual position, relative to game area origin
  581.     x = ((not ref_x or ref_x == 0) and x) or x - math.floor(text_length*ref_x)
  582.     y = ((not ref_y or ref_y == 0) and y) or y - math.floor(font_height*ref_y)
  583.    
  584.     -- adjustment needed if text is supposed to be on screen area
  585.     local x_end = x + text_length
  586.     local y_end = y + font_height
  587.    
  588.     if always_on_game then
  589.         if x < 0 then x = 0 end
  590.         if y < 0 then y = 0 end
  591.        
  592.         if x_end > buffer_width  then x = buffer_width  - text_length end
  593.         if y_end > buffer_height then y = buffer_height - font_height end
  594.        
  595.     elseif always_on_client then
  596.         if x < -border_left then x = -border_left end
  597.         if y < -border_top  then y = -border_top  end
  598.        
  599.         if x_end > buffer_width  + border_right  then x = buffer_width  + border_right  - text_length end
  600.         if y_end > buffer_height + border_bottom then y = buffer_height + border_bottom - font_height end
  601.     end
  602.    
  603.     return x, y, text_length
  604. end
  605.  
  606.  
  607. -- Complex function for drawing, that uses text_position
  608. function draw.text(x, y, text, text_color, bg_color, halo_color, always_on_client, always_on_game, ref_x, ref_y)
  609.     -- Read external variables
  610.     local font_name = draw.Font_name or false
  611.     local font_width  = draw.font_width()
  612.     local font_height = draw.font_height()
  613.     text_color = text_color or COLOUR.text
  614.     bg_color = bg_color or -1--COLOUR.background -- EDIT
  615.     halo_color = halo_color or -1 --COLOUR.halo -- EDIT
  616.    
  617.     -- Apply transparency
  618.     text_color = draw.change_transparency(text_color, draw.Text_global_opacity * draw.Text_local_opacity)
  619.     bg_color = draw.change_transparency(bg_color, draw.Bg_global_opacity * draw.Bg_local_opacity)
  620.     halo_color = draw.change_transparency(halo_color, draw.Text_global_opacity * draw.Text_local_opacity)
  621.    
  622.     -- Calculate actual coordinates and plot text
  623.     local x_pos, y_pos, length = draw.text_position(x, y, text, font_width, font_height,
  624.                                     always_on_client, always_on_game, ref_x, ref_y)
  625.     ;
  626.     draw.font[font_name](x_pos, y_pos, text, text_color, bg_color, halo_color)
  627.    
  628.     return x_pos + length, y_pos + font_height, length
  629. end
  630.  
  631.  
  632. function draw.alert_text(x, y, text, text_color, bg_color, always_on_game, ref_x, ref_y)
  633.     -- Reads external variables
  634.     local font_width  = LSNES.FONT_WIDTH
  635.     local font_height = LSNES.FONT_HEIGHT
  636.    
  637.     local x_pos, y_pos, text_length = draw.text_position(x, y, text, font_width, font_height, false, always_on_game, ref_x, ref_y)
  638.    
  639.     text_color = draw.change_transparency(text_color, draw.Text_global_opacity * draw.Text_local_opacity)
  640.     bg_color = draw.change_transparency(bg_color, draw.Bg_global_opacity * draw.Bg_local_opacity)
  641.     gui.text(x_pos, y_pos, text, text_color, bg_color)
  642.    
  643.     return x_pos + text_length, y_pos + font_height, text_length
  644. end
  645.  
  646.  
  647. local function draw_over_text(x, y, value, base, color_base, color_value, color_bg, always_on_client, always_on_game, ref_x, ref_y)
  648.     value = decode_bits(value, base)
  649.     local x_end, y_end, length = draw.text(x, y, base, color_base, color_bg, nil, always_on_client, always_on_game, ref_x, ref_y)
  650.     draw.font[Font](x_end - length, y_end - draw.font_height(), value, color_value or COLOUR.text)
  651.    
  652.     return x_end, y_end, length
  653. end
  654.  
  655.  
  656. -- displays a button everytime in (x,y)
  657. -- object can be a text or a dbitmap
  658. -- if user clicks onto it, fn is executed once
  659. draw.buttons_table = {}
  660. function draw.button(x, y, object, fn, extra_options)
  661.     local always_on_client, always_on_game, ref_x, ref_y, button_pressed
  662.     if extra_options then
  663.         always_on_client, always_on_game, ref_x, ref_y, button_pressed =
  664.         extra_options.always_on_client, extra_options.always_on_game, extra_options.ref_x, extra_options.ref_y, extra_options.button_pressed
  665.     end
  666.    
  667.     local width, height
  668.     local object_type = type(object)
  669.    
  670.     if object_type == "string" then
  671.         width, height = draw.font_width(), draw.font_height()
  672.         x, y, width = draw.text_position(x, y, object, width, height, always_on_client, always_on_game, ref_x, ref_y)
  673.     elseif object_type == "userdata" then  -- lsnes specific
  674.         width, height = object:size()
  675.         x, y = draw.text_position(x, y, nil, width, height, always_on_client, always_on_game, ref_x, ref_y)
  676.     elseif object_type == "boolean" then
  677.         width, height = LSNES_FONT_WIDTH, LSNES_FONT_HEIGHT
  678.         x, y = draw.text_position(x, y, nil, width, height, always_on_client, always_on_game, ref_x, ref_y)
  679.     else error"Type of buttton not supported yet"
  680.     end
  681.    
  682.     -- draw the button
  683.     if button_pressed then
  684.         gui.box(x, y, width, height, 1, 0x808080, 0xffffff, 0xe0e0e0) -- unlisted colour
  685.     else
  686.         gui.box(x, y, width, height, 1)
  687.     end
  688.    
  689.     if object_type == "string" then
  690.         draw.text(x, y, object, COLOUR.button_text, -1, -1)  -- EDIT
  691.     elseif object_type == "userdata" then
  692.         object:draw(x, y)
  693.     elseif object_type == "boolean" then
  694.         gui.solidrectangle(x +1, y + 1, width - 2, height - 2, 0x00ff00)  -- unlisted colour
  695.     end
  696.    
  697.     -- updates the table of buttons
  698.     table.insert(draw.buttons_table, {x = x, y = y, width = width, height = height, object = object, action = fn})
  699. end
  700.  
  701.  
  702. -- Returns frames-time conversion
  703. local function frame_time(frame)
  704.     if not ROM_INFO.info_loaded or not ROM_INFO.is_loaded then return "no time" end
  705.    
  706.     local total_seconds = frame / ROM_INFO.game_fps
  707.     local hours, minutes, seconds = bit.multidiv(total_seconds, 3600, 60)
  708.     seconds = math.floor(seconds)
  709.    
  710.     local miliseconds = 1000* (total_seconds%1)
  711.     if hours == 0 then hours = "" else hours = string.format("%d:", hours) end
  712.     local str = string.format("%s%.2d:%.2d.%03.0f", hours, minutes, seconds, miliseconds)
  713.     return str
  714. end
  715.  
  716.  
  717. -- draw a pixel given (x,y) with SNES' pixel sizes
  718. function draw.pixel(x, y, color, shadow)
  719.     if shadow and shadow ~= COLOUR.transparent then
  720.         gui.rectangle(x*LSNES.AR_x - 1, y*LSNES.AR_y - 1, 2*LSNES.AR_x, 2*LSNES.AR_y, 1, shadow, color)
  721.     else
  722.         gui.solidrectangle(x*LSNES.AR_x, y*LSNES.AR_y, LSNES.AR_x, LSNES.AR_y, color)
  723.     end
  724. end
  725.  
  726.  
  727. -- draws a line given (x,y) and (x',y') with given scale and SNES' pixel thickness
  728. function draw.line(x1, y1, x2, y2, color)
  729.     x1, y1, x2, y2 = x1*LSNES.AR_x, y1*LSNES.AR_y, x2*LSNES.AR_x, y2*LSNES.AR_y
  730.     if x1 == x2 then
  731.         gui.line(x1, y1, x2, y2 + 1, color)
  732.         gui.line(x1 + 1, y1, x2 + 1, y2 + 1, color)
  733.     elseif y1 == y2 then
  734.         gui.line(x1, y1, x2 + 1, y2, color)
  735.         gui.line(x1, y1 + 1, x2 + 1, y2 + 1, color)
  736.     else
  737.         gui.line(x1, y1, x2 + 1, y2 + 1, color)
  738.     end
  739. end
  740.  
  741.  
  742. -- draws a box given (x,y) and (x',y') with SNES' pixel sizes
  743. function draw.box(x1, y1, x2, y2, ...)
  744.     -- Draw from top-left to bottom-right
  745.     if x2 < x1 then
  746.         x1, x2 = x2, x1
  747.     end
  748.     if y2 < y1 then
  749.         y1, y2 = y2, y1
  750.     end
  751.    
  752.     gui.rectangle(x1*LSNES.AR_x, y1*LSNES.AR_y, (x2 - x1 + 1)*LSNES.AR_x, (y2 - y1 + 1)*LSNES.AR_x, LSNES.AR_x, ...)
  753. end
  754.  
  755.  
  756. -- draws a rectangle given (x,y) and dimensions, with SNES' pixel sizes
  757. function draw.rectangle(x, y, w, h, ...)
  758.     gui.rectangle(x*LSNES.AR_x, y*LSNES.AR_y, w*LSNES.AR_x, h*LSNES.AR_y, LSNES.AR_x, ...)
  759. end
  760.  
  761.  
  762. -- Background opacity functions
  763. function draw.increase_opacity()
  764.     if draw.Text_global_opacity <= 0.9 then draw.Text_global_opacity = draw.Text_global_opacity + 0.1
  765.     else
  766.         if draw.Bg_global_opacity <= 0.9 then draw.Bg_global_opacity = draw.Bg_global_opacity + 0.1 end
  767.     end
  768. end
  769.  
  770.  
  771. function draw.decrease_opacity()
  772.     if  draw.Bg_global_opacity >= 0.1 then draw.Bg_global_opacity = draw.Bg_global_opacity - 0.1
  773.     else
  774.         if draw.Text_global_opacity >= 0.1 then draw.Text_global_opacity = draw.Text_global_opacity - 0.1 end
  775.     end
  776. end
  777.  
  778.  
  779. -- Creates lateral gaps
  780. local function create_gaps()
  781.     gui.left_gap(LSNES.left_gap)  -- for input display -- TEST
  782.     gui.right_gap(LSNES.right_gap)
  783.     gui.top_gap(LSNES.top_gap)
  784.     gui.bottom_gap(LSNES.bottom_gap)
  785. end
  786.  
  787.  
  788. local function show_movie_info()
  789.     -- Font
  790.     draw.Font_name = false
  791.     draw.opacity(1.0, 1.0)
  792.    
  793.     local y_text = - LSNES.Border_top
  794.     local x_text = 0
  795.     local width = draw.font_width()
  796.    
  797.     local rec_color = MOVIE.readonly and COLOUR.text or COLOUR.warning
  798.     local recording_bg = MOVIE.readonly and COLOUR.background or COLOUR.warning_bg
  799.    
  800.     -- Read-only or read-write?
  801.     local movie_type = MOVIE.readonly and "Movie " or "REC "
  802.     x_text = draw.alert_text(x_text, y_text, movie_type, rec_color, recording_bg)
  803.    
  804.     -- Frame count
  805.     local movie_info
  806.     if MOVIE.readonly then
  807.         movie_info = string.format("%d/%d", MOVIE.last_frame_started_movie, MOVIE.framecount)
  808.     else
  809.         movie_info = MOVIE.last_frame_started_movie
  810.     end
  811.     x_text = draw.text(x_text, y_text, movie_info)  -- Shows the latest frame emulated, not the frame being run now
  812.    
  813.     -- Rerecord and lag count
  814.     x_text = draw.text(x_text, y_text, string.format("|%d ", MOVIE.rerecords), COLOUR.weak)
  815.     x_text = draw.text(x_text, y_text, MOVIE.lagcount, COLOUR.warning)
  816.    
  817.     -- Run mode and emulator speed
  818.     local lsnesmode_info
  819.     if LSNES.Lsnes_speed == "turbo" then
  820.         lsnesmode_info = fmt(" %s(%s)", LSNES.Runmode, LSNES.Lsnes_speed)
  821.     elseif LSNES.Lsnes_speed ~= 1 then
  822.         lsnesmode_info = fmt(" %s(%.0f%%)", LSNES.Runmode, 100*LSNES.Lsnes_speed)
  823.     else
  824.         lsnesmode_info = fmt(" %s", LSNES.Runmode)
  825.     end
  826.    
  827.     x_text = draw.text(x_text, y_text, lsnesmode_info, COLOUR.weak)
  828.    
  829.     local str = frame_time(MOVIE.last_frame_started_movie)    -- Shows the latest frame emulated, not the frame being run now
  830.     draw.alert_text(LSNES.Buffer_width, LSNES.Buffer_height, str, COLOUR.text, recording_bg, false, 1.0, 1.0)
  831.    
  832.     if LSNES.Is_lagged then
  833.         gui.textHV(LSNES.Buffer_middle_x - 3*LSNES.FONT_WIDTH, 2*LSNES.FONT_HEIGHT, "Lag", COLOUR.warning, draw.change_transparency(COLOUR.warning_bg, draw.Bg_global_opacity))
  834.     end
  835.    
  836. end
  837.  
  838.  
  839. function LSNES.size_frame(frame)
  840.     return frame > 0 and movie.frame_subframes(frame) or -1
  841. end
  842.  
  843.  
  844. function LSNES.get_input(subframe)
  845.     local total = MOVIE.subframe_count or movie.get_size()
  846.    
  847.     return (subframe <= total and subframe > 0) and movie.get_frame(subframe - 1) or false
  848. end
  849.  
  850.  
  851. function LSNES.set_input(subframe, data)
  852.     local total = MOVIE.subframe_count or movie.get_size()
  853.     local current_subframe = MOVIE.current_subframe
  854.    
  855.     if subframe <= total and subframe > current_subframe then
  856.         movie.set_frame(subframe - 1, data)
  857.     --[[
  858.     elseif subframe == current_subframe then
  859.         local lcid =
  860.         input.joyset(lcid, )
  861.     --]]
  862.     end
  863. end
  864.  
  865.  
  866. function LSNES.treat_input(input_obj)
  867.     local presses = {}
  868.     local index = 1
  869.     local number_controls = CONTROLLER.total_controllers
  870.     for lcid = 1, number_controls do
  871.         local port, cnum = CONTROLLER[lcid].port, CONTROLLER[lcid].controller
  872.         local is_gamepad = CONTROLLER[lcid].class == "gamepad"
  873.        
  874.         -- Currently shows all ports and controllers
  875.         for control = 1, CONTROLLER[lcid].button_count do
  876.             local button_value, str
  877.             if is_gamepad or control > 2 then  -- only the first 2 buttons can be axis
  878.                 button_value = input_obj:get_button(port, cnum, control-1)
  879.                 str = button_value and CONTROLLER[lcid][control].symbol or " "
  880.             else
  881.                 str = control == 1 and "x" or "y"  -- TODO: should display the whole number for axis
  882.                 --[[
  883.                 str = fmt("%+.5d ", input_obj:get_axis(port, cnum, control-1))
  884.                 --]]
  885.             end
  886.            
  887.             presses[index] = str
  888.             index = index + 1
  889.         end
  890.     end
  891.    
  892.     return table.concat(presses)
  893. end
  894.  
  895.  
  896. function subframe_to_frame(subf)  -- unused
  897.     local total_frames = MOVIE.framecount or movie.count_frames(nil)
  898.     local total_subframes = MOVIE.subframe_count or movie.get_size(nil)
  899.    
  900.     if total_subframes < subf then return total_frames + (subf - total_subframes) --end
  901.     else return movie.subframe_to_frame(subf - 1) end
  902. end
  903.  
  904.  
  905. -- Colour schemes:
  906. COLOUR.input_readonly = 0xffffff
  907. COLOUR.input_readwrite = 0xffff00
  908. COLOUR.input_subframes = 0xff00
  909. COLOUR.input_background = 0x60606000
  910. COLOUR.framecount_halo = -1 -- initially 0x000040
  911. local end_of_movie_text = "END OF MOVIE"
  912. function LSNES.display_input()
  913.     -- Font
  914.     draw.Font_name = "snes9xluaclever"
  915.     local default_color = MOVIE.readonly and COLOUR.input_readonly or COLOUR.input_readwrite
  916.     local width  = draw.font_width()--LSNES.FONT_WIDTH
  917.     local height = draw.font_height()--LSNES.FONT_HEIGHT
  918.    
  919.     -- Input grid settings
  920.     local grid_width, grid_height = width*CONTROLLER.total_width, LSNES.Buffer_height
  921.     local x_grid, y_grid = - grid_width, 0
  922.     local grid_subframe_slots = grid_height//height - 1  -- discount the header
  923.     grid_height = (grid_subframe_slots + 1)*height  -- if grid_height is not a multiple of height, cut it
  924.     local past_inputs_number = (grid_subframe_slots - 1)//2  -- discount the present
  925.     local future_inputs_number = grid_subframe_slots - past_inputs_number  -- current frame is included here
  926.     local y_present = y_grid + (past_inputs_number + 1)*height  -- add header
  927.     local x_text, y_text = x_grid, y_present - height
  928.    
  929.     -- Extra settings
  930.     local color, subframe_around = nil, false
  931.     local input
  932.     local subframe = MOVIE.current_subframe
  933.     local frame = MOVIE.frame_of_past_subframe -- frame corresponding to subframe-1
  934.     local length_frame_string = #tostringx(subframe + future_inputs_number - 1)
  935.     local x_frame = x_text - length_frame_string*width - 2
  936.     local starting_subframe_grid = subframe - past_inputs_number
  937.     local last_subframe_grid = subframe + future_inputs_number - 1
  938.     local is_reset_subframe
  939.    
  940.     -- Draw background
  941.     local complete_input_sequence = CONTROLLER.complete_input_sequence
  942.     for y = subframe > past_inputs_number and 1 or past_inputs_number - subframe + 2 , grid_subframe_slots do  -- don't draw for negative frames
  943.         draw.text(x_text, 16*y, complete_input_sequence, COLOUR.input_background)
  944.     end
  945.     -- Draw grid
  946.     local colour = 0x909090
  947.     gui.line(x_text, y_present, x_text + grid_width - 1, y_present, 0xff0000)  -- drawing the bottom base of the rectangle is misleading
  948.     gui.rectangle(x_text, y_present, grid_width, height, 1, COLOUR.transparency, 0xc0ff0000)  -- users should know where the past ends
  949.     gui.rectangle(x_grid, y_grid, grid_width, grid_height, 1, colour)
  950.     local total_previous_button = 0
  951.     for line = 1, CONTROLLER.total_controllers, 1 do
  952.         -- fmt("%d:%d", CONTROLLER[line].port, CONTROLLER[line].controller) -> better header?
  953.         draw.text(x_grid + width*total_previous_button + 1, y_grid, line, colour, nil, COLOUR.halo)
  954.         if line == CONTROLLER.total_controllers then break end
  955.         total_previous_button = total_previous_button + CONTROLLER[line].button_count
  956.         gui.line(x_grid + width*total_previous_button, y_grid, x_grid + width*total_previous_button, grid_height - 1, colour)
  957.     end
  958.    
  959.     for subframe_id = subframe - 1, subframe - past_inputs_number, -1 do  -- discount header?
  960.         if subframe_id <= 0 then
  961.             starting_subframe_grid = 1
  962.             break
  963.         end
  964.        
  965.         local is_nullinput, is_startframe, is_delayedinput
  966.         local raw_input = LSNES.get_input(subframe_id)
  967.         if raw_input then
  968.             input = LSNES.treat_input(raw_input)
  969.             is_startframe = raw_input:get_button(0, 0, 0)
  970.             is_reset_subframe = raw_input:get_button(0, 0, 1)
  971.             if not is_startframe then subframe_around = true end
  972.             color = is_startframe and default_color or COLOUR.input_subframes -- edit
  973.         elseif frame == MOVIE.current_frame then
  974.             draw.text(0, 0, "frame == MOVIE.current_frame", "red", nil, "black") -- test -- delete
  975.             input = LSNES.treat_input(MOVIE.last_input_computed)
  976.             is_delayedinput = true
  977.             color = 0x00ffff
  978.         else
  979.             input = end_of_movie_text
  980.             is_nullinput = true
  981.             color = 0xff8080
  982.         end
  983.        
  984.         draw.text(x_frame, y_text, frame, color, nil, COLOUR.framecount_halo)
  985.         draw.text(x_text, y_text, input, color)
  986.         if is_reset_subframe then draw.text(x_text, y_text, "CONSOLE RESET", "red", 0x400000ff) end -- NEW
  987.        
  988.         if is_startframe or is_nullinput then
  989.             frame = frame - 1
  990.         end
  991.         y_text = y_text - height
  992.     end
  993.    
  994.     y_text = y_present
  995.     frame = MOVIE.current_frame
  996.    
  997.     for subframe_id = subframe, subframe + future_inputs_number - 1 do
  998.         local raw_input = LSNES.get_input(subframe_id)
  999.         local input = raw_input and LSNES.treat_input(raw_input) or "Unrecorded"
  1000.        
  1001.         is_reset_subframe = raw_input and raw_input:get_button(0, 0, 1)
  1002.         if raw_input and raw_input:get_button(0, 0, 0) then
  1003.             if subframe_id ~= MOVIE.current_subframe then frame = frame + 1 end
  1004.             color = default_color
  1005.         else
  1006.             if raw_input then
  1007.                 subframe_around = true
  1008.                 color = COLOUR.input_subframes -- 0xff edit
  1009.             else
  1010.                 if subframe_id ~= MOVIE.current_subframe then frame = frame + 1 end
  1011.                 color = 0x00ff00
  1012.             end
  1013.         end
  1014.        
  1015.         draw.text(x_frame, y_text, frame, color, nil, COLOUR.framecount_halo)
  1016.         draw.text(x_text, y_text, input, color)
  1017.         if is_reset_subframe then draw.text(x_text, y_text, "CONSOLE RESET", "red", 0x400000ff) end -- NEW
  1018.         y_text = y_text + height
  1019.        
  1020.         if not raw_input then
  1021.             last_subframe_grid = subframe_id
  1022.             break
  1023.         end
  1024.     end
  1025.    
  1026.     -- for subframe advance:
  1027.     LSNES.subframe_update = subframe_around
  1028.     gui.subframe_update(LSNES.subframe_update)
  1029. end
  1030.  
  1031.  
  1032. function LSNES.left_click()
  1033.     if SCRIPT_DEBUG_INFO then print"left_click" end
  1034.    
  1035.     -- Script buttons
  1036.     for _, field in ipairs(draw.buttons_table) do
  1037.         -- if mouse is over the button
  1038.         if mouse_onregion(field.x, field.y, field.x + field.width, field.y + field.height) then
  1039.                 field.action()
  1040.                 return
  1041.         end
  1042.     end
  1043. end
  1044.  
  1045.  
  1046. --#############################################################################
  1047. -- MAIN --
  1048.  
  1049.  
  1050. function on_frame_emulated()
  1051.     LSNES.Is_lagged = memory.get_lag_flag()
  1052.     LSNES.frame_boundary = "end"
  1053. end
  1054.  
  1055. function on_frame()
  1056.     LSNES.frame_boundary = "start"
  1057. end
  1058.  
  1059.  
  1060. function on_input(subframe)
  1061.     --print"ON INPUT"
  1062.     LSNES.Is_lagged_latch = true
  1063.     LSNES.frame_boundary = "middle"
  1064. end
  1065. function on_snoop(port, controller, button, value)
  1066.     --if port == 1 and controller == 0 and button == 0 then print"ON SNOOP" end
  1067. end
  1068. function on_latch()
  1069.     LSNES.Is_lagged_latch = false
  1070.     --print"ON LATCH"
  1071. end
  1072.  
  1073. local Paint_context = gui.renderctx.new(1, 1)
  1074. function on_paint(authentic_paint)
  1075.     --print(memory.get_lag_flag(), 1)
  1076.     -- Initial values, don't make drawings here
  1077.     read_raw_input()
  1078.     LSNES.Runmode = gui.get_runmode()
  1079.     LSNES.Runmode_paused = LSNES_PAUSED_RUNMODES[LSNES.Runmode]
  1080.     LSNES.Lsnes_speed = settings.get_speed()
  1081.    
  1082.     if not ROM_INFO.info_loaded then LSNES.get_rom_info() end
  1083.     if not CONTROLLER.info_loaded then LSNES.get_controller_info() end
  1084.     LSNES.get_screen_info()
  1085.     LSNES.get_movie_info()
  1086.     create_gaps()
  1087.     -- test
  1088.     Paint_context:clear()
  1089.     Paint_context:set()
  1090.    
  1091.     if OPTIONS.use_movie_editor_tool then LSNES.display_input() end
  1092.    
  1093.     --if SCRIPT_DEBUG_INFO then LSNES.debug_movie() end
  1094.     --show_movie_info(OPTIONS.display_movie_info)
  1095.    
  1096.     -- gets back to default paint context / video callback doesn't capture anything
  1097.     gui.renderctx.setnull()
  1098.     Paint_context:run()
  1099.    
  1100.     -- Buttons context
  1101.     Widgets_context:clear() -- reset the buttons' context and table
  1102.     draw.buttons_table = {}
  1103.     Widgets_context:set()
  1104.     if User_input.mouse_inwindow == 1 and LSNES.Runmode_paused then
  1105.         draw.button(LSNES.Buffer_width, - LSNES.Border_top, "Debug Script", function()
  1106.             SCRIPT_DEBUG_INFO = not SCRIPT_DEBUG_INFO
  1107.         end, {always_on_client = true})
  1108.        
  1109.         draw.button(0, 0, OPTIONS.use_movie_editor_tool and "Hide Input" or "Show Input", function()
  1110.             OPTIONS.use_movie_editor_tool = not OPTIONS.use_movie_editor_tool
  1111.         end, {always_on_client = true, ref_x = 1.0, ref_y = 1.0})
  1112.     end
  1113.     gui.renderctx.setnull()
  1114.     Widgets_context:run()
  1115.    
  1116.     --gui.text(0, 0, tostringx(LSNES.Is_lagged_latch), 'red', 'blue')-- test
  1117.     if SCRIPT_DEBUG_INFO then gui.text(2, 432, string.format("Garbage %.0fkB", collectgarbage("count")), "orange", nil, "black") end -- remove
  1118. end
  1119.  
  1120.  
  1121. -- VIDEO CALLBACK
  1122. settings.set("avi-large", "yes")
  1123. function on_video()
  1124.     -- Renders the same context of on_paint over video
  1125.     -- avi-large must be "yes"
  1126.     Paint_context:run()
  1127.     create_gaps()
  1128. end
  1129.  
  1130.  
  1131. -- Loading a state
  1132. function on_pre_load(...)
  1133.     if SCRIPT_DEBUG_INFO then print("PRE LOAD", ...) end
  1134.     LSNES.frame_boundary = "start"
  1135.     LSNES.Is_lagged = false
  1136. end
  1137.  
  1138.  
  1139. function on_post_load(...)
  1140.     if SCRIPT_DEBUG_INFO then print("POST LOAD", ...) end
  1141. end
  1142.  
  1143.  
  1144. -- Functions called on specific events
  1145. function on_readwrite()
  1146.     gui.repaint()
  1147. end
  1148.  
  1149.  
  1150. -- Rewind functions
  1151. function on_rewind()
  1152.     LSNES.frame_boundary = "start"
  1153. end
  1154.  
  1155.  
  1156. function on_movie_lost(kind)
  1157.     if SCRIPT_DEBUG_INFO then print("ON MOVIE LOST", kind) end
  1158.    
  1159.     if kind == "reload" then  -- just before reloading the ROM in rec mode or closing/loading new ROM
  1160.         LSNES.frame_boundary = "start"
  1161.         ROM_INFO.info_loaded = false
  1162.         CONTROLLER.info_loaded = false
  1163.        
  1164.     elseif kind == "load" then -- this is called just before loading / use on_post_load when needed
  1165.         ROM_INFO.info_loaded = false
  1166.         CONTROLLER.info_loaded = false
  1167.        
  1168.     end
  1169.    
  1170. end
  1171.  
  1172.  
  1173. function on_idle()
  1174.     if User_input.mouse_inwindow == 1 then gui.repaint() end
  1175.     set_idle_timeout(1000000//30)
  1176. end
  1177.  
  1178.  
  1179. --#############################################################################
  1180. -- ON START --
  1181.  
  1182. LSNES.subframe_update = false
  1183. gui.subframe_update(LSNES.subframe_update)
  1184.  
  1185. -- Get initial frame boudary state:
  1186. -- cannot be "end" in a repaint, only in authentic paints. When script starts, it should never be authentic
  1187. LSNES.frame_boundary = movie.pollcounter(0, 0, 0) ~= 0 and "middle" or "start"
  1188.  
  1189. -- KEYHOOK callback
  1190. on_keyhook = Keys.altkeyhook
  1191.  
  1192. -- Key presses:
  1193. Keys.registerkeypress("mouse_inwindow", gui.repaint)
  1194. Keys.registerkeypress(OPTIONS.hotkey_increase_opacity, function() draw.increase_opacity() end)
  1195. Keys.registerkeypress(OPTIONS.hotkey_decrease_opacity, function() draw.decrease_opacity() end)
  1196. Keys.registerkeypress("mouse_left", function() LSNES.left_click(); gui.repaint() end)
  1197. Keys.registerkeypress("period", function()
  1198.     LSNES.subframe_update = not LSNES.subframe_update
  1199.     gui.subframe_update(LSNES.subframe_update)
  1200.     gui.repaint()
  1201. end)
  1202.  
  1203. set_idle_timeout(1000000//30)
  1204. gui.repaint()
Add Comment
Please, Sign In to add comment