/// <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> /// <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 multi-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> /// 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); }