27 using System.Collections.Generic;
30 using System.Threading.Tasks;
33 using System.Windows.Controls;
34 using System.Windows.Media;
63 fs =
new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
65 catch (Exception exc) {
66 Console.WriteLine(
"Error: {0}", exc.Message);
71 byte[] sbuf =
new byte[fs.Length];
72 int bytes = fs.Read(sbuf, 0, (
int)fs.Length);
73 Debug.Assert(bytes == fs.Length);
75 bool status = decodeIPF(sbuf, fd);
86 private bool decodeIPF(byte[] buffer,
Floppy fd) {
92 fd.tracks =
new Track[84, 2];
96 Dictionary<uint, ImageRecord> images =
new Dictionary<uint, ImageRecord>();
99 _infoBox.FontFamily =
new FontFamily(
"Consolas");
100 _infoBox.FontSize = 14;
101 _infoBox.ScrollToHome();
103 while (currentPos < buffer.Count()) {
105 startPos = currentPos;
106 recordType = readHeader(header, buffer, ref currentPos);
108 displayHeader(header, startPos, crc);
109 if (crc != header.
crc)
return false;
111 switch (recordType) {
114 _infoBox.AppendText(
"IPF file error: CAPS record must be first");
120 if (startPos != 12) {
121 _infoBox.AppendText(
"IPF file error: INFO record must be second");
124 readInfoRecord(info, buffer, ref currentPos);
126 _infoBox.AppendText(info.
ToString());
130 ImageRecord image = readImageRecord(buffer, ref currentPos, fd);
131 images.Add(image.dataKey, image);
132 _infoBox.AppendText(image.ToString());
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");
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));
147 if (data.length > 0) {
148 startPos = currentPos;
149 readExtraDataSegment(buffer, currentPos, info.
encoderType, img, fd);
150 currentPos = startPos + data.length;
155 CteiRecord ctei = readCteiRecord(buffer, ref currentPos);
156 _infoBox.AppendText(ctei.ToString());
160 CtexRecord ctex = readCtexRecord(buffer, ref currentPos);
161 _infoBox.AppendText(ctex.ToString());
165 displayHeader(header, startPos, crc);
166 _infoBox.AppendText(
"IPF file error: Unknown record");
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);
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));
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);
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);
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");
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;
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);
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);
269 bd.blockFlags = (
BlockFlags)readUInt32(buffer, ref currentPos);
270 bd.gapDefaultValue = readUInt32(buffer, ref currentPos);
271 bd.dataOffset = readUInt32(buffer, ref currentPos);
276 private void readExtraDataSegment(byte[] buffer, uint currentPos,
EncoderType ipfEncoder, ImageRecord img, Floppy fd) {
277 StringBuilder dataString =
new StringBuilder();
278 uint startDataArea = currentPos;
280 for (uint i = 0; i < img.blockCount; i++) {
281 uint startPos = currentPos;
282 BlockDescriptor bd = readBlockDescriptor(buffer, ref currentPos);
284 Sector sector =
new Sector();
285 sector.dataBits = bd.dataBits;
286 sector.gapBits = bd.gapBits;
287 sector.flags = bd.blockFlags;
289 string bdString = null;
290 switch (ipfEncoder) {
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);
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);
301 dataString.Append(String.Format(
"{0} ({1}-{2})\n", bdString, startPos, currentPos - 1));
304 if ((ipfEncoder ==
EncoderType.SPS) && (bd.gapBits > 0)) {
305 uint gapPos = startDataArea + bd.gapOffset;
306 uint gapBitsSpecified = 0;
308 if (_dataElem.IsChecked ==
true)
309 dataString.Append(String.Format(
" --- GAP --- {0} bits @{1}\n",
310 bd.gapBits, gapPos));
312 if (bd.blockFlags.HasFlag(
BlockFlags.FwGap)) {
314 while ((gapHead = buffer[gapPos++]) != 0) {
315 uint startGapElemPos = gapPos - 1;
318 int gapSizeWidth = gapHead >> 5;
320 for (
int n = 0; n < gapSizeWidth; n++) {
321 gapSize = (gapSize << 8) + buffer[gapPos++];
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));
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();
337 gap.gapBytes = gapBytes;
338 gap.value = gapSample[0];
339 sector.gapElems.Add(gap);
343 gapBitsSpecified += gapSize;
344 gapBytes = gapSize / 8;
346 if (_dataElem.IsChecked ==
true) dataString.Append(String.Format(
" @{0}\n", startGapElemPos));
350 if (bd.blockFlags.HasFlag(
BlockFlags.BwGap)) {
352 while ((gapHead = buffer[gapPos++]) != 0) {
353 uint startGapElemPos = gapPos - 1;
356 int gapSizeWidth = gapHead >> 5;
358 for (
int n = 0; n < gapSizeWidth; n++) {
359 gapSize = (gapSize << 8) + buffer[gapPos++];
361 if (_dataElem.IsChecked ==
true)
362 dataString.Append(String.Format(
" Backward GSW={0} {1} {2} bits",
363 gapSizeWidth, gapType.ToString(), gapSize));
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();
374 gap.gapBytes = gapBytes;
375 gap.value = gapSample[0];
376 sector.gapElems.Add(gap);
380 gapBytes = gapSize / 8;
381 gapBitsSpecified += gapSize;
383 if (_dataElem.IsChecked ==
true) dataString.Append(String.Format(
" @{0}\n", startGapElemPos));
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));
392 dataString.Append(String.Format(
" complete gap specification {0} bits\n", bd.gapBits));
397 if (bd.dataBits > 0) {
398 uint dataPos = startDataArea + bd.dataOffset;
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();
406 int dataSizeWidth = dataHead >> 5;
408 for (
int n = 0; n < dataSizeWidth; n++) {
409 dataSize = (dataSize << 8) + buffer[dataPos++];
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));
419 if (_dataElem.IsChecked ==
true) {
420 dataString.Append(String.Format(
" {0} DSW={1} sample {2} bytes @{3}",
421 dataType.ToString(), dataSizeWidth, dataSize, dataPos));
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));
431 data.dataBytes = (uint)dataSample.Count;
432 data.type = dataType;
433 data.value = dataSample;
434 sector.dataElems.Add(data);
436 if (_dataElem.IsChecked ==
true) dataString.Append(
"\n");
439 fd.tracks[img.track, img.side].sectors.Add(sector);
441 _infoBox.AppendText(dataString.ToString());
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);
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);
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);
476 private static char readChar(byte[] buf, ref uint pos) {
477 return (
char)buf[pos++];
BlockFlags
Flags for a block descriptor
DataType
Type of Data in data element
Store information about all tracks of a FD
EncoderType
Image encoder Type
MediaType
Type of media imaged
GapType
Type of a gap element
Platform
Platform on which the image can be run
SignalType
Signal Processing Type
GapElemType
Type of information in a gap element
The info record definition
Density
The cell density for this track
EncoderType encoderType
image encoder type
TrackFlags
Flags for the track
IPFReader(TextBox tb, CheckBox de)
The IPF reader constructor
Store information about one track
BlockEncoderType
Block Encoder Type
bool readIPF(string fileName, Floppy fd)
Read an IPF file and fills the Floppy structure
override string ToString()
Override ToString() to display info record content