ZXHawk: Starting on UDI and IPF disk image support (although neither are fully working or hooked up yet)
This commit is contained in:
parent
faaf4d2f18
commit
c76e2f35a0
|
@ -288,9 +288,11 @@
|
|||
<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" />
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -158,6 +158,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
case DiskType.CPC:
|
||||
found = CPCFloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
case DiskType.UDI:
|
||||
found = UDI1_0FloppyDisk.SplitDoubleSided(m, working);
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
|
@ -262,6 +265,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// 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!"))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
|
@ -95,6 +96,53 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
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>
|
||||
|
|
Loading…
Reference in New Issue