/[projet1]/public/pc/tools/osdk/main/FloppyBuilder/Floppy.cpp
Defence Force logotype

Contents of /public/pc/tools/osdk/main/FloppyBuilder/Floppy.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1445 - (show annotations)
Thu Mar 15 10:22:57 2018 UTC (2 years ago) by dbug
File size: 37229 byte(s)
FloppyBuilder 1.2:
- Hopefully the CRC of floppies should now be correct (thanks ISS and ThomH)
- Gap2 value changed from 0x22 to 0x4E
1
2 #include "infos.h"
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <sstream>
7 #include <iostream>
8 #include <string>
9 #include <string.h>
10
11 #include <assert.h>
12
13 #include "Floppy.h"
14
15 #include "common.h"
16
17
18
19
20 FloppyHeader::FloppyHeader()
21 {
22 assert(sizeof(*this)==256);
23 memset(this,0,sizeof(*this));
24 }
25
26 FloppyHeader::~FloppyHeader()
27 {
28 }
29
30 bool FloppyHeader::IsValidHeader() const
31 {
32 if (memcmp(m_Signature,"MFM_DISK",8)!=0) return false;
33
34 int sideNumber=GetSideNumber();
35 if ((sideNumber<1) || (sideNumber>2)) return false;
36
37 int trackNumber=GetTrackNumber();
38 if ((trackNumber<30) || (trackNumber>82)) return false;
39
40 return true;
41 }
42
43
44 void FloppyHeader::Clear()
45 {
46 memset(this,0,sizeof(FloppyHeader));
47 }
48
49 void FloppyHeader::SetSignature(const char signature[8])
50 {
51 memcpy(m_Signature,signature,8);
52 }
53
54
55 void FloppyHeader::SetSideNumber(int sideNumber)
56 {
57 m_Sides[0]=(sideNumber>>0)&255;
58 m_Sides[1]=(sideNumber>>8)&255;
59 m_Sides[2]=(sideNumber>>16)&255;
60 m_Sides[3]=(sideNumber>>24)&255;
61 }
62
63 int FloppyHeader::GetSideNumber() const
64 {
65 int sideNumber= ( ( ( ( (m_Sides[3]<<8) | m_Sides[2]) << 8 ) | m_Sides[1]) << 8 ) | m_Sides[0];
66 return sideNumber;
67 }
68
69
70 void FloppyHeader::SetTrackNumber(int trackNumber)
71 {
72 m_Tracks[0]=(trackNumber>>0)&255;
73 m_Tracks[1]=(trackNumber>>8)&255;
74 m_Tracks[2]=(trackNumber>>16)&255;
75 m_Tracks[3]=(trackNumber>>24)&255;
76 }
77
78 int FloppyHeader::GetTrackNumber() const
79 {
80 int trackNumber= ( ( ( ( (m_Tracks[3]<<8) | m_Tracks[2]) << 8 ) | m_Tracks[1]) << 8 ) | m_Tracks[0];
81 return trackNumber;
82 }
83
84 void FloppyHeader::SetGeometry(int geometry)
85 {
86 m_Geometry[0]=(geometry>>0)&255;
87 m_Geometry[1]=(geometry>>8)&255;
88 m_Geometry[2]=(geometry>>16)&255;
89 m_Geometry[3]=(geometry>>24)&255;
90 }
91
92 int FloppyHeader::GetGeometry() const
93 {
94 int geometry= ( ( ( ( (m_Geometry[3]<<8) | m_Geometry[2]) << 8 ) | m_Geometry[1]) << 8 ) | m_Geometry[0];
95 return geometry;
96 }
97
98 int FloppyHeader::FindNumberOfSectors(int& firstSectorOffset,int& sectorInterleave,std::vector<int>& sectorOffsets) const
99 {
100 std::map<int,int> sectorsFound;
101
102 firstSectorOffset=0;
103 sectorInterleave=0;
104
105 /*
106 Format of a track:
107 6400 bytes in total
108 - gap1: 72/12 bytes at the start of the track (with zeroes)
109 Then for each sector:
110 - ?: 4 bytes (A1 A1 A1 FE)
111 - track number: 1 byte (0-40-80...)
112 - side number: 1 byte (0-1)
113 - sector number: 1 byte (1-18-19)
114 - one: 1 byte (1)
115 - crc: 2 bytes (crc of the 8 previous bytes)
116 - gap2: 34 bytes (22xAE , 12x00)
117 - ?: 4 bytes (A1 A1 A1 FB)
118 - data: 256 bytes
119 - crc: 2 bytes (crc of the 256+4 previous bytes)
120 - gap3: 50/46 bytes
121 */
122 unsigned char* trackDataStart=(unsigned char*)(this+1);
123 unsigned char* trackData =trackDataStart;
124 unsigned char* trackDataEnd=trackDataStart+6400;
125
126 int lastSectorFound=0;
127 while (trackData<(trackDataEnd-16))
128 {
129 if ( (trackData[0]==0xa1) && (trackData[1]==0xa1) && (trackData[2]==0xa1) && (trackData[3]==0xfe) )
130 {
131 // Found a marker for a synchronization sequence for a sector [#A1 #A1 #A1], [#FE Track Side Sector tt CRC CRC]
132 int sectorNumber=trackData[6];
133 trackData+=10; // Skip synchronization sequence
134 trackData+=34; // - gap2: 34 bytes (22xAE , 12x00)
135 trackData+=4; // - ?: 4 bytes (A1 A1 A1 FB)
136
137 int sectorOffset=trackData-trackDataStart;
138
139 auto ret=sectorsFound.emplace(std::make_pair(sectorNumber,sectorOffset));
140 if (!ret.second)
141 {
142 ShowError("There's something wrong in the track structure of the floppy, sector %u was already found.",sectorNumber);
143 }
144
145 if (sectorNumber>lastSectorFound)
146 {
147 lastSectorFound=sectorNumber;
148 }
149
150 if (sectorNumber==1)
151 {
152 firstSectorOffset=sectorOffset;
153 }
154 else
155 if (sectorNumber==2)
156 {
157 sectorInterleave=sectorOffset-firstSectorOffset;
158 }
159 trackData+=256; // Sector data
160 }
161 else
162 {
163 trackData++;
164 }
165 }
166 if ((int)sectorsFound.size()!=lastSectorFound)
167 {
168 ShowError("There's something wrong in the track structure of the floppy, the sector id does not make sense.");
169 }
170 sectorOffsets.resize(lastSectorFound);
171
172 for (auto it(sectorsFound.begin());it!=sectorsFound.end();++it)
173 {
174 sectorOffsets[it->first-1]=it->second;
175 }
176 return lastSectorFound;
177 }
178
179
180
181 FileEntry::FileEntry()
182 :m_FloppyNumber(0)
183 ,m_StartSide(0)
184 ,m_StartTrack(0)
185 ,m_StartSector(1)
186 ,m_SectorCount(0)
187 ,m_FinalFileSize(0)
188 ,m_StoredFileSize(0)
189 ,m_CompressionMode(e_CompressionNone)
190 {
191 }
192
193 FileEntry::~FileEntry()
194 {
195 }
196
197
198
199
200 Floppy::Floppy()
201 :m_Buffer(0)
202 ,m_BufferSize(0)
203 ,m_TrackCount(0)
204 ,m_SectorCount(0)
205 ,m_SideCount(0)
206 ,m_OffsetFirstSector(156) // 156 (Location of the first byte of data of the first sector)
207 ,m_InterSectorSpacing(358) // 358 (Number of bytes to skip to go to the next sector: 256+59+43)
208 ,m_CurrentTrack(0)
209 ,m_CurrentSector(1)
210 ,m_CompressionMode(e_CompressionNone)
211 ,m_AllFilesAreResolved(true)
212 ,m_AllowMissingFiles(false)
213 ,m_LastFileWithMetadata(0)
214 ,m_LoaderTrackPosition(0)
215 ,m_LoaderSectorPosition(0)
216 ,m_LoaderLoadAddress(0)
217 {
218 }
219
220
221 Floppy::~Floppy()
222 {
223 free(m_Buffer);
224 }
225
226
227 unsigned short crctab[256] =
228 {
229 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
230 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
231 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
232 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
233 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
234 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
235 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
236 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
237 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
238 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
239 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
240 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
241 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
242 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
243 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
244 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
245 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
246 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
247 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
248 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
249 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
250 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
251 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
252 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
253 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
254 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
255 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
256 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
257 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
258 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
259 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
260 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
261 };
262
263 void compute_crc(unsigned char *ptr,int count)
264 {
265 int i;
266 unsigned short crc=0xFFFF,byte;
267 for (i=0;i<count;i++)
268 {
269 byte= *ptr++;
270 crc=(crc<<8)^crctab[(crc>>8)^byte];
271 }
272 *ptr++=crc>>8;
273 *ptr++=crc&0xFF;
274 }
275
276
277 bool Floppy::CreateDisk(int numberOfSides,int numberOfTracks,int numberOfSectors,int interleave)
278 {
279 // Compute the interleave sector order
280 std::vector<unsigned char> sectorList;
281 sectorList.resize(numberOfSectors);
282
283 unsigned int offset=0;
284 for (unsigned char sector=0;sector<numberOfSectors;sector++)
285 {
286 while (sectorList[offset]!=0)
287 {
288 offset++;
289 if (offset>=sectorList.size())
290 {
291 offset=0;
292 }
293 }
294 sectorList[offset]=sector+1;
295 offset+=interleave;
296 if (offset>=sectorList.size())
297 {
298 offset=0;
299 }
300 }
301
302
303 // Heavily based on MakeDisk and Tap2DSk
304 // Additional comments from "Sedoric a Nu", page 62
305 int gap1,gap2,gap3;
306
307 switch (numberOfSectors)
308 {
309 case 15: case 16: case 17:
310 gap1=72; gap2=34; gap3=50;
311 break;
312
313 case 18:
314 gap1=12; gap2=34; gap3=46;
315 break;
316
317 default:
318 ShowError("Unrealistic sectors per track number\n");
319 return false;
320 }
321
322 m_BufferSize=256+numberOfSides*numberOfTracks*6400;
323 m_Buffer=(unsigned char*)malloc(m_BufferSize);
324 if (m_Buffer)
325 {
326 m_TrackCount =numberOfTracks; // 42
327 m_SectorCount=numberOfSectors; // 17
328 m_SideCount =numberOfSides; // 2
329
330 FloppyHeader& header(*((FloppyHeader*)m_Buffer));
331 header.Clear();
332 header.SetSignature("MFM_DISK");
333 header.SetSideNumber(numberOfSides);
334 header.SetTrackNumber(numberOfTracks);
335 header.SetGeometry(1);
336
337 unsigned char* trackbuf=(unsigned char*)m_Buffer+256;
338 for (unsigned char side=0;side<numberOfSides;side++)
339 {
340 for (unsigned char track=0;track<numberOfTracks;track++)
341 {
342 {
343 int offset=0;
344 for (int i=0;i<gap1-12;i++)
345 {
346 trackbuf[offset++]=0x4E;
347 }
348 for (int sector=0;sector<numberOfSectors;sector++)
349 {
350 for (int i=0;i<12;i++) trackbuf[offset++]=0; // 00 00 00 00 00 00 00 00 00 00 00 00
351 for (int i=0;i<3;i++) trackbuf[offset++]=0xA1; // A1 A1 A1 Synchronization sequence
352 trackbuf[offset++]=0xFE; // FE
353 for (int i=0;i<6;i++) offset++; // TRACK SIDE SECTOR SIZE CRC CRC (Size is 1 for 256 bytes large sectors, 2 for 512 bytes)
354 for (int i=0;i<gap2-12;i++) trackbuf[offset++]=0x4E; // 4E 4E 4E 4E ... (more or less depending of gaps)
355 for (int i=0;i<12;i++) trackbuf[offset++]=0; // 00 00 00 00 00 00 00 00 00 00 00 00
356 for (int i=0;i<3;i++) trackbuf[offset++]=0xA1; // A1 A1 A1 Synchronization sequence
357 trackbuf[offset++]=0xFB; // FB
358 for (int i=0;i<258;i++) offset++; // 256 bytes of DATA + 2 bytes of CRC
359 for (int i=0;i<gap3-12;i++) trackbuf[offset++]=0x4E; // 4E 4E 4E 4E ... (more or less depending of gaps)
360 }
361
362 while (offset<6400)
363 {
364 trackbuf[offset++]=0x4E; // End of track padding
365 }
366 }
367 int offset=gap1;
368 for (unsigned char sector=0;sector<numberOfSectors;sector++)
369 {
370 trackbuf[offset+4]=track;
371 trackbuf[offset+5]=side;
372 trackbuf[offset+6]=sectorList[sector]; //sector+1;
373 trackbuf[offset+7]=1;
374 compute_crc(trackbuf+offset,4+4); // Compute the CRC of [A1 A1 A1|FE|TRACK SIDE SECTOR SIZE]
375 offset+=4+6;
376 offset+=gap2;
377 memset(trackbuf+offset+4,0,256);
378 compute_crc(trackbuf+offset,4+256); // Compute the CRC of [A1 A1 A1|FB|TRACK DATA...]
379 offset+=256+6;
380 offset+=gap3;
381 }
382 trackbuf+=6400;
383 }
384 }
385 if (header.IsValidHeader())
386 {
387 m_SectorCount=header.FindNumberOfSectors(m_OffsetFirstSector,m_InterSectorSpacing,m_SectorOffset); //17; // Can't figure out that from the header Oo
388 return true;
389 }
390 }
391
392 return false;
393 }
394
395
396 bool Floppy::LoadDisk(const char* fileName)
397 {
398 if (LoadFile(fileName,(void*)m_Buffer,m_BufferSize))
399 {
400 const FloppyHeader& header(*((FloppyHeader*)m_Buffer));
401 if (header.IsValidHeader())
402 {
403 m_TrackCount =header.GetTrackNumber();
404 m_SideCount =header.GetSideNumber();
405 m_SectorCount=header.FindNumberOfSectors(m_OffsetFirstSector,m_InterSectorSpacing,m_SectorOffset); //17; // Can't figure out that from the header Oo
406 return true;
407 }
408 }
409 return false;
410 }
411
412
413 bool Floppy::SaveDisk(const char* fileName) const
414 {
415 if (m_Buffer)
416 {
417 return SaveFile(fileName,m_Buffer,m_BufferSize);
418 }
419 return false;
420 }
421
422 /*
423 Début de la piste (facultatif): 80 [#4E], 12 [#00], [#C2 #C2 #C2 #FC] et 50 [#4E] (soit 146 octets selon
424 la norme IBM) ou 40 [#4E], 12 [#00], [#C2 #C2 #C2 #FC] et 40 [#4E] (soit 96 octets pour SEDORIC).
425
426 Pour chaque secteur: 12 [#00], 3 [#A1] [#FE #pp #ff #ss #tt CRC], 22 [#4E], 12 [#00], 3 [#A1], [#FB],
427 les 512 octets, [CRC CRC], 80 octets [#4E] (#tt = #02) (soit 141 + 512 = 653 octets selon la norme IBM)
428 ou 12 [#00], 3 [#A1] [#FE #pp #ff #ss #01 CRC CRC], 22 [#4E], 12 [#00], 3 [#A1], [#FB], les 256
429 octets, [CRC CRC], 12, 30 ou 40 octets [#4E] (selon le nombre de secteurs/piste). Soit environ 256 + (72
430 à 100) = 328 à 356 octets pour SEDORIC.
431
432 Fin de la piste (facultatif): un nombre variable d'octets [#4E
433
434 Selon NIBBLE,
435 une piste IBM compte 146 octets de début de piste + 9 secteurs de 653 octets + 257 octets de fin de piste = 6280 octets.
436 Une piste SEDORIC, formatée à 17 secteurs, compte 96 octets de début de piste + 17 secteurs de 358 octets + 98 octets de fin de piste = 6280 octets.
437 Une piste SEDORIC, formatée à 19 secteurs, compte 0 octet de début de piste + 19 secteurs de 328 octets + 48 octets de fin de piste = 6280 octets.
438 On comprend mieux le manque de fiabilité du formatage en 19 secteurs/piste dû à la faible largeur des zones de sécurité (12 [#4E] entre chaque secteur et 48 octets entre le dernier et le premier).
439
440 Lors de l'élaboration du tampon de formatage SEDORIC, les octets #C2 sont remplacés par des octets
441 #F6, les octets #A1 sont remplacés par des octets #F5 et chaque paire de 2 octets [CRC CRC] et
442 remplacée par un octet #F7. Comme on le voit, nombre de variantes sont utilisées, sauf la zone 22 [#4E],
443 12 [#00], 3 [#A1] qui est strictement obligatoire.
444
445 // From DskTool:
446 15, 16 or 17 sectors: gap1=72; gap2=34; gap3=50;
447 18 sectors: gap1=12; gap2=34; gap3=46;
448 */
449 // Header secteur
450 #define nb_oct_before_sector 59 // Cas de 17 secteurs/pistes !
451 #define nb_oct_after_sector 43 //#define nb_oct_after_sector 31
452
453 unsigned int Floppy::GetDskImageOffset()
454 {
455 unsigned int offset=256; // Add the DSK file header size
456 offset+=m_CurrentTrack*6400; // And move to the correct track
457 //offset+=m_OffsetFirstSector; // Add the offset from the start of track to the data of the first sector
458 offset+=m_SectorOffset[m_CurrentSector-1]; // m_InterSectorSpacing*(m_CurrentSector-1);
459 return offset;
460 }
461
462
463 // 0x0319 -> 793
464 bool Floppy::WriteSector(const char *fileName)
465 {
466 bool isOk=true;
467 if (!m_AllowMissingFiles)
468 {
469 if (!m_Buffer)
470 {
471 return false;
472 }
473
474 std::string filteredFileName(StringTrim(fileName," \t\f\v\n\r"));
475
476 void* buffer;
477 size_t bufferSize;
478
479 if (LoadFile(filteredFileName.c_str(),buffer,bufferSize))
480 {
481 if (bufferSize>256)
482 {
483 ShowError("File '%s' is too large and will not fit in a sector (%d bytes are %d too many).",filteredFileName.c_str(),bufferSize,bufferSize-256);
484 }
485
486 unsigned int sectorOffset=GetDskImageOffset();
487 if (m_BufferSize>sectorOffset+256)
488 {
489 memset((char*)m_Buffer+sectorOffset,0,256); // Clear the entire content of the sector
490 memcpy((char*)m_Buffer+sectorOffset,buffer,bufferSize); // Copy over the content of the file (maybe less than 256 bytes)
491 compute_crc((unsigned char*)m_Buffer+sectorOffset-4,4+256); // Compute the CRC of [A1 A1 A1|FB|TRACK DATA...]
492 }
493 MarkCurrentSectorUsed();
494 printf("Boot sector '%s' installed, %u free bytes remaining in this sector.\n",filteredFileName.c_str(),(unsigned int)(256-bufferSize));
495
496 isOk=MoveToNextSector();
497 free(buffer);
498 }
499 else
500 {
501 ShowError("Boot Sector file '%s' not found",filteredFileName.c_str());
502 }
503 }
504 return isOk;
505 }
506
507
508
509
510 bool Floppy::WriteLoader(const char *fileName,int loadAddress)
511 {
512 if (!m_Buffer)
513 {
514 return false;
515 }
516
517 if (m_LoaderLoadAddress)
518 {
519 ShowError("There can be only one loader on the disk.\n");
520 }
521 m_LoaderTrackPosition =m_CurrentTrack;
522 m_LoaderSectorPosition=m_CurrentSector;
523 m_LoaderLoadAddress =loadAddress;
524
525 void* fileBuffer;
526 size_t fileSize;
527 if (!LoadFile(fileName,fileBuffer,fileSize))
528 {
529 m_AllFilesAreResolved=false;
530 if (!m_AllowMissingFiles)
531 {
532 ShowError("Error can't open loader file '%s'\n",fileName);
533 }
534 const char* message="Place holder file generated by FloppyBuilder";
535 fileBuffer=strdup(message);
536 fileSize =strlen(message);
537 }
538
539 unsigned char* fileData=(unsigned char*)fileBuffer;
540
541 //
542 // Finally write the data to the disk structure
543 //
544 while (fileSize)
545 {
546 unsigned int offset=SetPosition(m_CurrentTrack,m_CurrentSector);
547
548 int sizeToWrite=256;
549 if (fileSize<256)
550 {
551 sizeToWrite=fileSize;
552 }
553 fileSize-=sizeToWrite;
554
555 MarkCurrentSectorUsed();
556 memset((char*)m_Buffer+offset,0,256);
557 memcpy((char*)m_Buffer+offset,fileData,sizeToWrite);
558 compute_crc((unsigned char*)m_Buffer+offset-4,4+256); // Compute the CRC of [A1 A1 A1|FB|TRACK DATA...]
559 fileData+=sizeToWrite;
560
561 if (!MoveToNextSector())
562 {
563 ShowError("Floppy disk is full, not enough space to store '%s'.\n",fileName);
564 }
565 }
566 free(fileBuffer);
567
568 return true;
569 }
570
571
572
573 class TapeInfo
574 {
575 public:
576 TapeInfo()
577 : m_PtrData(nullptr)
578 , m_DataSize(0)
579 , m_StartAddress(0)
580 , m_EndAddress(0)
581 , m_FileType(0)
582 , m_AutoStarts(false)
583 {
584
585 }
586
587 bool ParseHeader(void* fileBuffer,size_t fileSize)
588 {
589 m_DataSize=fileSize;
590 m_PtrData =(unsigned char*)fileBuffer;
591 while (m_DataSize && (m_PtrData[0]==0x16))
592 {
593 m_DataSize--;
594 m_PtrData++;
595 }
596 if (m_DataSize>8 && (m_PtrData[0]==0x24) /*&& (m_PtrData[1]==0x00) && (m_PtrData[2]==0x00)*/ ) // Harrier Attack has 0x24 0x80 0xBF
597 {
598 // At this point at least we have a valid synchro sequence and we know we have a usable header
599 m_FileType = m_PtrData[3];
600 m_AutoStarts =(m_PtrData[4]!=0);
601 m_EndAddress =(m_PtrData[5]<<8)|m_PtrData[6];
602 m_StartAddress=(m_PtrData[7]<<8)|m_PtrData[8];
603
604 m_DataSize-=9;
605 m_PtrData+=9;
606
607 if (m_DataSize /*&& (m_PtrData[0]==0x00)*/ ) // Harrier Attack has 0xE8
608 {
609 // Skip the zero
610 m_DataSize--;
611 m_PtrData++;
612
613 // Now we read the name
614 while (m_DataSize && (m_PtrData[0]!=0x00))
615 {
616 m_FileName+=m_PtrData[0];
617 m_DataSize--;
618 m_PtrData++;
619 }
620 if (m_DataSize && (m_PtrData[0]==0x00) )
621 {
622 // Skip the zero
623 m_DataSize--;
624 m_PtrData++;
625
626 // Now ptr points on the actual data
627 return true;
628 }
629 }
630 }
631 // Not a valid tape file
632 return false;
633 }
634
635 public:
636 unsigned char* m_PtrData;
637 int m_DataSize;
638 int m_StartAddress;
639 int m_EndAddress;
640 int m_FileType;
641 bool m_AutoStarts;
642 std::string m_FileName;
643 };
644
645
646 bool Floppy::WriteFile(const char *fileName,bool removeHeaderIfPresent,const std::map<std::string,std::string>& metadata)
647 {
648 if (!m_Buffer)
649 {
650 return false;
651 }
652
653 void* fileBuffer;
654 size_t fileSize;
655 if (!LoadFile(fileName,fileBuffer,fileSize))
656 {
657 m_AllFilesAreResolved=false;
658 if (!m_AllowMissingFiles)
659 {
660 ShowError("Error can't open file '%s'\n",fileName);
661 }
662 const char* message="Place holder file generated by FloppyBuilder";
663 fileBuffer=strdup(message);
664 fileSize =strlen(message);
665 }
666
667 unsigned char* fileData=(unsigned char*)fileBuffer;
668
669 if (removeHeaderIfPresent)
670 {
671 TapeInfo tapeInfo;
672 if (!tapeInfo.ParseHeader(fileBuffer,fileSize))
673 {
674 ShowError("File '%s' is not a valid tape file\n",fileName);
675 }
676 // If the file was a valid tape header, then we use these new information
677 fileData=tapeInfo.m_PtrData;
678 fileSize=tapeInfo.m_DataSize;
679 }
680
681 FileEntry fileEntry;
682 fileEntry.m_FloppyNumber=0; // 0 for a single floppy program
683
684 if (m_CurrentTrack>41) // face 2
685 {
686 fileEntry.m_StartSide=1;
687 }
688 else
689 {
690 fileEntry.m_StartSide=0;
691 }
692
693 fileEntry.m_StartTrack =m_CurrentTrack; // 0 to 42 (80...)
694 fileEntry.m_StartSector=m_CurrentSector; // 1 to 17 (or 16 or 18...)
695 fileEntry.m_StoredFileSize=fileSize;
696 fileEntry.m_FinalFileSize =fileSize;
697 fileEntry.m_SectorCount=(fileSize+255)/256;
698 fileEntry.m_FilePath =fileName;
699 fileEntry.m_CompressionMode=e_CompressionNone;
700
701 if (!metadata.empty())
702 {
703 fileEntry.m_Metadata = metadata;
704 m_LastFileWithMetadata=m_FileEntries.size();
705 }
706
707 for (auto metadataIt(metadata.begin());metadataIt!=metadata.end();++metadataIt)
708 {
709 m_MetadataCategories.insert(metadataIt->first);
710 }
711
712 std::vector<unsigned char> compressedBuffer;
713 if (m_CompressionMode==e_CompressionFilepack)
714 {
715 // So the user requested FilePack compression.
716 // Great, we can do that.
717 compressedBuffer.resize(fileSize);
718 gLZ77_XorMask=0;
719 size_t compressedFileSize=LZ77_Compress(fileData,compressedBuffer.data(),fileSize);
720 if (compressedFileSize<fileSize)
721 {
722 // We actually did manage to compress the data
723 fileData=compressedBuffer.data();
724 fileSize=compressedFileSize;
725
726 fileEntry.m_CompressionMode=e_CompressionFilepack;
727 fileEntry.m_StoredFileSize=compressedFileSize;
728 fileEntry.m_SectorCount=(fileSize+255)/256;
729 }
730 }
731
732 //
733 // Finally write the data to the disk structure
734 //
735 while (fileSize)
736 {
737 unsigned int offset=SetPosition(m_CurrentTrack,m_CurrentSector);
738
739 int sizeToWrite=256;
740 if (fileSize<256)
741 {
742 sizeToWrite=fileSize;
743 }
744 fileSize-=sizeToWrite;
745
746 MarkCurrentSectorUsed();
747 memset((char*)m_Buffer+offset,0,256);
748 memcpy((char*)m_Buffer+offset,fileData,sizeToWrite);
749 compute_crc((unsigned char*)m_Buffer+offset-4,4+256); // Compute the CRC of [A1 A1 A1|FB|TRACK DATA...]
750 fileData+=sizeToWrite;
751
752 if (!MoveToNextSector())
753 {
754 ShowError("Floppy disk is full, not enough space to store '%s'.\n",fileName);
755 }
756 }
757 free(fileBuffer);
758
759 m_FileEntries.push_back(fileEntry);
760
761 return true;
762 }
763
764
765
766 bool Floppy::ReserveSectors(int sectorCount,int fillValue,const std::map<std::string,std::string>& metadata)
767 {
768 FileEntry fileEntry;
769 fileEntry.m_FloppyNumber=0; // 0 for a single floppy program
770
771 if (m_CurrentTrack>41) // face 2
772 {
773 fileEntry.m_StartSide=1;
774 }
775 else
776 {
777 fileEntry.m_StartSide=0;
778 }
779
780 fileEntry.m_StartTrack =m_CurrentTrack; // 0 to 42 (80...)
781 fileEntry.m_StartSector=m_CurrentSector; // 1 to 17 (or 16 or 18...)
782 fileEntry.m_StoredFileSize=sectorCount*256;
783 fileEntry.m_FinalFileSize =sectorCount*256;
784 fileEntry.m_SectorCount=sectorCount;
785 fileEntry.m_FilePath ="Reserved sectors";
786 fileEntry.m_CompressionMode=e_CompressionNone;
787
788 if (!metadata.empty())
789 {
790 fileEntry.m_Metadata = metadata;
791 m_LastFileWithMetadata=m_FileEntries.size();
792 }
793
794 for (auto metadataIt(metadata.begin());metadataIt!=metadata.end();++metadataIt)
795 {
796 m_MetadataCategories.insert(metadataIt->first);
797 }
798
799 //
800 // Finally write the data to the disk structure
801 //
802 while (sectorCount--)
803 {
804 unsigned int offset=SetPosition(m_CurrentTrack,m_CurrentSector);
805
806 MarkCurrentSectorUsed();
807 memset((char*)m_Buffer+offset,fillValue,256);
808 compute_crc((unsigned char*)m_Buffer+offset-4,4+256); // Compute the CRC of [A1 A1 A1|FB|TRACK DATA...]
809
810 if (!MoveToNextSector())
811 {
812 ShowError("Floppy disk is full, not enough space to reserve %u more sectors.\n",sectorCount);
813 }
814 }
815
816 m_FileEntries.push_back(fileEntry);
817
818 return true;
819 }
820
821
822 bool Floppy::ExtractFile(const char *fileName,int trackNumber,int sectorNumber,int sectorCount)
823 {
824 if (!m_Buffer)
825 {
826 return false;
827 }
828
829 int memoTrack=m_CurrentTrack;
830 int memoSector=m_CurrentSector;
831
832 if (trackNumber>=128)
833 {
834 trackNumber=(trackNumber-128)+m_TrackCount;
835 }
836
837 SetPosition(trackNumber,sectorNumber);
838
839 std::vector<char> fileBuffer;
840 fileBuffer.resize(sectorCount*256);
841
842 char* writeAddress=fileBuffer.data();
843 while (sectorCount--)
844 {
845 unsigned int offset=SetPosition(m_CurrentTrack,m_CurrentSector);
846 memcpy(writeAddress,(char*)m_Buffer+offset,256);
847 writeAddress+=256;
848
849 if (!MoveToNextSector())
850 {
851 ShowError("Reach the end of disk when extracting '%s'.\n",fileName);
852 }
853 }
854 if (!SaveFile(fileName,fileBuffer.data(),fileBuffer.size()))
855 {
856 ShowError("Reach the end of disk when extracting '%s'.\n",fileName);
857 }
858
859
860 // Restore the old position
861 SetPosition(memoTrack,memoSector);
862 return true;
863 }
864
865
866 void Floppy::MarkCurrentSectorUsed()
867 {
868 int magicValue=(m_SectorCount*m_CurrentTrack)+m_CurrentSector;
869 if (m_SectorUsageMap.find(magicValue)!=m_SectorUsageMap.end())
870 {
871 ShowError("Sector %d was already allocated",magicValue);
872 }
873 m_SectorUsageMap.insert(magicValue);
874 }
875
876 bool Floppy::SaveDescription(const char* fileName) const
877 {
878 std::stringstream layoutInfo;
879 layoutInfo << "//\n";
880 layoutInfo << "// Floppy layout generated by FloppyBuilder " << TOOL_VERSION_MAJOR << "." << TOOL_VERSION_MINOR << "\n";
881 if (!m_AllFilesAreResolved)
882 {
883 layoutInfo << "// (The generated floppy is missing some files, a new build pass is required)\n";
884 }
885 layoutInfo << "//\n";
886 layoutInfo << "\n";
887
888 layoutInfo << "#ifdef ASSEMBLER\n";
889 layoutInfo << "//\n";
890 layoutInfo << "// Information for the Assembler\n";
891 layoutInfo << "//\n";
892 layoutInfo << "#ifdef LOADER\n";
893
894 std::stringstream code_sector;
895 std::stringstream code_track;
896 std::stringstream code_compressed;
897 std::stringstream code_size_low;
898 std::stringstream code_size_high;
899 std::stringstream code_stored_size_low;
900 std::stringstream code_stored_size_high;
901
902 std::map<std::string,std::string> metadata_content;
903
904 std::set<std::string> metadata_entries;
905
906 {
907 for (auto metadataIt(m_MetadataCategories.begin());metadataIt!=m_MetadataCategories.end();metadataIt++)
908 {
909 const std::string& metadataCategoryName(*metadataIt);
910 metadata_content[metadataCategoryName+"_Low"] += "_MetaData_" + metadataCategoryName + "_Low .byt ";
911 metadata_content[metadataCategoryName+"_High"] += "_MetaData_" + metadataCategoryName + "_High .byt ";
912 }
913 }
914
915
916 std::stringstream file_list_summary;
917
918 int counter=0;
919 for (auto it(m_FileEntries.begin());it!=m_FileEntries.end();++it)
920 {
921 const FileEntry& fileEntry(*it);
922
923 if (it!=m_FileEntries.begin())
924 {
925 code_stored_size_low << ",";
926 code_stored_size_high << ",";
927
928 code_size_low << ",";
929 code_size_high << ",";
930
931 code_sector << ",";
932 code_track << ",";
933
934 code_compressed << ",";
935
936 if (counter<=m_LastFileWithMetadata)
937 {
938 for (auto metadataIt(metadata_content.begin());metadataIt!=metadata_content.end();metadataIt++)
939 {
940 metadataIt->second += ",";
941 }
942 }
943 }
944
945 code_size_low << "<" << fileEntry.m_FinalFileSize;
946 code_size_high << ">" << fileEntry.m_FinalFileSize;
947
948 code_stored_size_low << "<" << fileEntry.m_StoredFileSize;
949 code_stored_size_high << ">" << fileEntry.m_StoredFileSize;
950
951 code_sector << fileEntry.GetSector();
952
953 file_list_summary << "// Entry #" << counter << " '"<< fileEntry.m_FilePath << "'\n";
954 file_list_summary << "// - Starts on ";
955 if (fileEntry.m_StartTrack<m_TrackCount)
956 {
957 // First side
958 file_list_summary << " track " << fileEntry.m_StartTrack;
959 code_track << fileEntry.m_StartTrack;
960 }
961 else
962 {
963 // Second side
964 file_list_summary << "the second side on track " << (fileEntry.m_StartTrack-m_TrackCount);
965 code_track << fileEntry.m_StartTrack-m_TrackCount+128;
966 }
967 file_list_summary << " sector " << fileEntry.m_StartSector << " and is " << fileEntry.m_SectorCount << " sectors long ";
968
969 if (fileEntry.m_CompressionMode==e_CompressionNone)
970 {
971 // Uncompressed file
972 file_list_summary << "(" << fileEntry.m_FinalFileSize << " bytes).\n";
973 }
974 else
975 {
976 // Compressed file
977 file_list_summary << "(" << fileEntry.m_StoredFileSize << " compressed bytes: " << (fileEntry.m_StoredFileSize*100)/fileEntry.m_FinalFileSize << "% of " << fileEntry.m_FinalFileSize << " bytes).\n";
978 }
979
980 if (counter<=m_LastFileWithMetadata)
981 {
982 if (!fileEntry.m_Metadata.empty())
983 {
984 file_list_summary << "// - Associated metadata: ";
985 }
986 for (auto metadataIt(m_MetadataCategories.begin());metadataIt!=m_MetadataCategories.end();metadataIt++)
987 {
988 const std::string& metadataCategoryName(*metadataIt);
989
990 std::string metadataLabelEntry;
991 std::string metadataEntry;
992
993 auto fileMetadataIt=fileEntry.m_Metadata.find(metadataCategoryName);
994 if (fileMetadataIt==fileEntry.m_Metadata.end())
995 {
996 // No entries for that one
997 metadataLabelEntry="metadata_none";
998 metadataEntry=metadataLabelEntry+" .byt \"\",0";
999 }
1000 else
1001 {
1002 const std::string& key(fileMetadataIt->first);
1003 const std::string& value(fileMetadataIt->second);
1004 file_list_summary << key << "='" << value << "' ";
1005
1006 std::string labelValue(StringMakeLabel(value));
1007 metadataLabelEntry="metadata_"+key+"_"+labelValue;
1008 metadataEntry=metadataLabelEntry+" .byt \""+value+"\",0";
1009 }
1010 metadata_entries.insert(metadataEntry);
1011 metadata_content[metadataCategoryName+"_Low"] += "<" + metadataLabelEntry;
1012 metadata_content[metadataCategoryName+"_High"] += ">" + metadataLabelEntry;
1013 }
1014 if (!fileEntry.m_Metadata.empty())
1015 {
1016 file_list_summary << "\n";
1017 }
1018 }
1019
1020 /*
1021 if (!fileEntry.m_Metadata.empty())
1022 {
1023 for (auto metaIt(fileEntry.m_Metadata.begin());metaIt!=fileEntry.m_Metadata.end();++metaIt)
1024 {
1025 }
1026 }
1027 file_list_summary << "\n";
1028 */
1029
1030 ++counter;
1031 }
1032
1033
1034 layoutInfo << "FileStartSector .byt ";
1035 layoutInfo << code_sector.str() << "\n";
1036
1037 layoutInfo << "FileStartTrack .byt ";
1038 layoutInfo << code_track.str() << "\n";
1039
1040 //layoutInfo << "FileStoredSizeLow .byt ";
1041 //layoutInfo << code_stored_size_low.str() << "\n";
1042
1043 //layoutInfo << "FileStoredSizeHigh .byt ";
1044 //layoutInfo << code_stored_size_high.str() << "\n";
1045
1046 layoutInfo << "FileSizeLow .byt ";
1047 layoutInfo << code_size_low.str() << "\n";
1048
1049 layoutInfo << "FileSizeHigh .byt ";
1050 layoutInfo << code_size_high.str() << "\n";
1051
1052
1053 layoutInfo << "#undef LOADER\n";
1054 layoutInfo << "#endif // LOADER\n";
1055 layoutInfo << "#undef ASSEMBLER\n";
1056
1057 layoutInfo << "#else\n";
1058 layoutInfo << "//\n";
1059 layoutInfo << "// Information for the Compiler\n";
1060 layoutInfo << "//\n";
1061 layoutInfo << "#endif\n";
1062
1063 layoutInfo << "\n";
1064 layoutInfo << "//\n";
1065 layoutInfo << "// Summary for this floppy building session:\n";
1066 layoutInfo << "//\n";
1067 layoutInfo << "#define FLOPPY_SIDE_NUMBER " << m_SideCount << " // Number of sides\n";
1068 layoutInfo << "#define FLOPPY_TRACK_NUMBER " << m_TrackCount << " // Number of tracks\n";
1069 layoutInfo << "#define FLOPPY_SECTOR_PER_TRACK " << m_SectorCount << " // Number of sectors per track\n";
1070 layoutInfo << "\n";
1071 layoutInfo << "#define FLOPPY_LOADER_TRACK " << m_LoaderTrackPosition << " // Track where the loader is stored\n";
1072 layoutInfo << "#define FLOPPY_LOADER_SECTOR " << m_LoaderSectorPosition << " // Sector where the loader is stored\n";
1073 layoutInfo << "#define FLOPPY_LOADER_ADDRESS " << m_LoaderLoadAddress << " // Address where the loader is loaded on boot ($" << std::hex << m_LoaderLoadAddress << std::dec << ")\n";
1074
1075 layoutInfo << "\n";
1076 layoutInfo << "//\n";
1077 layoutInfo << "// List of files written to the floppy\n";
1078 layoutInfo << "//\n";
1079 layoutInfo << file_list_summary.str();
1080 layoutInfo << "//\n";
1081
1082 int totalAvailableSectors=m_TrackCount*m_SectorCount*m_SideCount;
1083 layoutInfo << "// " << m_SectorUsageMap.size() << " sectors used, out of " << totalAvailableSectors << ". (" << (m_SectorUsageMap.size()*100)/totalAvailableSectors << "% of the total disk size used)\n";
1084 layoutInfo << "//\n";
1085
1086 if (m_DefineList.empty())
1087 {
1088 layoutInfo << "// No defines set\n";
1089 }
1090 else
1091 {
1092 std::set<std::string> definedValues;
1093 for (auto it(m_DefineList.begin());it!=m_DefineList.end();++it)
1094 {
1095 //printf("!!!!--------------> %s=%s\r\n",it->first.c_str(),it->second.c_str());
1096 if (definedValues.find(it->first)==definedValues.end())
1097 {
1098 layoutInfo << "#define " << it->first << " " << it->second << "\n";
1099 definedValues.insert(it->first);
1100 }
1101 else
1102 {
1103 layoutInfo << "// Ignored redefined value for #define " << it->first << " " << it->second << "\n";
1104 }
1105 }
1106 }
1107
1108 layoutInfo << "\n";
1109 layoutInfo << "//\n";
1110 layoutInfo << "// Metadata\n";
1111 layoutInfo << "//\n";
1112 layoutInfo << "#ifdef METADATA_STORAGE\n";
1113
1114 {
1115 for (auto metadataIt(metadata_entries.begin());metadataIt!=metadata_entries.end();metadataIt++)
1116 {
1117 layoutInfo << *metadataIt << "\n";
1118 }
1119 }
1120 layoutInfo << "\n";
1121
1122 {
1123 for (auto metadataIt(metadata_content.begin());metadataIt!=metadata_content.end();metadataIt++)
1124 {
1125 layoutInfo << metadataIt->second << "\n";
1126 }
1127 }
1128 layoutInfo << "#endif // METADATA_STORAGE\n";
1129 layoutInfo << "\n";
1130
1131 if (!SaveFile(fileName,layoutInfo.str().c_str(),layoutInfo.str().length()))
1132 {
1133 ShowError("Can't save '%s'\n",fileName);
1134 }
1135
1136 return true;
1137 }
1138
1139
1140 bool Floppy::AddDefine(std::string defineName,int defineValue)
1141 {
1142 std::stringstream tempValue;
1143 tempValue << defineValue;
1144 return AddDefine(defineName,tempValue.str());
1145 }
1146
1147
1148 bool Floppy::AddDefine(std::string defineName,std::string defineValue)
1149 {
1150 // Ugly token replacement, can do more optimal but as long as it works...
1151 if ((defineName.find("{File")!=std::string::npos) || (defineValue.find("{File")!=std::string::npos))
1152 {
1153 if (m_FileEntries.empty())
1154 {
1155 ShowError("AddDefine %s %s: A AddDefine using a {File* directive can be used only after a file was added\n",defineName.c_str(),defineValue.c_str());
1156 }
1157
1158
1159 {
1160 std::stringstream tempValue;
1161 tempValue << m_FileEntries.size()-1;
1162 StringReplace(defineName ,"{FileIndex}",tempValue.str());
1163 StringReplace(defineValue,"{FileIndex}",tempValue.str());
1164 }
1165
1166 {
1167 std::stringstream tempValue;
1168 tempValue << m_FileEntries.back().m_FinalFileSize;
1169 StringReplace(defineName ,"{FileSize}",tempValue.str());
1170 StringReplace(defineValue,"{FileSize}",tempValue.str());
1171 }
1172
1173 {
1174 std::stringstream tempValue;
1175 tempValue << m_FileEntries.back().m_StartTrack;
1176 StringReplace(defineName ,"{FileTrack}",tempValue.str());
1177 StringReplace(defineValue,"{FileTrack}",tempValue.str());
1178 }
1179
1180 {
1181 std::stringstream tempValue;
1182 tempValue << m_FileEntries.back().GetSector();
1183 StringReplace(defineName ,"{FileSector}",tempValue.str());
1184 StringReplace(defineValue,"{FileSector}",tempValue.str());
1185 }
1186
1187 {
1188 std::stringstream tempValue;
1189 tempValue << m_FileEntries.back().m_FinalFileSize;
1190 StringReplace(defineName ,"{FileSize}",tempValue.str());
1191 StringReplace(defineValue,"{FileSize}",tempValue.str());
1192 }
1193
1194 {
1195 std::stringstream tempValue;
1196 tempValue << m_FileEntries.back().m_StoredFileSize;
1197 StringReplace(defineName ,"{FileSizeCompressed}",tempValue.str());
1198 StringReplace(defineValue,"{FileSizeCompressed}",tempValue.str());
1199 }
1200 }
1201
1202 m_DefineList.push_back(std::pair<std::string,std::string>(defineName,defineValue));
1203 return true;
1204 }
1205
1206

  ViewVC Help
Powered by ViewVC 1.1.26