/// <summary> /// Attempts to parse incoming disk data /// </summary> /// <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 { TrackNumber = (byte)t, SideNumber = 0, TrackType = data[pos++], 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> /// 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("EXTENDED CPC DSK")) { // incorrect format return(false); } // read the disk information block DiskHeader.DiskIdent = ident; DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); DiskHeader.NumberOfTracks = data[0x30]; DiskHeader.NumberOfSides = data[0x31]; DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskData = data; int pos = 0x34; if (DiskHeader.NumberOfSides > 1) { StringBuilder sbm = new StringBuilder(); sbm.AppendLine(); sbm.AppendLine(); sbm.AppendLine("The detected disk image contains multiple sides."); sbm.AppendLine("This is NOT currently supported in ZXHawk."); sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); 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; } // move to first track information block pos = 0x100; // parse each track for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { // check for unformatted track if (DiskHeader.TrackSizes[i] == 0) { DiskTracks[i] = new Track(); DiskTracks[i].Sectors = new Sector[0]; continue; } int p = pos; DiskTracks[i] = new Track(); // track info block DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); p += 16; DiskTracks[i].TrackNumber = data[p++]; DiskTracks[i].SideNumber = data[p++]; DiskTracks[i].DataRate = data[p++]; DiskTracks[i].RecordingMode = data[p++]; DiskTracks[i].SectorSize = data[p++]; DiskTracks[i].NumberOfSectors = data[p++]; DiskTracks[i].GAP3Length = data[p++]; DiskTracks[i].FillerByte = data[p++]; int dpos = pos + 0x100; // sector info list DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) { DiskTracks[i].Sectors[s] = new Sector(); DiskTracks[i].Sectors[s].TrackNumber = data[p++]; DiskTracks[i].Sectors[s].SideNumber = data[p++]; DiskTracks[i].Sectors[s].SectorID = data[p++]; DiskTracks[i].Sectors[s].SectorSize = data[p++]; DiskTracks[i].Sectors[s].Status1 = data[p++]; DiskTracks[i].Sectors[s].Status2 = data[p++]; DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); p += 2; // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; // copy the data for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) { DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; } // check for multiple weak/random sectors stored if (DiskTracks[i].Sectors[s].SectorSize <= 7) { // sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize; if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength) { // more data stored than sectorsize defines // check for multiple weak/random copies if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0) { DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true; } } } // move dpos to the next sector data postion dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; } // move to the next track info block pos += DiskHeader.TrackSizes[i]; } // run protection scheme detector ParseProtection(); return(true); }
/// <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); }
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); }
/// <summary> /// Attempts to parse incoming disk data /// </summary> /// <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) { var e = ex.ToString(); } } // now process the blocks var infoBlock = blocks.FirstOrDefault(a => a.RecordType == RecordHeaderType.INFO); var IMGEblocks = blocks.Where(a => a.RecordType == RecordHeaderType.IMGE).ToList(); var DATAblocks = blocks.Where(a => a.RecordType == RecordHeaderType.DATA); 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.FirstOrDefault(a => a.DATAdataKey == img.IMGEdataKey); 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); }
/// <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("MV - CPC")) { // incorrect format return(false); } // read the disk information block DiskHeader.DiskIdent = ident; DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); DiskHeader.NumberOfTracks = data[0x30]; DiskHeader.NumberOfSides = data[0x31]; DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskData = data; int pos = 0x32; if (DiskHeader.NumberOfSides > 1) { StringBuilder sbm = new StringBuilder(); sbm.AppendLine(); sbm.AppendLine(); sbm.AppendLine("The detected disk image contains multiple sides."); sbm.AppendLine("This is NOT currently supported in ZXHawk."); sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); 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++) { DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos); } // move to first track information block pos = 0x100; // parse each track for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { // check for unformatted track if (DiskHeader.TrackSizes[i] == 0) { DiskTracks[i] = new Track(); DiskTracks[i].Sectors = new Sector[0]; continue; } int p = pos; DiskTracks[i] = new Track(); // track info block DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); p += 16; DiskTracks[i].TrackNumber = data[p++]; DiskTracks[i].SideNumber = data[p++]; p += 2; DiskTracks[i].SectorSize = data[p++]; DiskTracks[i].NumberOfSectors = data[p++]; DiskTracks[i].GAP3Length = data[p++]; DiskTracks[i].FillerByte = data[p++]; int dpos = pos + 0x100; // sector info list DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) { DiskTracks[i].Sectors[s] = new Sector(); DiskTracks[i].Sectors[s].TrackNumber = data[p++]; DiskTracks[i].Sectors[s].SideNumber = data[p++]; DiskTracks[i].Sectors[s].SectorID = data[p++]; DiskTracks[i].Sectors[s].SectorSize = data[p++]; DiskTracks[i].Sectors[s].Status1 = data[p++]; DiskTracks[i].Sectors[s].Status2 = data[p++]; DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); p += 2; // actualdatabytelength value is calculated now if (DiskTracks[i].Sectors[s].SectorSize == 0) { // no sectorsize specified - DTL will be used at runtime DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; } else if (DiskTracks[i].Sectors[s].SectorSize > 6) { // invalid - wrap around to 0 DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; } else if (DiskTracks[i].Sectors[s].SectorSize == 6) { // only 0x1800 bytes are stored DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800; } else { // valid sector size for this format DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize; } // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; // copy the data for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) { DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; } // move dpos to the next sector data postion dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; } // move to the next track info block pos += DiskHeader.TrackSizes[i]; } // run protection scheme detector ParseProtection(); 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); }
/// <summary> /// Takes a double-sided disk byte array and converts into 2 single-sided arrays /// </summary> 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> /// 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("EXTENDED CPC DSK")) { // incorrect format return(false); } // read the disk information block DiskHeader.DiskIdent = ident; DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); DiskHeader.NumberOfTracks = data[0x30]; DiskHeader.NumberOfSides = data[0x31]; DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskData = data; int pos = 0x34; for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { DiskHeader.TrackSizes[i] = data[pos++] * 256; } // move to first track information block pos = 0x100; // parse each track for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { // check for unformatted track if (DiskHeader.TrackSizes[i] == 0) { DiskTracks[i] = new Track(); DiskTracks[i].Sectors = new Sector[0]; continue; } int p = pos; DiskTracks[i] = new Track(); // track info block DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); p += 16; DiskTracks[i].TrackNumber = data[p++]; DiskTracks[i].SideNumber = data[p++]; DiskTracks[i].DataRate = data[p++]; DiskTracks[i].RecordingMode = data[p++]; DiskTracks[i].SectorSize = data[p++]; DiskTracks[i].NumberOfSectors = data[p++]; DiskTracks[i].GAP3Length = data[p++]; DiskTracks[i].FillerByte = data[p++]; int dpos = pos + 0x100; // sector info list DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) { DiskTracks[i].Sectors[s] = new Sector(); DiskTracks[i].Sectors[s].TrackNumber = data[p++]; DiskTracks[i].Sectors[s].SideNumber = data[p++]; DiskTracks[i].Sectors[s].SectorID = data[p++]; DiskTracks[i].Sectors[s].SectorSize = data[p++]; DiskTracks[i].Sectors[s].Status1 = data[p++]; DiskTracks[i].Sectors[s].Status2 = data[p++]; DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); p += 2; // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; // copy the data for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) { DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; } // check for multiple weak/random sectors stored if (DiskTracks[i].Sectors[s].SectorSize <= 7) { // sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize; if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength) { // more data stored than sectorsize defines // check for multiple weak/random copies if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0) { DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true; } } } // move dpos to the next sector data postion dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; } // move to the next track info block pos += DiskHeader.TrackSizes[i]; } // run protection scheme detector ParseProtection(); return(true); }