Pasti file Reader-Writer  1.0
Pasti files
PastiWrite.cs
Go to the documentation of this file.
1 
25 using System;
26 using System.Collections.Generic;
27 using System.Diagnostics;
28 using System.IO;
29 using System.Linq;
30 using System.Text;
31 using System.Threading.Tasks;
32 using System.Windows.Controls;
33 using System.Windows.Media;
34 
35 namespace Pasti {
39  class PastiWriter {
40  TextBox _infoBox;
41 
46  public PastiWriter(TextBox tb) {
47  _infoBox = tb;
48  }
49 
53  public enum WriteStatus {
55  Ok = 0,
62  }
63 
64 
71  public WriteStatus writePasti(string fileName, Floppy fd) {
72  FileStream fs;
73 
74  // encode file into buffer
75  List<byte> sbuf = new List<byte>();
76  WriteStatus status = encodePasti(sbuf, fd);
77 
78 
79  try {
80  fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
81  }
82  catch (Exception exc) {
83  Console.WriteLine("Error: {0}", exc.Message);
84  return WriteStatus.FileNotFound;
85  }
86  fs.Write(sbuf.ToArray(), 0 , sbuf.Count);
87  fs.Close();
88 
89  return status;
90  }
91 
92 
99  private WriteStatus encodePasti(List<byte> buffer, Floppy floppy) {
100  int start = 0;
101 
102  // compute the number of tracks in file
103  int trackCount = 0;
104  for (int track = 0; track < 84; track++)
105  for (int side = 0; side < 2; side++)
106  if (floppy.tracks[track, side] != null) trackCount++;
107 
108  FileDesc fd = new FileDesc();
109  fd.version = 3;
110  fd.revision = 2;
111  //fd.trackCount = floppy.trackCount;
112  fd.tool = 0x0FF;
113 
114  writeByte(buffer, (byte)'R'); // File header
115  writeByte(buffer, (byte)'S');
116  writeByte(buffer, (byte)'Y');
117  writeByte(buffer, 0);
118 
119  writeInt16(buffer, fd.version); // version
120  writeInt16(buffer, fd.tool); // tool
121  writeInt16(buffer, 0); // reserved 1
122  writeByte(buffer, fd.trackCount); // trackCount
123  writeByte(buffer, fd.revision); // revision
124  writeInt32(buffer, 0); // reserved 2
125 
126  _infoBox.Clear();
127  _infoBox.ScrollToHome();
128  _infoBox.FontFamily = new FontFamily("Consolas");
129  _infoBox.FontSize = 14;
130  _infoBox.AppendText(fd.ToString());
131  _infoBox.AppendText(String.Format(" - ({0}-{1})\n", start, buffer.Count-1));
132 
133  // Write all track records
134  //for (int tnum = 0; tnum < floppy.trackCount; tnum++) {
135  for (int track = 0; track < 84; track++)
136  for (int side = 0; side < 2; side++) {
137  if (floppy.tracks[track, side] == null) continue;
138  int startTrackRecord = buffer.Count;
139  TrackDesc td = new TrackDesc();
140 
141  td.recordSize = 16; // initial value = size of TrackDesc
142  //track.trackFlags = 0x0010; // Only bit 5 set to 1 initially
143  td.sectorCount = (ushort)floppy.tracks[track, side].sectors.Count();
144  td.trackLength = (ushort)floppy.tracks[track, side].byteCount;
145  td.trackNumber = (byte)floppy.tracks[track, side].number;
146  if (floppy.tracks[track, side].side == 1) td.trackNumber |= 0x80;
147  td.trackType = 0;
148 
149  // do we have sector descriptors
150  if (trackNeedSectorDesc(floppy.tracks[track, side])) {
152  td.recordSize += (uint)(16 * td.sectorCount);
153  }
154 
155  // do we have fuzzy record
156  for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
157  if (floppy.tracks[track, side].sectors[sect].fuzzyData != null)
158  td.fuzzyCount += (uint)floppy.tracks[track, side].sectors[sect].fuzzyData.Count();
159  td.recordSize += td.fuzzyCount;
160 
161  bool trkSync = false;
162  // do we have track data
163  if (trackNeedTrackData(floppy.tracks[track, side])) {
164  if (trkSync) {
165  td.trackFlags |= TrackDesc.TRK_IMAGE | TrackDesc.TRK_SYNC;
166  td.recordSize += 4; // header
167  }
168  else {
169  td.trackFlags |= TrackDesc.TRK_IMAGE;
170  td.recordSize += 2; // header
171  }
172  td.recordSize += floppy.tracks[track, side].byteCount;
173  // we write even number of bytes
174  td.recordSize += floppy.tracks[track, side].byteCount % 2;
175  }
176 
177  // do we have timing record
178  int timingEntries = 0;
179  for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
180  if (floppy.tracks[track, side].sectors[sect].timmingData != null)
181  timingEntries += floppy.tracks[track, side].sectors[sect].timmingData.Count();
182 
183  if (timingEntries > 0)
184  td.recordSize += (uint)((2 * timingEntries) + 4);
185 
186  // we had up the sector data - here we do not optimize (data shared with track data)
187  for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
188  if (floppy.tracks[track, side].sectors[sect].sectorData != null)
189  td.recordSize += (uint)floppy.tracks[track, side].sectors[sect].sectorData.Count();
190 
192  //td.recordSize += td.recordSize % 2;
193 
194  // Track Descriptor is ready => write it
195  start = buffer.Count;
196  writeInt32(buffer, td.recordSize);
197  writeInt32(buffer, td.fuzzyCount);
198  writeInt16(buffer, td.sectorCount);
199  writeInt16(buffer, td.trackFlags);
200  writeInt16(buffer, td.trackLength);
201  writeByte(buffer, td.trackNumber);
202  writeByte(buffer, td.trackType);
203 
204  _infoBox.AppendText(td.ToString());
205  _infoBox.AppendText(String.Format(" ({0}-{1})\n", start, buffer.Count - 1));
206 
207  // write sector descriptor if needed
208  if (trackNeedSectorDesc(floppy.tracks[track, side])) {
209  uint offset = 0;
210  if (trackNeedTrackData(floppy.tracks[track, side]))
211  offset = (uint)(floppy.tracks[track, side].byteCount + (floppy.tracks[track, side].byteCount % 2) + (trkSync ? 4 : 2));
212  for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
213  SectorDesc sd = new SectorDesc();
214  sd.dataOffset = offset;
215  sd.bitPosition = floppy.tracks[track, side].sectors[sect].bitPosition;
216  sd.readTime = floppy.tracks[track, side].sectors[sect].readTime;
217  sd.id = floppy.tracks[track, side].sectors[sect].id; // TODO ?
218  sd.fdcFlags = floppy.tracks[track, side].sectors[sect].fdcFlags;
219  sd.reserved = 0;
220  if ((floppy.tracks[track, side].sectors[sect].fdcFlags & SectorDesc.SECT_RNF) == 0)
221  offset += (uint)(128 << floppy.tracks[track, side].sectors[sect].id.size);
222 
223  start = buffer.Count;
224  writeInt32(buffer, sd.dataOffset);
225  writeInt16(buffer, sd.bitPosition);
226  writeInt16(buffer, sd.readTime);
227 
228  writeByte(buffer, sd.id.track);
229  writeByte(buffer, sd.id.side);
230  writeByte(buffer, sd.id.number);
231  writeByte(buffer, sd.id.size);
232  writeInt16(buffer, sd.id.crc);
233 
234  writeByte(buffer, sd.fdcFlags);
235  writeByte(buffer, sd.reserved);
236 
237  _infoBox.AppendText(sd.ToString());
238  _infoBox.AppendText(String.Format(" ({0}-{1})\n", start, buffer.Count - 1));
239  }
240  }
241 
242  // write fuzzy mask if needed
243  for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
244  if (floppy.tracks[track, side].sectors[sect].fuzzyData != null) {
245  start = buffer.Count;
246  for (int i = 0; i < floppy.tracks[track, side].sectors[sect].fuzzyData.Count(); i++)
247  writeByte(buffer, floppy.tracks[track, side].sectors[sect].fuzzyData[i]);
248  _infoBox.AppendText(String.Format(" Writing Fuzzy {0} bytes for sector {1} ({2}-{3})\n",
249  floppy.tracks[track, side].sectors[sect].fuzzyData.Count(), floppy.tracks[track, side].sectors[sect].id.number, start, buffer.Count - 1));
250  } // fuzzy found
251  } // all sect
252 
253 
254  // the track data are located after the sector descriptor records & fuzzy record
255  int track_data_start = buffer.Count; // store the position of data in track record
256  _infoBox.AppendText(String.Format(" Start of Track data {0}\n", track_data_start));
257 
258  // write track data if needed
259  if (trackNeedTrackData(floppy.tracks[track, side])) {
260  start = buffer.Count;
261  if (trkSync)
262  writeInt16(buffer, floppy.tracks[track, side].firstSyncOffset);
263  writeInt16(buffer, (ushort)floppy.tracks[track, side].byteCount);
264  for (int i = 0; i < floppy.tracks[track, side].byteCount; i++)
265  writeByte(buffer, floppy.tracks[track, side].trackData[i]);
266  // we write even number
267  if ((floppy.tracks[track, side].byteCount % 2) != 0)
268  writeByte(buffer, 0xFF);
269 
270  _infoBox.AppendText(String.Format(" Writing Track {0}{1} bytes SyncPos={2} ({3}-{4})\n",
271  floppy.tracks[track, side].byteCount + ((trkSync) ? 4 : 2), ((floppy.tracks[track, side].byteCount % 2) != 0) ? "(+1)" : "",
272  floppy.tracks[track, side].firstSyncOffset, start, buffer.Count - 1));
273  }
274 
275  // write all sectors data
276  for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
277  start = buffer.Count;
278  if ((floppy.tracks[track, side].sectors[sect].fdcFlags & SectorDesc.SECT_RNF) == 0) {
279  for (int i = 0; i < floppy.tracks[track, side].sectors[sect].sectorData.Count(); i++)
280  writeByte(buffer, floppy.tracks[track, side].sectors[sect].sectorData[i]);
281  _infoBox.AppendText(String.Format(" Writing Sector {0} {1} bytes ({2}-{3})\n",
282  floppy.tracks[track, side].sectors[sect].id.number, floppy.tracks[track, side].sectors[sect].sectorData.Count(), start, buffer.Count - 1));
283  }
284  }
285 
286  // write timing if necessary
287  if (timingEntries > 0) {
288  // write header
289  start = buffer.Count;
290  writeInt16(buffer, 0x05);
291  writeInt16(buffer, (ushort)(2 * timingEntries + 4));
292  _infoBox.AppendText(String.Format(" Writing Timing header Flag=5 size={0} ({1}-{2})\n", 2 * timingEntries + 4, start, buffer.Count - 1));
293  // write timing
294  for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
295  if (floppy.tracks[track, side].sectors[sect].timmingData != null) {
296  start = buffer.Count;
297  for (int i = 0; i < floppy.tracks[track, side].sectors[sect].timmingData.Count(); i++) {
298  // Big Indian value
299  buffer.Add((byte)(floppy.tracks[track, side].sectors[sect].timmingData[i] << 8));
300  buffer.Add((byte)floppy.tracks[track, side].sectors[sect].timmingData[i]);
301  }
302  _infoBox.AppendText(String.Format(" Writing Timing {0} values for sector {1} ({2}-{3})\n",
303  floppy.tracks[track, side].sectors[sect].timmingData.Count(), floppy.tracks[track, side].sectors[sect].id.number, start, buffer.Count - 1));
304  } // timing found
305  } // all sect
306  }
307 
308  if (buffer.Count % 2 != 0) {
309  writeByte(buffer, 0xFF); // dummy extra byte
310  } // even
311 
312  Debug.Assert(buffer.Count == startTrackRecord + td.recordSize, "Invalid Track Size",
313  String.Format("Track {0} Current pointer position = {1} next track = {2}", td.trackNumber, buffer.Count, startTrackRecord + td.recordSize));
314  } // for all tracks
315 
316  return WriteStatus.Ok;
317 
318  } // encode pasti
319 
320 
321  private static void writeInt32(List<byte> buffer, uint val) {
322  buffer.Add((byte)(val & 0xFF));
323  buffer.Add((byte)((val >> 8) & 0xFF));
324  buffer.Add((byte)((val >> 16) & 0xFF));
325  buffer.Add((byte)((val >> 24) & 0xFF));
326  }
327 
328 
329  private static void writeInt16(List<byte> buffer, ushort val) {
330  buffer.Add((byte)(val & 0xFF));
331  buffer.Add((byte)(val >> 8));
332  }
333 
334  private static void writeByte(List<byte> buffer, byte val) {
335  buffer.Add(val);
336  }
337 
338 
339  private bool trackNeedSectorDesc(Track track) {
340  // here we test for
341  // - All the sectors of the track are numbered sequentially from 1 to n
342  // - All the sectors use valid sector numbers from 0x00-0xF4
343  // - All the sectors of the track have unique sector number
344  // - All sectors in the track have standard timing values
345  // - invalid track number
346  // - Special Flags: No data, CRC, DDAM (cover sector no data)
347 
348  int secnum = 1;
349  for (int sect = 0; sect < track.sectorCount; sect++) {
350  // sequential sector number 1..n
351  if (track.sectors[sect].id.number != secnum++)
352  track.standardSectors = false;
353 
354  // duplicate sector
355  for (int j = sect + 1; j < track.sectorCount; j++)
356  if (track.sectors[sect].id.number == track.sectors[j].id.number)
357  track.standardSectors = false;
358 
359  // sector with invalid sector number
360  if ((track.sectors[sect].id.number >= 0xF5) && (track.sectors[sect].id.number <= 0xF7))
361  track.standardSectors = false;
362 
363  // sector with invalid track number
364  if (track.sectors[sect].id.track != track.number)
365  track.standardSectors = false;
366 
367  // sector with invalid side/head
368  if (track.sectors[sect].id.side != track.side)
369  track.standardSectors = false;
370 
371  // sector with invalid size
372  if (track.sectors[sect].id.size > 3)
373  track.standardSectors = false;
374 
375  // short long sector time
376  if (track.sectors[sect].readTime != 0)
377  track.standardSectors = false;
378 
379  // FDC Flags - Here we read the saved flags
380 
381  // RNF = sector with no data
382  //if ((track.sectors[sect].fdcFlags & SectorDesc.SECT_RNF) != 0)
383  if (track.sectors[sect].sectorData == null)
384  track.standardSectors = false;
385 
386  // CRC error = ID / Data
387  if ((track.sectors[sect].fdcFlags & SectorDesc.CRC_ERR) != 0)
388  track.standardSectors = false;
389 
390  // Record type = DDAM
391  if ((track.sectors[sect].fdcFlags & SectorDesc.REC_TYPE) != 0)
392  track.standardSectors = false;
393 
394  // FDC - Fuzzy
395  if (track.sectors[sect].fuzzyData != null)
396  track.standardSectors = false;
397 
398  // FDC - Timing
399  if (track.sectors[sect].timmingData != null)
400  track.standardSectors = false;
401  }
402  if (trackNeedTrackData(track))
403  return true;
404  return !track.standardSectors;
405  }
406 
407 
408  private bool trackNeedTrackData(Track track) {
409  // We need Optional Track Data when:
410  // - Single Data Segment = ID found but no data
411  // - Invalid Data in Gap ?
412  // - Shifted Track: Data over Index + Data beyond Index + ID over index
413  // - Invalid sync mark sequence
414  // - Sector within Sector
415  // - No Flux Area
416  // - Short long track ??
417  // - SWS
418  // - no data
419 
420  // for now we are cheating by using the TRK_IMAGE flag
421  return !track.standardTrack;
422  }
423 
424  }
425 
426 }
Pasti File Track Descriptor
Definition: PastiStruct.cs:79
Track[,] tracks
Array of Tracks
Definition: FloppyStruct.cs:90
Pasti File Header Descriptor
Definition: PastiStruct.cs:40
override string ToString()
Override ToString() to display Track header information
Definition: PastiStruct.cs:128
ushort trackFlags
This field contain bit-mask flags that provide information about the content of the track record as f...
Definition: PastiStruct.cs:106
override string ToString()
Override ToString() to display File header information
Definition: PastiStruct.cs:69
byte trackType
track image type (not used)
Definition: PastiStruct.cs:112
uint sectorCount
Number of sectors for this track
Definition: FloppyStruct.cs:58
ushort sectorCount
number of sectors in the track
Definition: PastiStruct.cs:92
uint fuzzyCount
number of bytes in the fuzzy mask
Definition: PastiStruct.cs:87
ushort bitPosition
position in the track of the sector address field in bits
Definition: FloppyStruct.cs:42
const byte TRK_SECT_DESC
The track record contains sectorCount SectDesc
Definition: PastiStruct.cs:122
The Pasti Writer Class
Definition: PastiWrite.cs:39
byte size
Size value from address field
Definition: PastiStruct.cs:149
byte[] trackData
buffer for the track data if necessary
Definition: FloppyStruct.cs:61
byte trackCount
Number of track records
Definition: PastiStruct.cs:56
ushort trackLength
length of the track in number of bytes. Usually around 6250 bytes.
Definition: PastiStruct.cs:108
ushort version
File version number: Should be equal to 3.
Definition: PastiStruct.cs:46
Contains information about a complete Floppy disk
Definition: FloppyStruct.cs:88
byte trackNumber
Bit 7 contains the side (0 or 1) and bits 6-0 contain the track number (usually 0 to 79)...
Definition: PastiStruct.cs:110
IDField id
Address field content
Definition: FloppyStruct.cs:46
PastiWriter(TextBox tb)
The Pasti Writer Constructor
Definition: PastiWrite.cs:46
ushort firstSyncOffset
First sync byte offset: This is the offset in byte of the first 0xA1 sync byte found in the track...
Definition: FloppyStruct.cs:65
byte number
Sector number from address field
Definition: PastiStruct.cs:147
uint byteCount
Number of bytes in the track
Definition: FloppyStruct.cs:68
uint number
track number
Definition: FloppyStruct.cs:71
Sector[] sectors
Array of Sectors
Definition: FloppyStruct.cs:55
ushort readTime
read time of the track in ms or 0 if standard sector
Definition: FloppyStruct.cs:44
byte[] sectorData
buffer for the sector data
Definition: FloppyStruct.cs:36
WriteStatus
Status returned when reading Pasti file
Definition: PastiWrite.cs:53
ushort tool
Tool used to create image
Definition: PastiStruct.cs:52
uint recordSize
Total size of this track record.
Definition: PastiStruct.cs:83
byte revision
revision number of the file.
Definition: PastiStruct.cs:61
ushort[] timmingData
buffer for timing bytes if necessary
Definition: FloppyStruct.cs:40
Could not open file for reading or file not found
File read correctly
WriteStatus writePasti(string fileName, Floppy fd)
Read a Pasti file and fills the Floppy structure
Definition: PastiWrite.cs:71
byte[] fuzzyData
buffer for fuzzy mask bytes if necessary
Definition: FloppyStruct.cs:38
The header in the file is not RSY
uint side
track side
Definition: FloppyStruct.cs:74
byte fdcFlags
Status returned by the FDC
Definition: FloppyStruct.cs:48