merge PR #503 from adituv - Movies: Reflection-based dispatching; PJM/PXM imports
This commit is contained in:
commit
4b8c7d77ac
|
@ -158,6 +158,7 @@
|
||||||
<Compile Include="movie\bk2\Bk2Movie.HeaderApi.cs">
|
<Compile Include="movie\bk2\Bk2Movie.HeaderApi.cs">
|
||||||
<DependentUpon>Bk2Movie.cs</DependentUpon>
|
<DependentUpon>Bk2Movie.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="movie\import\PXMImport.cs" />
|
||||||
<Compile Include="movie\tasproj\StateManagerState.cs" />
|
<Compile Include="movie\tasproj\StateManagerState.cs" />
|
||||||
<Compile Include="movie\tasproj\TasBranch.cs" />
|
<Compile Include="movie\tasproj\TasBranch.cs" />
|
||||||
<Compile Include="movie\tasproj\TasMovie.History.cs" />
|
<Compile Include="movie\tasproj\TasMovie.History.cs" />
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Common.BufferExtensions;
|
using BizHawk.Common.BufferExtensions;
|
||||||
|
@ -73,23 +74,72 @@ namespace BizHawk.Client.Common
|
||||||
warningMsg = string.Empty;
|
warningMsg = string.Empty;
|
||||||
string ext = path != null ? Path.GetExtension(path).ToUpper() : 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 (UsesLegacyImporter(ext))
|
||||||
//if (ext == ".FM2")
|
|
||||||
//{
|
|
||||||
// var result = new Fm2Import().Import(path);
|
|
||||||
// errorMsg = result.Errors.First();
|
|
||||||
// warningMsg = result.Errors.First();
|
|
||||||
// return result.Movie;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (ext == ".PJM")
|
|
||||||
{
|
{
|
||||||
var result = new PJMImport().Import(path);
|
return LegacyImportFile(ext, path, out errorMsg, out warningMsg).ToBk2();
|
||||||
errorMsg = result.Errors.First();
|
|
||||||
warningMsg = result.Errors.First();
|
|
||||||
return result.Movie;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Type> 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.Any(a => a.Extension.ToUpper() == ext.ToUpper()))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
BkmMovie m = new BkmMovie();
|
BkmMovie m = new BkmMovie();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -149,16 +199,23 @@ namespace BizHawk.Client.Common
|
||||||
errorMsg = except.ToString();
|
errorMsg = except.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack
|
return m;
|
||||||
return m.ToBk2();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return whether or not the type of file provided can currently be imported.
|
// Return whether or not the type of file provided can currently be imported.
|
||||||
public static bool IsValidMovieExtension(string extension)
|
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 =
|
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);
|
return extensions.Any(ext => extension.ToUpper() == "." + ext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
using System;
|
using BizHawk.Emulation.Cores.Sony.PSX;
|
||||||
using System.Collections.Generic;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
|
@ -11,7 +10,418 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
protected override void RunImport()
|
protected override void RunImport()
|
||||||
{
|
{
|
||||||
// TODO
|
Bk2Movie movie = Result.Movie;
|
||||||
|
MiscHeaderInfo info;
|
||||||
|
|
||||||
|
movie.HeaderEntries[HeaderKeys.PLATFORM] = "PSX";
|
||||||
|
|
||||||
|
using (var fs = SourceFile.OpenRead())
|
||||||
|
{
|
||||||
|
using (var br = new BinaryReader(fs))
|
||||||
|
{
|
||||||
|
info = parseHeader(movie, "PJM ", br);
|
||||||
|
|
||||||
|
fs.Seek(info.controllerDataOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
if (info.binaryFormat)
|
||||||
|
{
|
||||||
|
parseBinaryInputLog(br, movie, info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parseTextInputLog(br, movie, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
movie.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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[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 = OctoshockDll.ePeripheralType.None;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
info.player1Type = OctoshockDll.ePeripheralType.Pad;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
info.player1Type = OctoshockDll.ePeripheralType.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.player1Type = OctoshockDll.ePeripheralType.None;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
info.player1Type = OctoshockDll.ePeripheralType.Pad;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
info.player1Type = OctoshockDll.ePeripheralType.DualShock;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Result.Errors.Add("Movie has unrecognised controller type for Player 2.");
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Octoshock.SyncSettings syncsettings = new Octoshock.SyncSettings();
|
||||||
|
syncsettings.FIOConfig.Devices8 =
|
||||||
|
new[] {
|
||||||
|
info.player1Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,
|
||||||
|
info.player2Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
movie.SyncSettingsJson = JsonConvert.SerializeObject(new { o = (object)syncsettings }, jsonSettings);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
info.controllerDataOffset = br.ReadUInt32();
|
||||||
|
|
||||||
|
UInt32 authorNameLength = br.ReadUInt32();
|
||||||
|
char[] authorName = br.ReadChars((int)authorNameLength);
|
||||||
|
|
||||||
|
movie.HeaderEntries[HeaderKeys.AUTHOR] = new string(authorName);
|
||||||
|
|
||||||
|
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.FIOConfig.Devices8 =
|
||||||
|
new[] {
|
||||||
|
info.player1Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,
|
||||||
|
info.player2Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
int cdNumber = 1;
|
||||||
|
|
||||||
|
for (int frame = 0; frame < info.frameCount; ++frame)
|
||||||
|
{
|
||||||
|
if (info.player1Type != OctoshockDll.ePeripheralType.None)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
controllers["P1 L3"] = (controllerState & 0x2) != 0;
|
||||||
|
controllers["P1 R3"] = (controllerState & 0x4) != 0;
|
||||||
|
Tuple<string, float> leftX = new Tuple<string, float>("P1 LStick X", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> leftY = new Tuple<string, float>("P1 LStick Y", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> rightX = new Tuple<string, float>("P1 RStick X", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> rightY = new Tuple<string, float>("P1 RStick Y", (float)br.ReadByte());
|
||||||
|
|
||||||
|
controllers.AcceptNewFloats(new[] { leftX, leftY, rightX, rightY });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.player2Type != OctoshockDll.ePeripheralType.None)
|
||||||
|
{
|
||||||
|
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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
Tuple<string, float> leftX = new Tuple<string, float>("P2 LStick X", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> leftY = new Tuple<string, float>("P2 LStick Y", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> rightX = new Tuple<string, float>("P2 RStick X", (float)br.ReadByte());
|
||||||
|
Tuple<string, float> rightY = new Tuple<string, float>("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;
|
||||||
|
cdNumber++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controllers["Open"] = true;
|
||||||
|
}
|
||||||
|
isCdTrayOpen = !isCdTrayOpen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controllers["Close"] = false;
|
||||||
|
controllers["Open"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<string, float> discSelect = new Tuple<string, float>("Disc Select", cdNumber);
|
||||||
|
controllers.AcceptNewFloats(new[] { discSelect });
|
||||||
|
|
||||||
|
if ((controlState & 0xFC) != 0)
|
||||||
|
{
|
||||||
|
Result.Warnings.Add("Ignored toggle hack flag on frame " + frame.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
movie.AppendFrame(controllers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseTextInputLog(BinaryReader br, Bk2Movie movie, MiscHeaderInfo info)
|
||||||
|
{
|
||||||
|
Octoshock.SyncSettings settings = new Octoshock.SyncSettings();
|
||||||
|
SimpleController controllers = new SimpleController();
|
||||||
|
settings.FIOConfig.Devices8 =
|
||||||
|
new[] {
|
||||||
|
info.player1Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,
|
||||||
|
info.player2Type,
|
||||||
|
OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None,OctoshockDll.ePeripheralType.None
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
int cdNumber = 1;
|
||||||
|
|
||||||
|
for (int frame = 0; frame < info.frameCount; ++frame)
|
||||||
|
{
|
||||||
|
if (info.player1Type != OctoshockDll.ePeripheralType.None)
|
||||||
|
{
|
||||||
|
// 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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
// 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<string, float> leftX = new Tuple<string, float>("P1 LStick X", float.Parse(leftXRaw));
|
||||||
|
Tuple<string, float> leftY = new Tuple<string, float>("P1 LStick Y", float.Parse(leftYRaw));
|
||||||
|
Tuple<string, float> rightX = new Tuple<string, float>("P1 RStick X", float.Parse(rightXRaw));
|
||||||
|
Tuple<string, float> rightY = new Tuple<string, float>("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 != OctoshockDll.ePeripheralType.None)
|
||||||
|
{
|
||||||
|
// 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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
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 == OctoshockDll.ePeripheralType.DualShock)
|
||||||
|
{
|
||||||
|
// 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<string, float> leftX = new Tuple<string, float>("P2 LStick X", float.Parse(leftXRaw));
|
||||||
|
Tuple<string, float> leftY = new Tuple<string, float>("P2 LStick Y", float.Parse(leftYRaw));
|
||||||
|
Tuple<string, float> rightX = new Tuple<string, float>("P2 RStick X", float.Parse(rightXRaw));
|
||||||
|
Tuple<string, float> rightY = new Tuple<string, float>("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;
|
||||||
|
cdNumber++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controllers["Open"] = true;
|
||||||
|
}
|
||||||
|
isCdTrayOpen = !isCdTrayOpen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controllers["Close"] = false;
|
||||||
|
controllers["Open"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple<string, float> discSelect = new Tuple<string, float>("Disc Select", cdNumber);
|
||||||
|
controllers.AcceptNewFloats(new[] { discSelect });
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class MiscHeaderInfo
|
||||||
|
{
|
||||||
|
public bool binaryFormat = true;
|
||||||
|
public UInt32 controllerDataOffset;
|
||||||
|
public UInt32 frameCount;
|
||||||
|
public OctoshockDll.ePeripheralType player1Type;
|
||||||
|
public OctoshockDll.ePeripheralType player2Type;
|
||||||
|
|
||||||
|
public bool parseSuccessful = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
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[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -455,7 +455,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId),
|
InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId),
|
||||||
Multiselect = true,
|
Multiselect = true,
|
||||||
Filter = FormatFilter(
|
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",
|
"FCEUX", "*.fm2",
|
||||||
"PCEjin/Mednafen", "*.mc2;*.mcm",
|
"PCEjin/Mednafen", "*.mc2;*.mcm",
|
||||||
"Dega", "*.mmv",
|
"Dega", "*.mmv",
|
||||||
|
@ -469,6 +469,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
"Snes9x", "*.smv",
|
"Snes9x", "*.smv",
|
||||||
"Yabause", "*.ymv",
|
"Yabause", "*.ymv",
|
||||||
"ZSNES", "*.zmv",
|
"ZSNES", "*.zmv",
|
||||||
|
"PSXjin", "*.pjm",
|
||||||
|
"PCSX", "*.pxm",
|
||||||
"BizHawk Bkm", "*.bkm",
|
"BizHawk Bkm", "*.bkm",
|
||||||
"All Files", "*.*"),
|
"All Files", "*.*"),
|
||||||
RestoreDirectory = false
|
RestoreDirectory = false
|
||||||
|
|
|
@ -36,21 +36,18 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
||||||
{
|
{
|
||||||
public string SystemId { get { return "PSX"; } }
|
public string SystemId { get { return "PSX"; } }
|
||||||
|
|
||||||
private void SetControllerButtons()
|
public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings)
|
||||||
{
|
{
|
||||||
ControllerDefinition = new ControllerDefinition();
|
ControllerDefinition definition = new ControllerDefinition();
|
||||||
ControllerDefinition.Name = "PSX DualShock Controller"; // <-- for compatibility
|
definition.Name = "PSX DualShock Controller"; // <-- for compatibility
|
||||||
//ControllerDefinition.Name = "PSX FrontIO"; // TODO - later rename to this, I guess, so it's less misleading. don't want to wreck keybindings yet.
|
//ControllerDefinition.Name = "PSX FrontIO"; // TODO - later rename to this, I guess, so it's less misleading. don't want to wreck keybindings yet.
|
||||||
|
|
||||||
ControllerDefinition.BoolButtons.Clear();
|
var cfg = syncSettings.FIOConfig.ToLogical();
|
||||||
ControllerDefinition.FloatControls.Clear();
|
|
||||||
|
|
||||||
var cfg = _SyncSettings.FIOConfig.ToLogical();
|
|
||||||
|
|
||||||
for (int i = 0; i < cfg.NumPlayers; i++)
|
for (int i = 0; i < cfg.NumPlayers; i++)
|
||||||
{
|
{
|
||||||
int pnum = i + 1;
|
int pnum = i + 1;
|
||||||
ControllerDefinition.BoolButtons.AddRange(new[]
|
definition.BoolButtons.AddRange(new[]
|
||||||
{
|
{
|
||||||
"P" + pnum + " Up",
|
"P" + pnum + " Up",
|
||||||
"P" + pnum + " Down",
|
"P" + pnum + " Down",
|
||||||
|
@ -72,11 +69,11 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
||||||
|
|
||||||
if (type == OctoshockDll.ePeripheralType.DualShock || type == OctoshockDll.ePeripheralType.DualAnalog)
|
if (type == OctoshockDll.ePeripheralType.DualShock || type == OctoshockDll.ePeripheralType.DualAnalog)
|
||||||
{
|
{
|
||||||
ControllerDefinition.BoolButtons.Add("P" + pnum + " L3");
|
definition.BoolButtons.Add("P" + pnum + " L3");
|
||||||
ControllerDefinition.BoolButtons.Add("P" + pnum + " R3");
|
definition.BoolButtons.Add("P" + pnum + " R3");
|
||||||
ControllerDefinition.BoolButtons.Add("P" + pnum + " MODE");
|
definition.BoolButtons.Add("P" + pnum + " MODE");
|
||||||
|
|
||||||
ControllerDefinition.FloatControls.AddRange(new[]
|
definition.FloatControls.AddRange(new[]
|
||||||
{
|
{
|
||||||
"P" + pnum + " LStick X",
|
"P" + pnum + " LStick X",
|
||||||
"P" + pnum + " LStick Y",
|
"P" + pnum + " LStick Y",
|
||||||
|
@ -84,27 +81,34 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
||||||
"P" + pnum + " RStick Y"
|
"P" + pnum + " RStick Y"
|
||||||
});
|
});
|
||||||
|
|
||||||
ControllerDefinition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
|
definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
|
||||||
ControllerDefinition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
|
definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
|
||||||
ControllerDefinition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
|
definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
|
||||||
ControllerDefinition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
|
definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerDefinition.BoolButtons.AddRange(new[]
|
definition.BoolButtons.AddRange(new[]
|
||||||
{
|
{
|
||||||
"Open",
|
"Open",
|
||||||
"Close",
|
"Close",
|
||||||
"Reset"
|
"Reset"
|
||||||
});
|
});
|
||||||
|
|
||||||
ControllerDefinition.FloatControls.Add("Disc Select");
|
definition.FloatControls.Add("Disc Select");
|
||||||
|
|
||||||
ControllerDefinition.FloatRanges.Add(
|
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)
|
//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
|
//hmm.. I don't see why this wouldn't work
|
||||||
new[] { 0f, 1f, 1f }
|
new[] { 0f, 1f, 1f }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetControllerButtons()
|
||||||
|
{
|
||||||
|
ControllerDefinition = CreateControllerDefinition(_SyncSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BoardName { get { return null; } }
|
public string BoardName { get { return null; } }
|
||||||
|
|
Loading…
Reference in New Issue