add a new tool to mainform, the batch runner. it's only enabled in INTERIM. it allows you to mass instantiate a bunch of emu cores and look for failure to load or exceptions on frame advance. it doesn't do all that much, but can easily be extended if any other devs are interested in it

This commit is contained in:
goyuken 2014-01-15 02:16:06 +00:00
parent a54119db03
commit 7668b15052
9 changed files with 939 additions and 283 deletions

View File

@ -157,6 +157,9 @@ namespace BizHawk.Client.Common
}
}
// set this here so we can see what file we tried to load even if an error occurs
CanonicalFullPath = file.CanonicalFullPath;
IEmulator nextEmulator = null;
RomGame rom = null;
GameInfo game = null;
@ -234,18 +237,21 @@ namespace BizHawk.Client.Common
"The PCE-CD System Card you have selected is known to be a bad dump. This may cause problems playing PCE-CD games.\n\n"
+ "It is recommended that you find a good dump of the system card. Sorry to be the bearer of bad news!",
game.System);
return false;
}
else if (rom.GameInfo.NotInDatabase)
{
ThrowLoadError(
"The PCE-CD System Card you have selected is not recognized in our database. That might mean it's a bad dump, or isn't the correct rom.",
game.System);
return false;
}
else if (rom.GameInfo["BIOS"] == false)
{
ThrowLoadError(
"The PCE-CD System Card you have selected is not a BIOS image. You may have selected the wrong rom.",
game.System);
return false;
}
if (rom.GameInfo["SuperSysCard"])
@ -258,6 +264,7 @@ namespace BizHawk.Client.Common
ThrowLoadError(
"This game requires a version 3.0 System card and won't run with the system card you've selected. Try selecting a 3.0 System Card in Config->Paths->PC Engine.",
game.System);
return false;
}
game.FirmwareHash = Util.Hash_SHA1(rom.RomData);
@ -295,6 +302,7 @@ namespace BizHawk.Client.Common
catch (Exception ex)
{
ThrowLoadError(ex.ToString(), "XMLGame Load Error"); // TODO: don't pass in XMLGame Load Error as a system ID
return false;
}
}
else // most extensions
@ -449,7 +457,6 @@ namespace BizHawk.Client.Common
Rom = rom;
LoadedEmulator = nextEmulator;
Game = game;
CanonicalFullPath = file.CanonicalFullPath;
return true;
}
}

View File

@ -453,6 +453,13 @@
<DependentUpon>ScanlineSlider.cs</DependentUpon>
</Compile>
<Compile Include="Throttle.cs" />
<Compile Include="tools\BatchRun.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="tools\BatchRun.Designer.cs">
<DependentUpon>BatchRun.cs</DependentUpon>
</Compile>
<Compile Include="tools\BatchRunner.cs" />
<Compile Include="tools\Cheats\CheatEdit.cs">
<SubType>UserControl</SubType>
</Compile>
@ -966,6 +973,9 @@
<EmbeddedResource Include="ScanlineSlider.resx">
<DependentUpon>ScanlineSlider.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="tools\BatchRun.resx">
<DependentUpon>BatchRun.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="tools\Cheats\CheatEdit.resx">
<DependentUpon>CheatEdit.cs</DependentUpon>
</EmbeddedResource>

File diff suppressed because it is too large Load Diff

View File

@ -1107,6 +1107,7 @@ namespace BizHawk.Client.EmuHawk
TAStudioMenuItem.Enabled =
VirtualPadMenuItem.Enabled =
!(Global.Emulator is NullEmulator);
batchRunnerToolStripMenuItem.Visible = VersionInfo.INTERIM;
}
private void ToolBoxMenuItem_Click(object sender, EventArgs e)

View File

@ -3242,5 +3242,10 @@ namespace BizHawk.Client.EmuHawk
{
Global.Config.NES_InQuickNES ^= true;
}
private void batchRunnerToolStripMenuItem_Click(object sender, EventArgs e)
{
new BatchRun().ShowDialog();
}
}
}

View File

@ -0,0 +1,177 @@
namespace BizHawk.Client.EmuHawk
{
partial class BatchRun
{
/// <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.label1 = new System.Windows.Forms.Label();
this.listBox1 = new System.Windows.Forms.ListBox();
this.label2 = new System.Windows.Forms.Label();
this.buttonClear = new System.Windows.Forms.Button();
this.buttonGo = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.numericUpDownFrames = new System.Windows.Forms.NumericUpDown();
this.label4 = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.buttonDump = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownFrames)).BeginInit();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(83, 13);
this.label1.TabIndex = 1;
this.label1.Text = "Drag Files Here:";
//
// listBox1
//
this.listBox1.AllowDrop = true;
this.listBox1.FormattingEnabled = true;
this.listBox1.Location = new System.Drawing.Point(12, 25);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(268, 147);
this.listBox1.TabIndex = 2;
this.listBox1.DragDrop += new System.Windows.Forms.DragEventHandler(this.listBox1_DragDrop);
this.listBox1.DragEnter += new System.Windows.Forms.DragEventHandler(this.listBox1_DragEnter);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 175);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(92, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Number of Files: 0";
//
// buttonClear
//
this.buttonClear.Location = new System.Drawing.Point(12, 191);
this.buttonClear.Name = "buttonClear";
this.buttonClear.Size = new System.Drawing.Size(75, 23);
this.buttonClear.TabIndex = 4;
this.buttonClear.Text = "Clear!";
this.buttonClear.UseVisualStyleBackColor = true;
this.buttonClear.Click += new System.EventHandler(this.buttonClear_Click);
//
// buttonGo
//
this.buttonGo.Location = new System.Drawing.Point(12, 220);
this.buttonGo.Name = "buttonGo";
this.buttonGo.Size = new System.Drawing.Size(75, 23);
this.buttonGo.TabIndex = 5;
this.buttonGo.Text = "Go!";
this.buttonGo.UseVisualStyleBackColor = true;
this.buttonGo.Click += new System.EventHandler(this.buttonGo_Click);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 246);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(40, 13);
this.label3.TabIndex = 6;
this.label3.Text = "Status:";
//
// numericUpDownFrames
//
this.numericUpDownFrames.Location = new System.Drawing.Point(160, 194);
this.numericUpDownFrames.Maximum = new decimal(new int[] {
1000,
0,
0,
0});
this.numericUpDownFrames.Name = "numericUpDownFrames";
this.numericUpDownFrames.Size = new System.Drawing.Size(120, 20);
this.numericUpDownFrames.TabIndex = 7;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(157, 175);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(74, 13);
this.label4.TabIndex = 8;
this.label4.Text = "Frames to run:";
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(12, 262);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(268, 23);
this.progressBar1.TabIndex = 9;
//
// buttonDump
//
this.buttonDump.Location = new System.Drawing.Point(93, 220);
this.buttonDump.Name = "buttonDump";
this.buttonDump.Size = new System.Drawing.Size(75, 23);
this.buttonDump.TabIndex = 10;
this.buttonDump.Text = "Dump...";
this.buttonDump.UseVisualStyleBackColor = true;
this.buttonDump.Click += new System.EventHandler(this.buttonDump_Click);
//
// BatchRun
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 327);
this.Controls.Add(this.buttonDump);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.label4);
this.Controls.Add(this.numericUpDownFrames);
this.Controls.Add(this.label3);
this.Controls.Add(this.buttonGo);
this.Controls.Add(this.buttonClear);
this.Controls.Add(this.label2);
this.Controls.Add(this.listBox1);
this.Controls.Add(this.label1);
this.Name = "BatchRun";
this.Text = "BatchRun";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BatchRun_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.numericUpDownFrames)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button buttonClear;
private System.Windows.Forms.Button buttonGo;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.NumericUpDown numericUpDownFrames;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button buttonDump;
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
namespace BizHawk.Client.EmuHawk
{
public partial class BatchRun : Form
{
Thread thread = null;
List<BatchRunner.Result> MostRecentResults = null;
public BatchRun()
{
InitializeComponent();
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Link;
else
e.Effect = DragDropEffects.None;
}
private void SetCount()
{
label2.Text = string.Format("Number of files: {0}", listBox1.Items.Count);
}
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
listBox1.Items.AddRange(files);
SetCount();
}
}
private void buttonClear_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
SetCount();
}
private void buttonGo_Click(object sender, EventArgs e)
{
if (thread != null)
{
MessageBox.Show("Old one still running!");
}
else
{
if (listBox1.Items.Count == 0)
{
MessageBox.Show("No files!");
}
else
{
label3.Text = "Status: Running...";
int nframes = (int)numericUpDownFrames.Value;
List<string> files = new List<string>(listBox1.Items.Count);
foreach (string s in listBox1.Items)
files.Add(s);
thread = new Thread(ThreadProc);
thread.Start(new Tuple<int, List<string>>(nframes, files));
}
}
}
void ProgressUpdate(int curr, int max)
{
progressBar1.Maximum = max;
progressBar1.Value = curr;
}
void ThreadProc(object o)
{
try
{
var pp = (Tuple<int, List<string>>)o;
BatchRunner br = new BatchRunner(pp.Item2, pp.Item1);
br.OnProgress += br_OnProgress;
var results = br.Run();
this.Invoke(() => { label3.Text = "Status: Finished!"; MostRecentResults = results; });
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "The Whole Thing Died!");
this.Invoke(() => label3.Text = "Deaded!");
}
this.Invoke(() => thread = null);
}
void br_OnProgress(object sender, BatchRunner.ProgressEventArgs e)
{
this.Invoke(() => ProgressUpdate(e.Completed, e.Total));
e.ShouldCancel = false;
}
private void BatchRun_FormClosing(object sender, FormClosingEventArgs e)
{
if (thread != null)
{
MessageBox.Show("Can't close while task is running!");
e.Cancel = true;
}
}
private void buttonDump_Click(object sender, EventArgs e)
{
if (MostRecentResults != null)
{
using (var sfd = new SaveFileDialog())
{
var result = sfd.ShowDialog(this);
if (result == System.Windows.Forms.DialogResult.OK)
{
using (TextWriter tw = new StreamWriter(sfd.FileName))
{
foreach (var r in MostRecentResults)
r.DumpToTW(tw);
}
}
}
}
else
{
MessageBox.Show("No results to save!");
}
}
}
}

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

@ -0,0 +1,185 @@
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<string> files;
RomLoader ldr;
CoreComm Comm;
int numframes = 0;
int multiindex = 0;
bool multihasnext = false;
List<Result> Results = new List<Result>();
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<string> Messages = new List<string>();
public void DumpToTW(System.IO.TextWriter tw)
{
tw.WriteLine("{0}\t{1}\t{2}\t{3}\n", Filename, Fullname, CoreType, Status);
}
}
public BatchRunner(IEnumerable<string> files, int numframes)
{
this.files = new List<string>(files);
this.numframes = numframes;
ldr = new RomLoader();
ldr.OnLoadError += OnLoadError;
ldr.ChooseArchive = ChooseArchive;
Comm = new CoreComm(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<Result> Run()
{
Results.Clear();
current = null;
RunInternal();
return new List<Result>(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);
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);
}
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;
}
}
}