1 /*
2 * Copyright (C) 2002-2021 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "drives.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "bios_disk.h"
27 #include "bios.h"
28 #include "cross.h"
29 #include "dos_inc.h"
30 #include "string_utils.h"
31 #include "support.h"
32
33 #define IMGTYPE_FLOPPY 0
34 #define IMGTYPE_ISO 1
35 #define IMGTYPE_HDD 2
36
37 #define FAT12 0
38 #define FAT16 1
39 #define FAT32 2
40
41 class fatFile final : public DOS_File {
42 public:
43 fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
44 fatFile(const fatFile&) = delete; // prevent copy
45 fatFile& operator=(const fatFile&) = delete; // prevent assignment
46 bool Read(Bit8u * data,Bit16u * size);
47 bool Write(Bit8u * data,Bit16u * size);
48 bool Seek(Bit32u * pos,Bit32u type);
49 bool Close();
50 Bit16u GetInformation(void);
51 bool UpdateDateTimeFromHost(void);
52 public:
53 Bit32u firstCluster;
54 Bit32u seekpos;
55 Bit32u filelength;
56 Bit32u currentSector;
57 Bit32u curSectOff;
58 Bit8u sectorBuffer[512];
59 /* Record of where in the directory structure this file is located */
60 Bit32u dirCluster;
61 Bit32u dirIndex;
62
63 bool loadedSector;
64 fatDrive *myDrive;
65 };
66
67
68 /* IN - char * filename: Name in regular filename format, e.g. bob.txt */
69 /* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob txt */
convToDirFile(char * filename,char * filearray)70 static void convToDirFile(char *filename, char *filearray) {
71 Bit32u charidx = 0;
72 Bit32u flen,i;
73 flen = (Bit32u)strnlen(filename, DOS_NAMELENGTH_ASCII);
74 memset(filearray, 32, 11);
75 for(i=0;i<flen;i++) {
76 if(charidx >= 11) break;
77 if(filename[i] != '.') {
78 filearray[charidx] = filename[i];
79 charidx++;
80 } else {
81 charidx = 8;
82 }
83 }
84 }
85
fatFile(const char *,Bit32u startCluster,Bit32u fileLen,fatDrive * useDrive)86 fatFile::fatFile(const char* /*name*/,
87 Bit32u startCluster,
88 Bit32u fileLen,
89 fatDrive *useDrive)
90 : firstCluster(startCluster),
91 seekpos(0),
92 filelength(fileLen),
93 currentSector(0),
94 curSectOff(0),
95 sectorBuffer{0},
96 dirCluster(0),
97 dirIndex(0),
98 loadedSector(false),
99 myDrive(useDrive)
100 {
101 Bit32u seekto = 0;
102 open = true;
103 if(filelength > 0) {
104 Seek(&seekto, DOS_SEEK_SET);
105 }
106 }
107
Read(Bit8u * data,Bit16u * size)108 bool fatFile::Read(Bit8u * data, Bit16u *size) {
109 if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode
110 DOS_SetError(DOSERR_ACCESS_DENIED);
111 return false;
112 }
113 Bit16u sizedec, sizecount;
114 if(seekpos >= filelength) {
115 *size = 0;
116 return true;
117 }
118
119 if (!loadedSector) {
120 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
121 if(currentSector == 0) {
122 /* EOC reached before EOF */
123 *size = 0;
124 loadedSector = false;
125 return true;
126 }
127 curSectOff = seekpos % myDrive->getSectorSize();
128 myDrive->readSector(currentSector, sectorBuffer);
129 loadedSector = true;
130 }
131
132 sizedec = *size;
133 sizecount = 0;
134 while(sizedec != 0) {
135 if(seekpos >= filelength) {
136 *size = sizecount;
137 return true;
138 }
139 data[sizecount++] = sectorBuffer[curSectOff++];
140 seekpos++;
141 if(curSectOff >= myDrive->getSectorSize()) {
142 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
143 if(currentSector == 0) {
144 /* EOC reached before EOF */
145 //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
146 *size = sizecount;
147 loadedSector = false;
148 return true;
149 }
150 curSectOff = 0;
151 myDrive->readSector(currentSector, sectorBuffer);
152 loadedSector = true;
153 //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
154 }
155 --sizedec;
156 }
157 *size =sizecount;
158 return true;
159 }
160
Write(Bit8u * data,Bit16u * size)161 bool fatFile::Write(Bit8u * data, Bit16u *size) {
162 if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
163 DOS_SetError(DOSERR_ACCESS_DENIED);
164 return false;
165 }
166
167 direntry tmpentry;
168 Bit16u sizedec, sizecount;
169 sizedec = *size;
170 sizecount = 0;
171
172 if(seekpos < filelength && *size == 0) {
173 /* Truncate file to current position */
174 myDrive->deleteClustChain(firstCluster, seekpos);
175 filelength = seekpos;
176 goto finalizeWrite;
177 }
178
179 if(seekpos > filelength) {
180 /* Extend file to current position */
181 Bit32u clustSize = myDrive->getClusterSize();
182 if(filelength == 0) {
183 firstCluster = myDrive->getFirstFreeClust();
184 if(firstCluster == 0) goto finalizeWrite; // out of space
185 myDrive->allocateCluster(firstCluster, 0);
186 filelength = clustSize;
187 }
188 filelength = ((filelength - 1) / clustSize + 1) * clustSize;
189 while(filelength < seekpos) {
190 if(myDrive->appendCluster(firstCluster) == 0) goto finalizeWrite; // out of space
191 filelength += clustSize;
192 }
193 if(filelength > seekpos) filelength = seekpos;
194 if(*size == 0) goto finalizeWrite;
195 }
196
197 while(sizedec != 0) {
198 /* Increase filesize if necessary */
199 if(seekpos >= filelength) {
200 if(filelength == 0) {
201 firstCluster = myDrive->getFirstFreeClust();
202 if(firstCluster == 0) goto finalizeWrite; // out of space
203 myDrive->allocateCluster(firstCluster, 0);
204 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
205 myDrive->readSector(currentSector, sectorBuffer);
206 loadedSector = true;
207 }
208 if (!loadedSector) {
209 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
210 if(currentSector == 0) {
211 /* EOC reached before EOF - try to increase file allocation */
212 myDrive->appendCluster(firstCluster);
213 /* Try getting sector again */
214 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
215 if(currentSector == 0) {
216 /* No can do. lets give up and go home. We must be out of room */
217 goto finalizeWrite;
218 }
219 }
220 curSectOff = seekpos % myDrive->getSectorSize();
221 myDrive->readSector(currentSector, sectorBuffer);
222 loadedSector = true;
223 }
224 filelength = seekpos+1;
225 }
226 sectorBuffer[curSectOff++] = data[sizecount++];
227 seekpos++;
228 if(curSectOff >= myDrive->getSectorSize()) {
229 if(loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
230
231 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
232 if(currentSector == 0) loadedSector = false;
233 else {
234 curSectOff = 0;
235 myDrive->readSector(currentSector, sectorBuffer);
236 loadedSector = true;
237 }
238 }
239 --sizedec;
240 }
241 if(curSectOff>0 && loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
242
243 finalizeWrite:
244 myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex);
245 tmpentry.entrysize = filelength;
246 tmpentry.loFirstClust = (Bit16u)firstCluster;
247 myDrive->directoryChange(dirCluster, &tmpentry, dirIndex);
248
249 *size =sizecount;
250 return true;
251 }
252
Seek(Bit32u * pos,Bit32u type)253 bool fatFile::Seek(Bit32u *pos, Bit32u type) {
254 Bit32s seekto=0;
255
256 switch(type) {
257 case DOS_SEEK_SET:
258 seekto = (Bit32s)*pos;
259 break;
260 case DOS_SEEK_CUR:
261 /* Is this relative seek signed? */
262 seekto = (Bit32s)*pos + (Bit32s)seekpos;
263 break;
264 case DOS_SEEK_END:
265 seekto = (Bit32s)filelength + (Bit32s)*pos;
266 break;
267 }
268 // LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
269
270 if(seekto<0) seekto = 0;
271 seekpos = (Bit32u)seekto;
272 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
273 if (currentSector == 0) {
274 /* not within file size, thus no sector is available */
275 loadedSector = false;
276 } else {
277 curSectOff = seekpos % myDrive->getSectorSize();
278 myDrive->readSector(currentSector, sectorBuffer);
279 loadedSector = true;
280 }
281 *pos = seekpos;
282 return true;
283 }
284
Close()285 bool fatFile::Close() {
286 /* Flush buffer */
287 if (loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
288
289 return false;
290 }
291
GetInformation(void)292 Bit16u fatFile::GetInformation(void) {
293 return 0;
294 }
295
UpdateDateTimeFromHost(void)296 bool fatFile::UpdateDateTimeFromHost(void) {
297 return true;
298 }
299
getClustFirstSect(Bit32u clustNum)300 Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
301 return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
302 }
303
getClusterValue(Bit32u clustNum)304 Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
305 Bit32u fatoffset=0;
306 Bit32u fatsectnum;
307 Bit32u fatentoff;
308 Bit32u clustValue=0;
309
310 switch(fattype) {
311 case FAT12:
312 fatoffset = clustNum + (clustNum / 2);
313 break;
314 case FAT16:
315 fatoffset = clustNum * 2;
316 break;
317 case FAT32:
318 fatoffset = clustNum * 4;
319 break;
320 }
321 fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
322 fatentoff = fatoffset % bootbuffer.bytespersector;
323
324 if(curFatSect != fatsectnum) {
325 /* Load two sectors at once for FAT12 */
326 readSector(fatsectnum, &fatSectBuffer[0]);
327 if (fattype==FAT12)
328 readSector(fatsectnum+1, &fatSectBuffer[512]);
329 curFatSect = fatsectnum;
330 }
331
332 switch(fattype) {
333 case FAT12:
334 clustValue = var_read((Bit16u *)&fatSectBuffer[fatentoff]);
335 if(clustNum & 0x1) {
336 clustValue >>= 4;
337 } else {
338 clustValue &= 0xfff;
339 }
340 break;
341 case FAT16:
342 clustValue = var_read((Bit16u *)&fatSectBuffer[fatentoff]);
343 break;
344 case FAT32:
345 clustValue = var_read((Bit32u *)&fatSectBuffer[fatentoff]);
346 break;
347 }
348
349 return clustValue;
350 }
351
setClusterValue(Bit32u clustNum,Bit32u clustValue)352 void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
353 Bit32u fatoffset=0;
354 Bit32u fatsectnum;
355 Bit32u fatentoff;
356
357 switch(fattype) {
358 case FAT12:
359 fatoffset = clustNum + (clustNum / 2);
360 break;
361 case FAT16:
362 fatoffset = clustNum * 2;
363 break;
364 case FAT32:
365 fatoffset = clustNum * 4;
366 break;
367 }
368 fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
369 fatentoff = fatoffset % bootbuffer.bytespersector;
370
371 if(curFatSect != fatsectnum) {
372 /* Load two sectors at once for FAT12 */
373 readSector(fatsectnum, &fatSectBuffer[0]);
374 if (fattype==FAT12)
375 readSector(fatsectnum+1, &fatSectBuffer[512]);
376 curFatSect = fatsectnum;
377 }
378
379 switch(fattype) {
380 case FAT12: {
381 Bit16u tmpValue = var_read((Bit16u *)&fatSectBuffer[fatentoff]);
382 if(clustNum & 0x1) {
383 clustValue &= 0xfff;
384 clustValue <<= 4;
385 tmpValue &= 0xf;
386 tmpValue |= (Bit16u)clustValue;
387
388 } else {
389 clustValue &= 0xfff;
390 tmpValue &= 0xf000;
391 tmpValue |= (Bit16u)clustValue;
392 }
393 var_write((Bit16u *)&fatSectBuffer[fatentoff], tmpValue);
394 break;
395 }
396 case FAT16:
397 var_write((Bit16u *)&fatSectBuffer[fatentoff], (Bit16u)clustValue);
398 break;
399 case FAT32:
400 var_write((Bit32u *)&fatSectBuffer[fatentoff], clustValue);
401 break;
402 }
403 for(int fc=0;fc<bootbuffer.fatcopies;fc++) {
404 writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
405 if (fattype==FAT12) {
406 if (fatentoff>=511)
407 writeSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]);
408 }
409 }
410 }
411
getEntryName(char * fullname,char * entname)412 bool fatDrive::getEntryName(char *fullname, char *entname) {
413 char dirtoken[DOS_PATHLENGTH];
414
415 char * findDir;
416 char * findFile;
417 safe_strcpy(dirtoken, fullname);
418
419 //LOG_MSG("Testing for filename %s", fullname);
420 findDir = strtok(dirtoken,"\\");
421 if (findDir==NULL) {
422 return true; // root always exists
423 }
424 findFile = findDir;
425 while(findDir != NULL) {
426 findFile = findDir;
427 findDir = strtok(NULL,"\\");
428 }
429
430 assert(entname);
431 entname[0] = '\0';
432 strncat(entname, findFile, DOS_NAMELENGTH_ASCII - 1);
433 return true;
434 }
435
getFileDirEntry(char const * const filename,direntry * useEntry,Bit32u * dirClust,Bit32u * subEntry)436 bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) {
437 size_t len = strnlen(filename, DOS_PATHLENGTH);
438 char dirtoken[DOS_PATHLENGTH];
439 Bit32u currentClust = 0;
440
441 direntry foundEntry;
442 char * findDir;
443 char * findFile;
444 safe_strcpy(dirtoken, filename);
445 findFile=dirtoken;
446
447 /* Skip if testing in root directory */
448 if ((len>0) && (filename[len-1]!='\\')) {
449 //LOG_MSG("Testing for filename %s", filename);
450 findDir = strtok(dirtoken,"\\");
451 findFile = findDir;
452 while(findDir != NULL) {
453 imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
454 imgDTA->SetDirID(0);
455
456 findFile = findDir;
457 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
458 else {
459 //Found something. See if it's a directory (findfirst always finds regular files)
460 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
461 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
462 if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
463 }
464
465 currentClust = foundEntry.loFirstClust;
466 findDir = strtok(NULL,"\\");
467 }
468 } else {
469 /* Set to root directory */
470 }
471
472 /* Search found directory for our file */
473 imgDTA->SetupSearch(0,0x7,findFile);
474 imgDTA->SetDirID(0);
475 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false;
476
477 memcpy(useEntry, &foundEntry, sizeof(direntry));
478 *dirClust = currentClust;
479 *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
480 return true;
481 }
482
getDirClustNum(char * dir,Bit32u * clustNum,bool parDir)483 bool fatDrive::getDirClustNum(char *dir, Bit32u *clustNum, bool parDir) {
484 Bit32u len = (Bit32u)strnlen(dir, DOS_PATHLENGTH);
485 char dirtoken[DOS_PATHLENGTH];
486 Bit32u currentClust = 0;
487 direntry foundEntry;
488 char * findDir;
489 safe_strcpy(dirtoken, dir);
490
491 /* Skip if testing for root directory */
492 if ((len>0) && (dir[len-1]!='\\')) {
493 //LOG_MSG("Testing for dir %s", dir);
494 findDir = strtok(dirtoken,"\\");
495 while(findDir != NULL) {
496 imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
497 imgDTA->SetDirID(0);
498 findDir = strtok(NULL,"\\");
499 if(parDir && (findDir == NULL)) break;
500
501 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
502 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
503 return false;
504 } else {
505 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
506 if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
507 }
508 currentClust = foundEntry.loFirstClust;
509
510 }
511 *clustNum = currentClust;
512 } else {
513 /* Set to root directory */
514 *clustNum = 0;
515 }
516 return true;
517 }
518
readSector(Bit32u sectnum,void * data)519 Bit8u fatDrive::readSector(Bit32u sectnum, void * data) {
520 // Guard
521 if (!loadedDisk) {
522 return 0;
523 }
524
525 if (absolute) {
526 return loadedDisk->Read_AbsoluteSector(sectnum, data);
527 }
528 Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
529 Bit32u cylinder = sectnum / cylindersize;
530 sectnum %= cylindersize;
531 Bit32u head = sectnum / bootbuffer.sectorspertrack;
532 Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
533 return loadedDisk->Read_Sector(head, cylinder, sector, data);
534 }
535
writeSector(Bit32u sectnum,void * data)536 Bit8u fatDrive::writeSector(Bit32u sectnum, void * data) {
537 // Guard
538 if (!loadedDisk) {
539 return 0;
540 }
541
542 if (absolute) {
543 return loadedDisk->Write_AbsoluteSector(sectnum, data);
544 }
545 Bit32u cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack;
546 Bit32u cylinder = sectnum / cylindersize;
547 sectnum %= cylindersize;
548 Bit32u head = sectnum / bootbuffer.sectorspertrack;
549 Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
550 return loadedDisk->Write_Sector(head, cylinder, sector, data);
551 }
552
getSectorSize(void)553 Bit32u fatDrive::getSectorSize(void) {
554 return bootbuffer.bytespersector;
555 }
556
getClusterSize(void)557 Bit32u fatDrive::getClusterSize(void) {
558 return bootbuffer.sectorspercluster * bootbuffer.bytespersector;
559 }
560
getAbsoluteSectFromBytePos(Bit32u startClustNum,Bit32u bytePos)561 Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
562 return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
563 }
564
getAbsoluteSectFromChain(Bit32u startClustNum,Bit32u logicalSector)565 Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
566 Bit32s skipClust = logicalSector / bootbuffer.sectorspercluster;
567 Bit32u sectClust = logicalSector % bootbuffer.sectorspercluster;
568
569 Bit32u currentClust = startClustNum;
570 Bit32u testvalue;
571
572 while(skipClust!=0) {
573 bool isEOF = false;
574 testvalue = getClusterValue(currentClust);
575 switch(fattype) {
576 case FAT12:
577 if(testvalue >= 0xff8) isEOF = true;
578 break;
579 case FAT16:
580 if(testvalue >= 0xfff8) isEOF = true;
581 break;
582 case FAT32:
583 if(testvalue >= 0xfffffff8) isEOF = true;
584 break;
585 }
586 if((isEOF) && (skipClust>=1)) {
587 //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
588 if (skipClust == 1 && fattype == FAT12) {
589 //break;
590 LOG(LOG_DOSMISC, LOG_ERROR)("End of cluster chain reached, but maybe good after all ?");
591 }
592 return 0;
593 }
594 currentClust = testvalue;
595 --skipClust;
596 }
597
598 return (getClustFirstSect(currentClust) + sectClust);
599 }
600
deleteClustChain(Bit32u startCluster,Bit32u bytePos)601 void fatDrive::deleteClustChain(Bit32u startCluster, Bit32u bytePos) {
602 Bit32u clustSize = getClusterSize();
603 Bit32u endClust = (bytePos + clustSize - 1) / clustSize;
604 Bit32u countClust = 1;
605
606 Bit32u testvalue;
607 Bit32u currentClust = startCluster;
608 bool isEOF = false;
609 while(!isEOF) {
610 testvalue = getClusterValue(currentClust);
611 if(testvalue == 0) {
612 /* What the crap? Cluster is already empty - BAIL! */
613 break;
614 }
615 switch(fattype) {
616 case FAT12:
617 if(testvalue >= 0xff8) isEOF = true;
618 break;
619 case FAT16:
620 if(testvalue >= 0xfff8) isEOF = true;
621 break;
622 case FAT32:
623 if(testvalue >= 0xfffffff8) isEOF = true;
624 break;
625 }
626 if(countClust == endClust && !isEOF) {
627 /* Mark cluster as end */
628 switch(fattype) {
629 case FAT12:
630 setClusterValue(currentClust, 0xfff);
631 break;
632 case FAT16:
633 setClusterValue(currentClust, 0xffff);
634 break;
635 case FAT32:
636 setClusterValue(currentClust, 0xffffffff);
637 break;
638 }
639 } else if(countClust > endClust) {
640 /* Mark cluster as empty */
641 setClusterValue(currentClust, 0);
642 }
643 if(isEOF) break;
644 currentClust = testvalue;
645 countClust++;
646 }
647 }
648
appendCluster(Bit32u startCluster)649 Bit32u fatDrive::appendCluster(Bit32u startCluster) {
650 Bit32u testvalue;
651 Bit32u currentClust = startCluster;
652 bool isEOF = false;
653
654 while(!isEOF) {
655 testvalue = getClusterValue(currentClust);
656 switch(fattype) {
657 case FAT12:
658 if(testvalue >= 0xff8) isEOF = true;
659 break;
660 case FAT16:
661 if(testvalue >= 0xfff8) isEOF = true;
662 break;
663 case FAT32:
664 if(testvalue >= 0xfffffff8) isEOF = true;
665 break;
666 }
667 if(isEOF) break;
668 currentClust = testvalue;
669 }
670
671 Bit32u newClust = getFirstFreeClust();
672 /* Drive is full */
673 if(newClust == 0) return 0;
674
675 if(!allocateCluster(newClust, currentClust)) return 0;
676
677 zeroOutCluster(newClust);
678
679 return newClust;
680 }
681
allocateCluster(Bit32u useCluster,Bit32u prevCluster)682 bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
683
684 /* Can't allocate cluster #0 */
685 if(useCluster == 0) return false;
686
687 if(prevCluster != 0) {
688 /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
689 if(!getClusterValue(prevCluster)) return false;
690
691 /* Point cluster to new cluster in chain */
692 setClusterValue(prevCluster, useCluster);
693 //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
694 }
695
696 switch(fattype) {
697 case FAT12:
698 setClusterValue(useCluster, 0xfff);
699 break;
700 case FAT16:
701 setClusterValue(useCluster, 0xffff);
702 break;
703 case FAT32:
704 setClusterValue(useCluster, 0xffffffff);
705 break;
706 }
707 return true;
708 }
709
fatDrive(const char * sysFilename,Bit32u bytesector,Bit32u cylsector,Bit32u headscyl,Bit32u cylinders,Bit32u startSector)710 fatDrive::fatDrive(const char *sysFilename,
711 Bit32u bytesector,
712 Bit32u cylsector,
713 Bit32u headscyl,
714 Bit32u cylinders,
715 Bit32u startSector)
716 : loadedDisk(nullptr),
717 created_successfully(true),
718 bootbuffer{{0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, 0},
719 absolute(false),
720 fattype(0),
721 CountOfClusters(0),
722 partSectOff(0),
723 firstDataSector(0),
724 firstRootDirSect(0),
725 cwdDirCluster(0),
726 fatSectBuffer{0},
727 curFatSect(0)
728 {
729 FILE *diskfile;
730 Bit32u filesize;
731 bool is_hdd;
732 struct partTable mbrData;
733
734 if(imgDTASeg == 0) {
735 imgDTASeg = DOS_GetMemory(2);
736 imgDTAPtr = RealMake(imgDTASeg, 0);
737 imgDTA = new DOS_DTA(imgDTAPtr);
738 }
739
740 diskfile = fopen_wrap(sysFilename, "rb+");
741 if (!diskfile) {
742 created_successfully = false;
743 return;
744 }
745 fseek(diskfile, 0L, SEEK_END);
746 filesize = (Bit32u)ftell(diskfile) / 1024L;
747 is_hdd = (filesize > 2880);
748
749 /* Load disk image */
750 loadedDisk.reset(new imageDisk(diskfile, sysFilename, filesize, is_hdd));
751
752 if(is_hdd) {
753 /* Set user specified harddrive parameters */
754 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
755
756 loadedDisk->Read_Sector(0,0,1,&mbrData);
757
758 if(mbrData.magic1!= 0x55 || mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
759
760 startSector = 63;
761 int m;
762 for(m=0;m<4;m++) {
763 /* Pick the first available partition */
764 if(mbrData.pentry[m].partSize != 0x00) {
765 mbrData.pentry[m].absSectStart = host_to_le(mbrData.pentry[m].absSectStart);
766 mbrData.pentry[m].partSize = host_to_le(mbrData.pentry[m].partSize);
767 LOG_MSG("Using partition %d on drive; skipping %d sectors", m, mbrData.pentry[m].absSectStart);
768 startSector = mbrData.pentry[m].absSectStart;
769 break;
770 }
771 }
772
773 if(m==4) LOG_MSG("No good partition found in image.");
774
775 partSectOff = startSector;
776 } else {
777 /* Get floppy disk parameters based on image size */
778 loadedDisk->Get_Geometry(&headscyl, &cylinders, &cylsector, &bytesector);
779 /* Floppy disks don't have partitions */
780 partSectOff = 0;
781 }
782
783 if (bytesector != 512) {
784 /* Non-standard sector sizes not implemented */
785 created_successfully = false;
786 return;
787 }
788
789 loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
790
791 bootbuffer.bytespersector = host_to_le(bootbuffer.bytespersector);
792 bootbuffer.reservedsectors = host_to_le(bootbuffer.reservedsectors);
793 bootbuffer.rootdirentries = host_to_le(bootbuffer.rootdirentries);
794 bootbuffer.totalsectorcount = host_to_le(bootbuffer.totalsectorcount);
795 bootbuffer.sectorsperfat = host_to_le(bootbuffer.sectorsperfat);
796 bootbuffer.sectorspertrack = host_to_le(bootbuffer.sectorspertrack);
797 bootbuffer.headcount = host_to_le(bootbuffer.headcount);
798 bootbuffer.hiddensectorcount = host_to_le(bootbuffer.hiddensectorcount);
799 bootbuffer.totalsecdword = host_to_le(bootbuffer.totalsecdword);
800
801 if (!is_hdd) {
802 /* Identify floppy format */
803 if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 ||
804 (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) &&
805 (bootbuffer.mediadescriptor & 0xf0) == 0xf0) {
806 /* DOS 2.x or later format, BPB assumed valid */
807
808 if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) &&
809 (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) {
810 /* Fix pre-DOS 3.2 single-sided floppy */
811 bootbuffer.sectorspercluster = 1;
812 }
813 } else {
814 /* Read media descriptor in FAT */
815 Bit8u sectorBuffer[512];
816 loadedDisk->Read_AbsoluteSector(1,§orBuffer);
817 Bit8u mdesc = sectorBuffer[0];
818
819 if (mdesc >= 0xf8) {
820 /* DOS 1.x format, create BPB for 160kB floppy */
821 bootbuffer.bytespersector = 512;
822 bootbuffer.sectorspercluster = 1;
823 bootbuffer.reservedsectors = 1;
824 bootbuffer.fatcopies = 2;
825 bootbuffer.rootdirentries = 64;
826 bootbuffer.totalsectorcount = 320;
827 bootbuffer.mediadescriptor = mdesc;
828 bootbuffer.sectorsperfat = 1;
829 bootbuffer.sectorspertrack = 8;
830 bootbuffer.headcount = 1;
831 bootbuffer.magic1 = 0x55; // to silence warning
832 bootbuffer.magic2 = 0xaa;
833 if (!(mdesc & 0x2)) {
834 /* Adjust for 9 sectors per track */
835 bootbuffer.totalsectorcount = 360;
836 bootbuffer.sectorsperfat = 2;
837 bootbuffer.sectorspertrack = 9;
838 }
839 if (mdesc & 0x1) {
840 /* Adjust for 2 sides */
841 bootbuffer.sectorspercluster = 2;
842 bootbuffer.rootdirentries = 112;
843 bootbuffer.totalsectorcount *= 2;
844 bootbuffer.headcount = 2;
845 }
846 } else {
847 /* Unknown format */
848 created_successfully = false;
849 return;
850 }
851 }
852 }
853
854 if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
855 /* Not a FAT filesystem */
856 LOG_MSG("Loaded image has no valid magicnumbers at the end!");
857 }
858
859 /* Sanity checks */
860 if ((bootbuffer.sectorsperfat == 0) || // FAT32 not implemented yet
861 (bootbuffer.bytespersector != 512) || // non-standard sector sizes not implemented
862 (bootbuffer.sectorspercluster == 0) ||
863 (bootbuffer.rootdirentries == 0) ||
864 (bootbuffer.fatcopies == 0) ||
865 (bootbuffer.headcount == 0) ||
866 (bootbuffer.headcount > headscyl) ||
867 (bootbuffer.sectorspertrack == 0) ||
868 (bootbuffer.sectorspertrack > cylsector)) {
869 created_successfully = false;
870 return;
871 }
872
873 /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */
874 absolute = ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector));
875
876 /* Determine FAT format, 12, 16 or 32 */
877
878 /* Get size of root dir in sectors */
879 Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector;
880 Bit32u DataSectors;
881 if(bootbuffer.totalsectorcount != 0) {
882 DataSectors = bootbuffer.totalsectorcount - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
883 } else {
884 DataSectors = bootbuffer.totalsecdword - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
885
886 }
887 CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
888
889 firstDataSector = (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors) + partSectOff;
890 firstRootDirSect = bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + partSectOff;
891
892 if(CountOfClusters < 4085) {
893 /* Volume is FAT12 */
894 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
895 fattype = FAT12;
896 } else if (CountOfClusters < 65525) {
897 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
898 fattype = FAT16;
899 } else {
900 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
901 fattype = FAT32;
902 }
903
904 /* There is no cluster 0, this means we are in the root directory */
905 cwdDirCluster = 0;
906
907 memset(fatSectBuffer,0,1024);
908 curFatSect = 0xffffffff;
909
910 safe_strcpy(info, "fatDrive ");
911 safe_strcat(info, sysFilename);
912 }
913
AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters)914 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
915 // Guard
916 if (!loadedDisk) {
917 return false;
918 }
919
920 Bit32u hs, cy, sect,sectsize;
921 Bit32u countFree = 0;
922 Bit32u i;
923
924 loadedDisk->Get_Geometry(&hs, &cy, §, §size);
925 *_bytes_sector = (Bit16u)sectsize;
926 *_sectors_cluster = bootbuffer.sectorspercluster;
927
928 if (CountOfClusters<65536) {
929 *_total_clusters = (Bit16u)CountOfClusters;
930 } else {
931 // maybe some special handling needed for fat32
932 *_total_clusters = 65535;
933 }
934
935 for (i=0; i<CountOfClusters; i++) {
936 if (!getClusterValue(i + 2)) {
937 countFree++;
938 }
939 }
940
941 if (countFree<65536) {
942 *_free_clusters = (Bit16u)countFree;
943 } else {
944 // maybe some special handling needed for fat32
945 *_free_clusters = 65535;
946 }
947 return true;
948 }
949
getFirstFreeClust(void)950 Bit32u fatDrive::getFirstFreeClust(void) {
951 Bit32u i;
952 for(i=0;i<CountOfClusters;i++) {
953 if(!getClusterValue(i+2)) return (i+2);
954 }
955
956 /* No free cluster found */
957 return 0;
958 }
959
isRemote(void)960 bool fatDrive::isRemote(void) { return false; }
isRemovable(void)961 bool fatDrive::isRemovable(void) { return false; }
962
UnMount(void)963 Bits fatDrive::UnMount(void) {
964 delete this;
965 return 0;
966 }
967
GetMediaByte(void)968 Bit8u fatDrive::GetMediaByte(void) {
969 return loadedDisk ? loadedDisk->GetBiosType() : 0;
970 }
971
972 // name can be a full DOS path with filename, up-to DOS_PATHLENGTH in length
FileCreate(DOS_File ** file,char * name,Bit16u attributes)973 bool fatDrive::FileCreate(DOS_File **file, char *name, Bit16u attributes) {
974 direntry fileEntry;
975 Bit32u dirClust, subEntry;
976 char dirName[DOS_NAMELENGTH_ASCII];
977 char pathName[11]; // pathName is actually just the filename, without path
978
979 Bit16u save_errorcode=dos.errorcode;
980
981 /* Check if file already exists */
982 if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
983 /* Truncate file */
984 fileEntry.entrysize=0;
985 directoryChange(dirClust, &fileEntry, subEntry);
986 if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
987 } else {
988 /* Can we even get the name of the file itself? */
989 if(!getEntryName(name, &dirName[0])) return false;
990 convToDirFile(&dirName[0], &pathName[0]);
991
992 /* Can we find the base directory? */
993 if(!getDirClustNum(name, &dirClust, true)) return false;
994 memset(&fileEntry, 0, sizeof(direntry));
995 memcpy(&fileEntry.entryname, &pathName[0], 11);
996 fileEntry.attrib = (Bit8u)(attributes & 0xff);
997 addDirectoryEntry(dirClust, fileEntry);
998
999 /* Check if file exists now */
1000 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
1001 }
1002
1003 /* Empty file created, now lets open it */
1004 /* TODO: check for read-only flag and requested write access */
1005 *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
1006 (*file)->flags=OPEN_READWRITE;
1007 ((fatFile *)(*file))->dirCluster = dirClust;
1008 ((fatFile *)(*file))->dirIndex = subEntry;
1009 /* Maybe modTime and date should be used ? (crt matches findnext) */
1010 ((fatFile *)(*file))->time = fileEntry.crtTime;
1011 ((fatFile *)(*file))->date = fileEntry.crtDate;
1012
1013 dos.errorcode=save_errorcode;
1014 return true;
1015 }
1016
FileExists(const char * name)1017 bool fatDrive::FileExists(const char *name) {
1018 direntry fileEntry;
1019 Bit32u dummy1, dummy2;
1020 Bit16u save_errorcode = dos.errorcode;
1021 bool found = getFileDirEntry(name, &fileEntry, &dummy1, &dummy2);
1022 dos.errorcode = save_errorcode;
1023 return found;
1024 }
1025
FileOpen(DOS_File ** file,char * name,Bit32u flags)1026 bool fatDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) {
1027 direntry fileEntry;
1028 Bit32u dirClust, subEntry;
1029 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
1030 /* TODO: check for read-only flag and requested write access */
1031 *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
1032 (*file)->flags = flags;
1033 ((fatFile *)(*file))->dirCluster = dirClust;
1034 ((fatFile *)(*file))->dirIndex = subEntry;
1035 /* Maybe modTime and date should be used ? (crt matches findnext) */
1036 ((fatFile *)(*file))->time = fileEntry.crtTime;
1037 ((fatFile *)(*file))->date = fileEntry.crtDate;
1038 return true;
1039 }
1040
FileStat(const char *,FileStat_Block * const)1041 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
1042 /* TODO: Stub */
1043 return false;
1044 }
1045
FileUnlink(char * name)1046 bool fatDrive::FileUnlink(char * name) {
1047 direntry fileEntry;
1048 Bit32u dirClust, subEntry;
1049
1050 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
1051 DOS_SetError(DOSERR_FILE_NOT_FOUND);
1052 return false;
1053 }
1054 /*
1055 Technically correct, but maybe an unwanted obstruction, so inactive for now.
1056
1057 if(fileEntry.attrib & (DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN)) {
1058 DOS_SetError(DOSERR_FILE_NOT_FOUND);
1059 return false;
1060 }
1061 if(fileEntry.attrib & DOS_ATTR_READ_ONLY) {
1062 DOS_SetError(DOSERR_ACCESS_DENIED);
1063 return false;
1064 }
1065 */
1066 fileEntry.entryname[0] = 0xe5;
1067 directoryChange(dirClust, &fileEntry, subEntry);
1068
1069 if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
1070
1071 return true;
1072 }
1073
FindFirst(char * _dir,DOS_DTA & dta,bool)1074 bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
1075 direntry dummyClust;
1076 #if 0
1077 Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
1078 dta.GetSearchParams(attr,pattern);
1079 if(attr==DOS_ATTR_VOLUME) {
1080 if (strcmp(GetLabel(), "") == 0 ) {
1081 DOS_SetError(DOSERR_NO_MORE_FILES);
1082 return false;
1083 }
1084 dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
1085 return true;
1086 }
1087 if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst
1088 LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!");
1089 #endif
1090 if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
1091 DOS_SetError(DOSERR_PATH_NOT_FOUND);
1092 return false;
1093 }
1094 dta.SetDirID(0);
1095 dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
1096 return FindNextInternal(cwdDirCluster, dta, &dummyClust);
1097 }
1098
removeTrailingSpaces(char * str,const size_t max_len)1099 char* removeTrailingSpaces(char* str, const size_t max_len) {
1100 const auto str_len = strnlen(str, max_len);
1101 if (str_len == 0)
1102 return str;
1103
1104 char* end = str + str_len;
1105 while((*--end == ' ') && (end > str)) {
1106 /* do nothing; break on while criteria */
1107 }
1108 *++end = '\0';
1109 return str;
1110 }
1111
removeLeadingSpaces(char * str,const size_t max_len)1112 char* removeLeadingSpaces(char* str, const size_t max_len) {
1113 const size_t len = strnlen(str, max_len);
1114 const size_t pos = strspn(str, " ");
1115 memmove(str, str + pos, len - pos + 1);
1116 return str;
1117 }
1118
trimString(char * str,const size_t max_len)1119 char* trimString(char* str, const size_t max_len) {
1120 return removeTrailingSpaces(removeLeadingSpaces(str, max_len), max_len);
1121 }
1122
copyDirEntry(const direntry * src,direntry * dst)1123 static void copyDirEntry(const direntry *src, direntry *dst) {
1124 memcpy(dst->entryname, src->entryname, sizeof(src->entryname));
1125 dst->attrib = host_to_le(src->attrib);
1126 dst->NTRes = host_to_le(src->NTRes);
1127 dst->milliSecondStamp = host_to_le(src->milliSecondStamp);
1128 dst->crtTime = host_to_le(src->crtTime);
1129 dst->crtDate = host_to_le(src->crtDate);
1130 dst->accessDate = host_to_le(src->accessDate);
1131 dst->hiFirstClust = host_to_le(src->hiFirstClust);
1132 dst->modTime = host_to_le(src->modTime);
1133 dst->modDate = host_to_le(src->modDate);
1134 dst->loFirstClust = host_to_le(src->loFirstClust);
1135 dst->entrysize = host_to_le(src->entrysize);
1136 }
1137
FindNextInternal(Bit32u dirClustNumber,DOS_DTA & dta,direntry * foundEntry)1138 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
1139 direntry sectbuf[16]; /* 16 directory entries per sector */
1140 Bit32u logentsector; /* Logical entry sector */
1141 Bit32u entryoffset; /* Index offset within sector */
1142 Bit32u tmpsector;
1143 Bit8u attrs;
1144 Bit16u dirPos;
1145 char srch_pattern[DOS_NAMELENGTH_ASCII];
1146 char find_name[DOS_NAMELENGTH_ASCII];
1147 char extension[4];
1148
1149 dta.GetSearchParams(attrs, srch_pattern);
1150 dirPos = dta.GetDirID();
1151
1152 nextfile:
1153 logentsector = dirPos / 16;
1154 entryoffset = dirPos % 16;
1155
1156 if(dirClustNumber==0) {
1157 if(dirPos >= bootbuffer.rootdirentries) {
1158 DOS_SetError(DOSERR_NO_MORE_FILES);
1159 return false;
1160 }
1161 readSector(firstRootDirSect+logentsector,sectbuf);
1162 } else {
1163 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1164 /* A zero sector number can't happen */
1165 if(tmpsector == 0) {
1166 DOS_SetError(DOSERR_NO_MORE_FILES);
1167 return false;
1168 }
1169 readSector(tmpsector,sectbuf);
1170 }
1171 dirPos++;
1172 dta.SetDirID(dirPos);
1173
1174 /* Deleted file entry */
1175 if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
1176
1177 /* End of directory list */
1178 if (sectbuf[entryoffset].entryname[0] == 0x00) {
1179 DOS_SetError(DOSERR_NO_MORE_FILES);
1180 return false;
1181 }
1182 memset(find_name,0,DOS_NAMELENGTH_ASCII);
1183 memset(extension,0,4);
1184 memcpy(find_name,§buf[entryoffset].entryname[0],8);
1185 memcpy(extension,§buf[entryoffset].entryname[8],3);
1186 trimString(&find_name[0], sizeof(find_name));
1187 trimString(&extension[0], sizeof(extension));
1188
1189 //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY))
1190 if (extension[0]!=0) {
1191 safe_strcat(find_name, ".");
1192 safe_strcat(find_name, extension);
1193 }
1194
1195 /* Compare attributes to search attributes */
1196
1197 //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
1198 if (attrs == DOS_ATTR_VOLUME) {
1199 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
1200 dirCache.SetLabel(find_name, false, true);
1201 } else {
1202 if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile;
1203 }
1204
1205
1206 /* Compare name to search pattern */
1207 if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
1208
1209 copyDirEntry(§buf[entryoffset], foundEntry);
1210
1211 //dta.SetResult(find_name, foundEntry->entrysize, foundEntry->crtDate, foundEntry->crtTime, foundEntry->attrib);
1212
1213 dta.SetResult(find_name, foundEntry->entrysize, foundEntry->modDate, foundEntry->modTime, foundEntry->attrib);
1214
1215 return true;
1216 }
1217
FindNext(DOS_DTA & dta)1218 bool fatDrive::FindNext(DOS_DTA &dta) {
1219 direntry dummyClust;
1220
1221 return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
1222 }
1223
GetFileAttr(char * name,Bit16u * attr)1224 bool fatDrive::GetFileAttr(char *name, Bit16u *attr) {
1225 direntry fileEntry;
1226 Bit32u dirClust, subEntry;
1227 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
1228 char dirName[DOS_NAMELENGTH_ASCII];
1229 char pathName[11];
1230
1231 /* Can we even get the name of the directory itself? */
1232 if(!getEntryName(name, &dirName[0])) return false;
1233 convToDirFile(&dirName[0], &pathName[0]);
1234
1235 /* Get parent directory starting cluster */
1236 if(!getDirClustNum(name, &dirClust, true)) return false;
1237
1238 /* Find directory entry in parent directory */
1239 Bit32s fileidx = 2;
1240 if (dirClust==0) fileidx = 0; // root directory
1241 Bit32s last_idx=0;
1242 while(directoryBrowse(dirClust, &fileEntry, fileidx, last_idx)) {
1243 if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
1244 *attr=fileEntry.attrib;
1245 return true;
1246 }
1247 last_idx=fileidx;
1248 fileidx++;
1249 }
1250 return false;
1251 } else *attr=fileEntry.attrib;
1252 return true;
1253 }
1254
directoryBrowse(Bit32u dirClustNumber,direntry * useEntry,Bit32s entNum,Bit32s start)1255 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
1256 direntry sectbuf[16]; /* 16 directory entries per sector */
1257 Bit32u logentsector; /* Logical entry sector */
1258 Bit32u entryoffset = 0; /* Index offset within sector */
1259 Bit32u tmpsector;
1260 if ((start<0) || (start>65535)) return false;
1261 Bit16u dirPos = (Bit16u)start;
1262 if (entNum<start) return false;
1263 entNum-=start;
1264
1265 while(entNum>=0) {
1266
1267 logentsector = dirPos / 16;
1268 entryoffset = dirPos % 16;
1269
1270 if(dirClustNumber==0) {
1271 if(dirPos >= bootbuffer.rootdirentries) return false;
1272 tmpsector = firstRootDirSect+logentsector;
1273 readSector(tmpsector,sectbuf);
1274 } else {
1275 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1276 /* A zero sector number can't happen */
1277 if(tmpsector == 0) return false;
1278 readSector(tmpsector,sectbuf);
1279 }
1280 dirPos++;
1281
1282
1283 /* End of directory list */
1284 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
1285 --entNum;
1286 }
1287
1288 copyDirEntry(§buf[entryoffset], useEntry);
1289 return true;
1290 }
1291
directoryChange(Bit32u dirClustNumber,direntry * useEntry,Bit32s entNum)1292 bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
1293 direntry sectbuf[16]; /* 16 directory entries per sector */
1294 Bit32u logentsector; /* Logical entry sector */
1295 Bit32u entryoffset = 0; /* Index offset within sector */
1296 Bit32u tmpsector = 0;
1297 Bit16u dirPos = 0;
1298
1299 while(entNum>=0) {
1300
1301 logentsector = dirPos / 16;
1302 entryoffset = dirPos % 16;
1303
1304 if(dirClustNumber==0) {
1305 if(dirPos >= bootbuffer.rootdirentries) return false;
1306 tmpsector = firstRootDirSect+logentsector;
1307 readSector(tmpsector,sectbuf);
1308 } else {
1309 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1310 /* A zero sector number can't happen */
1311 if(tmpsector == 0) return false;
1312 readSector(tmpsector,sectbuf);
1313 }
1314 dirPos++;
1315
1316
1317 /* End of directory list */
1318 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
1319 --entNum;
1320 }
1321 if(tmpsector != 0) {
1322 copyDirEntry(useEntry, §buf[entryoffset]);
1323 writeSector(tmpsector, sectbuf);
1324 return true;
1325 } else {
1326 return false;
1327 }
1328 }
1329
addDirectoryEntry(Bit32u dirClustNumber,direntry useEntry)1330 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
1331 direntry sectbuf[16]; /* 16 directory entries per sector */
1332 Bit32u logentsector; /* Logical entry sector */
1333 Bit32u entryoffset; /* Index offset within sector */
1334 Bit32u tmpsector;
1335 Bit16u dirPos = 0;
1336
1337 for(;;) {
1338
1339 logentsector = dirPos / 16;
1340 entryoffset = dirPos % 16;
1341
1342 if(dirClustNumber==0) {
1343 if(dirPos >= bootbuffer.rootdirentries) return false;
1344 tmpsector = firstRootDirSect+logentsector;
1345 readSector(tmpsector,sectbuf);
1346 } else {
1347 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1348 /* A zero sector number can't happen - we need to allocate more room for this directory*/
1349 if(tmpsector == 0) {
1350 Bit32u newClust;
1351 newClust = appendCluster(dirClustNumber);
1352 if(newClust == 0) return false;
1353 /* Try again to get tmpsector */
1354 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1355 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
1356 }
1357 readSector(tmpsector,sectbuf);
1358 }
1359 dirPos++;
1360
1361 /* Deleted file entry or end of directory list */
1362 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
1363 copyDirEntry(&useEntry, §buf[entryoffset]);
1364 writeSector(tmpsector,sectbuf);
1365 break;
1366 }
1367 }
1368
1369 return true;
1370 }
1371
zeroOutCluster(Bit32u clustNumber)1372 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
1373 Bit8u secBuffer[512];
1374
1375 memset(&secBuffer[0], 0, 512);
1376
1377 int i;
1378 for(i=0;i<bootbuffer.sectorspercluster;i++) {
1379 writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
1380 }
1381 }
1382
MakeDir(char * dir)1383 bool fatDrive::MakeDir(char *dir) {
1384 Bit32u dummyClust, dirClust;
1385 direntry tmpentry;
1386 char dirName[DOS_NAMELENGTH_ASCII];
1387 char pathName[11];
1388
1389 /* Can we even get the name of the directory itself? */
1390 if(!getEntryName(dir, &dirName[0])) return false;
1391 convToDirFile(&dirName[0], &pathName[0]);
1392
1393 /* Fail to make directory if already exists */
1394 if(getDirClustNum(dir, &dummyClust, false)) return false;
1395
1396 dummyClust = getFirstFreeClust();
1397 /* No more space */
1398 if(dummyClust == 0) return false;
1399
1400 if(!allocateCluster(dummyClust, 0)) return false;
1401
1402 zeroOutCluster(dummyClust);
1403
1404 /* Can we find the base directory? */
1405 if(!getDirClustNum(dir, &dirClust, true)) return false;
1406
1407 /* Add the new directory to the base directory */
1408 memset(&tmpentry,0, sizeof(direntry));
1409 memcpy(&tmpentry.entryname, &pathName[0], 11);
1410 tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
1411 tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
1412 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1413 addDirectoryEntry(dirClust, tmpentry);
1414
1415 /* Add the [.] and [..] entries to our new directory*/
1416 /* [.] entry */
1417 memset(&tmpentry,0, sizeof(direntry));
1418 memcpy(&tmpentry.entryname, ". ", 11);
1419 tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
1420 tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
1421 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1422 addDirectoryEntry(dummyClust, tmpentry);
1423
1424 /* [..] entry */
1425 memset(&tmpentry,0, sizeof(direntry));
1426 memcpy(&tmpentry.entryname, ".. ", 11);
1427 tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
1428 tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
1429 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1430 addDirectoryEntry(dummyClust, tmpentry);
1431
1432 return true;
1433 }
1434
RemoveDir(char * dir)1435 bool fatDrive::RemoveDir(char *dir) {
1436 Bit32u dummyClust, dirClust;
1437 direntry tmpentry;
1438 char dirName[DOS_NAMELENGTH_ASCII];
1439 char pathName[11];
1440
1441 /* Can we even get the name of the directory itself? */
1442 if(!getEntryName(dir, &dirName[0])) return false;
1443 convToDirFile(&dirName[0], &pathName[0]);
1444
1445 /* Get directory starting cluster */
1446 if(!getDirClustNum(dir, &dummyClust, false)) return false;
1447
1448 /* Can't remove root directory */
1449 if(dummyClust == 0) return false;
1450
1451 /* Get parent directory starting cluster */
1452 if(!getDirClustNum(dir, &dirClust, true)) return false;
1453
1454 /* Check to make sure directory is empty */
1455 Bit32u filecount = 0;
1456 /* Set to 2 to skip first 2 entries, [.] and [..] */
1457 Bit32s fileidx = 2;
1458 while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
1459 /* Check for non-deleted files */
1460 if(tmpentry.entryname[0] != 0xe5) filecount++;
1461 fileidx++;
1462 }
1463
1464 /* Return if directory is not empty */
1465 if(filecount > 0) return false;
1466
1467 /* Find directory entry in parent directory */
1468 if (dirClust==0) fileidx = 0; // root directory
1469 else fileidx = 2;
1470 bool found = false;
1471 while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
1472 if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
1473 found = true;
1474 tmpentry.entryname[0] = 0xe5;
1475 directoryChange(dirClust, &tmpentry, fileidx);
1476 deleteClustChain(dummyClust, 0);
1477
1478 break;
1479 }
1480 fileidx++;
1481 }
1482
1483 if(!found) return false;
1484
1485 return true;
1486 }
1487
Rename(char * oldname,char * newname)1488 bool fatDrive::Rename(char * oldname, char * newname) {
1489 direntry fileEntry1;
1490 Bit32u dirClust1, subEntry1;
1491 if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
1492 /* File to be renamed really exists */
1493
1494 direntry fileEntry2;
1495 Bit32u dirClust2, subEntry2;
1496
1497 /* Check if file already exists */
1498 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
1499 /* Target doesn't exist, can rename */
1500
1501 char dirName2[DOS_NAMELENGTH_ASCII];
1502 char pathName2[11];
1503 /* Can we even get the name of the file itself? */
1504 if(!getEntryName(newname, &dirName2[0])) return false;
1505 convToDirFile(&dirName2[0], &pathName2[0]);
1506
1507 /* Can we find the base directory? */
1508 if(!getDirClustNum(newname, &dirClust2, true)) return false;
1509 memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
1510 memcpy(&fileEntry2.entryname, &pathName2[0], 11);
1511 addDirectoryEntry(dirClust2, fileEntry2);
1512
1513 /* Check if file exists now */
1514 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
1515
1516 /* Remove old entry */
1517 fileEntry1.entryname[0] = 0xe5;
1518 directoryChange(dirClust1, &fileEntry1, subEntry1);
1519
1520 return true;
1521 }
1522
1523 /* Target already exists, fail */
1524 return false;
1525 }
1526
TestDir(char * dir)1527 bool fatDrive::TestDir(char *dir) {
1528 Bit32u dummyClust;
1529 return getDirClustNum(dir, &dummyClust, false);
1530 }
1531