Commit: dc18a4a9f9fcd70d172f598aaed1aaa78a458133 Parent: 47a859ff214ae8b3a8ed68e56d87b678a46e0777 Author: Vi Grey Date: 2021-06-14 23:04 UTC Summary: Fix SNES dump for bh and prevent freezing after movie src/tasrdm/resources/dumpscript/bizhawk_dump.lua | 288 +++++++++++++++++++++++++++++++++++++++---------------------------------------- src/tasrdm/resources/dumpscript/fceux_dump.lua | 219 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 250 insertions(+), 257 deletions(-) diff --git a/src/tasrdm/resources/dumpscript/bizhawk_dump.lua b/src/tasrdm/resources/dumpscript/bizhawk_dump.lua index f7d6df7..119370a 100644 --- a/src/tasrdm/resources/dumpscript/bizhawk_dump.lua +++ b/src/tasrdm/resources/dumpscript/bizhawk_dump.lua @@ -27,10 +27,6 @@ local version = {}; local reset_value = 0; local movie_playing = false; local movie_started = false; -local file_name = ""; -local player_active = {}; -local player_controls = {}; -local lag_frame = false; local resets = {}; local frame_total = 0; local rerecord_count = 0; @@ -57,6 +53,14 @@ local gb_controls = {"A", "B", "Select", "Start", "Right", "Left", "Up", "Down"} local gba_controls = {"A", "B", "Select", "Start", "Right", "Left", "Up", "Down", "L", "R"}; local max_players = {["NES"]=2, ["SNES"]=8, ["GB"]=1, ["GBC"]=1, ["GBA"]=1}; +local file_name = ""; +local r_file_name = ""; +local tcr_file_name = ""; +local r_file; +local tcr_file; + +local controller_input_buffer = ""; +local controller_tcr_input_buffer = ""; function get_version() for num in string.gmatch(client.getversion(), "[0-9]+") do @@ -67,12 +71,12 @@ end function gb_check_version() local comp_version = {2, 6, 2}; for i = 1, #comp_version do - if version[i] == nil then + if (version[i] == nil) then return; end - if version[i] < comp_version[i] then + if (version[i] < comp_version[i]) then return; - elseif version[i] > comp_version[i] then + elseif (version[i] > comp_version[i]) then break; end end @@ -100,11 +104,7 @@ function get_console() end end --- Write the r08/r16m file -function write_r_file() - local r_file_name = ""; - local content = ""; - local x = 1; +function setup_r_file() if (console == "NES") then r_file_name = file_name .. ".r08"; elseif (console == "SNES") then @@ -112,125 +112,89 @@ function write_r_file() elseif (console == "GB" or console == "GBC" or console == "GBA") then r_file_name = file_name .. ".txt"; end - print("Writing to " .. r_file_name); - print("Emulator may freeze for a moment."); - print("Please be patient.\n"); - if (console == "NES" or console == "SNES") then - while (x <= #player_controls[1]) do - for i = 1, #player_controls do - if (console == "NES") then - content = content .. string.sub(player_controls[i], x, x); - elseif (console == "SNES") then - content = (content .. - string.sub(player_controls[snes_controller_order[i]], - x, x + 1)); - end - end - if (console == "SNES") then - x = x + 1; - end - x = x + 1; - end - elseif (console == "GB" or console == "GBC" or console == "GBA") then - content = player_controls[1]; - end - rfile = io.open(r_file_name, "wb+"); - rfile:write(content); - rfile:close(); - print("Finished writing to " .. r_file_name .. "\n"); + r_file = io.open(r_file_name, "wb+"); + print("Created replay dump file " .. r_file_name .. "\n"); +end + +function finish_r_file() + r_file:write(controller_input_buffer); + r_file:close(); + print("Finished writing " .. r_file_name .. "\n"); +end + +function setup_tcr_file() + tcr_file_name = file_name .. ".tcr"; + tcr_file = io.open(tcr_file_name, "wb+"); + print("Created replay dump file " .. tcr_file_name .. "\n"); + initialize_tcr_file(); end -- Write the TCR (TAS Console Replay) file -function write_tcr_file() - print("Writing to " .. file_name .. ".tcr"); - print("Emulator may freeze for a moment."); - print("Please be patient.\n"); +function initialize_tcr_file() local content = "{"; content = content .. "\"console\":\"" .. console .. "\","; - content = content .. "\"players\":"; - local players_str = "["; - for x = 1, #player_active do - if (player_active[x]) then - players_str = players_str .. tostring(x) .. ","; - end - end - if (#players_str > 0) then - players_str = string.sub(players_str, 1, -2); - end - players_str = players_str .. "],"; - content = content .. players_str; content = content .. "\"frames\":" .. tostring(frame_total) .. ","; content = content .. "\"rom_hash\":\"" .. rom_hash .. "\","; - content = content .. "\"rerecord_count\":" .. tostring(rerecord_count) .. ","; + content = content .. ("\"rerecord_count\":" .. tostring(rerecord_count) .. + ","); if (console ~= "GB" and console ~= "GBC" and console ~= "GBA") then content = content .. "\"clock_filter\":4,"; end if (console == "NES") then content = content .. "\"dpcm\":true,"; end + content = content .. "\"inputs\":[\"base64:"; + tcr_file:write(content); +end + +function finish_tcr_file() + local content = encode_base64(controller_tcr_input_buffer) .. "\"]"; if (console ~= "GB" and console ~= "GBC" and console ~= "GBA") then - content = content .. "\"resets\":"; - local resets_str = "["; + content = content .. ",\"resets\":["; for x = 1, #resets do - resets_str = resets_str .. tostring(resets[x]) .. ","; - end - if (#resets_str > 1) then - resets_str = string.sub(resets_str, 1, -2); - end - resets_str = resets_str .. "],"; - content = content .. resets_str; - end - content = content .. "\"inputs\":"; - local inputs_str = "["; - local inputs_count = 0; - local player_val = 1; - for x = 1, #player_controls do - if (player_active[x]) then - inputs_str = inputs_str .. "\"" .. encode_base64(player_controls[x]) .. "\","; + content = content .. tostring(resets[x]); + if (x < #resets) then + content = content .. ","; + end end + content = content .. "]"; end - if (#inputs_str > 0) then - inputs_str = string.sub(inputs_str, 1, -2); - end - inputs_str = inputs_str .. "]"; - content = content .. inputs_str; content = content .. "}"; - tcrfile = io.open(file_name .. ".tcr", "wb+"); - tcrfile:write(""); - tcrfile:write(content); - tcrfile:close(); - print("Finished writing to " .. file_name .. ".tcr" .. "\n"); + tcr_file:write(content); + tcr_file:close(); + print("Finished writing " .. tcr_file_name .. "\n"); end -- Convert input to base64 function encode_base64(input) - local base64_str = "base64:"; - local input_str_len = #input; - for i = 1, #input, 3 do - local b1 = math.floor(string.byte(string.sub(input, i, i)) / 4) + 1; - if (input_str_len + 1 - i >= 3) then - local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + - math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); - local b3 = ((string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + - math.floor(string.byte(string.sub(input, i+2, i+2)) / 64) + 1); - local b4 = string.byte(string.sub(input, i+2, i+2)) % 64 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. string.sub(base64_chars, b3, b3); - base64_str = base64_str .. string.sub(base64_chars, b4, b4); - elseif (input_str_len + 1 - i == 2) then - local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + - math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); - local b3 = (string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. string.sub(base64_chars, b3, b3); - base64_str = base64_str .. "="; - else - local b2 = (string.byte(string.sub(input, i, i)) % 4) * 16 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. "=="; + local base64_str = ""; + if (#input > 0) then + for i = 1, #input, 3 do + local b1 = math.floor(string.byte(string.sub(input, i, i)) / 4) + 1; + if (#input + 1 - i >= 3) then + local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + + math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); + local b3 = ((string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + + math.floor(string.byte(string.sub(input, i+2, i+2)) / 64) + 1); + local b4 = string.byte(string.sub(input, i+2, i+2)) % 64 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. string.sub(base64_chars, b3, b3); + base64_str = base64_str .. string.sub(base64_chars, b4, b4); + elseif (#input + 1 - i == 2) then + local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + + math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); + local b3 = (string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. string.sub(base64_chars, b3, b3); + base64_str = base64_str .. "="; + else + local b2 = (string.byte(string.sub(input, i, i)) % 4) * 16 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. "=="; + end end end return base64_str; @@ -257,6 +221,15 @@ function handle_frame() end end end + r_file:write(controller_input_buffer); + controller_input_buffer = ""; + -- Only write to files if controller_tcr_input_buffer is divisible by + -- 3 so it can be encoded perfectly into base64 for TCR file + if (#controller_tcr_input_buffer > 0 and + #controller_tcr_input_buffer % 3 == 0) then + tcr_file:write(encode_base64(controller_tcr_input_buffer)); + controller_tcr_input_buffer = ""; + end end -- Controller handling for GB, GBC, and GBA @@ -283,7 +256,7 @@ function handle_frame_gb_c_a() end -- We grab the cycle count now and use that value for next frame gb_cycles = emu.totalexecutedcycles(); - else + else local controls = movie.getinput(frame - 1); for x = 1, #gba_controls do if (controls[gba_controls[x]]) then @@ -293,62 +266,88 @@ function handle_frame_gb_c_a() c = math.floor((frame * 280896 - 83776) / 4096); end if (controller_input ~= controller_last) then - player_active[1] = true; - player_controls[1] = (player_controls[1] .. - string.format("%08X %04X", c, controller_input) .. - "\n"); + controller_input_buffer = (controller_input_buffer .. + string.format("%08X %04X", c, controller_input) .. "\n"); + for mod = 24, 0, -8 do + controller_tcr_input_buffer = (controller_tcr_input_buffer .. + string.char(bit.band(bit.rshift(c, mod), 0xff))); + end + for mod = 8, 0, -8 do + controller_tcr_input_buffer = (controller_tcr_input_buffer .. + string.char(bit.band(bit.rshift(controller_input, mod), 0xff))); + end controller_last = controller_input; end end -- Controller handling for NES and SNES function handle_frame_s_nes() - for i = 1, #player_controls do - local controls = movie.getinput(emu.framecount() - 1, i); + for i = 1, max_players[console] do local controller_input = 0; - local byte_width = ""; if (console == "NES") then + local controls = movie.getinput(emu.framecount() - 1, i); for x = 1, #nes_controls do if (controls[nes_controls[x]]) then controller_input = controller_input + bit.lshift(1, x - 1); - player_active[i] = true; end end - player_controls[i] = player_controls[i] .. string.char(controller_input); + controller_input_buffer = (controller_input_buffer .. + string.char(controller_input)); + controller_tcr_input_buffer = (controller_tcr_input_buffer .. + string.char(controller_input)); else + -- Use snes_controller_order[i] because P2-P4 may be in + -- controller port 1 if a multitap is connected to port 1 + local controls = movie.getinput(emu.framecount() - 1, + snes_controller_order[i]); + -- Get first byte of SNES controller data for x = 1, #snes_controls / 2 do - if (controls[nes_controls[x]]) then + if (controls[snes_controls[x]]) then controller_input = controller_input + bit.lshift(1, x - 1); - player_active[i] = true; end end - player_controls[i] = player_controls[i] .. string.char(controller_input); + controller_input_buffer = (controller_input_buffer .. + string.char(controller_input)); + controller_tcr_input_buffer = (controller_tcr_input_buffer .. + string.char(controller_input)); + -- Get second byte of SNES controller data controller_input = 0; for x = 1, #snes_controls / 2 do if (controls[snes_controls[x + #snes_controls / 2]]) then controller_input = controller_input + bit.lshift(1, x - 1); - player_active[i] = true; end end - player_controls[i] = player_controls[i] .. string.char(controller_input); + controller_input_buffer = (controller_input_buffer .. + string.char(controller_input)); + controller_tcr_input_buffer = (controller_tcr_input_buffer .. + string.char(controller_input)); end end end -- Initialize values upon movie startup function setup_dump() + controller_input_buffer = ""; + controller_tcr_input_buffer = ""; + client.unpause(); + movie_playing = true; + frame_total = movie.length(); + rerecord_count = movie.getrerecordcount(); + local cur_time = os.time(); + file_name = movie.filename():match("(.+)%..+$"); + file_name = file_name .. "-" .. os.date("!%Y%m%d%H%M%S", os.time()); + setup_r_file(); + setup_tcr_file(); if (console ~= "GBA") then - print("Starting movie. Replay file will be written after movie is finished.\n"); + print("Starting movie.\n"); + print("Replay files will not be finished writing until the movie is " .. + "finished.\n"); else print("Dumping GBA movie file."); + print("GBA movie will start after replay file is finished being written."); print("Emulator may freeze for a moment."); print("Please be patient.\n"); end - client.unpause(); - movie_playing = true; - frame_total = movie.length(); - rerecord_count = movie.getrerecordcount(); - file_name = movie.filename():match("(.+)%..+$"); client.speedmode(800); resets = {}; frame = 0; @@ -365,13 +364,10 @@ function setup_dump() return; end end - for i = 1, max_players[console] do - table.insert(player_active, false); - table.insert(player_controls, ""); - end end -- Start of dumping script +get_version(); if (rom_hash == "") then print("ERROR: ROM must be loaded. Load a ROM and try running this Lua script again\n"); else @@ -382,7 +378,7 @@ else if (emu.framecount() ~= 1 or movie.mode() ~= "PLAY") then client.reboot_core(); client.pause(); - print("Select movie file and then unpause the emulator (Emulation > Pause)") + print("Select movie file and then unpause the emulator (Emulation > Pause)"); print("DO NOT CHECK \"Last Frame\" OPTION IN \"Play Movie\" WINDOW\n"); else movie_started = true; @@ -391,34 +387,34 @@ else if (movie_started) then -- Movie is loaded/active if (movie_playing == false) then - get_version() console = get_console(); + core = movie.getheader()["Core"]; if (console == "SNES") then - snes_get_controller_order() + snes_get_controller_order(); end if (max_players[console] == nil) then print("ERROR: Unsupported console type. Load a ROM of a supported console type and try running this Lua script again.\n"); break - end - core = movie.getheader()["Core"] + end setup_dump(); else if (console == "GBA") then - for frame = 1, movie.length() do + for i = 1, movie.length() do + frame = i; handle_frame(); end - write_tcr_file(); - write_r_file(); + finish_r_file(); + finish_tcr_file(); movie_playing = false; client.speedmode(100); - print("Starting movie. Closing Lua Script.\n"); + print("Finished dumping GBA replay file. Closing Lua Script.\n"); break; else if (emu.framecount() > movie.length()) then -- End of movie - print("Movie finished.\n") - write_tcr_file(); - write_r_file(); + print("Movie finished.\n"); + finish_r_file(); + finish_tcr_file(); movie_playing = false; movie.stop(); client.speedmode(100); diff --git a/src/tasrdm/resources/dumpscript/fceux_dump.lua b/src/tasrdm/resources/dumpscript/fceux_dump.lua index ab084d8..874b512 100644 --- a/src/tasrdm/resources/dumpscript/fceux_dump.lua +++ b/src/tasrdm/resources/dumpscript/fceux_dump.lua @@ -25,129 +25,117 @@ local reset_value = 0; local movie_playing = false; -local file_name = ""; -local player_active = {}; -local player_controls = {}; -local lag_frame = false; local resets = {}; local frame_total = 0; local rerecord_count = 0; local rom_hash = ""; local base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; local frame = 0; -local nes_controls = {"right", "left", "down", "up", "start", "select", "B", "A"} +local nes_controls = {"right", "left", "down", "up", "start", "select", "B", "A"}; +local file_name = ""; +local r_file_name = ""; +local tcr_file_name = ""; +local r_file; +local tcr_file; --- Write the r08/r16m file -function write_r_file() - local r_file_name = file_name .. ".r08" - print("Writing to " .. r_file_name) - print("Emulator may freeze for a moment.") - print("Please be patient.\n"); - local content = "" - for x = 1, #player_controls[1] do - for i = 1, #player_controls do - content = content .. string.sub(player_controls[i], x, x) - end +local controller_input_buffer = ""; + + +-- If reset and not start of movie, add reset to resets table +function detect_reset() + if (frame > 0) then + table.insert(resets, frame); + elseif (movie.framecount() > 1) then + table.insert(resets, frame); end - rfile = io.open(r_file_name, "wb+"); - rfile:write(content); - rfile:close(); - print("Finished writing to " .. r_file_name .. "\n"); +end + +function setup_r_file() + r_file_name = file_name .. ".r08"; + r_file = io.open(r_file_name, "wb+"); + print("Created replay dump file " .. r_file_name .. "\n"); +end + +function finish_r_file() + r_file:write(controller_input_buffer); + r_file:close(); + print("Finished writing " .. r_file_name .. "\n"); +end + +function setup_tcr_file() + tcr_file_name = file_name .. ".tcr"; + tcr_file = io.open(tcr_file_name, "wb+"); + print("Created replay dump file " .. tcr_file_name .. "\n"); + initialize_tcr_file(); end -- Write the TCR (TAS Console Replay) file -function write_tcr_file() - print("Writing to " .. file_name .. ".tcr") - print("Emulator may freeze for a moment.") - print("Please be patient.\n"); +function initialize_tcr_file() local content = "{"; content = content .. "\"console\":\"NES\","; - content = content .. "\"players\":"; - local players_str = "["; - for x = 1, #player_active do - if (player_active[x]) then - players_str = players_str .. tostring(x) .. ","; - end - end - if (#players_str > 0) then - players_str = string.sub(players_str, 1, -2); - end - players_str = players_str .. "],"; - content = content .. players_str; content = content .. "\"frames\":" .. tostring(frame_total) .. ","; content = content .. "\"rom_hash\":\"" .. rom_hash .. "\","; - content = content .. "\"rerecord_count\":" .. tostring(rerecord_count) .. ","; - content = content .. "\"blank\":0,"; - content = content .. "\"clock_filter\":4,"; - content = content .. "\"dpcm\":true,"; - content = content .. "\"resets\":"; - local resets_str = "["; - for x = 1, #resets do - resets_str = resets_str .. tostring(resets[x]) .. ","; - end - if (#resets_str > 1) then - resets_str = string.sub(resets_str, 1, -2); + content = content .. ("\"rerecord_count\":" .. tostring(rerecord_count) .. + ","); + if (console ~= "GB" and console ~= "GBC" and console ~= "GBA") then + content = content .. "\"clock_filter\":4,"; end - resets_str = resets_str .. "],"; - content = content .. resets_str; - content = content .. "\"inputs\":"; - local inputs_str = "["; - for x = 1, #player_controls do - if (player_active[x]) then - inputs_str = inputs_str .. "\"" .. encode_base64(player_controls[x]) .. "\","; - end + if (console == "NES") then + content = content .. "\"dpcm\":true,"; end - if (#inputs_str > 0) then - inputs_str = string.sub(inputs_str, 1, -2); - end - inputs_str = inputs_str .. "]"; - content = content .. inputs_str; - content = content .. "}"; - tcrfile = io.open(file_name .. ".tcr", "wb+"); - tcrfile:write(content); - tcrfile:close(); - print("Finished writing to " .. file_name .. ".tcr\n"); + content = content .. "\"inputs\":[\"base64:"; + tcr_file:write(content); end --- If reset and not start of movie, add reset to resets table -function detect_reset() - if (frame > 0) then - table.insert(resets, frame); - elseif (movie.framecount() > 1) then - table.insert(resets, frame); +function finish_tcr_file() + local content = encode_base64(controller_input_buffer) .. "\"]"; + if (console ~= "GB" and console ~= "GBC" and console ~= "GBA") then + content = content .. ",\"resets\":["; + for x = 1, #resets do + content = content .. tostring(resets[x]); + if (x < #resets) then + content = content .. ","; + end + end + content = content .. "]"; end + content = content .. "}"; + tcr_file:write(content); + tcr_file:close(); + print("Finished writing " .. tcr_file_name .. "\n"); end -- Convert input to base64 function encode_base64(input) - local base64_str = "base64:"; - local input_str_len = #input; - for i = 1, #input, 3 do - local b1 = math.floor(string.byte(string.sub(input, i, i)) / 4) + 1; - if (input_str_len + 1 - i >= 3) then - local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + - math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); - local b3 = ((string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + - math.floor(string.byte(string.sub(input, i+2, i+2)) / 64) + 1); - local b4 = string.byte(string.sub(input, i+2, i+2)) % 64 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. string.sub(base64_chars, b3, b3); - base64_str = base64_str .. string.sub(base64_chars, b4, b4); - elseif (input_str_len + 1 - i == 2) then - local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + - math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); - local b3 = (string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. string.sub(base64_chars, b3, b3); - base64_str = base64_str .. "="; - else - local b2 = (string.byte(string.sub(input, i, i)) % 4) * 16 + 1; - base64_str = base64_str .. string.sub(base64_chars, b1, b1); - base64_str = base64_str .. string.sub(base64_chars, b2, b2); - base64_str = base64_str .. "=="; + local base64_str = ""; + if (#input > 0) then + for i = 1, #input, 3 do + local b1 = math.floor(string.byte(string.sub(input, i, i)) / 4) + 1; + if (#input + 1 - i >= 3) then + local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + + math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); + local b3 = ((string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + + math.floor(string.byte(string.sub(input, i+2, i+2)) / 64) + 1); + local b4 = string.byte(string.sub(input, i+2, i+2)) % 64 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. string.sub(base64_chars, b3, b3); + base64_str = base64_str .. string.sub(base64_chars, b4, b4); + elseif (#input + 1 - i == 2) then + local b2 = ((string.byte(string.sub(input, i, i)) % 4) * 16 + + math.floor(string.byte(string.sub(input, i+1, i+1)) / 16) + 1); + local b3 = (string.byte(string.sub(input, i+1, i+1)) % 16) * 4 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. string.sub(base64_chars, b3, b3); + base64_str = base64_str .. "="; + else + local b2 = (string.byte(string.sub(input, i, i)) % 4) * 16 + 1; + base64_str = base64_str .. string.sub(base64_chars, b1, b1); + base64_str = base64_str .. string.sub(base64_chars, b2, b2); + base64_str = base64_str .. "=="; + end end end return base64_str; @@ -156,24 +144,30 @@ end -- Get controller input for frame function handle_frame() if (emu.lagged() == false) then - for i = 1, #player_controls do + for i = 1, 2 do local controls = joypad.get(i); local controller_input = 0; for x = 1, #nes_controls do if (controls[nes_controls[x]]) then controller_input = controller_input + bit.lshift(1, x - 1); - player_active[i] = true; end end - player_controls[i] = player_controls[i] .. string.char(controller_input) + controller_input_buffer = (controller_input_buffer .. + string.char(controller_input)); + end + -- Only write to files if controller_input_buffer is divisible by + -- 3 so it can be encoded perfectly into base64 for TCR file + if (#controller_input_buffer > 0 and #controller_input_buffer % 3 == 0) then + r_file:write(controller_input_buffer); + tcr_file:write(encode_base64(controller_input_buffer)); + controller_input_buffer = ""; end - frame = frame + 1; end end function setup_dump() -- Movie has not been started since the lua script started - print("Starting movie. Replay file will be written after movie is finished.\n"); + controller_input_buffer = ""; reset_value = memory.readbyteunsigned(0xfffd) * 256; reset_value = reset_value + memory.readbyteunsigned(0xfffc); movie.playbeginning(); @@ -182,11 +176,15 @@ function setup_dump() rerecord_count = movie.rerecordcount(); rom_hash = "md5:" .. rom.gethash("md5"); file_name = movie.name():match("(.+)%..+$"); + file_name = file_name .. "-" .. os.date("!%Y%m%d%H%M%S", os.time()); + setup_r_file(); + setup_tcr_file(); + print("Starting movie.\n"); + print("Replay files will not be finished writing until the movie is " .. + "finished.\n"); emu.speedmode("maximum"); resets = {}; - frame = 0 - player_active = { false, false }; - player_controls = { "", "" }; + frame = 0; memory.registerexec(reset_value, detect_reset); end @@ -199,14 +197,13 @@ while (true) do else if (movie.framecount() > movie.length()) then -- End of movie - print("Movie finished.\n") - write_tcr_file(); - write_r_file(); + print("Movie finished.\n"); + finish_r_file(); + finish_tcr_file(); movie_playing = false; movie.close(); emu.speedmode("normal"); - end - if (movie.framecount() > 1) then + elseif (movie.framecount() > 1) then handle_frame(); end end gemini://vigrey.com/git/tasrdm/commit/dc18a4a9f9fcd70d172f598aaed1aaa78a458133.txt

-- Leo's gemini proxy

-- Connecting to vigrey.com:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/plain; charset=UTF-8

-- Response ended

-- Page fetched on Mon Jun 3 01:18:22 2024