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