2013-05-13 00:46:06 +00:00
-- M64 reader script
-- Translates M64 file movies into button presses for Bizhawk, accounting for lag frames.
-- This script will automatically pause at the end of the movie
-- This script will not clear the saveram. If a movie requires empty saveram it must be cleared before using this script.
-- If you are trying to convert a M64 into BKM format, you can try pausing the emulator and starting recording a movie, then loading this script and letting it run. Beginning a new movie will clear the saveram for you.
2013-06-02 03:16:11 +00:00
local m64_filename = forms.openfile ( nil , nil , " Mupen Movie Files (*.M64)|*.M64|All Files (*.*)|*.* " )
2013-05-13 00:46:06 +00:00
2013-06-02 03:16:11 +00:00
console.clear ( )
if m64_filename == " " then
2014-03-26 22:30:48 +00:00
console.log ( " No movie selected. Exiting. " )
2013-06-02 03:16:11 +00:00
return
end
2014-03-26 22:30:48 +00:00
console.log ( " Opening movie for playback: " .. m64_filename )
2013-05-13 00:46:06 +00:00
-- Open the file and read past the header data
local input_file = assert ( io.open ( m64_filename , " rb " ) )
2013-06-02 03:16:11 +00:00
local header = input_file : read ( 0x400 )
-- Check the file and display some info
if string.sub ( header , 1 , 3 ) ~= " M64 " or string.byte ( header , 4 ) ~= 0x1A then
2014-03-26 22:30:48 +00:00
console.log ( " File signature is not M64 \\ x1A. This might not be an .m64 movie, but I'll try to play it anyway " )
2013-06-02 03:16:11 +00:00
end
function remove_nulls ( s )
if string.len ( s ) == 0 then
return s
end
local i = 1
while string.byte ( s , i ) ~= 0 and i <= string.len ( s ) do
i = i + 1
end
return string.sub ( s , 1 , i - 1 )
end
local movie_rom_name = string.sub ( header , 0x0C5 , 0x0E4 )
movie_rom_name = remove_nulls ( movie_rom_name )
2014-03-26 22:30:48 +00:00
console.log ( " Rom name: " .. movie_rom_name )
2013-06-02 03:16:11 +00:00
local rerecords = string.byte ( header , 0x11 ) + string.byte ( header , 0x12 ) * 0x100 + string.byte ( header , 0x13 ) * 0x10000 + string.byte ( header , 0x14 ) * 0x1000000
2014-03-26 22:30:48 +00:00
console.log ( " # of rerecords: " .. rerecords )
2013-06-02 03:16:11 +00:00
local rerecords = string.byte ( header , 0x0D ) + string.byte ( header , 0x0E ) * 0x100 + string.byte ( header , 0x0F ) * 0x10000 + string.byte ( header , 0x10 ) * 0x1000000
2014-03-26 22:30:48 +00:00
console.log ( " # of frames: " .. rerecords )
2013-06-02 03:16:11 +00:00
local author_info = string.sub ( header , 0x223 , 0x300 )
author_info = remove_nulls ( author_info )
2014-03-26 22:30:48 +00:00
console.log ( " Author: " .. author_info )
2013-06-02 03:16:11 +00:00
local description = string.sub ( header , 0x301 , 0x400 )
description = remove_nulls ( description )
2014-03-26 22:30:48 +00:00
console.log ( " Description: " .. description )
2013-06-02 03:16:11 +00:00
local video_plugin = string.sub ( header , 0x123 , 0x162 )
video_plugin = remove_nulls ( video_plugin )
2014-03-26 22:30:48 +00:00
console.log ( " Video Plugin: " .. video_plugin )
2013-06-02 03:16:11 +00:00
local audio_plugin = string.sub ( header , 0x163 , 0x1A2 )
audio_plugin = remove_nulls ( audio_plugin )
2014-03-26 22:30:48 +00:00
console.log ( " Audio Plugin: " .. audio_plugin )
2013-06-02 03:16:11 +00:00
local input_plugin = string.sub ( header , 0x1A3 , 0x1E2 )
input_plugin = remove_nulls ( input_plugin )
2014-03-26 22:30:48 +00:00
console.log ( " Input Plugin: " .. input_plugin )
2013-06-02 03:16:11 +00:00
local rsp_plugin = string.sub ( header , 0x1E3 , 0x222 )
rsp_plugin = remove_nulls ( rsp_plugin )
2014-03-26 22:30:48 +00:00
console.log ( " RSP Plugin: " .. rsp_plugin )
2013-05-13 00:46:06 +00:00
-- Flag to note that we've reached the end of the movie
local finished = false
-- Since m64 movies do not record on lag frames, we need to know if the input was actually used for the current frame
local input_was_used = false
function input_used ( )
if not finished then
input_was_used = true
end
end
event.oninputpoll ( input_used )
local buttons = { }
local X
local Y
-- Reads in the next frame of data from the movie, or sets the finished flag if no frames are left
function read_next_frame ( )
2013-06-02 03:16:11 +00:00
local data = input_file : read ( 4 )
if not data or string.len ( data ) ~= 4 then
2013-05-13 00:46:06 +00:00
finished = true
return
end
local byte = string.byte ( string.sub ( data , 1 , 1 ) )
if bit.band ( byte , 0x01 ) ~= 0 then
buttons [ " DPad R " ] = true
else
buttons [ " DPad R " ] = false
end
if bit.band ( byte , 0x02 ) ~= 0 then
buttons [ " DPad L " ] = true
else
buttons [ " DPad L " ] = false
end
if bit.band ( byte , 0x04 ) ~= 0 then
buttons [ " DPad D " ] = true
else
buttons [ " DPad D " ] = false
end
if bit.band ( byte , 0x08 ) ~= 0 then
buttons [ " DPad U " ] = true
else
buttons [ " DPad U " ] = false
end
if bit.band ( byte , 0x10 ) ~= 0 then
buttons [ " Start " ] = true
else
buttons [ " Start " ] = false
end
if bit.band ( byte , 0x20 ) ~= 0 then
buttons [ " Z " ] = true
else
buttons [ " Z " ] = false
end
if bit.band ( byte , 0x40 ) ~= 0 then
buttons [ " B " ] = true
else
buttons [ " B " ] = false
end
if bit.band ( byte , 0x80 ) ~= 0 then
buttons [ " A " ] = true
else
buttons [ " A " ] = false
end
byte = string.byte ( string.sub ( data , 2 , 2 ) )
if bit.band ( byte , 0x01 ) ~= 0 then
buttons [ " C Right " ] = true
else
buttons [ " C Right " ] = false
end
if bit.band ( byte , 0x02 ) ~= 0 then
buttons [ " C Left " ] = true
else
buttons [ " C Left " ] = false
end
if bit.band ( byte , 0x04 ) ~= 0 then
buttons [ " C Down " ] = true
else
buttons [ " C Down " ] = false
end
if bit.band ( byte , 0x08 ) ~= 0 then
buttons [ " C Up " ] = true
else
buttons [ " C Up " ] = false
end
if bit.band ( byte , 0x10 ) ~= 0 then
buttons [ " R " ] = true
else
buttons [ " R " ] = false
end
if bit.band ( byte , 0x20 ) ~= 0 then
buttons [ " L " ] = true
else
buttons [ " L " ] = false
end
X = string.byte ( string.sub ( data , 3 , 3 ) )
if X > 127 then
X = X - 256
end
Y = string.byte ( string.sub ( data , 4 , 4 ) )
if Y > 127 then
Y = Y - 256
end
end
while true do
-- Only read the next frame of data if the last one was used
if input_was_used and not finished then
read_next_frame ( )
input_was_used = false
end
if not finished then
joypad.set ( buttons , 1 )
local analogs = { [ " X Axis " ] = X , [ " Y Axis " ] = Y }
joypad.setanalog ( analogs , 1 )
end
if finished then
2014-03-26 22:30:48 +00:00
console.log ( " Movie finished " )
2013-11-01 15:19:35 +00:00
client.pause ( )
2013-06-02 03:16:11 +00:00
return
2013-05-13 00:46:06 +00:00
end
emu.frameadvance ( )
end