IPF Image-File Reader-Writer  1.0
IPF File information
IPFReader.cs
Go to the documentation of this file.
1 
26 using System;
27 using System.Collections.Generic;
28 using System.Linq;
29 using System.Text;
30 using System.Threading.Tasks;
31 using System.Diagnostics;
32 using System.IO;
33 using System.Windows.Controls;
34 using System.Windows.Media;
35 
36 namespace ipf {
37 
38 
42  public class IPFReader {
43  TextBox _infoBox;
44  CheckBox _dataElem;
45 
48  public IPFReader(TextBox tb, CheckBox de) {
49  _infoBox = tb;
50  _dataElem = de;
51  }
52 
53 
60  public bool readIPF(string fileName, Floppy fd) {
61  FileStream fs;
62  try {
63  fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
64  }
65  catch (Exception exc) {
66  Console.WriteLine("Error: {0}", exc.Message);
67  return false;
68  }
69 
70  // read file into buffer
71  byte[] sbuf = new byte[fs.Length];
72  int bytes = fs.Read(sbuf, 0, (int)fs.Length);
73  Debug.Assert(bytes == fs.Length); // should be same
74  fs.Close();
75  bool status = decodeIPF(sbuf, fd);
76  return status;
77  }
78 
79 
86  private bool decodeIPF(byte[] buffer, Floppy fd) {
87  uint currentPos = 0;
88  uint startPos = 0;
89  string recordType;
90  uint crc = 0;
91 
92  fd.tracks = new Track[84, 2];
93 
94  InfoRecord info = new InfoRecord();
95  RecordHeader header = new RecordHeader();
96  Dictionary<uint, ImageRecord> images = new Dictionary<uint, ImageRecord>();
97 
98  _infoBox.Clear();
99  _infoBox.FontFamily = new FontFamily("Consolas");
100  _infoBox.FontSize = 14;
101  _infoBox.ScrollToHome();
102 
103  while (currentPos < buffer.Count()) {
104  // Read Record header
105  startPos = currentPos;
106  recordType = readHeader(header, buffer, ref currentPos);
107  crc = Utilities.crc32Header(buffer, startPos, header.length);
108  displayHeader(header, startPos, crc);
109  if (crc != header.crc) return false;
110 
111  switch (recordType) {
112  case "CAPS":
113  if (startPos != 0) {
114  _infoBox.AppendText("IPF file error: CAPS record must be first");
115  return false;
116  }
117  break;
118 
119  case "INFO":
120  if (startPos != 12) {
121  _infoBox.AppendText("IPF file error: INFO record must be second");
122  return false;
123  }
124  readInfoRecord(info, buffer, ref currentPos);
125  fd.info = info;
126  _infoBox.AppendText(info.ToString());
127  break;
128 
129  case "IMGE":
130  ImageRecord image = readImageRecord(buffer, ref currentPos, fd);
131  images.Add(image.dataKey, image);
132  _infoBox.AppendText(image.ToString());
133  break;
134 
135  case "DATA":
136  DataRecord data = readDataRecord(buffer, ref currentPos);
137  crc = Utilities.crc32Buffer(buffer, currentPos, data.length);
138  if (!images.ContainsKey(data.key)) {
139  _infoBox.AppendText("IPF file error: No matching image key in Data record");
140  return false;
141  }
142  ImageRecord img = images[data.key];
143  _infoBox.AppendText(data.ToString());
144  _infoBox.AppendText(String.Format(" {0} [T{1:D2}.{2}] - ({3}-{4})\n",
145  (crc == data.crc) ? "Good" : "Error", img.track, img.side, currentPos, currentPos + data.length - 1));
146 
147  if (data.length > 0) {
148  startPos = currentPos;
149  readExtraDataSegment(buffer, currentPos, info.encoderType, img, fd);
150  currentPos = startPos + data.length;
151  } // extra data segment
152  break;
153 
154  case "CTEI":
155  CteiRecord ctei = readCteiRecord(buffer, ref currentPos);
156  _infoBox.AppendText(ctei.ToString());
157  break;
158 
159  case "CTEX":
160  CtexRecord ctex = readCtexRecord(buffer, ref currentPos);
161  _infoBox.AppendText(ctex.ToString());
162  break;
163 
164  default:
165  displayHeader(header, startPos, crc);
166  _infoBox.AppendText("IPF file error: Unknown record");
167  return false;
168  }
169 
170  }
171 
172  return true;
173  }
174 
175 
176  private static string readHeader(RecordHeader record, byte[] buffer, ref uint currentPos) {
177  record.type[0] = readChar(buffer, ref currentPos);
178  record.type[1] = readChar(buffer, ref currentPos);
179  record.type[2] = readChar(buffer, ref currentPos);
180  record.type[3] = readChar(buffer, ref currentPos);
181  record.length = readUInt32(buffer, ref currentPos);
182  record.crc = readUInt32(buffer, ref currentPos);
183  return new string(record.type);
184  }
185 
186 
187  private void displayHeader(RecordHeader record, uint startPos, uint crc) {
188  _infoBox.AppendText(record.ToString());
189  _infoBox.AppendText(String.Format(" {0} - ({1}-{2})\n",
190  (crc == record.crc) ? "Good" : "Error", startPos, startPos + record.length - 1));
191  }
192 
193 
194  private static void readInfoRecord(InfoRecord info, byte[] buffer, ref uint bpos) {
195  info.mediaType = (MediaType)readUInt32(buffer, ref bpos);
196  info.encoderType = (EncoderType)readUInt32(buffer, ref bpos);
197  info.encoderRev = readUInt32(buffer, ref bpos);
198  info.fileKey = readUInt32(buffer, ref bpos);
199  info.fileRev = readUInt32(buffer, ref bpos);
200  info.origin = readUInt32(buffer, ref bpos);
201  info.minTrack = readUInt32(buffer, ref bpos);
202  info.maxTrack = readUInt32(buffer, ref bpos);
203  info.minSide = readUInt32(buffer, ref bpos);
204  info.maxSide = readUInt32(buffer, ref bpos);
205  info.creationDate = readUInt32(buffer, ref bpos);
206  info.creationTime = readUInt32(buffer, ref bpos);
207  for (int i = 0; i < 4; i++)
208  info.platforms[i] = (Platform)readUInt32(buffer, ref bpos);
209  info.diskNumber = readUInt32(buffer, ref bpos);
210  info.creatorId = readUInt32(buffer, ref bpos);
211  for (int i = 0; i < 3; i++)
212  info.reserved[i] = readUInt32(buffer, ref bpos);
213  }
214 
215 
216  private static ImageRecord readImageRecord(byte[] buffer, ref uint bpos, Floppy fd) {
217  ImageRecord image = new ImageRecord();
218  image.track = readUInt32(buffer, ref bpos);
219  image.side = readUInt32(buffer, ref bpos);
220  image.density = (Density)readUInt32(buffer, ref bpos);
221  image.signalType = (SignalType)readUInt32(buffer, ref bpos);
222  image.trackBytes = readUInt32(buffer, ref bpos);
223  image.startBytePos = readUInt32(buffer, ref bpos);
224  image.startBitPos = readUInt32(buffer, ref bpos);
225  image.dataBits = readUInt32(buffer, ref bpos);
226  image.gapBits = readUInt32(buffer, ref bpos);
227  image.trackBits = readUInt32(buffer, ref bpos);
228  image.blockCount = readUInt32(buffer, ref bpos);
229  image.encoder = readUInt32(buffer, ref bpos);
230  image.trackFlags = (TrackFlags)readUInt32(buffer, ref bpos);
231  image.dataKey = readUInt32(buffer, ref bpos);
232  for (int i = 0; i < 3; i++)
233  image.reserved[i] = readUInt32(buffer, ref bpos);
234 
235  Debug.Assert(image.trackBits == (image.dataBits + image.gapBits), "Track bit does not equal dataBits + GapBits");
236  Debug.Assert(image.startBytePos == (image.startBitPos / 8), "StratByte does not equal StratBit rounded");
237 
238  Track track = new Track();
239  track.trackBytes = image.trackBytes;
240  track.density = image.density;
241  track.startBitPos = image.startBitPos;
242  track.dataBits = image.dataBits;
243  track.gapBits = image.gapBits;
244  track.blockCount = image.blockCount;
245  track.trackFlags = image.trackFlags;
246  fd.tracks[image.track, image.side] = track;
247 
248  return image;
249  }
250 
251 
252  private static DataRecord readDataRecord(byte[] buffer, ref uint currentPos) {
253  DataRecord data = new DataRecord();
254  data.length = readUInt32(buffer, ref currentPos);
255  data.bitSize = readUInt32(buffer, ref currentPos);
256  data.crc = readUInt32(buffer, ref currentPos);
257  data.key = readUInt32(buffer, ref currentPos);
258  return data;
259  }
260 
261 
262  private static BlockDescriptor readBlockDescriptor(byte[] buffer, ref uint currentPos) {
263  BlockDescriptor bd = new BlockDescriptor();
264  bd.dataBits = readUInt32(buffer, ref currentPos);
265  bd.gapBits = readUInt32(buffer, ref currentPos);
266  bd.gapOffset = readUInt32(buffer, ref currentPos);
267  bd.cellType = (SignalType)readUInt32(buffer, ref currentPos);
268  bd.encoderType = (BlockEncoderType)readUInt32(buffer, ref currentPos);
269  bd.blockFlags = (BlockFlags)readUInt32(buffer, ref currentPos);
270  bd.gapDefaultValue = readUInt32(buffer, ref currentPos);
271  bd.dataOffset = readUInt32(buffer, ref currentPos);
272  return bd;
273  }
274 
275 
276  private void readExtraDataSegment(byte[] buffer, uint currentPos, EncoderType ipfEncoder, ImageRecord img, Floppy fd) {
277  StringBuilder dataString = new StringBuilder();
278  uint startDataArea = currentPos;
279  // read block descriptors
280  for (uint i = 0; i < img.blockCount; i++) {
281  uint startPos = currentPos;
282  BlockDescriptor bd = readBlockDescriptor(buffer, ref currentPos);
283 
284  Sector sector = new Sector();
285  sector.dataBits = bd.dataBits;
286  sector.gapBits = bd.gapBits;
287  sector.flags = bd.blockFlags;
288 
289  string bdString = null;
290  switch (ipfEncoder) {
291  case EncoderType.CAPS:
292  bdString = String.Format(" + Block={0} Data={1} ({2}) Gap={3} ({4}) {5} GapDef={6} DataOff={7:D4}",
293  i, bd.dataBits, bd.dataBytes, bd.gapBits, bd.gapBytes, bd.encoderType, bd.gapDefaultValue, bd.dataOffset);
294  // in caps mode we do not have gap element TODO
295  break;
296  case EncoderType.SPS:
297  bdString = String.Format(" + Block={0} Data={1} Off={2} Gap={3} Off={4} {5} {6} GapDef={7} Flags={8}",
298  i, bd.dataBits, bd.dataOffset, bd.gapBits, bd.gapOffset, bd.cellType.ToString(), bd.encoderType, bd.gapDefaultValue, bd.blockFlags);
299  break;
300  }
301  dataString.Append(String.Format("{0} ({1}-{2})\n", bdString, startPos, currentPos - 1));
302 
303  // read GAP elements
304  if ((ipfEncoder == EncoderType.SPS) && (bd.gapBits > 0)) {
305  uint gapPos = startDataArea + bd.gapOffset;
306  uint gapBitsSpecified = 0;
307  byte gapHead;
308  if (_dataElem.IsChecked == true)
309  dataString.Append(String.Format(" --- GAP --- {0} bits @{1}\n",
310  bd.gapBits, gapPos));
311 
312  if (bd.blockFlags.HasFlag(BlockFlags.FwGap)) {
313  uint gapBytes = 0;
314  while ((gapHead = buffer[gapPos++]) != 0) {
315  uint startGapElemPos = gapPos - 1;
316  GapElement gap;
317  uint gapSize = 0;
318  int gapSizeWidth = gapHead >> 5;
319  GapElemType gapType = (GapElemType)(gapHead & 0x1F);
320  for (int n = 0; n < gapSizeWidth; n++) {
321  gapSize = (gapSize << 8) + buffer[gapPos++];
322  }
323  if (_dataElem.IsChecked == true)
324  if (_dataElem.IsChecked == true)
325  dataString.Append(String.Format(" Forward GSW={0} {1} {2} bits",
326  gapSizeWidth, gapType.ToString(), gapSize));
327  if (gapType == GapElemType.SampleLength) {
328  List<byte> gapSample = new List<byte>();
329  for (int n = 0; n < gapSize / 8; n++)
330  gapSample.Add(buffer[gapPos++]);
331  if (_dataElem.IsChecked == true) dataString.Append(" Value=");
332  foreach (byte b in gapSample)
333  if (_dataElem.IsChecked == true)
334  dataString.Append(String.Format("{0:X2} ", b));
335  gap = new GapElement();
336  gap.type = GapType.Forward;
337  gap.gapBytes = gapBytes;
338  gap.value = gapSample[0];
339  sector.gapElems.Add(gap);
340  gapBytes = 0;
341  }
342  else {
343  gapBitsSpecified += gapSize;
344  gapBytes = gapSize / 8;
345  }
346  if (_dataElem.IsChecked == true) dataString.Append(String.Format(" @{0}\n", startGapElemPos));
347  }
348  } // forward
349 
350  if (bd.blockFlags.HasFlag(BlockFlags.BwGap)) {
351  uint gapBytes = 0;
352  while ((gapHead = buffer[gapPos++]) != 0) {
353  uint startGapElemPos = gapPos - 1;
354  GapElement gap;
355  uint gapSize = 0;
356  int gapSizeWidth = gapHead >> 5;
357  GapElemType gapType = (GapElemType)(gapHead & 0x1F);
358  for (int n = 0; n < gapSizeWidth; n++) {
359  gapSize = (gapSize << 8) + buffer[gapPos++];
360  }
361  if (_dataElem.IsChecked == true)
362  dataString.Append(String.Format(" Backward GSW={0} {1} {2} bits",
363  gapSizeWidth, gapType.ToString(), gapSize));
364  if (gapType == GapElemType.SampleLength) {
365  List<byte> gapSample = new List<byte>();
366  for (int n = 0; n < gapSize / 8; n++)
367  gapSample.Add(buffer[gapPos++]);
368  if (_dataElem.IsChecked == true) dataString.Append(" Value=");
369  foreach (byte b in gapSample)
370  if (_dataElem.IsChecked == true)
371  dataString.Append(String.Format("{0:X2} ", b));
372  gap = new GapElement();
373  gap.type = GapType.Backward;
374  gap.gapBytes = gapBytes;
375  gap.value = gapSample[0];
376  sector.gapElems.Add(gap);
377  gapBytes = 0;
378  }
379  else {
380  gapBytes = gapSize / 8;
381  gapBitsSpecified += gapSize;
382  }
383  if (_dataElem.IsChecked == true) dataString.Append(String.Format(" @{0}\n", startGapElemPos));
384  }
385  } // backward
386 
387  if (_dataElem.IsChecked == true) {
388  if (gapBitsSpecified * 2 != bd.gapBits)
389  dataString.Append(String.Format(" Incomplete gap specification {0} bits out of {1}\n",
390  gapBitsSpecified * 2, bd.gapBits));
391  else
392  dataString.Append(String.Format(" complete gap specification {0} bits\n", bd.gapBits));
393  }
394  } // GAP elements exist
395 
396  // read data elements
397  if (bd.dataBits > 0) {
398  uint dataPos = startDataArea + bd.dataOffset;
399  byte dataHead;
400  if (_dataElem.IsChecked == true)
401  dataString.Append(String.Format(" --- DATA --- {0} bits @{1}\n",
402  bd.dataBits, dataPos));
403  while ((dataHead = buffer[dataPos++]) != 0) {
404  DataElem data = new DataElem();
405  uint dataSize = 0;
406  int dataSizeWidth = dataHead >> 5;
407  DataType dataType = (DataType)(dataHead & 0x1F);
408  for (int n = 0; n < dataSizeWidth; n++) {
409  dataSize = (dataSize << 8) + buffer[dataPos++];
410  }
411 
412  if (bd.blockFlags.HasFlag(BlockFlags.DataInBit)) {
413  if (_dataElem.IsChecked == true)
414  dataString.Append(String.Format(" {0} DSW={1} sample {2} bits @{3}",
415  dataType.ToString(), dataSizeWidth, dataSize, dataPos));
416  }
417  else {
418 
419  if (_dataElem.IsChecked == true) {
420  dataString.Append(String.Format(" {0} DSW={1} sample {2} bytes @{3}",
421  dataType.ToString(), dataSizeWidth, dataSize, dataPos));
422  dataSize *= 8; }
423  }
424 
425  List<byte> dataSample = new List<byte>();
426  for (int n = 0; n < dataSize / 8; n++)
427  dataSample.Add(buffer[dataPos++]);
428  foreach (byte b in dataSample)
429  if (_dataElem.IsChecked == true) dataString.Append(String.Format(" {0:X2}", b));
430 
431  data.dataBytes = (uint)dataSample.Count;
432  data.type = dataType;
433  data.value = dataSample;
434  sector.dataElems.Add(data);
435 
436  if (_dataElem.IsChecked == true) dataString.Append("\n");
437  } // data elem
438  } // data elem exist
439  fd.tracks[img.track, img.side].sectors.Add(sector);
440  } // for all block descriptor
441  _infoBox.AppendText(dataString.ToString());
442  }
443 
444 
445  private static CteiRecord readCteiRecord(byte[] buffer, ref uint bpos) {
446  CteiRecord ctei = new CteiRecord();
447  ctei.releaseCrc = readUInt32(buffer, ref bpos);
448  ctei.analyzerRev = readUInt32(buffer, ref bpos);
449  for (int i = 0; i < ctei.reserved.Count(); i++)
450  ctei.reserved[i] = readUInt32(buffer, ref bpos);
451  return ctei;
452  }
453 
454 
455  private static CtexRecord readCtexRecord(byte[] buffer, ref uint bpos) {
456  CtexRecord ctex = new CtexRecord();
457  ctex.track = readUInt32(buffer, ref bpos);
458  ctex.side = readUInt32(buffer, ref bpos);
459  ctex.density = (Density)readUInt32(buffer, ref bpos);
460  ctex.formatId = readUInt32(buffer, ref bpos);
461  ctex.fix = readUInt32(buffer, ref bpos);
462  ctex.trackSize = readUInt32(buffer, ref bpos);
463  for (int i = 0; i < ctex.reserved.Count(); i++)
464  ctex.reserved[i] = readUInt32(buffer, ref bpos);
465  return ctex;
466  }
467 
468 
469  private static uint readUInt32(byte[] buf, ref uint pos) {
470  uint val = (uint)buf[pos + 3] + (uint)(buf[pos + 2] << 8) + (uint)(buf[pos + 1] << 16) + (uint)(buf[pos] << 24);
471  pos += 4;
472  return val;
473  }
474 
475 
476  private static char readChar(byte[] buf, ref uint pos) {
477  return (char)buf[pos++];
478  }
479 
480  }
481 
482 }
BlockFlags
Flags for a block descriptor
Definition: IPFStruct.cs:53
IPF record Header Descriptor
Definition: IPFStruct.cs:78
DataType
Type of Data in data element
Definition: IPFStruct.cs:68
Store information about all tracks of a FD
Definition: Floppy.cs:56
EncoderType
Image encoder Type
Definition: IPFStruct.cs:13
uint length
Length of the record
Definition: IPFStruct.cs:83
MediaType
Type of media imaged
Definition: IPFStruct.cs:10
GapType
Type of a gap element
Definition: IPFStruct.cs:71
Definition: App.xaml.cs:9
Platform
Platform on which the image can be run
Definition: IPFStruct.cs:27
SignalType
Signal Processing Type
Definition: IPFStruct.cs:38
GapElemType
Type of information in a gap element
Definition: IPFStruct.cs:74
The info record definition
Definition: IPFStruct.cs:100
Density
The cell density for this track
Definition: IPFStruct.cs:32
EncoderType encoderType
image encoder type
Definition: IPFStruct.cs:109
The reader class
Definition: IPFReader.cs:42
TrackFlags
Flags for the track
Definition: IPFStruct.cs:42
IPFReader(TextBox tb, CheckBox de)
The IPF reader constructor
Definition: IPFReader.cs:48
Store information about one track
Definition: Floppy.cs:41
BlockEncoderType
Block Encoder Type
Definition: IPFStruct.cs:48
bool readIPF(string fileName, Floppy fd)
Read an IPF file and fills the Floppy structure
Definition: IPFReader.cs:60
override string ToString()
Override ToString() to display info record content
Definition: IPFStruct.cs:168
uint crc
CRC32 for the complete record
Definition: IPFStruct.cs:86