-.Close()'d everything.

-Added a FourScore header and applied it to ImportFile and ImportFCM.
--Not sure if there's any equivalent header for FMV/FCM.
-Seemingly have the update bytes being read properly.

TODO: Make ImportFCM actually utilize the update bytes to write frames.
This commit is contained in:
brandman211 2012-03-02 04:15:15 +00:00
parent ff12c2c5ef
commit bedbf1d5e1
2 changed files with 320 additions and 180 deletions

View File

@ -26,6 +26,7 @@ namespace BizHawk.MultiClient
public const string RERECORDS = "rerecordCount"; public const string RERECORDS = "rerecordCount";
public const string GUID = "GUID"; public const string GUID = "GUID";
public const string STARTSFROMSAVESTATE = "StartsFromSavestate"; public const string STARTSFROMSAVESTATE = "StartsFromSavestate";
public const string FOURSCORE = "FourScore";
public static string MovieVersion = "BizHawk v0.0.1"; public static string MovieVersion = "BizHawk v0.0.1";

View File

@ -100,7 +100,8 @@ namespace BizHawk.MultiClient
errorMsg = ""; errorMsg = "";
warningMsg = ""; warningMsg = "";
Movie m = new Movie(Path.ChangeExtension(path, ".tas"), MOVIEMODE.PLAY); Movie m = new Movie(Path.ChangeExtension(path, ".tas"), MOVIEMODE.PLAY);
var file = new FileInfo(path); FileInfo file = new FileInfo(path);
StreamReader sr = file.OpenText();
string[] buttons = new string[] { }; string[] buttons = new string[] { };
string controller = ""; string controller = "";
string emulator = ""; string emulator = "";
@ -121,8 +122,6 @@ namespace BizHawk.MultiClient
break; break;
} }
m.Header.SetHeaderLine(MovieHeader.PLATFORM, platform); m.Header.SetHeaderLine(MovieHeader.PLATFORM, platform);
using (StreamReader sr = file.OpenText())
{
int lineNum = 0; int lineNum = 0;
string line = ""; string line = "";
while ((line = sr.ReadLine()) != null) while ((line = sr.ReadLine()) != null)
@ -162,6 +161,7 @@ namespace BizHawk.MultiClient
if (ParseHeader(line, "StartsFromSavestate") == "1") if (ParseHeader(line, "StartsFromSavestate") == "1")
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
sr.Close();
return null; return null;
} }
} }
@ -179,6 +179,13 @@ namespace BizHawk.MultiClient
} }
m.Header.SetHeaderLine("PAL", pal.ToString()); m.Header.SetHeaderLine("PAL", pal.ToString());
} }
else if (line.StartsWith("fourscore"))
m.Header.SetHeaderLine(
MovieHeader.FOURSCORE,
Convert.ToBoolean(
int.Parse(ParseHeader(line, "fourscore"))
).ToString()
);
else if (line.StartsWith("sub")) else if (line.StartsWith("sub"))
{ {
Subtitle s = new Subtitle(); Subtitle s = new Subtitle();
@ -223,7 +230,7 @@ namespace BizHawk.MultiClient
warningMsg = "FDS Insert"; warningMsg = "FDS Insert";
break; break;
case '8': case '8':
warningMsg = "FDS Select"; warningMsg = "FDS Select Side";
break; break;
default: default:
warningMsg = "unknown"; warningMsg = "unknown";
@ -260,7 +267,7 @@ namespace BizHawk.MultiClient
// Everything not explicitly defined is treated as a comment. // Everything not explicitly defined is treated as a comment.
m.Header.Comments.Add(line); m.Header.Comments.Add(line);
} }
} sr.Close();
return m; return m;
} }
@ -296,6 +303,8 @@ namespace BizHawk.MultiClient
if (signature != "FCM\x1A") if (signature != "FCM\x1A")
{ {
errorMsg = "This is not a valid .FCM file."; errorMsg = "This is not a valid .FCM file.";
r.Close();
fs.Close();
return null; return null;
} }
// 004 4-byte little-endian unsigned int: version number, must be 2 // 004 4-byte little-endian unsigned int: version number, must be 2
@ -317,6 +326,8 @@ namespace BizHawk.MultiClient
if ((int)(flags & 2) == 0) if ((int)(flags & 2) == 0)
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null; return null;
} }
bool pal = false; bool pal = false;
@ -340,8 +351,13 @@ namespace BizHawk.MultiClient
m.SetRerecords((int)rerecordCount); m.SetRerecords((int)rerecordCount);
// 014 4-byte little-endian unsigned int: length of controller data in bytes // 014 4-byte little-endian unsigned int: length of controller data in bytes
uint movieDataSize = r.ReadUInt32(); uint movieDataSize = r.ReadUInt32();
// 018 4-byte little-endian unsigned int: offset to the savestate inside file /*
uint savestateOffset = r.ReadUInt32(); 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.
*/
r.ReadUInt32();
// 01C 4-byte little-endian unsigned int: offset to the controller data inside file // 01C 4-byte little-endian unsigned int: offset to the controller data inside file
uint firstFrameOffset = r.ReadUInt32(); uint firstFrameOffset = r.ReadUInt32();
// 020 16-byte md5sum of the ROM used // 020 16-byte md5sum of the ROM used
@ -374,28 +390,115 @@ namespace BizHawk.MultiClient
r.ReadByte(); r.ReadByte();
string author = System.Text.Encoding.UTF8.GetString(authorBytes.ToArray()); string author = System.Text.Encoding.UTF8.GetString(authorBytes.ToArray());
m.Header.SetHeaderLine(MovieHeader.AUTHOR, author); m.Header.SetHeaderLine(MovieHeader.AUTHOR, author);
// Advance to first byte of input data // Advance to first byte of input data.
// byte[] throwaway = new byte[firstFrameOffset];
// r.Read(throwaway, 0, (int)firstFrameOffset);
r.BaseStream.Position = firstFrameOffset; r.BaseStream.Position = firstFrameOffset;
// moviedatasize stuff string[] buttons = new string[8] { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right" };
// read frame data bool fourscore = false;
// TODO: special commands like fds disk switch, etc, and power/reset int frame = 1;
// TODO: use stringbuilder class for speed while (frame <= frameCount)
// string ButtonLookup = "RLDUSsBARLDUSsBARLDUSsBARLDUSsBA"; //TODO: This assumes input data is the same in fcm as bizhawk, which it isn't {
// string frame = "|0|"; //TODO: read reset command rather than hard code it off byte update = r.ReadByte();
for (int frame = 0; frame < frameCount; frame++) SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
controllers.Type.Name = "NES Controller";
if ((int)(update & 0x128) != 0)
{
// Control update: 1aabbbbb
bool reset = false;
int command = update & 0x1F;
/*
bbbbb:
** 0 Do nothing
** 1 Reset
** 2 Power cycle
** 7 VS System Insert Coin
** 8 VS System Dipswitch 0 Toggle
** 24 FDS Insert
** 25 FDS Eject
** 26 FDS Select Side
*/
switch (command)
{
case 0:
break;
case 1:
reset = true;
controllers["Reset"] = true;
break;
case 2:
reset = true;
if (frame != 1)
{
warningMsg = "hard reset";
}
break;
case 7:
warningMsg = "VS System Insert Coin";
break;
case 8:
warningMsg = "VS System Dipswitch 0 Toggle";
break;
case 24:
warningMsg = "FDS Insert";
break;
case 25:
warningMsg = "FDS Eject";
break;
case 26:
warningMsg = "FDS Select Side";
break;
default:
warningMsg = "unknown";
break;
}
if (warningMsg != "")
warningMsg = "Unable to import " + warningMsg + " command at frame " + frame + ".";
/*
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.
*/
if (!reset && frame == 1)
{
errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null;
}
}
else
{ {
/* /*
byte joy = r.ReadByte(); Controller update: 0aabbccc
// Read each byte of controller one data * bb: Gamepad number minus one (?)
frame += "|";
r.ReadBytes(3); //Lose remaining controllers for now
m.AppendFrame("Test");
*/ */
int player = ((update >> 3) & 3) + 1;
/*
ccc:
* 0 A
* 1 B
* 2 Select
* 3 Start
* 4 Up
* 5 Down
* 6 Left
* 7 Right
*/
int button = update & 7;
} }
// set 4 score flag if necessary // aa: Number of delta bytes to follow
int delta = (update >> 5) & 3;
MnemonicsGenerator mg = new MnemonicsGenerator();
mg.SetSource(controllers);
string mnemonic = mg.GetControllersAsMnemonic();
while (false)
m.AppendFrame(mnemonic);
frame++;
}
m.Header.SetHeaderLine(MovieHeader.FOURSCORE, fourscore.ToString());
r.Close(); r.Close();
fs.Close();
return m; return m;
} }
@ -421,6 +524,8 @@ namespace BizHawk.MultiClient
if (signature != "FMV\x1A") if (signature != "FMV\x1A")
{ {
errorMsg = "This is not a valid .FMV file."; errorMsg = "This is not a valid .FMV file.";
r.Close();
fs.Close();
return null; return null;
} }
// 004 1-byte flags: // 004 1-byte flags:
@ -432,6 +537,8 @@ namespace BizHawk.MultiClient
if ((int)(flags & 0x80) != 0) if ((int)(flags & 0x80) != 0)
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null; return null;
} }
// 005 1-byte flags: // 005 1-byte flags:
@ -483,6 +590,8 @@ namespace BizHawk.MultiClient
if (!controller1 && !controller2 && !FDS) if (!controller1 && !controller2 && !FDS)
{ {
warningMsg = "No input recorded."; warningMsg = "No input recorded.";
r.Close();
fs.Close();
return m; return m;
} }
/* /*
@ -503,12 +612,6 @@ namespace BizHawk.MultiClient
if (FDS) if (FDS)
bytesPerFrame++; bytesPerFrame++;
long frames = (fs.Length - 144) / bytesPerFrame; long frames = (fs.Length - 144) / bytesPerFrame;
for (long frame = 0; frame < frames; frame++)
{
byte controllerstate;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
controllers.Type.Name = "NES Controller";
/* /*
* 01 Right * 01 Right
* 02 Left * 02 Left
@ -520,6 +623,12 @@ namespace BizHawk.MultiClient
* 80 Start * 80 Start
*/ */
string[] buttons = new string[8] { "Right", "Left", "Up", "Down", "B", "A", "Select", "Start" }; string[] buttons = new string[8] { "Right", "Left", "Up", "Down", "B", "A", "Select", "Start" };
for (long frame = 1; frame <= frames; frame++)
{
byte controllerstate;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
controllers.Type.Name = "NES Controller";
/* /*
Each frame consists of 1 or more bytes. Controller 1 takes 1 byte, controller 2 takes 1 byte, and the FDS 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 data takes 1 byte. If all three exist, the frame is 3 bytes. For example, if the movie is a regular NES game
@ -552,6 +661,8 @@ namespace BizHawk.MultiClient
mg.SetSource(controllers); mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic()); m.AppendFrame(mg.GetControllersAsMnemonic());
} }
r.Close();
fs.Close();
return m; return m;
} }
@ -577,8 +688,12 @@ namespace BizHawk.MultiClient
if (signature != "NSS\x1A") if (signature != "NSS\x1A")
{ {
errorMsg = "This is not a valid .NMV file."; errorMsg = "This is not a valid .NMV file.";
r.Close();
fs.Close();
return null; return null;
} }
r.Close();
fs.Close();
return m; return m;
} }
@ -614,6 +729,8 @@ namespace BizHawk.MultiClient
if (signature != "MMV\0") if (signature != "MMV\0")
{ {
errorMsg = "This is not a valid .MMV file."; errorMsg = "This is not a valid .MMV file.";
r.Close();
fs.Close();
return null; return null;
} }
// 0004: 4-byte little endian unsigned int: dega version // 0004: 4-byte little endian unsigned int: dega version
@ -629,6 +746,8 @@ namespace BizHawk.MultiClient
if (reset == 0) if (reset == 0)
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null; return null;
} }
// 0014: 4-byte little endian unsigned int: offset of state information // 0014: 4-byte little endian unsigned int: offset of state information
@ -675,12 +794,6 @@ namespace BizHawk.MultiClient
// 00e4-00f3: binary: rom MD5 digest // 00e4-00f3: binary: rom MD5 digest
string MD5 = BytesToString(r, 16, true); string MD5 = BytesToString(r, 16, true);
m.Header.SetHeaderLine("MD5", MD5); m.Header.SetHeaderLine("MD5", MD5);
for (int frame = 0; frame < frameCount; frame++)
{
byte controllerstate;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
controllers.Type.Name = "SMS Controller";
/* /*
76543210 76543210
* bit 0 (0x01): up * bit 0 (0x01): up
@ -693,6 +806,12 @@ namespace BizHawk.MultiClient
* bit 7 (0x80): start (Game Gear) * bit 7 (0x80): start (Game Gear)
*/ */
string[] buttons = new string[6] { "Up", "Down", "Left", "Right", "B1", "B2" }; string[] buttons = new string[6] { "Up", "Down", "Left", "Right", "B1", "B2" };
for (int frame = 1; frame <= frameCount; frame++)
{
byte controllerstate;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
controllers.Type.Name = "SMS Controller";
/* /*
Controller data is made up of one input packet per frame. Each packet currently consists of 2 bytes. The 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 first byte is for controller 1 and the second controller 2. The Game Gear only uses the controller 1 input
@ -717,6 +836,8 @@ namespace BizHawk.MultiClient
mg.SetSource(controllers); mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic()); m.AppendFrame(mg.GetControllersAsMnemonic());
} }
r.Close();
fs.Close();
return m; return m;
} }
@ -734,6 +855,8 @@ namespace BizHawk.MultiClient
if (signature.Substring(0, 3) != "SMV") if (signature.Substring(0, 3) != "SMV")
{ {
errorMsg = "This is not a valid .SMV file."; errorMsg = "This is not a valid .SMV file.";
r.Close();
fs.Close();
return null; return null;
} }
@ -750,6 +873,8 @@ namespace BizHawk.MultiClient
default: default:
{ {
errorMsg = "SMV version not recognized, 143, 151, and 152 are currently supported."; errorMsg = "SMV version not recognized, 143, 151, and 152 are currently supported.";
r.Close();
fs.Close();
return null; return null;
} }
} }
@ -799,11 +924,11 @@ namespace BizHawk.MultiClient
//TODO: get extra rom info //TODO: get extra rom info
r.BaseStream.Position = FrameDataOffset; r.BaseStream.Position = FrameDataOffset;
for (int x = 0; x < frameCount; x++) for (int frame = 1; frame <= frameCount; frame++)
{ {
//TODO: FF FF for all controllers = Reset //TODO: FF FF for all controllers = Reset
//string frame = "|0|"; //string frame = "|0|";
for (int y = 0; y < numControllers; y++) for (int controller = 1; controller <= numControllers; controller++)
{ {
ushort fd = r.ReadUInt16(); ushort fd = r.ReadUInt16();
} }
@ -842,6 +967,8 @@ namespace BizHawk.MultiClient
if (signature != 0x56424D1A) if (signature != 0x56424D1A)
{ {
errorMsg = "This is not a valid .VBM file."; errorMsg = "This is not a valid .VBM file.";
r.Close();
fs.Close();
return null; return null;
} }
@ -865,6 +992,8 @@ namespace BizHawk.MultiClient
if (startfromquicksave & startfromsram) if (startfromquicksave & startfromsram)
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null; return null;
} }
@ -894,6 +1023,8 @@ namespace BizHawk.MultiClient
if (is_gb & is_gbc & is_gba & is_sgb) if (is_gb & is_gbc & is_gba & is_sgb)
{ {
errorMsg = "Not a valid .VBM platform type."; errorMsg = "Not a valid .VBM platform type.";
r.Close();
fs.Close();
return null; return null;
} }
//TODO: set platform in header //TODO: set platform in header
@ -916,6 +1047,8 @@ namespace BizHawk.MultiClient
if ((flags & 0x08) != 0) if ((flags & 0x08) != 0)
{ {
errorMsg = "Invalid .VBM file."; errorMsg = "Invalid .VBM file.";
r.Close();
fs.Close();
return null; return null;
} }
if ((flags & 0x04) != 0) rtcenable = true; if ((flags & 0x04) != 0) rtcenable = true;
@ -983,6 +1116,8 @@ namespace BizHawk.MultiClient
MnemonicsGenerator mg = new MnemonicsGenerator(); MnemonicsGenerator mg = new MnemonicsGenerator();
mg.SetSource(controllers); mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic()); m.AppendFrame(mg.GetControllersAsMnemonic());
r.Close();
fs.Close();
return m; return m;
} }
@ -999,8 +1134,12 @@ namespace BizHawk.MultiClient
if (signature != "VirtuaNES MV") if (signature != "VirtuaNES MV")
{ {
errorMsg = "This is not a valid .VMV file."; errorMsg = "This is not a valid .VMV file.";
r.Close();
fs.Close();
return null; return null;
} }
r.Close();
fs.Close();
return m; return m;
} }
} }