From 189390f001da55acb560bc4153ebc9be707ef891 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Mon, 21 Sep 2015 18:01:49 +0100 Subject: [PATCH 01/10] Refactored out the import-via-legacy code --- .../movie/import/MovieImport.cs | 163 +++++++++--------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs index 3be588f999..85c4315ea2 100644 --- a/BizHawk.Client.Common/movie/import/MovieImport.cs +++ b/BizHawk.Client.Common/movie/import/MovieImport.cs @@ -67,98 +67,95 @@ namespace BizHawk.Client.Common } // Attempt to import another type of movie file into a movie object. - public static Bk2Movie ImportFile(string path, out string errorMsg, out string warningMsg) - { - errorMsg = string.Empty; - warningMsg = string.Empty; - string ext = path != null ? Path.GetExtension(path).ToUpper() : string.Empty; + public static Bk2Movie ImportFile(string path, out string errorMsg, out string warningMsg) { + errorMsg = string.Empty; + 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 (UsesLegacyImporter(ext)) { + return LegacyImportFile(ext, path, out errorMsg, out warningMsg).ToBk2(); + } else { + errorMsg = "No importer found for " + ext; + } + return null; + } - if (ext == ".PJM") - { - var result = new PJMImport().Import(path); - errorMsg = result.Errors.First(); - warningMsg = result.Errors.First(); - return result.Movie; - } + private static BkmMovie LegacyImportFile(string ext, string path, out string errorMsg, out string warningMsg) { + errorMsg = string.Empty; + warningMsg = string.Empty; - BkmMovie m = new BkmMovie(); - try - { - switch (ext) - { - case ".FCM": - m = ImportFCM(path, out errorMsg, out warningMsg); - break; - case ".FM2": - m = ImportFM2(path, out errorMsg, out warningMsg); - break; - case ".FMV": - m = ImportFMV(path, out errorMsg, out warningMsg); - break; - case ".GMV": - m = ImportGMV(path, out errorMsg, out warningMsg); - break; - case ".LSMV": - m = ImportLSMV(path, out errorMsg, out warningMsg); - break; - case ".MCM": - m = ImportMCM(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; - case ".NMV": - m = ImportNMV(path, out errorMsg, out warningMsg); - break; - case ".SMV": - m = ImportSMV(path, out errorMsg, out warningMsg); - break; - case ".VBM": - m = ImportVBM(path, out errorMsg, out warningMsg); - break; - case ".VMV": - m = ImportVMV(path, out errorMsg, out warningMsg); - break; - case ".YMV": - m = ImportYMV(path, out errorMsg, out warningMsg); - break; - case ".ZMV": - m = ImportZMV(path, out errorMsg, out warningMsg); - break; - case ".BKM": - m.Filename = path; - m.Load(false); - break; - } - } - catch (Exception except) - { - errorMsg = except.ToString(); - } + BkmMovie m = new BkmMovie(); - // Hack - return m.ToBk2(); - } + try { + switch (ext) { + case ".FCM": + m = ImportFCM(path, out errorMsg, out warningMsg); + break; + case ".FM2": + m = ImportFM2(path, out errorMsg, out warningMsg); + break; + case ".FMV": + m = ImportFMV(path, out errorMsg, out warningMsg); + break; + case ".GMV": + m = ImportGMV(path, out errorMsg, out warningMsg); + break; + case ".LSMV": + m = ImportLSMV(path, out errorMsg, out warningMsg); + break; + case ".MCM": + m = ImportMCM(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; + case ".NMV": + m = ImportNMV(path, out errorMsg, out warningMsg); + break; + case ".SMV": + m = ImportSMV(path, out errorMsg, out warningMsg); + break; + case ".VBM": + m = ImportVBM(path, out errorMsg, out warningMsg); + break; + case ".VMV": + m = ImportVMV(path, out errorMsg, out warningMsg); + break; + case ".YMV": + m = ImportYMV(path, out errorMsg, out warningMsg); + break; + case ".ZMV": + m = ImportZMV(path, out errorMsg, out warningMsg); + break; + case ".BKM": + m.Filename = path; + m.Load(false); + break; + } + } catch (Exception except) { + errorMsg = except.ToString(); + } - // Return whether or not the type of file provided can currently be imported. - public static bool IsValidMovieExtension(string extension) + return m; + } + + // Return whether or not the type of file provided can currently be imported. + public static bool IsValidMovieExtension(string extension) { + // TODO: Other movie formats that don't use a legacy importer (PJM/PXM, etc), + // when those are implemented + return UsesLegacyImporter(extension); + } + + // Return whether or not the type of file provided is currently imported by a legacy (i.e. to BKM not BK2) importer + public static bool UsesLegacyImporter(string extension) { string[] extensions = { - "FCM", "FM2", "FMV", "GMV", "MCM", "MC2", "MMV", "NMV", "LSMV", "SMV", "VBM", "VMV", "YMV", "ZMV" + "BKM", "FCM", "FM2", "FMV", "GMV", "MCM", "MC2", "MMV", "NMV", "LSMV", "SMV", "VBM", "VMV", "YMV", "ZMV" }; return extensions.Any(ext => extension.ToUpper() == "." + ext); } From 0e010a29883978a2f42c129f422b6da6d199a382 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Mon, 21 Sep 2015 18:13:58 +0100 Subject: [PATCH 02/10] Implement reflection-based importer The reflection-based importer, for file types that aren't imported via a legacy bkm file, dynamically searches the current module (i.e. BizHawk.Client.Common) for all IMovieImport instances with the ImportExtension attribute. This is difficult to truly test without more non-legacy import implementations, but appears to work with a dummy .pjm file. It may be desirable to instead search the current assembly instead of the current module, but I think all implementations should probably go in BizHawk.Client.Common, so I chose to search by module instead. --- .../movie/import/MovieImport.cs | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs index 85c4315ea2..f1311ce971 100644 --- a/BizHawk.Client.Common/movie/import/MovieImport.cs +++ b/BizHawk.Client.Common/movie/import/MovieImport.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; +using System.Reflection; using BizHawk.Common; using BizHawk.Common.BufferExtensions; @@ -74,10 +75,52 @@ namespace BizHawk.Client.Common if (UsesLegacyImporter(ext)) { return LegacyImportFile(ext, path, out errorMsg, out warningMsg).ToBk2(); - } else { - errorMsg = "No importer found for " + ext; } - return null; + + var importers = ImportersForExtension(ext); + var importerType = importers.FirstOrDefault(); + + if (importerType == default(Type)) { + errorMsg = "No importer found for file type " + ext; + return null; + } + + // Create a new instance of the importer class using the no-argument constructor + IMovieImport importer = importerType.GetConstructor(new Type[] { }) + .Invoke(new object[] { }) as IMovieImport; + + Bk2Movie movie = null; + + try { + var result = importer.Import(path); + if (result.Errors.Count() > 0) errorMsg = result.Errors.First(); + if (result.Warnings.Count() > 0) warningMsg = result.Warnings.First(); + movie = result.Movie; + } catch (Exception ex) { + errorMsg = ex.ToString(); + } + + return movie; + } + + private static IEnumerable ImportersForExtension(string ext) { + var info = typeof(MovieImport).Module; + var importers = from t in info.GetTypes() + where typeof(IMovieImport).IsAssignableFrom(t) + && TypeImportsExtension(t, ext) + select t; + + return importers; + } + + private static bool TypeImportsExtension(Type t, string ext) { + var attrs = (ImportExtension[])t.GetCustomAttributes(typeof(ImportExtension), inherit: false); + + if (attrs.Where(a => a.Extension.ToUpper() == ext.ToUpper()).Count() > 0) { + return true; + } else { + return false; + } } private static BkmMovie LegacyImportFile(string ext, string path, out string errorMsg, out string warningMsg) { From bb05bb57a83d3103a0727760a7ffe2c07da5f264 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Mon, 21 Sep 2015 22:33:29 +0100 Subject: [PATCH 03/10] Implement PJM format input Created a prototype input reader for binary-format PJMs. Also exposed controller definition creation as a static method on Octoshock as a convenient way to define the controller setup. --- .../movie/import/PJMImport.cs | 225 +++++++++++++++++- .../Consoles/Sony/PSX/Octoshock.cs | 135 ++++++----- 2 files changed, 290 insertions(+), 70 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index 342a12d492..a404a8b302 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -1,4 +1,6 @@ -using System; +using BizHawk.Emulation.Cores.Sony.PSX; +using BizHawk.Emulation.Common; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,9 +11,224 @@ namespace BizHawk.Client.Common [ImportExtension(".pjm")] public class PJMImport : MovieImporter { - protected override void RunImport() + protected override void RunImport() { - // TODO + Bk2Movie movie = Result.Movie; + MiscHeaderInfo info; + + movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + + using (var fs = SourceFile.OpenRead()) { + using (var br = new BinaryReader(fs)) { + info = parseHeader(movie, br); + + fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); + + if(info.binaryFormat) { + parseBinaryInputLog(br, movie, info); + return; + } + } + + if (!info.parseSuccessful) { + return; + } + + using (var sr = new StreamReader(fs)) { + parseTextInputLog(sr, movie, info); + } + } } - } + + private MiscHeaderInfo parseHeader(Bk2Movie movie, BinaryReader br) { + var info = new MiscHeaderInfo(); + + string magic = new string(br.ReadChars(4)); + if (magic != "PJM ") { + Result.Errors.Add("Not a PJM file: invalid magic number in file header."); + return info; + } + + UInt32 movieVersionNumber = br.ReadUInt32(); + if (movieVersionNumber != 2) { + Result.Warnings.Add(String.Format("Unexpected PJM format version: got {0}, expecting 2", movieVersionNumber)); + } + + // 008: UInt32 emulator version. + br.ReadUInt32(); + + byte flags = br.ReadByte(); + byte flags2 = br.ReadByte(); + if ((flags & 0x02) != 0) { + Result.Errors.Add("PJM file starts from savestate; this is currently unsupported."); + } + if ((flags & 0x04) != 0) { + movie.HeaderEntries.Add(HeaderKeys.PAL, "1"); + } + if ((flags & 0x08) != 0) { + Result.Errors.Add("PJM file contains embedded memory cards; this is currently unsupported."); + } + if ((flags & 0x10) != 0) { + Result.Errors.Add("PJM file contains embedded cheat list; this is currently unsupported."); + } + if ((flags & 0x20) != 0 || (flags2 & 0x06) != 0) { + Result.Errors.Add("PJM file relies on emulator hacks; this is currently unsupported."); + } + if ((flags & 0x40) != 0) { + info.binaryFormat = false; + } + if ((flags & 0x80) != 0 || (flags2 & 0x01) != 0) { + Result.Errors.Add("PJM file uses multitap; this is currently unsupported."); + return info; + } + + switch (br.ReadByte()) { + case 0: + info.player1Type.IsConnected = false; + break; + case 4: + info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; + break; + case 7: + info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; + break; + default: + Result.Errors.Add("PJM file has unrecognised controller type for Player 1."); + return info; + } + + switch (br.ReadByte()) { + case 0: + info.player2Type.IsConnected = false; + break; + case 4: + info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; + break; + case 7: + info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; + break; + default: + Result.Errors.Add("PJM file has unrecognised controller type for Player 2."); + return info; + } + + info.frameCount = br.ReadUInt32(); + UInt32 rerecordCount = br.ReadUInt32(); + movie.HeaderEntries.Add(HeaderKeys.RERECORDS, rerecordCount.ToString()); + + // 018: UInt32 savestateOffset + br.ReadUInt32(); + + // 01C: UInt32 memoryCard1Offset + br.ReadUInt32(); + + // 020: UInt32 memoryCard2Offset + br.ReadUInt32(); + + // 024: UInt32 cheatListOffset + br.ReadUInt32(); + + // 028: UInt32 cdRomIdOffset + // Source format is just the first up-to-8 alphanumeric characters of the CD label, + // so not so useful. + br.ReadUInt32(); + + info.controllerDataOffset = br.ReadUInt32(); + + UInt32 authorNameLength = br.ReadUInt32(); + char[] authorName = br.ReadChars((int)authorNameLength); + + movie.HeaderEntries.Add(HeaderKeys.AUTHOR, new string(authorName)); + + info.parseSuccessful = true; + return info; + } + + private void parseBinaryInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { + Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); + SimpleController controllers = new SimpleController(); + settings.Controllers = new[] { info.player1Type, info.player2Type }; + controllers.Type = Octoshock.CreateControllerDefinition(settings); + + string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", + "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; + + bool isCdTrayOpen = false; + + for (int frame = 0; frame < movie.FrameCount; ++frame) { + if (info.player1Type.IsConnected) { + UInt16 controllerState = br.ReadUInt16(); + for (int button = 0; button < buttons.Length; button++) { + controllers["P1 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); + if (((controllerState >> button) & 0x1) != 0 && button > 15) { + continue; + } + } + + if(info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + Tuple leftX = new Tuple("P1 LStick X", (float)br.ReadByte()); + Tuple leftY = new Tuple("P1 LStick Y", (float)br.ReadByte()); + Tuple rightX = new Tuple("P1 RStick X", (float)br.ReadByte()); + Tuple rightY = new Tuple("P1 RStick Y", (float)br.ReadByte()); + + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } + + if (info.player2Type.IsConnected) { + UInt16 controllerState = br.ReadUInt16(); + for (int button = 0; button < buttons.Length; button++) { + controllers["P2 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); + if (((controllerState >> button) & 0x1) != 0 && button > 15) { + continue; + } + } + + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + Tuple leftX = new Tuple("P2 LStick X", (float)br.ReadByte()); + Tuple leftY = new Tuple("P2 LStick Y", (float)br.ReadByte()); + Tuple rightX = new Tuple("P2 RStick X", (float)br.ReadByte()); + Tuple rightY = new Tuple("P2 RStick Y", (float)br.ReadByte()); + + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } + + byte controlState = br.ReadByte(); + controllers["Reset"] = (controlState & 0x02) != 0; + if((controlState & 0x04) != 0) { + if(isCdTrayOpen) { + controllers["Close"] = true; + } else { + controllers["Open"] = true; + } + isCdTrayOpen = !isCdTrayOpen; + } else { + controllers["Close"] = false; + controllers["Open"] = false; + } + + if((controlState & 0xFC) != 0) { + Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); + } + + movie.AppendFrame(controllers); + } + } + + private void parseTextInputLog(TextReader tr, Bk2Movie movie, MiscHeaderInfo info) { + throw new NotImplementedException(); + } + + private class MiscHeaderInfo { + public bool binaryFormat; + public UInt32 controllerDataOffset; + public UInt32 frameCount; + public Octoshock.ControllerSetting player1Type = new Octoshock.ControllerSetting() { IsConnected = true }; + public Octoshock.ControllerSetting player2Type = new Octoshock.ControllerSetting() { IsConnected = true }; + + public bool parseSuccessful = false; + } + + } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs index c8c738ac72..fd6684b904 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs @@ -36,74 +36,77 @@ namespace BizHawk.Emulation.Cores.Sony.PSX { public string SystemId { get { return "PSX"; } } + public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings) { + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = syncSettings.Controllers.All(c => c.Type == ControllerSetting.ControllerType.Gamepad) + ? "PSX Gamepad Controller" + : "PSX DualShock Controller"; // Meh, more nuanced logic doesn't really work with a simple property + + definition.BoolButtons.Clear(); + definition.FloatControls.Clear(); + + for (int i = 0; i < syncSettings.Controllers.Length; i++) { + if (syncSettings.Controllers[i].IsConnected) { + definition.BoolButtons.AddRange(new[] + { + "P" + (i + 1) + " Up", + "P" + (i + 1) + " Down", + "P" + (i + 1) + " Left", + "P" + (i + 1) + " Right", + "P" + (i + 1) + " Select", + "P" + (i + 1) + " Start", + "P" + (i + 1) + " Square", + "P" + (i + 1) + " Triangle", + "P" + (i + 1) + " Circle", + "P" + (i + 1) + " Cross", + "P" + (i + 1) + " L1", + "P" + (i + 1) + " R1", + "P" + (i + 1) + " L2", + "P" + (i + 1) + " R2", + }); + + if (syncSettings.Controllers[i].Type != ControllerSetting.ControllerType.Gamepad) { + definition.BoolButtons.Add("P" + (i + 1) + " L3"); + definition.BoolButtons.Add("P" + (i + 1) + " R3"); + definition.BoolButtons.Add("P" + (i + 1) + " MODE"); + + definition.FloatControls.AddRange(new[] + { + "P" + (i + 1) + " LStick X", + "P" + (i + 1) + " LStick Y", + "P" + (i + 1) + " RStick X", + "P" + (i + 1) + " RStick Y" + }); + + definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); + definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); + definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); + definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); + } + } + } + + definition.BoolButtons.AddRange(new[] + { + "Open", + "Close", + "Reset" + }); + + definition.FloatControls.Add("Disc Select"); + + definition.FloatRanges.Add( + //new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set) + //hmm.. I don't see why this wouldn't work + new[] { 0f, 1f, 1f } + ); + + return definition; + } + private void SetControllerButtons() { - ControllerDefinition = new ControllerDefinition(); - ControllerDefinition.Name = _SyncSettings.Controllers.All(c => c.Type == ControllerSetting.ControllerType.Gamepad) - ? "PSX Gamepad Controller" - : "PSX DualShock Controller"; // Meh, more nuanced logic doesn't really work with a simple property - - ControllerDefinition.BoolButtons.Clear(); - ControllerDefinition.FloatControls.Clear(); - - for (int i = 0; i < _SyncSettings.Controllers.Length; i++) - { - if (_SyncSettings.Controllers[i].IsConnected) - { - ControllerDefinition.BoolButtons.AddRange(new[] - { - "P" + (i + 1) + " Up", - "P" + (i + 1) + " Down", - "P" + (i + 1) + " Left", - "P" + (i + 1) + " Right", - "P" + (i + 1) + " Select", - "P" + (i + 1) + " Start", - "P" + (i + 1) + " Square", - "P" + (i + 1) + " Triangle", - "P" + (i + 1) + " Circle", - "P" + (i + 1) + " Cross", - "P" + (i + 1) + " L1", - "P" + (i + 1) + " R1", - "P" + (i + 1) + " L2", - "P" + (i + 1) + " R2", - }); - - if (_SyncSettings.Controllers[i].Type != ControllerSetting.ControllerType.Gamepad) - { - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " L3"); - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " R3"); - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " MODE"); - - ControllerDefinition.FloatControls.AddRange(new[] - { - "P" + (i + 1) + " LStick X", - "P" + (i + 1) + " LStick Y", - "P" + (i + 1) + " RStick X", - "P" + (i + 1) + " RStick Y" - }); - - ControllerDefinition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); - ControllerDefinition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); - ControllerDefinition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); - ControllerDefinition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); - } - } - } - - ControllerDefinition.BoolButtons.AddRange(new[] - { - "Open", - "Close", - "Reset" - }); - - ControllerDefinition.FloatControls.Add("Disc Select"); - - ControllerDefinition.FloatRanges.Add( - //new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set) - //hmm.. I don't see why this wouldn't work - new[] { 0f, 1f, 1f } - ); + ControllerDefinition = CreateControllerDefinition(_SyncSettings); } public string BoardName { get { return null; } } From 1df8397b1cff634512c6dce55a640e02be1e7062 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 00:47:03 +0100 Subject: [PATCH 04/10] Implement text flavour PJM importing Also implemented PXM importing via binary flavour PJM importing. Various bug fixes in my existing binary flavour PJM implementation. --- .../BizHawk.Client.Common.csproj | 1 + .../movie/import/PJMImport.cs | 191 +++++++++++++----- .../movie/import/PXMImport.cs | 36 ++++ 3 files changed, 182 insertions(+), 46 deletions(-) create mode 100644 BizHawk.Client.Common/movie/import/PXMImport.cs diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj index 9817297aa8..3be66de629 100644 --- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj @@ -158,6 +158,7 @@ Bk2Movie.cs + diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index a404a8b302..c1c4f0a4cd 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -1,14 +1,10 @@ using BizHawk.Emulation.Cores.Sony.PSX; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -namespace BizHawk.Client.Common -{ - [ImportExtension(".pjm")] +using System; +using System.IO; + +namespace BizHawk.Client.Common { + [ImportExtension(".pjm")] public class PJMImport : MovieImporter { protected override void RunImport() @@ -20,38 +16,33 @@ namespace BizHawk.Client.Common using (var fs = SourceFile.OpenRead()) { using (var br = new BinaryReader(fs)) { - info = parseHeader(movie, br); + info = parseHeader(movie, "PJM ", br); fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); if(info.binaryFormat) { parseBinaryInputLog(br, movie, info); - return; + } else { + parseTextInputLog(br, movie, info); } } - - if (!info.parseSuccessful) { - return; - } - - using (var sr = new StreamReader(fs)) { - parseTextInputLog(sr, movie, info); - } } + + movie.Save(); } - private MiscHeaderInfo parseHeader(Bk2Movie movie, BinaryReader br) { + protected MiscHeaderInfo parseHeader(Bk2Movie movie, string expectedMagic, BinaryReader br) { var info = new MiscHeaderInfo(); string magic = new string(br.ReadChars(4)); - if (magic != "PJM ") { - Result.Errors.Add("Not a PJM file: invalid magic number in file header."); + if (magic != expectedMagic) { + Result.Errors.Add("Not a " + expectedMagic + "file: invalid magic number in file header."); return info; } UInt32 movieVersionNumber = br.ReadUInt32(); if (movieVersionNumber != 2) { - Result.Warnings.Add(String.Format("Unexpected PJM format version: got {0}, expecting 2", movieVersionNumber)); + Result.Warnings.Add(String.Format("Unexpected movie version: got {0}, expecting 2", movieVersionNumber)); } // 008: UInt32 emulator version. @@ -60,30 +51,34 @@ namespace BizHawk.Client.Common byte flags = br.ReadByte(); byte flags2 = br.ReadByte(); if ((flags & 0x02) != 0) { - Result.Errors.Add("PJM file starts from savestate; this is currently unsupported."); + Result.Errors.Add("Movie starts from savestate; this is currently unsupported."); } if ((flags & 0x04) != 0) { movie.HeaderEntries.Add(HeaderKeys.PAL, "1"); } if ((flags & 0x08) != 0) { - Result.Errors.Add("PJM file contains embedded memory cards; this is currently unsupported."); + Result.Errors.Add("Movie contains embedded memory cards; this is currently unsupported."); } if ((flags & 0x10) != 0) { - Result.Errors.Add("PJM file contains embedded cheat list; this is currently unsupported."); + Result.Errors.Add("Movie contains embedded cheat list; this is currently unsupported."); } if ((flags & 0x20) != 0 || (flags2 & 0x06) != 0) { - Result.Errors.Add("PJM file relies on emulator hacks; this is currently unsupported."); + Result.Errors.Add("Movie relies on emulator hacks; this is currently unsupported."); } if ((flags & 0x40) != 0) { info.binaryFormat = false; } if ((flags & 0x80) != 0 || (flags2 & 0x01) != 0) { - Result.Errors.Add("PJM file uses multitap; this is currently unsupported."); + Result.Errors.Add("Movie uses multitap; this is currently unsupported."); return info; } + // Player 1 controller type switch (br.ReadByte()) { + // It seems to be inconsistent in the files I looked at which of these is used + // to mean no controller present. case 0: + case 8: info.player1Type.IsConnected = false; break; case 4: @@ -93,12 +88,14 @@ namespace BizHawk.Client.Common info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; break; default: - Result.Errors.Add("PJM file has unrecognised controller type for Player 1."); + Result.Errors.Add("Movie has unrecognised controller type for Player 1."); return info; } + // Player 2 controller type switch (br.ReadByte()) { case 0: + case 8: info.player2Type.IsConnected = false; break; case 4: @@ -108,30 +105,24 @@ namespace BizHawk.Client.Common info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; break; default: - Result.Errors.Add("PJM file has unrecognised controller type for Player 2."); + Result.Errors.Add("Movie has unrecognised controller type for Player 2."); return info; } info.frameCount = br.ReadUInt32(); UInt32 rerecordCount = br.ReadUInt32(); - movie.HeaderEntries.Add(HeaderKeys.RERECORDS, rerecordCount.ToString()); + movie.HeaderEntries[HeaderKeys.RERECORDS] = rerecordCount.ToString(); // 018: UInt32 savestateOffset - br.ReadUInt32(); - // 01C: UInt32 memoryCard1Offset - br.ReadUInt32(); - // 020: UInt32 memoryCard2Offset - br.ReadUInt32(); - // 024: UInt32 cheatListOffset - br.ReadUInt32(); // 028: UInt32 cdRomIdOffset // Source format is just the first up-to-8 alphanumeric characters of the CD label, // so not so useful. - br.ReadUInt32(); + + br.ReadBytes(20); info.controllerDataOffset = br.ReadUInt32(); @@ -144,7 +135,7 @@ namespace BizHawk.Client.Common return info; } - private void parseBinaryInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { + protected void parseBinaryInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); SimpleController controllers = new SimpleController(); settings.Controllers = new[] { info.player1Type, info.player2Type }; @@ -155,10 +146,15 @@ namespace BizHawk.Client.Common bool isCdTrayOpen = false; - for (int frame = 0; frame < movie.FrameCount; ++frame) { + for (int frame = 0; frame < info.frameCount; ++frame) { if (info.player1Type.IsConnected) { UInt16 controllerState = br.ReadUInt16(); - for (int button = 0; button < buttons.Length; button++) { + + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P1 Select"] = (controllerState & 0x1) != 0; + + for (int button = 3; button < buttons.Length; button++) { controllers["P1 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); if (((controllerState >> button) & 0x1) != 0 && button > 15) { continue; @@ -166,6 +162,8 @@ namespace BizHawk.Client.Common } if(info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + controllers["P1 L3"] = (controllerState & 0x2) != 0; + controllers["P1 R3"] = (controllerState & 0x4) != 0; Tuple leftX = new Tuple("P1 LStick X", (float)br.ReadByte()); Tuple leftY = new Tuple("P1 LStick Y", (float)br.ReadByte()); Tuple rightX = new Tuple("P1 RStick X", (float)br.ReadByte()); @@ -216,12 +214,113 @@ namespace BizHawk.Client.Common } } - private void parseTextInputLog(TextReader tr, Bk2Movie movie, MiscHeaderInfo info) { - throw new NotImplementedException(); + protected void parseTextInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { + Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); + SimpleController controllers = new SimpleController(); + settings.Controllers = new[] { info.player1Type, info.player2Type }; + controllers.Type = Octoshock.CreateControllerDefinition(settings); + + string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", + "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; + + bool isCdTrayOpen = false; + + for (int frame = 0; frame < info.frameCount; ++frame) { + if (info.player1Type.IsConnected) { + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P1 Select"] = br.ReadChar() != '.'; + + if(info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + controllers["P1 L3"] = br.ReadChar() != '.'; + controllers["P1 R3"] = br.ReadChar() != '.'; + } + + for (int button = 3; button < buttons.Length; button++) { + controllers["P1 " + buttons[button]] = br.ReadChar() != '.'; + } + + if (info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + // The analog controls are encoded as four space-separated numbers with a leading space + string leftXRaw = new string(br.ReadChars(4)).Trim(); + string leftYRaw = new string(br.ReadChars(4)).Trim(); + string rightXRaw = new string(br.ReadChars(4)).Trim(); + string rightYRaw = new string(br.ReadChars(4)).Trim(); + + + Tuple leftX = new Tuple("P1 LStick X", float.Parse(leftXRaw)); + Tuple leftY = new Tuple("P1 LStick Y", float.Parse(leftYRaw)); + Tuple rightX = new Tuple("P1 RStick X", float.Parse(rightXRaw)); + Tuple rightY = new Tuple("P1 RStick Y", float.Parse(rightYRaw)); + + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } + + // Each controller is terminated with a pipeline. + br.ReadChar(); + + if (info.player2Type.IsConnected) { + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P2 Select"] = br.ReadChar() != '.'; + + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + controllers["P2 L3"] = br.ReadChar() != '.'; + controllers["P2 R3"] = br.ReadChar() != '.'; + } + + for (int button = 3; button < buttons.Length; button++) { + controllers["P2 " + buttons[button]] = br.ReadChar() != '.'; + } + + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { + // The analog controls are encoded as four space-separated numbers with a leading space + string leftXRaw = new string(br.ReadChars(4)).Trim(); + string leftYRaw = new string(br.ReadChars(4)).Trim(); + string rightXRaw = new string(br.ReadChars(4)).Trim(); + string rightYRaw = new string(br.ReadChars(4)).Trim(); + + + Tuple leftX = new Tuple("P2 LStick X", float.Parse(leftXRaw)); + Tuple leftY = new Tuple("P2 LStick Y", float.Parse(leftYRaw)); + Tuple rightX = new Tuple("P2 RStick X", float.Parse(rightXRaw)); + Tuple rightY = new Tuple("P2 RStick Y", float.Parse(rightYRaw)); + + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } + + // Each controller is terminated with a pipeline. + br.ReadChar(); + + byte controlState = br.ReadByte(); + controllers["Reset"] = (controlState & 0x02) != 0; + if ((controlState & 0x04) != 0) { + if (isCdTrayOpen) { + controllers["Close"] = true; + } else { + controllers["Open"] = true; + } + isCdTrayOpen = !isCdTrayOpen; + } else { + controllers["Close"] = false; + controllers["Open"] = false; + } + + if ((controlState & 0xFC) != 0) { + Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); + } + + // Each controller is terminated with a pipeline. + br.ReadChar(); + + movie.AppendFrame(controllers); + } } - private class MiscHeaderInfo { - public bool binaryFormat; + protected class MiscHeaderInfo { + public bool binaryFormat = true; public UInt32 controllerDataOffset; public UInt32 frameCount; public Octoshock.ControllerSetting player1Type = new Octoshock.ControllerSetting() { IsConnected = true }; diff --git a/BizHawk.Client.Common/movie/import/PXMImport.cs b/BizHawk.Client.Common/movie/import/PXMImport.cs new file mode 100644 index 0000000000..61cebf85e8 --- /dev/null +++ b/BizHawk.Client.Common/movie/import/PXMImport.cs @@ -0,0 +1,36 @@ +using System.IO; + +namespace BizHawk.Client.Common.movie.import { + + // PXM files are directly compatible with binary-format PJM files, with the only + // difference being fewer flags implemented in the header, hence just calling the + // base class methods via a subclass. + // + // However, the magic number/file signature is slightly different, requiring some + // refactoring to avoid PXM-specific code in the PJMImport class. + [ImportExtension(".pxm")] + class PXMImport : PJMImport { + protected override void RunImport() { + Bk2Movie movie = Result.Movie; + MiscHeaderInfo info; + + movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + + using (var fs = SourceFile.OpenRead()) { + using (var br = new BinaryReader(fs)) { + info = parseHeader(movie, "PXM ", br); + + fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); + + if (info.binaryFormat) { + parseBinaryInputLog(br, movie, info); + } else { + parseTextInputLog(br, movie, info); + } + } + } + + movie.Save(); + } + } +} From 3f899b60c9d675fe8ec5d24d819fe79637b3bf3f Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 01:02:49 +0100 Subject: [PATCH 05/10] Formatting rules. Bleh --- .../movie/import/MovieImport.cs | 275 +++++---- .../movie/import/PJMImport.cs | 579 ++++++++++-------- .../movie/import/PXMImport.cs | 62 +- .../Consoles/Sony/PSX/Octoshock.cs | 122 ++-- 4 files changed, 560 insertions(+), 478 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs index f1311ce971..794f30b8c5 100644 --- a/BizHawk.Client.Common/movie/import/MovieImport.cs +++ b/BizHawk.Client.Common/movie/import/MovieImport.cs @@ -54,7 +54,7 @@ namespace BizHawk.Client.Common if (!string.IsNullOrWhiteSpace(warningMsg)) { messageCallback(warningMsg); - + } else { @@ -68,133 +68,150 @@ namespace BizHawk.Client.Common } // Attempt to import another type of movie file into a movie object. - public static Bk2Movie ImportFile(string path, out string errorMsg, out string warningMsg) { - errorMsg = string.Empty; - warningMsg = string.Empty; - string ext = path != null ? Path.GetExtension(path).ToUpper() : string.Empty; + public static Bk2Movie ImportFile(string path, out string errorMsg, out string warningMsg) + { + errorMsg = string.Empty; + warningMsg = string.Empty; + string ext = path != null ? Path.GetExtension(path).ToUpper() : string.Empty; - if (UsesLegacyImporter(ext)) { - return LegacyImportFile(ext, path, out errorMsg, out warningMsg).ToBk2(); - } + if (UsesLegacyImporter(ext)) + { + return LegacyImportFile(ext, path, out errorMsg, out warningMsg).ToBk2(); + } - var importers = ImportersForExtension(ext); - var importerType = importers.FirstOrDefault(); + var importers = ImportersForExtension(ext); + var importerType = importers.FirstOrDefault(); - if (importerType == default(Type)) { - errorMsg = "No importer found for file type " + ext; - return null; - } + if (importerType == default(Type)) + { + errorMsg = "No importer found for file type " + ext; + return null; + } - // Create a new instance of the importer class using the no-argument constructor - IMovieImport importer = importerType.GetConstructor(new Type[] { }) - .Invoke(new object[] { }) as IMovieImport; + // Create a new instance of the importer class using the no-argument constructor + IMovieImport importer = importerType.GetConstructor(new Type[] { }) + .Invoke(new object[] { }) as IMovieImport; - Bk2Movie movie = null; + Bk2Movie movie = null; - try { - var result = importer.Import(path); - if (result.Errors.Count() > 0) errorMsg = result.Errors.First(); - if (result.Warnings.Count() > 0) warningMsg = result.Warnings.First(); - movie = result.Movie; - } catch (Exception ex) { - errorMsg = ex.ToString(); - } + try + { + var result = importer.Import(path); + if (result.Errors.Count() > 0) errorMsg = result.Errors.First(); + if (result.Warnings.Count() > 0) warningMsg = result.Warnings.First(); + movie = result.Movie; + } + catch (Exception ex) + { + errorMsg = ex.ToString(); + } - return movie; - } + return movie; + } - private static IEnumerable ImportersForExtension(string ext) { - var info = typeof(MovieImport).Module; - var importers = from t in info.GetTypes() - where typeof(IMovieImport).IsAssignableFrom(t) - && TypeImportsExtension(t, ext) - select t; + private static IEnumerable ImportersForExtension(string ext) + { + var info = typeof(MovieImport).Module; + var importers = from t in info.GetTypes() + where typeof(IMovieImport).IsAssignableFrom(t) + && TypeImportsExtension(t, ext) + select t; - return importers; - } + return importers; + } - private static bool TypeImportsExtension(Type t, string ext) { - var attrs = (ImportExtension[])t.GetCustomAttributes(typeof(ImportExtension), inherit: false); + private static bool TypeImportsExtension(Type t, string ext) + { + var attrs = (ImportExtension[])t.GetCustomAttributes(typeof(ImportExtension), inherit: false); - if (attrs.Where(a => a.Extension.ToUpper() == ext.ToUpper()).Count() > 0) { - return true; - } else { - return false; - } - } + if (attrs.Where(a => a.Extension.ToUpper() == ext.ToUpper()).Count() > 0) + { + return true; + } + else + { + return false; + } + } - private static BkmMovie LegacyImportFile(string ext, string path, out string errorMsg, out string warningMsg) { - errorMsg = string.Empty; - warningMsg = string.Empty; + private static BkmMovie LegacyImportFile(string ext, string path, out string errorMsg, out string warningMsg) + { + errorMsg = string.Empty; + warningMsg = string.Empty; - BkmMovie m = new BkmMovie(); + BkmMovie m = new BkmMovie(); - try { - switch (ext) { - case ".FCM": - m = ImportFCM(path, out errorMsg, out warningMsg); - break; - case ".FM2": - m = ImportFM2(path, out errorMsg, out warningMsg); - break; - case ".FMV": - m = ImportFMV(path, out errorMsg, out warningMsg); - break; - case ".GMV": - m = ImportGMV(path, out errorMsg, out warningMsg); - break; - case ".LSMV": - m = ImportLSMV(path, out errorMsg, out warningMsg); - break; - case ".MCM": - m = ImportMCM(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; - case ".NMV": - m = ImportNMV(path, out errorMsg, out warningMsg); - break; - case ".SMV": - m = ImportSMV(path, out errorMsg, out warningMsg); - break; - case ".VBM": - m = ImportVBM(path, out errorMsg, out warningMsg); - break; - case ".VMV": - m = ImportVMV(path, out errorMsg, out warningMsg); - break; - case ".YMV": - m = ImportYMV(path, out errorMsg, out warningMsg); - break; - case ".ZMV": - m = ImportZMV(path, out errorMsg, out warningMsg); - break; - case ".BKM": - m.Filename = path; - m.Load(false); - break; - } - } catch (Exception except) { - errorMsg = except.ToString(); - } + try + { + switch (ext) + { + case ".FCM": + m = ImportFCM(path, out errorMsg, out warningMsg); + break; + case ".FM2": + m = ImportFM2(path, out errorMsg, out warningMsg); + break; + case ".FMV": + m = ImportFMV(path, out errorMsg, out warningMsg); + break; + case ".GMV": + m = ImportGMV(path, out errorMsg, out warningMsg); + break; + case ".LSMV": + m = ImportLSMV(path, out errorMsg, out warningMsg); + break; + case ".MCM": + m = ImportMCM(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; + case ".NMV": + m = ImportNMV(path, out errorMsg, out warningMsg); + break; + case ".SMV": + m = ImportSMV(path, out errorMsg, out warningMsg); + break; + case ".VBM": + m = ImportVBM(path, out errorMsg, out warningMsg); + break; + case ".VMV": + m = ImportVMV(path, out errorMsg, out warningMsg); + break; + case ".YMV": + m = ImportYMV(path, out errorMsg, out warningMsg); + break; + case ".ZMV": + m = ImportZMV(path, out errorMsg, out warningMsg); + break; + case ".BKM": + m.Filename = path; + m.Load(false); + break; + } + } + catch (Exception except) + { + errorMsg = except.ToString(); + } - return m; - } + return m; + } - // Return whether or not the type of file provided can currently be imported. - public static bool IsValidMovieExtension(string extension) { - // TODO: Other movie formats that don't use a legacy importer (PJM/PXM, etc), - // when those are implemented - return UsesLegacyImporter(extension); - } + // Return whether or not the type of file provided can currently be imported. + public static bool IsValidMovieExtension(string extension) + { + // TODO: Other movie formats that don't use a legacy importer (PJM/PXM, etc), + // when those are implemented + return UsesLegacyImporter(extension); + } - // Return whether or not the type of file provided is currently imported by a legacy (i.e. to BKM not BK2) importer - public static bool UsesLegacyImporter(string extension) + // Return whether or not the type of file provided is currently imported by a legacy (i.e. to BKM not BK2) importer + public static bool UsesLegacyImporter(string extension) { string[] extensions = { @@ -253,7 +270,7 @@ namespace BizHawk.Client.Common controller = "Saturn Controller"; break; } - var controllers = new SimpleController {Type = new ControllerDefinition {Name = controller}}; + var controllers = new SimpleController { Type = new ControllerDefinition { Name = controller } }; // Split up the sections of the frame. string[] sections = line.Split('|'); if (ext == ".FM2" && sections.Length >= 2 && sections[1].Length != 0) @@ -683,7 +700,7 @@ namespace BizHawk.Client.Common m.Header[HeaderKeys.AUTHOR] = author; // Advance to first byte of input data. r.BaseStream.Position = firstFrameOffset; - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "NES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } }; string[] buttons = { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right" }; bool fds = false; bool fourscore = false; @@ -872,7 +889,7 @@ namespace BizHawk.Client.Common else { FDS = false; - + } m.Header[HeaderKeys.PLATFORM] = "NES"; @@ -914,7 +931,7 @@ namespace BizHawk.Client.Common */ m.Header[HeaderKeys.PAL] = "False"; // 090 frame data begins here - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "NES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } }; /* * 01 Right * 02 Left @@ -1418,7 +1435,7 @@ namespace BizHawk.Client.Common r.ReadBytes(103); // TODO: Verify if NTSC/"PAL" mode used for the movie can be detected or not. // 100 variable Input data - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = name + " Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = name + " Controller" } }; int bytes = 256; // The input stream consists of 1 byte for power-on and reset, and then X bytes per each input port per frame. if (platform == "nes") @@ -1539,7 +1556,7 @@ namespace BizHawk.Client.Common // 00e4-00f3: binary: rom MD5 digest byte[] md5 = r.ReadBytes(16); m.Header[MD5] = string.Format("{0:x8}", md5.BytesToHexString().ToLower()); - var controllers = new SimpleController { Type = new ControllerDefinition { Name = "SMS Controller" }}; + var controllers = new SimpleController { Type = new ControllerDefinition { Name = "SMS Controller" } }; /* 76543210 * bit 0 (0x01): up @@ -1764,7 +1781,7 @@ namespace BizHawk.Client.Common // ... 4-byte little-endian unsigned int: length of controller data in bytes uint length = r.ReadUInt32(); // ... (variable) controller data - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "NES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } }; /* Standard controllers store data in the following format: * 01: A @@ -1867,7 +1884,7 @@ namespace BizHawk.Client.Common * bit 4: controller 5 in use * other: reserved, set to 0 */ - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "SNES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SNES Controller" } }; bool[] controllersUsed = new bool[5]; for (int controller = 1; controller <= controllersUsed.Length; controller++) { @@ -1984,7 +2001,7 @@ namespace BizHawk.Client.Common { "Right", "Left", "Down", "Up", "Start", "Select", "Y", "B", "R", "L", "X", "A" }; - + for (int frame = 0; frame <= frameCount; frame++) { controllers["Reset"] = true; @@ -2179,16 +2196,16 @@ namespace BizHawk.Client.Common // bit 2: if "1", movie is for the SGB system bool is_sgb = (((flags >> 2) & 0x1) != 0); // other: reserved, set to 0 - + // (At most one of bits 0, 1, 2 can be "1") //if (!(is_gba ^ is_gbc ^ is_sgb) && (is_gba || is_gbc || is_sgb)) //TODO: adelikat: this doesn't do what the comment above suggests it is trying to check for, it is always false! //{ - //errorMsg = "This is not a valid .VBM file."; - //r.Close(); - //fs.Close(); - //return null; + //errorMsg = "This is not a valid .VBM file."; + //r.Close(); + //fs.Close(); + //return null; //} - + // (If all 3 of these bits are "0", it is for regular GB.) string platform = "GB"; if (is_gba) @@ -2278,7 +2295,7 @@ namespace BizHawk.Client.Common string movieDescription = NullTerminated(r.ReadStringFixedAscii(128)); m.Comments.Add(COMMENT + " " + movieDescription); r.BaseStream.Position = firstFrameOffset; - SimpleController controllers = new SimpleController {Type = new ControllerDefinition()}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition() }; if (platform != "GBA") { controllers.Type.Name = "Gameboy Controller"; @@ -2308,7 +2325,7 @@ namespace BizHawk.Client.Common * 00 40 Down motion sensor * 00 80 Up motion sensor */ - string[] other = + string[] other = { "Reset (old timing)" , "Reset (new timing since version 1.1)", "Left motion sensor", "Right motion sensor", "Down motion sensor", "Up motion sensor" @@ -2456,7 +2473,7 @@ namespace BizHawk.Client.Common return m; } r.BaseStream.Position = firstFrameOffset; - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "NES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "NES Controller" } }; /* * 01 A * 02 B @@ -2693,7 +2710,7 @@ namespace BizHawk.Client.Common uint savestateSize = (uint)((r.ReadByte() | (r.ReadByte() << 8) | (r.ReadByte() << 16)) & 0x7FFFFF); // Next follows a ZST format savestate. r.ReadBytes((int)savestateSize); - SimpleController controllers = new SimpleController {Type = new ControllerDefinition {Name = "SNES Controller"}}; + SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SNES Controller" } }; /* * bit 11: A * bit 10: X diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index c1c4f0a4cd..045ccdaf1b 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -3,331 +3,384 @@ using System; using System.IO; -namespace BizHawk.Client.Common { - [ImportExtension(".pjm")] +namespace BizHawk.Client.Common +{ + [ImportExtension(".pjm")] public class PJMImport : MovieImporter { - protected override void RunImport() + protected override void RunImport() { - Bk2Movie movie = Result.Movie; - MiscHeaderInfo info; + Bk2Movie movie = Result.Movie; + MiscHeaderInfo info; - movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); - using (var fs = SourceFile.OpenRead()) { - using (var br = new BinaryReader(fs)) { - info = parseHeader(movie, "PJM ", br); + using (var fs = SourceFile.OpenRead()) + { + using (var br = new BinaryReader(fs)) + { + info = parseHeader(movie, "PJM ", br); - fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); + fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); - if(info.binaryFormat) { - parseBinaryInputLog(br, movie, info); - } else { - parseTextInputLog(br, movie, info); - } - } - } + if (info.binaryFormat) + { + parseBinaryInputLog(br, movie, info); + } + else + { + parseTextInputLog(br, movie, info); + } + } + } - movie.Save(); + movie.Save(); } - protected MiscHeaderInfo parseHeader(Bk2Movie movie, string expectedMagic, BinaryReader br) { - var info = new MiscHeaderInfo(); + protected MiscHeaderInfo parseHeader(Bk2Movie movie, string expectedMagic, BinaryReader br) + { + var info = new MiscHeaderInfo(); - string magic = new string(br.ReadChars(4)); - if (magic != expectedMagic) { - Result.Errors.Add("Not a " + expectedMagic + "file: invalid magic number in file header."); - return info; - } + string magic = new string(br.ReadChars(4)); + if (magic != expectedMagic) + { + Result.Errors.Add("Not a " + expectedMagic + "file: invalid magic number in file header."); + return info; + } - UInt32 movieVersionNumber = br.ReadUInt32(); - if (movieVersionNumber != 2) { - Result.Warnings.Add(String.Format("Unexpected movie version: got {0}, expecting 2", movieVersionNumber)); - } + UInt32 movieVersionNumber = br.ReadUInt32(); + if (movieVersionNumber != 2) + { + Result.Warnings.Add(String.Format("Unexpected movie version: got {0}, expecting 2", movieVersionNumber)); + } - // 008: UInt32 emulator version. - br.ReadUInt32(); + // 008: UInt32 emulator version. + br.ReadUInt32(); - byte flags = br.ReadByte(); - byte flags2 = br.ReadByte(); - if ((flags & 0x02) != 0) { - Result.Errors.Add("Movie starts from savestate; this is currently unsupported."); - } - if ((flags & 0x04) != 0) { - movie.HeaderEntries.Add(HeaderKeys.PAL, "1"); - } - if ((flags & 0x08) != 0) { - Result.Errors.Add("Movie contains embedded memory cards; this is currently unsupported."); - } - if ((flags & 0x10) != 0) { - Result.Errors.Add("Movie contains embedded cheat list; this is currently unsupported."); - } - if ((flags & 0x20) != 0 || (flags2 & 0x06) != 0) { - Result.Errors.Add("Movie relies on emulator hacks; this is currently unsupported."); - } - if ((flags & 0x40) != 0) { - info.binaryFormat = false; - } - if ((flags & 0x80) != 0 || (flags2 & 0x01) != 0) { - Result.Errors.Add("Movie uses multitap; this is currently unsupported."); - return info; - } + byte flags = br.ReadByte(); + byte flags2 = br.ReadByte(); + if ((flags & 0x02) != 0) + { + Result.Errors.Add("Movie starts from savestate; this is currently unsupported."); + } + if ((flags & 0x04) != 0) + { + movie.HeaderEntries.Add(HeaderKeys.PAL, "1"); + } + if ((flags & 0x08) != 0) + { + Result.Errors.Add("Movie contains embedded memory cards; this is currently unsupported."); + } + if ((flags & 0x10) != 0) + { + Result.Errors.Add("Movie contains embedded cheat list; this is currently unsupported."); + } + if ((flags & 0x20) != 0 || (flags2 & 0x06) != 0) + { + Result.Errors.Add("Movie relies on emulator hacks; this is currently unsupported."); + } + if ((flags & 0x40) != 0) + { + info.binaryFormat = false; + } + if ((flags & 0x80) != 0 || (flags2 & 0x01) != 0) + { + Result.Errors.Add("Movie uses multitap; this is currently unsupported."); + return info; + } - // Player 1 controller type - switch (br.ReadByte()) { - // It seems to be inconsistent in the files I looked at which of these is used - // to mean no controller present. - case 0: - case 8: - info.player1Type.IsConnected = false; - break; - case 4: - info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; - break; - case 7: - info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; - break; - default: - Result.Errors.Add("Movie has unrecognised controller type for Player 1."); - return info; - } + // Player 1 controller type + switch (br.ReadByte()) + { + // It seems to be inconsistent in the files I looked at which of these is used + // to mean no controller present. + case 0: + case 8: + info.player1Type.IsConnected = false; + break; + case 4: + info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; + break; + case 7: + info.player1Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; + break; + default: + Result.Errors.Add("Movie has unrecognised controller type for Player 1."); + return info; + } - // Player 2 controller type - switch (br.ReadByte()) { - case 0: - case 8: - info.player2Type.IsConnected = false; - break; - case 4: - info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; - break; - case 7: - info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; - break; - default: - Result.Errors.Add("Movie has unrecognised controller type for Player 2."); - return info; - } + // Player 2 controller type + switch (br.ReadByte()) + { + case 0: + case 8: + info.player2Type.IsConnected = false; + break; + case 4: + info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.Gamepad; + break; + case 7: + info.player2Type.Type = Octoshock.ControllerSetting.ControllerType.DualShock; + break; + default: + Result.Errors.Add("Movie has unrecognised controller type for Player 2."); + return info; + } - info.frameCount = br.ReadUInt32(); - UInt32 rerecordCount = br.ReadUInt32(); - movie.HeaderEntries[HeaderKeys.RERECORDS] = rerecordCount.ToString(); + info.frameCount = br.ReadUInt32(); + UInt32 rerecordCount = br.ReadUInt32(); + movie.HeaderEntries[HeaderKeys.RERECORDS] = rerecordCount.ToString(); - // 018: UInt32 savestateOffset - // 01C: UInt32 memoryCard1Offset - // 020: UInt32 memoryCard2Offset - // 024: UInt32 cheatListOffset + // 018: UInt32 savestateOffset + // 01C: UInt32 memoryCard1Offset + // 020: UInt32 memoryCard2Offset + // 024: UInt32 cheatListOffset - // 028: UInt32 cdRomIdOffset - // Source format is just the first up-to-8 alphanumeric characters of the CD label, - // so not so useful. + // 028: UInt32 cdRomIdOffset + // Source format is just the first up-to-8 alphanumeric characters of the CD label, + // so not so useful. - br.ReadBytes(20); + br.ReadBytes(20); - info.controllerDataOffset = br.ReadUInt32(); + info.controllerDataOffset = br.ReadUInt32(); - UInt32 authorNameLength = br.ReadUInt32(); - char[] authorName = br.ReadChars((int)authorNameLength); + UInt32 authorNameLength = br.ReadUInt32(); + char[] authorName = br.ReadChars((int)authorNameLength); - movie.HeaderEntries.Add(HeaderKeys.AUTHOR, new string(authorName)); + movie.HeaderEntries.Add(HeaderKeys.AUTHOR, new string(authorName)); - info.parseSuccessful = true; - return info; - } + info.parseSuccessful = true; + return info; + } - protected void parseBinaryInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { - Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); - SimpleController controllers = new SimpleController(); - settings.Controllers = new[] { info.player1Type, info.player2Type }; - controllers.Type = Octoshock.CreateControllerDefinition(settings); + protected void parseBinaryInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) + { + Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); + SimpleController controllers = new SimpleController(); + settings.Controllers = new[] { info.player1Type, info.player2Type }; + controllers.Type = Octoshock.CreateControllerDefinition(settings); - string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", - "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; + string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", + "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; - bool isCdTrayOpen = false; + bool isCdTrayOpen = false; - for (int frame = 0; frame < info.frameCount; ++frame) { - if (info.player1Type.IsConnected) { - UInt16 controllerState = br.ReadUInt16(); + for (int frame = 0; frame < info.frameCount; ++frame) + { + if (info.player1Type.IsConnected) + { + UInt16 controllerState = br.ReadUInt16(); - // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately - // due to the layout, we handle select separately too first. - controllers["P1 Select"] = (controllerState & 0x1) != 0; + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P1 Select"] = (controllerState & 0x1) != 0; - for (int button = 3; button < buttons.Length; button++) { - controllers["P1 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); - if (((controllerState >> button) & 0x1) != 0 && button > 15) { - continue; - } - } + for (int button = 3; button < buttons.Length; button++) + { + controllers["P1 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); + if (((controllerState >> button) & 0x1) != 0 && button > 15) + { + continue; + } + } - if(info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - controllers["P1 L3"] = (controllerState & 0x2) != 0; - controllers["P1 R3"] = (controllerState & 0x4) != 0; - Tuple leftX = new Tuple("P1 LStick X", (float)br.ReadByte()); - Tuple leftY = new Tuple("P1 LStick Y", (float)br.ReadByte()); - Tuple rightX = new Tuple("P1 RStick X", (float)br.ReadByte()); - Tuple rightY = new Tuple("P1 RStick Y", (float)br.ReadByte()); + if (info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + controllers["P1 L3"] = (controllerState & 0x2) != 0; + controllers["P1 R3"] = (controllerState & 0x4) != 0; + Tuple leftX = new Tuple("P1 LStick X", (float)br.ReadByte()); + Tuple leftY = new Tuple("P1 LStick Y", (float)br.ReadByte()); + Tuple rightX = new Tuple("P1 RStick X", (float)br.ReadByte()); + Tuple rightY = new Tuple("P1 RStick Y", (float)br.ReadByte()); - controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); - } - } + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } - if (info.player2Type.IsConnected) { - UInt16 controllerState = br.ReadUInt16(); - for (int button = 0; button < buttons.Length; button++) { - controllers["P2 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); - if (((controllerState >> button) & 0x1) != 0 && button > 15) { - continue; - } - } + if (info.player2Type.IsConnected) + { + UInt16 controllerState = br.ReadUInt16(); + for (int button = 0; button < buttons.Length; button++) + { + controllers["P2 " + buttons[button]] = (((controllerState >> button) & 0x1) != 0); + if (((controllerState >> button) & 0x1) != 0 && button > 15) + { + continue; + } + } - if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - Tuple leftX = new Tuple("P2 LStick X", (float)br.ReadByte()); - Tuple leftY = new Tuple("P2 LStick Y", (float)br.ReadByte()); - Tuple rightX = new Tuple("P2 RStick X", (float)br.ReadByte()); - Tuple rightY = new Tuple("P2 RStick Y", (float)br.ReadByte()); + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + Tuple leftX = new Tuple("P2 LStick X", (float)br.ReadByte()); + Tuple leftY = new Tuple("P2 LStick Y", (float)br.ReadByte()); + Tuple rightX = new Tuple("P2 RStick X", (float)br.ReadByte()); + Tuple rightY = new Tuple("P2 RStick Y", (float)br.ReadByte()); - controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); - } - } + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } - byte controlState = br.ReadByte(); - controllers["Reset"] = (controlState & 0x02) != 0; - if((controlState & 0x04) != 0) { - if(isCdTrayOpen) { - controllers["Close"] = true; - } else { - controllers["Open"] = true; - } - isCdTrayOpen = !isCdTrayOpen; - } else { - controllers["Close"] = false; - controllers["Open"] = false; - } + byte controlState = br.ReadByte(); + controllers["Reset"] = (controlState & 0x02) != 0; + if ((controlState & 0x04) != 0) + { + if (isCdTrayOpen) + { + controllers["Close"] = true; + } + else + { + controllers["Open"] = true; + } + isCdTrayOpen = !isCdTrayOpen; + } + else + { + controllers["Close"] = false; + controllers["Open"] = false; + } - if((controlState & 0xFC) != 0) { - Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); - } + if ((controlState & 0xFC) != 0) + { + Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); + } - movie.AppendFrame(controllers); - } - } + movie.AppendFrame(controllers); + } + } - protected void parseTextInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) { - Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); - SimpleController controllers = new SimpleController(); - settings.Controllers = new[] { info.player1Type, info.player2Type }; - controllers.Type = Octoshock.CreateControllerDefinition(settings); + protected void parseTextInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info) + { + Octoshock.SyncSettings settings = new Octoshock.SyncSettings(); + SimpleController controllers = new SimpleController(); + settings.Controllers = new[] { info.player1Type, info.player2Type }; + controllers.Type = Octoshock.CreateControllerDefinition(settings); - string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", - "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; + string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", + "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; - bool isCdTrayOpen = false; + bool isCdTrayOpen = false; - for (int frame = 0; frame < info.frameCount; ++frame) { - if (info.player1Type.IsConnected) { - // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately - // due to the layout, we handle select separately too first. - controllers["P1 Select"] = br.ReadChar() != '.'; + for (int frame = 0; frame < info.frameCount; ++frame) + { + if (info.player1Type.IsConnected) + { + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P1 Select"] = br.ReadChar() != '.'; - if(info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - controllers["P1 L3"] = br.ReadChar() != '.'; - controllers["P1 R3"] = br.ReadChar() != '.'; - } + if (info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + controllers["P1 L3"] = br.ReadChar() != '.'; + controllers["P1 R3"] = br.ReadChar() != '.'; + } - for (int button = 3; button < buttons.Length; button++) { - controllers["P1 " + buttons[button]] = br.ReadChar() != '.'; - } + for (int button = 3; button < buttons.Length; button++) + { + controllers["P1 " + buttons[button]] = br.ReadChar() != '.'; + } - if (info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - // The analog controls are encoded as four space-separated numbers with a leading space - string leftXRaw = new string(br.ReadChars(4)).Trim(); - string leftYRaw = new string(br.ReadChars(4)).Trim(); - string rightXRaw = new string(br.ReadChars(4)).Trim(); - string rightYRaw = new string(br.ReadChars(4)).Trim(); + if (info.player1Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + // The analog controls are encoded as four space-separated numbers with a leading space + string leftXRaw = new string(br.ReadChars(4)).Trim(); + string leftYRaw = new string(br.ReadChars(4)).Trim(); + string rightXRaw = new string(br.ReadChars(4)).Trim(); + string rightYRaw = new string(br.ReadChars(4)).Trim(); - Tuple leftX = new Tuple("P1 LStick X", float.Parse(leftXRaw)); - Tuple leftY = new Tuple("P1 LStick Y", float.Parse(leftYRaw)); - Tuple rightX = new Tuple("P1 RStick X", float.Parse(rightXRaw)); - Tuple rightY = new Tuple("P1 RStick Y", float.Parse(rightYRaw)); + Tuple leftX = new Tuple("P1 LStick X", float.Parse(leftXRaw)); + Tuple leftY = new Tuple("P1 LStick Y", float.Parse(leftYRaw)); + Tuple rightX = new Tuple("P1 RStick X", float.Parse(rightXRaw)); + Tuple rightY = new Tuple("P1 RStick Y", float.Parse(rightYRaw)); - controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); - } - } + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } - // Each controller is terminated with a pipeline. - br.ReadChar(); + // Each controller is terminated with a pipeline. + br.ReadChar(); - if (info.player2Type.IsConnected) { - // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately - // due to the layout, we handle select separately too first. - controllers["P2 Select"] = br.ReadChar() != '.'; + if (info.player2Type.IsConnected) + { + // As L3 and R3 don't exist on a standard gamepad, handle them separately later. Unfortunately + // due to the layout, we handle select separately too first. + controllers["P2 Select"] = br.ReadChar() != '.'; - if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - controllers["P2 L3"] = br.ReadChar() != '.'; - controllers["P2 R3"] = br.ReadChar() != '.'; - } + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + controllers["P2 L3"] = br.ReadChar() != '.'; + controllers["P2 R3"] = br.ReadChar() != '.'; + } - for (int button = 3; button < buttons.Length; button++) { - controllers["P2 " + buttons[button]] = br.ReadChar() != '.'; - } + for (int button = 3; button < buttons.Length; button++) + { + controllers["P2 " + buttons[button]] = br.ReadChar() != '.'; + } - if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) { - // The analog controls are encoded as four space-separated numbers with a leading space - string leftXRaw = new string(br.ReadChars(4)).Trim(); - string leftYRaw = new string(br.ReadChars(4)).Trim(); - string rightXRaw = new string(br.ReadChars(4)).Trim(); - string rightYRaw = new string(br.ReadChars(4)).Trim(); + if (info.player2Type.Type != Octoshock.ControllerSetting.ControllerType.Gamepad) + { + // The analog controls are encoded as four space-separated numbers with a leading space + string leftXRaw = new string(br.ReadChars(4)).Trim(); + string leftYRaw = new string(br.ReadChars(4)).Trim(); + string rightXRaw = new string(br.ReadChars(4)).Trim(); + string rightYRaw = new string(br.ReadChars(4)).Trim(); - Tuple leftX = new Tuple("P2 LStick X", float.Parse(leftXRaw)); - Tuple leftY = new Tuple("P2 LStick Y", float.Parse(leftYRaw)); - Tuple rightX = new Tuple("P2 RStick X", float.Parse(rightXRaw)); - Tuple rightY = new Tuple("P2 RStick Y", float.Parse(rightYRaw)); + Tuple leftX = new Tuple("P2 LStick X", float.Parse(leftXRaw)); + Tuple leftY = new Tuple("P2 LStick Y", float.Parse(leftYRaw)); + Tuple rightX = new Tuple("P2 RStick X", float.Parse(rightXRaw)); + Tuple rightY = new Tuple("P2 RStick Y", float.Parse(rightYRaw)); - controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); - } - } + controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY }); + } + } - // Each controller is terminated with a pipeline. - br.ReadChar(); + // Each controller is terminated with a pipeline. + br.ReadChar(); - byte controlState = br.ReadByte(); - controllers["Reset"] = (controlState & 0x02) != 0; - if ((controlState & 0x04) != 0) { - if (isCdTrayOpen) { - controllers["Close"] = true; - } else { - controllers["Open"] = true; - } - isCdTrayOpen = !isCdTrayOpen; - } else { - controllers["Close"] = false; - controllers["Open"] = false; - } + byte controlState = br.ReadByte(); + controllers["Reset"] = (controlState & 0x02) != 0; + if ((controlState & 0x04) != 0) + { + if (isCdTrayOpen) + { + controllers["Close"] = true; + } + else + { + controllers["Open"] = true; + } + isCdTrayOpen = !isCdTrayOpen; + } + else + { + controllers["Close"] = false; + controllers["Open"] = false; + } - if ((controlState & 0xFC) != 0) { - Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); - } + if ((controlState & 0xFC) != 0) + { + Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); + } - // Each controller is terminated with a pipeline. - br.ReadChar(); + // Each controller is terminated with a pipeline. + br.ReadChar(); - movie.AppendFrame(controllers); - } - } + movie.AppendFrame(controllers); + } + } - protected class MiscHeaderInfo { - public bool binaryFormat = true; - public UInt32 controllerDataOffset; - public UInt32 frameCount; - public Octoshock.ControllerSetting player1Type = new Octoshock.ControllerSetting() { IsConnected = true }; - public Octoshock.ControllerSetting player2Type = new Octoshock.ControllerSetting() { IsConnected = true }; + protected class MiscHeaderInfo + { + public bool binaryFormat = true; + public UInt32 controllerDataOffset; + public UInt32 frameCount; + public Octoshock.ControllerSetting player1Type = new Octoshock.ControllerSetting() { IsConnected = true }; + public Octoshock.ControllerSetting player2Type = new Octoshock.ControllerSetting() { IsConnected = true }; - public bool parseSuccessful = false; - } + public bool parseSuccessful = false; + } - } + } } diff --git a/BizHawk.Client.Common/movie/import/PXMImport.cs b/BizHawk.Client.Common/movie/import/PXMImport.cs index 61cebf85e8..850c2bd206 100644 --- a/BizHawk.Client.Common/movie/import/PXMImport.cs +++ b/BizHawk.Client.Common/movie/import/PXMImport.cs @@ -1,36 +1,44 @@ using System.IO; -namespace BizHawk.Client.Common.movie.import { +namespace BizHawk.Client.Common.movie.import +{ - // PXM files are directly compatible with binary-format PJM files, with the only - // difference being fewer flags implemented in the header, hence just calling the - // base class methods via a subclass. - // - // However, the magic number/file signature is slightly different, requiring some - // refactoring to avoid PXM-specific code in the PJMImport class. - [ImportExtension(".pxm")] - class PXMImport : PJMImport { - protected override void RunImport() { - Bk2Movie movie = Result.Movie; - MiscHeaderInfo info; + // PXM files are directly compatible with binary-format PJM files, with the only + // difference being fewer flags implemented in the header, hence just calling the + // base class methods via a subclass. + // + // However, the magic number/file signature is slightly different, requiring some + // refactoring to avoid PXM-specific code in the PJMImport class. + [ImportExtension(".pxm")] + class PXMImport : PJMImport + { + protected override void RunImport() + { + Bk2Movie movie = Result.Movie; + MiscHeaderInfo info; - movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); - using (var fs = SourceFile.OpenRead()) { - using (var br = new BinaryReader(fs)) { - info = parseHeader(movie, "PXM ", br); + using (var fs = SourceFile.OpenRead()) + { + using (var br = new BinaryReader(fs)) + { + info = parseHeader(movie, "PXM ", br); - fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); + fs.Seek(info.controllerDataOffset, SeekOrigin.Begin); - if (info.binaryFormat) { - parseBinaryInputLog(br, movie, info); - } else { - parseTextInputLog(br, movie, info); - } - } - } + if (info.binaryFormat) + { + parseBinaryInputLog(br, movie, info); + } + else + { + parseTextInputLog(br, movie, info); + } + } + } - movie.Save(); - } - } + movie.Save(); + } + } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs index fd6684b904..afe5119b3d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs @@ -36,77 +36,81 @@ namespace BizHawk.Emulation.Cores.Sony.PSX { public string SystemId { get { return "PSX"; } } - public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings) { - ControllerDefinition definition = new ControllerDefinition(); - definition.Name = syncSettings.Controllers.All(c => c.Type == ControllerSetting.ControllerType.Gamepad) - ? "PSX Gamepad Controller" - : "PSX DualShock Controller"; // Meh, more nuanced logic doesn't really work with a simple property + public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings) + { + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = syncSettings.Controllers.All(c => c.Type == ControllerSetting.ControllerType.Gamepad) + ? "PSX Gamepad Controller" + : "PSX DualShock Controller"; // Meh, more nuanced logic doesn't really work with a simple property - definition.BoolButtons.Clear(); - definition.FloatControls.Clear(); + definition.BoolButtons.Clear(); + definition.FloatControls.Clear(); - for (int i = 0; i < syncSettings.Controllers.Length; i++) { - if (syncSettings.Controllers[i].IsConnected) { - definition.BoolButtons.AddRange(new[] - { - "P" + (i + 1) + " Up", - "P" + (i + 1) + " Down", - "P" + (i + 1) + " Left", - "P" + (i + 1) + " Right", - "P" + (i + 1) + " Select", - "P" + (i + 1) + " Start", - "P" + (i + 1) + " Square", - "P" + (i + 1) + " Triangle", - "P" + (i + 1) + " Circle", - "P" + (i + 1) + " Cross", - "P" + (i + 1) + " L1", - "P" + (i + 1) + " R1", - "P" + (i + 1) + " L2", - "P" + (i + 1) + " R2", - }); + for (int i = 0; i < syncSettings.Controllers.Length; i++) + { + if (syncSettings.Controllers[i].IsConnected) + { + definition.BoolButtons.AddRange(new[] + { + "P" + (i + 1) + " Up", + "P" + (i + 1) + " Down", + "P" + (i + 1) + " Left", + "P" + (i + 1) + " Right", + "P" + (i + 1) + " Select", + "P" + (i + 1) + " Start", + "P" + (i + 1) + " Square", + "P" + (i + 1) + " Triangle", + "P" + (i + 1) + " Circle", + "P" + (i + 1) + " Cross", + "P" + (i + 1) + " L1", + "P" + (i + 1) + " R1", + "P" + (i + 1) + " L2", + "P" + (i + 1) + " R2", + }); - if (syncSettings.Controllers[i].Type != ControllerSetting.ControllerType.Gamepad) { - definition.BoolButtons.Add("P" + (i + 1) + " L3"); - definition.BoolButtons.Add("P" + (i + 1) + " R3"); - definition.BoolButtons.Add("P" + (i + 1) + " MODE"); + if (syncSettings.Controllers[i].Type != ControllerSetting.ControllerType.Gamepad) + { + definition.BoolButtons.Add("P" + (i + 1) + " L3"); + definition.BoolButtons.Add("P" + (i + 1) + " R3"); + definition.BoolButtons.Add("P" + (i + 1) + " MODE"); - definition.FloatControls.AddRange(new[] - { - "P" + (i + 1) + " LStick X", - "P" + (i + 1) + " LStick Y", - "P" + (i + 1) + " RStick X", - "P" + (i + 1) + " RStick Y" - }); + definition.FloatControls.AddRange(new[] + { + "P" + (i + 1) + " LStick X", + "P" + (i + 1) + " LStick Y", + "P" + (i + 1) + " RStick X", + "P" + (i + 1) + " RStick Y" + }); - definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); - definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); - definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); - definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); - } - } - } + definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); + definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); + definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); + definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); + } + } + } - definition.BoolButtons.AddRange(new[] - { - "Open", - "Close", - "Reset" - }); + definition.BoolButtons.AddRange(new[] + { + "Open", + "Close", + "Reset" + }); - definition.FloatControls.Add("Disc Select"); + definition.FloatControls.Add("Disc Select"); - definition.FloatRanges.Add( - //new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set) - //hmm.. I don't see why this wouldn't work - new[] { 0f, 1f, 1f } - ); + definition.FloatRanges.Add( + //new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set) + //hmm.. I don't see why this wouldn't work + new[] { 0f, 1f, 1f } + ); - return definition; - } + return definition; + } private void SetControllerButtons() { - ControllerDefinition = CreateControllerDefinition(_SyncSettings); + ControllerDefinition = CreateControllerDefinition(_SyncSettings); } public string BoardName { get { return null; } } From d94acff2955a1350274d341c8435185a5d4cfe14 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 01:10:22 +0100 Subject: [PATCH 06/10] Add PJM and PXM filters to the import movie dialog --- BizHawk.Client.EmuHawk/MainForm.Events.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 2d68d2746c..01663dc71f 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -455,7 +455,7 @@ namespace BizHawk.Client.EmuHawk InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId), Multiselect = true, Filter = FormatFilter( - "Movie Files", "*.fm2;*.mc2;*.mcm;*.mmv;*.gmv;*.vbm;*.lsmv;*.fcm;*.fmv;*.vmv;*.nmv;*.smv;*.ymv;*.zmv;*.bkm", + "Movie Files", "*.fm2;*.mc2;*.mcm;*.mmv;*.gmv;*.vbm;*.lsmv;*.fcm;*.fmv;*.vmv;*.nmv;*.smv;*.ymv;*.zmv;*.bkm;*.pjm;*.pxm", "FCEUX", "*.fm2", "PCEjin/Mednafen", "*.mc2;*.mcm", "Dega", "*.mmv", @@ -469,6 +469,8 @@ namespace BizHawk.Client.EmuHawk "Snes9x", "*.smv", "Yabause", "*.ymv", "ZSNES", "*.zmv", + "PSXjin", "*.pjm", + "PCSX", "*.pxm", "BizHawk Bkm", "*.bkm", "All Files", "*.*"), RestoreDirectory = false From 7e2d4a75b4e0168f6ddf927a318138b58c85b08f Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 22:46:59 +0100 Subject: [PATCH 07/10] Use overloaded this[] rather than .Add on header import --- BizHawk.Client.Common/movie/import/PJMImport.cs | 6 +++--- BizHawk.Client.Common/movie/import/PXMImport.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index 045ccdaf1b..5a5874fffd 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -13,7 +13,7 @@ namespace BizHawk.Client.Common Bk2Movie movie = Result.Movie; MiscHeaderInfo info; - movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + movie.HeaderEntries[HeaderKeys.PLATFORM] = "PSX"; using (var fs = SourceFile.OpenRead()) { @@ -65,7 +65,7 @@ namespace BizHawk.Client.Common } if ((flags & 0x04) != 0) { - movie.HeaderEntries.Add(HeaderKeys.PAL, "1"); + movie.HeaderEntries[HeaderKeys.PAL] = "1"; } if ((flags & 0x08) != 0) { @@ -147,7 +147,7 @@ namespace BizHawk.Client.Common UInt32 authorNameLength = br.ReadUInt32(); char[] authorName = br.ReadChars((int)authorNameLength); - movie.HeaderEntries.Add(HeaderKeys.AUTHOR, new string(authorName)); + movie.HeaderEntries[HeaderKeys.AUTHOR] = new string(authorName); info.parseSuccessful = true; return info; diff --git a/BizHawk.Client.Common/movie/import/PXMImport.cs b/BizHawk.Client.Common/movie/import/PXMImport.cs index 850c2bd206..a4ef2ebdd3 100644 --- a/BizHawk.Client.Common/movie/import/PXMImport.cs +++ b/BizHawk.Client.Common/movie/import/PXMImport.cs @@ -17,7 +17,7 @@ namespace BizHawk.Client.Common.movie.import Bk2Movie movie = Result.Movie; MiscHeaderInfo info; - movie.HeaderEntries.Add(HeaderKeys.PLATFORM, "PSX"); + movie.HeaderEntries[HeaderKeys.PLATFORM] = "PSX"; using (var fs = SourceFile.OpenRead()) { From d153c00c7781846b14b5eb9c772ca794c0f7fd03 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 22:47:23 +0100 Subject: [PATCH 08/10] Add controller types to sync settings --- BizHawk.Client.Common/movie/import/PJMImport.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index 5a5874fffd..772bcc9a81 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -1,5 +1,5 @@ using BizHawk.Emulation.Cores.Sony.PSX; - +using Newtonsoft.Json; using System; using System.IO; @@ -127,6 +127,15 @@ namespace BizHawk.Client.Common return info; } + Octoshock.SyncSettings syncsettings = new Octoshock.SyncSettings(); + syncsettings.Controllers = new[] { info.player1Type, info.player2Type }; + + var jsonSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }; + movie.SyncSettingsJson = JsonConvert.SerializeObject(new { o = (object)syncsettings }, jsonSettings); + info.frameCount = br.ReadUInt32(); UInt32 rerecordCount = br.ReadUInt32(); movie.HeaderEntries[HeaderKeys.RERECORDS] = rerecordCount.ToString(); @@ -164,6 +173,7 @@ namespace BizHawk.Client.Common "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; bool isCdTrayOpen = false; + int cdNumber = 1; for (int frame = 0; frame < info.frameCount; ++frame) { @@ -227,6 +237,7 @@ namespace BizHawk.Client.Common if (isCdTrayOpen) { controllers["Close"] = true; + cdNumber++; } else { @@ -240,6 +251,9 @@ namespace BizHawk.Client.Common controllers["Open"] = false; } + Tuple discSelect = new Tuple("Disc Select", cdNumber); + controllers.AcceptNewFloats(new[] { discSelect }); + if ((controlState & 0xFC) != 0) { Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString()); From 33c3d8b2fc6f61c0dd5a392782271e0b521bb6da Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Tue, 22 Sep 2015 23:28:33 +0100 Subject: [PATCH 09/10] Fix LINQ brainfart Also tweak code alignment to use spaces rather than tabs for alignment that doesn't affect indentation level. Where(...).Count() > 0 ===> Any(...) --- BizHawk.Client.Common/movie/import/MovieImport.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs index 794f30b8c5..94059338f6 100644 --- a/BizHawk.Client.Common/movie/import/MovieImport.cs +++ b/BizHawk.Client.Common/movie/import/MovieImport.cs @@ -113,9 +113,9 @@ namespace BizHawk.Client.Common { var info = typeof(MovieImport).Module; var importers = from t in info.GetTypes() - where typeof(IMovieImport).IsAssignableFrom(t) - && TypeImportsExtension(t, ext) - select t; + where typeof(IMovieImport).IsAssignableFrom(t) + && TypeImportsExtension(t, ext) + select t; return importers; } @@ -124,7 +124,7 @@ namespace BizHawk.Client.Common { var attrs = (ImportExtension[])t.GetCustomAttributes(typeof(ImportExtension), inherit: false); - if (attrs.Where(a => a.Extension.ToUpper() == ext.ToUpper()).Count() > 0) + if (attrs.Any(a => a.Extension.ToUpper() == ext.ToUpper())) { return true; } From 217f425f094a3b9f78e57dd9dc47480595615058 Mon Sep 17 00:00:00 2001 From: Iris Ward Date: Wed, 23 Sep 2015 15:38:52 +0100 Subject: [PATCH 10/10] Propagate cdNumber change Include the cdNumber counting change in the text flavour PJM code. --- BizHawk.Client.Common/movie/import/PJMImport.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/BizHawk.Client.Common/movie/import/PJMImport.cs b/BizHawk.Client.Common/movie/import/PJMImport.cs index 772bcc9a81..63b46668a5 100644 --- a/BizHawk.Client.Common/movie/import/PJMImport.cs +++ b/BizHawk.Client.Common/movie/import/PJMImport.cs @@ -130,6 +130,10 @@ namespace BizHawk.Client.Common Octoshock.SyncSettings syncsettings = new Octoshock.SyncSettings(); syncsettings.Controllers = new[] { info.player1Type, info.player2Type }; + // Annoying kludge to force the json serializer to serialize the type name for "o" object. + // For just the "o" object to have type information, it must be cast to a superclass such + // that the TypeNameHandling.Auto decides to serialize the type as well as the object + // contents. As such, the object cast is NOT redundant var jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto @@ -271,9 +275,10 @@ namespace BizHawk.Client.Common controllers.Type = Octoshock.CreateControllerDefinition(settings); string[] buttons = { "Select", "L3", "R3", "Start", "Up", "Right", "Down", "Left", - "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; + "L2", "R2", "L1", "R1", "Triangle", "Circle", "Cross", "Square"}; bool isCdTrayOpen = false; + int cdNumber = 1; for (int frame = 0; frame < info.frameCount; ++frame) { @@ -360,6 +365,7 @@ namespace BizHawk.Client.Common if (isCdTrayOpen) { controllers["Close"] = true; + cdNumber++; } else { @@ -373,6 +379,9 @@ namespace BizHawk.Client.Common controllers["Open"] = false; } + Tuple discSelect = new Tuple("Disc Select", cdNumber); + controllers.AcceptNewFloats(new[] { discSelect }); + if ((controlState & 0xFC) != 0) { Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString());