proof of concept syncless recorder AV out module

This commit is contained in:
zeromus 2014-06-18 02:28:07 +00:00
parent 6133164a7f
commit 176c306439
16 changed files with 577 additions and 37 deletions

View File

@ -16,6 +16,8 @@ namespace BizHawk.Client.EmuHawk
AviWriterSegment currSegment;
IEnumerator<string> nameProvider;
public void SetFrame(int frame) { }
bool IsOpen { get { return nameProvider != null; } }
public void Dispose()

View File

@ -58,6 +58,8 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
string ext;
public void SetFrame(int frame) { }
public void OpenFile(string baseName)
{
this.baseName = System.IO.Path.Combine(

View File

@ -107,6 +107,7 @@ namespace BizHawk.Client.EmuHawk
int fpsnum = 1, fpsden = 1;
public void SetFrame(int frame) { }
public void OpenFile(string baseName)
{

View File

@ -32,6 +32,11 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
void CloseFile();
/// <summary>
/// tells which emulation frame we're on. Happens before AddFrame() or AddSamples()
/// </summary>
void SetFrame(int frame);
/// <summary>
/// adds a frame to the stream
/// </summary>
@ -111,7 +116,8 @@ namespace BizHawk.Client.EmuHawk
new WavWriterV(),
new FFmpegWriter(),
new NutWriter(),
new GifWriter()
new GifWriter(),
new SynclessRecorder()
};
return ret;
}

View File

@ -804,5 +804,7 @@ namespace BizHawk.Client.EmuHawk
{
return "jmd";
}
public void SetFrame(int frame) { }
}
}

View File

@ -23,6 +23,8 @@ namespace BizHawk.Client.EmuHawk
}
}
public void SetFrame(int frame) { }
public void SetVideoCodecToken(IDisposable token)
{
// ignored

View File

@ -0,0 +1,151 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BizHawk.Emulation;
using BizHawk.Emulation.Common;
using BizHawk.Bizware.BizwareGL;
namespace BizHawk.Client.EmuHawk
{
public class SynclessRecorder : IVideoWriter
{
public void Dispose() { }
public void SetVideoCodecToken(IDisposable token) { }
public void SetDefaultVideoCodecToken() { }
public void SetFrame(int frame)
{
mCurrFrame = frame;
}
int mCurrFrame;
string mBaseDirectory, mFramesDirectory;
string mProjectFile;
public void OpenFile(string projFile)
{
mProjectFile = projFile;
mBaseDirectory = Path.GetDirectoryName(mProjectFile);
string basename = Path.GetFileNameWithoutExtension(projFile);
string framesDirFragment = basename + "_frames";
mFramesDirectory = Path.Combine(mBaseDirectory, framesDirFragment);
StringBuilder sb = new StringBuilder();
sb.AppendLine("version=1");
sb.AppendLine("framesdir=" + framesDirFragment);
File.WriteAllText(mProjectFile, sb.ToString());
}
public void CloseFile() { }
public void AddFrame(IVideoProvider source)
{
using (var bb = new BitmapBuffer(source.BufferWidth, source.BufferHeight, source.GetVideoBuffer()))
{
string subpath = GetAndCreatePathForFrameNum(mCurrFrame);
string path = subpath + ".png";
bb.ToSysdrawingBitmap().Save(path, System.Drawing.Imaging.ImageFormat.Png);
}
}
public void AddSamples(short[] samples)
{
string subpath = GetAndCreatePathForFrameNum(mCurrFrame);
string path = subpath + ".wav";
WavWriterV wwv = new WavWriterV();
wwv.SetAudioParameters(paramSampleRate, paramChannels, paramBits);
wwv.OpenFile(path);
wwv.AddSamples(samples);
wwv.CloseFile();
wwv.Dispose();
}
class DummyDisposable : IDisposable { public void Dispose() { } }
public IDisposable AcquireVideoCodecToken(System.Windows.Forms.IWin32Window hwnd) { return new DummyDisposable(); }
public void SetMovieParameters(int fpsnum, int fpsden)
{
//should probably todo in here
}
public void SetVideoParameters(int width, int height)
{
//may want to todo
}
int paramSampleRate, paramChannels, paramBits;
public void SetAudioParameters(int sampleRate, int channels, int bits)
{
paramSampleRate = sampleRate;
paramChannels = channels;
paramBits = bits;
}
public void SetMetaData(string gameName, string authors, UInt64 lengthMS, UInt64 rerecords)
{
//not needed
}
public override string ToString()
{
return "Syncless Recording";
}
public string WriterDescription()
{
return "Writes each frame to a directory as a PNG and WAV pair, identified by frame number. The results can be exported into one video file.";
}
public string DesiredExtension() { return "syncless.txt"; }
public string ShortName() { return "syncless"; }
/// <summary>
/// splits the string into chunks of length s
/// </summary>
static List<string> StringChunkSplit(string s, int len)
{
if (len == 0) throw new ArgumentException("Invalid len", "len");
int numChunks = (s.Length + len - 1) / len;
List<string> output = new List<string>(numChunks);
for (int i = 0, j = 0; i < numChunks; i++, j += len)
{
int todo = len;
int remain = s.Length - j;
if (remain < todo) todo = remain;
output.Add(s.Substring(j, todo));
}
return output;
}
string GetAndCreatePathForFrameNum(int index)
{
string subpath = GetPathFragmentForFrameNum(index);
string path = mFramesDirectory;
path = Path.Combine(path, subpath);
string fpath = path + ".nothing";
Directory.CreateDirectory(Path.GetDirectoryName(fpath));
return path;
}
public static string GetPathFragmentForFrameNum(int index)
{
var chunks = StringChunkSplit(index.ToString(), 2);
string subpath = string.Join("/", chunks);
return subpath;
}
}
}

View File

@ -0,0 +1,64 @@
namespace BizHawk.Client.EmuHawk
{
partial class SynclessRecordingTools
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnExport = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnExport
//
this.btnExport.Location = new System.Drawing.Point(57, 29);
this.btnExport.Name = "btnExport";
this.btnExport.Size = new System.Drawing.Size(75, 23);
this.btnExport.TabIndex = 0;
this.btnExport.Text = "Export";
this.btnExport.UseVisualStyleBackColor = true;
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
//
// SynclessRecordingTools
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(274, 128);
this.Controls.Add(this.btnExport);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SynclessRecordingTools";
this.Text = "Syncless Recording";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button btnExport;
}
}

View File

@ -0,0 +1,139 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BizHawk.Bizware;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class SynclessRecordingTools : Form
{
public SynclessRecordingTools()
{
InitializeComponent();
}
void GetPaths(int index, out string png, out string wav)
{
string subpath = SynclessRecorder.GetPathFragmentForFrameNum(index);
string path = mFramesDirectory;
path = Path.Combine(path, subpath);
png = path + ".png";
wav = path + ".wav";
}
string mSynclessConfigFile;
string mFramesDirectory;
public void Run()
{
var ofd = new OpenFileDialog();
ofd.FileName = PathManager.FilesystemSafeName(Global.Game) + ".syncless.txt";
ofd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
return;
mSynclessConfigFile = ofd.FileName;
//---- this is pretty crappy:
var lines = File.ReadAllLines(mSynclessConfigFile);
string framesdir = "";
foreach (var line in lines)
{
int idx = line.IndexOf('=');
string key = line.Substring(0, idx);
string value = line.Substring(idx + 1, line.Length - (idx + 1));
if (key == "framesdir")
{
framesdir = value;
}
}
mFramesDirectory = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(mSynclessConfigFile)), framesdir);
//scan frames directory
int frame = 1; //hacky! skip frame 0, because we have a problem with dumping that frame somehow
for (; ; )
{
string wav, png;
GetPaths(frame, out png, out wav);
if (!File.Exists(png) || !File.Exists(wav))
break;
mFrameInfos.Add(new FrameInfo()
{
pngPath = png,
wavPath = wav
});
frame++;
}
ShowDialog();
}
List<FrameInfo> mFrameInfos = new List<FrameInfo>();
struct FrameInfo
{
public string wavPath, pngPath;
}
private void btnExport_Click(object sender, EventArgs e)
{
if(mFrameInfos.Count == 0) return;
int width, height;
using(var bmp = new Bitmap(mFrameInfos[0].pngPath))
{
width = bmp.Width;
height = bmp.Height;
}
var sfd = new SaveFileDialog();
sfd.FileName = Path.ChangeExtension(mSynclessConfigFile, ".avi");
sfd.InitialDirectory = Path.GetDirectoryName(sfd.FileName);
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
return;
using (AviWriter avw = new AviWriter())
{
avw.SetAudioParameters(44100, 2, 16); //hacky
avw.SetMovieParameters(60, 1); //hacky
avw.SetVideoParameters(width, height);
var token = avw.AcquireVideoCodecToken(this);
avw.SetVideoCodecToken(token);
avw.OpenFile(sfd.FileName);
foreach (var fi in mFrameInfos)
{
using (var bb = new BitmapBuffer(fi.pngPath, new BitmapLoadOptions()))
{
var bbvp = new BitmapBufferVideoProvider(bb);
avw.AddFrame(bbvp);
}
//offset = 44 dec
var wavBytes = File.ReadAllBytes(fi.wavPath);
var ms = new MemoryStream(wavBytes);
ms.Position = 44;
var br = new BinaryReader(ms);
List<short> sampledata = new List<short>();
while (br.BaseStream.Position != br.BaseStream.Length)
{
sampledata.Add(br.ReadInt16());
}
avw.AddSamples(sampledata.ToArray());
}
avw.CloseFile();
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -202,6 +202,7 @@ namespace BizHawk.Client.EmuHawk
public void AddFrame(IVideoProvider source) { }
public void SetMovieParameters(int fpsnum, int fpsden) { }
public void SetVideoParameters(int width, int height) { }
public void SetFrame(int frame) { }
class WavWriterVToken : IDisposable
{

View File

@ -152,6 +152,13 @@
</Compile>
<Compile Include="AVOut\NutMuxer.cs" />
<Compile Include="AVOut\NutWriter.cs" />
<Compile Include="AVOut\SynclessRecorder.cs" />
<Compile Include="AVOut\SynclessRecordingTools.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="AVOut\SynclessRecordingTools.Designer.cs">
<DependentUpon>SynclessRecordingTools.cs</DependentUpon>
</Compile>
<Compile Include="AVOut\VideoWriterChooserForm.cs">
<SubType>Form</SubType>
</Compile>
@ -1020,6 +1027,9 @@
<EmbeddedResource Include="AVOut\JMDForm.resx">
<DependentUpon>JMDForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="AVOut\SynclessRecordingTools.resx">
<DependentUpon>SynclessRecordingTools.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="AVOut\VideoWriterChooserForm.resx">
<DependentUpon>VideoWriterChooserForm.cs</DependentUpon>
</EmbeddedResource>

View File

@ -5,6 +5,9 @@ using System.Windows.Forms;
using System.ComponentModel;
using System.Security.Permissions;
//I believe this code is from http://support.microsoft.com/kb/306285
//The license is assumed to be effectively public domain.
//I saw a version of it with at least one bug fixed at https://github.com/slavat/MailSystem.NET/blob/master/Queuing%20System/ActiveQLibrary/CustomControl/FolderBrowser.cs
namespace BizHawk.Client.EmuHawk
{
@ -156,6 +159,7 @@ namespace BizHawk.Client.EmuHawk
}
// Convert to a string.
SelectedPath = sb.ToString();
}
finally
{

View File

@ -89,6 +89,7 @@
this.AVSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.RecordAVMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.StopAVIMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SynclessRecordingMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator19 = new System.Windows.Forms.ToolStripSeparator();
this.CaptureOSDMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ScreenshotSubMenu = new System.Windows.Forms.ToolStripMenuItem();
@ -425,7 +426,7 @@
//
this.OpenRomMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.OpenFile;
this.OpenRomMenuItem.Name = "OpenRomMenuItem";
this.OpenRomMenuItem.Size = new System.Drawing.Size(134, 22);
this.OpenRomMenuItem.Size = new System.Drawing.Size(152, 22);
this.OpenRomMenuItem.Text = "Open ROM";
this.OpenRomMenuItem.Click += new System.EventHandler(this.OpenRomMenuItem_Click);
//
@ -435,7 +436,7 @@
this.toolStripSeparator3});
this.RecentRomSubMenu.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Recent;
this.RecentRomSubMenu.Name = "RecentRomSubMenu";
this.RecentRomSubMenu.Size = new System.Drawing.Size(134, 22);
this.RecentRomSubMenu.Size = new System.Drawing.Size(152, 22);
this.RecentRomSubMenu.Text = "Recent ROM";
this.RecentRomSubMenu.DropDownOpened += new System.EventHandler(this.RecentRomMenuItem_DropDownOpened);
//
@ -448,14 +449,14 @@
//
this.CloseRomMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Close;
this.CloseRomMenuItem.Name = "CloseRomMenuItem";
this.CloseRomMenuItem.Size = new System.Drawing.Size(134, 22);
this.CloseRomMenuItem.Size = new System.Drawing.Size(152, 22);
this.CloseRomMenuItem.Text = "&Close ROM";
this.CloseRomMenuItem.Click += new System.EventHandler(this.CloseRomMenuItem_Click);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(131, 6);
this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6);
//
// SaveStateSubMenu
//
@ -473,7 +474,7 @@
this.toolStripSeparator6,
this.SaveNamedStateMenuItem});
this.SaveStateSubMenu.Name = "SaveStateSubMenu";
this.SaveStateSubMenu.Size = new System.Drawing.Size(134, 22);
this.SaveStateSubMenu.Size = new System.Drawing.Size(152, 22);
this.SaveStateSubMenu.Text = "Save State";
this.SaveStateSubMenu.DropDownOpened += new System.EventHandler(this.SaveStateSubMenu_DropDownOpened);
//
@ -577,7 +578,7 @@
this.toolStripSeparator21,
this.AutoloadLastSlotMenuItem});
this.LoadStateSubMenu.Name = "LoadStateSubMenu";
this.LoadStateSubMenu.Size = new System.Drawing.Size(134, 22);
this.LoadStateSubMenu.Size = new System.Drawing.Size(152, 22);
this.LoadStateSubMenu.Text = "Load State";
this.LoadStateSubMenu.DropDownOpened += new System.EventHandler(this.LoadStateSubMenu_DropDownOpened);
//
@ -694,7 +695,7 @@
this.SaveToCurrentSlotMenuItem,
this.LoadCurrentSlotMenuItem});
this.SaveSlotSubMenu.Name = "SaveSlotSubMenu";
this.SaveSlotSubMenu.Size = new System.Drawing.Size(134, 22);
this.SaveSlotSubMenu.Size = new System.Drawing.Size(152, 22);
this.SaveSlotSubMenu.Text = "SaveSlot";
this.SaveSlotSubMenu.DropDownOpened += new System.EventHandler(this.SaveSlotSubMenu_DropDownOpened);
//
@ -806,7 +807,7 @@
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(131, 6);
this.toolStripMenuItem2.Size = new System.Drawing.Size(149, 6);
//
// MovieSubMenu
//
@ -825,7 +826,7 @@
this.AutomaticallyBackupMoviesMenuItem,
this.FullMovieLoadstatesMenuItem});
this.MovieSubMenu.Name = "MovieSubMenu";
this.MovieSubMenu.Size = new System.Drawing.Size(134, 22);
this.MovieSubMenu.Size = new System.Drawing.Size(152, 22);
this.MovieSubMenu.Text = "Movie";
this.MovieSubMenu.DropDownOpened += new System.EventHandler(this.MovieSubMenu_DropDownOpened);
//
@ -938,9 +939,10 @@
this.RecordAVMenuItem,
this.StopAVIMenuItem,
this.toolStripSeparator19,
this.CaptureOSDMenuItem});
this.CaptureOSDMenuItem,
this.SynclessRecordingMenuItem});
this.AVSubMenu.Name = "AVSubMenu";
this.AVSubMenu.Size = new System.Drawing.Size(134, 22);
this.AVSubMenu.Size = new System.Drawing.Size(152, 22);
this.AVSubMenu.Text = "AVI/WAV";
this.AVSubMenu.DropDownOpened += new System.EventHandler(this.AVSubMenu_DropDownOpened);
//
@ -948,27 +950,34 @@
//
this.RecordAVMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.AVI;
this.RecordAVMenuItem.Name = "RecordAVMenuItem";
this.RecordAVMenuItem.Size = new System.Drawing.Size(155, 22);
this.RecordAVMenuItem.Text = "Record AVI/WAV";
this.RecordAVMenuItem.Size = new System.Drawing.Size(194, 22);
this.RecordAVMenuItem.Text = "&Record AVI/WAV";
this.RecordAVMenuItem.Click += new System.EventHandler(this.RecordAVMenuItem_Click);
//
// StopAVIMenuItem
//
this.StopAVIMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Stop;
this.StopAVIMenuItem.Name = "StopAVIMenuItem";
this.StopAVIMenuItem.Size = new System.Drawing.Size(155, 22);
this.StopAVIMenuItem.Text = "Stop AVI/WAV";
this.StopAVIMenuItem.Size = new System.Drawing.Size(194, 22);
this.StopAVIMenuItem.Text = "&Stop AVI/WAV";
this.StopAVIMenuItem.Click += new System.EventHandler(this.StopAVMenuItem_Click);
//
// SynclessRecordingMenuItem
//
this.SynclessRecordingMenuItem.Name = "SynclessRecordingMenuItem";
this.SynclessRecordingMenuItem.Size = new System.Drawing.Size(194, 22);
this.SynclessRecordingMenuItem.Text = "S&yncless Recording Tools";
this.SynclessRecordingMenuItem.Click += new System.EventHandler(this.SynclessRecordingMenuItem_Click);
//
// toolStripSeparator19
//
this.toolStripSeparator19.Name = "toolStripSeparator19";
this.toolStripSeparator19.Size = new System.Drawing.Size(152, 6);
this.toolStripSeparator19.Size = new System.Drawing.Size(191, 6);
//
// CaptureOSDMenuItem
//
this.CaptureOSDMenuItem.Name = "CaptureOSDMenuItem";
this.CaptureOSDMenuItem.Size = new System.Drawing.Size(155, 22);
this.CaptureOSDMenuItem.Size = new System.Drawing.Size(194, 22);
this.CaptureOSDMenuItem.Text = "Capture OSD";
this.CaptureOSDMenuItem.Click += new System.EventHandler(this.CaptureOSDMenuItem_Click);
//
@ -981,7 +990,7 @@
this.toolStripSeparator20,
this.ScreenshotCaptureOSDMenuItem1});
this.ScreenshotSubMenu.Name = "ScreenshotSubMenu";
this.ScreenshotSubMenu.Size = new System.Drawing.Size(134, 22);
this.ScreenshotSubMenu.Size = new System.Drawing.Size(152, 22);
this.ScreenshotSubMenu.Text = "Screenshot";
this.ScreenshotSubMenu.DropDownOpening += new System.EventHandler(this.ScreenshotSubMenu_DropDownOpening);
//
@ -1023,13 +1032,13 @@
// toolStripSeparator4
//
this.toolStripSeparator4.Name = "toolStripSeparator4";
this.toolStripSeparator4.Size = new System.Drawing.Size(131, 6);
this.toolStripSeparator4.Size = new System.Drawing.Size(149, 6);
//
// ExitMenuItem
//
this.ExitMenuItem.Name = "ExitMenuItem";
this.ExitMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4)));
this.ExitMenuItem.Size = new System.Drawing.Size(134, 22);
this.ExitMenuItem.Size = new System.Drawing.Size(152, 22);
this.ExitMenuItem.Text = "Exit";
this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click);
//
@ -3531,6 +3540,7 @@
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ProfilesMenuItem;
private System.Windows.Forms.ToolStripMenuItem PceSoundDebuggerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SynclessRecordingMenuItem;
}
}

View File

@ -457,6 +457,11 @@ namespace BizHawk.Client.EmuHawk
this.StopAv();
}
private void SynclessRecordingMenuItem_Click(object sender, EventArgs e)
{
new SynclessRecordingTools().Run();
}
private void CaptureOSDMenuItem_Click(object sender, EventArgs e)
{
Global.Config.AVI_CaptureOSD ^= true;

View File

@ -2714,28 +2714,47 @@ namespace BizHawk.Client.EmuHawk
}
else
{
var sfd = new SaveFileDialog();
if (!(Global.Emulator is NullEmulator))
string ext = aw.DesiredExtension();
string pathForOpenFile;
//handle directories first
if (ext == "<directory>")
{
sfd.FileName = PathManager.FilesystemSafeName(Global.Game) + "." + aw.DesiredExtension(); //dont use Path.ChangeExtension, it might wreck game names with dots in them
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
var fbd = new FolderBrowserEx();
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
{
aw.Dispose();
return;
}
pathForOpenFile = fbd.SelectedPath;
}
else
{
sfd.FileName = "NULL";
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
var sfd = new SaveFileDialog();
if (!(Global.Emulator is NullEmulator))
{
sfd.FileName = PathManager.FilesystemSafeName(Global.Game) + "." + ext; //dont use Path.ChangeExtension, it might wreck game names with dots in them
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
}
else
{
sfd.FileName = "NULL";
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null);
}
sfd.Filter = String.Format("{0} (*.{0})|*.{0}|All Files|*.*", ext);
var result = sfd.ShowHawkDialog();
if (result == DialogResult.Cancel)
{
aw.Dispose();
return;
}
pathForOpenFile = sfd.FileName;
}
sfd.Filter = String.Format("{0} (*.{0})|*.{0}|All Files|*.*", aw.DesiredExtension());
var result = sfd.ShowHawkDialog();
if (result == DialogResult.Cancel)
{
aw.Dispose();
return;
}
aw.OpenFile(sfd.FileName);
aw.OpenFile(pathForOpenFile);
}
// commit the avi writing last, in case there were any errors earlier
@ -2877,6 +2896,7 @@ namespace BizHawk.Client.EmuHawk
output = Global.Emulator.VideoProvider;
}
_currAviWriter.SetFrame(Global.Emulator.Frame);
_currAviWriter.AddFrame(output);
if (disposableOutput != null)
@ -3230,5 +3250,6 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.OSD.AddMessage("Profile config aborted");
}
}
}
}