diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj index 5246ade05f..e08b916a9d 100644 --- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj @@ -156,6 +156,7 @@ + diff --git a/BizHawk.Client.Common/movie/import/Mc2Import.cs b/BizHawk.Client.Common/movie/import/Mc2Import.cs new file mode 100644 index 0000000000..3df4b4f798 --- /dev/null +++ b/BizHawk.Client.Common/movie/import/Mc2Import.cs @@ -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); + } + } +} diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs index cb307bb317..63ec1cb4d5 100644 --- a/BizHawk.Client.Common/movie/import/MovieImport.cs +++ b/BizHawk.Client.Common/movie/import/MovieImport.cs @@ -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); - } } }