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

  ViewVC Help
Powered by ViewVC 1.1.26