using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using BizHawk.Emulation.Common; using BizHawk.Bizware.BizwareGL; namespace BizHawk.Client.EmuHawk { [VideoWriter("syncless", "Syncless Recording", "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 class SynclessRecorder : IVideoWriter { public void Dispose() { } public void SetVideoCodecToken(IDisposable token) { } public void SetDefaultVideoCodecToken() { } public void SetFrame(int frame) { mCurrFrame = frame; } private int mCurrFrame; private string mBaseDirectory, mFramesDirectory; private 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(); } public bool UsesAudio => true; public bool UsesVideo => true; private class DummyDisposable : IDisposable { public void Dispose() { } } public IDisposable AcquireVideoCodecToken(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 } private 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 string DesiredExtension() { return "syncless.txt"; } /// /// splits the string into chunks of length s /// private static List StringChunkSplit(string s, int len) { if (len == 0) { throw new ArgumentException("Invalid len", nameof(len)); } int numChunks = (s.Length + len - 1) / len; var output = new List(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; } private 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; } } }