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