2011-05-18 01:30:25 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
2011-06-05 02:50:50 +00:00
using System.IO ;
2011-05-18 01:30:25 +00:00
2013-10-27 22:07:40 +00:00
using BizHawk.Common ;
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2011-07-14 01:12:18 +00:00
2013-10-27 18:18:58 +00:00
namespace BizHawk.Client.Common
2011-05-18 01:30:25 +00:00
{
2012-02-15 00:43:21 +00:00
public static class MovieImport
2011-07-06 01:53:15 +00:00
{
2012-03-11 12:24:56 +00:00
public const string COMMENT = "comment" ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
public const string COREORIGIN = "CoreOrigin" ;
2012-10-04 06:14:21 +00:00
public const string CRC16 = "CRC16" ;
public const string CRC32 = "CRC32" ;
2012-03-09 09:30:33 +00:00
public const string EMULATIONORIGIN = "emuOrigin" ;
2012-10-04 06:14:21 +00:00
public const string GAMECODE = "GameCode" ;
public const string INTERNALCHECKSUM = "InternalChecksum" ;
public const string JAPAN = "Japan" ;
public const string MD5 = "MD5" ;
2012-03-06 03:41:11 +00:00
public const string MOVIEORIGIN = "MovieOrigin" ;
2012-10-04 06:50:47 +00:00
public const string PORT1 = "port1" ;
public const string PORT2 = "port2" ;
public const string PROJECTID = "ProjectID" ;
2012-10-04 06:14:21 +00:00
public const string SHA256 = "SHA256" ;
public const string SUPERGAMEBOYMODE = "SuperGameBoyMode" ;
2012-10-04 06:50:47 +00:00
public const string STARTSECOND = "StartSecond" ;
public const string STARTSUBSECOND = "StartSubSecond" ;
2012-03-11 12:24:56 +00:00
public const string SYNCHACK = "SyncHack" ;
2012-10-04 06:14:21 +00:00
public const string UNITCODE = "UnitCode" ;
2012-03-06 03:41:11 +00:00
2012-02-27 18:52:08 +00:00
// Attempt to import another type of movie file into a movie object.
2013-11-10 02:55:11 +00:00
public static Movie ImportFile ( string path , out string errorMsg , out string warningMsg )
2012-02-15 00:43:21 +00:00
{
2013-11-10 02:55:11 +00:00
Movie m = new Movie ( ) ;
2013-10-25 00:57:23 +00:00
errorMsg = String . Empty ;
warningMsg = String . Empty ;
2013-04-15 02:14:14 +00:00
2013-11-10 02:55:11 +00:00
string ext = path ! = null ? Path . GetExtension ( path ) . ToUpper ( ) : String . Empty ;
2012-02-24 07:48:06 +00:00
try
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-04-15 02:14:14 +00:00
switch ( ext )
2012-02-24 07:48:06 +00:00
{
case ".FCM" :
2013-11-10 02:55:11 +00:00
m = ImportFCM ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".FM2" :
2013-11-10 02:55:11 +00:00
m = ImportFM2 ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".FMV" :
2013-11-10 02:55:11 +00:00
m = ImportFMV ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".GMV" :
2013-11-10 02:55:11 +00:00
m = ImportGMV ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
2012-09-10 21:35:56 +00:00
case ".LSMV" :
2013-11-10 02:55:11 +00:00
m = ImportLSMV ( path , out errorMsg , out warningMsg ) ;
2012-03-01 09:16:14 +00:00
break ;
2012-02-24 07:48:06 +00:00
case ".MCM" :
2013-11-10 02:55:11 +00:00
m = ImportMCM ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".MC2" :
2013-11-10 02:55:11 +00:00
m = ImportMC2 ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".MMV" :
2013-11-10 02:55:11 +00:00
m = ImportMMV ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
2012-09-10 21:35:56 +00:00
case ".NMV" :
2013-11-10 02:55:11 +00:00
m = ImportNMV ( path , out errorMsg , out warningMsg ) ;
2012-09-10 21:35:56 +00:00
break ;
2012-02-24 07:48:06 +00:00
case ".SMV" :
2013-11-10 02:55:11 +00:00
m = ImportSMV ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
case ".VBM" :
2013-11-10 02:55:11 +00:00
m = ImportVBM ( path , out errorMsg , out warningMsg ) ;
2012-02-24 07:48:06 +00:00
break ;
2012-03-01 09:16:14 +00:00
case ".VMV" :
2013-11-10 02:55:11 +00:00
m = ImportVMV ( path , out errorMsg , out warningMsg ) ;
2012-03-01 09:16:14 +00:00
break ;
2012-09-10 21:35:56 +00:00
case ".ZMV" :
2013-11-10 02:55:11 +00:00
m = ImportZMV ( path , out errorMsg , out warningMsg ) ;
2012-09-10 21:35:56 +00:00
break ;
2012-02-24 07:48:06 +00:00
}
2013-10-25 00:57:23 +00:00
if ( errorMsg = = String . Empty )
2012-02-28 23:11:19 +00:00
{
2013-12-10 01:45:45 +00:00
m . Header [ HeaderKeys . MOVIEVERSION ] = HeaderKeys . MovieVersion1 ;
2012-02-28 23:11:19 +00:00
}
2012-02-24 07:48:06 +00:00
}
catch ( Exception except )
{
errorMsg = except . ToString ( ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-02-24 07:48:06 +00:00
return m ;
2012-02-15 00:43:21 +00:00
}
2012-02-27 18:52:08 +00:00
// Return whether or not the type of file provided can currently be imported.
2012-02-15 00:43:21 +00:00
public static bool IsValidMovieExtension ( string extension )
{
2013-04-15 02:14:14 +00:00
string [ ] extensions = new [ ]
{
2012-09-12 04:11:35 +00:00
"FCM" , "FM2" , "FMV" , "GMV" , "MCM" , "MC2" , "MMV" , "NMV" , "LSMV" , "SMV" , "VBM" , "VMV" , "ZMV"
2012-03-01 09:16:14 +00:00
} ;
2013-04-15 02:14:14 +00:00
return extensions . Any ( ext = > extension . ToUpper ( ) = = "." + ext ) ;
2012-02-15 00:43:21 +00:00
}
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
// Reduce all whitespace to single spaces.
private static string SingleSpaces ( string line )
{
line = line . Replace ( "\t" , " " ) ;
line = line . Replace ( "\n" , " " ) ;
line = line . Replace ( "\r" , " " ) ;
line = line . Replace ( "\r\n" , " " ) ;
string prev ;
do
{
prev = line ;
line = line . Replace ( " " , " " ) ;
}
while ( prev ! = line ) ;
return line ;
}
2012-07-04 07:34:52 +00:00
// Import a frame from a text-based format.
2012-09-27 06:23:05 +00:00
private static Movie ImportTextFrame ( string line , int lineNum , Movie m , string path , string platform ,
2013-10-27 18:18:58 +00:00
ref string warningMsg )
2012-07-04 07:34:52 +00:00
{
string [ ] buttons = new string [ ] { } ;
string controller = "" ;
2013-04-15 02:14:14 +00:00
string ext = path ! = null ? Path . GetExtension ( path ) . ToUpper ( ) : "" ;
switch ( ext )
2012-07-04 07:34:52 +00:00
{
case ".FM2" :
2013-04-15 02:14:14 +00:00
buttons = new [ ] { "Right" , "Left" , "Down" , "Up" , "Start" , "Select" , "B" , "A" } ;
2012-07-04 07:34:52 +00:00
controller = "NES Controller" ;
break ;
case ".MC2" :
2013-04-15 02:14:14 +00:00
buttons = new [ ] { "Up" , "Down" , "Left" , "Right" , "B1" , "B2" , "Run" , "Select" } ;
2012-07-04 07:34:52 +00:00
controller = "PC Engine Controller" ;
break ;
2012-09-12 05:59:09 +00:00
case ".LSMV" :
2013-04-15 02:14:14 +00:00
buttons = new [ ] {
2012-09-12 05:59:09 +00:00
"B" , "Y" , "Select" , "Start" , "Up" , "Down" , "Left" , "Right" , "A" , "X" , "L" , "R"
} ;
controller = "SNES Controller" ;
2012-09-27 06:23:05 +00:00
if ( platform = = "GB" | | platform = = "GBC" )
{
2013-04-15 02:14:14 +00:00
buttons = new [ ] { "A" , "B" , "Select" , "Start" , "Right" , "Left" , "Up" , "Down" } ;
2012-09-27 06:23:05 +00:00
controller = "Gameboy Controller" ;
}
2012-09-12 05:59:09 +00:00
break ;
2012-07-04 07:34:52 +00:00
}
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = controller } } ;
2012-07-04 07:34:52 +00:00
// Split up the sections of the frame.
string [ ] sections = line . Split ( '|' ) ;
2013-04-15 02:14:14 +00:00
if ( ext = = ".FM2" & & sections . Length > = 2 & & sections [ 1 ] . Length ! = 0 )
2012-07-04 07:34:52 +00:00
{
controllers [ "Reset" ] = ( sections [ 1 ] [ 0 ] = = '1' ) ;
// Get the first invalid command warning message that arises.
2013-10-27 18:18:58 +00:00
if ( String . IsNullOrEmpty ( ( warningMsg ) ) )
2012-07-04 07:34:52 +00:00
{
switch ( sections [ 1 ] [ 0 ] )
{
case '0' :
break ;
case '1' :
break ;
case '2' :
2013-11-30 03:10:05 +00:00
if ( m . FrameCount ! = 0 )
2012-12-06 05:19:08 +00:00
{
2012-07-04 07:34:52 +00:00
warningMsg = "hard reset" ;
2012-12-06 05:19:08 +00:00
}
2012-07-04 07:34:52 +00:00
break ;
case '4' :
warningMsg = "FDS Insert" ;
break ;
case '8' :
warningMsg = "FDS Select Side" ;
break ;
default :
warningMsg = "unknown" ;
break ;
}
if ( warningMsg ! = "" )
2012-12-06 05:19:08 +00:00
{
2012-07-04 07:34:52 +00:00
warningMsg = "Unable to import " + warningMsg + " command on line " + lineNum + "." ;
2012-12-06 05:19:08 +00:00
}
2012-07-04 07:34:52 +00:00
}
}
2013-04-15 02:14:14 +00:00
if ( ext = = ".LSMV" & & sections . Length ! = 0 )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
string flags = sections [ 0 ] ;
2012-09-13 10:26:44 +00:00
char [ ] off = { '.' , ' ' , '\t' , '\n' , '\r' } ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
if ( flags . Length = = 0 | | off . Contains ( flags [ 0 ] ) )
{
2012-09-27 06:23:05 +00:00
if ( warningMsg = = "" )
warningMsg = "Unable to import subframe." ;
return m ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
}
bool reset = ( flags . Length > = 2 & & ! off . Contains ( flags [ 1 ] ) ) ;
flags = SingleSpaces ( flags . Substring ( 2 ) ) ;
if ( reset & & ( ( flags . Length > = 2 & & flags [ 1 ] ! = '0' ) | | ( flags . Length > = 4 & & flags [ 3 ] ! = '0' ) ) )
{
2012-09-27 06:23:05 +00:00
if ( warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-27 06:23:05 +00:00
warningMsg = "Unable to import delayed reset." ;
2012-12-06 05:19:08 +00:00
}
2012-09-27 06:23:05 +00:00
return m ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
}
controllers [ "Reset" ] = reset ;
}
2012-07-04 07:34:52 +00:00
/ *
2012-09-12 05:59:09 +00:00
Skip the first two sections of the split , which consist of everything before the starting | and the command .
Do not use the section after the last | . In other words , get the sections for the players .
2012-07-04 07:34:52 +00:00
* /
2012-09-12 05:59:09 +00:00
int start = 2 ;
int end = sections . Length - 1 ;
int player_offset = - 1 ;
2013-04-15 02:14:14 +00:00
if ( ext = = ".LSMV" )
2012-09-12 05:59:09 +00:00
{
// LSNES frames don't start or end with a |.
start - - ;
end + + ;
player_offset + + ;
}
for ( int section = start ; section < end ; section + + )
2012-07-04 07:34:52 +00:00
{
// The player number is one less than the section number for the reasons explained above.
2012-09-12 05:59:09 +00:00
int player = section + player_offset ;
2012-09-27 06:23:05 +00:00
string prefix = "P" + ( player ) . ToString ( ) + " " ;
// Gameboy doesn't currently have a prefix saying which player the input is for.
if ( controllers . Type . Name = = "Gameboy Controller" )
2012-12-06 05:19:08 +00:00
{
2012-09-27 06:23:05 +00:00
prefix = "" ;
2012-12-06 05:19:08 +00:00
}
2012-07-04 07:34:52 +00:00
// Only count lines with that have the right number of buttons and are for valid players.
if (
sections [ section ] . Length = = buttons . Length & &
2013-07-14 14:53:32 +00:00
player < = MnemonicConstants . PLAYERS [ controllers . Type . Name ]
2012-07-04 07:34:52 +00:00
)
2012-12-06 05:19:08 +00:00
{
2012-07-04 07:34:52 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-07-04 07:34:52 +00:00
// Consider the button pressed so long as its spot is not occupied by a ".".
2012-09-27 06:23:05 +00:00
controllers [ prefix + buttons [ button ] ] = ( sections [ section ] [ button ] ! = '.' ) ;
2012-12-06 05:19:08 +00:00
}
}
2012-07-04 07:34:52 +00:00
}
// Convert the data for the controllers to a mnemonic and add it as a frame.
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-07-04 07:34:52 +00:00
return m ;
}
// Import a subtitle from a text-based format.
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
private static Movie ImportTextSubtitle ( string line , Movie m , string path )
2012-07-04 07:34:52 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
line = SingleSpaces ( line ) ;
2012-07-04 07:34:52 +00:00
// The header name, frame, and message are separated by whitespace.
int first = line . IndexOf ( ' ' ) ;
int second = line . IndexOf ( ' ' , first + 1 ) ;
if ( first ! = - 1 & & second ! = - 1 )
{
// Concatenate the frame and message with default values for the additional fields.
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
string frame ;
string length ;
2013-04-15 02:14:14 +00:00
string ext = path ! = null ? Path . GetExtension ( path ) . ToUpper ( ) : "" ;
if ( ext ! = ".LSMV" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
frame = line . Substring ( first + 1 , second - first - 1 ) ;
length = "200" ;
}
else
{
frame = line . Substring ( 0 , first ) ;
length = line . Substring ( first + 1 , second - first - 1 ) ;
}
2013-04-15 02:14:14 +00:00
string message = line . Substring ( second + 1 ) . Trim ( ) ;
2013-11-29 20:26:24 +00:00
m . Header . Subtitles . AddFromString ( "subtitle " + frame + " 0 0 " + length + " FFFFFFFF " + message ) ;
2012-07-04 07:34:52 +00:00
}
return m ;
}
2012-02-25 03:04:22 +00:00
// Import a text-based movie format. This works for .FM2 and .MC2.
2013-11-10 02:55:11 +00:00
private static Movie ImportText ( string path , out string errorMsg , out string warningMsg )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-03-02 04:15:15 +00:00
FileInfo file = new FileInfo ( path ) ;
StreamReader sr = file . OpenText ( ) ;
2013-11-10 02:55:11 +00:00
string emulator = String . Empty ;
string platform = String . Empty ;
2012-02-25 03:04:22 +00:00
switch ( Path . GetExtension ( path ) . ToUpper ( ) )
{
case ".FM2" :
emulator = "FCEUX" ;
platform = "NES" ;
break ;
case ".MC2" :
emulator = "Mednafen/PCEjin" ;
platform = "PCE" ;
break ;
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = platform ;
2012-03-02 04:15:15 +00:00
int lineNum = 0 ;
2013-04-15 02:14:14 +00:00
string line ;
2012-03-02 04:15:15 +00:00
while ( ( line = sr . ReadLine ( ) ) ! = null )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2012-03-02 04:15:15 +00:00
lineNum + + ;
if ( line = = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-02 04:15:15 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2012-07-04 07:34:52 +00:00
else if ( line [ 0 ] = = '|' )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2013-10-27 18:18:58 +00:00
m = ImportTextFrame ( line , lineNum , m , path , platform , ref warningMsg ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
if ( errorMsg ! = "" )
{
sr . Close ( ) ;
return null ;
}
}
2012-07-04 07:34:52 +00:00
else if ( line . ToLower ( ) . StartsWith ( "sub" ) )
2012-12-06 05:19:08 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
m = ImportTextSubtitle ( line , m , path ) ;
2012-12-06 05:19:08 +00:00
}
2012-07-04 07:34:52 +00:00
else if ( line . ToLower ( ) . StartsWith ( "emuversion" ) )
2012-12-06 05:19:08 +00:00
{
2012-09-15 18:46:41 +00:00
m . Header . Comments . Add (
EMULATIONORIGIN + " " + emulator + " version " + ParseHeader ( line , "emuVersion" )
) ;
2012-12-06 05:19:08 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "version" ) )
2012-07-04 06:41:57 +00:00
{
string version = ParseHeader ( line , "version" ) ;
2012-03-02 04:15:15 +00:00
m . Header . Comments . Add (
2012-07-04 06:41:57 +00:00
MOVIEORIGIN + " " + Path . GetExtension ( path ) + " version " + version
2012-03-02 04:15:15 +00:00
) ;
2012-07-04 07:34:52 +00:00
if ( Path . GetExtension ( path ) . ToUpper ( ) = = ".FM2" & & version ! = "3" )
2012-07-04 06:41:57 +00:00
{
errorMsg = ".FM2 movie version must always be 3." ;
sr . Close ( ) ;
return null ;
}
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "romfilename" ) )
2012-12-06 05:19:08 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = ParseHeader ( line , "romFilename" ) ;
2012-12-06 05:19:08 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "romchecksum" ) )
2012-03-09 13:26:06 +00:00
{
2012-07-03 16:47:08 +00:00
string blob = ParseHeader ( line , "romChecksum" ) ;
2012-10-04 06:14:21 +00:00
byte [ ] md5 = DecodeBlob ( blob ) ;
if ( md5 ! = null & & md5 . Length = = 16 )
2012-12-06 05:19:08 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ MD5 ] = Util . BytesToHexString ( md5 ) . ToLower ( ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
else
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
warningMsg = "Bad ROM checksum." ;
2012-12-06 05:19:08 +00:00
}
2012-03-09 13:26:06 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "comment author" ) )
2012-12-06 05:19:08 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = ParseHeader ( line , "comment author" ) ;
2012-12-06 05:19:08 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "rerecordcount" ) )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2012-03-02 04:15:15 +00:00
int rerecordCount ;
// Try to parse the re-record count as an integer, defaulting to 0 if it fails.
try
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2012-03-02 04:15:15 +00:00
rerecordCount = int . Parse ( ParseHeader ( line , "rerecordCount" ) ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-03-02 04:15:15 +00:00
catch
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2012-03-02 04:15:15 +00:00
rerecordCount = 0 ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = ( ulong ) rerecordCount ;
2012-03-02 04:15:15 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "guid" ) )
2012-12-06 05:19:08 +00:00
{
2013-12-04 03:04:29 +00:00
continue ; //We no longer care to keep this info
2012-12-06 05:19:08 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "startsfromsavestate" ) )
2012-03-02 04:15:15 +00:00
{
// If this movie starts from a savestate, we can't support it.
if ( ParseHeader ( line , "StartsFromSavestate" ) = = "1" )
2012-03-01 09:16:14 +00:00
{
2012-03-02 04:15:15 +00:00
errorMsg = "Movies that begin with a savestate are not supported." ;
sr . Close ( ) ;
return null ;
2012-03-01 09:16:14 +00:00
}
2012-03-02 04:15:15 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "palflag" ) )
2012-03-02 04:15:15 +00:00
{
2012-03-12 20:35:10 +00:00
bool pal = ( ParseHeader ( line , "palFlag" ) = = "1" ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-03-02 04:15:15 +00:00
}
2012-07-03 16:47:08 +00:00
else if ( line . ToLower ( ) . StartsWith ( "fourscore" ) )
2012-07-03 22:24:24 +00:00
{
bool fourscore = ( ParseHeader ( line , "fourscore" ) = = "1" ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . FOURSCORE ] = fourscore . ToString ( ) ;
2012-07-03 22:24:24 +00:00
}
2012-03-02 04:15:15 +00:00
else
// Everything not explicitly defined is treated as a comment.
m . Header . Comments . Add ( line ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-03-02 04:15:15 +00:00
sr . Close ( ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
return m ;
}
2012-02-28 10:59:16 +00:00
// Get the content for a particular header.
private static string ParseHeader ( string line , string headerName )
{
2012-07-03 16:47:08 +00:00
// Case-insensitive search.
int x = line . ToLower ( ) . LastIndexOf (
headerName . ToLower ( )
) + headerName . Length ;
2013-04-15 02:14:14 +00:00
string str = line . Substring ( x + 1 , line . Length - x - 1 ) ;
2012-07-03 16:47:08 +00:00
return str . Trim ( ) ;
2012-02-28 10:59:16 +00:00
}
2012-03-09 13:26:06 +00:00
// Decode a blob used in FM2 (base64:..., 0x123456...)
private static byte [ ] DecodeBlob ( string blob )
{
2012-03-11 13:27:11 +00:00
if ( blob . Length < 2 )
2012-12-06 05:19:08 +00:00
{
2012-03-11 13:27:11 +00:00
return null ;
2012-12-06 05:19:08 +00:00
}
2012-03-11 13:27:11 +00:00
if ( blob [ 0 ] = = '0' & & ( blob [ 1 ] = = 'x' | | blob [ 1 ] = = 'X' ) )
2012-12-06 05:19:08 +00:00
{
2012-03-11 13:27:11 +00:00
// hex
2013-04-15 02:14:14 +00:00
return Util . HexStringToBytes ( blob . Substring ( 2 ) ) ;
2012-12-06 05:19:08 +00:00
}
else
{
2012-03-11 13:27:11 +00:00
// base64
2012-12-06 05:19:08 +00:00
if ( ! blob . ToLower ( ) . StartsWith ( "base64:" ) )
{
2012-03-11 13:27:11 +00:00
return null ;
2012-12-06 05:19:08 +00:00
}
2012-03-09 13:26:06 +00:00
try
{
return Convert . FromBase64String ( blob . Substring ( 7 ) ) ;
}
catch ( FormatException )
{
return null ;
}
}
}
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
// Ends the string where a NULL character is found.
private static string NullTerminated ( string str )
2012-02-28 10:59:16 +00:00
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
int pos = str . IndexOf ( '\0' ) ;
if ( pos ! = - 1 )
2012-12-06 05:19:08 +00:00
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
str = str . Substring ( 0 , pos ) ;
2012-12-06 05:19:08 +00:00
}
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
return str ;
2012-02-28 10:59:16 +00:00
}
2012-02-24 05:18:59 +00:00
// FCM file format: http://code.google.com/p/fceu/wiki/FCM
2013-11-10 02:55:11 +00:00
private static Movie ImportFCM ( string path , out string errorMsg , out string warningMsg )
2011-07-06 01:53:15 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-02-24 04:44:40 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
2012-03-01 09:16:14 +00:00
// 000 4-byte signature: 46 43 4D 1A "FCM\x1A"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 4 ) ;
2012-03-01 09:16:14 +00:00
if ( signature ! = "FCM\x1A" )
2011-07-06 01:53:15 +00:00
{
2012-02-28 23:11:19 +00:00
errorMsg = "This is not a valid .FCM file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-24 04:44:40 +00:00
return null ;
}
2012-03-01 09:16:14 +00:00
// 004 4-byte little-endian unsigned int: version number, must be 2
2012-02-28 10:59:16 +00:00
uint version = r . ReadUInt32 ( ) ;
2012-03-06 03:41:11 +00:00
if ( version ! = 2 )
{
errorMsg = ".FCM movie version must always be 2." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
m . Header . Comments . Add ( MOVIEORIGIN + " .FCM version " + version ) ;
2012-03-01 09:16:14 +00:00
// 008 1-byte flags
byte flags = r . ReadByte ( ) ;
2012-03-06 03:41:11 +00:00
// bit 0: reserved, set to 0
2012-03-01 09:16:14 +00:00
/ *
2012-03-06 03:41:11 +00:00
bit 1 :
* if "0" , movie begins from an embedded "quicksave" snapshot
* if "1" , movie begins from reset or power - on [ 1 ]
2012-03-01 09:16:14 +00:00
* /
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 1 ) & 0x1 ) = = 0 )
2012-03-01 09:16:14 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-03-01 09:16:14 +00:00
return null ;
}
2012-03-05 17:20:30 +00:00
/ *
2012-03-06 03:41:11 +00:00
bit 2 :
2012-03-07 02:48:19 +00:00
* if "0" , NTSC timing
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
* if "1" , "PAL" timing
Starting with version 0.98 . 12 released on September 19 , 2004 , a "PAL" flag was added to the header but
unfortunately it is not reliable - the emulator does not take the "PAL" setting from the ROM , but from a user
2012-03-06 03:41:11 +00:00
preference . This means that this site cannot calculate movie lengths reliably .
2012-03-05 17:20:30 +00:00
* /
2012-09-17 07:22:35 +00:00
bool pal = ( ( ( flags > > 2 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-03-11 12:24:56 +00:00
// other: reserved, set to 0
2012-09-17 07:22:35 +00:00
bool syncHack = ( ( ( flags > > 4 ) & 0x1 ) ! = 0 ) ;
2012-03-11 12:24:56 +00:00
m . Header . Comments . Add ( SYNCHACK + " " + syncHack . ToString ( ) ) ;
2012-09-15 18:46:41 +00:00
// 009 1-byte flags: reserved, set to 0
r . ReadByte ( ) ;
// 00A 1-byte flags: reserved, set to 0
r . ReadByte ( ) ;
// 00B 1-byte flags: reserved, set to 0
r . ReadByte ( ) ;
2012-03-01 09:16:14 +00:00
// 00C 4-byte little-endian unsigned int: number of frames
2012-02-28 10:59:16 +00:00
uint frameCount = r . ReadUInt32 ( ) ;
2012-03-01 09:16:14 +00:00
// 010 4-byte little-endian unsigned int: rerecord count
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-03-02 04:15:15 +00:00
/ *
2012-03-06 03:41:11 +00:00
018 4 - byte little - endian unsigned int : offset to the savestate inside file
The savestate offset is < header_size + length_of_metadata_in_bytes + padding > . The savestate offset should be
4 - byte aligned . At the savestate offset there is a savestate file . The savestate exists even if the movie is
reset - based .
2012-03-02 04:15:15 +00:00
* /
r . ReadUInt32 ( ) ;
2012-03-01 09:16:14 +00:00
// 01C 4-byte little-endian unsigned int: offset to the controller data inside file
2012-02-28 10:59:16 +00:00
uint firstFrameOffset = r . ReadUInt32 ( ) ;
2012-03-01 09:16:14 +00:00
// 020 16-byte md5sum of the ROM used
2012-10-04 06:14:21 +00:00
byte [ ] md5 = r . ReadBytes ( 16 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ MD5 ] = Util . BytesToHexString ( md5 ) . ToLower ( ) ;
2012-03-01 09:16:14 +00:00
// 030 4-byte little-endian unsigned int: version of the emulator used
2012-02-28 23:11:19 +00:00
uint emuVersion = r . ReadUInt32 ( ) ;
2012-03-06 03:41:11 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " FCEU " + emuVersion . ToString ( ) ) ;
2012-03-01 09:16:14 +00:00
// 034 name of the ROM used - UTF8 encoded nul-terminated string.
List < byte > gameBytes = new List < byte > ( ) ;
while ( r . PeekChar ( ) ! = 0 )
gameBytes . Add ( r . ReadByte ( ) ) ;
// Advance past null byte.
r . ReadByte ( ) ;
2012-09-13 19:33:55 +00:00
string gameName = Encoding . UTF8 . GetString ( gameBytes . ToArray ( ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gameName ;
2012-03-01 09:16:14 +00:00
/ *
2012-03-06 03:41:11 +00:00
After the header comes "metadata" , which is UTF8 - coded movie title string . The metadata begins after the ROM
name and ends at the savestate offset . This string is displayed as "Author Info" in the Windows version of the
emulator .
2012-03-01 09:16:14 +00:00
* /
2012-02-24 04:44:40 +00:00
List < byte > authorBytes = new List < byte > ( ) ;
2012-03-01 09:16:14 +00:00
while ( r . PeekChar ( ) ! = 0 )
authorBytes . Add ( r . ReadByte ( ) ) ;
// Advance past null byte.
r . ReadByte ( ) ;
2012-09-13 19:33:55 +00:00
string author = Encoding . UTF8 . GetString ( authorBytes . ToArray ( ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-03-02 04:15:15 +00:00
// Advance to first byte of input data.
2012-02-24 04:44:40 +00:00
r . BaseStream . Position = firstFrameOffset ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } } ;
string [ ] buttons = new [ ] { "A" , "B" , "Select" , "Start" , "Up" , "Down" , "Left" , "Right" } ;
2012-03-09 11:49:43 +00:00
bool fds = false ;
2012-03-02 04:15:15 +00:00
bool fourscore = false ;
int frame = 1 ;
while ( frame < = frameCount )
2012-02-24 04:44:40 +00:00
{
2012-03-02 04:15:15 +00:00
byte update = r . ReadByte ( ) ;
2012-03-05 17:36:52 +00:00
// aa: Number of delta bytes to follow
2012-09-17 07:22:35 +00:00
int delta = ( update > > 5 ) & 0x3 ;
2012-03-05 17:36:52 +00:00
int frames = 0 ;
/ *
2012-03-06 03:41:11 +00:00
The delta byte ( s ) indicate the number of emulator frames between this update and the next update . It is
encoded in little - endian format and its size depends on the magnitude of the delta :
2012-03-07 02:48:19 +00:00
Delta of : Number of bytes :
0 0
1 - 255 1
256 - 65535 2
2012-03-06 03:41:11 +00:00
65536 - ( 2 ^ 24 - 1 ) 3
2012-03-05 17:36:52 +00:00
* /
for ( int b = 0 ; b < delta ; b + + )
2012-12-06 05:19:08 +00:00
{
2012-03-05 17:36:52 +00:00
frames + = r . ReadByte ( ) * ( int ) Math . Pow ( 2 , b * 8 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-11 13:27:11 +00:00
frame + = frames ;
2012-03-05 17:36:52 +00:00
while ( frames > 0 )
{
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-03-05 17:36:52 +00:00
if ( controllers [ "Reset" ] )
{
controllers [ "Reset" ] = false ;
}
frames - - ;
}
2012-09-17 07:22:35 +00:00
if ( ( ( update > > 7 ) & 0x1 ) ! = 0 )
2012-03-02 04:15:15 +00:00
{
// Control update: 1aabbbbb
bool reset = false ;
int command = update & 0x1F ;
2012-03-06 03:41:11 +00:00
// bbbbb:
2012-03-11 13:27:11 +00:00
controllers [ "Reset" ] = ( command = = 1 ) ;
2012-03-06 03:41:11 +00:00
if ( warningMsg = = "" )
2012-03-02 04:15:15 +00:00
{
2012-03-06 03:41:11 +00:00
switch ( command )
{
// Do nothing
case 0 :
break ;
// Reset
case 1 :
2012-03-11 13:27:11 +00:00
reset = true ;
2012-03-06 03:41:11 +00:00
break ;
// Power cycle
case 2 :
reset = true ;
if ( frame ! = 1 )
2012-12-06 05:19:08 +00:00
{
2012-03-06 03:41:11 +00:00
warningMsg = "hard reset" ;
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
break ;
// VS System Insert Coin
case 7 :
warningMsg = "VS System Insert Coin" ;
break ;
// VS System Dipswitch 0 Toggle
case 8 :
warningMsg = "VS System Dipswitch 0 Toggle" ;
break ;
// FDS Insert
case 24 :
2012-03-09 11:49:43 +00:00
fds = true ;
2012-03-06 03:41:11 +00:00
warningMsg = "FDS Insert" ;
break ;
// FDS Eject
case 25 :
2012-03-09 11:49:43 +00:00
fds = true ;
2012-03-06 03:41:11 +00:00
warningMsg = "FDS Eject" ;
break ;
// FDS Select Side
case 26 :
2012-03-09 11:49:43 +00:00
fds = true ;
2012-03-06 03:41:11 +00:00
warningMsg = "FDS Select Side" ;
break ;
default :
warningMsg = "unknown" ;
break ;
}
if ( warningMsg ! = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-06 03:41:11 +00:00
warningMsg = "Unable to import " + warningMsg + " command at frame " + frame + "." ;
2012-12-06 05:19:08 +00:00
}
2012-03-02 04:15:15 +00:00
}
/ *
2012-03-06 03:41:11 +00:00
1 Even if the header says "movie begins from reset" , the file still contains a quicksave , and the
quicksave is actually loaded . This flag can ' t therefore be trusted . To check if the movie actually
begins from reset , one must analyze the controller data and see if the first non - idle command in the
file is a Reset or Power Cycle type control command .
2012-03-02 04:15:15 +00:00
* /
if ( ! reset & & frame = = 1 )
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
}
else
{
/ *
Controller update : 0 aabbccc
* bb : Gamepad number minus one ( ? )
* /
2012-09-17 07:22:35 +00:00
int player = ( ( update > > 3 ) & 0x3 ) + 1 ;
2012-03-12 20:35:10 +00:00
if ( player > 2 )
2012-12-06 05:19:08 +00:00
{
2012-03-12 20:35:10 +00:00
fourscore = true ;
2012-12-06 05:19:08 +00:00
}
2012-03-02 04:15:15 +00:00
/ *
ccc :
2012-03-07 02:48:19 +00:00
* 0 A
* 1 B
* 2 Select
* 3 Start
* 4 Up
* 5 Down
* 6 Left
* 7 Right
2012-03-02 04:15:15 +00:00
* /
2012-09-17 07:22:35 +00:00
int button = update & 0x7 ;
2012-03-05 17:20:30 +00:00
/ *
2012-03-06 03:41:11 +00:00
The controller update toggles the affected input . Controller update data is emitted to the movie file
only when the state of the controller changes .
2012-03-05 17:20:30 +00:00
* /
2012-03-02 15:59:34 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ! controllers [ "P" + player + " " + buttons [ button ] ] ;
2012-03-02 04:15:15 +00:00
}
2011-07-06 01:53:15 +00:00
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "NES" ;
if ( fds ) { m . Header [ HeaderKeys . BOARDNAME ] = "FDS" ; }
m . Header [ HeaderKeys . FOURSCORE ] = fourscore . ToString ( ) ;
2012-02-24 04:44:40 +00:00
r . Close ( ) ;
2012-03-02 04:15:15 +00:00
fs . Close ( ) ;
2012-02-24 04:44:40 +00:00
return m ;
2011-07-06 01:53:15 +00:00
}
2012-02-24 04:44:40 +00:00
// FM2 file format: http://www.fceux.com/web/FM2.html
2013-11-10 02:55:11 +00:00
private static Movie ImportFM2 ( string path , out string errorMsg , out string warningMsg )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-11-10 02:55:11 +00:00
return ImportText ( path , out errorMsg , out warningMsg ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-02-24 05:18:59 +00:00
// FMV file format: http://tasvideos.org/FMV.html
2013-11-10 02:55:11 +00:00
private static Movie ImportFMV ( string path , out string errorMsg , out string warningMsg )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-02-28 23:11:19 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
// 000 4-byte signature: 46 4D 56 1A "FMV\x1A"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 4 ) ;
2012-02-28 23:11:19 +00:00
if ( signature ! = "FMV\x1A" )
{
errorMsg = "This is not a valid .FMV file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-28 23:11:19 +00:00
return null ;
}
// 004 1-byte flags:
byte flags = r . ReadByte ( ) ;
2012-03-06 03:41:11 +00:00
// bit 7: 0=reset-based, 1=savestate-based
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 2 ) & 0x1 ) ! = 0 )
2012-02-28 23:11:19 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-28 23:11:19 +00:00
return null ;
}
2012-03-06 03:41:11 +00:00
// other bits: unknown, set to 0
2012-02-28 23:11:19 +00:00
// 005 1-byte flags:
flags = r . ReadByte ( ) ;
2012-03-06 03:41:11 +00:00
// bit 5: is a FDS recording
2012-02-28 23:11:19 +00:00
bool FDS ;
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 5 ) & 0x1 ) ! = 0 )
2012-02-28 23:11:19 +00:00
{
FDS = true ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . BOARDNAME ] = "FDS" ;
2012-02-28 23:11:19 +00:00
}
else
{
FDS = false ;
2013-08-24 20:11:41 +00:00
2012-02-28 23:11:19 +00:00
}
2013-08-24 20:11:41 +00:00
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "NES" ;
2013-08-24 20:11:41 +00:00
2012-03-06 03:41:11 +00:00
// bit 6: uses controller 2
2012-09-17 07:22:35 +00:00
bool controller2 = ( ( ( flags > > 6 ) & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// bit 7: uses controller 1
2012-09-17 07:22:35 +00:00
bool controller1 = ( ( ( flags > > 7 ) & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// other bits: unknown, set to 0
2012-02-28 23:11:19 +00:00
// 006 4-byte little-endian unsigned int: unknown, set to 00000000
r . ReadInt32 ( ) ;
// 00A 4-byte little-endian unsigned int: rerecord count minus 1
uint rerecordCount = r . ReadUInt32 ( ) ;
2012-02-29 04:47:30 +00:00
/ *
2012-03-06 03:41:11 +00:00
The rerecord count stored in the file is the number of times a savestate was loaded . If a savestate was never
loaded , the number is 0. Famtasia however displays "1" in such case . It always adds 1 to the number found in
the file .
2012-02-29 04:47:30 +00:00
* /
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount + 1 ;
2012-02-28 23:11:19 +00:00
// 00E 2-byte little-endian unsigned int: unknown, set to 0000
r . ReadInt16 ( ) ;
// 010 64-byte zero-terminated emulator identifier string
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string emuVersion = NullTerminated ( r . ReadStringFixedAscii ( 64 ) ) ;
2012-03-06 03:41:11 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " Famtasia version " + emuVersion ) ;
2012-03-12 20:35:10 +00:00
m . Header . Comments . Add ( MOVIEORIGIN + " .FMV" ) ;
2012-02-28 23:11:19 +00:00
// 050 64-byte zero-terminated movie title string
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string description = NullTerminated ( r . ReadStringFixedAscii ( 64 ) ) ;
2012-03-11 12:24:56 +00:00
m . Header . Comments . Add ( COMMENT + " " + description ) ;
2012-02-29 04:47:30 +00:00
if ( ! controller1 & & ! controller2 & & ! FDS )
2012-02-28 23:11:19 +00:00
{
2012-02-29 04:47:30 +00:00
warningMsg = "No input recorded." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-29 04:47:30 +00:00
return m ;
}
/ *
2012-09-15 18:46:41 +00:00
The file format has no means of identifying NTSC / "PAL" . It is always assumed that the game is NTSC - that is ,
60 fps .
2012-02-29 04:47:30 +00:00
* /
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = "False" ;
2012-03-11 12:24:56 +00:00
// 090 frame data begins here
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } } ;
2012-03-11 12:24:56 +00:00
/ *
* 01 Right
* 02 Left
* 04 Up
* 08 Down
* 10 B
* 20 A
* 40 Select
* 80 Start
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ] { "Right" , "Left" , "Up" , "Down" , "B" , "A" , "Select" , "Start" } ;
bool [ ] masks = new [ ] { controller1 , controller2 , FDS } ;
2012-02-29 04:47:30 +00:00
/ *
2012-03-06 03:41:11 +00:00
The file has no terminator byte or frame count . The number of frames is the < filesize minus 144 > divided by
< number of bytes per frame > .
2012-02-29 04:47:30 +00:00
* /
int bytesPerFrame = 0 ;
2012-03-13 02:37:59 +00:00
for ( int player = 1 ; player < = masks . Length ; player + + )
2012-12-06 05:19:08 +00:00
{
2012-03-13 02:37:59 +00:00
if ( masks [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-03-13 02:37:59 +00:00
bytesPerFrame + + ;
2012-12-06 05:19:08 +00:00
}
}
2012-03-15 02:23:01 +00:00
long frameCount = ( fs . Length - 144 ) / bytesPerFrame ;
for ( long frame = 1 ; frame < = frameCount ; frame + + )
2012-02-29 04:47:30 +00:00
{
/ *
2012-03-06 03:41:11 +00:00
Each frame consists of 1 or more bytes . Controller 1 takes 1 byte , controller 2 takes 1 byte , and the FDS
data takes 1 byte . If all three exist , the frame is 3 bytes . For example , if the movie is a regular NES game
with only controller 1 data , a frame is 1 byte .
2012-02-29 04:47:30 +00:00
* /
2012-03-13 02:37:59 +00:00
for ( int player = 1 ; player < = masks . Length ; player + + )
2012-02-29 04:47:30 +00:00
{
2012-03-13 02:37:59 +00:00
if ( ! masks [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-03-13 02:37:59 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2012-03-13 02:37:59 +00:00
byte controllerState = r . ReadByte ( ) ;
2012-02-29 04:47:30 +00:00
if ( player ! = 3 )
2012-12-06 05:19:08 +00:00
{
2012-02-29 04:47:30 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
}
2012-03-13 02:37:59 +00:00
else
2012-12-06 05:19:08 +00:00
{
2012-03-15 04:22:37 +00:00
warningMsg = "FDS commands are not properly supported." ;
2012-12-06 05:19:08 +00:00
}
2012-02-29 04:47:30 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-02-28 23:11:19 +00:00
}
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-24 05:18:59 +00:00
return m ;
}
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
2012-02-24 05:18:59 +00:00
// GMV file format: http://code.google.com/p/gens-rerecording/wiki/GMV
2013-11-10 02:55:11 +00:00
private static Movie ImportGMV ( string path , out string errorMsg , out string warningMsg )
2012-02-24 05:18:59 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-03-07 02:48:19 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
// 000 16-byte signature and format version: "Gens Movie TEST9"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 15 ) ;
2012-03-07 02:48:19 +00:00
if ( signature ! = "Gens Movie TEST" )
{
errorMsg = "This is not a valid .GMV file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "Genesis" ;
2012-03-07 02:48:19 +00:00
// 00F ASCII-encoded GMV file format version. The most recent is 'A'. (?)
2012-03-11 12:24:56 +00:00
string version = r . ReadStringFixedAscii ( 1 ) ;
m . Header . Comments . Add ( MOVIEORIGIN + " .GMV version " + version ) ;
2012-03-12 20:35:10 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " Gens" ) ;
2012-03-07 02:48:19 +00:00
// 010 4-byte little-endian unsigned int: rerecord count
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-03-07 02:48:19 +00:00
// 014 ASCII-encoded controller config for player 1. '3' or '6'.
2012-03-06 05:27:50 +00:00
string player1Config = r . ReadStringFixedAscii ( 1 ) ;
2012-03-07 02:48:19 +00:00
// 015 ASCII-encoded controller config for player 2. '3' or '6'.
2012-03-06 05:27:50 +00:00
string player2Config = r . ReadStringFixedAscii ( 1 ) ;
2012-03-07 02:48:19 +00:00
if ( player1Config = = "6" | | player2Config = = "6" )
2012-12-06 05:19:08 +00:00
{
2012-03-11 12:24:56 +00:00
warningMsg = "6 button controllers are not properly supported." ;
2012-12-06 05:19:08 +00:00
}
2012-03-07 02:48:19 +00:00
// 016 special flags (Version A and up only)
2012-03-11 12:24:56 +00:00
byte flags = r . ReadByte ( ) ;
2012-03-07 02:48:19 +00:00
/ *
2012-09-15 18:46:41 +00:00
bit 7 ( most significant ) : if "1" , movie runs at 50 frames per second ; if "0" , movie runs at 60 frames per
second The file format has no means of identifying NTSC / "PAL" , but the FPS can still be derived from the
header .
2012-03-07 02:48:19 +00:00
* /
2012-09-17 07:22:35 +00:00
bool pal = ( ( ( flags > > 7 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-03-07 02:48:19 +00:00
// bit 6: if "1", movie requires a savestate.
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 6 ) & 0x1 ) ! = 0 )
2012-03-07 02:48:19 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
// bit 5: if "1", movie is 3-player movie; if "0", movie is 2-player movie
2012-09-17 07:22:35 +00:00
bool threePlayers = ( ( ( flags > > 5 ) & 0x1 ) ! = 0 ) ;
2012-03-07 02:48:19 +00:00
// Unknown.
r . ReadByte ( ) ;
// 018 40-byte zero-terminated ASCII movie name string
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string description = NullTerminated ( r . ReadStringFixedAscii ( 40 ) ) ;
2012-03-11 12:24:56 +00:00
m . Header . Comments . Add ( COMMENT + " " + description ) ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController
{
Type = new ControllerDefinition { Name = "Genesis 3-Button Controller" }
} ;
2012-03-07 02:48:19 +00:00
/ *
040 frame data
2012-09-15 18:46:41 +00:00
For controller bytes , each value is determined by OR - ing together values for whichever of the following are
left unpressed :
2012-03-07 02:48:19 +00:00
* 0x01 Up
* 0x02 Down
* 0x04 Left
* 0x08 Right
* 0x10 A
* 0x20 B
* 0x40 C
* 0x80 Start
* /
2013-10-27 18:18:58 +00:00
string [ ] buttons = new [ ] { "Up" , "Down" , "Left" , "Right" , "A" , "B" , "C" , "Start" } ;
2012-03-07 02:48:19 +00:00
/ *
For XYZ - mode , each value is determined by OR - ing together values for whichever of the following are left
unpressed :
* 0x01 Controller 1 X
* 0x02 Controller 1 Y
* 0x04 Controller 1 Z
* 0x08 Controller 1 Mode
* 0x10 Controller 2 X
* 0x20 Controller 2 Y
* 0x40 Controller 2 Z
* 0x80 Controller 2 Mode
* /
2013-10-27 18:18:58 +00:00
string [ ] other = new [ ] { "X" , "Y" , "Z" , "Mode" } ;
2012-03-11 12:24:56 +00:00
// The file has no terminator byte or frame count. The number of frames is the <filesize minus 64> divided by 3.
2012-03-15 02:23:01 +00:00
long frameCount = ( fs . Length - 64 ) / 3 ;
for ( long frame = 1 ; frame < = frameCount ; frame + + )
2012-03-07 02:48:19 +00:00
{
// Each frame consists of 3 bytes.
2012-03-11 12:24:56 +00:00
for ( int player = 1 ; player < = 3 ; player + + )
2012-03-07 02:48:19 +00:00
{
2012-03-11 12:24:56 +00:00
byte controllerState = r . ReadByte ( ) ;
// * is controller 3 if a 3-player movie, or XYZ-mode if a 2-player movie.
if ( player ! = 3 | | threePlayers )
2012-12-06 05:19:08 +00:00
{
2012-03-11 12:24:56 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) = = 0 ) ;
2012-12-06 05:19:08 +00:00
}
}
2012-03-11 12:24:56 +00:00
else
2012-12-06 05:19:08 +00:00
{
2012-03-11 12:24:56 +00:00
for ( int button = 0 ; button < other . Length ; button + + )
{
if ( player1Config = = "6" )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P1 " + other [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) = = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-11 12:24:56 +00:00
if ( player2Config = = "6" )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P2 " + other [ button ] ] = ( ( ( controllerState > > ( button + 4 ) ) & 0x1 ) = = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-11 12:24:56 +00:00
}
2012-12-06 05:19:08 +00:00
}
2012-03-07 02:48:19 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-03-07 02:48:19 +00:00
}
2012-03-01 09:16:14 +00:00
return m ;
}
2012-09-10 21:35:56 +00:00
// LSMV file format: http://tasvideos.org/Lsnes/Movieformat.html
2013-11-10 02:55:11 +00:00
private static Movie ImportLSMV ( string path , out string errorMsg , out string warningMsg )
2012-09-10 21:35:56 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-09-12 04:11:35 +00:00
HawkFile hf = new HawkFile ( path ) ;
// .LSMV movies are .zip files containing data files.
if ( ! hf . IsArchive )
{
errorMsg = "This is not an archive." ;
return null ;
}
2012-09-27 06:23:05 +00:00
string platform = "SNES" ;
2012-09-12 04:11:35 +00:00
foreach ( var item in hf . ArchiveItems )
{
2014-02-04 21:15:33 +00:00
if ( item . Name = = "authors" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string authors = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) ;
string author_list = "" ;
string author_last = "" ;
using ( StringReader reader = new StringReader ( authors ) )
{
string line ;
// Each author is on a different line.
while ( ( line = reader . ReadLine ( ) ) ! = null )
{
string author = line . Trim ( ) ;
if ( author ! = "" )
{
if ( author_last ! = "" )
2012-12-06 05:19:08 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
author_list + = author_last + ", " ;
2012-12-06 05:19:08 +00:00
}
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
author_last = author ;
}
}
}
if ( author_list ! = "" )
2012-12-06 05:19:08 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
author_list + = "and " ;
2012-12-06 05:19:08 +00:00
}
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
if ( author_last ! = "" )
2012-12-06 05:19:08 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
author_list + = author_last ;
2012-12-06 05:19:08 +00:00
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author_list ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "coreversion" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string coreversion = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
m . Header . Comments . Add ( COREORIGIN + " " + coreversion ) ;
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "gamename" )
2012-09-13 02:45:08 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-09-13 02:45:08 +00:00
var stream = hf . GetStream ( ) ;
string gamename = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gamename ;
2012-09-13 02:45:08 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "gametype" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string gametype = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
// TODO: Handle the other types.
2012-09-27 06:23:05 +00:00
switch ( gametype )
{
case "gdmg" :
platform = "GB" ;
break ;
case "ggbc" :
case "ggbca" :
platform = "GBC" ;
break ;
case "sgb_ntsc" :
case "sgb_pal" :
2012-10-05 18:20:27 +00:00
platform = "SNES" ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . SGB ] = "True" ;
2012-09-27 06:23:05 +00:00
break ;
}
bool pal = ( gametype = = "snes_pal" | | gametype = = "sgb_pal" ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "input" )
2012-09-12 04:11:35 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-09-12 04:11:35 +00:00
var stream = hf . GetStream ( ) ;
string input = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) ;
2012-09-12 05:59:09 +00:00
int lineNum = 0 ;
using ( StringReader reader = new StringReader ( input ) )
{
lineNum + + ;
string line ;
while ( ( line = reader . ReadLine ( ) ) ! = null )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2012-09-13 10:26:44 +00:00
if ( line = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-13 10:26:44 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2013-10-27 18:18:58 +00:00
m = ImportTextFrame ( line , lineNum , m , path , platform , ref warningMsg ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
if ( errorMsg ! = "" )
{
hf . Unbind ( ) ;
return null ;
}
}
2012-09-12 05:59:09 +00:00
}
2012-09-13 02:45:08 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name . StartsWith ( "moviesram." ) )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-09-27 06:23:05 +00:00
var stream = hf . GetStream ( ) ;
byte [ ] moviesram = Util . ReadAllBytes ( stream ) ;
if ( moviesram . Length ! = 0 )
{
errorMsg = "Movies that begin with SRAM are not supported." ;
hf . Unbind ( ) ;
return null ;
}
2012-09-15 18:46:41 +00:00
hf . Unbind ( ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "port1" )
2012-10-04 06:50:47 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-10-04 06:50:47 +00:00
var stream = hf . GetStream ( ) ;
string port1 = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ PORT1 ] = port1 ;
2012-10-04 06:50:47 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "port2" )
2012-10-04 06:50:47 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-10-04 06:50:47 +00:00
var stream = hf . GetStream ( ) ;
string port2 = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ PORT2 ] = port2 ;
2012-10-04 06:50:47 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "projectid" )
2012-10-04 06:50:47 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-10-04 06:50:47 +00:00
var stream = hf . GetStream ( ) ;
string projectid = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ PROJECTID ] = projectid ;
2012-10-04 06:50:47 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "rerecords" )
2012-09-13 02:45:08 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-09-13 02:45:08 +00:00
var stream = hf . GetStream ( ) ;
string rerecords = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) ;
int rerecordCount ;
// Try to parse the re-record count as an integer, defaulting to 0 if it fails.
try
{
rerecordCount = int . Parse ( rerecords ) ;
}
catch
{
rerecordCount = 0 ;
}
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = ( ulong ) rerecordCount ;
2012-09-13 02:45:08 +00:00
hf . Unbind ( ) ;
2012-09-12 04:11:35 +00:00
}
2014-02-04 21:15:33 +00:00
else if ( item . Name . EndsWith ( ".sha256" ) )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string rom = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2014-02-04 21:15:33 +00:00
int pos = item . Name . LastIndexOf ( ".sha256" ) ;
string name = item . Name . Substring ( 0 , pos ) ;
2013-11-30 02:20:34 +00:00
m . Header [ SHA256 + "_" + name ] = rom ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "savestate" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
return null ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "subtitles" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string subtitles = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) ;
using ( StringReader reader = new StringReader ( subtitles ) )
{
string line ;
while ( ( line = reader . ReadLine ( ) ) ! = null )
m = ImportTextSubtitle ( line , m , path ) ;
}
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "starttime.second" )
2012-10-04 06:50:47 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-10-04 06:50:47 +00:00
var stream = hf . GetStream ( ) ;
string startSecond = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ STARTSECOND ] = startSecond ;
2012-10-04 06:50:47 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "starttime.subsecond" )
2012-10-04 06:50:47 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
2012-10-04 06:50:47 +00:00
var stream = hf . GetStream ( ) ;
string startSubSecond = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ STARTSUBSECOND ] = startSubSecond ;
2012-10-04 06:50:47 +00:00
hf . Unbind ( ) ;
}
2014-02-04 21:15:33 +00:00
else if ( item . Name = = "systemid" )
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
{
2014-02-04 21:15:33 +00:00
hf . BindArchiveMember ( item . Index ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
var stream = hf . GetStream ( ) ;
string systemid = Encoding . UTF8 . GetString ( Util . ReadAllBytes ( stream ) ) . Trim ( ) ;
m . Header . Comments . Add ( EMULATIONORIGIN + " " + systemid ) ;
hf . Unbind ( ) ;
}
2012-09-12 04:11:35 +00:00
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = platform ;
2012-09-10 21:35:56 +00:00
return m ;
}
2012-03-15 02:23:01 +00:00
/ *
MCM file format : http : //code.google.com/p/mednafen-rr/wiki/MCM
Mednafen - rr switched to MC2 from r261 , so see r260 for details .
* /
2013-11-10 02:55:11 +00:00
private static Movie ImportMCM ( string path , out string errorMsg , out string warningMsg )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-03-09 09:24:47 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
2012-12-06 05:19:08 +00:00
// 000 8-byte "MDFNMOVI" signature
2012-03-15 02:23:01 +00:00
string signature = r . ReadStringFixedAscii ( 8 ) ;
if ( signature ! = "MDFNMOVI" )
2012-03-09 09:24:47 +00:00
{
errorMsg = "This is not a valid .MCM file." ;
2012-03-15 02:23:01 +00:00
r . Close ( ) ;
fs . Close ( ) ;
return null ;
2012-03-09 09:24:47 +00:00
}
2012-12-06 05:19:08 +00:00
// 008 uint32 Mednafen Version (Current is 0A 08)
2012-03-09 09:24:47 +00:00
uint emuVersion = r . ReadUInt32 ( ) ;
m . Header . Comments . Add ( EMULATIONORIGIN + " Mednafen " + emuVersion . ToString ( ) ) ;
2012-12-06 05:19:08 +00:00
// 00C uint32 Movie Format Version (Current is 01)
2012-03-09 09:24:47 +00:00
uint version = r . ReadUInt32 ( ) ;
m . Header . Comments . Add ( MOVIEORIGIN + " .MCM version " + version ) ;
2012-12-06 05:19:08 +00:00
// 010 32-byte MD5 of the ROM used
2012-10-04 06:14:21 +00:00
byte [ ] md5 = r . ReadBytes ( 16 ) ;
2012-03-15 02:23:01 +00:00
// Discard the second 16 bytes.
r . ReadBytes ( 16 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ MD5 ] = Util . BytesToHexString ( md5 ) . ToLower ( ) ;
2012-12-06 05:19:08 +00:00
// 030 64-byte Filename of the ROM used (with extension)
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string gameName = NullTerminated ( r . ReadStringFixedAscii ( 64 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gameName ;
2012-12-06 05:19:08 +00:00
// 070 uint32 Re-record Count
2012-03-09 09:24:47 +00:00
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = ( ulong ) rerecordCount ;
2012-12-06 05:19:08 +00:00
// 074 5-byte Console indicator (pce, ngp, pcfx, wswan)
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string platform = NullTerminated ( r . ReadStringFixedAscii ( 5 ) ) ;
2013-04-15 02:14:14 +00:00
Dictionary < string , Dictionary < string , object > > platforms = new Dictionary < string , Dictionary < string , object > >
{
2012-03-15 04:22:37 +00:00
{
/ *
2012-09-15 18:46:41 +00:00
Normally , NES receives from 5 input ports , where the first 4 have a length of 1 byte , and the last has
a length of 0. For the sake of simplicity , it is interpreted as 4 ports of 1 byte length for
re - recording .
2012-03-15 04:22:37 +00:00
* /
"nes" , new Dictionary < string , object >
{
{ "name" , "NES" } , { "ports" , 4 } , { "bytesPerPort" , 1 } ,
2013-04-15 02:14:14 +00:00
{ "buttons" , new [ ] { "A" , "B" , "Select" , "Start" , "Up" , "Down" , "Left" , "Right" } }
2012-03-15 04:22:37 +00:00
}
} ,
{
"pce" , new Dictionary < string , object >
{
{ "name" , "PC Engine" } , { "ports" , 5 } , { "bytesPerPort" , 2 } ,
2013-04-15 02:14:14 +00:00
{ "buttons" , new [ ] { "B1" , "B2" , "Select" , "Run" , "Up" , "Right" , "Down" , "Left" } }
2012-03-15 04:22:37 +00:00
}
}
} ;
if ( ! platforms . ContainsKey ( platform ) )
{
errorMsg = "Platform " + platform + " not supported." ;
2012-03-15 02:23:01 +00:00
r . Close ( ) ;
fs . Close ( ) ;
return null ;
2012-03-09 09:24:47 +00:00
}
2012-03-15 04:22:37 +00:00
string name = ( string ) platforms [ platform ] [ "name" ] ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = name ;
2012-12-06 05:19:08 +00:00
// 079 32-byte Author name
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string author = NullTerminated ( r . ReadStringFixedAscii ( 32 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-03-15 02:23:01 +00:00
// 099 103-byte Padding 0s
r . ReadBytes ( 103 ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
// TODO: Verify if NTSC/"PAL" mode used for the movie can be detected or not.
2012-03-15 02:23:01 +00:00
// 100 variable Input data
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = name + " Controller" } } ;
2012-03-15 04:22:37 +00:00
int bytes = 256 ;
// The input stream consists of 1 byte for power-on and reset, and then X bytes per each input port per frame.
if ( platform = = "nes" )
{
// Power-on.
r . ReadByte ( ) ;
bytes + + ;
}
string [ ] buttons = ( string [ ] ) platforms [ platform ] [ "buttons" ] ;
int ports = ( int ) platforms [ platform ] [ "ports" ] ;
int bytesPerPort = ( int ) platforms [ platform ] [ "bytesPerPort" ] ;
// Frame Size (with Control Byte)
int size = ( ports * bytesPerPort ) + 1 ;
long frameCount = ( fs . Length - bytes ) / size ;
2012-03-15 02:23:01 +00:00
for ( int frame = 1 ; frame < = frameCount ; frame + + )
2012-03-09 09:24:47 +00:00
{
2012-03-15 04:22:37 +00:00
for ( int player = 1 ; player < = ports ; player + + )
2012-03-09 09:24:47 +00:00
{
2012-03-15 04:22:37 +00:00
if ( bytesPerPort = = 2 )
2012-12-06 05:19:08 +00:00
{
2012-03-15 04:22:37 +00:00
// Discard the first byte.
r . ReadByte ( ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-15 02:23:01 +00:00
ushort controllerState = r . ReadByte ( ) ;
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-09 09:24:47 +00:00
}
2012-03-15 02:23:01 +00:00
r . ReadByte ( ) ;
2012-03-15 04:22:37 +00:00
if ( platform = = "nes" & & warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-15 04:22:37 +00:00
warningMsg = "Control commands are not properly supported." ;
2012-12-06 05:19:08 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-03-09 09:24:47 +00:00
}
r . Close ( ) ;
fs . Close ( ) ;
2012-03-09 12:18:16 +00:00
return m ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-02-24 04:44:40 +00:00
// MC2 file format: http://code.google.com/p/pcejin/wiki/MC2
2013-11-10 02:55:11 +00:00
private static Movie ImportMC2 ( string path , out string errorMsg , out string warningMsg )
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
{
2013-11-10 02:55:11 +00:00
return ImportText ( path , out errorMsg , out warningMsg ) ;
-Began working on the importer.
--Created ImportFile to decide what function to use for each filetype.
---It currently automatically writes to a .TAS file, but that option will eventually only be applied when specified in the GUI, hopefully completely external from this class.
--Made IsValidMovieExtension work.
--Created LoadText to do the majority of the work that both .FM2 and .MC2 need to be done.
--.MC2 seems to work perfectly, not that it was a hard conversion!
--.FM2 seems to convert most headers correct, except for subtitles, which replaces the beginning portions of each subtitle's text with 0 0 120 4294967295. Not sure what that's about, though this sure feels like deja vu...
--I still need to switch around the order of the buttons the frames are added, but I need to find out what way I can do this without reinventing the wheel.
-Added the FixMnemonic function to Movie.cs. It currently does nothing, but my goal is to have it correct the mnemonic for all frames in a movie file based on the position of the characters.
--As of right now, ImportFile uses this.
-MainForm.IsValidMovieExtension only checks whether or not its .TAS or not now.
TODO:
-Fix the FM2 subtitles.
-Shift around the FM2 buttons.
--After completed, test a .FM2 file that should sync and see if it works, with and without FixMnemonic being used.
-Make FixMnemonic actually do something.
-Refactor code? I originally thought it'd be best to treat Movie.LoadText just like any other importer, but I think at this point it might just be best to keep these things completely separate.
-Consider the possibility of working with the binary file importers.
--Yes adelikat, I am somewhat interested, especially considering how useful it would be to have a working .FCM importer so I can compare old runs when TASing. I already was hoping to learn about .VBM and .SMV for my ButtonCount.lua script. By the way, might this be bundled with bizhawk as it is with FCEUX 2.1.6? :)
2012-02-15 06:54:09 +00:00
}
2012-02-24 05:18:59 +00:00
// MMV file format: http://tasvideos.org/MMV.html
2013-11-10 02:55:11 +00:00
private static Movie ImportMMV ( string path , out string errorMsg , out string warningMsg )
2011-07-06 01:53:15 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2011-07-06 01:53:15 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
2012-02-28 10:59:16 +00:00
// 0000: 4-byte signature: "MMV\0"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 4 ) ;
2011-07-06 01:53:15 +00:00
if ( signature ! = "MMV\0" )
2011-07-13 02:11:20 +00:00
{
2012-02-28 23:11:19 +00:00
errorMsg = "This is not a valid .MMV file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-13 02:11:20 +00:00
return null ;
}
2012-02-28 10:59:16 +00:00
// 0004: 4-byte little endian unsigned int: dega version
2012-02-28 23:11:19 +00:00
uint emuVersion = r . ReadUInt32 ( ) ;
2012-03-06 03:41:11 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " Dega version " + emuVersion . ToString ( ) ) ;
2012-03-12 20:35:10 +00:00
m . Header . Comments . Add ( MOVIEORIGIN + " .MMV" ) ;
2012-02-28 10:59:16 +00:00
// 0008: 4-byte little endian unsigned int: frame count
2012-03-01 09:16:14 +00:00
uint frameCount = r . ReadUInt32 ( ) ;
2012-02-28 10:59:16 +00:00
// 000c: 4-byte little endian unsigned int: rerecord count
2012-02-28 23:11:19 +00:00
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-02-28 10:59:16 +00:00
// 0010: 4-byte little endian flag: begin from reset?
2012-02-28 23:11:19 +00:00
uint reset = r . ReadUInt32 ( ) ;
if ( reset = = 0 )
2011-07-13 02:11:20 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-13 02:11:20 +00:00
return null ;
}
2012-02-28 10:59:16 +00:00
// 0014: 4-byte little endian unsigned int: offset of state information
r . ReadUInt32 ( ) ;
// 0018: 4-byte little endian unsigned int: offset of input data
r . ReadUInt32 ( ) ;
// 001c: 4-byte little endian unsigned int: size of input packet
r . ReadUInt32 ( ) ;
// 0020-005f: string: author info (UTF-8)
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string author = NullTerminated ( r . ReadStringFixedAscii ( 64 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-02-28 10:59:16 +00:00
// 0060: 4-byte little endian flags
2011-07-06 01:53:15 +00:00
byte flags = r . ReadByte ( ) ;
2012-03-06 03:41:11 +00:00
// bit 0: unused
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
// bit 1: "PAL"
2012-09-17 07:22:35 +00:00
bool pal = ( ( ( flags > > 1 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-03-06 03:41:11 +00:00
// bit 2: Japan
2012-09-17 07:22:35 +00:00
bool japan = ( ( ( flags > > 2 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ JAPAN ] = japan . ToString ( ) ;
2012-03-06 03:41:11 +00:00
// bit 3: Game Gear (version 1.16+)
2011-07-06 01:53:15 +00:00
bool gamegear ;
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 3 ) & 0x1 ) ! = 0 )
2011-07-06 01:53:15 +00:00
{
gamegear = true ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "GG" ;
2011-07-06 01:53:15 +00:00
}
else
{
gamegear = false ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "SMS" ;
2011-07-06 01:53:15 +00:00
}
2012-02-28 10:59:16 +00:00
// bits 4-31: unused
r . ReadBytes ( 3 ) ;
// 0064-00e3: string: rom name (ASCII)
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string gameName = NullTerminated ( r . ReadStringFixedAscii ( 128 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gameName ;
2012-02-28 10:59:16 +00:00
// 00e4-00f3: binary: rom MD5 digest
2012-10-04 06:14:21 +00:00
byte [ ] md5 = r . ReadBytes ( 16 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ MD5 ] = String . Format ( "{0:x8}" , Util . BytesToHexString ( md5 ) . ToLower ( ) ) ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SMS Controller" } } ;
2012-03-02 04:15:15 +00:00
/ *
76543210
* bit 0 ( 0x01 ) : up
* bit 1 ( 0x02 ) : down
* bit 2 ( 0x04 ) : left
* bit 3 ( 0x08 ) : right
* bit 4 ( 0x10 ) : 1
* bit 5 ( 0x20 ) : 2
* bit 6 ( 0x40 ) : start ( Master System )
* bit 7 ( 0x80 ) : start ( Game Gear )
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ] { "Up" , "Down" , "Left" , "Right" , "B1" , "B2" } ;
2012-03-02 04:15:15 +00:00
for ( int frame = 1 ; frame < = frameCount ; frame + + )
2011-07-06 01:53:15 +00:00
{
2012-02-29 04:47:30 +00:00
/ *
2012-03-06 03:41:11 +00:00
Controller data is made up of one input packet per frame . Each packet currently consists of 2 bytes . The
first byte is for controller 1 and the second controller 2. The Game Gear only uses the controller 1 input
however both bytes are still present .
2012-02-29 04:47:30 +00:00
* /
2012-02-19 07:09:24 +00:00
for ( int player = 1 ; player < = 2 ; player + + )
{
2012-03-06 03:41:11 +00:00
byte controllerState = r . ReadByte ( ) ;
2012-02-24 04:44:40 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-02-19 07:09:24 +00:00
if ( player = = 1 )
2012-12-06 05:19:08 +00:00
{
2012-02-24 04:44:40 +00:00
controllers [ "Pause" ] = (
2012-09-17 07:22:35 +00:00
( ( ( controllerState > > 6 ) & 0x1 ) ! = 0 & & ( ! gamegear ) ) | |
( ( ( controllerState > > 7 ) & 0x1 ) ! = 0 & & gamegear )
2012-02-24 04:44:40 +00:00
) ;
2012-12-06 05:19:08 +00:00
}
2012-02-19 07:09:24 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2011-07-06 01:53:15 +00:00
}
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-06 01:53:15 +00:00
return m ;
}
2012-03-11 12:24:56 +00:00
// NMV file format: http://tasvideos.org/NMV.html
2013-11-10 02:55:11 +00:00
private static Movie ImportNMV ( string path , out string errorMsg , out string warningMsg )
2012-03-11 12:24:56 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-03-11 12:24:56 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
// 000 4-byte signature: 4E 53 53 1A "NSS\x1A"
string signature = r . ReadStringFixedAscii ( 4 ) ;
if ( signature ! = "NSS\x1A" )
{
errorMsg = "This is not a valid .NMV file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
2012-03-14 20:08:00 +00:00
// 004 4-byte version string (example "0960")
string emuVersion = r . ReadStringFixedAscii ( 4 ) ;
m . Header . Comments . Add ( EMULATIONORIGIN + " Nintendulator version " + emuVersion ) ;
2012-03-12 20:35:10 +00:00
m . Header . Comments . Add ( MOVIEORIGIN + " .NMV" ) ;
2012-03-14 20:08:00 +00:00
// 008 4-byte file size, not including the 16-byte header
r . ReadUInt32 ( ) ;
/ *
00 C 4 - byte file type string
* "NSAV" - standard savestate
* "NREC" - savestate saved during movie recording
* "NMOV" - standalone movie file
* /
string type = r . ReadStringFixedAscii ( 4 ) ;
if ( type ! = "NMOV" )
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
/ *
2012-09-15 18:46:41 +00:00
Individual blocks begin with an 8 - byte header , consisting of a 4 - byte signature and a 4 - byte length ( which
does not include the length of the block header ) .
2012-03-14 20:08:00 +00:00
The final block in the file is of type "NMOV"
* /
string header = r . ReadStringFixedAscii ( 4 ) ;
if ( header ! = "NMOV" )
{
errorMsg = "This is not a valid .NMV file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
r . ReadUInt32 ( ) ;
// 000 1-byte controller #1 type (see below)
byte controller1 = r . ReadByte ( ) ;
// 001 1-byte controller #2 type (or four-score mask, see below)
byte controller2 = r . ReadByte ( ) ;
/ *
2012-09-15 18:46:41 +00:00
Controller data is variant , depending on which controllers are attached at the time of recording . The
following controllers are implemented :
2012-03-14 20:08:00 +00:00
* 0 - Unconnected
* 1 - Standard Controller ( 1 byte )
* 2 - Zapper ( 3 bytes )
* 3 - Arkanoid Paddle ( 2 bytes )
* 4 - Power Pad ( 2 bytes )
* 5 - Four - Score ( special )
* 6 - SNES controller ( 2 bytes ) - A / B become B / Y , adds A / X and L / R shoulder buttons
* 7 - Vs Unisystem Zapper ( 3 bytes )
* /
bool fourscore = ( controller1 = = 5 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . FOURSCORE ] = fourscore . ToString ( ) ;
2013-04-15 02:14:14 +00:00
bool [ ] masks = new [ ] { false , false , false , false , false } ;
2012-03-14 20:08:00 +00:00
if ( fourscore )
{
/ *
When a Four - Score is indicated for Controller # 1 , the Controller # 2 byte becomes a bit mask to indicate
which ports on the Four - Score have controllers connected to them . Each connected controller stores 1 byte
per frame . Nintendulator ' s Four - Score recording is seemingly broken .
* /
for ( int controller = 1 ; controller < masks . Length ; controller + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
masks [ controller - 1 ] = ( ( ( controller2 > > ( controller - 1 ) ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
warningMsg = "Nintendulator's Four Score recording is seemingly broken." ;
}
else
{
2013-04-15 02:14:14 +00:00
byte [ ] types = new [ ] { controller1 , controller2 } ;
2012-03-14 20:08:00 +00:00
for ( int controller = 1 ; controller < = types . Length ; controller + + )
{
masks [ controller - 1 ] = ( types [ controller - 1 ] = = 1 ) ;
// Get the first unsupported controller warning message that arises.
if ( warningMsg = = "" )
{
switch ( types [ controller - 1 ] )
{
case 0 :
break ;
case 2 :
warningMsg = "Zapper" ;
break ;
case 3 :
warningMsg = "Arkanoid Paddle" ;
break ;
case 4 :
warningMsg = "Power Pad" ;
break ;
case 5 :
warningMsg = "A Four Score in the second controller port is invalid." ;
continue ;
case 6 :
warningMsg = "SNES controller" ;
break ;
case 7 :
warningMsg = "Vs Unisystem Zapper" ;
break ;
}
if ( warningMsg ! = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
warningMsg = warningMsg + " is not properly supported." ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
}
}
}
// 002 1-byte expansion port controller type
byte expansion = r . ReadByte ( ) ;
/ *
2012-09-15 18:46:41 +00:00
The expansion port can potentially have an additional controller connected . The following expansion
controllers are implemented :
2012-03-14 20:08:00 +00:00
* 0 - Unconnected
* 1 - Famicom 4 - player adapter ( 2 bytes )
* 2 - Famicom Arkanoid paddle ( 2 bytes )
* 3 - Family Basic Keyboard ( currently does not support demo recording )
* 4 - Alternate keyboard layout ( currently does not support demo recording )
* 5 - Family Trainer ( 2 bytes )
* 6 - Oeka Kids writing tablet ( 3 bytes )
* /
2013-04-15 02:14:14 +00:00
string [ ] expansions = new [ ] {
2012-03-14 20:08:00 +00:00
"Unconnected" , "Famicom 4-player adapter" , "Famicom Arkanoid paddle" , "Family Basic Keyboard" ,
"Alternate keyboard layout" , "Family Trainer" , "Oeka Kids writing tablet"
} ;
if ( expansion ! = 0 & & warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
warningMsg = "Expansion port is not properly supported. This movie uses " + expansions [ expansion ] + "." ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
// 003 1-byte number of bytes per frame, plus flags
byte data = r . ReadByte ( ) ;
int bytesPerFrame = data & 0xF ;
int bytes = 0 ;
for ( int controller = 1 ; controller < masks . Length ; controller + + )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
if ( masks [ controller - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
bytes + + ;
2012-12-06 05:19:08 +00:00
}
}
2012-03-14 20:08:00 +00:00
/ *
Depending on the mapper used by the game in question , an additional byte of data may be stored during each
frame . This is most frequently used for FDS games ( storing either the disk number or 0xFF to eject ) or VS
Unisystem coin / DIP switch toggles ( limited to 1 action per frame ) . This byte exists if the bytes per frame do
not match up with the amount of bytes the controllers take up .
* /
if ( bytes ! = bytesPerFrame )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
masks [ 4 ] = true ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
// bit 6: Game Genie active
/ *
bit 7 : Framerate
* if "0" , NTSC timing
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
* if "1" , "PAL" timing
2012-03-14 20:08:00 +00:00
* /
2012-09-17 07:22:35 +00:00
bool pal = ( ( ( data > > 7 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-03-14 20:08:00 +00:00
// 004 4-byte little-endian unsigned int: rerecord count
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-03-14 20:08:00 +00:00
/ *
008 4 - byte little - endian unsigned int : length of movie description
00 C ( variable ) null - terminated UTF - 8 text , movie description ( currently not implemented )
* /
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string movieDescription = NullTerminated ( r . ReadStringFixedAscii ( ( int ) r . ReadUInt32 ( ) ) ) ;
2012-03-14 20:08:00 +00:00
m . Header . Comments . Add ( COMMENT + " " + movieDescription ) ;
// ... 4-byte little-endian unsigned int: length of controller data in bytes
uint length = r . ReadUInt32 ( ) ;
// ... (variable) controller data
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } } ;
2012-03-14 20:08:00 +00:00
/ *
Standard controllers store data in the following format :
* 01 : A
* 02 : B
* 04 : Select
* 08 : Start
* 10 : Up
* 20 : Down
* 40 : Left
* 80 : Right
Other controllers store data in their own formats , and are beyond the scope of this document .
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ] { "A" , "B" , "Select" , "Start" , "Up" , "Down" , "Left" , "Right" } ;
2012-03-14 20:08:00 +00:00
// The controller data contains <number_of_bytes> / <bytes_per_frame> frames.
2012-03-15 02:23:01 +00:00
long frameCount = length / bytesPerFrame ;
for ( int frame = 1 ; frame < = frameCount ; frame + + )
2012-03-14 20:08:00 +00:00
{
// Controller update data is emitted to the movie file during every frame.
for ( int player = 1 ; player < = masks . Length ; player + + )
{
if ( ! masks [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
byte controllerState = r . ReadByte ( ) ;
if ( player ! = 5 )
2012-12-06 05:19:08 +00:00
{
2012-03-14 20:08:00 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
}
2012-03-14 20:08:00 +00:00
else if ( warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-15 04:22:37 +00:00
warningMsg = "Extra input is not properly supported." ;
2012-12-06 05:19:08 +00:00
}
2012-03-14 20:08:00 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-03-14 20:08:00 +00:00
}
2012-03-11 12:24:56 +00:00
r . Close ( ) ;
fs . Close ( ) ;
return m ;
}
2013-11-10 02:55:11 +00:00
private static Movie ImportSMV ( string path , out string errorMsg , out string warningMsg )
2011-07-06 01:53:15 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2011-07-06 01:53:15 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
2012-09-13 19:33:55 +00:00
// 000 4-byte signature: 53 4D 56 1A "SMV\x1A"
string signature = r . ReadStringFixedAscii ( 4 ) ;
if ( signature ! = "SMV\x1A" )
2011-07-14 01:12:18 +00:00
{
2012-02-28 23:11:19 +00:00
errorMsg = "This is not a valid .SMV file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-14 01:12:18 +00:00
return null ;
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "SNES" ;
2012-09-13 19:33:55 +00:00
// 004 4-byte little-endian unsigned int: version number
2012-09-15 09:38:20 +00:00
uint versionNumber = r . ReadUInt32 ( ) ;
string version ;
switch ( versionNumber )
2011-07-06 01:53:15 +00:00
{
case 1 :
2012-09-15 09:38:20 +00:00
version = "1.43" ;
2012-09-13 19:33:55 +00:00
break ;
2011-07-06 01:53:15 +00:00
case 4 :
2012-09-15 09:38:20 +00:00
version = "1.51" ;
2012-09-13 19:33:55 +00:00
break ;
2011-07-06 01:53:15 +00:00
case 5 :
2012-09-15 09:38:20 +00:00
version = "1.52" ;
2012-09-13 19:33:55 +00:00
break ;
2011-07-06 01:53:15 +00:00
default :
2012-09-10 21:35:56 +00:00
errorMsg = "SMV version not recognized. 1.43, 1.51, and 1.52 are currently supported." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-02-24 05:18:59 +00:00
return null ;
2011-07-06 01:53:15 +00:00
}
2012-09-15 09:38:20 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " Snes9x version " + version ) ;
2012-09-13 19:33:55 +00:00
m . Header . Comments . Add ( MOVIEORIGIN + " .SMV" ) ;
/ *
008 4 - byte little - endian integer : movie "uid" - identifies the movie - savestate relationship , also used as the
recording time in Unix epoch format
* /
uint uid = r . ReadUInt32 ( ) ;
2013-12-04 03:04:29 +00:00
2012-09-13 19:33:55 +00:00
// 00C 4-byte little-endian unsigned int: rerecord count
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = r . ReadUInt32 ( ) ;
2012-09-13 19:33:55 +00:00
// 010 4-byte little-endian unsigned int: number of frames
2012-03-01 09:16:14 +00:00
uint frameCount = r . ReadUInt32 ( ) ;
2012-09-13 19:33:55 +00:00
// 014 1-byte flags "controller mask"
2012-09-15 09:38:20 +00:00
byte controllerFlags = r . ReadByte ( ) ;
2012-09-13 19:33:55 +00:00
/ *
* bit 0 : controller 1 in use
* bit 1 : controller 2 in use
* bit 2 : controller 3 in use
* bit 3 : controller 4 in use
* bit 4 : controller 5 in use
* other : reserved , set to 0
* /
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SNES Controller" } } ;
2012-09-17 04:16:27 +00:00
bool [ ] controllersUsed = new bool [ 5 ] ;
for ( int controller = 1 ; controller < = controllersUsed . Length ; controller + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
controllersUsed [ controller - 1 ] = ( ( ( controllerFlags > > ( controller - 1 ) ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-13 19:33:55 +00:00
// 015 1-byte flags "movie options"
2012-09-15 09:38:20 +00:00
byte movieFlags = r . ReadByte ( ) ;
2012-09-13 19:33:55 +00:00
/ *
bit 0 :
if "0" , movie begins from an embedded "quicksave" snapshot
if "1" , a SRAM is included instead of a quicksave ; movie begins from reset
* /
2012-09-15 09:38:20 +00:00
if ( ( movieFlags & 0x1 ) = = 0 )
2012-09-13 19:33:55 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
2012-09-15 09:38:20 +00:00
fs . Close ( ) ;
2012-09-13 19:33:55 +00:00
return null ;
}
// bit 1: if "0", movie is NTSC (60 fps); if "1", movie is PAL (50 fps)
2012-09-15 09:38:20 +00:00
bool pal = ( ( ( movieFlags > > 1 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-09-13 19:33:55 +00:00
// other: reserved, set to 0
/ *
016 1 - byte flags "sync options" :
bit 0 : MOVIE_SYNC2_INIT_FASTROM
other : reserved , set to 0
* /
r . ReadByte ( ) ;
/ *
017 1 - byte flags "sync options" :
bit 0 : MOVIE_SYNC_DATA_EXISTS
if "1" , all sync options flags are defined .
if "0" , all sync options flags have no meaning .
bit 1 : MOVIE_SYNC_WIP1TIMING
bit 2 : MOVIE_SYNC_LEFTRIGHT
bit 3 : MOVIE_SYNC_VOLUMEENVX
bit 4 : MOVIE_SYNC_FAKEMUTE
bit 5 : MOVIE_SYNC_SYNCSOUND
bit 6 : MOVIE_SYNC_HASROMINFO
if "1" , there is extra ROM info located right in between of the metadata and the savestate .
bit 7 : set to 0.
* /
2012-09-15 09:38:20 +00:00
byte syncFlags = r . ReadByte ( ) ;
2012-09-14 01:53:04 +00:00
/ *
Extra ROM info is always positioned right before the savestate . Its size is 30 bytes if MOVIE_SYNC_HASROMINFO
is used ( and MOVIE_SYNC_DATA_EXISTS is set ) , 0 bytes otherwise .
* /
2012-09-15 09:38:20 +00:00
int extraRomInfo = ( ( ( syncFlags > > 6 ) & 0x1 ) ! = 0 & & ( syncFlags & 0x1 ) ! = 0 ) ? 30 : 0 ;
2012-09-13 19:33:55 +00:00
// 018 4-byte little-endian unsigned int: offset to the savestate inside file
2012-09-14 01:53:04 +00:00
uint savestateOffset = r . ReadUInt32 ( ) ;
2012-09-13 19:33:55 +00:00
// 01C 4-byte little-endian unsigned int: offset to the controller data inside file
uint firstFrameOffset = r . ReadUInt32 ( ) ;
2012-09-15 09:38:20 +00:00
int [ ] controllerTypes = new int [ 2 ] ;
// The (.SMV 1.51 and up) header has an additional 32 bytes at the end
if ( version ! = "1.43" )
{
// 020 4-byte little-endian unsigned int: number of input samples, primarily for peripheral-using games
r . ReadBytes ( 4 ) ;
/ *
024 2 1 - byte unsigned ints : what type of controller is plugged into ports 1 and 2 respectively : 0 = NONE ,
1 = JOYPAD , 2 = MOUSE , 3 = SUPERSCOPE , 4 = JUSTIFIER , 5 = MULTITAP
* /
controllerTypes [ 0 ] = r . ReadByte ( ) ;
controllerTypes [ 1 ] = r . ReadByte ( ) ;
// 026 4 1-byte signed ints: controller IDs of port 1, or -1 for unplugged
r . ReadBytes ( 4 ) ;
// 02A 4 1-byte signed ints: controller IDs of port 2, or -1 for unplugged
r . ReadBytes ( 4 ) ;
// 02E 18 bytes: reserved for future use
r . ReadBytes ( 18 ) ;
}
2012-09-13 19:33:55 +00:00
/ *
After the header comes "metadata" , which is UTF16 - coded movie title string ( author info ) . The metadata begins
2012-09-15 09:38:20 +00:00
from position 32 ( 0x20 ( 0x40 for 1.51 and up ) ) and ends at < savestate_offset -
length_of_extra_rom_info_in_bytes > .
2012-09-13 19:33:55 +00:00
* /
2012-09-15 09:48:13 +00:00
byte [ ] metadata = r . ReadBytes ( ( int ) ( savestateOffset - extraRomInfo - ( ( version ! = "1.43" ) ? 0x40 : 0x20 ) ) ) ;
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string author = NullTerminated ( Encoding . Unicode . GetString ( metadata ) . Trim ( ) ) ;
2012-09-14 17:36:49 +00:00
if ( author ! = "" )
2012-12-06 05:19:08 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-12-06 05:19:08 +00:00
}
2012-09-14 17:36:49 +00:00
if ( extraRomInfo = = 30 )
{
// 000 3 bytes of zero padding: 00 00 00 003 4-byte integer: CRC32 of the ROM 007 23-byte ascii string
r . ReadBytes ( 3 ) ;
2012-09-15 09:38:20 +00:00
int crc32 = r . ReadInt32 ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ CRC32 ] = crc32 . ToString ( ) ;
2012-09-14 17:36:49 +00:00
// the game name copied from the ROM, truncated to 23 bytes (the game name in the ROM is 21 bytes)
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string gameName = NullTerminated ( Encoding . UTF8 . GetString ( r . ReadBytes ( 23 ) ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gameName ;
2012-09-14 17:36:49 +00:00
}
2012-09-13 19:33:55 +00:00
r . BaseStream . Position = firstFrameOffset ;
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
/ *
01 00 ( reserved )
02 00 ( reserved )
04 00 ( reserved )
08 00 ( reserved )
10 00 R
20 00 L
40 00 X
80 00 A
00 01 Right
00 02 Left
00 04 Down
00 08 Up
00 10 Start
00 20 Select
00 40 Y
00 80 B
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ]
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
"Right" , "Left" , "Down" , "Up" , "Start" , "Select" , "Y" , "B" , "R" , "L" , "X" , "A"
} ;
2013-04-15 02:14:14 +00:00
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
for ( int frame = 0 ; frame < = frameCount ; frame + + )
2011-07-06 01:53:15 +00:00
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
controllers [ "Reset" ] = true ;
2012-09-17 04:16:27 +00:00
for ( int player = 1 ; player < = controllersUsed . Length ; player + + )
2011-07-06 01:53:15 +00:00
{
2012-09-17 04:16:27 +00:00
if ( ! controllersUsed [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
/ *
Each frame consists of 2 bytes per controller . So if there are 3 controllers , a frame is 6 bytes and
if there is only 1 controller , a frame is 2 bytes .
* /
byte controllerState1 = r . ReadByte ( ) ;
byte controllerState2 = r . ReadByte ( ) ;
/ *
In the reset - recording patch , a frame that contains the value FF FF for every controller denotes a
reset . The reset is done through the S9xSoftReset routine .
* /
if ( controllerState1 ! = 0xFF | | controllerState2 ! = 0xFF )
2012-12-06 05:19:08 +00:00
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
controllers [ "Reset" ] = false ;
2012-12-06 05:19:08 +00:00
}
2012-09-15 09:38:20 +00:00
/ *
While the meaning of controller data ( for 1.51 and up ) for a single standard SNES controller pad
remains the same , each frame of controller data can contain additional bytes if input for peripherals
is being recorded .
* /
2012-09-17 04:16:27 +00:00
if ( version ! = "1.43" & & player < = controllerTypes . Length )
2012-09-15 09:38:20 +00:00
{
string peripheral = "" ;
switch ( controllerTypes [ player - 1 ] )
{
// NONE
case 0 :
continue ;
// JOYPAD
case 1 :
break ;
// MOUSE
case 2 :
peripheral = "Mouse" ;
// 5*num_mouse_ports
r . ReadBytes ( 5 ) ;
break ;
// SUPERSCOPE
case 3 :
peripheral = "Super Scope" ;
// 6*num_superscope_ports
r . ReadBytes ( 6 ) ;
break ;
// JUSTIFIER
case 4 :
peripheral = "Justifier" ;
// 11*num_justifier_ports
r . ReadBytes ( 11 ) ;
break ;
// MULTITAP
case 5 :
peripheral = "Multitap" ;
break ;
}
2012-09-21 09:11:30 +00:00
if ( peripheral ! = "" & & warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-15 18:46:41 +00:00
warningMsg = "Unable to import " + peripheral + "." ;
2012-12-06 05:19:08 +00:00
}
2012-09-15 09:38:20 +00:00
}
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
ushort controllerState = ( ushort ) ( ( ( controllerState1 < < 4 ) & 0x0F00 ) | controllerState2 ) ;
2013-07-14 14:53:32 +00:00
if ( player < = MnemonicConstants . PLAYERS [ controllers . Type . Name ] )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
{
controllers [ "P" + player + " " + buttons [ button ] ] = (
2012-09-17 07:22:35 +00:00
( ( controllerState > > button ) & 0x1 ) ! = 0
2012-09-17 04:16:27 +00:00
) ;
}
2012-12-06 05:19:08 +00:00
}
2012-09-21 09:11:30 +00:00
else if ( warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
warningMsg = "Controller " + player + " not supported." ;
2012-12-06 05:19:08 +00:00
}
2011-07-06 01:53:15 +00:00
}
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
// The controller data contains <number_of_frames + 1> frames.
if ( frame = = 0 )
2012-12-06 05:19:08 +00:00
{
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2011-07-06 01:53:15 +00:00
}
2012-09-13 19:33:55 +00:00
r . Close ( ) ;
2012-09-15 09:38:20 +00:00
fs . Close ( ) ;
2011-07-06 01:53:15 +00:00
return m ;
}
2012-03-12 20:35:10 +00:00
// VBM file format: http://code.google.com/p/vba-rerecording/wiki/VBM
2013-11-10 02:55:11 +00:00
private static Movie ImportVBM ( string path , out string errorMsg , out string warningMsg )
2011-07-06 01:53:15 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2011-07-06 01:53:15 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
2012-03-06 03:41:11 +00:00
// 000 4-byte signature: 56 42 4D 1A "VBM\x1A"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 4 ) ;
2012-03-06 03:41:11 +00:00
if ( signature ! = "VBM\x1A" )
2011-07-14 01:12:18 +00:00
{
2012-02-28 23:11:19 +00:00
errorMsg = "This is not a valid .VBM file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-14 01:12:18 +00:00
return null ;
}
2012-03-06 03:41:11 +00:00
// 004 4-byte little-endian unsigned int: major version number, must be "1"
uint majorVersion = r . ReadUInt32 ( ) ;
if ( majorVersion ! = 1 )
{
errorMsg = ".VBM major movie version must be 1." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
/ *
008 4 - byte little - endian integer : movie "uid" - identifies the movie - savestate relationship , also used as the
recording time in Unix epoch format
* /
uint uid = r . ReadUInt32 ( ) ;
2013-12-04 03:04:29 +00:00
2012-03-06 03:41:11 +00:00
// 00C 4-byte little-endian unsigned int: number of frames
2012-03-01 09:16:14 +00:00
uint frameCount = r . ReadUInt32 ( ) ;
2012-03-06 03:41:11 +00:00
// 010 4-byte little-endian unsigned int: rerecord count
2012-02-28 23:11:19 +00:00
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-03-06 03:41:11 +00:00
// 014 1-byte flags: (movie start flags)
byte flags = r . ReadByte ( ) ;
// bit 0: if "1", movie starts from an embedded "quicksave" snapshot
2012-09-17 07:22:35 +00:00
bool startfromquicksave = ( ( flags & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// bit 1: if "1", movie starts from reset with an embedded SRAM
2012-09-17 07:22:35 +00:00
bool startfromsram = ( ( ( flags > > 1 ) & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// other: reserved, set to 0
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
// (If both bits 0 and 1 are "1", the movie file is invalid)
if ( startfromquicksave & & startfromsram )
{
errorMsg = "This is not a valid .VBM file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
if ( startfromquicksave )
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
if ( startfromsram )
2011-07-06 01:53:15 +00:00
{
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
errorMsg = "Movies that begin with SRAM are not supported." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-14 01:12:18 +00:00
return null ;
2011-07-06 01:53:15 +00:00
}
2012-03-12 20:35:10 +00:00
// 015 1-byte flags: controller flags
2012-09-17 04:16:27 +00:00
byte controllerFlags = r . ReadByte ( ) ;
/ *
* bit 0 : controller 1 in use
* bit 1 : controller 2 in use ( SGB games can be 2 - player multiplayer )
* bit 2 : controller 3 in use ( SGB games can be 3 - or 4 - player multiplayer with multitap )
* bit 3 : controller 4 in use ( SGB games can be 3 - or 4 - player multiplayer with multitap )
* /
bool [ ] controllersUsed = new bool [ 4 ] ;
for ( int controller = 1 ; controller < = controllersUsed . Length ; controller + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
controllersUsed [ controller - 1 ] = ( ( ( controllerFlags > > ( controller - 1 ) ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-17 04:16:27 +00:00
if ( ! controllersUsed [ 0 ] )
2011-07-06 01:53:15 +00:00
{
2012-03-06 03:41:11 +00:00
errorMsg = "Controller 1 must be in use." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-14 01:12:18 +00:00
return null ;
2011-07-06 01:53:15 +00:00
}
2012-03-06 03:41:11 +00:00
// other: reserved
// 016 1-byte flags: system flags (game always runs at 60 frames/sec)
flags = r . ReadByte ( ) ;
// bit 0: if "1", movie is for the GBA system
2012-09-17 07:22:35 +00:00
bool is_gba = ( ( flags & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// bit 1: if "1", movie is for the GBC system
2012-09-17 07:22:35 +00:00
bool is_gbc = ( ( ( flags > > 1 ) & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// bit 2: if "1", movie is for the SGB system
2012-09-17 07:22:35 +00:00
bool is_sgb = ( ( ( flags > > 2 ) & 0x1 ) ! = 0 ) ;
2012-03-06 03:41:11 +00:00
// other: reserved, set to 0
2013-04-15 02:14:14 +00:00
2012-03-06 03:41:11 +00:00
// (At most one of bits 0, 1, 2 can be "1")
2013-04-15 02:14:14 +00:00
//if (!(is_gba ^ is_gbc ^ is_sgb) && (is_gba || is_gbc || is_sgb)) //TODO: adelikat: this doesn't do what the comment above suggests it is trying to check for, it is always false!
//{
//errorMsg = "This is not a valid .VBM file.";
//r.Close();
//fs.Close();
//return null;
//}
2012-03-06 03:41:11 +00:00
// (If all 3 of these bits are "0", it is for regular GB.)
string platform = "GB" ;
if ( is_gba )
2012-12-06 05:19:08 +00:00
{
2012-03-06 03:41:11 +00:00
platform = "GBA" ;
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
if ( is_gbc )
2012-12-06 05:19:08 +00:00
{
2012-03-06 03:41:11 +00:00
platform = "GBC" ;
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
if ( is_sgb )
2012-12-06 05:19:08 +00:00
{
2012-10-04 06:14:21 +00:00
m . Header . Comments . Add ( SUPERGAMEBOYMODE + " True" ) ;
2012-12-06 05:19:08 +00:00
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = platform ;
2012-03-06 03:41:11 +00:00
// 017 1-byte flags: (values of some boolean emulator options)
flags = r . ReadByte ( ) ;
/ *
* bit 0 : ( useBiosFile ) if "1" and the movie is of a GBA game , the movie was made using a GBA BIOS file .
* bit 1 : ( skipBiosFile ) if "0" and the movie was made with a GBA BIOS file , the BIOS intro is included in the
* movie .
* bit 2 : ( rtcEnable ) if "1" , the emulator "real time clock" feature was enabled .
* /
// bit 3: (unsupported) must be "0" or the movie file is considered invalid (legacy).
2012-09-17 07:22:35 +00:00
if ( ( ( flags > > 3 ) & 0x1 ) ! = 0 )
2012-03-06 03:41:11 +00:00
{
errorMsg = "This is not a valid .VBM file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
/ *
* bit 4 : ( lagReduction ) if "0" and the movie is of a GBA game , the movie was made using the old excessively
* laggy GBA timing .
* bit 5 : ( gbcHdma5Fix ) if "0" and the movie is of a GBC game , the movie was made using the old buggy HDMA5
* timing .
2012-09-15 18:46:41 +00:00
* bit 6 : ( echoRAMFix ) if "1" and the movie is of a GB , GBC , or SGB game , the movie was made with Echo RAM
* Fix on , otherwise it was made with Echo RAM Fix off .
2012-03-06 03:41:11 +00:00
* bit 7 : reserved , set to 0.
* /
/ *
018 4 - byte little - endian unsigned int : theApp . winSaveType ( value of that emulator option )
01 C 4 - byte little - endian unsigned int : theApp . winFlashSize ( value of that emulator option )
020 4 - byte little - endian unsigned int : gbEmulatorType ( value of that emulator option )
* /
r . ReadBytes ( 12 ) ;
/ *
024 12 - byte character array : the internal game title of the ROM used while recording , not necessarily
null - terminated ( ASCII ? )
* /
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string gameName = NullTerminated ( r . ReadStringFixedAscii ( 12 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . GAMENAME ] = gameName ;
2012-03-06 03:41:11 +00:00
// 030 1-byte unsigned char: minor version/revision number of current VBM version, the latest is "1"
byte minorVersion = r . ReadByte ( ) ;
m . Header . Comments . Add ( MOVIEORIGIN + " .VBM version " + majorVersion + "." + minorVersion ) ;
2012-03-12 20:35:10 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " Visual Boy Advance" ) ;
2012-09-15 09:38:20 +00:00
// 031 1-byte unsigned char: the internal CRC of the ROM used while recording
r . ReadByte ( ) ;
/ *
032 2 - byte little - endian unsigned short : the internal Checksum of the ROM used while recording , or a
calculated CRC16 of the BIOS if GBA
* /
2012-10-04 06:14:21 +00:00
ushort checksum_crc16 = r . ReadUInt16 ( ) ;
2012-03-06 03:41:11 +00:00
/ *
034 4 - byte little - endian unsigned int : the Game Code of the ROM used while recording , or the Unit Code if not
GBA
* /
2012-10-04 06:14:21 +00:00
uint gameCode_unitCode = r . ReadUInt32 ( ) ;
2012-09-15 18:46:41 +00:00
if ( platform = = "GBA" )
2012-10-04 06:14:21 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ CRC16 ] = checksum_crc16 . ToString ( ) ;
m . Header [ GAMECODE ] = gameCode_unitCode . ToString ( ) ;
2012-10-04 06:14:21 +00:00
}
2012-09-15 18:46:41 +00:00
else
2012-10-04 06:14:21 +00:00
{
2013-11-30 02:20:34 +00:00
m . Header [ INTERNALCHECKSUM ] = checksum_crc16 . ToString ( ) ;
m . Header [ UNITCODE ] = gameCode_unitCode . ToString ( ) ;
2012-10-04 06:14:21 +00:00
}
2012-09-15 09:38:20 +00:00
// 038 4-byte little-endian unsigned int: offset to the savestate or SRAM inside file, set to 0 if unused
r . ReadBytes ( 4 ) ;
2012-03-06 03:41:11 +00:00
// 03C 4-byte little-endian unsigned int: offset to the controller data inside file
uint firstFrameOffset = r . ReadUInt32 ( ) ;
// After the header is 192 bytes of text. The first 64 of these 192 bytes are for the author's name (or names).
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string author = NullTerminated ( r . ReadStringFixedAscii ( 64 ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-03-06 03:41:11 +00:00
// The following 128 bytes are for a description of the movie. Both parts must be null-terminated.
-ImportSMV143
--Added platform name.
--Handled input.
---The input seems reasonable, but the frame counts were originally off by 2. Because the docs say "The controller data contains <number_of_frames + 1> frames," I connected the two and discarded the first frame read. I also noticed that Snes9x first accepts input at frame 0 whereas BizHawk does at frame 1, so that makes up for the other frame. I think this is correct.
--Apparently, Snes9x adds garbage bytes to the metadata when you play a movie. The stuff we want, however, is before a NULL character. As such, I refurbished RemoveNull into NullTerminated, which takes a string and returns everything before the first NULL character. Now the author will be displayed properly no matter what.
-Removed an unncessary dependence to Global.Emulator.SystemId in Get/SetControllersAsMnemonic which made it so that importing a movie without loading a game resulted in blank frames.
-ImportVBM now actually appends the frames it parses. Looks pretty good.
--Note that BizHawk cannot currently do anything with the platform (GB, GBC, GBA, SGB), nor are any of the "other" buttons handleable.
2012-09-14 22:15:42 +00:00
string movieDescription = NullTerminated ( r . ReadStringFixedAscii ( 128 ) ) ;
2012-03-11 12:24:56 +00:00
m . Header . Comments . Add ( COMMENT + " " + movieDescription ) ;
2012-03-06 03:41:11 +00:00
r . BaseStream . Position = firstFrameOffset ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition ( ) } ;
2012-12-06 05:19:08 +00:00
if ( platform = = "GBA" )
{
controllers . Type . Name = "Gameboy Controller" ;
}
else
{
controllers . Type . Name = "GBA Controller" ;
}
2012-03-06 03:41:11 +00:00
/ *
* 01 00 A
* 02 00 B
* 04 00 Select
* 08 00 Start
* 10 00 Right
* 20 00 Left
* 40 00 Up
* 80 00 Down
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ] { "A" , "B" , "Select" , "Start" , "Right" , "Left" , "Up" , "Down" } ;
2012-03-06 03:41:11 +00:00
/ *
* 00 01 R
* 00 02 L
* 00 04 Reset ( old timing )
* 00 08 Reset ( new timing since version 1.1 )
* 00 10 Left motion sensor
* 00 20 Right motion sensor
* 00 40 Down motion sensor
* 00 80 Up motion sensor
* /
2013-04-15 02:14:14 +00:00
string [ ] other = new [ ] {
2012-03-06 03:41:11 +00:00
"R" , "L" , "Reset (old timing)" , "Reset (new timing since version 1.1)" , "Left motion sensor" ,
"Right motion sensor" , "Down motion sensor" , "Up motion sensor"
} ;
2012-03-01 09:16:14 +00:00
for ( int frame = 1 ; frame < = frameCount ; frame + + )
2011-07-06 01:53:15 +00:00
{
2012-03-06 03:41:11 +00:00
/ *
2012-09-15 18:46:41 +00:00
A stream of 2 - byte bitvectors which indicate which buttons are pressed at each point in time . They will
come in groups of however many controllers are active , in increasing order .
2012-03-06 03:41:11 +00:00
* /
ushort controllerState = r . ReadUInt16 ( ) ;
2012-02-24 04:44:40 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
// TODO: Handle the other buttons.
if ( warningMsg = = "" )
{
for ( int button = 0 ; button < other . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
if ( ( ( controllerState > > ( button + 8 ) ) & 0x1 ) ! = 0 )
2012-03-06 03:41:11 +00:00
{
warningMsg = other [ button ] ;
break ;
}
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
if ( warningMsg ! = "" )
2012-12-06 05:19:08 +00:00
{
2012-03-06 03:41:11 +00:00
warningMsg = "Unable to import " + warningMsg + " at frame " + frame + "." ;
2012-12-06 05:19:08 +00:00
}
2012-03-06 03:41:11 +00:00
}
// TODO: Handle the additional controllers.
2012-09-17 04:16:27 +00:00
for ( int player = 2 ; player < = controllersUsed . Length ; player + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
if ( controllersUsed [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
r . ReadBytes ( 2 ) ;
2012-12-06 05:19:08 +00:00
}
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2011-07-06 01:53:15 +00:00
}
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2011-07-06 01:53:15 +00:00
return m ;
}
2012-03-01 09:16:14 +00:00
// VMV file format: http://tasvideos.org/VMV.html
2013-11-10 02:55:11 +00:00
private static Movie ImportVMV ( string path , out string errorMsg , out string warningMsg )
2012-03-01 09:16:14 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-03-01 09:16:14 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
// 000 12-byte signature: "VirtuaNES MV"
2012-03-06 05:27:50 +00:00
string signature = r . ReadStringFixedAscii ( 12 ) ;
2012-03-01 09:16:14 +00:00
if ( signature ! = "VirtuaNES MV" )
{
errorMsg = "This is not a valid .VMV file." ;
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-03-01 09:16:14 +00:00
return null ;
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "NES" ;
2012-03-12 20:35:10 +00:00
// 00C 2-byte little-endian integer: movie version 0x0400
ushort version = r . ReadUInt16 ( ) ;
m . Header . Comments . Add ( MOVIEORIGIN + " .VMV version " + version ) ;
m . Header . Comments . Add ( EMULATIONORIGIN + " VirtuaNES" ) ;
// 00E 2-byte little-endian integer: record version
ushort recordVersion = r . ReadUInt16 ( ) ;
m . Header . Comments . Add ( COMMENT + " Record version " + recordVersion ) ;
// 010 4-byte flags (control byte)
uint flags = r . ReadUInt32 ( ) ;
2012-09-17 04:16:27 +00:00
/ *
* bit 0 : controller 1 in use
* bit 1 : controller 2 in use
* bit 2 : controller 3 in use
* bit 3 : controller 4 in use
* /
bool [ ] controllersUsed = new bool [ 4 ] ;
for ( int controller = 1 ; controller < = controllersUsed . Length ; controller + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 04:16:27 +00:00
controllersUsed [ controller - 1 ] = ( ( ( flags > > ( controller - 1 ) ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-17 04:16:27 +00:00
bool fourscore = ( controllersUsed [ 2 ] | | controllersUsed [ 3 ] ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . FOURSCORE ] = fourscore . ToString ( ) ;
2012-03-12 20:35:10 +00:00
/ *
bit 6 : 1 = reset - based , 0 = savestate - based ( movie version < = 0x300 is always savestate - based )
If the movie version is < 0x400 , or the "from-reset" flag is not set , a savestate is loaded from the movie .
Otherwise , the savestate is ignored .
* /
2012-09-17 07:22:35 +00:00
if ( version < 0x400 | | ( ( flags > > 6 ) & 0x1 ) = = 0 )
2012-03-12 20:35:10 +00:00
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
/ *
bit 7 : disable rerecording
2012-09-17 07:22:35 +00:00
For the other control bytes , if a key from 1 P to 4 P ( whichever one ) is entirely ON , the following 4 bytes
becomes the controller data . TODO : Figure out what this means .
2012-03-12 20:35:10 +00:00
Other bits : reserved , set to 0
* /
2012-12-06 05:19:08 +00:00
// 014 DWORD Ext0; // ROM:program CRC FDS:program ID
2012-09-15 18:46:41 +00:00
r . ReadBytes ( 4 ) ;
2012-12-06 05:19:08 +00:00
// 018 WORD Ext1; // ROM:unused,0 FDS:maker ID
2012-09-15 18:46:41 +00:00
r . ReadBytes ( 2 ) ;
2012-12-06 05:19:08 +00:00
// 01A WORD Ext2; // ROM:unused,0 FDS:disk no.
2012-09-15 18:46:41 +00:00
r . ReadBytes ( 2 ) ;
2012-03-12 20:35:10 +00:00
// 01C 4-byte little-endian integer: rerecord count
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-03-12 20:35:10 +00:00
/ *
2012-12-06 05:19:08 +00:00
020 BYTE RenderMethod
2012-03-12 20:35:10 +00:00
0 = POST_ALL , 1 = PRE_ALL
2 = POST_RENDER , 3 = PRE_RENDER
4 = TILE_RENDER
* /
2012-09-15 18:46:41 +00:00
r . ReadByte ( ) ;
2012-12-06 05:19:08 +00:00
// 021 BYTE IRQtype // IRQ type
2012-09-15 18:46:41 +00:00
r . ReadByte ( ) ;
2012-12-06 05:19:08 +00:00
// 022 BYTE FrameIRQ // FrameIRQ not allowed
2012-09-15 18:46:41 +00:00
r . ReadByte ( ) ;
-.LSMV importer.
--Handled authors, gametype, systemid, coreversion, rom.sha256, moviesram.*, savestate, and subtitles.
--Skipped port1, port2, controlsversion, projectid, saveframe, lagcounter, pollcounters, hostmemory, screenshot, sram.*, rrdata, starttime.*, savetime.*, and prefix.
---I don't think any of these are useful for us, at least not at this stage.
---The other *.sha256 files will be useful if / when BS-X, Sufami turbo, and SGB are implemented.
--Handled flags.
---Handled resets.
---Disallowed subframes and delayed resets.
-.LSMV is good enough for now. .SMV and .ZMV coming next.
2012-09-13 10:21:25 +00:00
// 023 1-byte flag: 0=NTSC (60 Hz), 1="PAL" (50 Hz)
2012-03-12 20:35:10 +00:00
bool pal = ( r . ReadByte ( ) = = 1 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-09-15 18:46:41 +00:00
// 024 8-bytes: reserved, set to 0
r . ReadBytes ( 8 ) ;
// 02C 4-byte little-endian integer: save state start offset
r . ReadBytes ( 4 ) ;
// 030 4-byte little-endian integer: save state end offset
r . ReadBytes ( 4 ) ;
2012-03-13 02:37:59 +00:00
// 034 4-byte little-endian integer: movie data offset
uint firstFrameOffset = r . ReadUInt32 ( ) ;
2012-03-12 20:35:10 +00:00
// 038 4-byte little-endian integer: movie frame count
uint frameCount = r . ReadUInt32 ( ) ;
// 03C 4-byte little-endian integer: CRC (CRC excluding this data(to prevent cheating))
2012-09-15 18:46:41 +00:00
int crc32 = r . ReadInt32 ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ CRC32 ] = crc32 . ToString ( ) ;
2012-09-17 04:16:27 +00:00
if ( ! controllersUsed [ 0 ] & & ! controllersUsed [ 1 ] & & ! controllersUsed [ 2 ] & & ! controllersUsed [ 3 ] )
2012-03-12 20:35:10 +00:00
{
warningMsg = "No input recorded." ;
r . Close ( ) ;
fs . Close ( ) ;
return m ;
}
2012-03-13 02:37:59 +00:00
r . BaseStream . Position = firstFrameOffset ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } } ;
2012-03-12 20:35:10 +00:00
/ *
2012-03-13 02:37:59 +00:00
* 01 A
* 02 B
* 04 Select
* 08 Start
* 10 Up
* 20 Down
* 40 Left
* 80 Right
2012-03-12 20:35:10 +00:00
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ] { "A" , "B" , "Select" , "Start" , "Up" , "Down" , "Left" , "Right" } ;
2012-03-12 20:35:10 +00:00
for ( int frame = 1 ; frame < = frameCount ; frame + + )
{
2012-03-13 02:37:59 +00:00
/ *
2012-09-15 18:46:41 +00:00
Each frame consists of 1 or more bytes . Controller 1 takes 1 byte , controller 2 takes 1 byte , controller
3 takes 1 byte , and controller 4 takes 1 byte . If all four exist , the frame is 4 bytes . For example , if
the movie only has controller 1 data , a frame is 1 byte .
2012-03-13 02:37:59 +00:00
* /
2012-09-17 07:22:35 +00:00
controllers [ "Reset" ] = false ;
2012-09-17 04:16:27 +00:00
for ( int player = 1 ; player < = controllersUsed . Length ; player + + )
2012-03-13 02:37:59 +00:00
{
2012-09-17 04:16:27 +00:00
if ( ! controllersUsed [ player - 1 ] )
2012-12-06 05:19:08 +00:00
{
2012-03-13 02:37:59 +00:00
continue ;
2012-12-06 05:19:08 +00:00
}
2012-03-13 02:37:59 +00:00
byte controllerState = r . ReadByte ( ) ;
2012-09-17 07:22:35 +00:00
if ( controllerState > = 0xF0 )
{
if ( controllerState = = 0xF0 )
{
ushort command = r . ReadUInt16 ( ) ;
string commandName = "" ;
if ( ( command & 0xFF00 ) = = 0 )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
switch ( command & 0x00FF )
{
// NESCMD_NONE
case 0 :
break ;
// NESCMD_HWRESET
case 1 :
controllers [ "Reset" ] = true ;
break ;
// NESCMD_SWRESET
case 2 :
controllers [ "Reset" ] = true ;
break ;
// NESCMD_EXCONTROLLER
case 3 :
commandName = "NESCMD_EXCONTROLLER, 0" ;
break ;
// NESCMD_DISK_THROTTLE_ON
case 4 :
commandName = "NESCMD_DISK_THROTTLE_ON, 0" ;
break ;
// NESCMD_DISK_THROTTLE_OFF
case 5 :
commandName = "NESCMD_DISK_THROTTLE_OFF, 0" ;
break ;
// NESCMD_DISK_EJECT
case 6 :
commandName = "NESCMD_DISK_EJECT, 0" ;
break ;
// NESCMD_DISK_0A
case 7 :
commandName = "NESCMD_DISK_0A, 0" ;
break ;
// NESCMD_DISK_0B
case 8 :
commandName = "NESCMD_DISK_0B, 0" ;
break ;
// NESCMD_DISK_1A
case 9 :
commandName = "NESCMD_DISK_1A, 0" ;
break ;
// NESCMD_DISK_1B
case 10 :
commandName = "NESCMD_DISK_1B, 0" ;
break ;
default :
commandName = "invalid command" ;
break ;
}
2012-12-06 05:19:08 +00:00
}
2012-09-17 07:22:35 +00:00
else
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
commandName = "NESCMD_EXCONTROLLER, " + ( command & 0xFF00 ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-21 09:11:30 +00:00
if ( commandName ! = "" & & warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
warningMsg = "Unable to run command \"" + commandName + "\"." ;
2012-12-06 05:19:08 +00:00
}
2012-09-17 07:22:35 +00:00
}
else if ( controllerState = = 0xF3 )
{
uint dwdata = r . ReadUInt32 ( ) ;
// TODO: Make a clearer warning message.
2012-09-21 09:11:30 +00:00
if ( warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
warningMsg = "Unable to run SetSyncExData(" + dwdata + ")." ;
2012-12-06 05:19:08 +00:00
}
2012-09-17 07:22:35 +00:00
}
controllerState = r . ReadByte ( ) ;
}
2012-03-13 02:37:59 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-17 07:22:35 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = ( ( ( controllerState > > button ) & 0x1 ) ! = 0 ) ;
2012-12-06 05:19:08 +00:00
}
2012-03-13 02:37:59 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-03-12 20:35:10 +00:00
}
2012-03-02 04:15:15 +00:00
r . Close ( ) ;
fs . Close ( ) ;
2012-03-01 09:16:14 +00:00
return m ;
}
2012-09-10 21:35:56 +00:00
// ZMV file format: http://tasvideos.org/ZMV.html
2013-11-10 02:55:11 +00:00
private static Movie ImportZMV ( string path , out string errorMsg , out string warningMsg )
2012-09-10 21:35:56 +00:00
{
2013-11-10 02:55:11 +00:00
errorMsg = warningMsg = String . Empty ;
Movie m = new Movie ( path + "." + Global . Config . MovieExtension ) ;
2012-09-15 18:46:41 +00:00
FileStream fs = new FileStream ( path , FileMode . Open , FileAccess . Read ) ;
BinaryReader r = new BinaryReader ( fs ) ;
// 000 3-byte signature: 5A 4D 56 "ZMV"
string signature = r . ReadStringFixedAscii ( 3 ) ;
if ( signature ! = "ZMV" )
{
errorMsg = "This is not a valid .ZMV file." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PLATFORM ] = "SNES" ;
2012-09-15 18:46:41 +00:00
// 003 2-byte little-endian unsigned int: zsnes version number
short version = r . ReadInt16 ( ) ;
2012-09-19 01:52:54 +00:00
m . Header . Comments . Add ( EMULATIONORIGIN + " ZSNES version " + version ) ;
2012-12-06 05:19:08 +00:00
m . Header . Comments . Add ( MOVIEORIGIN + " .ZMV" ) ;
2012-09-15 18:46:41 +00:00
// 005 4-byte little-endian integer: CRC32 of the ROM
int crc32 = r . ReadInt32 ( ) ;
2013-11-30 02:20:34 +00:00
m . Header [ CRC32 ] = crc32 . ToString ( ) ;
2012-09-15 18:46:41 +00:00
// 009 4-byte little-endian unsigned int: number of frames
uint frameCount = r . ReadUInt32 ( ) ;
// 00D 4-byte little-endian unsigned int: number of rerecords
uint rerecordCount = r . ReadUInt32 ( ) ;
2013-12-02 17:50:29 +00:00
m . Header . Rerecords = rerecordCount ;
2012-09-15 18:46:41 +00:00
// 011 4-byte little-endian unsigned int: number of frames removed by rerecord
r . ReadBytes ( 4 ) ;
// 015 4-byte little-endian unsigned int: number of frames advanced step by step
r . ReadBytes ( 4 ) ;
2012-09-17 04:16:27 +00:00
// 019 1-byte: average recording frames per second
2012-09-19 01:52:54 +00:00
r . ReadByte ( ) ;
2012-09-17 04:16:27 +00:00
// 01A 4-byte little-endian unsigned int: number of key combos
2012-09-15 18:46:41 +00:00
r . ReadBytes ( 4 ) ;
// 01E 2-byte little-endian unsigned int: number of internal chapters
2012-10-06 20:28:32 +00:00
ushort internalChaptersCount = r . ReadUInt16 ( ) ;
2012-09-15 18:46:41 +00:00
// 020 2-byte little-endian unsigned int: length of the author name field in bytes
ushort authorSize = r . ReadUInt16 ( ) ;
// 022 3-byte little-endian unsigned int: size of an uncompressed save state in bytes
2012-09-19 01:52:54 +00:00
r . ReadBytes ( 3 ) ;
// 025 1-byte: reserved
r . ReadByte ( ) ;
2012-09-15 18:46:41 +00:00
/ *
2012-09-19 01:52:54 +00:00
026 1 - byte flags : initial input configuration
2012-09-15 18:46:41 +00:00
bit 7 : first input enabled
bit 6 : second input enabled
bit 5 : third input enabled
bit 4 : fourth input enabled
bit 3 : fifth input enabled
2012-09-19 06:40:16 +00:00
bit 2 : mouse in first port
bit 1 : mouse in second port
bit 0 : super scope in second port
2012-09-15 18:46:41 +00:00
* /
byte controllerFlags = r . ReadByte ( ) ;
2012-09-21 09:11:30 +00:00
string peripheral = "" ;
2012-09-19 06:40:16 +00:00
bool superScope = ( ( controllerFlags & 0x1 ) ! = 0 ) ;
if ( superScope )
2012-12-06 05:19:08 +00:00
{
2012-09-21 09:11:30 +00:00
peripheral = "Super Scope" ;
2012-12-06 05:19:08 +00:00
}
2012-09-15 18:46:41 +00:00
controllerFlags > > = 1 ;
2012-09-19 06:40:16 +00:00
bool secondMouse = ( ( controllerFlags & 0x1 ) ! = 0 ) ;
2012-09-21 09:11:30 +00:00
if ( secondMouse & & peripheral = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-21 09:11:30 +00:00
peripheral = "Second Mouse" ;
2012-12-06 05:19:08 +00:00
}
2012-09-17 04:16:27 +00:00
controllerFlags > > = 1 ;
2012-09-19 06:40:16 +00:00
bool firstMouse = ( ( controllerFlags & 0x1 ) ! = 0 ) ;
2012-09-21 09:11:30 +00:00
if ( firstMouse & & peripheral = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-21 09:11:30 +00:00
peripheral = "First Mouse" ;
2012-12-06 05:19:08 +00:00
}
2012-09-21 09:11:30 +00:00
if ( peripheral ! = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-21 09:11:30 +00:00
warningMsg = "Unable to import " + peripheral + "." ;
2012-12-06 05:19:08 +00:00
}
2012-09-15 18:46:41 +00:00
// 027 1-byte flags:
byte movieFlags = r . ReadByte ( ) ;
byte begins = ( byte ) ( movieFlags & 0xC0 ) ;
/ *
bits 7 , 6 :
if "00" , movie begins from savestate
* /
if ( begins = = 0x00 )
{
errorMsg = "Movies that begin with a savestate are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
// if "10", movie begins from reset
// if "01", movie begins from power-on
if ( begins = = 0x40 )
{
errorMsg = "Movies that begin with SRAM are not supported." ;
r . Close ( ) ;
fs . Close ( ) ;
return null ;
}
// if "11", movie begins from power-on with SRAM clear
// bit 5: if "0", movie is NTSC (60 fps); if "1", movie is PAL (50 fps)
bool pal = ( ( ( movieFlags > > 5 ) & 0x1 ) ! = 0 ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . PAL ] = pal . ToString ( ) ;
2012-09-15 18:46:41 +00:00
// other: reserved, set to 0
/ *
028 3 - byte little - endian unsigned int : initial save state size , highest bit specifies compression , next 23
specifies size
* /
2012-10-05 01:10:58 +00:00
uint savestateSize = ( uint ) ( ( r . ReadByte ( ) | ( r . ReadByte ( ) < < 8 ) | ( r . ReadByte ( ) < < 16 ) ) & 0x7FFFFF ) ;
2012-09-17 04:16:27 +00:00
// Next follows a ZST format savestate.
r . ReadBytes ( ( int ) savestateSize ) ;
2013-04-15 02:14:14 +00:00
SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SNES Controller" } } ;
2012-10-06 22:36:50 +00:00
/ *
* bit 11 : A
* bit 10 : X
* bit 9 : L
* bit 8 : R
* bit 7 : B
* bit 6 : Y
* bit 5 : Select
* bit 4 : Start
* bit 3 : Up
* bit 2 : Down
* bit 1 : Left
* bit 0 : Right
* /
2013-04-15 02:14:14 +00:00
string [ ] buttons = new [ ]
{
2012-10-06 20:28:32 +00:00
"Right" , "Left" , "Down" , "Up" , "Start" , "Select" , "Y" , "B" , "R" , "L" , "X" , "A"
2012-09-19 06:40:16 +00:00
} ;
int frames = 1 ;
2012-10-06 20:28:32 +00:00
int internalChapters = 1 ;
while ( frames < = frameCount | | internalChapters < = internalChaptersCount )
2012-09-19 06:40:16 +00:00
{
/ *
000 1 - byte flags :
bit 7 : "1" if controller 1 changed , "0" otherwise
bit 6 : "1" if controller 2 changed , "0" otherwise
bit 5 : "1" if controller 3 changed , "0" otherwise
bit 4 : "1" if controller 4 changed , "0" otherwise
bit 3 : "1" if controller 5 changed , "0" otherwise
bit 2 : "1" if this is a "chapter" update , "0" if it ' s a "controller" update
bit 1 : "1" if this is RLE data instead of any of the above
bit 0 : "1" if this is command data instead any of the above
* /
byte flag = r . ReadByte ( ) ;
if ( ( flag & 0x1 ) ! = 0 )
{
/ *
If the event is a command , the other seven bits define the command . The command could be "reset now"
or similar things .
* /
flag > > = 1 ;
if ( flag = = 0x0 )
2012-10-06 20:28:32 +00:00
{
2012-09-19 06:40:16 +00:00
controllers [ "Reset" ] = true ;
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-10-06 20:28:32 +00:00
controllers [ "Reset" ] = false ;
}
2012-09-19 06:40:16 +00:00
// TODO: Other commands.
}
else if ( ( ( flag > > 1 ) & 0x1 ) ! = 0 )
{
// If the event is RLE data, next follows 4 bytes which is the frame to repeat current input till.
uint frame = r . ReadUInt32 ( ) ;
2012-09-21 09:11:30 +00:00
if ( frame > frameCount )
2012-12-06 05:19:08 +00:00
{
2012-10-06 20:28:32 +00:00
throw new ArgumentException ( "RLE data repeats for frames beyond the total frame count." ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-19 06:40:16 +00:00
for ( ; frames < = frame ; frames + + )
2012-12-06 05:19:08 +00:00
{
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-12-06 05:19:08 +00:00
}
2012-09-19 06:40:16 +00:00
}
else if ( ( ( flag > > 2 ) & 0x1 ) ! = 0 )
{
/ *
If the event is a "chapter" update , the packet follows with a ZST format savestate . Using a header of :
000 3 - byte little endian unsigned int : save state size in format defined above
* /
2012-10-05 01:10:58 +00:00
savestateSize = ( uint ) ( ( r . ReadByte ( ) | ( r . ReadByte ( ) < < 8 ) | ( r . ReadByte ( ) < < 16 ) ) & 0x7FFFFF ) ;
2012-09-19 06:40:16 +00:00
// 001 above size: save state
r . ReadBytes ( ( int ) savestateSize ) ;
// above size+001 4-byte little endian unsigned int: frame number save state loads to
r . ReadBytes ( 4 ) ;
// above size+005 2-byte: controller status bit field, see below
r . ReadBytes ( 2 ) ;
// above size+007 9-byte: previous controller input bits
r . ReadBytes ( 9 ) ;
2012-10-06 20:28:32 +00:00
internalChapters + + ;
2012-09-19 06:40:16 +00:00
}
else
{
flag > > = 3 ;
/ *
If the event is a "controller" update , next comes the controller data for each changed controller , 12
bits per controller , or 20 bits in the case of the super scope , zeropadding up to full bytes . The
minimum length of the controller data is 2 bytes , and the maximum length is 9 bytes .
* /
2012-10-06 20:28:32 +00:00
bool leftOver = false ;
byte leftOverValue = 0x0 ;
for ( int player = 1 ; player < = 5 ; player + + )
2012-12-06 05:19:08 +00:00
{
2012-09-21 09:11:30 +00:00
// If the controller has changed:
2012-10-06 20:28:32 +00:00
if ( ( ( flag > > ( 5 - player ) ) & 0x1 ) ! = 0 )
2012-09-19 06:40:16 +00:00
{
2012-10-06 22:36:50 +00:00
/ *
For example , if this frame contained input for controllers 1 and 2 :
byte 1 :
bit 7 : P1 B
bit 6 : P1 Y
bit 5 : P1 Select
bit 4 : P1 Start
bit 3 : P1 Up
bit 2 : P1 Down
bit 1 : P1 Left
bit 0 : P1 Right
byte 2 :
bit 7 : P2 B
bit 6 : P2 Y
bit 5 : P2 Select
bit 4 : P2 Start
bit 3 : P1 A
bit 2 : P1 X
bit 1 : P1 L
bit 0 : P1 R
byte 3 :
bit 7 : P2 Up
bit 6 : P2 Down
bit 5 : P2 Left
bit 4 : P2 Right
bit 3 : P2 A
bit 2 : P2 X
bit 1 : P2 L
bit 0 : P2 R
* /
2012-09-19 06:40:16 +00:00
byte controllerState1 = r . ReadByte ( ) ;
uint controllerState ;
2012-10-06 20:28:32 +00:00
if ( ! leftOver )
2012-09-19 06:40:16 +00:00
{
byte controllerState2 = r . ReadByte ( ) ;
if ( player = = 2 & & superScope )
{
byte controllerState3 = r . ReadByte ( ) ;
2012-10-06 20:28:32 +00:00
controllerState = ( uint ) ( ( controllerState1 | ( controllerState2 < < 8 ) |
( controllerState3 < < 12 ) ) & 0x0FFFFF ) ;
leftOverValue = ( byte ) ( ( controllerState3 > > 4 ) & 0x0F ) ;
2012-09-19 06:40:16 +00:00
}
else
2012-10-06 20:28:32 +00:00
{
controllerState = ( uint ) ( ( controllerState1 | ( controllerState2 < < 4 ) ) & 0x0FFF ) ;
leftOverValue = ( byte ) ( ( controllerState2 > > 4 ) & 0x0F ) ;
}
2012-09-19 06:40:16 +00:00
}
2012-10-06 20:28:32 +00:00
else
2012-09-19 06:40:16 +00:00
{
2012-10-06 20:28:32 +00:00
controllerState = ( uint ) ( ( controllerState1 > > 4 ) | ( leftOverValue < < 4 ) |
2012-10-06 22:36:50 +00:00
( ( controllerState1 < < 8 ) & 0x0F00 ) ) ;
2012-09-19 06:40:16 +00:00
if ( player = = 2 & & superScope )
{
byte controllerState2 = r . ReadByte ( ) ;
2012-10-06 20:28:32 +00:00
controllerState | = ( uint ) ( controllerState2 < < 12 ) ;
2012-09-19 06:40:16 +00:00
}
}
2012-10-06 20:28:32 +00:00
leftOver = ! leftOver ;
2013-07-14 14:53:32 +00:00
if ( player < = MnemonicConstants . PLAYERS [ controllers . Type . Name ] )
2012-09-21 09:11:30 +00:00
{
2012-10-06 20:28:32 +00:00
if ( player ! = 2 | | ! superScope )
2012-12-06 05:19:08 +00:00
{
2012-09-19 06:40:16 +00:00
for ( int button = 0 ; button < buttons . Length ; button + + )
2012-12-06 05:19:08 +00:00
{
2012-09-19 06:40:16 +00:00
controllers [ "P" + player + " " + buttons [ button ] ] = (
( ( controllerState > > button ) & 0x1 ) ! = 0
) ;
2012-12-06 05:19:08 +00:00
}
}
2012-09-21 09:11:30 +00:00
}
else if ( warningMsg = = "" )
2012-12-06 05:19:08 +00:00
{
2012-09-19 06:40:16 +00:00
warningMsg = "Controller " + player + " not supported." ;
2012-12-06 05:19:08 +00:00
}
2012-09-19 06:40:16 +00:00
}
2012-12-06 05:19:08 +00:00
}
2013-12-07 16:31:04 +00:00
m . AppendFrame ( controllers ) ;
2012-09-19 06:40:16 +00:00
frames + + ;
}
}
2012-10-06 22:36:50 +00:00
r . BaseStream . Position = r . BaseStream . Length - authorSize ;
// Last in the file comes the author name field, which is an UTF-8 encoded text string.
string author = Encoding . UTF8 . GetString ( r . ReadBytes ( authorSize ) ) ;
2013-11-30 02:20:34 +00:00
m . Header [ HeaderKeys . AUTHOR ] = author ;
2012-09-10 21:35:56 +00:00
return m ;
}
2011-07-06 01:53:15 +00:00
}
2012-03-09 09:24:47 +00:00
}