--It doesn't seem like I have to worry about PCECD anymore.
--I also don't think I can validate the CRC32 for .SMV because I don't have the original ROM bytes. This was probably used to compare a ROM loaded by Snes9x with the movie. I added it to the movie header in case a core could utilize it.
--I don't think the "start data" of a .VBM is important. After looking at the docs again, it's clear that it's just another term for either the movie's savestate or SRAM, and that the related note about controller data is just an approximation.
-Added Platforms for Import functions that were missing them.
-Merged the ImportSMV functions all into one; most of the stuff is the same.
-Implemented .SMV 1.51 handling. My Arcade's Revenge run synced and beat the first level!
--This also seems to work for 1.52; 3295S finished Act 2.
--1.53 seems identical as well...3587S syncs all the way through.
-Next comes ImportZMV.
This commit is contained in:
brandman211 2012-09-15 09:38:20 +00:00
parent 27b5d29f0c
commit dbe985d3b9
1 changed files with 95 additions and 63 deletions

View File

@ -808,6 +808,7 @@ namespace BizHawk.MultiClient
fs.Close();
return null;
}
m.Header.SetHeaderLine(MovieHeader.PLATFORM, "Genesis");
// 00F ASCII-encoded GMV file format version. The most recent is 'A'. (?)
string version = r.ReadStringFixedAscii(1);
m.Header.Comments.Add(MOVIEORIGIN + " .GMV version " + version);
@ -1180,7 +1181,6 @@ namespace BizHawk.MultiClient
errorMsg = "";
warningMsg = "";
Movie m = ImportText(path, out errorMsg, out warningMsg);
// TODO: PCECD equivalent.
return m;
}
@ -1516,6 +1516,7 @@ namespace BizHawk.MultiClient
{
errorMsg = "";
warningMsg = "";
Movie m = new Movie(path + "." + Global.Config.MovieExtension);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader r = new BinaryReader(fs);
// 000 4-byte signature: 53 4D 56 1A "SMV\x1A"
@ -1527,19 +1528,20 @@ namespace BizHawk.MultiClient
fs.Close();
return null;
}
m.Header.SetHeaderLine(MovieHeader.PLATFORM, "SNES");
// 004 4-byte little-endian unsigned int: version number
uint version = r.ReadUInt32();
Movie m;
switch (version)
uint versionNumber = r.ReadUInt32();
string version;
switch (versionNumber)
{
case 1:
m = ImportSMV143(r, path, out errorMsg, out warningMsg);
version = "1.43";
break;
case 4:
m = ImportSMV151(r, path, out errorMsg, out warningMsg);
version = "1.51";
break;
case 5:
m = ImportSMV152(r, path, out errorMsg, out warningMsg);
version = "1.52";
break;
default:
errorMsg = "SMV version not recognized. 1.43, 1.51, and 1.52 are currently supported.";
@ -1547,19 +1549,7 @@ namespace BizHawk.MultiClient
fs.Close();
return null;
}
r.Close();
fs.Close();
m.Header.SetHeaderLine(MovieHeader.PLATFORM, "SNES");
return m;
}
// SMV 1.43 file format: http://code.google.com/p/snes9x-rr/wiki/SMV143
private static Movie ImportSMV143(BinaryReader r, string path, out string errorMsg, out string warningMsg)
{
errorMsg = "";
warningMsg = "";
Movie m = new Movie(path + "." + Global.Config.MovieExtension);
m.Header.Comments.Add(EMULATIONORIGIN + " Snes9x version 1.43");
m.Header.Comments.Add(EMULATIONORIGIN + " Snes9x version " + version);
m.Header.Comments.Add(MOVIEORIGIN + " .SMV");
/*
008 4-byte little-endian integer: movie "uid" - identifies the movie-savestate relationship, also used as the
@ -1572,7 +1562,7 @@ namespace BizHawk.MultiClient
// 010 4-byte little-endian unsigned int: number of frames
uint frameCount = r.ReadUInt32();
// 014 1-byte flags "controller mask"
byte flags = r.ReadByte();
byte controllerFlags = r.ReadByte();
int players = 0;
/*
* bit 0: controller 1 in use
@ -1583,23 +1573,24 @@ namespace BizHawk.MultiClient
* other: reserved, set to 0
*/
for (int controller = 1; controller <= 5; controller++)
if (((flags >> (controller - 1)) & 0x1) != 0)
if (((controllerFlags >> (controller - 1)) & 0x1) != 0)
players++;
// 015 1-byte flags "movie options"
flags = r.ReadByte();
byte movieFlags = r.ReadByte();
/*
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
*/
if ((flags & 0x1) == 0)
if ((movieFlags & 0x1) == 0)
{
errorMsg = "Movies that begin with a savestate are not supported.";
r.Close();
fs.Close();
return null;
}
// bit 1: if "0", movie is NTSC (60 fps); if "1", movie is PAL (50 fps)
bool pal = (((flags >> 1) & 0x1) != 0);
bool pal = (((movieFlags >> 1) & 0x1) != 0);
m.Header.SetHeaderLine("PAL", pal.ToString());
// other: reserved, set to 0
/*
@ -1622,19 +1613,39 @@ namespace BizHawk.MultiClient
if "1", there is extra ROM info located right in between of the metadata and the savestate.
bit 7: set to 0.
*/
flags = r.ReadByte();
byte syncFlags = r.ReadByte();
/*
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.
*/
int extraRomInfo = (((flags >> 6) & 0x1) != 0 && (flags & 0x1) != 0) ? 30 : 0;
int extraRomInfo = (((syncFlags >> 6) & 0x1) != 0 && (syncFlags & 0x1) != 0) ? 30 : 0;
// 018 4-byte little-endian unsigned int: offset to the savestate inside file
uint savestateOffset = r.ReadUInt32();
// 01C 4-byte little-endian unsigned int: offset to the controller data inside file
uint firstFrameOffset = r.ReadUInt32();
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);
}
/*
After the header comes "metadata", which is UTF16-coded movie title string (author info). The metadata begins
from position 32 (0x20) and ends at <savestate_offset - length_of_extra_rom_info_in_bytes>.
from position 32 (0x20 (0x40 for 1.51 and up)) and ends at <savestate_offset -
length_of_extra_rom_info_in_bytes>.
*/
byte[] metadata = r.ReadBytes((int)(savestateOffset - extraRomInfo - 0x20));
string author = NullTerminated(Encoding.Unicode.GetString(metadata).Trim());
@ -1644,7 +1655,8 @@ namespace BizHawk.MultiClient
{
// 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);
int crc32 = r.ReadInt32(); // TODO: Validate.
int crc32 = r.ReadInt32();
m.Header.SetHeaderLine("CRC32", crc32.ToString());
// the game name copied from the ROM, truncated to 23 bytes (the game name in the ROM is 21 bytes)
string gameName = NullTerminated(Encoding.UTF8.GetString(r.ReadBytes(23)));
m.Header.SetHeaderLine(MovieHeader.GAMENAME, gameName);
@ -1692,6 +1704,48 @@ namespace BizHawk.MultiClient
*/
if (controllerState1 != 0xFF || controllerState2 != 0xFF)
controllers["Reset"] = false;
/*
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.
*/
if (version != "1.43")
{
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;
}
if (warningMsg != "" && peripheral != "")
warningMsg = peripheral;
}
ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2);
for (int button = 0; button < buttons.Length; button++)
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 1) == 1);
@ -1703,30 +1757,7 @@ namespace BizHawk.MultiClient
m.AppendFrame(mg.GetControllersAsMnemonic());
}
r.Close();
return m;
}
// SMV 1.51 file format: http://code.google.com/p/snes9x-rr/wiki/SMV151
private static Movie ImportSMV151(BinaryReader r, string path, out string errorMsg, out string warningMsg)
{
errorMsg = "";
warningMsg = "";
Movie m = new Movie(path + "." + Global.Config.MovieExtension);
m.Header.Comments.Add(EMULATIONORIGIN + " Snes9x version 1.51");
m.Header.Comments.Add(MOVIEORIGIN + " .SMV");
// TODO: Import.
return m;
}
private static Movie ImportSMV152(BinaryReader r, string path, out string errorMsg, out string warningMsg)
{
errorMsg = "";
warningMsg = "";
Movie m = new Movie(path + "." + Global.Config.MovieExtension);
uint GUID = r.ReadUInt32();
m.Header.Comments.Add(EMULATIONORIGIN + " Snes9x version 1.52");
m.Header.Comments.Add(MOVIEORIGIN + " .SMV");
// TODO: Import.
fs.Close();
return m;
}
@ -1887,15 +1918,20 @@ namespace BizHawk.MultiClient
byte minorVersion = r.ReadByte();
m.Header.Comments.Add(MOVIEORIGIN + " .VBM version " + majorVersion + "." + minorVersion);
m.Header.Comments.Add(EMULATIONORIGIN + " Visual Boy Advance");
// 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
*/
r.ReadBytes(2);
/*
031 1-byte unsigned char: the internal CRC of the ROM used while recording
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
034 4-byte little-endian unsigned int: the Game Code of the ROM used while recording, or the Unit Code if not
GBA
038 4-byte little-endian unsigned int: offset to the savestate or SRAM inside file, set to 0 if unused
*/
r.ReadBytes(11);
r.ReadBytes(4);
// 038 4-byte little-endian unsigned int: offset to the savestate or SRAM inside file, set to 0 if unused
r.ReadBytes(4);
// 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).
@ -1904,11 +1940,6 @@ namespace BizHawk.MultiClient
// The following 128 bytes are for a description of the movie. Both parts must be null-terminated.
string movieDescription = NullTerminated(r.ReadStringFixedAscii(128));
m.Header.Comments.Add(COMMENT + " " + movieDescription);
/*
TODO: implement start data. There are no specifics on the googlecode page as to how long the SRAM or savestate
should be.
*/
// If there is no "Start Data", this will probably begin at byte 0x100 in the file, but this is not guaranteed.
r.BaseStream.Position = firstFrameOffset;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
@ -1987,6 +2018,7 @@ namespace BizHawk.MultiClient
fs.Close();
return null;
}
m.Header.SetHeaderLine(MovieHeader.PLATFORM, "NES");
// 00C 2-byte little-endian integer: movie version 0x0400
ushort version = r.ReadUInt16();
m.Header.Comments.Add(MOVIEORIGIN + " .VMV version " + version);