Merge branch 'master' of https://github.com/nog5/BizHawk
This commit is contained in:
commit
ab78d3ff0d
|
@ -11,3 +11,7 @@
|
|||
path = waterbox/snes9x
|
||||
url = https://github.com/TASVideos/snes9x.git
|
||||
branch = bizsnes
|
||||
[submodule "mgba"]
|
||||
path = mgba
|
||||
url = https://github.com/TASVideos/mgba.git
|
||||
branch = bizhawk-0.6
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace BizHawk.Client.Common
|
|||
//handling of initial .. was removed (Path.GetFullPath can handle it)
|
||||
//handling of file:// or file:\\ was removed (can Path.GetFullPath handle it? not sure)
|
||||
|
||||
// all pad paths default to EXE
|
||||
// all bad paths default to EXE
|
||||
return GetExeDirectoryAbsolute();
|
||||
}
|
||||
|
||||
|
@ -461,5 +461,19 @@ namespace BizHawk.Client.Common
|
|||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts the currently configured temp path into the environment for use as actual temp directory
|
||||
/// </summary>
|
||||
public static void RefreshTempPath()
|
||||
{
|
||||
if (Global.Config.PathEntries.TempFilesFragment != "")
|
||||
{
|
||||
//TODO - BUG - needs to route through PathManager.MakeAbsolutePath or something similar, but how?
|
||||
string target = Global.Config.PathEntries.TempFilesFragment;
|
||||
BizHawk.Common.TempFileManager.HelperSetTempPath(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
PathEntries.ResolveWithDefaults();
|
||||
HotkeyBindings.ResolveWithDefaults();
|
||||
PathManager.RefreshTempPath();
|
||||
}
|
||||
|
||||
// Core preference for generic file extension, key: file extension, value: a systemID or empty if no preference
|
||||
|
|
|
@ -165,6 +165,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public string GlobalRomFragment => Global.Config.PathEntries["Global", "ROM"].Path;
|
||||
|
||||
public string TempFilesFragment => Global.Config.PathEntries["Global", "Temp Files"].Path;
|
||||
|
||||
// this one is special
|
||||
public string GlobalBaseFragment => Global.Config.PathEntries["Global", "Base"].Path;
|
||||
|
||||
|
@ -184,6 +186,7 @@ namespace BizHawk.Client.Common
|
|||
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "TAStudio states", Path = Path.Combine(".", "Movies", "TAStudio states"), Ordinal = 12 },
|
||||
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Multi-Disk Bundles", Path = Path.Combine(".", ""), Ordinal = 13 },
|
||||
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "External Tools", Path = Path.Combine(".", "ExternalTools"), Ordinal = 14 },
|
||||
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Temp Files", Path = "", Ordinal = 15 },
|
||||
|
||||
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Base", Path = Path.Combine(".", "Intellivision"), Ordinal = 0 },
|
||||
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "ROM", Path = ".", Ordinal = 1 },
|
||||
|
|
|
@ -133,10 +133,10 @@ namespace BizHawk.Client.Common
|
|||
var input = Movie.GetInputState(Global.Emulator.Frame);
|
||||
|
||||
// adelikat: TODO: this is likely the source of frame 0 TAStudio bugs, I think the intent is to check if the movie is 0 length?
|
||||
if (Global.Emulator.Frame == 0) // Hacky
|
||||
{
|
||||
HandleMovieAfterFrameLoop(); // Frame 0 needs to be handled.
|
||||
}
|
||||
//if (Global.Emulator.Frame == 0) // Hacky
|
||||
//{
|
||||
// HandleMovieAfterFrameLoop(); // Frame 0 needs to be handled.
|
||||
//}
|
||||
|
||||
if (input == null)
|
||||
{
|
||||
|
@ -327,6 +327,10 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
HandlePlaybackEnd();
|
||||
}
|
||||
else if (Movie.IsPlaying && !Movie.IsFinished)
|
||||
{
|
||||
LatchInputFromLog();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HandleMovieLoadState(string path)
|
||||
|
@ -456,6 +460,7 @@ namespace BizHawk.Client.Common
|
|||
else
|
||||
{
|
||||
Movie.StartNewPlayback();
|
||||
LatchInputFromLog();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -281,9 +281,35 @@ namespace BizHawk.Client.Common
|
|||
["Space"] = '_'
|
||||
},
|
||||
["ZXSpectrum"] = new Dictionary<string, char>
|
||||
{
|
||||
["Return"] = 'e',
|
||||
["Space"] = 's',
|
||||
{
|
||||
["Caps Shift"] = '^',
|
||||
["Caps Lock"] = 'L',
|
||||
["Period"] = '_',
|
||||
["Symbol Shift"] = 'v',
|
||||
["Semi-Colon"] = ';',
|
||||
["Quote"] = '"',
|
||||
["Comma"] = ',',
|
||||
["True Video"] = 'T',
|
||||
["Inv Video"] = 'I',
|
||||
["Break"] = 'B',
|
||||
["Delete"] = 'D',
|
||||
["Graph"] = 'G',
|
||||
["Extend Mode"] = 'M',
|
||||
["Edit"] = 'E',
|
||||
["Play Tape"] = 'P',
|
||||
["Stop Tape"] = 'S',
|
||||
["RTZ Tape"] = 'r',
|
||||
["Record Tape"] = 'R',
|
||||
["Insert Next Tape"] = '>',
|
||||
["Insert Previous Tape"] = '<',
|
||||
["Next Tape Block"] = ']',
|
||||
["Prev Tape Block"] = '[',
|
||||
["Get Tape Status"] = 'S',
|
||||
["Insert Next Disk"] = '}',
|
||||
["Insert Previous Disk"] = '{',
|
||||
["Get Disk Status"] = 's',
|
||||
["Return"] = 'e',
|
||||
["Space"] = '-',
|
||||
["Up Cursor"] = 'u',
|
||||
["Down Cursor"] = 'd',
|
||||
["Left Cursor"] = 'l',
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace BizHawk.Client.Common
|
|||
_mDisk = disk;
|
||||
if (disk)
|
||||
{
|
||||
var path = TempFileCleaner.GetTempFilename("movieOnDisk");
|
||||
var path = TempFileManager.GetTempFilename("movieOnDisk");
|
||||
stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace BizHawk.Client.Common
|
|||
_mCapacity = capacity;
|
||||
if (onDisk)
|
||||
{
|
||||
var path = TempFileCleaner.GetTempFilename("rewindbuf");
|
||||
var path = TempFileManager.GetTempFilename("rewindbuf");
|
||||
|
||||
// I checked the DeleteOnClose operation to make sure it cleans up when the process is aborted, and it seems to.
|
||||
// Otherwise we would have a more complex tempfile management problem here.
|
||||
|
|
|
@ -21,6 +21,14 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
public static SeparatorWatch Instance => new SeparatorWatch();
|
||||
|
||||
public static SeparatorWatch NewSeparatorWatch(string description)
|
||||
{
|
||||
return new SeparatorWatch
|
||||
{
|
||||
Notes = description
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the appropriate DisplayType
|
||||
/// </summary>
|
||||
|
@ -45,10 +53,10 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
public override int Previous => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Ignore that stuff
|
||||
/// </summary>
|
||||
public override string ValueString => "";
|
||||
/// <summary>
|
||||
/// Ignore that stuff
|
||||
/// </summary>
|
||||
public override string ValueString => Notes; //"";
|
||||
|
||||
/// <summary>
|
||||
/// Ignore that stuff
|
||||
|
@ -62,13 +70,26 @@ namespace BizHawk.Client.Common
|
|||
/// <returns>A well formatted string representation</returns>
|
||||
public override string ToDisplayString()
|
||||
{
|
||||
return "----";
|
||||
if (Notes == null || Notes == "")
|
||||
return "----";
|
||||
else
|
||||
return Notes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ignore that stuff
|
||||
/// <summary>
|
||||
/// Transforms the current instance into a string
|
||||
/// </summary>
|
||||
public override bool Poke(string value)
|
||||
/// <returns>A <see cref="string"/> representation of the current <see cref="Watch"/></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"0\tS\t_\t1\t\t{Notes.Trim('\r', '\n')}";
|
||||
//return $"{(Domain == null && Address == 0 ? "0" : Address.ToHexString((Domain?.Size ?? 0xFF - 1).NumHexDigits()))}\t{SizeAsChar}\t{TypeAsChar}\t{Convert.ToInt32(BigEndian)}\t{Domain?.Name}\t{Notes.Trim('\r', '\n')}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ignore that stuff
|
||||
/// </summary>
|
||||
public override bool Poke(string value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -95,6 +116,8 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
}
|
||||
//AddressString = (Notes == null || Notes == "") ? "" : Notes;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
default:
|
||||
case WatchSize.Separator:
|
||||
return SeparatorWatch.Instance;
|
||||
return SeparatorWatch.NewSeparatorWatch(note);
|
||||
case WatchSize.Byte:
|
||||
return new ByteWatch(domain, address, type, bigEndian, note, (byte)value, (byte)prev, changeCount);
|
||||
case WatchSize.Word:
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(291, 165);
|
||||
this.label2.TabIndex = 2;
|
||||
this.label2.Text = "EMULATES\r\nYOUR\r\nMOM";
|
||||
this.label2.Text = "EMULATES\r\nY̶̷̼̬̟̘̦̼͙̝̯̦̠̜͙͖̘ͨ̒͂͐ͪ͂̒̄ͯͯ͌ͨͯ̽ͨ́͝ͅO̡̝̞̗̩͖͖̼̹̖̫͍̙̖͓̩ͪ͂̊ͭ͑ͮͤ̀̄͌̑ͨͣͩ̿ͫ͂͟͝͞ͅU̴̮̝͍̜̰̦ͥ̓ͩ̌̎̾ͥͪ̋̾́͠Rͨ͊̉̾҉̢̫͔̗͓͖̫̫̘̖̰͟\r\nMOM";
|
||||
//
|
||||
// timer1
|
||||
//
|
||||
|
|
|
@ -1840,6 +1840,9 @@
|
|||
<None Include="config\ControllerImages\GENController.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\MoveTop.png" />
|
||||
<None Include="Resources\MoveBottom.png" />
|
||||
<None Include="Resources\MoveTop.bmp" />
|
||||
<None Include="Resources\ZXSpectrumKeyboard.bmp" />
|
||||
<None Include="images\WSW.png" />
|
||||
<None Include="images\WNW.png" />
|
||||
|
|
|
@ -610,18 +610,19 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
FilterProgram UpdateSourceInternal(JobInfo job)
|
||||
{
|
||||
if (job.chain_outsize.Width == 0 || job.chain_outsize.Height == 0)
|
||||
{
|
||||
//this has to be a NOP, because lots of stuff will malfunction on a 0-sized viewport
|
||||
return null;
|
||||
}
|
||||
|
||||
//no drawing actually happens. it's important not to begin drawing on a control
|
||||
if (!job.simulate && !job.offscreen)
|
||||
{
|
||||
GLManager.Activate(CR_GraphicsControl);
|
||||
}
|
||||
|
||||
if (job.chain_outsize.Width == 0 || job.chain_outsize.Height == 0)
|
||||
{
|
||||
//this has to be a NOP, because lots of stuff will malfunction on a 0-sized viewport
|
||||
UpdateSourceDrawingWork(job); //but we still need to do this, because of vsync
|
||||
return null;
|
||||
}
|
||||
|
||||
IVideoProvider videoProvider = job.videoProvider;
|
||||
bool simulate = job.simulate;
|
||||
Size chain_outsize = job.chain_outsize;
|
||||
|
|
|
@ -389,7 +389,9 @@
|
|||
this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ZXSpectrumMediaMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ZXSpectrumTapesSubMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ZXSpectrumDisksSubMenu = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.MainStatusBar = new StatusStripEx();
|
||||
this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton();
|
||||
|
@ -462,8 +464,7 @@
|
|||
this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.timerMouseIdle = new System.Windows.Forms.Timer(this.components);
|
||||
this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.MainformMenu.SuspendLayout();
|
||||
this.MainStatusBar.SuspendLayout();
|
||||
this.MainFormContextMenu.SuspendLayout();
|
||||
|
@ -3454,7 +3455,8 @@
|
|||
//
|
||||
this.ZXSpectrumMediaMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ZXSpectrumTapesSubMenu,
|
||||
this.ZXSpectrumDisksSubMenu});
|
||||
this.ZXSpectrumDisksSubMenu,
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem});
|
||||
this.ZXSpectrumMediaMenuItem.Name = "ZXSpectrumMediaMenuItem";
|
||||
this.ZXSpectrumMediaMenuItem.Size = new System.Drawing.Size(201, 22);
|
||||
this.ZXSpectrumMediaMenuItem.Text = "Media";
|
||||
|
@ -3465,19 +3467,31 @@
|
|||
this.ZXSpectrumTapesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.zxt1ToolStripMenuItem});
|
||||
this.ZXSpectrumTapesSubMenu.Name = "ZXSpectrumTapesSubMenu";
|
||||
this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(152, 22);
|
||||
this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(159, 22);
|
||||
this.ZXSpectrumTapesSubMenu.Text = "Tapes";
|
||||
this.ZXSpectrumTapesSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumTapesSubMenu_DropDownOpened);
|
||||
//
|
||||
// zxt1ToolStripMenuItem
|
||||
//
|
||||
this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem";
|
||||
this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(94, 22);
|
||||
this.zxt1ToolStripMenuItem.Text = "zxt1";
|
||||
//
|
||||
// ZXSpectrumDisksSubMenu
|
||||
//
|
||||
this.ZXSpectrumDisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.zxt2ToolStripMenuItem});
|
||||
this.ZXSpectrumDisksSubMenu.Name = "ZXSpectrumDisksSubMenu";
|
||||
this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(152, 22);
|
||||
this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(159, 22);
|
||||
this.ZXSpectrumDisksSubMenu.Text = "Disks";
|
||||
this.ZXSpectrumDisksSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumDisksSubMenu_DropDownOpened);
|
||||
//
|
||||
// zxt2ToolStripMenuItem
|
||||
//
|
||||
this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem";
|
||||
this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.zxt2ToolStripMenuItem.Text = "zxt2";
|
||||
//
|
||||
// Atari7800HawkCoreMenuItem
|
||||
//
|
||||
this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem";
|
||||
|
@ -4104,17 +4118,12 @@
|
|||
this.timerMouseIdle.Interval = 2000;
|
||||
this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick);
|
||||
//
|
||||
// zxt1ToolStripMenuItem
|
||||
// ZXSpectrumExportSnapshotMenuItemMenuItem
|
||||
//
|
||||
this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem";
|
||||
this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.zxt1ToolStripMenuItem.Text = "zxt1";
|
||||
//
|
||||
// zxt2ToolStripMenuItem
|
||||
//
|
||||
this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem";
|
||||
this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.zxt2ToolStripMenuItem.Text = "zxt2";
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Name = "ZXSpectrumExportSnapshotMenuItemMenuItem";
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Size = new System.Drawing.Size(159, 22);
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Text = "Export Snapshot";
|
||||
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Click += new System.EventHandler(this.ZXSpectrumExportSnapshotMenuItemMenuItem_Click);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
|
@ -4591,5 +4600,6 @@
|
|||
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumDisksSubMenu;
|
||||
private System.Windows.Forms.ToolStripMenuItem zxt1ToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem zxt2ToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumExportSnapshotMenuItemMenuItem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2564,6 +2564,32 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
private void ZXSpectrumExportSnapshotMenuItemMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog zxSnapExpDialog = new SaveFileDialog();
|
||||
zxSnapExpDialog.RestoreDirectory = true;
|
||||
zxSnapExpDialog.Title = "EXPERIMENTAL - Export 3rd party snapshot formats";
|
||||
zxSnapExpDialog.DefaultExt = "szx";
|
||||
zxSnapExpDialog.Filter = "ZX-State files (*.szx)|*.szx";
|
||||
zxSnapExpDialog.SupportMultiDottedExtensions = true;
|
||||
|
||||
try
|
||||
{
|
||||
var res = zxSnapExpDialog.ShowDialog();
|
||||
if (res == DialogResult.OK)
|
||||
{
|
||||
var speccy = (ZXSpectrum)Emulator;
|
||||
var snap = speccy.GetSZXSnapshot();
|
||||
File.WriteAllBytes(zxSnapExpDialog.FileName, snap);
|
||||
//File.WriteAllText(zxSnapExpDialog.FileName, snap);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var ee = ex;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Help
|
||||
|
|
|
@ -1834,6 +1834,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
break;
|
||||
case "ZXSpectrum":
|
||||
zXSpectrumToolStripMenuItem.Visible = true;
|
||||
#if DEBUG
|
||||
ZXSpectrumExportSnapshotMenuItemMenuItem.Visible = true;
|
||||
#else
|
||||
ZXSpectrumExportSnapshotMenuItemMenuItem.Visible = false;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -4368,7 +4373,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
GenericCoreConfig.DoDialog(this, "PC-FX Settings");
|
||||
}
|
||||
|
||||
|
||||
private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording)
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
BizHawk.Common.TempFileCleaner.Start();
|
||||
BizHawk.Common.TempFileManager.Start();
|
||||
|
||||
|
||||
HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler();
|
||||
|
|
|
@ -900,6 +900,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap MoveBottom {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("MoveBottom", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
@ -930,6 +940,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap MoveTop {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("MoveTop", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
|
|
@ -1560,4 +1560,10 @@
|
|||
<data name="ZXSpectrumKeyboards" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\config\ControllerImages\ZXSpectrumKeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="MoveBottom" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\MoveBottom.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="MoveTop" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\MoveTop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
|
@ -6,14 +6,17 @@ namespace BizHawk.Client.EmuHawk
|
|||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ToolAttribute : Attribute
|
||||
{
|
||||
public ToolAttribute(bool released, string[] supportedSystems)
|
||||
public ToolAttribute(bool released, string[] supportedSystems, string[] unsupportedCores = null)
|
||||
{
|
||||
Released = released;
|
||||
SupportedSystems = supportedSystems;
|
||||
UnsupportedCores = unsupportedCores;
|
||||
}
|
||||
|
||||
public bool Released { get; private set; }
|
||||
|
||||
public IEnumerable<string> SupportedSystems { get; private set; }
|
||||
|
||||
public IEnumerable<string> UnsupportedCores { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,6 +308,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void Ok_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveSettings();
|
||||
|
||||
PathManager.RefreshTempPath();
|
||||
|
||||
GlobalWin.OSD.AddMessage("Path settings saved");
|
||||
Close();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
//Verify all wording in the error reports
|
||||
|
||||
|
||||
[Tool(released: true, supportedSystems: new[] { "GB", "GBA", "GEN", "N64", "NES", "PSX", "SAT", "SMS", "SNES" })]
|
||||
[Tool(released: true, supportedSystems: new[] { "GB", "GBA", "GEN", "N64", "NES", "PSX", "SAT", "SMS", "SNES" },
|
||||
unsupportedCores: new[] { "Snes9x" })]
|
||||
public partial class GameShark : Form, IToolForm, IToolFormAutoConfig
|
||||
{
|
||||
#region " Game Genie Dictionary "
|
||||
|
|
|
@ -391,6 +391,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
return GlobalWin.MainForm.DesktopLocation.Y;
|
||||
}
|
||||
|
||||
[LuaMethodExample("local incbhver = client.getversion( );")]
|
||||
[LuaMethod("getversion", "Returns the current stable BizHawk version")]
|
||||
public static string GetVersion()
|
||||
{
|
||||
return VersionInfo.Mainversion;
|
||||
}
|
||||
|
||||
[LuaMethodExample("local nlcliget = client.getavailabletools( );")]
|
||||
[LuaMethod("getavailabletools", "Returns a list of the tools currently open")]
|
||||
public LuaTable GetAvailableTools()
|
||||
|
|
|
@ -1314,7 +1314,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
ToolStripMenuItem sender = o as ToolStripMenuItem;
|
||||
foreach (ToolStripMenuItem menuItem in dummyObject.DropDownItems)
|
||||
{
|
||||
TasView.AllColumns.Find(c => c.Name == (string)menuItem.Tag).Visible = sender.Checked;
|
||||
menuItem.Checked ^= true;
|
||||
}
|
||||
|
||||
CurrentTasMovie.FlagChanges();
|
||||
|
@ -1342,7 +1342,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
ToolStripMenuItem sender = o as ToolStripMenuItem;
|
||||
foreach (ToolStripMenuItem menuItem in dummyObject.DropDownItems)
|
||||
{
|
||||
TasView.AllColumns.Find(c => c.Name == (string)menuItem.Tag).Visible = sender.Checked;
|
||||
menuItem.Checked ^= true;
|
||||
}
|
||||
|
||||
CurrentTasMovie.FlagChanges();
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Windows.Forms;
|
|||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common.ReflectionExtensions;
|
||||
using BizHawk.Client.EmuHawk.CoreExtensions;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
@ -743,13 +744,30 @@ namespace BizHawk.Client.EmuHawk
|
|||
.OfType<ToolAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
// If no supported systems mentioned assume all
|
||||
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
|
||||
// start with the assumption that if no supported systems are mentioned and no unsupported cores are mentioned
|
||||
// then this is available for all
|
||||
bool supported = true;
|
||||
|
||||
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
|
||||
{
|
||||
return attr.SupportedSystems.Contains(Global.Emulator.SystemId);
|
||||
}
|
||||
// supported systems are available
|
||||
supported = attr.SupportedSystems.Contains(Global.Emulator.SystemId);
|
||||
|
||||
return true;
|
||||
if (supported)
|
||||
{
|
||||
// check for a core not supported override
|
||||
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
|
||||
supported = false;
|
||||
}
|
||||
}
|
||||
else if (attr?.UnsupportedCores != null && attr.UnsupportedCores.Any())
|
||||
{
|
||||
// no supported system specified, but unsupported cores are
|
||||
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
|
||||
supported = false;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
// Note: Referencing these properties creates an instance of the tool and persists it. They should be referenced by type if this is not desired
|
||||
|
@ -973,7 +991,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
public void LoadGameGenieEc()
|
||||
{
|
||||
if (GlobalWin.Tools.IsAvailable<GameShark>())
|
||||
{
|
||||
{
|
||||
GlobalWin.Tools.Load<GameShark>();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -94,6 +94,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
get { return SelectedItems.Where(x => !x.IsSeparator); }
|
||||
}
|
||||
|
||||
private IEnumerable<Watch> SelectedSeparators
|
||||
{
|
||||
get
|
||||
{
|
||||
return SelectedItems.Where(x => x.IsSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Watch> Watches
|
||||
{
|
||||
get
|
||||
|
@ -427,6 +435,32 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
UpdateValues();
|
||||
}
|
||||
else if (SelectedSeparators.Any() && !duplicate)
|
||||
{
|
||||
var inputPrompt = new InputPrompt
|
||||
{
|
||||
Text = "Edit Separator",
|
||||
StartLocation = this.ChildPointToScreen(WatchListView),
|
||||
Message = "Separator Text:",
|
||||
TextInputType = InputPrompt.InputType.Text
|
||||
};
|
||||
|
||||
var result = inputPrompt.ShowHawkDialog();
|
||||
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
Changes();
|
||||
|
||||
for (int i = 0; i < SelectedSeparators.Count(); i++)
|
||||
{
|
||||
var sep = SelectedSeparators.ToList()[i];
|
||||
sep.Notes = inputPrompt.PromptText;
|
||||
_watches[indexes[i]] = sep;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateValues();
|
||||
}
|
||||
}
|
||||
|
||||
private string ComputeDisplayType(Watch w)
|
||||
|
@ -627,11 +661,21 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
text = "";
|
||||
|
||||
if (index >= _watches.Count || _watches[index].IsSeparator)
|
||||
if (index >= _watches.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_watches[index].IsSeparator)
|
||||
{
|
||||
if (WatchListView.Columns[column].Name == WatchList.ADDRESS)
|
||||
{
|
||||
text = _watches[index].Notes;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var columnName = WatchListView.Columns[column].Name;
|
||||
|
||||
switch (columnName)
|
||||
|
@ -733,6 +777,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
RemoveWatchMenuItem.Enabled =
|
||||
MoveUpMenuItem.Enabled =
|
||||
MoveDownMenuItem.Enabled =
|
||||
MoveTopMenuItem.Enabled =
|
||||
MoveBottomMenuItem.Enabled =
|
||||
SelectedIndices.Any();
|
||||
|
||||
PokeAddressMenuItem.Enabled =
|
||||
|
@ -913,7 +959,67 @@ namespace BizHawk.Client.EmuHawk
|
|||
WatchListView.ItemCount = _watches.Count;
|
||||
}
|
||||
|
||||
private void SelectAllMenuItem_Click(object sender, EventArgs e)
|
||||
private void MoveTopMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var indexes = SelectedIndices.ToList();
|
||||
if (!indexes.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < indexes.Count; i++)
|
||||
{
|
||||
var watch = _watches[indexes[i]];
|
||||
_watches.RemoveAt(indexes[i]);
|
||||
_watches.Insert(i, watch);
|
||||
indexes[i] = i;
|
||||
}
|
||||
|
||||
Changes();
|
||||
|
||||
WatchListView.SelectedIndices.Clear();
|
||||
foreach (var t in indexes)
|
||||
{
|
||||
WatchListView.SelectItem(t, true);
|
||||
}
|
||||
|
||||
WatchListView.ItemCount = _watches.Count;
|
||||
}
|
||||
|
||||
private void MoveBottomMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var indices = SelectedIndices.ToList();
|
||||
if (indices.Count == 0) // || indices.Last() == _watches.Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < indices.Count; i++)
|
||||
{
|
||||
var watch = _watches[indices[i] - i];
|
||||
_watches.RemoveAt(indices[i] - i);
|
||||
_watches.Insert(_watches.Count, watch);
|
||||
//_watches.Add(watch);
|
||||
//indices[i] = (_watches.Count - 1 - indices.Count) + i;
|
||||
}
|
||||
|
||||
var newInd = new List<int>();
|
||||
for (int i = 0, x = _watches.Count - indices.Count; i < indices.Count; i++, x++)
|
||||
{
|
||||
newInd.Add(x);
|
||||
}
|
||||
|
||||
WatchListView.SelectedIndices.Clear();
|
||||
foreach (var t in newInd)
|
||||
{
|
||||
WatchListView.SelectItem(t, true);
|
||||
}
|
||||
|
||||
Changes();
|
||||
WatchListView.ItemCount = _watches.Count;
|
||||
}
|
||||
|
||||
private void SelectAllMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
WatchListView.SelectAll();
|
||||
}
|
||||
|
@ -1066,6 +1172,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
InsertSeperatorContextMenuItem.Visible =
|
||||
MoveUpContextMenuItem.Visible =
|
||||
MoveDownContextMenuItem.Visible =
|
||||
MoveTopContextMenuItem.Visible =
|
||||
MoveBottomContextMenuItem.Visible =
|
||||
indexes.Count > 0;
|
||||
|
||||
ReadBreakpointContextMenuItem.Visible =
|
||||
|
@ -1231,5 +1339,5 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
base.GenericDragEnter(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,13 +120,66 @@
|
|||
<metadata name="ListViewContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>264, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="MoveTopContextMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>434, 17</value>
|
||||
</metadata>
|
||||
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>159, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="newToolStripButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
|
@ -215,6 +268,59 @@
|
|||
<metadata name="RamWatchMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<data name="MoveTopMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAA
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace BizHawk.Common
|
|||
public InstanceDll(string dllPath)
|
||||
{
|
||||
// copy the dll to a temp directory
|
||||
var path = TempFileCleaner.GetTempFilename(string.Format("{0}", Path.GetFileNameWithoutExtension(dllPath)),".dll",false);
|
||||
var path = TempFileManager.GetTempFilename(string.Format("{0}", Path.GetFileNameWithoutExtension(dllPath)),".dll",false);
|
||||
using (var stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.ReadWrite | FileShare.Delete, 4 * 1024, FileOptions.None))
|
||||
using (var sdll = File.OpenRead(dllPath))
|
||||
sdll.CopyTo(stream);
|
||||
|
@ -32,7 +32,7 @@ namespace BizHawk.Common
|
|||
var lastError = GetLastError();
|
||||
throw new InvalidOperationException($"Failed to load plugin {path}, error code: 0x{lastError:X}");
|
||||
}
|
||||
var newfname = TempFileCleaner.RenameTempFilenameForDelete(path);
|
||||
var newfname = TempFileManager.RenameTempFilenameForDelete(path);
|
||||
File.Move(path, newfname);
|
||||
}
|
||||
finally
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
@ -10,7 +12,7 @@ namespace BizHawk.Common
|
|||
/// Files shouldn't be named that unless they're safe to delete, but notably, they may stil be in use. That won't hurt this component.
|
||||
/// When they're no longer in use, this component will then be able to delete them.
|
||||
/// </summary>
|
||||
public static class TempFileCleaner
|
||||
public static class TempFileManager
|
||||
{
|
||||
// TODO - manage paths other than %temp%, make not static, or allow adding multiple paths to static instance
|
||||
|
||||
|
@ -41,7 +43,7 @@ namespace BizHawk.Common
|
|||
|
||||
public static void Start()
|
||||
{
|
||||
lock (typeof(TempFileCleaner))
|
||||
lock (typeof(TempFileManager))
|
||||
{
|
||||
if (thread != null)
|
||||
{
|
||||
|
@ -64,27 +66,49 @@ namespace BizHawk.Common
|
|||
|
||||
static void ThreadProc()
|
||||
{
|
||||
var di = new DirectoryInfo(Path.GetTempPath());
|
||||
//squirrely logic, trying not to create garbage
|
||||
HashSet<string> knownTempDirs = new HashSet<string>();
|
||||
List<DirectoryInfo> dis = new List<DirectoryInfo>();
|
||||
for (;;)
|
||||
{
|
||||
var fis = di.GetFiles("bizdelete-*");
|
||||
foreach (var fi in fis)
|
||||
lock (typeof(TempFileManager))
|
||||
{
|
||||
knownTempDirs.Add(Path.GetTempPath());
|
||||
if (dis.Count != knownTempDirs.Count)
|
||||
dis = knownTempDirs.Select(x => new DirectoryInfo(x)).ToList();
|
||||
}
|
||||
|
||||
foreach(var di in dis)
|
||||
{
|
||||
FileInfo[] fis = null;
|
||||
try
|
||||
{
|
||||
// SHUT. UP. THE. EXCEPTIONS.
|
||||
#if WINDOWS
|
||||
DeleteFileW(fi.FullName);
|
||||
#else
|
||||
fi.Delete();
|
||||
#endif
|
||||
fis = di.GetFiles("bizdelete-*");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if(fis != null)
|
||||
{
|
||||
foreach (var fi in fis)
|
||||
{
|
||||
try
|
||||
{
|
||||
// SHUT. UP. THE. EXCEPTIONS.
|
||||
#if WINDOWS
|
||||
DeleteFileW(fi.FullName);
|
||||
#else
|
||||
fi.Delete();
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// try not to do more than one thing per frame
|
||||
Thread.Sleep(100);
|
||||
// try not to do more than one thing per frame
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try not to slam the filesystem too hard, we dont want this to cause any hiccups
|
||||
|
@ -97,5 +121,13 @@ namespace BizHawk.Common
|
|||
}
|
||||
|
||||
static Thread thread;
|
||||
|
||||
public static void HelperSetTempPath(string path)
|
||||
{
|
||||
//yes... this is how we're doing it, for now, until it's proven to be troublesome
|
||||
Directory.CreateDirectory(path);
|
||||
Environment.SetEnvironmentVariable("TMP", path);
|
||||
Environment.SetEnvironmentVariable("TEMP", path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -288,11 +288,15 @@
|
|||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Memory.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Port.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum16K\ZX16.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCExtendedFloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFormat\CPCExtendedFloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFormat\CPCFloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\IPFFormat\IPFFloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\UDIFormat\UDI1_0FloppyDisk.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverter.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverterType.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Snapshot\SZX\SZX.Objects.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Snapshot\SZX\SZX.Methods.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\CSW\CswConverter.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\PZX\PzxConverter.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeCommand.cs" />
|
||||
|
|
|
@ -7,6 +7,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
public long TotalExecutedCycles;
|
||||
|
||||
private int EI_pending;
|
||||
// ZXHawk needs to be able to read this for zx-state snapshot export
|
||||
public int EIPending { get { return EI_pending; } }
|
||||
|
||||
public const ushort CBpre = 0;
|
||||
public const ushort EXTDpre = 1;
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// Activates a register
|
||||
/// </summary>
|
||||
int SelectedRegister { get; set; }
|
||||
|
||||
int[] ExportRegisters();
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the PSG
|
||||
|
|
|
@ -816,6 +816,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
fdd = new CPCFloppyDisk();
|
||||
found = fdd.ParseDisk(diskData);
|
||||
break;
|
||||
case DiskType.IPF:
|
||||
fdd = new IPFFloppyDisk();
|
||||
found = fdd.ParseDisk(diskData);
|
||||
break;
|
||||
case DiskType.UDI:
|
||||
fdd = new UDI1_0FloppyDisk();
|
||||
found = fdd.ParseDisk(diskData);
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
|
|
|
@ -167,6 +167,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for snapshot generation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int[] ExportRegisters()
|
||||
{
|
||||
return _registers;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
@ -176,6 +185,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (i == 6)
|
||||
_registers[i] = 0xff;
|
||||
else
|
||||
_registers[i] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
_noiseVal = 0x0FFFF;
|
||||
_outABC = 0;
|
||||
|
|
|
@ -389,6 +389,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
{
|
||||
// fetch instruction without incrementing pc
|
||||
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -141,6 +142,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
diskImages.Add(m);
|
||||
Spectrum._diskInfo.Add(Spectrum._gameInfo[cnt]);
|
||||
break;
|
||||
case SpectrumMediaType.DiskDoubleSided:
|
||||
// this is a bit tricky. we will attempt to parse the double sided disk image byte array,
|
||||
// then output two separate image byte arrays
|
||||
List<byte[]> working = new List<byte[]>();
|
||||
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DiskType.CPCExtended:
|
||||
found = CPCExtendedFloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
case DiskType.CPC:
|
||||
found = CPCFloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
case DiskType.UDI:
|
||||
found = UDI1_0FloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
// add side 1
|
||||
diskImages.Add(working[0]);
|
||||
// add side 2
|
||||
diskImages.Add(working[1]);
|
||||
|
||||
Common.GameInfo one = new Common.GameInfo();
|
||||
Common.GameInfo two = new Common.GameInfo();
|
||||
var gi = Spectrum._gameInfo[cnt];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Common.GameInfo work = new Common.GameInfo();
|
||||
if (i == 0)
|
||||
{
|
||||
work = one;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
work = two;
|
||||
}
|
||||
|
||||
work.FirmwareHash = gi.FirmwareHash;
|
||||
work.Hash = gi.Hash;
|
||||
work.Name = gi.Name + " (Parsed Side " + (i + 1) + ")";
|
||||
work.Region = gi.Region;
|
||||
work.NotInDatabase = gi.NotInDatabase;
|
||||
work.Status = gi.Status;
|
||||
work.System = gi.System;
|
||||
|
||||
Spectrum._diskInfo.Add(work);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
|
@ -192,13 +253,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC"))
|
||||
{
|
||||
// spectrum .dsk disk file
|
||||
return SpectrumMediaType.Disk;
|
||||
// check for number of sides
|
||||
var sides = data[0x31];
|
||||
if (sides == 1)
|
||||
return SpectrumMediaType.Disk;
|
||||
else
|
||||
return SpectrumMediaType.DiskDoubleSided;
|
||||
}
|
||||
if (hdr.ToUpper().StartsWith("FDI"))
|
||||
{
|
||||
// spectrum .fdi disk file
|
||||
return SpectrumMediaType.Disk;
|
||||
}
|
||||
if (hdr.ToUpper().StartsWith("CAPS"))
|
||||
{
|
||||
// IPF format file
|
||||
return SpectrumMediaType.Disk;
|
||||
}
|
||||
if (hdr.ToUpper().StartsWith("UDI!") && data[0x08] == 0)
|
||||
{
|
||||
// UDI v1.0
|
||||
if (hdr.StartsWith("udi!"))
|
||||
{
|
||||
throw new NotSupportedException("ZXHawk currently does not supported UDIv1.0 with compression.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data[0x0A] == 0x01)
|
||||
return SpectrumMediaType.DiskDoubleSided;
|
||||
else
|
||||
return SpectrumMediaType.Disk;
|
||||
}
|
||||
}
|
||||
|
||||
// tape checking
|
||||
if (hdr.ToUpper().StartsWith("ZXTAPE!"))
|
||||
|
@ -231,6 +317,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
{
|
||||
None,
|
||||
Tape,
|
||||
Disk
|
||||
Disk,
|
||||
DiskDoubleSided
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// Signs that the shadow screen is now displaying
|
||||
/// Note: normal screen memory in RAM5 is not altered, the ULA just outputs Screen1 instead (RAM7)
|
||||
/// </summary>
|
||||
protected bool SHADOWPaged;
|
||||
public bool SHADOWPaged;
|
||||
|
||||
/// <summary>
|
||||
/// Index of the current RAM page
|
||||
|
@ -66,22 +66,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <summary>
|
||||
/// High bit of the ROM selection (in normal paging mode)
|
||||
/// </summary>
|
||||
protected bool ROMhigh = false;
|
||||
public bool ROMhigh = false;
|
||||
|
||||
/// <summary>
|
||||
/// Low bit of the ROM selection (in normal paging mode)
|
||||
/// </summary>
|
||||
protected bool ROMlow = false;
|
||||
public bool ROMlow = false;
|
||||
|
||||
/// <summary>
|
||||
/// Signs that the +2a/+3 special paging mode is activated
|
||||
/// </summary>
|
||||
protected bool SpecialPagingMode;
|
||||
public bool SpecialPagingMode;
|
||||
|
||||
/// <summary>
|
||||
/// Index of the current special paging mode (0-3)
|
||||
/// </summary>
|
||||
protected int PagingConfiguration;
|
||||
public int PagingConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// The last byte that was read after contended cycles
|
||||
|
|
|
@ -17,6 +17,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
set { LastULAOutByte = value; }
|
||||
}
|
||||
|
||||
public byte Last7ffd;
|
||||
public byte LastFe;
|
||||
public byte Last1ffd;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified port address
|
||||
/// </summary>
|
||||
|
|
|
@ -96,6 +96,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// but it is only partially decoded so it actually responds to any port with bits 1 and 15 reset
|
||||
if (portBits[1] == false && portBits[15] == false)
|
||||
{
|
||||
Last7ffd = value;
|
||||
|
||||
// if paging is disabled then all writes to this port are ignored until the next reboot
|
||||
if (!PagingDisabled)
|
||||
{
|
||||
|
@ -136,6 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// Only even addresses address the ULA
|
||||
if (lowBitReset)
|
||||
{
|
||||
LastFe = value;
|
||||
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set
|
||||
if (!portBits[1] && !portBits[15] && portBits[14])
|
||||
{
|
||||
Last7ffd = value;
|
||||
|
||||
if (!PagingDisabled)
|
||||
{
|
||||
// bits 0, 1, 2 select the RAM page
|
||||
|
@ -97,6 +99,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set
|
||||
if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15])
|
||||
{
|
||||
Last1ffd = value;
|
||||
|
||||
if (!PagingDisabled)
|
||||
{
|
||||
if (!bits[0])
|
||||
|
@ -134,6 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// Only even addresses address the ULA
|
||||
if (lowBitReset)
|
||||
{
|
||||
LastFe = value;
|
||||
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
|
|
|
@ -68,6 +68,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <param name="value"></param>
|
||||
public override void WritePort(ushort port, byte value)
|
||||
{
|
||||
if (port == 0x7ffd)
|
||||
Last7ffd = value;
|
||||
else if (port == 0x1ffd)
|
||||
Last1ffd = value;
|
||||
else if ((port & 0x01) == 0)
|
||||
LastFe = value;
|
||||
|
||||
// get a BitArray of the port
|
||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
||||
// get a BitArray of the value byte
|
||||
|
|
|
@ -71,6 +71,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
if ((port & 0x0001) != 0)
|
||||
return;
|
||||
|
||||
LastFe = value;
|
||||
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
|
@ -53,6 +55,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
throw new System.NotImplementedException(sbm.ToString());
|
||||
}
|
||||
|
||||
if (DiskHeader.NumberOfTracks > 42)
|
||||
{
|
||||
StringBuilder sbm = new StringBuilder();
|
||||
sbm.AppendLine();
|
||||
sbm.AppendLine();
|
||||
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
|
||||
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
|
||||
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
|
||||
throw new System.NotImplementedException(sbm.ToString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
|
||||
{
|
||||
DiskHeader.TrackSizes[i] = data[pos++] * 256;
|
||||
|
@ -144,6 +157,88 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 16);
|
||||
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] S0 = new byte[data.Length];
|
||||
byte[] S1 = new byte[data.Length];
|
||||
|
||||
// disk info block
|
||||
Array.Copy(data, 0, S0, 0, 0x100);
|
||||
Array.Copy(data, 0, S1, 0, 0x100);
|
||||
// change side number
|
||||
S0[0x31] = 1;
|
||||
S1[0x31] = 1;
|
||||
|
||||
// extended format can have different track sizes
|
||||
int[] trkSizes = new int[data[0x30] * data[0x31]];
|
||||
|
||||
int pos = 0x34;
|
||||
for (int i = 0; i < data[0x30] * data[0x31]; i++)
|
||||
{
|
||||
trkSizes[i] = data[pos] * 256;
|
||||
// clear destination trk sizes (will be added later)
|
||||
S0[pos] = 0;
|
||||
S1[pos] = 0;
|
||||
pos++;
|
||||
}
|
||||
|
||||
// start at track info blocks
|
||||
int mPos = 0x100;
|
||||
int s0Pos = 0x100;
|
||||
int s0tCount = 0;
|
||||
int s1tCount = 0;
|
||||
int s1Pos = 0x100;
|
||||
int tCount = 0;
|
||||
|
||||
while (tCount < data[0x30] * data[0x31])
|
||||
{
|
||||
// which side is this?
|
||||
var side = data[mPos + 0x11];
|
||||
if (side == 0)
|
||||
{
|
||||
// side 1
|
||||
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
|
||||
s0Pos += trkSizes[tCount];
|
||||
// trk size table
|
||||
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
|
||||
}
|
||||
else if (side == 1)
|
||||
{
|
||||
// side 2
|
||||
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
|
||||
s1Pos += trkSizes[tCount];
|
||||
// trk size table
|
||||
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
|
||||
}
|
||||
|
||||
mPos += trkSizes[tCount++];
|
||||
}
|
||||
|
||||
byte[] s0final = new byte[s0Pos];
|
||||
byte[] s1final = new byte[s1Pos];
|
||||
Array.Copy(S0, 0, s0final, 0, s0Pos);
|
||||
Array.Copy(S1, 0, s1final, 0, s1Pos);
|
||||
|
||||
results.Add(s0final);
|
||||
results.Add(s1final);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
|
@ -1,5 +1,7 @@
|
|||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
|
@ -53,6 +55,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
throw new System.NotImplementedException(sbm.ToString());
|
||||
}
|
||||
|
||||
if (DiskHeader.NumberOfTracks > 42)
|
||||
{
|
||||
StringBuilder sbm = new StringBuilder();
|
||||
sbm.AppendLine();
|
||||
sbm.AppendLine();
|
||||
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
|
||||
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
|
||||
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
|
||||
throw new System.NotImplementedException(sbm.ToString());
|
||||
}
|
||||
|
||||
// standard CPC format all track sizes are the same in the image
|
||||
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
|
||||
{
|
||||
|
@ -149,6 +162,77 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 16);
|
||||
if (!ident.ToUpper().Contains("MV - CPC"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] S0 = new byte[data.Length];
|
||||
byte[] S1 = new byte[data.Length];
|
||||
|
||||
// disk info block
|
||||
Array.Copy(data, 0, S0, 0, 0x100);
|
||||
Array.Copy(data, 0, S1, 0, 0x100);
|
||||
// change side number
|
||||
S0[0x31] = 1;
|
||||
S1[0x31] = 1;
|
||||
|
||||
var trkSize = MediaConverter.GetWordValue(data, 0x32);
|
||||
|
||||
// start at track info blocks
|
||||
int mPos = 0x100;
|
||||
int s0Pos = 0x100;
|
||||
int s1Pos = 0x100;
|
||||
|
||||
var numTrks = data[0x30];
|
||||
var numSides = data[0x31];
|
||||
|
||||
while (mPos < trkSize * data[0x30] * data[0x31])
|
||||
{
|
||||
// which side is this?
|
||||
var side = data[mPos + 0x11];
|
||||
if (side == 0)
|
||||
{
|
||||
// side 1
|
||||
Array.Copy(data, mPos, S0, s0Pos, trkSize);
|
||||
s0Pos += trkSize;
|
||||
}
|
||||
else if (side == 1)
|
||||
{
|
||||
// side 2
|
||||
Array.Copy(data, mPos, S1, s1Pos, trkSize);
|
||||
s1Pos += trkSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
mPos += trkSize;
|
||||
}
|
||||
|
||||
byte[] s0final = new byte[s0Pos];
|
||||
byte[] s1final = new byte[s1Pos];
|
||||
Array.Copy(S0, 0, s0final, 0, s0Pos);
|
||||
Array.Copy(S1, 0, s1final, 0, s1Pos);
|
||||
|
||||
results.Add(s0final);
|
||||
results.Add(s1final);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
|
@ -14,6 +14,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <summary>
|
||||
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
|
||||
/// </summary>
|
||||
CPCExtended
|
||||
CPCExtended,
|
||||
|
||||
/// <summary>
|
||||
/// Interchangeable Preservation Format
|
||||
/// </summary>
|
||||
IPF,
|
||||
|
||||
/// <summary>
|
||||
/// Ultra Disk Image Format (v1.0)
|
||||
/// </summary>
|
||||
UDI,
|
||||
|
||||
/// <summary>
|
||||
/// Ultra Disk Image Format (v1.1)
|
||||
/// </summary>
|
||||
UDIv1_1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -602,13 +602,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
public byte NumberOfSectors { get; set; }
|
||||
public byte GAP3Length { get; set; }
|
||||
public byte FillerByte { get; set; }
|
||||
public Sector[] Sectors { get; set; }
|
||||
public virtual Sector[] Sectors { get; set; }
|
||||
|
||||
#region UDI
|
||||
|
||||
public virtual byte TrackType { get; set; }
|
||||
public virtual int TLEN { get; set; }
|
||||
public virtual int CLEN { get { return TLEN / 8 + (TLEN % 8 / 7) / 8; } }
|
||||
public virtual byte[] TrackData { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Presents a contiguous byte array of all sector data for this track
|
||||
/// (including any multiple weak/random data)
|
||||
/// </summary>
|
||||
public byte[] TrackSectorData
|
||||
public virtual byte[] TrackSectorData
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -626,15 +635,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
|
||||
public class Sector
|
||||
{
|
||||
public byte TrackNumber { get; set; }
|
||||
public byte SideNumber { get; set; }
|
||||
public byte SectorID { get; set; }
|
||||
public byte SectorSize { get; set; }
|
||||
public byte Status1 { get; set; }
|
||||
public byte Status2 { get; set; }
|
||||
public int ActualDataByteLength { get; set; }
|
||||
public byte[] SectorData { get; set; }
|
||||
public bool ContainsMultipleWeakSectors { get; set; }
|
||||
public virtual byte TrackNumber { get; set; }
|
||||
public virtual byte SideNumber { get; set; }
|
||||
public virtual byte SectorID { get; set; }
|
||||
public virtual byte SectorSize { get; set; }
|
||||
public virtual byte Status1 { get; set; }
|
||||
public virtual byte Status2 { get; set; }
|
||||
public virtual int ActualDataByteLength { get; set; }
|
||||
public virtual byte[] SectorData { get; set; }
|
||||
public virtual bool ContainsMultipleWeakSectors { get; set; }
|
||||
|
||||
public int WeakReadIndex = 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,461 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public class IPFFloppyDisk : FloppyDisk
|
||||
{
|
||||
/// <summary>
|
||||
/// The format type
|
||||
/// </summary>
|
||||
public override DiskType DiskFormatType => DiskType.IPF;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse incoming disk data
|
||||
/// </summary>
|
||||
/// <param name="diskData"></param>
|
||||
/// <returns>
|
||||
/// TRUE: disk parsed
|
||||
/// FALSE: unable to parse disk
|
||||
/// </returns>
|
||||
public override bool ParseDisk(byte[] data)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 16);
|
||||
|
||||
if (!ident.ToUpper().Contains("CAPS"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
|
||||
List<IPFBlock> blocks = new List<IPFBlock>();
|
||||
|
||||
while (pos < data.Length)
|
||||
{
|
||||
try
|
||||
{
|
||||
var block = IPFBlock.ParseNextBlock(ref pos, this, data, blocks);
|
||||
|
||||
if (block == null)
|
||||
{
|
||||
// EOF
|
||||
break;
|
||||
}
|
||||
|
||||
if (block.RecordType == RecordHeaderType.None)
|
||||
{
|
||||
// unknown block
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// now process the blocks
|
||||
var infoBlock = blocks.Where(a => a.RecordType == RecordHeaderType.INFO).FirstOrDefault();
|
||||
var IMGEblocks = blocks.Where(a => a.RecordType == RecordHeaderType.IMGE).ToList();
|
||||
var DATAblocks = blocks.Where(a => a.RecordType == RecordHeaderType.DATA).ToList();
|
||||
|
||||
DiskHeader.NumberOfTracks = (byte)(IMGEblocks.Count());
|
||||
DiskHeader.NumberOfSides = (byte)(infoBlock.INFOmaxSide + 1);
|
||||
DiskTracks = new Track[DiskHeader.NumberOfTracks];
|
||||
|
||||
for (int t = 0; t < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; t++)
|
||||
{
|
||||
// each imge block represents one track
|
||||
var img = IMGEblocks[t];
|
||||
DiskTracks[t] = new Track();
|
||||
var trk = DiskTracks[t];
|
||||
|
||||
var blockCount = img.IMGEblockCount;
|
||||
var dataBlock = DATAblocks.Where(a => a.DATAdataKey == img.IMGEdataKey).FirstOrDefault();
|
||||
|
||||
trk.SideNumber = (byte)img.IMGEside;
|
||||
trk.TrackNumber = (byte)img.IMGEtrack;
|
||||
|
||||
trk.Sectors = new Sector[blockCount];
|
||||
|
||||
// process data block descriptors
|
||||
int p = 0;
|
||||
for (int d = 0; d < blockCount; d++)
|
||||
{
|
||||
var extraDataAreaStart = 32 * blockCount;
|
||||
trk.Sectors[d] = new Sector();
|
||||
var sector = trk.Sectors[d];
|
||||
|
||||
int dataBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
int gapBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
int dataBytes;
|
||||
int gapBytes;
|
||||
int gapOffset;
|
||||
int cellType;
|
||||
if (infoBlock.INFOencoderType == 1)
|
||||
{
|
||||
dataBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
gapBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
}
|
||||
else if (infoBlock.INFOencoderType == 2)
|
||||
{
|
||||
gapOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
cellType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
}
|
||||
int encoderType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
int? blockFlags = null;
|
||||
if (infoBlock.INFOencoderType == 2)
|
||||
{
|
||||
blockFlags = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p);
|
||||
}
|
||||
p += 4;
|
||||
|
||||
int gapDefault = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
int dataOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
|
||||
|
||||
// gap stream elements
|
||||
if (infoBlock.INFOencoderType == 2 && gapBits != 0 && blockFlags != null)
|
||||
{
|
||||
if (!blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
|
||||
{
|
||||
// no gap stream
|
||||
}
|
||||
if (!blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
|
||||
{
|
||||
// Forward gap stream list only
|
||||
}
|
||||
if (blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
|
||||
{
|
||||
// Backward gap stream list only
|
||||
}
|
||||
if (blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
|
||||
{
|
||||
// Forward and Backward stream lists
|
||||
}
|
||||
}
|
||||
|
||||
// data stream elements
|
||||
if (dataBits != 0)
|
||||
{
|
||||
var dsLocation = dataOffset;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
byte dataHead = dataBlock.DATAextraDataRaw[dsLocation++];
|
||||
if (dataHead == 0)
|
||||
{
|
||||
// end of data stream list
|
||||
break;
|
||||
}
|
||||
|
||||
var sampleSize = ((dataHead & 0xE0) >> 5);
|
||||
var dataType = dataHead & 0x1F;
|
||||
byte[] dSize = new byte[sampleSize];
|
||||
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dSize, 0, sampleSize);
|
||||
var dataSize = MediaConverter.GetBEInt32FromByteArray(dSize);
|
||||
dsLocation += dSize.Length;
|
||||
int dataLen;
|
||||
byte[] dataStream = new byte[0];
|
||||
|
||||
if (blockFlags != null && blockFlags.Value.Bit(2))
|
||||
{
|
||||
// bits
|
||||
if (dataType != 5)
|
||||
{
|
||||
dataLen = dataSize / 8;
|
||||
if (dataSize % 8 != 0)
|
||||
{
|
||||
// bits left over
|
||||
}
|
||||
dataStream = new byte[dataLen];
|
||||
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// bytes
|
||||
if (dataType != 5)
|
||||
{
|
||||
dataStream = new byte[dataSize];
|
||||
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
// dataStream[] now contains the data
|
||||
switch (dataType)
|
||||
{
|
||||
// SYNC
|
||||
case 1:
|
||||
break;
|
||||
// DATA
|
||||
case 2:
|
||||
if (dataStream.Length == 7)
|
||||
{
|
||||
// ID
|
||||
// first byte IAM
|
||||
sector.TrackNumber = dataStream[1];
|
||||
sector.SideNumber = dataStream[2];
|
||||
sector.SectorID = dataStream[3];
|
||||
sector.SectorSize = dataStream[4];
|
||||
}
|
||||
else if (dataStream.Length > 255)
|
||||
{
|
||||
// DATA
|
||||
// first byte DAM
|
||||
if (dataStream[0] == 0xF8)
|
||||
{
|
||||
// deleted address mark
|
||||
//sector.Status1
|
||||
}
|
||||
sector.SectorData = new byte[dataStream.Length - 1 - 2];
|
||||
Array.Copy(dataStream, 1, sector.SectorData, 0, dataStream.Length - 1 - 2);
|
||||
}
|
||||
break;
|
||||
// GAP
|
||||
case 3:
|
||||
break;
|
||||
// RAW
|
||||
case 4:
|
||||
break;
|
||||
// FUZZY
|
||||
case 5:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
dsLocation += dataStream.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public class IPFBlock
|
||||
{
|
||||
public RecordHeaderType RecordType;
|
||||
public int BlockLength;
|
||||
public int CRC;
|
||||
public byte[] RawBlockData;
|
||||
public int StartPos;
|
||||
|
||||
#region INFO
|
||||
|
||||
public int INFOmediaType;
|
||||
public int INFOencoderType;
|
||||
public int INFOencoderRev;
|
||||
public int INFOfileKey;
|
||||
public int INFOfileRev;
|
||||
public int INFOorigin;
|
||||
public int INFOminTrack;
|
||||
public int INFOmaxTrack;
|
||||
public int INFOminSide;
|
||||
public int INFOmaxSide;
|
||||
public int INFOcreationDate;
|
||||
public int INFOcreationTime;
|
||||
public int INFOplatform1;
|
||||
public int INFOplatform2;
|
||||
public int INFOplatform3;
|
||||
public int INFOplatform4;
|
||||
public int INFOdiskNumber;
|
||||
public int INFOcreatorId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMGE
|
||||
|
||||
public int IMGEtrack;
|
||||
public int IMGEside;
|
||||
public int IMGEdensity;
|
||||
public int IMGEsignalType;
|
||||
public int IMGEtrackBytes;
|
||||
public int IMGEstartBytePos;
|
||||
public int IMGEstartBitPos;
|
||||
public int IMGEdataBits;
|
||||
public int IMGEgapBits;
|
||||
public int IMGEtrackBits;
|
||||
public int IMGEblockCount;
|
||||
public int IMGEencoderProcess;
|
||||
public int IMGEtrackFlags;
|
||||
public int IMGEdataKey;
|
||||
|
||||
#endregion
|
||||
|
||||
#region DATA
|
||||
|
||||
public int DATAlength;
|
||||
public int DATAbitSize;
|
||||
public int DATAcrc;
|
||||
public int DATAdataKey;
|
||||
public byte[] DATAextraDataRaw;
|
||||
|
||||
#endregion
|
||||
|
||||
public static IPFBlock ParseNextBlock(ref int startPos, FloppyDisk disk, byte[] data, List<IPFBlock> blockCollection)
|
||||
{
|
||||
IPFBlock ipf = new IPFBlock();
|
||||
ipf.StartPos = startPos;
|
||||
|
||||
if (startPos >= data.Length)
|
||||
{
|
||||
// EOF
|
||||
return null;
|
||||
}
|
||||
|
||||
// assume the startPos passed in is actually the start of a new block
|
||||
// look for record header ident
|
||||
string ident = Encoding.ASCII.GetString(data, startPos, 4);
|
||||
startPos += 4;
|
||||
try
|
||||
{
|
||||
ipf.RecordType = (RecordHeaderType)Enum.Parse(typeof(RecordHeaderType), ident);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ipf.RecordType = RecordHeaderType.None;
|
||||
}
|
||||
|
||||
// setup for actual block size
|
||||
ipf.BlockLength = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.CRC = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.RawBlockData = new byte[ipf.BlockLength];
|
||||
Array.Copy(data, ipf.StartPos, ipf.RawBlockData, 0, ipf.BlockLength);
|
||||
|
||||
switch (ipf.RecordType)
|
||||
{
|
||||
// Nothing to process / unknown
|
||||
// just move ahead
|
||||
case RecordHeaderType.CAPS:
|
||||
case RecordHeaderType.TRCK:
|
||||
case RecordHeaderType.DUMP:
|
||||
case RecordHeaderType.CTEI:
|
||||
case RecordHeaderType.CTEX:
|
||||
default:
|
||||
startPos = ipf.StartPos + ipf.BlockLength;
|
||||
break;
|
||||
|
||||
// INFO block
|
||||
case RecordHeaderType.INFO:
|
||||
// INFO header is followed immediately by an INFO block
|
||||
ipf.INFOmediaType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOencoderType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOencoderRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOfileKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOfileRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOorigin = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOminTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOmaxTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOminSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOmaxSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOcreationDate = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOcreationTime = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOplatform1 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOplatform2 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOplatform3 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOplatform4 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOdiskNumber = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.INFOcreatorId = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
startPos += 12; // reserved
|
||||
break;
|
||||
|
||||
case RecordHeaderType.IMGE:
|
||||
ipf.IMGEtrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEside = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEdensity = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEsignalType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEtrackBytes = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEstartBytePos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEstartBitPos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEdataBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEgapBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEtrackBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEblockCount = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEencoderProcess = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEtrackFlags = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.IMGEdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
startPos += 12; // reserved
|
||||
break;
|
||||
|
||||
case RecordHeaderType.DATA:
|
||||
ipf.DATAlength = MediaConverter.GetBEInt32(data, startPos);
|
||||
if (ipf.DATAlength == 0)
|
||||
{
|
||||
ipf.DATAextraDataRaw = new byte[0];
|
||||
ipf.DATAlength = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ipf.DATAextraDataRaw = new byte[ipf.DATAlength];
|
||||
}
|
||||
startPos += 4;
|
||||
ipf.DATAbitSize = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.DATAcrc = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
ipf.DATAdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
|
||||
|
||||
if (ipf.DATAlength != 0)
|
||||
{
|
||||
Array.Copy(data, startPos, ipf.DATAextraDataRaw, 0, ipf.DATAlength);
|
||||
}
|
||||
|
||||
startPos += ipf.DATAlength;
|
||||
break;
|
||||
}
|
||||
|
||||
return ipf;
|
||||
}
|
||||
}
|
||||
|
||||
public enum RecordHeaderType
|
||||
{
|
||||
None,
|
||||
CAPS,
|
||||
DUMP,
|
||||
DATA,
|
||||
TRCK,
|
||||
INFO,
|
||||
IMGE,
|
||||
CTEI,
|
||||
CTEX,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
||||
/// <param name="ser"></param>
|
||||
public override void SyncState(Serializer ser)
|
||||
{
|
||||
ser.BeginSection("Plus3FloppyDisk");
|
||||
|
||||
ser.Sync("CylinderCount", ref CylinderCount);
|
||||
ser.Sync("SideCount", ref SideCount);
|
||||
ser.Sync("BytesPerTrack", ref BytesPerTrack);
|
||||
ser.Sync("WriteProtected", ref WriteProtected);
|
||||
ser.SyncEnum("Protection", ref Protection);
|
||||
|
||||
ser.Sync("DirtyData", ref DirtyData);
|
||||
if (DirtyData)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// sync deterministic track and sector counters
|
||||
ser.Sync(" _randomCounter", ref _randomCounter);
|
||||
RandomCounter = _randomCounter;
|
||||
|
||||
ser.EndSection();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
using BizHawk.Common;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public class UDI1_0FloppyDisk : FloppyDisk
|
||||
{
|
||||
/// <summary>
|
||||
/// The format type
|
||||
/// </summary>
|
||||
public override DiskType DiskFormatType => DiskType.UDI;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse incoming disk data
|
||||
/// </summary>
|
||||
/// <param name="diskData"></param>
|
||||
/// <returns>
|
||||
/// TRUE: disk parsed
|
||||
/// FALSE: unable to parse disk
|
||||
/// </returns>
|
||||
public override bool ParseDisk(byte[] data)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 4);
|
||||
|
||||
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[0x08] != 0)
|
||||
{
|
||||
// wrong version
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ident == "udi!")
|
||||
{
|
||||
// cant handle compression yet
|
||||
return false;
|
||||
}
|
||||
|
||||
DiskHeader.DiskIdent = ident;
|
||||
DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1);
|
||||
DiskHeader.NumberOfSides = (byte)(data[0x0A] + 1);
|
||||
|
||||
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
|
||||
|
||||
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
|
||||
|
||||
// ignore extended header
|
||||
var extHdrSize = MediaConverter.GetInt32(data, 0x0C);
|
||||
int pos = 0x10 + extHdrSize;
|
||||
|
||||
// process track information
|
||||
for (int t = 0; t < DiskHeader.NumberOfTracks; t++)
|
||||
{
|
||||
DiskTracks[t] = new UDIv1Track();
|
||||
DiskTracks[t].TrackNumber = (byte)t;
|
||||
DiskTracks[t].SideNumber = 0;
|
||||
DiskTracks[t].TrackType = data[pos++];
|
||||
DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2;
|
||||
DiskTracks[t].TrackData = new byte[DiskTracks[t].TLEN + DiskTracks[t].CLEN];
|
||||
Array.Copy(data, pos, DiskTracks[t].TrackData, 0, DiskTracks[t].TLEN + DiskTracks[t].CLEN);
|
||||
pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
|
||||
{
|
||||
// look for standard magic string
|
||||
string ident = Encoding.ASCII.GetString(data, 0, 4);
|
||||
|
||||
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
|
||||
{
|
||||
// incorrect format
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[0x08] != 0)
|
||||
{
|
||||
// wrong version
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ident == "udi!")
|
||||
{
|
||||
// cant handle compression yet
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] S0 = new byte[data.Length];
|
||||
byte[] S1 = new byte[data.Length];
|
||||
|
||||
// header
|
||||
var extHdr = MediaConverter.GetInt32(data, 0x0C);
|
||||
Array.Copy(data, 0, S0, 0, 0x10 + extHdr);
|
||||
Array.Copy(data, 0, S1, 0, 0x10 + extHdr);
|
||||
// change side number
|
||||
S0[0x0A] = 0;
|
||||
S1[0x0A] = 0;
|
||||
|
||||
int pos = 0x10 + extHdr;
|
||||
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
|
||||
|
||||
int s0Pos = pos;
|
||||
int s1Pos = pos;
|
||||
|
||||
// process track information
|
||||
for (int t = 0; t < (data[0x09] + 1) * 2; t++)
|
||||
{
|
||||
var TLEN = MediaConverter.GetWordValue(data, pos + 1);
|
||||
var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8;
|
||||
var blockSize = TLEN + CLEN + 3;
|
||||
|
||||
// 2 sided image: side 0 tracks will all have t as an even number
|
||||
try
|
||||
{
|
||||
if (t == 0 || t % 2 == 0)
|
||||
{
|
||||
Array.Copy(data, pos, S0, s0Pos, blockSize);
|
||||
s0Pos += blockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(data, pos, S1, s1Pos, blockSize);
|
||||
s1Pos += blockSize;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
pos += blockSize;
|
||||
}
|
||||
|
||||
// skip checkum bytes for now
|
||||
|
||||
byte[] s0final = new byte[s0Pos];
|
||||
byte[] s1final = new byte[s1Pos];
|
||||
Array.Copy(S0, 0, s0final, 0, s0Pos);
|
||||
Array.Copy(S1, 0, s1final, 0, s1Pos);
|
||||
|
||||
results.Add(s0final);
|
||||
results.Add(s1final);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public class UDIv1Track : Track
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse the UDI TrackData byte[] array into sector objects
|
||||
/// </summary>
|
||||
public override Sector[] Sectors
|
||||
{
|
||||
get
|
||||
{
|
||||
List<UDIv1Sector> secs = new List<UDIv1Sector>();
|
||||
var datas = TrackData.Skip(3).Take(TLEN).ToArray();
|
||||
var clocks = new BitArray(TrackData.Skip(3 + TLEN).Take(CLEN).ToArray());
|
||||
|
||||
return secs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UDIv1Sector : Sector
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// State serlialization
|
||||
/// </summary>
|
||||
/// <param name="ser"></param>
|
||||
public override void SyncState(Serializer ser)
|
||||
{
|
||||
ser.BeginSection("Plus3FloppyDisk");
|
||||
|
||||
ser.Sync("CylinderCount", ref CylinderCount);
|
||||
ser.Sync("SideCount", ref SideCount);
|
||||
ser.Sync("BytesPerTrack", ref BytesPerTrack);
|
||||
ser.Sync("WriteProtected", ref WriteProtected);
|
||||
ser.SyncEnum("Protection", ref Protection);
|
||||
|
||||
ser.Sync("DirtyData", ref DirtyData);
|
||||
if (DirtyData)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// sync deterministic track and sector counters
|
||||
ser.Sync(" _randomCounter", ref _randomCounter);
|
||||
RandomCounter = _randomCounter;
|
||||
|
||||
ser.EndSection();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
|
@ -89,11 +91,69 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <param name="buf"></param>
|
||||
/// <param name="offsetIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetInt32(byte[] buf, int offsetIndex)
|
||||
public static int GetInt32(byte[] buf, int offsetIndex)
|
||||
{
|
||||
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an int32 from a byte array based on offset (in BIG ENDIAN format)
|
||||
/// </summary>
|
||||
/// <param name="buf"></param>
|
||||
/// <param name="offsetIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetBEInt32(byte[] buf, int offsetIndex)
|
||||
{
|
||||
byte[] b = new byte[4];
|
||||
Array.Copy(buf, offsetIndex, b, 0, 4);
|
||||
byte[] buffer = b.Reverse().ToArray();
|
||||
int pos = 0;
|
||||
return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format)
|
||||
/// </summary>
|
||||
/// <param name="buf"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetBEInt32FromByteArray(byte[] buf)
|
||||
{
|
||||
byte[] b = buf.Reverse().ToArray();
|
||||
if (b.Length == 0)
|
||||
return 0;
|
||||
int res = b[0];
|
||||
int pos = 1;
|
||||
switch (b.Length)
|
||||
{
|
||||
case 1:
|
||||
default:
|
||||
return res;
|
||||
case 2:
|
||||
return res | b[pos] << (8 * pos++);
|
||||
case 3:
|
||||
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
|
||||
case 4:
|
||||
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
|
||||
case 5:
|
||||
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
|
||||
case 6:
|
||||
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
|
||||
case 7:
|
||||
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an int32 from a byte array based on offset
|
||||
/// </summary>
|
||||
/// <param name="buf"></param>
|
||||
/// <param name="offsetIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static uint GetUInt32(byte[] buf, int offsetIndex)
|
||||
{
|
||||
return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an uint16 from a byte array based on offset
|
||||
/// </summary>
|
||||
|
@ -148,6 +208,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
ds.Read(destBuffer, 0, destBuffer.Length);
|
||||
}
|
||||
|
||||
|
||||
public static byte[] SerializeRaw(object obj)
|
||||
{
|
||||
int rSize = Marshal.SizeOf(obj);
|
||||
IntPtr buff = Marshal.AllocHGlobal(rSize);
|
||||
Marshal.StructureToPtr(obj, buff, false);
|
||||
byte[] rData = new byte[rSize];
|
||||
Marshal.Copy(buff, rData, 0, rSize);
|
||||
return rData;
|
||||
}
|
||||
|
||||
public static T DeserializeRaw<T>(byte[] rData, int pos)
|
||||
{
|
||||
int rSize = Marshal.SizeOf(typeof(T));
|
||||
if (rSize > rData.Length - pos)
|
||||
throw new Exception();
|
||||
IntPtr buff = Marshal.AllocHGlobal(rSize);
|
||||
Marshal.Copy(rData, pos, buff, rSize);
|
||||
T rObj = (T)Marshal.PtrToStructure(buff, typeof(T));
|
||||
Marshal.FreeHGlobal(buff);
|
||||
return rObj;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
using BizHawk.Common.NumberExtensions;
|
||||
using BizHawk.Emulation.Cores.Components.Z80A;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
/// <summary>
|
||||
/// SZX Methods
|
||||
/// Based on the work done by ArjunNair in ZERO spectrum emulator: https://github.com/ArjunNair/Zero-Emulator/blob/master/Ziggy/Peripherals/SZXFile.cs
|
||||
/// </summary>
|
||||
public partial class SZX
|
||||
{
|
||||
private SpectrumBase _machine;
|
||||
|
||||
private Z80A _cpu => _machine.CPU;
|
||||
|
||||
private SZX(SpectrumBase machine)
|
||||
{
|
||||
_machine = machine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports state information to a byte array in ZX-State format
|
||||
/// </summary>
|
||||
/// <param name="machine"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ExportSZX(SpectrumBase machine)
|
||||
{
|
||||
var s = new SZX(machine);
|
||||
|
||||
byte[] result = null;
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (BinaryWriter r = new BinaryWriter(ms))
|
||||
{
|
||||
// temp buffer
|
||||
byte[] buff;
|
||||
// working block
|
||||
ZXSTBLOCK block = new ZXSTBLOCK();
|
||||
|
||||
// header
|
||||
ZXSTHEADER header = new ZXSTHEADER();
|
||||
header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0);
|
||||
header.chMajorVersion = 1;
|
||||
header.chMinorVersion = 4;
|
||||
header.chFlags = 0;
|
||||
switch (s._machine.Spectrum.MachineType)
|
||||
{
|
||||
case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break;
|
||||
case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break;
|
||||
case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break;
|
||||
case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break;
|
||||
case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break;
|
||||
case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break;
|
||||
}
|
||||
buff = MediaConverter.SerializeRaw(header);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTCREATOR
|
||||
var bStruct = s.GetZXSTCREATOR();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(bStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(bStruct);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTZ80REGS
|
||||
var cStruct = s.GetZXSTZ80REGS();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(cStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(cStruct);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTSPECREGS
|
||||
var dStruct = s.GetZXSTSPECREGS();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(dStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(dStruct);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTKEYBOARD
|
||||
var eStruct = s.GetZXSTKEYBOARD();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(eStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(eStruct);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTJOYSTICK
|
||||
var fStruct = s.GetZXSTJOYSTICK();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(fStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(fStruct);
|
||||
r.Write(buff);
|
||||
|
||||
|
||||
// ZXSTAYBLOCK
|
||||
if (s._machine.Spectrum.MachineType != MachineType.ZXSpectrum16 && s._machine.Spectrum.MachineType != MachineType.ZXSpectrum48)
|
||||
{
|
||||
var gStruct = s.GetZXSTAYBLOCK();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("AY\0\0"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(gStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(gStruct);
|
||||
r.Write(buff);
|
||||
}
|
||||
|
||||
// ZXSTRAMPAGE
|
||||
switch (s._machine.Spectrum.MachineType)
|
||||
{
|
||||
// For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved.
|
||||
case MachineType.ZXSpectrum16:
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
|
||||
var rp16 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(rp16);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(rp16);
|
||||
r.Write(buff);
|
||||
break;
|
||||
// For 48k Spectrums and Timex TS/TC models, pages 5, 2 (0x8000 - 0xbfff) and 0 (0xc000 - 0xffff) are saved.
|
||||
case MachineType.ZXSpectrum48:
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
|
||||
var rp48_0 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(rp48_0);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(rp48_0);
|
||||
r.Write(buff);
|
||||
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
|
||||
var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1);
|
||||
block.dwSize = (uint)Marshal.SizeOf(rp48_1);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(rp48_1);
|
||||
r.Write(buff);
|
||||
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
|
||||
var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2);
|
||||
block.dwSize = (uint)Marshal.SizeOf(rp48_2);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(rp48_2);
|
||||
r.Write(buff);
|
||||
break;
|
||||
// For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved.
|
||||
case MachineType.ZXSpectrum128:
|
||||
case MachineType.ZXSpectrum128Plus2:
|
||||
case MachineType.ZXSpectrum128Plus2a:
|
||||
case MachineType.ZXSpectrum128Plus3:
|
||||
List<byte[]> rams = new List<byte[]>
|
||||
{
|
||||
s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3,
|
||||
s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7
|
||||
};
|
||||
for (byte i = 0; i < 8; i++)
|
||||
{
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
|
||||
var rp = s.GetZXSTRAMPAGE(i, rams[i]);
|
||||
block.dwSize = (uint)Marshal.SizeOf(rp);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(rp);
|
||||
r.Write(buff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
// ZXSTPLUS3
|
||||
if (s._machine.Spectrum.MachineType == MachineType.ZXSpectrum128Plus3)
|
||||
{
|
||||
var iStruct = s.GetZXSTPLUS3();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("+3\0\0"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(iStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(iStruct);
|
||||
r.Write(buff);
|
||||
|
||||
// ZXSTDSKFILE
|
||||
if (s._machine.diskImages.Count() > 0)
|
||||
{
|
||||
var jStruct = s.GetZXSTDSKFILE();
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("DSK\0"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(jStruct);
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(jStruct);
|
||||
r.Write(buff);
|
||||
}
|
||||
}
|
||||
|
||||
// ZXSTTAPE
|
||||
if (s._machine.tapeImages.Count() > 0)
|
||||
{
|
||||
var hStruct = s.GetZXSTTAPE();
|
||||
var tapeData = s._machine.tapeImages[s._machine.TapeMediaIndex];
|
||||
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("TAPE"), 0);
|
||||
block.dwSize = (uint)Marshal.SizeOf(hStruct) + (uint)tapeData.Length;
|
||||
buff = MediaConverter.SerializeRaw(block);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(hStruct);
|
||||
r.Write(buff);
|
||||
buff = MediaConverter.SerializeRaw(tapeData);
|
||||
r.Write(buff);
|
||||
char[] terminator = "\0".ToCharArray();
|
||||
r.Write(terminator);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
result = ms.ToArray();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM)
|
||||
{
|
||||
var s = new ZXSTRAMPAGE();
|
||||
s.wFlags = 0; // uncompressed only at the moment
|
||||
s.chPageNo = page;
|
||||
s.ramPage = RAM;
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTCREATOR GetZXSTCREATOR()
|
||||
{
|
||||
var s = new ZXSTCREATOR();
|
||||
var str = "BIZHAWK EMULATOR".ToCharArray();
|
||||
s.szCreator = new char[32];
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
s.szCreator[i] = str[i];
|
||||
s.chMajorVersion = 1;
|
||||
s.chMinorVersion = 4;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTZ80REGS GetZXSTZ80REGS()
|
||||
{
|
||||
var s = new ZXSTZ80REGS();
|
||||
s.AF = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["AF"].Value;
|
||||
s.BC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["BC"].Value;
|
||||
s.DE = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["DE"].Value;
|
||||
s.HL = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["HL"].Value;
|
||||
s.AF1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow AF"].Value;
|
||||
s.BC1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow BC"].Value;
|
||||
s.DE1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow DE"].Value;
|
||||
s.HL1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow HL"].Value;
|
||||
s.IX = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IX"].Value;
|
||||
s.IY = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IY"].Value;
|
||||
s.SP = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["SP"].Value;
|
||||
s.PC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["PC"].Value;
|
||||
s.I = (byte)_machine.CPU.Regs[_machine.CPU.I];
|
||||
s.R = (byte)_machine.CPU.Regs[_machine.CPU.R];
|
||||
s.IFF1 = (byte)(_machine.CPU.IFF1 ? 1 : 0);
|
||||
s.IFF2 = (byte)(_machine.CPU.IFF2 ? 1 : 0);
|
||||
s.IM = (byte)_machine.CPU.InterruptMode;
|
||||
s.dwCyclesStart = (uint)(_machine.CurrentFrameCycle + _machine.ULADevice.InterruptStartTime);
|
||||
s.wMemPtr = (ushort)(_machine.CPU.Regs[_machine.CPU.Z] + (_machine.CPU.Regs[_machine.CPU.W] << 8));
|
||||
//s.chHoldIntReqCycles = ?
|
||||
|
||||
if (_machine.CPU.EIPending > 0)
|
||||
{
|
||||
s.chFlags |= ZXSTZF_EILAST;
|
||||
}
|
||||
else if (_machine.CPU.halted)
|
||||
{
|
||||
s.chFlags |= ZXSTZF_HALTED;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTSPECREGS GetZXSTSPECREGS()
|
||||
{
|
||||
var s = new ZXSTSPECREGS();
|
||||
s.chBorder = _machine.ULADevice.BorderColor > 7 ? (byte)0 : (byte)_machine.ULADevice.BorderColor;
|
||||
s.chFe = _machine.LastFe;
|
||||
byte x7ffd = (byte)_machine.RAMPaged;
|
||||
byte x1ffd = 0;
|
||||
switch (_machine.Spectrum.MachineType)
|
||||
{
|
||||
case MachineType.ZXSpectrum16:
|
||||
case MachineType.ZXSpectrum48:
|
||||
s.ch7ffd = 0;
|
||||
s.unionPage = 0;
|
||||
break;
|
||||
|
||||
case MachineType.ZXSpectrum128:
|
||||
case MachineType.ZXSpectrum128Plus2:
|
||||
// 7FFD
|
||||
if (_machine._ROMpaged == 1)
|
||||
x7ffd |= 0x10;
|
||||
if (_machine.SHADOWPaged)
|
||||
x7ffd |= 0x08;
|
||||
if (_machine.PagingDisabled)
|
||||
x7ffd |= 0x20;
|
||||
break;
|
||||
|
||||
case MachineType.ZXSpectrum128Plus2a:
|
||||
case MachineType.ZXSpectrum128Plus3:
|
||||
if (_machine.UPDDiskDevice.FDD_FLAG_MOTOR)
|
||||
x1ffd |= 0x08;
|
||||
if (_machine.SpecialPagingMode)
|
||||
{
|
||||
x1ffd |= 0x01;
|
||||
switch (_machine.PagingConfiguration)
|
||||
{
|
||||
case 1:
|
||||
x1ffd |= 0x02;
|
||||
break;
|
||||
case 2:
|
||||
x1ffd |= 0x04;
|
||||
break;
|
||||
case 3:
|
||||
x1ffd |= 0x02;
|
||||
x1ffd |= 0x04;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_machine.ROMhigh)
|
||||
x1ffd |= 0x04;
|
||||
}
|
||||
if (_machine.ROMlow)
|
||||
x7ffd |= 0x10;
|
||||
if (_machine.SHADOWPaged)
|
||||
x7ffd |= 0x08;
|
||||
if (_machine.PagingDisabled)
|
||||
x7ffd |= 0x20;
|
||||
break;
|
||||
}
|
||||
|
||||
s.ch7ffd = x7ffd;
|
||||
s.unionPage = x1ffd;
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTKEYBOARD GetZXSTKEYBOARD()
|
||||
{
|
||||
var s = new ZXSTKEYBOARD();
|
||||
s.dwFlags = 0; //no issue 2 emulation
|
||||
s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE;
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTJOYSTICK GetZXSTJOYSTICK()
|
||||
{
|
||||
var s = new ZXSTJOYSTICK();
|
||||
s.dwFlags = 0; //depreciated
|
||||
s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON;
|
||||
s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1;
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTAYBLOCK GetZXSTAYBLOCK()
|
||||
{
|
||||
var s = new ZXSTAYBLOCK();
|
||||
s.cFlags = 0; // no external units
|
||||
s.chCurrentRegister = (byte)_machine.AYDevice.SelectedRegister;
|
||||
var regs = _machine.AYDevice.ExportRegisters();
|
||||
s.chAyRegs = new byte[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
s.chAyRegs[i] = (byte)regs[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTTAPE GetZXSTTAPE()
|
||||
{
|
||||
var s = new ZXSTTAPE();
|
||||
s.wFlags |= (int)CassetteRecorderState.ZXSTTP_EMBEDDED;
|
||||
s.wCurrentBlockNo = (ushort)_machine.TapeDevice.CurrentDataBlockIndex;
|
||||
s.dwCompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
|
||||
s.dwUncompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
|
||||
char[] ext = "tzx".ToCharArray();
|
||||
s.szFileExtension = new char[16];
|
||||
for (int f = 1; f < ext.Length; f++)
|
||||
{
|
||||
s.szFileExtension[f - 1] = ext[f];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTPLUS3 GetZXSTPLUS3()
|
||||
{
|
||||
var s = new ZXSTPLUS3();
|
||||
s.chNumDrives = 1;
|
||||
s.fMotorOn = _machine.UPDDiskDevice.FDD_FLAG_MOTOR ? (byte)1 : (byte)0;
|
||||
return s;
|
||||
}
|
||||
|
||||
private ZXSTDSKFILE GetZXSTDSKFILE()
|
||||
{
|
||||
var s = new ZXSTDSKFILE();
|
||||
s.wFlags = 0;
|
||||
s.chDriveNum = 0;
|
||||
s.dwUncompressedSize = 0;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,410 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
/// <summary>
|
||||
/// Structs, Constants and Enums
|
||||
/// http://www.spectaculator.com/docs/zx-state/intro.shtml
|
||||
/// </summary>
|
||||
public partial class SZX
|
||||
{
|
||||
#region ZX-State Header
|
||||
|
||||
public enum MachineIdentifier : byte
|
||||
{
|
||||
ZXSTMID_16K = 0,
|
||||
ZXSTMID_48K = 1,
|
||||
ZXSTMID_128K = 2,
|
||||
ZXSTMID_PLUS2 = 3,
|
||||
ZXSTMID_PLUS2A = 4,
|
||||
ZXSTMID_PLUS3 = 5,
|
||||
ZXSTMID_PLUS3E = 6,
|
||||
ZXSTMID_PENTAGON128 = 7,
|
||||
ZXSTMID_TC2048 = 8,
|
||||
ZXSTMID_TC2068 = 9,
|
||||
ZXSTMID_SCORPION = 10,
|
||||
ZXSTMID_SE = 11,
|
||||
ZXSTMID_TS2068 = 12,
|
||||
ZXSTMID_PENTAGON512 = 13,
|
||||
ZXSTMID_PENTAGON1024 = 14,
|
||||
ZXSTMID_NTSC48K = 15,
|
||||
ZXSTMID_128KE = 16
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If set, the emulated Spectrum uses alternate timings (one cycle later than normal timings). If reset, the emulated Spectrum uses standard timings.
|
||||
/// This flag is only applicable for the ZXSTMID_16K, ZXSTMID_48K and ZXSTMID_128K models.
|
||||
/// </summary>
|
||||
public const int ZXSTMF_ALTERNATETIMINGS = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The zx-state header appears right at the start of a zx-state (.szx) file.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTHEADER
|
||||
{
|
||||
public uint dwMagic;
|
||||
public byte chMajorVersion;
|
||||
public byte chMinorVersion;
|
||||
public byte chMachineId;
|
||||
public byte chFlags;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTBLOCK Header
|
||||
|
||||
/// <summary>
|
||||
/// Block Header. Each real block starts with this header.
|
||||
/// </summary>
|
||||
public struct ZXSTBLOCK
|
||||
{
|
||||
public uint dwId;
|
||||
public uint dwSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTCREATOR
|
||||
|
||||
/// <summary>
|
||||
/// This block identifies the program that created this zx-state file.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTCREATOR
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||
public char[] szCreator;
|
||||
public short chMajorVersion;
|
||||
public short chMinorVersion;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
|
||||
public byte[] chData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTZ80REGS
|
||||
|
||||
/// <summary>
|
||||
/// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix.
|
||||
/// </summary>
|
||||
public const int ZXSTZF_EILAST = 1;
|
||||
/// <summary>
|
||||
/// The last instruction executed was a HALT instruction. The CPU is currently executing NOPs and will continue to do so until the next interrupt occurs.
|
||||
/// This flag is mutually exclusive with ZXSTZF_EILAST.
|
||||
/// </summary>
|
||||
public const int ZXSTZF_HALTED = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Z80 registers and other internal state values. It does not contain any specific model registers.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTZ80REGS
|
||||
{
|
||||
public ushort AF, BC, DE, HL;
|
||||
public ushort AF1, BC1, DE1, HL1;
|
||||
public ushort IX, IY, SP, PC;
|
||||
public byte I;
|
||||
public byte R;
|
||||
public byte IFF1, IFF2;
|
||||
public byte IM;
|
||||
public uint dwCyclesStart;
|
||||
public byte chHoldIntReqCycles;
|
||||
public byte chFlags;
|
||||
public ushort wMemPtr;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTSPECREGS
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTSPECREGS
|
||||
{
|
||||
public byte chBorder;
|
||||
public byte ch7ffd;
|
||||
public byte unionPage;
|
||||
public byte chFe;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[] chReserved;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTAYBLOCK
|
||||
|
||||
/// <summary>
|
||||
/// Fuller Box emulation
|
||||
/// </summary>
|
||||
public const int ZXSTAYF_FULLERBOX = 1;
|
||||
/// <summary>
|
||||
/// Melodik Soundbox emulation.
|
||||
/// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums
|
||||
/// </summary>
|
||||
public const int ZXSTAYF_128AY = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The state of the AY chip found in all 128k Spectrums, Pentagons, Scorpions and Timex machines.
|
||||
/// This block may also be present for 16k/48k Spectrums if Fuller Box or Melodik emulation is enabled.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTAYBLOCK
|
||||
{
|
||||
public byte cFlags;
|
||||
public byte chCurrentRegister;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
||||
public byte[] chAyRegs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTRAMPAGE
|
||||
|
||||
/// <summary>
|
||||
/// Ram pages are compressed using Zlib
|
||||
/// </summary>
|
||||
public const int ZXSTRF_COMPRESSED = 1;
|
||||
|
||||
/// <summary>
|
||||
/// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTRAMPAGE
|
||||
{
|
||||
public ushort wFlags;
|
||||
public byte chPageNo;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
|
||||
public byte[] ramPage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTKEYBOARD
|
||||
|
||||
/// <summary>
|
||||
/// Keyboard state
|
||||
/// </summary>
|
||||
public const int ZXSTKF_ISSUE2 = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Supported joystick types
|
||||
/// </summary>
|
||||
public enum JoystickTypes
|
||||
{
|
||||
ZXSTKJT_KEMPSTON = 0,
|
||||
ZXSTKJT_FULLER = 1,
|
||||
ZXSTKJT_CURSOR = 2,
|
||||
ZXSTKJT_SINCLAIR1 = 3,
|
||||
ZXSTKJT_SINCLAIR2 = 4,
|
||||
ZXSTKJT_SPECTRUMPLUS = 5,
|
||||
ZXSTKJT_TIMEX1 = 6,
|
||||
ZXSTKJT_TIMEX2 = 7,
|
||||
ZXSTKJT_NONE = 8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state of the Spectrum keyboard and any keyboard joystick emulation.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTKEYBOARD
|
||||
{
|
||||
public uint dwFlags;
|
||||
public byte chKeyboardJoystick;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTJOYSTICK
|
||||
|
||||
/// <summary>
|
||||
/// Joystick setup for both players.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTJOYSTICK
|
||||
{
|
||||
public uint dwFlags;
|
||||
public byte chTypePlayer1;
|
||||
public byte chTypePlayer2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTTAPE
|
||||
|
||||
/// <summary>
|
||||
/// Cassette Recorder state
|
||||
/// </summary>
|
||||
public enum CassetteRecorderState
|
||||
{
|
||||
ZXSTTP_EMBEDDED = 1,
|
||||
ZXSTTP_COMPRESSED = 2
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTTAPE
|
||||
{
|
||||
public ushort wCurrentBlockNo;
|
||||
public ushort wFlags;
|
||||
public int dwUncompressedSize;
|
||||
public int dwCompressedSize;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
||||
public char[] szFileExtension;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTPLUS3
|
||||
|
||||
/// <summary>
|
||||
/// The number of drives connected to the Spectrum +3 and whether their motors are turned on.
|
||||
/// Any blocks specifying which disk files are in which drive will follow this one.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTPLUS3
|
||||
{
|
||||
public byte chNumDrives;
|
||||
public byte fMotorOn;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTDSKFILE
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
|
||||
/// </summary>
|
||||
public const int ZXSTDSKF_COMPRESSED = 1;
|
||||
/// <summary>
|
||||
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
|
||||
/// </summary>
|
||||
public const int ZXSTDSKF_EMBEDDED = 2;
|
||||
/// <summary>
|
||||
/// When a double-sided disk is inserted into a single-sided drive, specifies the side being read from/written to.
|
||||
/// If set, Side B is the active side, otherwise it is Side A.
|
||||
/// </summary>
|
||||
public const int ZXSTDSKF_SIDEB = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Each +3 disk drive that has a disk inserted in it will have one of these blocks.
|
||||
/// They follow the ZXSTPLUS3 block which identifies the number of drives.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ZXSTDSKFILE
|
||||
{
|
||||
public ushort wFlags;
|
||||
public byte chDriveNum;
|
||||
public int dwUncompressedSize;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Not Yet Implemented
|
||||
|
||||
#region ZXSTATASP
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTATARAM
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTCF
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTCFRAM
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTCOVOX
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTBETA128
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTBETADISK
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTDOCK
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTGS
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTGSRAMPAGE
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTIF1
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTIF2ROM
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTMCART
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTMOUSE
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTMULTIFACE
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTOPUS
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTOPUSDISK
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTPLUSD
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTPLUSDDISK
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTROM
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTSCLDREGS
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTSIDE
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTSPECDRUM
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTUSPEECH
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZXSTZXPRINTER
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -62,5 +62,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
|
||||
return m;
|
||||
}
|
||||
|
||||
public byte[] GetSZXSnapshot()
|
||||
{
|
||||
return SZX.ExportSZX(_machine);
|
||||
//return System.Text.Encoding.Default.GetString(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
deterministicEmulation = deterministic.Value;
|
||||
}
|
||||
|
||||
switch (SyncSettings.MachineType)
|
||||
MachineType = SyncSettings.MachineType;
|
||||
|
||||
switch (MachineType)
|
||||
{
|
||||
case MachineType.ZXSpectrum16:
|
||||
ControllerDefinition = ZXSpectrumControllerDefinition;
|
||||
|
@ -146,6 +148,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
private readonly TraceBuffer _tracer;
|
||||
public IController _controller;
|
||||
public SpectrumBase _machine;
|
||||
public MachineType MachineType;
|
||||
|
||||
public List<GameInfo> _gameInfo;
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@ using System.IO;
|
|||
|
||||
internal static class VersionInfo
|
||||
{
|
||||
public const string Mainversion = "2.0.0"; // Use numbers only or the new version notification won't work
|
||||
public static readonly string RELEASEDATE = "June 25, 2017";
|
||||
// keep this updated at every major release
|
||||
public const string Mainversion = "2.3.0"; // Use numbers only or the new version notification won't work
|
||||
public static readonly string RELEASEDATE = "June 24, 2018";
|
||||
public static readonly bool DeveloperBuild = true;
|
||||
public static readonly string HomePage = "http://tasvideos.org/BizHawk.html";
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit db0041340ff260424f38772df4f286fc3907c1c8
|
Binary file not shown.
Loading…
Reference in New Issue