1 /*
2 * Copyright (C) 2002-2010 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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 /* $Id: drive_fat.cpp,v 1.28 2009-06-19 18:28:10 c2woody Exp $ */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 #include "dosbox.h"
26 #include "dos_inc.h"
27 #include "drives.h"
28 #include "support.h"
29 #include "cross.h"
30 #include "bios.h"
31
32 #define IMGTYPE_FLOPPY 0
33 #define IMGTYPE_ISO 1
34 #define IMGTYPE_HDD 2
35
36 #define FAT12 0
37 #define FAT16 1
38 #define FAT32 2
39
40 Bit8u fatSectBuffer[1024];
41 Bit32u curFatSect;
42
43 class fatFile : public DOS_File {
44 public:
45 fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
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 private:
66 enum { NONE,READ,WRITE } last_action;
67 Bit16u info;
68 };
69
70
71 /* IN - char * filename: Name in regular filename format, e.g. bob.txt */
72 /* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob txt */
convToDirFile(char * filename,char * filearray)73 static void convToDirFile(char *filename, char *filearray) {
74 Bit32u charidx = 0;
75 Bit32u flen,i;
76 flen = (Bit32u)strlen(filename);
77 memset(filearray, 32, 11);
78 for(i=0;i<flen;i++) {
79 if(charidx >= 11) break;
80 if(filename[i] != '.') {
81 filearray[charidx] = filename[i];
82 charidx++;
83 } else {
84 charidx = 8;
85 }
86 }
87 }
88
fatFile(const char *,Bit32u startCluster,Bit32u fileLen,fatDrive * useDrive)89 fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) {
90 Bit32u seekto = 0;
91 firstCluster = startCluster;
92 myDrive = useDrive;
93 filelength = fileLen;
94 open = true;
95 loadedSector = false;
96 curSectOff = 0;
97 seekpos = 0;
98 memset(§orBuffer[0], 0, sizeof(sectorBuffer));
99
100 if(filelength > 0) {
101 Seek(&seekto, DOS_SEEK_SET);
102 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
103 loadedSector = true;
104 }
105 }
106
Read(Bit8u * data,Bit16u * size)107 bool fatFile::Read(Bit8u * data, Bit16u *size) {
108 if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode
109 DOS_SetError(DOSERR_ACCESS_DENIED);
110 return false;
111 }
112 Bit16u sizedec, sizecount;
113 if(seekpos >= filelength) {
114 *size = 0;
115 return true;
116 }
117
118 if (!loadedSector) {
119 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
120 if(currentSector == 0) {
121 /* EOC reached before EOF */
122 *size = 0;
123 loadedSector = false;
124 return true;
125 }
126 curSectOff = 0;
127 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
128 loadedSector = true;
129 }
130
131 sizedec = *size;
132 sizecount = 0;
133 while(sizedec != 0) {
134 if(seekpos >= filelength) {
135 *size = sizecount;
136 return true;
137 }
138 data[sizecount++] = sectorBuffer[curSectOff++];
139 seekpos++;
140 if(curSectOff >= myDrive->getSectorSize()) {
141 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
142 if(currentSector == 0) {
143 /* EOC reached before EOF */
144 //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
145 *size = sizecount;
146 loadedSector = false;
147 return true;
148 }
149 curSectOff = 0;
150 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
151 loadedSector = true;
152 //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
153 }
154 --sizedec;
155 }
156 *size =sizecount;
157 return true;
158 }
159
Write(Bit8u * data,Bit16u * size)160 bool fatFile::Write(Bit8u * data, Bit16u *size) {
161 /* TODO: Check for read-only bit */
162
163 if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
164 DOS_SetError(DOSERR_ACCESS_DENIED);
165 return false;
166 }
167
168 direntry tmpentry;
169 Bit16u sizedec, sizecount;
170 sizedec = *size;
171 sizecount = 0;
172
173 while(sizedec != 0) {
174 /* Increase filesize if necessary */
175 if(seekpos >= filelength) {
176 if(filelength == 0) {
177 firstCluster = myDrive->getFirstFreeClust();
178 myDrive->allocateCluster(firstCluster, 0);
179 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
180 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
181 loadedSector = true;
182 }
183 filelength = seekpos+1;
184 if (!loadedSector) {
185 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
186 if(currentSector == 0) {
187 /* EOC reached before EOF - try to increase file allocation */
188 myDrive->appendCluster(firstCluster);
189 /* Try getting sector again */
190 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
191 if(currentSector == 0) {
192 /* No can do. lets give up and go home. We must be out of room */
193 goto finalizeWrite;
194 }
195 }
196 curSectOff = 0;
197 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
198
199 loadedSector = true;
200 }
201 }
202 sectorBuffer[curSectOff++] = data[sizecount++];
203 seekpos++;
204 if(curSectOff >= myDrive->getSectorSize()) {
205 if(loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer);
206
207 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
208 if(currentSector == 0) {
209 /* EOC reached before EOF - try to increase file allocation */
210 myDrive->appendCluster(firstCluster);
211 /* Try getting sector again */
212 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
213 if(currentSector == 0) {
214 /* No can do. lets give up and go home. We must be out of room */
215 loadedSector = false;
216 goto finalizeWrite;
217 }
218 }
219 curSectOff = 0;
220 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
221
222 loadedSector = true;
223 }
224 --sizedec;
225 }
226 if(curSectOff>0 && loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer);
227
228 finalizeWrite:
229 myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex);
230 tmpentry.entrysize = filelength;
231 tmpentry.loFirstClust = (Bit16u)firstCluster;
232 myDrive->directoryChange(dirCluster, &tmpentry, dirIndex);
233
234 *size =sizecount;
235 return true;
236 }
237
Seek(Bit32u * pos,Bit32u type)238 bool fatFile::Seek(Bit32u *pos, Bit32u type) {
239 Bit32s seekto=0;
240
241 switch(type) {
242 case DOS_SEEK_SET:
243 seekto = (Bit32s)*pos;
244 break;
245 case DOS_SEEK_CUR:
246 /* Is this relative seek signed? */
247 seekto = (Bit32s)*pos + (Bit32s)seekpos;
248 break;
249 case DOS_SEEK_END:
250 seekto = (Bit32s)filelength + (Bit32s)*pos;
251 break;
252 }
253 // LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
254
255 if((Bit32u)seekto > filelength) seekto = (Bit32s)filelength;
256 if(seekto<0) seekto = 0;
257 seekpos = (Bit32u)seekto;
258 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
259 if (currentSector == 0) {
260 /* not within file size, thus no sector is available */
261 loadedSector = false;
262 } else {
263 curSectOff = seekpos % myDrive->getSectorSize();
264 myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer);
265 }
266 *pos = seekpos;
267 return true;
268 }
269
Close()270 bool fatFile::Close() {
271 /* Flush buffer */
272 if (loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer);
273
274 return false;
275 }
276
GetInformation(void)277 Bit16u fatFile::GetInformation(void) {
278 return 0;
279 }
280
UpdateDateTimeFromHost(void)281 bool fatFile::UpdateDateTimeFromHost(void) {
282 return true;
283 }
284
getClustFirstSect(Bit32u clustNum)285 Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
286 return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
287 }
288
getClusterValue(Bit32u clustNum)289 Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
290 Bit32u fatoffset=0;
291 Bit32u fatsectnum;
292 Bit32u fatentoff;
293 Bit32u clustValue=0;
294
295 switch(fattype) {
296 case FAT12:
297 fatoffset = clustNum + (clustNum / 2);
298 break;
299 case FAT16:
300 fatoffset = clustNum * 2;
301 break;
302 case FAT32:
303 fatoffset = clustNum * 4;
304 break;
305 }
306 fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
307 fatentoff = fatoffset % bootbuffer.bytespersector;
308
309 if(curFatSect != fatsectnum) {
310 /* Load two sectors at once for FAT12 */
311 loadedDisk->Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]);
312 if (fattype==FAT12)
313 loadedDisk->Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[512]);
314 curFatSect = fatsectnum;
315 }
316
317 switch(fattype) {
318 case FAT12:
319 clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
320 if(clustNum & 0x1) {
321 clustValue >>= 4;
322 } else {
323 clustValue &= 0xfff;
324 }
325 break;
326 case FAT16:
327 clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
328 break;
329 case FAT32:
330 clustValue = *((Bit32u *)&fatSectBuffer[fatentoff]);
331 break;
332 }
333
334 return clustValue;
335 }
336
setClusterValue(Bit32u clustNum,Bit32u clustValue)337 void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
338 Bit32u fatoffset=0;
339 Bit32u fatsectnum;
340 Bit32u fatentoff;
341
342 switch(fattype) {
343 case FAT12:
344 fatoffset = clustNum + (clustNum / 2);
345 break;
346 case FAT16:
347 fatoffset = clustNum * 2;
348 break;
349 case FAT32:
350 fatoffset = clustNum * 4;
351 break;
352 }
353 fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
354 fatentoff = fatoffset % bootbuffer.bytespersector;
355
356 if(curFatSect != fatsectnum) {
357 /* Load two sectors at once for FAT12 */
358 loadedDisk->Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]);
359 if (fattype==FAT12)
360 loadedDisk->Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[512]);
361 curFatSect = fatsectnum;
362 }
363
364 switch(fattype) {
365 case FAT12: {
366 Bit16u tmpValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
367 if(clustNum & 0x1) {
368 clustValue &= 0xfff;
369 clustValue <<= 4;
370 tmpValue &= 0xf;
371 tmpValue |= (Bit16u)clustValue;
372
373 } else {
374 clustValue &= 0xfff;
375 tmpValue &= 0xf000;
376 tmpValue |= (Bit16u)clustValue;
377 }
378 *((Bit16u *)&fatSectBuffer[fatentoff]) = tmpValue;
379 break;
380 }
381 case FAT16:
382 *((Bit16u *)&fatSectBuffer[fatentoff]) = (Bit16u)clustValue;
383 break;
384 case FAT32:
385 *((Bit32u *)&fatSectBuffer[fatentoff]) = clustValue;
386 break;
387 }
388 for(int fc=0;fc<bootbuffer.fatcopies;fc++) {
389 loadedDisk->Write_AbsoluteSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
390 if (fattype==FAT12) {
391 if (fatentoff>=511)
392 loadedDisk->Write_AbsoluteSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]);
393 }
394 }
395 }
396
getEntryName(char * fullname,char * entname)397 bool fatDrive::getEntryName(char *fullname, char *entname) {
398 char dirtoken[DOS_PATHLENGTH];
399
400 char * findDir;
401 char * findFile;
402 strcpy(dirtoken,fullname);
403
404 //LOG_MSG("Testing for filename %s", fullname);
405 findDir = strtok(dirtoken,"\\");
406 if (findDir==NULL) {
407 return true; // root always exists
408 }
409 findFile = findDir;
410 while(findDir != NULL) {
411 findFile = findDir;
412 findDir = strtok(NULL,"\\");
413 }
414 strcpy(entname, findFile);
415 return true;
416 }
417
getFileDirEntry(char const * const filename,direntry * useEntry,Bit32u * dirClust,Bit32u * subEntry)418 bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) {
419 size_t len = strlen(filename);
420 char dirtoken[DOS_PATHLENGTH];
421 Bit32u currentClust = 0;
422
423 direntry foundEntry;
424 char * findDir;
425 char * findFile;
426 strcpy(dirtoken,filename);
427 findFile=dirtoken;
428
429 /* Skip if testing in root directory */
430 if ((len>0) && (filename[len-1]!='\\')) {
431 //LOG_MSG("Testing for filename %s", filename);
432 findDir = strtok(dirtoken,"\\");
433 findFile = findDir;
434 while(findDir != NULL) {
435 imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
436 imgDTA->SetDirID(0);
437
438 findFile = findDir;
439 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
440 else {
441 //Found something. See if it's a directory (findfirst always finds regular files)
442 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
443 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
444 if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
445 }
446
447 currentClust = foundEntry.loFirstClust;
448 findDir = strtok(NULL,"\\");
449 }
450 } else {
451 /* Set to root directory */
452 }
453
454 /* Search found directory for our file */
455 imgDTA->SetupSearch(0,0x7,findFile);
456 imgDTA->SetDirID(0);
457 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false;
458
459 memcpy(useEntry, &foundEntry, sizeof(direntry));
460 *dirClust = (Bit32u)currentClust;
461 *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
462 return true;
463 }
464
getDirClustNum(char * dir,Bit32u * clustNum,bool parDir)465 bool fatDrive::getDirClustNum(char *dir, Bit32u *clustNum, bool parDir) {
466 Bit32u len = (Bit32u)strlen(dir);
467 char dirtoken[DOS_PATHLENGTH];
468 Bit32u currentClust = 0;
469 direntry foundEntry;
470 char * findDir;
471 strcpy(dirtoken,dir);
472
473 /* Skip if testing for root directory */
474 if ((len>0) && (dir[len-1]!='\\')) {
475 //LOG_MSG("Testing for dir %s", dir);
476 findDir = strtok(dirtoken,"\\");
477 while(findDir != NULL) {
478 imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
479 imgDTA->SetDirID(0);
480 findDir = strtok(NULL,"\\");
481 if(parDir && (findDir == NULL)) break;
482
483 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
484 if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
485 return false;
486 } else {
487 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
488 if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
489 }
490 currentClust = foundEntry.loFirstClust;
491
492 }
493 *clustNum = currentClust;
494 } else {
495 /* Set to root directory */
496 *clustNum = 0;
497 }
498 return true;
499 }
500
getSectorSize(void)501 Bit32u fatDrive::getSectorSize(void) {
502 return bootbuffer.bytespersector;
503 }
504
getAbsoluteSectFromBytePos(Bit32u startClustNum,Bit32u bytePos)505 Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
506 return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
507 }
508
getAbsoluteSectFromChain(Bit32u startClustNum,Bit32u logicalSector)509 Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
510 Bit32s skipClust = logicalSector / bootbuffer.sectorspercluster;
511 Bit32u sectClust = logicalSector % bootbuffer.sectorspercluster;
512
513 Bit32u currentClust = startClustNum;
514 Bit32u testvalue;
515
516 while(skipClust!=0) {
517 bool isEOF = false;
518 testvalue = getClusterValue(currentClust);
519 switch(fattype) {
520 case FAT12:
521 if(testvalue >= 0xff8) isEOF = true;
522 break;
523 case FAT16:
524 if(testvalue >= 0xfff8) isEOF = true;
525 break;
526 case FAT32:
527 if(testvalue >= 0xfffffff8) isEOF = true;
528 break;
529 }
530 if((isEOF) && (skipClust>=1)) {
531 //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
532 return 0;
533 }
534 currentClust = testvalue;
535 --skipClust;
536 }
537
538 return (getClustFirstSect(currentClust) + sectClust);
539 }
540
deleteClustChain(Bit32u startCluster)541 void fatDrive::deleteClustChain(Bit32u startCluster) {
542 Bit32u testvalue;
543 Bit32u currentClust = startCluster;
544 bool isEOF = false;
545 while(!isEOF) {
546 testvalue = getClusterValue(currentClust);
547 if(testvalue == 0) {
548 /* What the crap? Cluster is already empty - BAIL! */
549 break;
550 }
551 /* Mark cluster as empty */
552 setClusterValue(currentClust, 0);
553 switch(fattype) {
554 case FAT12:
555 if(testvalue >= 0xff8) isEOF = true;
556 break;
557 case FAT16:
558 if(testvalue >= 0xfff8) isEOF = true;
559 break;
560 case FAT32:
561 if(testvalue >= 0xfffffff8) isEOF = true;
562 break;
563 }
564 if(isEOF) break;
565 currentClust = testvalue;
566 }
567 }
568
appendCluster(Bit32u startCluster)569 Bit32u fatDrive::appendCluster(Bit32u startCluster) {
570 Bit32u testvalue;
571 Bit32u currentClust = startCluster;
572 bool isEOF = false;
573
574 while(!isEOF) {
575 testvalue = getClusterValue(currentClust);
576 switch(fattype) {
577 case FAT12:
578 if(testvalue >= 0xff8) isEOF = true;
579 break;
580 case FAT16:
581 if(testvalue >= 0xfff8) isEOF = true;
582 break;
583 case FAT32:
584 if(testvalue >= 0xfffffff8) isEOF = true;
585 break;
586 }
587 if(isEOF) break;
588 currentClust = testvalue;
589 }
590
591 Bit32u newClust = getFirstFreeClust();
592 /* Drive is full */
593 if(newClust == 0) return 0;
594
595 if(!allocateCluster(newClust, currentClust)) return 0;
596
597 zeroOutCluster(newClust);
598
599 return newClust;
600 }
601
allocateCluster(Bit32u useCluster,Bit32u prevCluster)602 bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
603
604 /* Can't allocate cluster #0 */
605 if(useCluster == 0) return false;
606
607 if(prevCluster != 0) {
608 /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
609 if(!getClusterValue(prevCluster)) return false;
610
611 /* Point cluster to new cluster in chain */
612 setClusterValue(prevCluster, useCluster);
613 //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
614 }
615
616 switch(fattype) {
617 case FAT12:
618 setClusterValue(useCluster, 0xfff);
619 break;
620 case FAT16:
621 setClusterValue(useCluster, 0xffff);
622 break;
623 case FAT32:
624 setClusterValue(useCluster, 0xffffffff);
625 break;
626 }
627 return true;
628 }
629
fatDrive(const char * sysFilename,Bit32u bytesector,Bit32u cylsector,Bit32u headscyl,Bit32u cylinders,Bit32u startSector)630 fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector) {
631 created_successfully = true;
632 FILE *diskfile;
633 Bit32u filesize;
634 struct partTable mbrData;
635
636 if(imgDTASeg == 0) {
637 imgDTASeg = DOS_GetMemory(2);
638 imgDTAPtr = RealMake(imgDTASeg, 0);
639 imgDTA = new DOS_DTA(imgDTAPtr);
640 }
641
642 diskfile = fopen_wrap(sysFilename, "rb+");
643 if(!diskfile) {created_successfully = false;return;}
644 fseek(diskfile, 0L, SEEK_END);
645 filesize = (Bit32u)ftell(diskfile) / 1024L;
646
647 /* Load disk image */
648 loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
649 if(!loadedDisk) {
650 created_successfully = false;
651 return;
652 }
653
654 if(filesize > 2880) {
655 /* Set user specified harddrive parameters */
656 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
657
658 loadedDisk->Read_Sector(0,0,1,&mbrData);
659
660 if(mbrData.magic1!= 0x55 || mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
661
662 startSector = 63;
663 int m;
664 for(m=0;m<4;m++) {
665 /* Pick the first available partition */
666 if(mbrData.pentry[m].partSize != 0x00) {
667 LOG_MSG("Using partition %d on drive; skipping %d sectors", m, mbrData.pentry[m].absSectStart);
668 startSector = mbrData.pentry[m].absSectStart;
669 break;
670 }
671 }
672
673 if(m==4) LOG_MSG("No good partiton found in image.");
674
675 partSectOff = startSector;
676 } else {
677 /* Floppy disks don't have partitions */
678 partSectOff = 0;
679 }
680
681 loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
682 if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
683 /* Not a FAT filesystem */
684 LOG_MSG("Loaded image has no valid magicnumbers at the end!");
685 }
686
687 if(!bootbuffer.sectorsperfat) {
688 /* FAT32 not implemented yet */
689 created_successfully = false;
690 return;
691 }
692
693
694 /* Determine FAT format, 12, 16 or 32 */
695
696 /* Get size of root dir in sectors */
697 /* TODO: Get 32-bit total sector count if needed */
698 Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector;
699 Bit32u DataSectors;
700 if(bootbuffer.totalsectorcount != 0) {
701 DataSectors = bootbuffer.totalsectorcount - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
702 } else {
703 DataSectors = bootbuffer.totalsecdword - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors);
704
705 }
706 CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
707
708 firstDataSector = (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors) + partSectOff;
709 firstRootDirSect = bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + partSectOff;
710
711 if(CountOfClusters < 4085) {
712 /* Volume is FAT12 */
713 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
714 fattype = FAT12;
715 } else if (CountOfClusters < 65525) {
716 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
717 fattype = FAT16;
718 } else {
719 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
720 fattype = FAT32;
721 }
722
723 /* There is no cluster 0, this means we are in the root directory */
724 cwdDirCluster = 0;
725
726 memset(fatSectBuffer,0,1024);
727 curFatSect = 0xffffffff;
728 }
729
AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters)730 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
731 Bit32u hs, cy, sect,sectsize;
732 Bit32u countFree = 0;
733 Bit32u i;
734
735 loadedDisk->Get_Geometry(&hs, &cy, §, §size);
736 *_bytes_sector = (Bit16u)sectsize;
737 *_sectors_cluster = bootbuffer.sectorspercluster;
738 if (CountOfClusters<65536) *_total_clusters = (Bit16u)CountOfClusters;
739 else {
740 // maybe some special handling needed for fat32
741 *_total_clusters = 65535;
742 }
743 for(i=0;i<CountOfClusters;i++) if(!getClusterValue(i+2)) countFree++;
744 if (countFree<65536) *_free_clusters = (Bit16u)countFree;
745 else {
746 // maybe some special handling needed for fat32
747 *_free_clusters = 65535;
748 }
749
750 return true;
751 }
752
getFirstFreeClust(void)753 Bit32u fatDrive::getFirstFreeClust(void) {
754 Bit32u i;
755 for(i=0;i<CountOfClusters;i++) {
756 if(!getClusterValue(i+2)) return (i+2);
757 }
758
759 /* No free cluster found */
760 return 0;
761 }
762
isRemote(void)763 bool fatDrive::isRemote(void) { return false; }
isRemovable(void)764 bool fatDrive::isRemovable(void) { return false; }
765
UnMount(void)766 Bits fatDrive::UnMount(void) {
767 delete this;
768 return 0;
769 }
770
GetMediaByte(void)771 Bit8u fatDrive::GetMediaByte(void) { return loadedDisk->GetBiosType(); }
772
FileCreate(DOS_File ** file,char * name,Bit16u attributes)773 bool fatDrive::FileCreate(DOS_File **file, char *name, Bit16u attributes) {
774 direntry fileEntry;
775 Bit32u dirClust, subEntry;
776 char dirName[DOS_NAMELENGTH_ASCII];
777 char pathName[11];
778
779 Bit16u save_errorcode=dos.errorcode;
780
781 /* Check if file already exists */
782 if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
783 /* Truncate file */
784 fileEntry.entrysize=0;
785 directoryChange(dirClust, &fileEntry, subEntry);
786 } else {
787 /* Can we even get the name of the file itself? */
788 if(!getEntryName(name, &dirName[0])) return false;
789 convToDirFile(&dirName[0], &pathName[0]);
790
791 /* Can we find the base directory? */
792 if(!getDirClustNum(name, &dirClust, true)) return false;
793 memset(&fileEntry, 0, sizeof(direntry));
794 memcpy(&fileEntry.entryname, &pathName[0], 11);
795 fileEntry.attrib = (Bit8u)(attributes & 0xff);
796 addDirectoryEntry(dirClust, fileEntry);
797
798 /* Check if file exists now */
799 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
800 }
801
802 /* Empty file created, now lets open it */
803 /* TODO: check for read-only flag and requested write access */
804 *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
805 (*file)->flags=OPEN_READWRITE;
806 ((fatFile *)(*file))->dirCluster = dirClust;
807 ((fatFile *)(*file))->dirIndex = subEntry;
808 /* Maybe modTime and date should be used ? (crt matches findnext) */
809 ((fatFile *)(*file))->time = fileEntry.crtTime;
810 ((fatFile *)(*file))->date = fileEntry.crtDate;
811
812 dos.errorcode=save_errorcode;
813 return true;
814 }
815
FileExists(const char * name)816 bool fatDrive::FileExists(const char *name) {
817 direntry fileEntry;
818 Bit32u dummy1, dummy2;
819 if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
820 return true;
821 }
822
FileOpen(DOS_File ** file,char * name,Bit32u flags)823 bool fatDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) {
824 direntry fileEntry;
825 Bit32u dirClust, subEntry;
826 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
827 /* TODO: check for read-only flag and requested write access */
828 *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
829 (*file)->flags = flags;
830 ((fatFile *)(*file))->dirCluster = dirClust;
831 ((fatFile *)(*file))->dirIndex = subEntry;
832 /* Maybe modTime and date should be used ? (crt matches findnext) */
833 ((fatFile *)(*file))->time = fileEntry.crtTime;
834 ((fatFile *)(*file))->date = fileEntry.crtDate;
835 return true;
836 }
837
FileStat(const char *,FileStat_Block * const)838 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
839 /* TODO: Stub */
840 return false;
841 }
842
FileUnlink(char * name)843 bool fatDrive::FileUnlink(char * name) {
844 direntry fileEntry;
845 Bit32u dirClust, subEntry;
846
847 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
848
849 fileEntry.entryname[0] = 0xe5;
850 directoryChange(dirClust, &fileEntry, subEntry);
851
852 if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust);
853
854 return true;
855 }
856
FindFirst(char * _dir,DOS_DTA & dta,bool)857 bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
858 direntry dummyClust;
859 Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
860 dta.GetSearchParams(attr,pattern);
861 if(attr==DOS_ATTR_VOLUME) {
862 if (strcmp(GetLabel(), "") == 0 ) {
863 DOS_SetError(DOSERR_NO_MORE_FILES);
864 return false;
865 }
866 dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
867 return true;
868 }
869 if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst
870 LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!");
871 if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
872 DOS_SetError(DOSERR_PATH_NOT_FOUND);
873 return false;
874 }
875 dta.SetDirID(0);
876 dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
877 return FindNextInternal(cwdDirCluster, dta, &dummyClust);
878 }
879
removeTrailingSpaces(char * str)880 char* removeTrailingSpaces(char* str) {
881 char* end = str + strlen(str);
882 while((*--end == ' ') && (end > str)) {};
883 *++end = '\0';
884 return str;
885 }
886
removeLeadingSpaces(char * str)887 char* removeLeadingSpaces(char* str) {
888 size_t len = strlen(str);
889 size_t pos = strspn(str," ");
890 memmove(str,str + pos,len - pos + 1);
891 return str;
892 }
893
trimString(char * str)894 char* trimString(char* str) {
895 return removeTrailingSpaces(removeLeadingSpaces(str));
896 }
897
FindNextInternal(Bit32u dirClustNumber,DOS_DTA & dta,direntry * foundEntry)898 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
899 direntry sectbuf[16]; /* 16 directory entries per sector */
900 Bit32u logentsector; /* Logical entry sector */
901 Bit32u entryoffset; /* Index offset within sector */
902 Bit32u tmpsector;
903 Bit8u attrs;
904 Bit16u dirPos;
905 char srch_pattern[DOS_NAMELENGTH_ASCII];
906 char find_name[DOS_NAMELENGTH_ASCII];
907 char extension[4];
908
909 dta.GetSearchParams(attrs, srch_pattern);
910 dirPos = dta.GetDirID();
911
912 nextfile:
913 logentsector = dirPos / 16;
914 entryoffset = dirPos % 16;
915
916 if(dirClustNumber==0) {
917 loadedDisk->Read_AbsoluteSector(firstRootDirSect+logentsector,sectbuf);
918 } else {
919 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
920 /* A zero sector number can't happen */
921 if(tmpsector == 0) {
922 DOS_SetError(DOSERR_NO_MORE_FILES);
923 return false;
924 }
925 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
926 }
927 dirPos++;
928 dta.SetDirID(dirPos);
929
930 /* Deleted file entry */
931 if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
932
933 /* End of directory list */
934 if (sectbuf[entryoffset].entryname[0] == 0x00) {
935 DOS_SetError(DOSERR_NO_MORE_FILES);
936 return false;
937 }
938
939 memset(find_name,0,DOS_NAMELENGTH_ASCII);
940 memset(extension,0,4);
941 memcpy(find_name,§buf[entryoffset].entryname[0],8);
942 memcpy(extension,§buf[entryoffset].entryname[8],3);
943 trimString(&find_name[0]);
944 trimString(&extension[0]);
945 if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY) || extension[0]!=0) {
946 strcat(find_name, ".");
947 strcat(find_name, extension);
948 }
949
950 /* Ignore files with volume label. FindFirst should search for those. (return the first one found) */
951 if(sectbuf[entryoffset].attrib & 0x8) goto nextfile;
952
953 /* Always find ARCHIVES even if bit is not set Perhaps test is not the best test */
954 if(~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM) ) goto nextfile;
955 if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
956
957 dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
958 memcpy(foundEntry, §buf[entryoffset], sizeof(direntry));
959
960 return true;
961 }
962
FindNext(DOS_DTA & dta)963 bool fatDrive::FindNext(DOS_DTA &dta) {
964 direntry dummyClust;
965
966 return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
967 }
968
GetFileAttr(char * name,Bit16u * attr)969 bool fatDrive::GetFileAttr(char *name, Bit16u *attr) {
970 direntry fileEntry;
971 Bit32u dirClust, subEntry;
972 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
973 char dirName[DOS_NAMELENGTH_ASCII];
974 char pathName[11];
975
976 /* Can we even get the name of the directory itself? */
977 if(!getEntryName(name, &dirName[0])) return false;
978 convToDirFile(&dirName[0], &pathName[0]);
979
980 /* Get parent directory starting cluster */
981 if(!getDirClustNum(name, &dirClust, true)) return false;
982
983 /* Find directory entry in parent directory */
984 Bit32s fileidx = 2;
985 if (dirClust==0) fileidx = 0; // root directory
986 while(directoryBrowse(dirClust, &fileEntry, fileidx)) {
987 if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
988 *attr=fileEntry.attrib;
989 return true;
990 }
991 fileidx++;
992 }
993 return false;
994 } else *attr=fileEntry.attrib;
995 return true;
996 }
997
directoryBrowse(Bit32u dirClustNumber,direntry * useEntry,Bit32s entNum)998 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
999 direntry sectbuf[16]; /* 16 directory entries per sector */
1000 Bit32u logentsector; /* Logical entry sector */
1001 Bit32u entryoffset = 0; /* Index offset within sector */
1002 Bit32u tmpsector;
1003 Bit16u dirPos = 0;
1004
1005 while(entNum>=0) {
1006
1007 logentsector = dirPos / 16;
1008 entryoffset = dirPos % 16;
1009
1010 if(dirClustNumber==0) {
1011 if(dirPos >= bootbuffer.rootdirentries) return false;
1012 tmpsector = firstRootDirSect+logentsector;
1013 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1014 } else {
1015 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1016 /* A zero sector number can't happen */
1017 if(tmpsector == 0) return false;
1018 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1019 }
1020 dirPos++;
1021
1022
1023 /* End of directory list */
1024 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
1025 --entNum;
1026 }
1027
1028 memcpy(useEntry, §buf[entryoffset],sizeof(direntry));
1029 return true;
1030 }
1031
directoryChange(Bit32u dirClustNumber,direntry * useEntry,Bit32s entNum)1032 bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
1033 direntry sectbuf[16]; /* 16 directory entries per sector */
1034 Bit32u logentsector; /* Logical entry sector */
1035 Bit32u entryoffset = 0; /* Index offset within sector */
1036 Bit32u tmpsector = 0;
1037 Bit16u dirPos = 0;
1038
1039 while(entNum>=0) {
1040
1041 logentsector = dirPos / 16;
1042 entryoffset = dirPos % 16;
1043
1044 if(dirClustNumber==0) {
1045 if(dirPos >= bootbuffer.rootdirentries) return false;
1046 tmpsector = firstRootDirSect+logentsector;
1047 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1048 } else {
1049 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1050 /* A zero sector number can't happen */
1051 if(tmpsector == 0) return false;
1052 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1053 }
1054 dirPos++;
1055
1056
1057 /* End of directory list */
1058 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
1059 --entNum;
1060 }
1061 if(tmpsector != 0) {
1062 memcpy(§buf[entryoffset], useEntry, sizeof(direntry));
1063 loadedDisk->Write_AbsoluteSector(tmpsector, sectbuf);
1064 return true;
1065 } else {
1066 return false;
1067 }
1068 }
1069
addDirectoryEntry(Bit32u dirClustNumber,direntry useEntry)1070 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
1071 direntry sectbuf[16]; /* 16 directory entries per sector */
1072 Bit32u logentsector; /* Logical entry sector */
1073 Bit32u entryoffset; /* Index offset within sector */
1074 Bit32u tmpsector;
1075 Bit16u dirPos = 0;
1076
1077 for(;;) {
1078
1079 logentsector = dirPos / 16;
1080 entryoffset = dirPos % 16;
1081
1082 if(dirClustNumber==0) {
1083 if(dirPos >= bootbuffer.rootdirentries) return false;
1084 tmpsector = firstRootDirSect+logentsector;
1085 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1086 } else {
1087 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1088 /* A zero sector number can't happen - we need to allocate more room for this directory*/
1089 if(tmpsector == 0) {
1090 Bit32u newClust;
1091 newClust = appendCluster(dirClustNumber);
1092 if(newClust == 0) return false;
1093 /* Try again to get tmpsector */
1094 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
1095 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
1096 }
1097 loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf);
1098 }
1099 dirPos++;
1100
1101 /* Deleted file entry or end of directory list */
1102 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
1103 sectbuf[entryoffset] = useEntry;
1104 loadedDisk->Write_AbsoluteSector(tmpsector,sectbuf);
1105 break;
1106 }
1107 }
1108
1109 return true;
1110 }
1111
zeroOutCluster(Bit32u clustNumber)1112 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
1113 Bit8u secBuffer[512];
1114
1115 memset(&secBuffer[0], 0, 512);
1116
1117 int i;
1118 for(i=0;i<bootbuffer.sectorspercluster;i++) {
1119 loadedDisk->Write_AbsoluteSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
1120 }
1121 }
1122
MakeDir(char * dir)1123 bool fatDrive::MakeDir(char *dir) {
1124 Bit32u dummyClust, dirClust;
1125 direntry tmpentry;
1126 char dirName[DOS_NAMELENGTH_ASCII];
1127 char pathName[11];
1128
1129 /* Can we even get the name of the directory itself? */
1130 if(!getEntryName(dir, &dirName[0])) return false;
1131 convToDirFile(&dirName[0], &pathName[0]);
1132
1133 /* Fail to make directory if already exists */
1134 if(getDirClustNum(dir, &dummyClust, false)) return false;
1135
1136 dummyClust = getFirstFreeClust();
1137 /* No more space */
1138 if(dummyClust == 0) return false;
1139
1140 if(!allocateCluster(dummyClust, 0)) return false;
1141
1142 zeroOutCluster(dummyClust);
1143
1144 /* Can we find the base directory? */
1145 if(!getDirClustNum(dir, &dirClust, true)) return false;
1146
1147 /* Add the new directory to the base directory */
1148 memset(&tmpentry,0, sizeof(direntry));
1149 memcpy(&tmpentry.entryname, &pathName[0], 11);
1150 tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
1151 tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
1152 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1153 addDirectoryEntry(dirClust, tmpentry);
1154
1155 /* Add the [.] and [..] entries to our new directory*/
1156 /* [.] entry */
1157 memset(&tmpentry,0, sizeof(direntry));
1158 memcpy(&tmpentry.entryname, ". ", 11);
1159 tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
1160 tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
1161 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1162 addDirectoryEntry(dummyClust, tmpentry);
1163
1164 /* [..] entry */
1165 memset(&tmpentry,0, sizeof(direntry));
1166 memcpy(&tmpentry.entryname, ".. ", 11);
1167 tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
1168 tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
1169 tmpentry.attrib = DOS_ATTR_DIRECTORY;
1170 addDirectoryEntry(dummyClust, tmpentry);
1171
1172 return true;
1173 }
1174
RemoveDir(char * dir)1175 bool fatDrive::RemoveDir(char *dir) {
1176 Bit32u dummyClust, dirClust;
1177 direntry tmpentry;
1178 char dirName[DOS_NAMELENGTH_ASCII];
1179 char pathName[11];
1180
1181 /* Can we even get the name of the directory itself? */
1182 if(!getEntryName(dir, &dirName[0])) return false;
1183 convToDirFile(&dirName[0], &pathName[0]);
1184
1185 /* Get directory starting cluster */
1186 if(!getDirClustNum(dir, &dummyClust, false)) return false;
1187
1188 /* Can't remove root directory */
1189 if(dummyClust == 0) return false;
1190
1191 /* Get parent directory starting cluster */
1192 if(!getDirClustNum(dir, &dirClust, true)) return false;
1193
1194 /* Check to make sure directory is empty */
1195 Bit32u filecount = 0;
1196 /* Set to 2 to skip first 2 entries, [.] and [..] */
1197 Bit32s fileidx = 2;
1198 while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
1199 /* Check for non-deleted files */
1200 if(tmpentry.entryname[0] != 0xe5) filecount++;
1201 fileidx++;
1202 }
1203
1204 /* Return if directory is not empty */
1205 if(filecount > 0) return false;
1206
1207 /* Find directory entry in parent directory */
1208 if (dirClust==0) fileidx = 0; // root directory
1209 else fileidx = 2;
1210 bool found = false;
1211 while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
1212 if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
1213 found = true;
1214 tmpentry.entryname[0] = 0xe5;
1215 directoryChange(dirClust, &tmpentry, fileidx);
1216 deleteClustChain(dummyClust);
1217
1218 break;
1219 }
1220 fileidx++;
1221 }
1222
1223 if(!found) return false;
1224
1225 return true;
1226 }
1227
Rename(char * oldname,char * newname)1228 bool fatDrive::Rename(char * oldname, char * newname) {
1229 direntry fileEntry1;
1230 Bit32u dirClust1, subEntry1;
1231 if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
1232 /* File to be renamed really exists */
1233
1234 direntry fileEntry2;
1235 Bit32u dirClust2, subEntry2;
1236
1237 /* Check if file already exists */
1238 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
1239 /* Target doesn't exist, can rename */
1240
1241 char dirName2[DOS_NAMELENGTH_ASCII];
1242 char pathName2[11];
1243 /* Can we even get the name of the file itself? */
1244 if(!getEntryName(newname, &dirName2[0])) return false;
1245 convToDirFile(&dirName2[0], &pathName2[0]);
1246
1247 /* Can we find the base directory? */
1248 if(!getDirClustNum(newname, &dirClust2, true)) return false;
1249 memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
1250 memcpy(&fileEntry2.entryname, &pathName2[0], 11);
1251 addDirectoryEntry(dirClust2, fileEntry2);
1252
1253 /* Check if file exists now */
1254 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
1255
1256 /* Remove old entry */
1257 fileEntry1.entryname[0] = 0xe5;
1258 directoryChange(dirClust1, &fileEntry1, subEntry1);
1259
1260 return true;
1261 }
1262
1263 /* Target already exists, fail */
1264 return false;
1265 }
1266
TestDir(char * dir)1267 bool fatDrive::TestDir(char *dir) {
1268 Bit32u dummyClust;
1269 return getDirClustNum(dir, &dummyClust, false);
1270 }
1271
1272