210 lines
5.3 KiB
C#
210 lines
5.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using BizHawk.Common;
|
|
using BizHawk.Client.Common;
|
|
using BizHawk.Emulation.Common;
|
|
using BizHawk.Emulation.Cores.Nintendo.GBA;
|
|
|
|
namespace BizHawk.Client.EmuHawk
|
|
{
|
|
public class BatchRunner
|
|
{
|
|
public class ProgressEventArgs
|
|
{
|
|
public int Completed { get; }
|
|
public int Total { get; }
|
|
public bool ShouldCancel { get; set; }
|
|
public ProgressEventArgs(int completed, int total)
|
|
{
|
|
Completed = completed;
|
|
Total = total;
|
|
}
|
|
}
|
|
|
|
public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);
|
|
public event ProgressEventHandler OnProgress;
|
|
|
|
private readonly List<string> _files;
|
|
private readonly List<Result> _results = new List<Result>();
|
|
private readonly RomLoader _ldr;
|
|
private readonly CoreComm _comm;
|
|
private readonly int _numFrames;
|
|
|
|
private int _multiIndex;
|
|
private bool _multiHasNext;
|
|
private Result _current;
|
|
|
|
public class Result
|
|
{
|
|
public string Filename { get; set; } // name of file
|
|
public string Fullname { get; set; } // filename + subfilename
|
|
public GameInfo Game { get; set; }
|
|
|
|
public Type CoreType { get; set; } // 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 { get; set; } // what happened
|
|
public List<string> Messages { get; set; } = new List<string>();
|
|
|
|
public int Frames { get; set; } // number of frames successfully run
|
|
public int LaggedFrames { get; set; } // number of those that were lagged
|
|
|
|
public string BoardName { get; set; } // IEmulator's board name return (could be null!)
|
|
|
|
public void DumpTo(TextWriter tw)
|
|
{
|
|
tw.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}", Filename, Fullname, CoreType, Status, Frames, LaggedFrames, Game.Hash, BoardName);
|
|
}
|
|
}
|
|
|
|
public BatchRunner(IEnumerable<string> files, int numFrames)
|
|
{
|
|
_files = new List<string>(files);
|
|
_numFrames = numFrames;
|
|
|
|
_ldr = new RomLoader();
|
|
_ldr.OnLoadError += OnLoadError;
|
|
_ldr.ChooseArchive = ChooseArchive;
|
|
_comm = new CoreComm(CommMessage, CommMessage);
|
|
CoreFileProvider.SyncCoreCommInputSignals(_comm);
|
|
}
|
|
|
|
private void OnLoadError(object sender, RomLoader.RomErrorArgs e)
|
|
{
|
|
_current.Status = Result.EStatus.ErrorOnLoad;
|
|
_current.Messages.Add($"{nameof(OnLoadError)}: {e.AttemptedCoreLoad}, {e.Message}, {e.Type}");
|
|
}
|
|
|
|
private void CommMessage(string msg)
|
|
{
|
|
_current.Messages.Add($"{nameof(CommMessage)}: {msg}");
|
|
}
|
|
|
|
private 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);
|
|
}
|
|
|
|
private 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadOne(string f)
|
|
{
|
|
_current = new Result { Filename = f };
|
|
bool result;
|
|
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.Game = _ldr.Game;
|
|
_current.CoreType = emu.GetType();
|
|
var controller = new Controller(emu.ControllerDefinition);
|
|
_current.BoardName = emu.HasBoardInfo() ? emu.AsBoardInfo().BoardName : null;
|
|
// hack
|
|
if (emu is VBANext vba)
|
|
{
|
|
_current.BoardName = vba.GameCode;
|
|
}
|
|
|
|
_current.Frames = 0;
|
|
_current.LaggedFrames = 0;
|
|
|
|
for (int i = 0; i < _numFrames; i++)
|
|
{
|
|
try
|
|
{
|
|
emu.FrameAdvance(controller, true);
|
|
|
|
// some cores really really really like it if you drain their audio every frame
|
|
if (emu.HasSoundProvider())
|
|
{
|
|
emu.AsSoundProvider().GetSamplesSync(out _, out _);
|
|
}
|
|
|
|
_current.Frames++;
|
|
if (emu.CanPollInput() && emu.AsInputPollable().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;
|
|
}
|
|
}
|
|
}
|