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(&sectorBuffer[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, &sect, &sectsize);
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,&sectbuf[entryoffset].entryname[0],8);
942 	memcpy(extension,&sectbuf[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, &sectbuf[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, &sectbuf[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(&sectbuf[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