Works with both Gambatte and GBHawk cores (by reimplementing lag-frame detection in the latter using a bus-read hook).
Tested with the following BizHawk versions:
- 2.3.2 (GBHawk)
- 2.5.2 (GBHawk)
- 2.9.1 (GBHawk and Gambatte)
function isVersionOrLater(major, minor, patch)
local major0, minor0, patch0 = client.getversion():match("(%d+)%.(%d+)%.(%d+)")
major0 = tonumber(major0)
if major0 ~= major then
return major0 > major
end
minor0 = tonumber(minor0)
if minor0 ~= minor then
return minor0 > minor
end
return tonumber(patch0) >= patch
end
function formatInput(input)
local formattedString = ""
for key, value in pairs(input) do
if value then
if formattedString ~= "" then
formattedString = formattedString .. ", "
end
formattedString = formattedString .. key
end
end
return formattedString
end
function my_joypad_getwithmovie()
return movie.getinput(emu.framecount() - 1)
end
function inputHookSimple()
didntReadInput = false
end
function inputHookFancy()
readTime = emu.totalexecutedcycles() / 70224 - emu.framecount()
if readInputFirst == nil then
readInputFirst = readTime
end
readInputLast = readTime
end
logfile = nil
movieCore = nil
didntReadInput = true
readInputFirst = nil
readInputLast = nil
joypad_getwithmovie = isVersionOrLater(2,9,1) and joypad.getwithmovie or my_joypad_getwithmovie
event_on_bus_read = isVersionOrLater(2,9,0) and event.on_bus_read or event.onmemoryread
function logFrame()
local emuFrame = emu.framecount()
local emuCycle = emu.totalexecutedcycles()
local isLagged = emu.islagged()
if movieCore == "Gambatte" then
emuFrame = emuFrame - 5
emuCycle = emuCycle * 2 - 53476
else
isLagged = didntReadInput
didntReadInput = true
end
local lagIndicator
if useFancyIndicator then
lagIndicator = readInputFirst~=nil and string.format("[%8.5f..%8.5f]", readInputFirst, readInputLast) or " "
else
lagIndicator = isLagged and " " or "!"
end
logfile:write(string.format("%6d, %12.6f: %s %s\n",
emuFrame,
emuCycle / 70224,
lagIndicator,
formatInput(joypad_getwithmovie()))
)
readInputFirst = nil
logfile:flush()
end
local lastFrame
local lastMovie = nil
local finished
local busHookID = nil
while true do
if movie.isloaded() and movie.mode() ~= "FINISHED" then
if emu.framecount() == 0 and (finished or lastFrame ~= 0 or movie.filename() ~= lastMovie) then
if logfile ~= nil then
logfile:close()
end
if busHookID ~= nil then
event.unregisterbyid(busHookID)
busHookID = nil
end
lastMovie = movie.filename()
logfile = io.open(lastMovie .. ".frame-list.txt", "w")
movieCore = movie.getheader()["Core"]
useFancyIndicator = movieCore ~= "Gambatte"
if useFancyIndicator then
busHookID = event_on_bus_read(useFancyIndicator and inputHookFancy or inputHookSimple, 0xFF00)
end
finished = false
elseif logfile ~= nil and emu.framecount() ~= lastFrame then
logFrame()
end
lastFrame = emu.framecount()
elseif logfile ~= nil then
if movie.isloaded() and emu.framecount() ~= lastFrame and not finished then
logFrame()
finished = true
end
if not movie.isloaded() or finished then
logfile:close()
logfile = nil
if busHookID ~= nil then
event.unregisterbyid(busHookID)
busHookID = nil
end
end
end
emu.yield()
end