From faaf4d2f18bf379f9253c913da772491803a802f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 11 Sep 2018 11:21:59 +0100 Subject: [PATCH] ZXHawk: Support double-sided *.dsk images and throw an exception if the images are not 42 track disks --- .../Machine/SpectrumBase.Media.cs | 70 +++++++++++++- .../Media/Disk/CPCExtendedFloppyDisk.cs | 95 +++++++++++++++++++ .../Media/Disk/CPCFloppyDisk.cs | 84 ++++++++++++++++ 3 files changed, 246 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index 02f3e6187e..826b2394d0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -141,6 +142,63 @@ 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 working = new List(); + 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; + } + + 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,7 +250,12 @@ 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")) { @@ -231,6 +294,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { None, Tape, - Disk + Disk, + DiskDoubleSided } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs index 6217f2a282..fcc06fdd35 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -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; } + /// + /// Takes a double-sided disk byte array and converts into 2 single-sided arrays + /// + /// + /// + /// + public static bool SplitDoubleSided(byte[] data, List 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; + } + /// /// State serlialization /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs index 00dfe4f61d..97f3b49cbf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -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; } + /// + /// Takes a double-sided disk byte array and converts into 2 single-sided arrays + /// + /// + /// + /// + public static bool SplitDoubleSided(byte[] data, List 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; + } + /// /// State serlialization ///