start a movie import refactor, and start implementing a new fm2 importer as a proof of concept, does not do anything useful yet (such as SyncSettings and input)
This commit is contained in:
parent
206b00252f
commit
124ee7b236
|
@ -180,6 +180,8 @@
|
|||
</Compile>
|
||||
<Compile Include="movie\conversions\MovieConversionExtensions.cs" />
|
||||
<Compile Include="movie\HeaderKeys.cs" />
|
||||
<Compile Include="movie\import\Fm2Import.cs" />
|
||||
<Compile Include="movie\import\IMovieImport.cs" />
|
||||
<Compile Include="movie\import\MovieImport.cs" />
|
||||
<Compile Include="movie\import\PJMImport.cs" />
|
||||
<Compile Include="movie\interfaces\ILogEntryGenerator.cs" />
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
[ImportExtension(".fm2")]
|
||||
public class Fm2Import : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
var emulator = "FCEUX";
|
||||
var platform = "NES"; // TODO: FDS?
|
||||
|
||||
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = platform;
|
||||
|
||||
using (var sr = SourceFile.OpenText())
|
||||
{
|
||||
string line;
|
||||
int lineNum = 0;
|
||||
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
lineNum++;
|
||||
|
||||
if (line == string.Empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (line[0] == '|')
|
||||
{
|
||||
// TODO: import a frame of input
|
||||
// TODO: report any errors importing this frame and bail out if so
|
||||
}
|
||||
else if (line.ToLower().StartsWith("sub"))
|
||||
{
|
||||
var subtitle = ImportTextSubtitle(line);
|
||||
|
||||
if (!string.IsNullOrEmpty(subtitle))
|
||||
{
|
||||
Result.Movie.Subtitles.AddFromString(subtitle);
|
||||
}
|
||||
}
|
||||
else if (line.ToLower().StartsWith("emuversion"))
|
||||
{
|
||||
Result.Movie.Comments.Add(
|
||||
string.Format("{0} {1} version {2}", EMULATIONORIGIN, emulator, ParseHeader(line, "emuVersion"))
|
||||
);
|
||||
}
|
||||
else if (line.ToLower().StartsWith("version"))
|
||||
{
|
||||
string version = ParseHeader(line, "version");
|
||||
|
||||
if (version != "3")
|
||||
{
|
||||
Result.Warnings.Add("Detected a .fm2 movie version other than 3, which is unsupported");
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Movie.Comments.Add(MOVIEORIGIN + " .fm2 version 3");
|
||||
}
|
||||
}
|
||||
else if (line.ToLower().StartsWith("romfilename"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "romFilename");
|
||||
}
|
||||
else if (line.ToLower().StartsWith("cdgamename"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "cdGameName");
|
||||
}
|
||||
else if (line.ToLower().StartsWith("romchecksum"))
|
||||
{
|
||||
string blob = ParseHeader(line, "romChecksum");
|
||||
byte[] md5 = DecodeBlob(blob);
|
||||
if (md5 != null && md5.Length == 16)
|
||||
{
|
||||
Result.Movie.HeaderEntries[MD5] = md5.BytesToHexString().ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Warnings.Add("Bad ROM checksum.");
|
||||
}
|
||||
}
|
||||
else if (line.ToLower().StartsWith("comment author"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = ParseHeader(line, "comment author");
|
||||
}
|
||||
else if (line.ToLower().StartsWith("rerecordcount"))
|
||||
{
|
||||
int rerecordCount = 0;
|
||||
int.TryParse(ParseHeader(line, "rerecordCount"), out rerecordCount);
|
||||
|
||||
Result.Movie.Rerecords = (ulong)rerecordCount;
|
||||
}
|
||||
else if (line.ToLower().StartsWith("guid"))
|
||||
{
|
||||
continue; //We no longer care to keep this info
|
||||
}
|
||||
else if (line.ToLower().StartsWith("startsfromsavestate"))
|
||||
{
|
||||
// If this movie starts from a savestate, we can't support it.
|
||||
if (ParseHeader(line, "StartsFromSavestate") == "1")
|
||||
{
|
||||
Result.Errors.Add("Movies that begin with a savestate are not supported.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (line.ToLower().StartsWith("palflag"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.PAL] = ParseHeader(line, "palFlag");
|
||||
}
|
||||
else if (line.ToLower().StartsWith("fourscore"))
|
||||
{
|
||||
bool fourscore = (ParseHeader(line, "fourscore") == "1");
|
||||
if (fourscore)
|
||||
{
|
||||
// TODO: set controller config sync settings
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Movie.Comments.Add(line); // Everything not explicitly defined is treated as a comment.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ImportTextSubtitle(string line)
|
||||
{
|
||||
line = SingleSpaces(line);
|
||||
|
||||
// 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.
|
||||
string frame = line.Substring(0, first);
|
||||
string length = line.Substring(first + 1, second - first - 1);
|
||||
string message = line.Substring(second + 1).Trim();
|
||||
|
||||
return "subtitle " + frame + " 0 0 " + length + " FFFFFFFF " + message;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Decode a blob used in FM2 (base64:..., 0x123456...)
|
||||
private static byte[] DecodeBlob(string blob)
|
||||
{
|
||||
if (blob.Length < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (blob[0] == '0' && (blob[1] == 'x' || blob[1] == 'X'))
|
||||
{
|
||||
// hex
|
||||
return Util.HexStringToBytes(blob.Substring(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// base64
|
||||
if (!blob.ToLower().StartsWith("base64:"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return Convert.FromBase64String(blob.Substring(7));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public interface IMovieImport
|
||||
{
|
||||
ImportResult Import(string path);
|
||||
}
|
||||
|
||||
public abstract class MovieImporter : IMovieImport
|
||||
{
|
||||
public const string COMMENT = "comment";
|
||||
public const string COREORIGIN = "CoreOrigin";
|
||||
public const string CRC16 = "CRC16";
|
||||
public const string CRC32 = "CRC32";
|
||||
public const string EMULATIONORIGIN = "emuOrigin";
|
||||
public const string GAMECODE = "GameCode";
|
||||
public const string INTERNALCHECKSUM = "InternalChecksum";
|
||||
public const string JAPAN = "Japan";
|
||||
public const string MD5 = "MD5";
|
||||
public const string MOVIEORIGIN = "MovieOrigin";
|
||||
public const string PORT1 = "port1";
|
||||
public const string PORT2 = "port2";
|
||||
public const string PROJECTID = "ProjectID";
|
||||
public const string SHA256 = "SHA256";
|
||||
public const string SUPERGAMEBOYMODE = "SuperGameBoyMode";
|
||||
public const string STARTSECOND = "StartSecond";
|
||||
public const string STARTSUBSECOND = "StartSubSecond";
|
||||
public const string SYNCHACK = "SyncHack";
|
||||
public const string UNITCODE = "UnitCode";
|
||||
|
||||
public ImportResult Import(string path)
|
||||
{
|
||||
SourceFile = new FileInfo(path);
|
||||
|
||||
if (!SourceFile.Exists)
|
||||
{
|
||||
Result.Errors.Add(string.Format("Could not find the file {0}", path));
|
||||
return Result;
|
||||
}
|
||||
|
||||
var newFileName = SourceFile.FullName + "." + Bk2Movie.Extension;
|
||||
Result.Movie = new Bk2Movie(newFileName);
|
||||
|
||||
RunImport();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
protected ImportResult Result = new ImportResult();
|
||||
|
||||
protected FileInfo SourceFile;
|
||||
|
||||
protected abstract void RunImport();
|
||||
|
||||
// Get the content for a particular header.
|
||||
protected static string ParseHeader(string line, string headerName)
|
||||
{
|
||||
// Case-insensitive search.
|
||||
int x = line.ToLower().LastIndexOf(
|
||||
headerName.ToLower()
|
||||
) + headerName.Length;
|
||||
string str = line.Substring(x + 1, line.Length - x - 1);
|
||||
return str.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public class ImportResult
|
||||
{
|
||||
public ImportResult()
|
||||
{
|
||||
Warnings = new List<string>();
|
||||
Errors = new List<string>();
|
||||
}
|
||||
|
||||
public IList<string> Warnings { get; private set; }
|
||||
public IList<string> Errors { get; private set; }
|
||||
|
||||
public Bk2Movie Movie { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ImportExtension : Attribute
|
||||
{
|
||||
public ImportExtension(string extension)
|
||||
{
|
||||
Extension = extension;
|
||||
}
|
||||
|
||||
public string Extension { get; private set; }
|
||||
}
|
||||
}
|
|
@ -73,9 +73,21 @@ namespace BizHawk.Client.Common
|
|||
warningMsg = string.Empty;
|
||||
string ext = path != null ? Path.GetExtension(path).ToUpper() : string.Empty;
|
||||
|
||||
// TODO: reflect off the assembly and find an IMovieImporter with the appropriate ImportExtension metadata
|
||||
if (ext == ".FM2")
|
||||
{
|
||||
var result = new Fm2Import().Import(path);
|
||||
errorMsg = result.Errors.First();
|
||||
warningMsg = result.Errors.First();
|
||||
return result.Movie;
|
||||
}
|
||||
|
||||
if (ext == ".PJM")
|
||||
{
|
||||
return PJMImport.Import(path, out errorMsg, out warningMsg);
|
||||
var result = new PJMImport().Import(path);
|
||||
errorMsg = result.Errors.First();
|
||||
warningMsg = result.Errors.First();
|
||||
return result.Movie;
|
||||
}
|
||||
|
||||
BkmMovie m = new BkmMovie();
|
||||
|
|
|
@ -6,13 +6,12 @@ using System.Text;
|
|||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public static class PJMImport
|
||||
[ImportExtension(".pjm")]
|
||||
public class PJMImport : MovieImporter
|
||||
{
|
||||
public static Bk2Movie Import(string path, out string errorMsg, out string warningMsg)
|
||||
protected override void RunImport()
|
||||
{
|
||||
errorMsg = string.Empty;
|
||||
warningMsg = string.Empty;
|
||||
return new Bk2Movie();
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue