Convert mc2 to new import style, properly support pcecd flag, and number of controller ports

This commit is contained in:
adelikat 2019-11-10 11:35:45 -06:00
parent ced5b313a5
commit c1516de259
3 changed files with 192 additions and 101 deletions

View File

@ -156,6 +156,7 @@
<Compile Include="movie\bk2\StringLogs.cs" />
<Compile Include="movie\import\FcmImport.cs" />
<Compile Include="movie\import\LsmvImport.cs" />
<Compile Include="movie\import\Mc2Import.cs" />
<Compile Include="movie\import\PxmImport.cs" />
<Compile Include="movie\tasproj\IStateManager.cs" />
<Compile Include="movie\tasproj\StateManagerDecay.cs" />

View File

@ -0,0 +1,184 @@
using BizHawk.Emulation.Cores.PCEngine;
namespace BizHawk.Client.Common.movie.import
{
// MC2 file format: http://code.google.com/p/pcejin/wiki/MC2
// ReSharper disable once UnusedMember.Global
[ImportExtension(".mc2")]
public class Mc2Import : MovieImporter
{
private PceControllerDeck _deck;
protected override void RunImport()
{
var ss = new PCEngine.PCESyncSettings
{
Port1 = PceControllerType.Unplugged,
Port2 = PceControllerType.Unplugged,
Port3 = PceControllerType.Unplugged,
Port4 = PceControllerType.Unplugged,
Port5 = PceControllerType.Unplugged
};
_deck = new PceControllerDeck(
ss.Port1,
ss.Port2,
ss.Port3,
ss.Port4,
ss.Port5);
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "PCE";
using var sr = SourceFile.OpenText();
string line;
while ((line = sr.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
if (line[0] == '|')
{
ImportTextFrame(line);
}
else if (line.ToLower()
.StartsWith("ports"))
{
var portNumStr = ParseHeader(line, "ports");
if (int.TryParse(portNumStr, out int ports))
{
// Ugh
if (ports > 0)
{
ss.Port1 = PceControllerType.GamePad;
}
if (ports > 1)
{
ss.Port2 = PceControllerType.GamePad;
}
if (ports > 2)
{
ss.Port3 = PceControllerType.GamePad;
}
if (ports > 3)
{
ss.Port4 = PceControllerType.GamePad;
}
if (ports > 4)
{
ss.Port5 = PceControllerType.GamePad;
}
_deck = new PceControllerDeck(
ss.Port1,
ss.Port2,
ss.Port3,
ss.Port4,
ss.Port5);
}
}
else if (line.ToLower().StartsWith("pcecd"))
{
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "PCECD";
}
else if (line.ToLower().StartsWith("emuversion"))
{
Result.Movie.Comments.Add($"{EmulationOrigin} Mednafen/PCEjin version {ParseHeader(line, "emuVersion")}");
}
else if (line.ToLower().StartsWith("version"))
{
string version = ParseHeader(line, "version");
Result.Movie.Comments.Add($"{MovieOrigin} .mc2 version {version}");
}
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("comment author"))
{
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = ParseHeader(line, "comment author");
}
else if (line.ToLower().StartsWith("rerecordcount"))
{
int rerecordCount;
// Try to parse the re-record count as an integer, defaulting to 0 if it fails.
try
{
rerecordCount = int.Parse(ParseHeader(line, "rerecordCount"));
}
catch
{
rerecordCount = 0;
}
Result.Movie.Rerecords = (ulong)rerecordCount;
}
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.");
}
}
else
{
// Everything not explicitly defined is treated as a comment.
Result.Movie.Comments.Add(line);
}
}
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(ss);
}
// Import a frame from a text-based format.
private void ImportTextFrame(string line)
{
var buttons = new[] { "Up", "Down", "Left", "Right", "B1", "B2", "Run", "Select" };
var controllers = new SimpleController { Definition = _deck.Definition };
// Split up the sections of the frame.
string[] sections = line.Split('|');
/*
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.
*/
int start = 2;
int end = sections.Length - 1;
int playerOffset = -1;
for (int section = start; section < end; section++)
{
// The player number is one less than the section number for the reasons explained above.
int player = section + playerOffset;
string prefix = $"P{player} ";
// Only count lines with that have the right number of buttons and are for valid players.
if (
sections[section].Length == buttons.Length)
{
for (int button = 0; button < buttons.Length; button++)
{
// Consider the button pressed so long as its spot is not occupied by a ".".
controllers[prefix + buttons[button]] = sections[section][button] != '.';
}
}
}
// Convert the data for the controllers to a mnemonic and add it as a frame.
Result.Movie.AppendFrame(controllers);
}
}
}

View File

@ -145,9 +145,6 @@ namespace BizHawk.Client.Common
case ".GMV":
m = ImportGmv(path, out errorMsg, out warningMsg);
break;
case ".MC2":
m = ImportMc2(path, out errorMsg, out warningMsg);
break;
case ".MMV":
m = ImportMmv(path, out errorMsg, out warningMsg);
break;
@ -205,40 +202,11 @@ namespace BizHawk.Client.Common
return extensions.Any(ext => extension.ToUpper() == $".{ext}");
}
// 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;
}
// Import a frame from a text-based format.
private static BkmMovie ImportTextFrame(string line, int lineNum, BkmMovie m, string path, string platform, ref string warningMsg)
{
string[] buttons = { };
var controller = "";
var ext = path != null ? Path.GetExtension(path).ToUpper() : "";
switch (ext)
{
case ".MC2":
buttons = new[] { "Up", "Down", "Left", "Right", "B1", "B2", "Run", "Select" };
controller = "PC Engine Controller";
break;
case ".YMV":
buttons = new[] { "Left", "Right", "Up", "Down", "Start", "A", "B", "C", "X", "Y", "Z", "L", "R" };
controller = "Saturn Controller";
break;
}
var buttons = new[] { "Left", "Right", "Up", "Down", "Start", "A", "B", "C", "X", "Y", "Z", "L", "R" };
var controller = "Saturn Controller";
var controllers = new SimpleController { Definition = new ControllerDefinition { Name = controller } };
@ -258,12 +226,6 @@ namespace BizHawk.Client.Common
// The player number is one less than the section number for the reasons explained above.
int player = section + playerOffset;
string prefix = $"P{player} ";
// Gameboy doesn't currently have a prefix saying which player the input is for.
if (controllers.Definition.Name == "Gameboy Controller")
{
prefix = "";
}
// Only count lines with that have the right number of buttons and are for valid players.
if (
@ -283,47 +245,16 @@ namespace BizHawk.Client.Common
return m;
}
// Import a subtitle from a text-based format.
private static BkmMovie ImportTextSubtitle(string line, BkmMovie m, string path)
{
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.
var frame = line.Substring(0, first);
var length = line.Substring(first + 1, second - first - 1);
string message = line.Substring(second + 1).Trim();
m.Subtitles.AddFromString($"subtitle {frame} 0 0 {length} FFFFFFFF {message}");
}
return m;
}
// Import a text-based movie format. This works for .MC2 and .YMV.
private static BkmMovie ImportText(string path, out string errorMsg, out string warningMsg)
// YMV file format: https://code.google.com/p/yabause-rr/wiki/YMVfileformat
private static BkmMovie ImportYmv(string path, out string errorMsg, out string warningMsg)
{
errorMsg = warningMsg = "";
var m = new BkmMovie(path);
var file = new FileInfo(path);
var sr = file.OpenText();
var emulator = "";
var platform = "";
switch (Path.GetExtension(path).ToUpper())
{
case ".MC2":
emulator = "Mednafen/PCEjin";
platform = "PCE";
break;
case ".YMV":
emulator = "Yabause";
platform = "Sega Saturn";
break;
}
var emulator = "Yabause";
var platform = "Sega Saturn";
m.Header[HeaderKeys.PLATFORM] = platform;
int lineNum = 0;
@ -345,10 +276,6 @@ namespace BizHawk.Client.Common
return null;
}
}
else if (line.ToLower().StartsWith("sub"))
{
m = ImportTextSubtitle(line, m, path);
}
else if (line.ToLower().StartsWith("emuversion"))
{
m.Comments.Add($"{EMULATIONORIGIN} {emulator} version {ParseHeader(line, "emuVersion")}");
@ -362,10 +289,6 @@ namespace BizHawk.Client.Common
{
m.Header[HeaderKeys.GAMENAME] = ParseHeader(line, "romFilename");
}
else if (line.ToLower().StartsWith("cdgamename"))
{
m.Header[HeaderKeys.GAMENAME] = ParseHeader(line, "cdGameName");
}
else if (line.ToLower().StartsWith("comment author"))
{
m.Header[HeaderKeys.AUTHOR] = ParseHeader(line, "comment author");
@ -396,11 +319,6 @@ namespace BizHawk.Client.Common
return null;
}
}
else if (line.ToLower().StartsWith("palflag"))
{
bool pal = ParseHeader(line, "palFlag") == "1";
m.Header[HeaderKeys.PAL] = pal.ToString();
}
else if (line.ToLower().StartsWith("ispal"))
{
bool pal = ParseHeader(line, "isPal") == "1";
@ -739,12 +657,6 @@ namespace BizHawk.Client.Common
return m;
}
// MC2 file format: http://code.google.com/p/pcejin/wiki/MC2
private static BkmMovie ImportMc2(string path, out string errorMsg, out string warningMsg)
{
return ImportText(path, out errorMsg, out warningMsg);
}
// MMV file format: http://tasvideos.org/MMV.html
private static BkmMovie ImportMmv(string path, out string errorMsg, out string warningMsg)
{
@ -1471,11 +1383,5 @@ namespace BizHawk.Client.Common
fs.Close();
return m;
}
// YMV file format: https://code.google.com/p/yabause-rr/wiki/YMVfileformat
private static BkmMovie ImportYmv(string path, out string errorMsg, out string warningMsg)
{
return ImportText(path, out errorMsg, out warningMsg);
}
}
}