ZXHawk: Added Compressed Square Wave (CSW) tape image support
This commit is contained in:
parent
b90c8f0bec
commit
b81a7539cf
|
@ -66,7 +66,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
RomData = FileData;
|
RomData = FileData;
|
||||||
}
|
}
|
||||||
else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX")
|
else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX" || file.Extension == ".CSW")
|
||||||
{
|
{
|
||||||
// these are not roms. unforunately if treated as such there are certain edge-cases
|
// these are not roms. unforunately if treated as such there are certain edge-cases
|
||||||
// where a header offset is detected. This should mitigate this issue until a cleaner solution is found
|
// where a header offset is detected. This should mitigate this issue until a cleaner solution is found
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF",
|
".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF",
|
||||||
".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX"
|
".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX", ".CSW"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2081,7 +2081,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
if (VersionInfo.DeveloperBuild)
|
if (VersionInfo.DeveloperBuild)
|
||||||
{
|
{
|
||||||
return FormatFilter(
|
return FormatFilter(
|
||||||
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;%ARCH%",
|
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;%ARCH%",
|
||||||
"Music Files", "*.psf;*.minipsf;*.sid;*.nsf",
|
"Music Files", "*.psf;*.minipsf;*.sid;*.nsf",
|
||||||
"Disc Images", "*.cue;*.ccd;*.mds;*.m3u",
|
"Disc Images", "*.cue;*.ccd;*.mds;*.m3u",
|
||||||
"NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%",
|
"NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%",
|
||||||
|
@ -2109,7 +2109,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
"Apple II", "*.dsk;*.do;*.po;%ARCH%",
|
"Apple II", "*.dsk;*.do;*.po;%ARCH%",
|
||||||
"Virtual Boy", "*.vb;%ARCH%",
|
"Virtual Boy", "*.vb;%ARCH%",
|
||||||
"Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%",
|
"Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%",
|
||||||
"Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;%ARCH%",
|
"Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;%ARCH%",
|
||||||
"All Files", "*.*");
|
"All Files", "*.*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,6 +305,7 @@ namespace BizHawk.Emulation.Common
|
||||||
|
|
||||||
case ".TZX":
|
case ".TZX":
|
||||||
case ".PZX":
|
case ".PZX":
|
||||||
|
case ".CSW":
|
||||||
game.System = "ZXSpectrum";
|
game.System = "ZXSpectrum";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,7 @@
|
||||||
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
|
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
|
||||||
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverter.cs" />
|
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverter.cs" />
|
||||||
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverterType.cs" />
|
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverterType.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\PZX\PzxConverter.cs" />
|
||||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeCommand.cs" />
|
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeCommand.cs" />
|
||||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeDataBlock.cs" />
|
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeDataBlock.cs" />
|
||||||
|
|
|
@ -324,6 +324,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
TzxConverter tzxSer = new TzxConverter(this);
|
TzxConverter tzxSer = new TzxConverter(this);
|
||||||
TapConverter tapSer = new TapConverter(this);
|
TapConverter tapSer = new TapConverter(this);
|
||||||
PzxConverter pzxSer = new PzxConverter(this);
|
PzxConverter pzxSer = new PzxConverter(this);
|
||||||
|
CswConverter cswSer = new CswConverter(this);
|
||||||
|
|
||||||
// TZX
|
// TZX
|
||||||
if (tzxSer.CheckType(tapeData))
|
if (tzxSer.CheckType(tapeData))
|
||||||
|
@ -365,6 +366,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSW
|
||||||
|
else if (cswSer.CheckType(tapeData))
|
||||||
|
{
|
||||||
|
// this file has a csw header - attempt serialization
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cswSer.Read(tapeData);
|
||||||
|
// reset block index
|
||||||
|
CurrentDataBlockIndex = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// exception during operation
|
||||||
|
var e = ex;
|
||||||
|
throw new Exception(this.GetType().ToString() +
|
||||||
|
"\n\nTape image file has a valid CSW header, but threw an exception whilst data was being parsed.\n\n" + e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Assume TAP
|
// Assume TAP
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -817,7 +838,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
{
|
{
|
||||||
if (_tapeIsPlaying && _autoPlay)
|
if (_tapeIsPlaying && _autoPlay)
|
||||||
{
|
{
|
||||||
|
if (DataBlocks.Count > 1 || _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.CSW_Recording)
|
||||||
|
{
|
||||||
|
// we should only stop the tape when there are multiple blocks
|
||||||
|
// if we just have one big block (maybe a CSW or WAV) then auto stopping will cock things up
|
||||||
_monitorTimeOut--;
|
_monitorTimeOut--;
|
||||||
|
}
|
||||||
|
|
||||||
if (_monitorTimeOut < 0)
|
if (_monitorTimeOut < 0)
|
||||||
{
|
{
|
||||||
|
@ -843,6 +869,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
if (timeout == 0)
|
if (timeout == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// dont autostop if there is only 1 block
|
||||||
|
if (DataBlocks.Count > 2 || _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.CSW_Recording)
|
||||||
|
return;
|
||||||
|
|
||||||
if (diff >= timeout * 2)
|
if (diff >= timeout * 2)
|
||||||
{
|
{
|
||||||
// There have been no attempted tape reads by the CPU within the double timeout period
|
// There have been no attempted tape reads by the CPU within the double timeout period
|
||||||
|
|
|
@ -202,6 +202,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
// spectrum .tzx tape file
|
// spectrum .tzx tape file
|
||||||
return SpectrumMediaType.Tape;
|
return SpectrumMediaType.Tape;
|
||||||
}
|
}
|
||||||
|
if (hdr.ToUpper().StartsWith("COMPRESSED SQ"))
|
||||||
|
{
|
||||||
|
// spectrum .tzx tape file
|
||||||
|
return SpectrumMediaType.Tape;
|
||||||
|
}
|
||||||
|
|
||||||
// if we get this far, assume a .tap file
|
// if we get this far, assume a .tap file
|
||||||
return SpectrumMediaType.Tape;
|
return SpectrumMediaType.Tape;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
{
|
{
|
||||||
|
@ -130,6 +132,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decompresses a byte array that is Z-RLE compressed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceBuffer"></param>
|
||||||
|
/// <param name="destBuffer"></param>
|
||||||
|
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
|
||||||
|
{
|
||||||
|
MemoryStream stream = new MemoryStream();
|
||||||
|
stream.Write(sourceBuffer, 0, sourceBuffer.Length);
|
||||||
|
stream.Position = 0;
|
||||||
|
stream.ReadByte();
|
||||||
|
stream.ReadByte();
|
||||||
|
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
|
||||||
|
ds.Read(destBuffer, 0, destBuffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
TZX,
|
TZX,
|
||||||
TAP,
|
TAP,
|
||||||
PZX,
|
PZX,
|
||||||
|
CSW,
|
||||||
DSK
|
DSK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
using BizHawk.Common.NumberExtensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Responsible for Compressed Square Wave conversion
|
||||||
|
/// https://web.archive.org/web/20171024182530/http://ramsoft.bbk.org.omegahg.com/csw.html
|
||||||
|
/// </summary>
|
||||||
|
public class CswConverter : MediaConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of serializer
|
||||||
|
/// </summary>
|
||||||
|
private MediaConverterType _formatType = MediaConverterType.CSW;
|
||||||
|
public override MediaConverterType FormatType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _formatType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position counter
|
||||||
|
/// </summary>
|
||||||
|
private int _position = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs whether this class can be used to read the data format
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsReader { get { return true; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs whether this class can be used to write the data format
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsWriter { get { return false; } }
|
||||||
|
|
||||||
|
#region Construction
|
||||||
|
|
||||||
|
private DatacorderDevice _datacorder;
|
||||||
|
|
||||||
|
public CswConverter(DatacorderDevice _tapeDevice)
|
||||||
|
{
|
||||||
|
_datacorder = _tapeDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns TRUE if pzx header is detected
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public override bool CheckType(byte[] data)
|
||||||
|
{
|
||||||
|
// CSW Header
|
||||||
|
|
||||||
|
// check whether this is a valid csw format file by looking at the identifier in the header
|
||||||
|
// (first 22 bytes of the file)
|
||||||
|
string ident = Encoding.ASCII.GetString(data, 0, 22);
|
||||||
|
|
||||||
|
// version info
|
||||||
|
int majorVer = data[8];
|
||||||
|
int minorVer = data[9];
|
||||||
|
|
||||||
|
if (ident.ToUpper() != "COMPRESSED SQUARE WAVE")
|
||||||
|
{
|
||||||
|
// this is not a valid CSW format file
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DeSerialization method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public override void Read(byte[] data)
|
||||||
|
{
|
||||||
|
// clear existing tape blocks
|
||||||
|
_datacorder.DataBlocks.Clear();
|
||||||
|
|
||||||
|
// CSW Header
|
||||||
|
|
||||||
|
// check whether this is a valid csw format file by looking at the identifier in the header
|
||||||
|
// (first 22 bytes of the file)
|
||||||
|
string ident = Encoding.ASCII.GetString(data, 0, 22);
|
||||||
|
|
||||||
|
if (ident.ToUpper() != "COMPRESSED SQUARE WAVE")
|
||||||
|
{
|
||||||
|
// this is not a valid CSW format file
|
||||||
|
throw new Exception(this.GetType().ToString() +
|
||||||
|
"This is not a valid CSW format file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[0x16] != 0x1a)
|
||||||
|
{
|
||||||
|
// invalid terminator code
|
||||||
|
throw new Exception(this.GetType().ToString() +
|
||||||
|
"This image reports as a CSW but has an invalid terminator code");
|
||||||
|
}
|
||||||
|
|
||||||
|
_position = 0;
|
||||||
|
|
||||||
|
// version info
|
||||||
|
int majorVer = data[0x17];
|
||||||
|
int minorVer = data[0x18];
|
||||||
|
|
||||||
|
int sampleRate;
|
||||||
|
int totalPulses;
|
||||||
|
byte compressionType;
|
||||||
|
byte flags;
|
||||||
|
byte headerExtensionLen;
|
||||||
|
byte[] cswData;
|
||||||
|
byte[] cswDataUncompressed;
|
||||||
|
|
||||||
|
if (majorVer == 2)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
CSW-2 Header
|
||||||
|
CSW global file header - status: required
|
||||||
|
Offset Value Type Description
|
||||||
|
0x00 (note) ASCII[22] "Compressed Square Wave" signature
|
||||||
|
0x16 0x1A BYTE Terminator code
|
||||||
|
0x17 0x02 BYTE CSW major revision number
|
||||||
|
0x18 0x00 BYTE CSW minor revision number
|
||||||
|
0x19 - DWORD Sample rate
|
||||||
|
0x1D - DWORD Total number of pulses (after decompression)
|
||||||
|
0x21 - BYTE Compression type (see notes below)
|
||||||
|
0x01: RLE
|
||||||
|
0x02: Z-RLE
|
||||||
|
0x22 - BYTE Flags
|
||||||
|
b0: initial polarity; if set, the signal starts at logical high
|
||||||
|
0x23 HDR BYTE Header extension length in bytes (0x00)
|
||||||
|
For future expansions only, see note below.
|
||||||
|
0x24 - ASCIIZ[16] Encoding application description
|
||||||
|
Information about the tool which created the file (e.g. name and version)
|
||||||
|
0x34 - BYTE[HDR] Header extension data (if present)
|
||||||
|
0x34+HDR - - CSW data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_position = 0x19;
|
||||||
|
sampleRate = GetInt32(data, _position);
|
||||||
|
_position += 4;
|
||||||
|
|
||||||
|
totalPulses = GetInt32(data, _position);
|
||||||
|
cswDataUncompressed = new byte[totalPulses + 1];
|
||||||
|
_position += 4;
|
||||||
|
|
||||||
|
compressionType = data[_position++];
|
||||||
|
flags = data[_position++];
|
||||||
|
headerExtensionLen = data[_position++];
|
||||||
|
|
||||||
|
_position = 0x34 + headerExtensionLen;
|
||||||
|
|
||||||
|
cswData = new byte[data.Length - _position];
|
||||||
|
Array.Copy(data, _position, cswData, 0, cswData.Length);
|
||||||
|
|
||||||
|
ProcessCSWV2(cswData, ref cswDataUncompressed, compressionType, totalPulses);
|
||||||
|
}
|
||||||
|
else if (majorVer == 1)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
CSW-1 Header
|
||||||
|
CSW global file header - status: required
|
||||||
|
Offset Value Type Description
|
||||||
|
0x00 (note) ASCII[22] "Compressed Square Wave" signature
|
||||||
|
0x16 0x1A BYTE Terminator code
|
||||||
|
0x17 0x01 BYTE CSW major revision number
|
||||||
|
0x18 0x01 BYTE CSW minor revision number
|
||||||
|
0x19 - WORD Sample rate
|
||||||
|
0x1B 0x01 BYTE Compression type
|
||||||
|
0x01: RLE
|
||||||
|
0x1C - BYTE Flags
|
||||||
|
b0: initial polarity; if set, the signal starts at logical high
|
||||||
|
0x1D 0x00 BYTE[3] Reserved.
|
||||||
|
0x20 - - CSW data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_position = 0x19;
|
||||||
|
sampleRate = GetWordValue(data, _position);
|
||||||
|
_position += 2;
|
||||||
|
|
||||||
|
compressionType = data[_position++];
|
||||||
|
flags = data[_position++];
|
||||||
|
|
||||||
|
_position += 3;
|
||||||
|
|
||||||
|
cswDataUncompressed = new byte[data.Length - _position];
|
||||||
|
|
||||||
|
if (compressionType == 1)
|
||||||
|
Array.Copy(data, _position, cswDataUncompressed, 0, cswDataUncompressed.Length);
|
||||||
|
else
|
||||||
|
throw new Exception(this.GetType().ToString() +
|
||||||
|
"CSW Format unknown compression type");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception(this.GetType().ToString() +
|
||||||
|
"CSW Format Version " + majorVer + "." + minorVer + " is not currently supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the single tape block
|
||||||
|
// (use DATA block for now so initial signal level is handled correctly by the datacorder device)
|
||||||
|
TapeDataBlock t = new TapeDataBlock();
|
||||||
|
t.BlockDescription = BlockType.CSW_Recording;
|
||||||
|
t.BlockID = 0x18;
|
||||||
|
t.DataPeriods = new List<int>();
|
||||||
|
|
||||||
|
if (flags.Bit(0))
|
||||||
|
t.InitialPulseLevel = true;
|
||||||
|
else
|
||||||
|
t.InitialPulseLevel = false;
|
||||||
|
|
||||||
|
var rate = (69888 * 50) / sampleRate;
|
||||||
|
|
||||||
|
for (int i = 0; i < cswDataUncompressed.Length;)
|
||||||
|
{
|
||||||
|
int length = cswDataUncompressed[i++] * rate;
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
length = GetInt32(cswDataUncompressed, i) / rate;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.DataPeriods.Add(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add closing period
|
||||||
|
t.DataPeriods.Add((69888 * 50) / 10);
|
||||||
|
|
||||||
|
// add to datacorder
|
||||||
|
_datacorder.DataBlocks.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a CSW v2 data block
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="srcBuff"></param>
|
||||||
|
/// <param name="destBuff"></param>
|
||||||
|
/// <param name="sampleRate"></param>
|
||||||
|
/// <param name="compType"></param>
|
||||||
|
/// <param name="pulseCount"></param>
|
||||||
|
public static void ProcessCSWV2(
|
||||||
|
byte[] srcBuff,
|
||||||
|
ref byte[] destBuff,
|
||||||
|
byte compType,
|
||||||
|
int pulseCount)
|
||||||
|
{
|
||||||
|
if (compType == 1)
|
||||||
|
{
|
||||||
|
Array.Copy(srcBuff, 0, destBuff, 0, pulseCount);
|
||||||
|
}
|
||||||
|
else if (compType == 2)
|
||||||
|
{
|
||||||
|
DecompressZRLE(srcBuff, ref destBuff);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Exception("CSW Format unknown compression type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns TRUE if tzx header is detected
|
/// Returns TRUE if pzx header is detected
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
public override bool CheckType(byte[] data)
|
public override bool CheckType(byte[] data)
|
||||||
|
|
|
@ -643,10 +643,51 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||||
int blockLen = GetInt32(data, _position);
|
int blockLen = GetInt32(data, _position);
|
||||||
_position += 4;
|
_position += 4;
|
||||||
|
|
||||||
_position += blockLen;
|
t.PauseInMS = GetWordValue(data, _position);
|
||||||
|
|
||||||
|
_position += 2;
|
||||||
|
|
||||||
|
int sampleRate = data[_position++] << 16 | data[_position++] << 8 | data[_position++];
|
||||||
|
byte compType = data[_position++];
|
||||||
|
int pulses = GetInt32(data, _position);
|
||||||
|
_position += 4;
|
||||||
|
|
||||||
|
int dataLen = blockLen - 10;
|
||||||
|
|
||||||
|
// build source array
|
||||||
|
byte[] src = new byte[dataLen];
|
||||||
|
// build destination array
|
||||||
|
byte[] dest = new byte[pulses + 1];
|
||||||
|
|
||||||
|
// process the CSW data
|
||||||
|
CswConverter.ProcessCSWV2(src, ref dest, compType, pulses);
|
||||||
|
|
||||||
|
// create the periods
|
||||||
|
var rate = (69888 * 50) / sampleRate;
|
||||||
|
|
||||||
|
for (int i = 0; i < dest.Length;)
|
||||||
|
{
|
||||||
|
int length = dest[i++] * rate;
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
length = GetInt32(dest, i) / rate;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.DataPeriods.Add(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add closing period
|
||||||
|
t.DataPeriods.Add((69888 * 50) / 10);
|
||||||
|
|
||||||
|
_position += dataLen;
|
||||||
|
//_position += blockLen;
|
||||||
|
|
||||||
// add the block
|
// add the block
|
||||||
_datacorder.DataBlocks.Add(t);
|
_datacorder.DataBlocks.Add(t);
|
||||||
|
|
||||||
|
// generate PAUSE block
|
||||||
|
CreatePauseBlock(_datacorder.DataBlocks.Last());
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue