IPF Image-File Reader-Writer  1.0
IPF File information
IPFWriter.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Linq;
6 using System.Text;
7 using System.Threading.Tasks;
8 using System.Windows.Controls;
9 
10 namespace ipf {
11  class IPFWriter {
12  private TextBox _infoBox;
13 
18  public IPFWriter(TextBox tb) {
19  _infoBox = tb;
20  }
21 
22 
29  public bool writeIPF(string fileName, Floppy fd) {
30  FileStream fs;
31 
32  // encode file into buffer
33  List<byte> buffer = new List<byte>();
34  bool status = encodeIPF(buffer, fd);
35 
36  try {
37  fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
38  }
39  catch (Exception exc) {
40  Console.WriteLine("Error: {0}", exc.Message);
41  return false;
42  }
43  fs.Write(buffer.ToArray(), 0, buffer.Count);
44  fs.Close();
45 
46  return status;
47  }
48 
49 
50  private bool encodeIPF(List<byte> buffer, Floppy fd) {
51  uint crc;
52  int start;
53  int crc_pos;
54 
55  // write CAPS Record
56  start = buffer.Count;
57  writeByte(buffer, (byte)'C'); // File header
58  writeByte(buffer, (byte)'A');
59  writeByte(buffer, (byte)'P');
60  writeByte(buffer, (byte)'S');
61  writeInt32(buffer, 12); // record length
62  crc_pos = buffer.Count;
63  writeInt32(buffer, 0); // CRC
64 
65  crc = Utilities.crc32Buffer(buffer, 0, 12);
66  writeInt32(buffer, crc_pos, crc);
67 
68  // in real life creating an info record may look like the commented code below
69 
70  //DateTime now = DateTime.Now;
71  //int date = now.Year * 10000 + now.Month * 100 + now.Day;
72  //int time = now.Hour * 10000000 + now.Minute * 100000 + now.Second * 1000 + now.Millisecond;
73 
74  //InfoRecord info = new InfoRecord();
75  //info.mediaType = MediaType.Floppy_Disk;
76  //info.encoderType = EncoderType.SPS;
77  //info.encoderRev = 1;
78  //info.fileKey = 0;
79  //info.fileRev = 1;
80  //info.origin = 0;
81  //info.minTrack = 0;
82  //info.maxTrack = 83;
83  //info.minSide = 0;
84  //info.maxSide = 1;
85  //info.creationDate = (uint)date;
86  //info.creationTime = (uint)time;
87  //info.platforms = new Platform[4];
88  //info.platforms[0] = Platform.Atari_ST;
89  //info.diskNumber = 1;
90  //info.creatorId = 0xAFAFAFAF;
91  //info.reserved = new uint[3];
92 
93  // but to compare with original ipf file we just reuse the same content
94  InfoRecord info = fd.info;
95 
96  // Write INFO Record
97  start = buffer.Count;
98  writeByte(buffer, (byte)'I'); // File header
99  writeByte(buffer, (byte)'N');
100  writeByte(buffer, (byte)'F');
101  writeByte(buffer, (byte)'O');
102  writeInt32(buffer, 96); // record length
103  crc_pos = buffer.Count;
104  writeInt32(buffer, 0); // CRC
105 
106  writeInt32(buffer, (uint)info.mediaType);
107  writeInt32(buffer, (uint)info.encoderType);
108  writeInt32(buffer, info.encoderRev);
109  writeInt32(buffer, info.fileKey);
110  writeInt32(buffer, info.fileRev);
111  writeInt32(buffer, info.origin);
112  writeInt32(buffer, info.minTrack);
113  writeInt32(buffer, info.maxTrack);
114  writeInt32(buffer, info.minSide);
115  writeInt32(buffer, info.maxSide);
116  writeInt32(buffer, info.creationDate);
117  writeInt32(buffer, info.creationTime);
118  for (int i = 0; i < 4; i++)
119  writeInt32(buffer, (uint)info.platforms[i]);
120  writeInt32(buffer, info.diskNumber);
121  writeInt32(buffer, info.creatorId);
122  for (int i = 0; i < 3; i++)
123  writeInt32(buffer, info.reserved[i]);
124 
125  crc = Utilities.crc32Buffer(buffer, start, 96);
126  writeInt32(buffer, crc_pos, crc);
127 
128  // write IMGE record
129  uint dataKey = 1;
130  for (uint track = 0; track < 84; track++) {
131  for (uint side = 0; side < 2; side++) {
132  Track t = fd.tracks[track, side];
133 
134  ImageRecord image = new ImageRecord();
135  image.track = track;
136  image.side = side;
137  image.density = t.density;
138  image.signalType = SignalType.cell_2us;
139  image.trackBytes = t.trackBytes;
140  image.startBytePos = t.startBitPos / 8;
141  image.startBitPos = t.startBitPos;
142  image.dataBits = t.dataBits;
143  image.gapBits = t.gapBits;
144  image.trackBits = t.dataBits + t.gapBits;
145  image.blockCount = t.blockCount;
146  image.encoder = 0;
147  image.trackFlags = t.trackFlags;
148  image.dataKey = dataKey++;
149  image.reserved = new uint[4];
150 
151  // Write IMGE Record
152  start = buffer.Count;
153  writeByte(buffer, (byte)'I'); // File header
154  writeByte(buffer, (byte)'M');
155  writeByte(buffer, (byte)'G');
156  writeByte(buffer, (byte)'E');
157  writeInt32(buffer, 80);
158  crc_pos = buffer.Count;
159  writeInt32(buffer, 0);
160 
161  writeInt32(buffer, image.track);
162  writeInt32(buffer, image.side);
163  writeInt32(buffer, (uint)image.density);
164  writeInt32(buffer, (uint)image.signalType);
165  writeInt32(buffer, image.trackBytes);
166  writeInt32(buffer, image.startBytePos);
167  writeInt32(buffer, image.startBitPos);
168  writeInt32(buffer, image.dataBits);
169  writeInt32(buffer, image.gapBits);
170  writeInt32(buffer, image.trackBits);
171  writeInt32(buffer, image.blockCount);
172  writeInt32(buffer, image.encoder);
173  writeInt32(buffer, (uint)image.trackFlags);
174  writeInt32(buffer, image.dataKey);
175  for (int i = 0; i < 3; i++)
176  writeInt32(buffer, image.reserved[i]);
177 
178  crc = Utilities.crc32Buffer(buffer, start, 80);
179  writeInt32(buffer, crc_pos, crc);
180  }
181  }
182 
183  dataKey = 1;
184  for (uint track = 0; track < 84; track++) {
185  for (uint side = 0; side < 2; side++) {
186  Track t = fd.tracks[track, side];
187 
188  start = buffer.Count;
189  writeByte(buffer, (byte)'D'); // File header
190  writeByte(buffer, (byte)'A');
191  writeByte(buffer, (byte)'T');
192  writeByte(buffer, (byte)'A');
193  writeInt32(buffer, 28); // record length
194  crc_pos = buffer.Count;
195  writeInt32(buffer, 0); // CRC
196 
197  // here we build the Extra DATA Block buffer
198  List<byte> extraDataBlock = new List<byte>();
199 
200  // we first write the block descriptors
201  foreach (Sector sector in t.sectors) {
202  BlockDescriptor bd = new BlockDescriptor();
203  bd.dataBits = sector.dataBits;
204  bd.gapBits = sector.gapBits;
205  bd.gapOffset = 0;
206  bd.cellType = SignalType.cell_2us;
207  bd.encoderType = BlockEncoderType.MFM;
208  bd.blockFlags = sector.flags;
209  bd.gapDefaultValue = 0;
210  bd.dataOffset = 0;
211 
212  writeInt32(extraDataBlock, bd.dataBits);
213  writeInt32(extraDataBlock, bd.gapBits);
214  writeInt32(extraDataBlock, bd.gapOffset);
215  writeInt32(extraDataBlock, (uint)bd.cellType);
216  writeInt32(extraDataBlock, (uint)bd.encoderType);
217  writeInt32(extraDataBlock, (uint)bd.blockFlags);
218  writeInt32(extraDataBlock, bd.gapDefaultValue);
219  writeInt32(extraDataBlock, bd.dataOffset);
220  }
221 
222  // then we write the set of Gap stream elements
223  int gapOffset = t.sectors.Count * 32;
224  int secNum = 0;
225  foreach (Sector sector in t.sectors) {
226 
227  // correction of gapOffset in block descriptor
228  if (!sector.flags.Equals(BlockFlags.None))
229  writeInt32(extraDataBlock, (secNum * 32) + 8, (uint)gapOffset);
230 
231  if (sector.flags.HasFlag(BlockFlags.FwGap)) {
232  foreach (GapElement ge in sector.gapElems) {
233  if (ge.type != GapType.Forward) continue;
234  writeGapElem(extraDataBlock, ge.gapBytes, ge.value);
235  gapOffset += (ge.gapBytes == 0) ? 3 : ((ge.gapBytes * 8) < 256) ? 5 : 6;
236  }
237  writeByte(extraDataBlock, 0);
238  gapOffset++;
239  }
240 
241  if (sector.flags.HasFlag(BlockFlags.BwGap)) {
242  foreach (GapElement ge in sector.gapElems) {
243  if (ge.type != GapType.Backward) continue;
244  writeGapElem(extraDataBlock, ge.gapBytes, ge.value);
245  gapOffset += (ge.gapBytes == 0) ? 3 : ((ge.gapBytes * 8) < 256) ? 5 : 6;
246  }
247  writeByte(extraDataBlock, 0);
248  gapOffset++;
249  }
250 
251  secNum++;
252  }
253 
254  // then we write the set of Data stream elements
255  //uint dataOffset = (uint)(t.sectors.Count * 32) + gapOffset;
256  int dataOffset = gapOffset;
257  secNum = 0;
258  foreach (Sector sector in t.sectors) {
259  // correction of dataOffset in block descriptor
260  writeInt32(extraDataBlock, (secNum * 32) + 28, (uint)dataOffset);
261 
262  foreach (DataElem de in sector.dataElems) {
263  writeDataElem(extraDataBlock, de.type, de.dataBytes, de.value);
264  dataOffset += ((de.dataBytes < 255) ? 2 : 3) + (int)de.dataBytes;
265  }
266  writeByte(extraDataBlock, 0);
267  dataOffset++;
268  secNum++;
269  }
270 
271  // now we have the complete extra data segment
272  // we can write the end of the Data record
273  DataRecord data = new DataRecord();
274  data.length = (uint)extraDataBlock.Count;
275  data.bitSize = data.length * 8;
276  data.crc = Utilities.crc32Buffer(extraDataBlock, 0, (int)data.length);
277  data.key = dataKey++;
278 
279  writeInt32(buffer, data.length);
280  writeInt32(buffer, data.bitSize);
281  writeInt32(buffer, data.crc);
282  writeInt32(buffer, data.key);
283 
284  crc = Utilities.crc32Buffer(buffer, start, 28);
285  writeInt32(buffer, crc_pos, crc);
286 
287  // and now copy the extra segment to the buffer
288  foreach (byte b in extraDataBlock)
289  buffer.Add(b);
290 
291  } // all sides
292  } // all tracks
293 
294  return true;
295  }
296 
297 
298  private void writeGapElem(List<byte> buffer, uint byteCount, byte value) {
299  uint bitCount = byteCount * 8;
300  // write GAP length
301  if (bitCount > 0) {
302  if (bitCount < 256) {
303  writeByte(buffer, 0x21); // size=1 & type=1
304  writeByte(buffer, (byte)bitCount);
305  }
306  else {
307  writeByte(buffer, 0x41); // size=2 & type=1
308  writeByte(buffer, (byte)(bitCount / 256));
309  writeByte(buffer, (byte)(bitCount & 0xFF));
310  }
311  }
312  // Write GAP sample value
313  writeByte(buffer, 0x22); // size=1 & type=2
314  writeByte(buffer, 8); // sample size
315  writeByte(buffer, value);
316  }
317 
318 
319  private void writeDataElem(List<byte> buffer, DataType type, uint byteCount, List<byte> value) {
320  byte dataHead = (byte)((int)type + ((byteCount < 256) ? 0x20 : 0x40));
321  writeByte(buffer, dataHead);
322  if (byteCount < 256) {
323  writeByte(buffer, (byte)byteCount);
324  }
325  else {
326  writeByte(buffer, (byte)(byteCount / 256));
327  writeByte(buffer, (byte)(byteCount & 0xFF));
328  }
329  // Write value
330  Debug.Assert(byteCount == value.Count, "Lenght error in dataElem");
331  for (int i = 0; i < byteCount; i++) {
332  writeByte(buffer, value[i]);
333  }
334  }
335 
336 
337  private static void writeInt32(List<byte> buffer, int buf_position, uint value) {
338  buffer[buf_position] = (byte)((value >> 24) & 0xFF);
339  buffer[buf_position + 1] = (byte)((value >> 16) & 0xFF);
340  buffer[buf_position + 2] = (byte)((value >> 8) & 0xFF);
341  buffer[buf_position + 3] = (byte)(value & 0xFF);
342  }
343 
344 
345  private static void writeInt32(List<byte> buffer, uint val) {
346  buffer.Add((byte)((val >> 24) & 0xFF));
347  buffer.Add((byte)((val >> 16) & 0xFF));
348  buffer.Add((byte)((val >> 8) & 0xFF));
349  buffer.Add((byte)(val & 0xFF));
350  }
351 
352 
353  private static void writeByte(List<byte> buffer, byte val) {
354  buffer.Add(val);
355  }
356 
357  }
358 }
uint diskNumber
Number of the disk in a multi-disc release otherwise 0
Definition: IPFStruct.cs:157
uint creatorId
A unique user ID of the disk image creator
Definition: IPFStruct.cs:160
BlockFlags
Flags for a block descriptor
Definition: IPFStruct.cs:53
bool writeIPF(string fileName, Floppy fd)
Write an IPF file from the Floppy structure
Definition: IPFWriter.cs:29
uint fileRev
Revision of the file.
Definition: IPFStruct.cs:123
uint creationTime
The image creation time: Specify the hour, the minute, the second, and the tick
Definition: IPFStruct.cs:149
DataType
Type of Data in data element
Definition: IPFStruct.cs:68
Store information about all tracks of a FD
Definition: Floppy.cs:56
uint origin
Reference to original source
Definition: IPFStruct.cs:126
uint[] reserved
reserved for future
Definition: IPFStruct.cs:163
uint[] reserved
reserved for future
Definition: IPFStruct.cs:203
uint creationDate
The image creation date: Specify the year, the month, the day
Definition: IPFStruct.cs:145
GapType
Type of a gap element
Definition: IPFStruct.cs:71
MediaType mediaType
Type of media imaged
Definition: IPFStruct.cs:103
Definition: App.xaml.cs:9
The image record definition
Definition: IPFStruct.cs:187
uint minTrack
The first track number of the floppy image.
Definition: IPFStruct.cs:130
uint encoderRev
Image encoder revision
Definition: IPFStruct.cs:113
SignalType
Signal Processing Type
Definition: IPFStruct.cs:38
uint minSide
The lowest head (side) number of the floppy image.
Definition: IPFStruct.cs:138
uint maxTrack
The last track number of the floppy image.
Definition: IPFStruct.cs:134
IPFWriter(TextBox tb)
The IPF Writer Constructor
Definition: IPFWriter.cs:18
uint fileKey
Each IPF file has a unique ID that can be used as a unique key for database
Definition: IPFStruct.cs:119
The info record definition
Definition: IPFStruct.cs:100
EncoderType encoderType
image encoder type
Definition: IPFStruct.cs:109
Store information about one track
Definition: Floppy.cs:41
BlockEncoderType
Block Encoder Type
Definition: IPFStruct.cs:48
Platform[] platforms
Array of four possible platforms
Definition: IPFStruct.cs:154
uint maxSide
The highest head (side) number of the floppy image. Usually 1
Definition: IPFStruct.cs:141