using System; using System.Collections.Generic; using System.Linq; using System.Text; using BizHawk.Client.Common; using BizHawk.Emulation.Common; using BizHawk.Common; namespace BizHawk.Client.EmuHawk { public class BatchRunner { public class ProgressEventArgs { public int Completed { get; private set; } public int Total { get; private set; } public bool ShouldCancel { get; set; } public ProgressEventArgs(int Completed, int Total) { this.Completed = Completed; this.Total = Total; } } public delegate void ProgressEventHandler(object sender, ProgressEventArgs e); public event ProgressEventHandler OnProgress; List files; RomLoader ldr; CoreComm Comm; int numframes = 0; int multiindex = 0; bool multihasnext = false; List Results = new List(); Result current; public class Result { public string Filename; // name of file public string Fullname; // filename + subfilename public GameInfo GI; public Type CoreType; // actual type of the core that was returned public enum EStatus { ExceptOnLoad, // exception thrown on load ErrorOnLoad, // error method thrown on load FalseOnLoad, // romloader returned false with no other information ExceptOnAdv, // exception thrown on frame advance Success, // load fully complete }; public EStatus Status; // what happened public List Messages = new List(); public int Frames; // number of frames successfully run public int LaggedFrames; // number of those that were lagged public void DumpToTW(System.IO.TextWriter tw) { tw.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", Filename, Fullname, CoreType, Status, Frames, LaggedFrames); } } public BatchRunner(IEnumerable files, int numframes) { this.files = new List(files); this.numframes = numframes; ldr = new RomLoader(); ldr.OnLoadError += OnLoadError; ldr.ChooseArchive = ChooseArchive; Comm = new CoreComm(CommMessage, CommMessage); CoreFileProvider.SyncCoreCommInputSignals(Comm); } void OnLoadError(object sender, RomLoader.RomErrorArgs e) { current.Status = Result.EStatus.ErrorOnLoad; current.Messages.Add(string.Format("OnLoadError: {0}, {1}", e.AttemptedCoreLoad, e.Message)); } void CommMessage(string msg) { current.Messages.Add(string.Format("CommMessage: {0}", msg)); } int? ChooseArchive(HawkFile hf) { int ret = multiindex; multiindex++; multihasnext = multiindex < hf.ArchiveItems.Count; return ret; } public List Run() { Results.Clear(); current = null; RunInternal(); return new List(Results); } void RunInternal() { for (int i = 0; i < files.Count; i++) { string f = files[i]; multihasnext = false; multiindex = 0; do { LoadOne(f); } while (multihasnext); if (OnProgress != null) { var e = new ProgressEventArgs(i + 1, files.Count); OnProgress(this, e); if (e.ShouldCancel) return; } } } void LoadOne(string f) { current = new Result { Filename = f }; bool result = false; try { result = ldr.LoadRom(f, Comm); } catch (Exception e) { current.Status = Result.EStatus.ExceptOnLoad; current.Messages.Add(e.ToString()); Results.Add(current); current = null; return; } current.Fullname = ldr.CanonicalFullPath; if (current.Status == Result.EStatus.ErrorOnLoad) { Results.Add(current); current = null; return; } if (result == false) { current.Status = Result.EStatus.FalseOnLoad; Results.Add(current); current = null; return; } using (IEmulator emu = ldr.LoadedEmulator) { current.GI = ldr.Game; current.CoreType = emu.GetType(); emu.Controller = new Controller(emu.ControllerDefinition); current.Frames = 0; current.LaggedFrames = 0; for (int i = 0; i < numframes; i++) { try { int nsamp; short[] samp; emu.FrameAdvance(true, true); // some cores really really really like it if you drain their audio every frame emu.SyncSoundProvider.GetSamples(out samp, out nsamp); current.Frames++; if (emu.IsLagFrame) current.LaggedFrames++; } catch (Exception e) { current.Messages.Add(e.ToString()); current.Status = Result.EStatus.ExceptOnAdv; Results.Add(current); current = null; return; } } } current.Status = Result.EStatus.Success; Results.Add(current); current = null; return; } } }