DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/drive_fat.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <time.h>
00024 #include "dosbox.h"
00025 #include "dos_inc.h"
00026 #include "drives.h"
00027 #include "support.h"
00028 #include "cross.h"
00029 #include "bios.h"
00030 #include "bios_disk.h"
00031 #include "qcow2_disk.h"
00032 
00033 #include <algorithm>
00034 
00035 #define IMGTYPE_FLOPPY 0
00036 #define IMGTYPE_ISO    1
00037 #define IMGTYPE_HDD        2
00038 
00039 #define FAT12              0
00040 #define FAT16              1
00041 #define FAT32              2
00042 
00043 class fatFile : public DOS_File {
00044 public:
00045         fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
00046         bool Read(Bit8u * data,Bit16u * size);
00047         bool Write(const Bit8u * data,Bit16u * size);
00048         bool Seek(Bit32u * pos,Bit32u type);
00049         bool Close();
00050         Bit16u GetInformation(void);
00051         bool UpdateDateTimeFromHost(void);   
00052         Bit32u GetSeekPos(void);
00053 public:
00054         Bit32u firstCluster;
00055         Bit32u seekpos;
00056         Bit32u filelength;
00057         Bit32u currentSector;
00058         Bit32u curSectOff;
00059         Bit8u sectorBuffer[SECTOR_SIZE_MAX];
00060         /* Record of where in the directory structure this file is located */
00061         Bit32u dirCluster;
00062         Bit32u dirIndex;
00063 
00064         bool loadedSector;
00065         fatDrive *myDrive;
00066 private:
00067 #if 0/*unused*/
00068     enum { NONE,READ,WRITE } last_action;
00069         Bit16u info;
00070 #endif
00071 };
00072 
00073 
00074 /* IN - char * filename: Name in regular filename format, e.g. bob.txt */
00075 /* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob     txt */
00076 static void convToDirFile(const char *filename, char *filearray) {
00077         Bit32u charidx = 0;
00078         Bit32u flen,i;
00079         flen = (Bit32u)strlen(filename);
00080         memset(filearray, 32, 11);
00081         for(i=0;i<flen;i++) {
00082                 if(charidx >= 11) break;
00083                 if(filename[i] != '.') {
00084                         filearray[charidx] = filename[i];
00085                         charidx++;
00086                 } else {
00087                         charidx = 8;
00088                 }
00089         }
00090 }
00091 
00092 fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) {
00093         Bit32u seekto = 0;
00094         firstCluster = startCluster;
00095         myDrive = useDrive;
00096         filelength = fileLen;
00097         open = true;
00098         loadedSector = false;
00099         curSectOff = 0;
00100         seekpos = 0;
00101         memset(&sectorBuffer[0], 0, sizeof(sectorBuffer));
00102         
00103         if(filelength > 0) {
00104                 Seek(&seekto, DOS_SEEK_SET);
00105                 myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00106                 loadedSector = true;
00107         }
00108 }
00109 
00110 bool fatFile::Read(Bit8u * data, Bit16u *size) {
00111         if ((this->flags & 0xf) == OPEN_WRITE) {        // check if file opened in write-only mode
00112                 DOS_SetError(DOSERR_ACCESS_DENIED);
00113                 return false;
00114         }
00115         Bit16u sizedec, sizecount;
00116         if(seekpos >= filelength) {
00117                 *size = 0;
00118                 return true;
00119         }
00120 
00121         if (!loadedSector) {
00122                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00123                 if(currentSector == 0) {
00124                         /* EOC reached before EOF */
00125                         *size = 0;
00126                         loadedSector = false;
00127                         return true;
00128                 }
00129                 curSectOff = 0;
00130                 myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00131                 loadedSector = true;
00132         }
00133 
00134         sizedec = *size;
00135         sizecount = 0;
00136         while(sizedec != 0) {
00137                 if(seekpos >= filelength) {
00138                         *size = sizecount;
00139                         return true; 
00140                 }
00141                 data[sizecount++] = sectorBuffer[curSectOff++];
00142                 seekpos++;
00143                 if(curSectOff >= myDrive->getSectorSize()) {
00144                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00145                         if(currentSector == 0) {
00146                                 /* EOC reached before EOF */
00147                                 //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
00148                                 *size = sizecount;
00149                                 loadedSector = false;
00150                                 return true;
00151                         }
00152                         curSectOff = 0;
00153                         myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00154                         loadedSector = true;
00155                         //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
00156                 }
00157                 --sizedec;
00158         }
00159         *size =sizecount;
00160         return true;
00161 }
00162 
00163 bool fatFile::Write(const Bit8u * data, Bit16u *size) {
00164         /* TODO: Check for read-only bit */
00165 
00166         if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
00167                 DOS_SetError(DOSERR_ACCESS_DENIED);
00168                 return false;
00169         }
00170 
00171         direntry tmpentry;
00172         Bit16u sizedec, sizecount;
00173         sizedec = *size;
00174         sizecount = 0;
00175 
00176         while(sizedec != 0) {
00177                 /* Increase filesize if necessary */
00178                 if(seekpos >= filelength) {
00179                         if(filelength == 0) {
00180                                 firstCluster = myDrive->getFirstFreeClust();
00181                                 myDrive->allocateCluster(firstCluster, 0);
00182                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00183                                 myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00184                                 loadedSector = true;
00185                         }
00186                         filelength = seekpos+1;
00187                         if (!loadedSector) {
00188                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00189                                 if(currentSector == 0) {
00190                                         /* EOC reached before EOF - try to increase file allocation */
00191                                         myDrive->appendCluster(firstCluster);
00192                                         /* Try getting sector again */
00193                                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00194                                         if(currentSector == 0) {
00195                                                 /* No can do. lets give up and go home.  We must be out of room */
00196                                                 goto finalizeWrite;
00197                                         }
00198                                 }
00199                                 curSectOff = 0;
00200                                 myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00201 
00202                                 loadedSector = true;
00203                         }
00204                 }
00205                 sectorBuffer[curSectOff++] = data[sizecount++];
00206                 seekpos++;
00207                 if(curSectOff >= myDrive->getSectorSize()) {
00208                         if(loadedSector) myDrive->Write_AbsoluteSector(currentSector, sectorBuffer);
00209 
00210                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00211                         if(currentSector == 0) {
00212                                 /* EOC reached before EOF - try to increase file allocation */
00213                                 myDrive->appendCluster(firstCluster);
00214                                 /* Try getting sector again */
00215                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00216                                 if(currentSector == 0) {
00217                                         /* No can do. lets give up and go home.  We must be out of room */
00218                                         loadedSector = false;
00219                                         goto finalizeWrite;
00220                                 }
00221                         }
00222                         curSectOff = 0;
00223                         myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00224 
00225                         loadedSector = true;
00226                 }
00227                 --sizedec;
00228         }
00229         if(curSectOff>0 && loadedSector) myDrive->Write_AbsoluteSector(currentSector, sectorBuffer);
00230 
00231 finalizeWrite:
00232         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00233         tmpentry.entrysize = filelength;
00234         tmpentry.loFirstClust = (Bit16u)firstCluster;
00235         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00236 
00237         *size =sizecount;
00238         return true;
00239 }
00240 
00241 bool fatFile::Seek(Bit32u *pos, Bit32u type) {
00242         Bit32s seekto=0;
00243         
00244         switch(type) {
00245                 case DOS_SEEK_SET:
00246                         seekto = (Bit32s)*pos;
00247                         break;
00248                 case DOS_SEEK_CUR:
00249                         /* Is this relative seek signed? */
00250                         seekto = (Bit32s)*pos + (Bit32s)seekpos;
00251                         break;
00252                 case DOS_SEEK_END:
00253                         seekto = (Bit32s)filelength + (Bit32s)*pos;
00254                         break;
00255         }
00256 //      LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
00257 
00258         if((Bit32u)seekto > filelength) seekto = (Bit32s)filelength;
00259         if(seekto<0) seekto = 0;
00260         seekpos = (Bit32u)seekto;
00261         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00262         if (currentSector == 0) {
00263                 /* not within file size, thus no sector is available */
00264                 loadedSector = false;
00265         } else {
00266                 curSectOff = seekpos % myDrive->getSectorSize();
00267                 myDrive->Read_AbsoluteSector(currentSector, sectorBuffer);
00268         }
00269         *pos = seekpos;
00270         return true;
00271 }
00272 
00273 bool fatFile::Close() {
00274         /* Flush buffer */
00275         if (loadedSector) myDrive->Write_AbsoluteSector(currentSector, sectorBuffer);
00276 
00277         return false;
00278 }
00279 
00280 Bit16u fatFile::GetInformation(void) {
00281         return 0;
00282 }
00283 
00284 bool fatFile::UpdateDateTimeFromHost(void) {
00285         return true;
00286 }
00287 
00288 Bit32u fatFile::GetSeekPos() {
00289         return seekpos;
00290 }
00291 
00292 Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
00293         return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
00294 }
00295 
00296 Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
00297         Bit32u fatoffset=0;
00298         Bit32u fatsectnum;
00299         Bit32u fatentoff;
00300         Bit32u clustValue=0;
00301 
00302         switch(fattype) {
00303                 case FAT12:
00304                         fatoffset = clustNum + (clustNum / 2);
00305                         break;
00306                 case FAT16:
00307                         fatoffset = clustNum * 2;
00308                         break;
00309                 case FAT32:
00310                         fatoffset = clustNum * 4;
00311                         break;
00312         }
00313         fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
00314         fatentoff = fatoffset % bootbuffer.bytespersector;
00315 
00316     assert((bootbuffer.bytespersector * 2) <= sizeof(fatSectBuffer));
00317 
00318         if(curFatSect != fatsectnum) {
00319                 /* Load two sectors at once for FAT12 */
00320                 Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]);
00321                 if (fattype==FAT12)
00322                         Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[bootbuffer.bytespersector]);
00323                 curFatSect = fatsectnum;
00324         }
00325 
00326         switch(fattype) {
00327                 case FAT12:
00328                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00329                         if(clustNum & 0x1) {
00330                                 clustValue >>= 4;
00331                         } else {
00332                                 clustValue &= 0xfff;
00333                         }
00334                         break;
00335                 case FAT16:
00336                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00337                         break;
00338                 case FAT32:
00339                         clustValue = *((Bit32u *)&fatSectBuffer[fatentoff]);
00340                         break;
00341         }
00342 
00343         return clustValue;
00344 }
00345 
00346 void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
00347         Bit32u fatoffset=0;
00348         Bit32u fatsectnum;
00349         Bit32u fatentoff;
00350 
00351         switch(fattype) {
00352                 case FAT12:
00353                         fatoffset = clustNum + (clustNum / 2);
00354                         break;
00355                 case FAT16:
00356                         fatoffset = clustNum * 2;
00357                         break;
00358                 case FAT32:
00359                         fatoffset = clustNum * 4;
00360                         break;
00361         }
00362         fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
00363         fatentoff = fatoffset % bootbuffer.bytespersector;
00364 
00365     assert((bootbuffer.bytespersector * 2) <= sizeof(fatSectBuffer));
00366 
00367         if(curFatSect != fatsectnum) {
00368                 /* Load two sectors at once for FAT12 */
00369                 Read_AbsoluteSector(fatsectnum, &fatSectBuffer[0]);
00370                 if (fattype==FAT12)
00371                         Read_AbsoluteSector(fatsectnum+1, &fatSectBuffer[bootbuffer.bytespersector]);
00372                 curFatSect = fatsectnum;
00373         }
00374 
00375         switch(fattype) {
00376                 case FAT12: {
00377                         Bit16u tmpValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00378                         if(clustNum & 0x1) {
00379                                 clustValue &= 0xfff;
00380                                 clustValue <<= 4;
00381                                 tmpValue &= 0xf;
00382                                 tmpValue |= (Bit16u)clustValue;
00383 
00384                         } else {
00385                                 clustValue &= 0xfff;
00386                                 tmpValue &= 0xf000;
00387                                 tmpValue |= (Bit16u)clustValue;
00388                         }
00389                         *((Bit16u *)&fatSectBuffer[fatentoff]) = tmpValue;
00390                         break;
00391                         }
00392                 case FAT16:
00393                         *((Bit16u *)&fatSectBuffer[fatentoff]) = (Bit16u)clustValue;
00394                         break;
00395                 case FAT32:
00396                         *((Bit32u *)&fatSectBuffer[fatentoff]) = clustValue;
00397                         break;
00398         }
00399         for(unsigned int fc=0;fc<bootbuffer.fatcopies;fc++) {
00400                 Write_AbsoluteSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
00401                 if (fattype==FAT12) {
00402                         if (fatentoff >= (bootbuffer.bytespersector-1U))
00403                                 Write_AbsoluteSector(fatsectnum+1u+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[bootbuffer.bytespersector]);
00404                 }
00405         }
00406 }
00407 
00408 bool fatDrive::getEntryName(const char *fullname, char *entname) {
00409         char dirtoken[DOS_PATHLENGTH];
00410 
00411         char * findDir;
00412         char * findFile;
00413         strcpy(dirtoken,fullname);
00414 
00415         //LOG_MSG("Testing for filename %s", fullname);
00416         findDir = strtok(dirtoken,"\\");
00417         if (findDir==NULL) {
00418                 return true;    // root always exists
00419         }
00420         findFile = findDir;
00421         while(findDir != NULL) {
00422                 findFile = findDir;
00423                 findDir = strtok(NULL,"\\");
00424         }
00425         strcpy(entname, findFile);
00426         return true;
00427 }
00428 
00429 bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) {
00430         size_t len = strlen(filename);
00431         char dirtoken[DOS_PATHLENGTH];
00432         Bit32u currentClust = 0;
00433 
00434         direntry foundEntry;
00435         char * findDir;
00436         char * findFile;
00437         strcpy(dirtoken,filename);
00438         findFile=dirtoken;
00439 
00440         /* Skip if testing in root directory */
00441         if ((len>0) && (filename[len-1]!='\\')) {
00442                 //LOG_MSG("Testing for filename %s", filename);
00443                 findDir = strtok(dirtoken,"\\");
00444                 findFile = findDir;
00445                 while(findDir != NULL) {
00446                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00447                         imgDTA->SetDirID(0);
00448                         
00449                         findFile = findDir;
00450                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
00451                         else {
00452                                 //Found something. See if it's a directory (findfirst always finds regular files)
00453                                 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00454                                 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
00455                                 if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
00456                         }
00457 
00458                         currentClust = foundEntry.loFirstClust;
00459                         findDir = strtok(NULL,"\\");
00460                 }
00461         } else {
00462                 /* Set to root directory */
00463         }
00464 
00465         /* Search found directory for our file */
00466         imgDTA->SetupSearch(0,0x7,findFile);
00467         imgDTA->SetDirID(0);
00468         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false;
00469 
00470         memcpy(useEntry, &foundEntry, sizeof(direntry));
00471         *dirClust = (Bit32u)currentClust;
00472         *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
00473         return true;
00474 }
00475 
00476 bool fatDrive::getDirClustNum(const char *dir, Bit32u *clustNum, bool parDir) {
00477         Bit32u len = (Bit32u)strlen(dir);
00478         char dirtoken[DOS_PATHLENGTH];
00479         Bit32u currentClust = 0;
00480         direntry foundEntry;
00481         char * findDir;
00482         strcpy(dirtoken,dir);
00483 
00484         /* Skip if testing for root directory */
00485         if ((len>0) && (dir[len-1]!='\\')) {
00486                 //LOG_MSG("Testing for dir %s", dir);
00487                 findDir = strtok(dirtoken,"\\");
00488                 while(findDir != NULL) {
00489                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00490                         imgDTA->SetDirID(0);
00491                         findDir = strtok(NULL,"\\");
00492                         if(parDir && (findDir == NULL)) break;
00493 
00494                         char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00495                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
00496                                 return false;
00497                         } else {
00498                                 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
00499                                 if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
00500                         }
00501                         currentClust = foundEntry.loFirstClust;
00502 
00503                 }
00504                 *clustNum = currentClust;
00505         } else {
00506                 /* Set to root directory */
00507                 *clustNum = 0;
00508         }
00509         return true;
00510 }
00511 
00512 Bit32u fatDrive::getSectorSize(void) {
00513         return bootbuffer.bytespersector;
00514 }
00515 
00516 Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
00517         return  getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
00518 }
00519 
00520 Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
00521         Bit32s skipClust = (Bit32s)(logicalSector / bootbuffer.sectorspercluster);
00522         Bit32u sectClust = (Bit32u)(logicalSector % bootbuffer.sectorspercluster);
00523 
00524         Bit32u currentClust = startClustNum;
00525         Bit32u testvalue;
00526 
00527         while(skipClust!=0) {
00528                 bool isEOF = false;
00529                 testvalue = getClusterValue(currentClust);
00530                 switch(fattype) {
00531                         case FAT12:
00532                                 if(testvalue >= 0xff8) isEOF = true;
00533                                 break;
00534                         case FAT16:
00535                                 if(testvalue >= 0xfff8) isEOF = true;
00536                                 break;
00537                         case FAT32:
00538                                 if(testvalue >= 0xfffffff8) isEOF = true;
00539                                 break;
00540                 }
00541                 if((isEOF) && (skipClust>=1)) {
00542                         //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
00543                         if (skipClust == 1 && fattype == FAT12) {
00544                                 //break;
00545                                 LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain reached, but maybe good afterall ?");
00546                         }
00547                         return 0;
00548                 }
00549                 currentClust = testvalue;
00550                 --skipClust;
00551         }
00552 
00553         return (getClustFirstSect(currentClust) + sectClust);
00554 }
00555 
00556 void fatDrive::deleteClustChain(Bit32u startCluster) {
00557         Bit32u testvalue;
00558         Bit32u currentClust = startCluster;
00559         bool isEOF = false;
00560         while(!isEOF) {
00561                 testvalue = getClusterValue(currentClust);
00562                 if(testvalue == 0) {
00563                         /* What the crap?  Cluster is already empty - BAIL! */
00564                         break;
00565                 }
00566                 /* Mark cluster as empty */
00567                 setClusterValue(currentClust, 0);
00568                 switch(fattype) {
00569                         case FAT12:
00570                                 if(testvalue >= 0xff8) isEOF = true;
00571                                 break;
00572                         case FAT16:
00573                                 if(testvalue >= 0xfff8) isEOF = true;
00574                                 break;
00575                         case FAT32:
00576                                 if(testvalue >= 0xfffffff8) isEOF = true;
00577                                 break;
00578                 }
00579                 if(isEOF) break;
00580                 currentClust = testvalue;
00581         }
00582 }
00583 
00584 Bit32u fatDrive::appendCluster(Bit32u startCluster) {
00585         Bit32u testvalue;
00586         Bit32u currentClust = startCluster;
00587         bool isEOF = false;
00588         
00589         while(!isEOF) {
00590                 testvalue = getClusterValue(currentClust);
00591                 switch(fattype) {
00592                         case FAT12:
00593                                 if(testvalue >= 0xff8) isEOF = true;
00594                                 break;
00595                         case FAT16:
00596                                 if(testvalue >= 0xfff8) isEOF = true;
00597                                 break;
00598                         case FAT32:
00599                                 if(testvalue >= 0xfffffff8) isEOF = true;
00600                                 break;
00601                 }
00602                 if(isEOF) break;
00603                 currentClust = testvalue;
00604         }
00605 
00606         Bit32u newClust = getFirstFreeClust();
00607         /* Drive is full */
00608         if(newClust == 0) return 0;
00609 
00610         if(!allocateCluster(newClust, currentClust)) return 0;
00611 
00612         zeroOutCluster(newClust);
00613 
00614         return newClust;
00615 }
00616 
00617 bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
00618 
00619         /* Can't allocate cluster #0 */
00620         if(useCluster == 0) return false;
00621 
00622         if(prevCluster != 0) {
00623                 /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
00624                 if(!getClusterValue(prevCluster)) return false;
00625 
00626                 /* Point cluster to new cluster in chain */
00627                 setClusterValue(prevCluster, useCluster);
00628                 //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
00629         } 
00630 
00631         switch(fattype) {
00632                 case FAT12:
00633                         setClusterValue(useCluster, 0xfff);
00634                         break;
00635                 case FAT16:
00636                         setClusterValue(useCluster, 0xffff);
00637                         break;
00638                 case FAT32:
00639                         setClusterValue(useCluster, 0xffffffff);
00640                         break;
00641         }
00642         return true;
00643 }
00644 
00645 fatDrive::~fatDrive() {
00646         if (loadedDisk) {
00647                 loadedDisk->Release();
00648                 loadedDisk = NULL;
00649         }
00650 }
00651 
00652 /* PC-98 IPL1 partition table entry.
00653  * Taken from GNU Parted source code.
00654  * Maximum 16 entries. */
00655 #pragma pack(push,1)
00656 struct _PC98RawPartition {
00657         uint8_t         mid;            /* 0x80 - boot */
00658         uint8_t         sid;            /* 0x80 - active */
00659         uint8_t         dum1;           /* dummy for padding */
00660         uint8_t         dum2;           /* dummy for padding */
00661         uint8_t         ipl_sect;       /* IPL sector */
00662         uint8_t         ipl_head;       /* IPL head */
00663         uint16_t        ipl_cyl;        /* IPL cylinder */
00664         uint8_t         sector;         /* starting sector */
00665         uint8_t         head;           /* starting head */
00666         uint16_t        cyl;            /* starting cylinder */
00667         uint8_t         end_sector;     /* end sector */
00668         uint8_t         end_head;       /* end head */
00669         uint16_t        end_cyl;        /* end cylinder */
00670         char            name[16];
00671 };
00672 #pragma pack(pop)
00673 
00674 fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders) : loadedDisk(NULL) {
00675         created_successfully = true;
00676         FILE *diskfile;
00677         Bit32u filesize;
00678         
00679         if(imgDTASeg == 0) {
00680                 imgDTASeg = DOS_GetMemory(2,"imgDTASeg");
00681                 imgDTAPtr = RealMake(imgDTASeg, 0);
00682                 imgDTA    = new DOS_DTA(imgDTAPtr);
00683         }
00684 
00685         diskfile = fopen64(sysFilename, "rb+");
00686         if(!diskfile) {created_successfully = false;return;}
00687 
00688     // all disk I/O is in sector-sized blocks.
00689     // modern OSes have good caching.
00690     // there are plenty of cases where this code aborts, exits, or re-execs itself (such as reboot)
00691     // where stdio buffering can cause loss of data.
00692     setbuf(diskfile,NULL);
00693 
00694         QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(diskfile);
00695 
00696         if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)){
00697                 Bit32u cluster_size = 1u << qcow2_header.cluster_bits;
00698                 if ((bytesector < 512) || ((cluster_size % bytesector) != 0)){
00699                         created_successfully = false;
00700                         return;
00701                 }
00702                 filesize = (Bit32u)(qcow2_header.size / 1024L);
00703                 loadedDisk = new QCow2Disk(qcow2_header, diskfile, (Bit8u *)sysFilename, filesize, bytesector, (filesize > 2880));
00704         }
00705         else{
00706                 fseeko64(diskfile, 0L, SEEK_SET);
00707         assert(sizeof(bootbuffer.bootcode) >= 256);
00708         fread(bootbuffer.bootcode,256,1,diskfile); // look for magic signatures
00709 
00710         const char *ext = strrchr(sysFilename,'.');
00711 
00712         if (ext != NULL && !strcasecmp(ext, ".d88")) {
00713             fseeko64(diskfile, 0L, SEEK_END);
00714             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00715             loadedDisk = new imageDiskD88(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00716         }
00717         else if (!memcmp(bootbuffer.bootcode,"VFD1.",5)) { /* FDD files */
00718             fseeko64(diskfile, 0L, SEEK_END);
00719             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00720             loadedDisk = new imageDiskVFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00721         }
00722         else {
00723             fseeko64(diskfile, 0L, SEEK_END);
00724             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00725             loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00726         }
00727         }
00728 
00729     fatDriveInit(sysFilename, bytesector, cylsector, headscyl, cylinders, filesize);
00730 }
00731 
00732 fatDrive::fatDrive(imageDisk *sourceLoadedDisk) : loadedDisk(NULL) {
00733         if (sourceLoadedDisk == 0) {
00734                 created_successfully = false;
00735                 return;
00736         }
00737         created_successfully = true;
00738         
00739         if(imgDTASeg == 0) {
00740                 imgDTASeg = DOS_GetMemory(2,"imgDTASeg");
00741                 imgDTAPtr = RealMake(imgDTASeg, 0);
00742                 imgDTA    = new DOS_DTA(imgDTAPtr);
00743         }
00744 
00745     loadedDisk = sourceLoadedDisk;
00746 
00747     fatDriveInit("", loadedDisk->sector_size, loadedDisk->sectors, loadedDisk->heads, loadedDisk->cylinders, loadedDisk->diskSizeK);
00748 }
00749 
00750 Bit8u fatDrive::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00751     if (loadedDisk != NULL) {
00752         /* this will only work if the logical sector size is larger than the disk sector size */
00753         const unsigned int lsz = loadedDisk->getSectSize();
00754         unsigned int c = sector_size / lsz;
00755 
00756         if (c != 0 && (sector_size % lsz) == 0) {
00757             Bit32u ssect = sectnum * c;
00758 
00759             while (c-- != 0) {
00760                 if (loadedDisk->Read_AbsoluteSector(ssect++,data) != 0)
00761                     return 0x05;
00762 
00763                 data = (void*)((char*)data + lsz);
00764             }
00765 
00766             return 0;
00767         }
00768     }
00769 
00770     return 0x05;
00771 }
00772 
00773 Bit8u fatDrive::Write_AbsoluteSector(Bit32u sectnum, void * data) {
00774     if (loadedDisk != NULL) {
00775         /* this will only work if the logical sector size is larger than the disk sector size */
00776         const unsigned int lsz = loadedDisk->getSectSize();
00777         unsigned int c = sector_size / lsz;
00778 
00779         if (c != 0 && (sector_size % lsz) == 0) {
00780             Bit32u ssect = sectnum * c;
00781 
00782             while (c-- != 0) {
00783                 if (loadedDisk->Write_AbsoluteSector(ssect++,data) != 0)
00784                     return 0x05;
00785 
00786                 data = (void*)((char*)data + lsz);
00787             }
00788 
00789             return 0;
00790         }
00791     }
00792 
00793     return 0x05;
00794 }
00795 
00796 Bit32u fatDrive::getSectSize(void) {
00797     return sector_size;
00798 }
00799 
00800 void fatDrive::fatDriveInit(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit64u filesize) {
00801         Bit32u startSector;
00802         bool pc98_512_to_1024_allow = false;
00803         struct partTable mbrData;
00804 
00805         if(!loadedDisk) {
00806                 created_successfully = false;
00807                 return;
00808         }
00809 
00810         loadedDisk->Addref();
00811 
00812     if (loadedDisk->getSectSize() > sizeof(bootbuffer)) {
00813         LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize());
00814                 created_successfully = false;
00815         return;
00816     }
00817 
00818         if(filesize > 2880) {
00819                 /* Set user specified harddrive parameters */
00820         if (headscyl > 0 && cylinders > 0 && cylsector > 0 && bytesector > 0)
00821                 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
00822 
00823         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
00824             created_successfully = false;
00825             LOG_MSG("No geometry");
00826             return;
00827         }
00828 
00829                 loadedDisk->Read_Sector(0,0,1,&mbrData);
00830 
00831                 if(mbrData.magic1!= 0x55 ||     mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
00832 
00833         startSector = 63;
00834 
00835         /* PC-98 bootloader support.
00836          * These can be identified by the "IPL1" in the boot sector.
00837          * These boot sectors do not have a valid partition table though the code below might
00838          * pick up a false partition #3 with a zero offset. Partition table is in sector 1 */
00839         if (!memcmp(mbrData.booter+4,"IPL1",4)) {
00840             unsigned char ipltable[SECTOR_SIZE_MAX];
00841             unsigned int max_entries = (std::min)(16UL,(unsigned long)(loadedDisk->getSectSize() / sizeof(_PC98RawPartition)));
00842             unsigned int i;
00843 
00844             LOG_MSG("PC-98 IPL1 signature detected");
00845 
00846             assert(sizeof(_PC98RawPartition) == 32);
00847 
00848             memset(ipltable,0,sizeof(ipltable));
00849             loadedDisk->Read_Sector(0,0,2,ipltable);
00850 
00851             for (i=0;i < max_entries;i++) {
00852                 _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
00853 
00854                 if (pe->mid == 0 && pe->sid == 0 &&
00855                     pe->ipl_sect == 0 && pe->ipl_head == 0 && pe->ipl_cyl == 0 &&
00856                     pe->sector == 0 && pe->head == 0 && pe->cyl == 0 &&
00857                     pe->end_sector == 0 && pe->end_head == 0 && pe->end_cyl == 0)
00858                     continue; /* unused */
00859 
00860                 /* We're looking for MS-DOS partitions.
00861                  * I've heard that some other OSes were once ported to PC-98, including Windows NT and OS/2,
00862                  * so I would rather not mistake NTFS or HPFS as FAT and cause damage. --J.C.
00863                  * FIXME: Is there a better way? */
00864                 if (!strncasecmp(pe->name,"MS-DOS",6) ||
00865                     !strncasecmp(pe->name,"MSDOS",5) ||
00866                     !strncasecmp(pe->name,"Windows",7)) {
00867                     /* unfortunately start and end are in C/H/S geometry, so we have to translate.
00868                      * this is why it matters so much to read the geometry from the HDI header.
00869                      *
00870                      * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
00871                     startSector =
00872                         (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
00873                         (pe->head * loadedDisk->sectors) +
00874                          pe->sector;
00875 
00876                     /* Many HDI images I've encountered so far indicate 512 bytes/sector,
00877                      * but then the FAT filesystem itself indicates 1024 bytes per sector. */
00878                     pc98_512_to_1024_allow = true;
00879 
00880                     {
00881                         /* FIXME: What if the label contains SHIFT-JIS? */
00882                         std::string name = std::string(pe->name,sizeof(pe->name));
00883 
00884                         LOG_MSG("Using IPL1 entry %u name '%s' which starts at sector %lu",
00885                             i,name.c_str(),(unsigned long)startSector);
00886                         break;
00887                     }
00888                 }
00889             }
00890 
00891             if (i == max_entries)
00892                 LOG_MSG("No partitions found in the IPL1 table");
00893         }
00894         else {
00895             /* IBM PC master boot record search */
00896             int m;
00897             for(m=0;m<4;m++) {
00898                 /* Pick the first available partition */
00899                 if(mbrData.pentry[m].partSize != 0x00 &&
00900                         (mbrData.pentry[m].parttype == 0x01 || mbrData.pentry[m].parttype == 0x04 ||
00901                          mbrData.pentry[m].parttype == 0x06 || mbrData.pentry[m].parttype == 0x0B ||
00902                          mbrData.pentry[m].parttype == 0x0C || mbrData.pentry[m].parttype == 0x0D ||
00903                          mbrData.pentry[m].parttype == 0x0E || mbrData.pentry[m].parttype == 0x0F)) {
00904                     LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
00905                     startSector = mbrData.pentry[m].absSectStart;
00906                     break;
00907                 }
00908             }
00909 
00910             if(m==4) LOG_MSG("No good partiton found in image.");
00911         }
00912 
00913                 partSectOff = startSector;
00914         } else {
00915                 /* Floppy disks don't have partitions */
00916                 partSectOff = 0;
00917 
00918         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
00919             created_successfully = false;
00920             LOG_MSG("No geometry");
00921             return;
00922         }
00923         }
00924 
00925         loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
00926 
00927         /* Check for DOS 1.x format floppy */
00928         if ((bootbuffer.mediadescriptor & 0xf0) != 0xf0 && filesize <= 360 && loadedDisk->getSectSize() == 512) {
00929 
00930                 Bit8u sectorBuffer[512];
00931                 loadedDisk->Read_AbsoluteSector(1,&sectorBuffer);
00932                 Bit8u mdesc = sectorBuffer[0];
00933 
00934                 /* Allowed if media descriptor in FAT matches image size  */
00935                 if ((mdesc == 0xfc && filesize == 180) ||
00936                         (mdesc == 0xfd && filesize == 360) ||
00937                         (mdesc == 0xfe && filesize == 160) ||
00938                         (mdesc == 0xff && filesize == 320)) {
00939 
00940                         /* Create parameters for a 160kB floppy */
00941                         bootbuffer.bytespersector = 512;
00942                         bootbuffer.sectorspercluster = 1;
00943                         bootbuffer.reservedsectors = 1;
00944                         bootbuffer.fatcopies = 2;
00945                         bootbuffer.rootdirentries = 64;
00946                         bootbuffer.totalsectorcount = 320;
00947                         bootbuffer.mediadescriptor = mdesc;
00948                         bootbuffer.sectorsperfat = 1;
00949                         bootbuffer.sectorspertrack = 8;
00950                         bootbuffer.headcount = 1;
00951                         bootbuffer.magic1 = 0x55;       // to silence warning
00952                         bootbuffer.magic2 = 0xaa;
00953                         if (!(mdesc & 0x2)) {
00954                                 /* Adjust for 9 sectors per track */
00955                                 bootbuffer.totalsectorcount = 360;
00956                                 bootbuffer.sectorsperfat = 2;
00957                                 bootbuffer.sectorspertrack = 9;
00958                         }
00959                         if (mdesc & 0x1) {
00960                                 /* Adjust for 2 sides */
00961                                 bootbuffer.sectorspercluster = 2;
00962                                 bootbuffer.rootdirentries = 112;
00963                                 bootbuffer.totalsectorcount *= 2;
00964                                 bootbuffer.headcount = 2;
00965                         }
00966                 }
00967         }
00968 
00969     LOG_MSG("FAT: BPB says %u sectors/track %u heads %u bytes/sector",
00970         bootbuffer.sectorspertrack,
00971         bootbuffer.headcount,
00972         bootbuffer.bytespersector);
00973 
00974     /* NTS: Some HDI images of PC-98 games do in fact have headcount == 0 */
00975     /* a clue that we're not really looking at FAT is invalid or weird values in the boot sector */
00976     if (bootbuffer.sectorspertrack == 0 || (bootbuffer.sectorspertrack > ((filesize <= 3000) ? 40 : 255)) ||
00977         (bootbuffer.headcount > ((filesize <= 3000) ? 64 : 255))) {
00978         LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem");
00979                 created_successfully = false;
00980         return;
00981     }
00982 
00983     /* work at this point in logical sectors */
00984         sector_size = loadedDisk->getSectSize();
00985 
00986     /* Many HDI images indicate a disk format of 256 or 512 bytes per sector combined with a FAT filesystem
00987      * that indicates 1024 bytes per sector. */
00988     if (pc98_512_to_1024_allow &&
00989          bootbuffer.bytespersector != getSectSize() &&
00990         (bootbuffer.bytespersector == 1024 || bootbuffer.bytespersector == 512) &&
00991         (getSectSize() == 512 || getSectSize() == 256)) {
00992         unsigned int ratio = (unsigned int)(bootbuffer.bytespersector / getSectSize());
00993         unsigned int ratiof = (unsigned int)(bootbuffer.bytespersector % getSectSize());
00994         unsigned int ratioshift = (ratio == 4) ? 2 : 1;
00995 
00996         LOG_MSG("Disk indicates %u bytes/sector, FAT filesystem indicates %u bytes/sector. Ratio=%u shift=%u",
00997             getSectSize(),bootbuffer.bytespersector,ratio,ratioshift);
00998         assert(ratiof == 0);
00999         assert((ratio & (ratio - 1)) == 0); /* power of 2 */
01000         assert(ratio >= 2);
01001         assert(ratio <= 4);
01002 
01003         /* we can hack things in place IF the starting sector is an even number */
01004         if ((partSectOff & (ratio - 1)) == 0) {
01005             partSectOff >>= ratioshift;
01006             startSector >>= ratioshift;
01007             sector_size = bootbuffer.bytespersector;
01008             LOG_MSG("Using logical sector size %u",sector_size);
01009         }
01010         else {
01011             LOG_MSG("However there's nothing I can do, because the partition starts on an odd sector");
01012         }
01013     }
01014 
01015     /* NTS: PC-98 floppies (the 1024 byte/sector format) do not have magic bytes */
01016     if (getSectSize() == 512) {
01017         if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
01018             /* Not a FAT filesystem */
01019             LOG_MSG("Loaded image has no valid magicnumbers at the end!");
01020             created_successfully = false;
01021             return;
01022         }
01023     }
01024 
01025         if(!bootbuffer.sectorsperfat) {
01026                 /* FAT32 not implemented yet */
01027                 LOG_MSG("FAT32 not implemented yet, mounting image only");
01028                 fattype = FAT32;        // Avoid parsing dir entries, see fatDrive::FindFirst()...should work for unformatted images as well
01029                 created_successfully = false;
01030                 return;
01031         }
01032 
01033     /* too much of this code assumes 512 bytes per sector or more.
01034      * MS-DOS itself as I understand it relies on bytes per sector being a power of 2.
01035      * this is to protect against errant FAT structures and to help prep this code
01036      * later to work with the 1024 bytes/sector used by PC-98 floppy formats.
01037      * When done, this code should be able to then handle the FDI/FDD images
01038      * PC-98 games are normally distributed in on the internet.
01039      *
01040      * The value "128" comes from the smallest sector size possible on the floppy
01041      * controller of MS-DOS based systems. */
01042     /* NTS: Power of 2 test: A number is a power of 2 if (x & (x - 1)) == 0
01043      *
01044      * 15        15 & 14       01111 AND 01110     RESULT: 01110 (15)
01045      * 16        16 & 15       10000 AND 01111     RESULT: 00000 (0)
01046      * 17        17 & 16       10001 AND 10000     RESULT: 10000 (16) */
01047     if (bootbuffer.bytespersector < 128 || bootbuffer.bytespersector > sizeof(bootbuffer) ||
01048         (bootbuffer.bytespersector & (bootbuffer.bytespersector - 1)) != 0/*not a power of 2*/) {
01049         LOG_MSG("FAT bytes/sector value %u not supported",bootbuffer.bytespersector);
01050                 created_successfully = false;
01051         return;
01052     }
01053 
01054     /* another fault of this code is that it assumes the sector size of the medium matches
01055      * the bytespersector value of the MS-DOS filesystem. if they don't match, problems
01056      * will result. */
01057     if (bootbuffer.bytespersector != getSectSize()) {
01058         LOG_MSG("FAT bytes/sector %u does not match disk image bytes/sector %u",
01059             (unsigned int)bootbuffer.bytespersector,
01060             (unsigned int)getSectSize());
01061                 created_successfully = false;
01062         return;
01063     }
01064 
01065         /* Determine FAT format, 12, 16 or 32 */
01066 
01067         /* Get size of root dir in sectors */
01068         /* TODO: Get 32-bit total sector count if needed */
01069         Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32u) + (bootbuffer.bytespersector - 1u)) / bootbuffer.bytespersector;
01070         Bit32u DataSectors;
01071         if(bootbuffer.totalsectorcount != 0) {
01072                 DataSectors = (Bitu)bootbuffer.totalsectorcount - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01073         } else {
01074                 DataSectors = (Bitu)bootbuffer.totalsecdword - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01075 
01076         }
01077         CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
01078 
01079         firstDataSector = ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
01080         firstRootDirSect = (Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)partSectOff;
01081 
01082         if(CountOfClusters < 4085) {
01083                 /* Volume is FAT12 */
01084                 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
01085                 fattype = FAT12;
01086         } else if (CountOfClusters < 65525) {
01087                 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
01088                 fattype = FAT16;
01089         } else {
01090                 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
01091                 fattype = FAT32;
01092         }
01093 
01094         /* There is no cluster 0, this means we are in the root directory */
01095         cwdDirCluster = 0;
01096 
01097         memset(fatSectBuffer,0,1024);
01098         curFatSect = 0xffffffff;
01099 
01100         strcpy(info, "fatDrive ");
01101         strcat(info, sysFilename);
01102 }
01103 
01104 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
01105         Bit32u countFree = 0;
01106         Bit32u i;
01107         
01108         *_bytes_sector = (Bit16u)getSectSize();
01109         *_sectors_cluster = bootbuffer.sectorspercluster;
01110         if (CountOfClusters<65536) *_total_clusters = (Bit16u)CountOfClusters;
01111         else {
01112                 // maybe some special handling needed for fat32
01113                 *_total_clusters = 65535;
01114         }
01115         for(i=0;i<CountOfClusters;i++) if(!getClusterValue(i+2)) countFree++;
01116         if (countFree<65536) *_free_clusters = (Bit16u)countFree;
01117         else {
01118                 // maybe some special handling needed for fat32
01119                 *_free_clusters = 65535;
01120         }
01121         
01122         return true;
01123 }
01124 
01125 Bit32u fatDrive::getFirstFreeClust(void) {
01126         Bit32u i;
01127         for(i=0;i<CountOfClusters;i++) {
01128                 if(!getClusterValue(i+2)) return (i+2);
01129         }
01130 
01131         /* No free cluster found */
01132         return 0;
01133 }
01134 
01135 bool fatDrive::isRemote(void) { return false; }
01136 bool fatDrive::isRemovable(void) { return false; }
01137 
01138 Bits fatDrive::UnMount(void) {
01139         delete this;
01140         return 0;
01141 }
01142 
01143 Bit8u fatDrive::GetMediaByte(void) { return loadedDisk->GetBiosType(); }
01144 
01145 bool fatDrive::FileCreate(DOS_File **file, const char *name, Bit16u attributes) {
01146         direntry fileEntry;
01147         Bit32u dirClust, subEntry;
01148         char dirName[DOS_NAMELENGTH_ASCII];
01149         char pathName[11];
01150 
01151         Bit16u save_errorcode=dos.errorcode;
01152 
01153         /* Check if file already exists */
01154         if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01155                 /* Truncate file */
01156                 fileEntry.entrysize=0;
01157                 directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01158         } else {
01159                 /* Can we even get the name of the file itself? */
01160                 if(!getEntryName(name, &dirName[0])) return false;
01161                 convToDirFile(&dirName[0], &pathName[0]);
01162 
01163                 /* Can we find the base directory? */
01164                 if(!getDirClustNum(name, &dirClust, true)) return false;
01165                 memset(&fileEntry, 0, sizeof(direntry));
01166                 memcpy(&fileEntry.entryname, &pathName[0], 11);
01167                 fileEntry.attrib = (Bit8u)(attributes & 0xff);
01168                 addDirectoryEntry(dirClust, fileEntry);
01169 
01170                 /* Check if file exists now */
01171                 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01172         }
01173 
01174         /* Empty file created, now lets open it */
01175         /* TODO: check for read-only flag and requested write access */
01176         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01177         (*file)->flags=OPEN_READWRITE;
01178         ((fatFile *)(*file))->dirCluster = dirClust;
01179         ((fatFile *)(*file))->dirIndex = subEntry;
01180         /* Maybe modTime and date should be used ? (crt matches findnext) */
01181         ((fatFile *)(*file))->time = fileEntry.crtTime;
01182         ((fatFile *)(*file))->date = fileEntry.crtDate;
01183 
01184         dos.errorcode=save_errorcode;
01185         return true;
01186 }
01187 
01188 bool fatDrive::FileExists(const char *name) {
01189         direntry fileEntry;
01190         Bit32u dummy1, dummy2;
01191         if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
01192         return true;
01193 }
01194 
01195 bool fatDrive::FileOpen(DOS_File **file, const char *name, Bit32u flags) {
01196         direntry fileEntry;
01197         Bit32u dirClust, subEntry;
01198         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01199         /* TODO: check for read-only flag and requested write access */
01200         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01201         (*file)->flags = flags;
01202         ((fatFile *)(*file))->dirCluster = dirClust;
01203         ((fatFile *)(*file))->dirIndex = subEntry;
01204         /* Maybe modTime and date should be used ? (crt matches findnext) */
01205         ((fatFile *)(*file))->time = fileEntry.crtTime;
01206         ((fatFile *)(*file))->date = fileEntry.crtDate;
01207         return true;
01208 }
01209 
01210 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
01211         /* TODO: Stub */
01212         return false;
01213 }
01214 
01215 bool fatDrive::FileUnlink(const char * name) {
01216         direntry fileEntry;
01217         Bit32u dirClust, subEntry;
01218 
01219         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01220 
01221         fileEntry.entryname[0] = 0xe5;
01222         directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01223 
01224         if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust);
01225 
01226         return true;
01227 }
01228 
01229 bool fatDrive::FindFirst(const char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
01230         direntry dummyClust;
01231         if(fattype==FAT32) return false;
01232 #if 0
01233         Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
01234         dta.GetSearchParams(attr,pattern);
01235         if(attr==DOS_ATTR_VOLUME) {
01236                 if (strcmp(GetLabel(), "") == 0 ) {
01237                         DOS_SetError(DOSERR_NO_MORE_FILES);
01238                         return false;
01239                 }
01240                 dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
01241                 return true;
01242         }
01243         if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst
01244                 LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!");
01245 #endif
01246         if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
01247                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
01248                 return false;
01249         }
01250         dta.SetDirID(0);
01251         dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
01252         return FindNextInternal(cwdDirCluster, dta, &dummyClust);
01253 }
01254 
01255 char* removeTrailingSpaces(char* str) {
01256         char* end = str + strlen(str) - 1;
01257         while (end >= str && *end == ' ') end--;
01258     /* NTS: The loop will exit with 'end' one char behind the last ' ' space character.
01259      *      So to ASCIIZ snip off the space, step forward one and overwrite with NUL.
01260      *      The loop may end with 'end' one char behind 'ptr' if the string was empty ""
01261      *      or nothing but spaces. This is OK because after the step forward, end >= str
01262      *      in all cases. */
01263         *(++end) = '\0';
01264         return str;
01265 }
01266 
01267 char* removeLeadingSpaces(char* str) {
01268         size_t len = strlen(str);
01269         size_t pos = strspn(str," ");
01270         memmove(str,str + pos,len - pos + 1);
01271         return str;
01272 }
01273 
01274 char* trimString(char* str) {
01275         return removeTrailingSpaces(removeLeadingSpaces(str));
01276 }
01277 
01278 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
01279         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01280         Bit32u logentsector; /* Logical entry sector */
01281         Bit32u entryoffset;  /* Index offset within sector */
01282         Bit32u tmpsector;
01283         Bit8u attrs;
01284         Bit16u dirPos;
01285         char srch_pattern[DOS_NAMELENGTH_ASCII];
01286         char find_name[DOS_NAMELENGTH_ASCII];
01287         char extension[4];
01288 
01289     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01290     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01291     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01292 
01293         dta.GetSearchParams(attrs, srch_pattern);
01294         dirPos = dta.GetDirID();
01295 
01296 nextfile:
01297         logentsector = (Bit32u)((size_t)dirPos / dirent_per_sector);
01298         entryoffset = (Bit32u)((size_t)dirPos % dirent_per_sector);
01299 
01300         if(dirClustNumber==0) {
01301                 if(dirPos >= bootbuffer.rootdirentries) {
01302                         DOS_SetError(DOSERR_NO_MORE_FILES);
01303                         return false;
01304                 }
01305                 Read_AbsoluteSector(firstRootDirSect+logentsector,sectbuf);
01306         } else {
01307                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01308                 /* A zero sector number can't happen */
01309                 if(tmpsector == 0) {
01310                         DOS_SetError(DOSERR_NO_MORE_FILES);
01311                         return false;
01312                 }
01313                 Read_AbsoluteSector(tmpsector,sectbuf);
01314         }
01315         dirPos++;
01316         dta.SetDirID(dirPos);
01317 
01318         /* Deleted file entry */
01319         if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
01320 
01321         /* End of directory list */
01322         if (sectbuf[entryoffset].entryname[0] == 0x00) {
01323                 DOS_SetError(DOSERR_NO_MORE_FILES);
01324                 return false;
01325         }
01326         memset(find_name,0,DOS_NAMELENGTH_ASCII);
01327         memset(extension,0,4);
01328         memcpy(find_name,&sectbuf[entryoffset].entryname[0],8);
01329         memcpy(extension,&sectbuf[entryoffset].entryname[8],3);
01330         trimString(&find_name[0]);
01331         trimString(&extension[0]);
01332         
01333         //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY))
01334         if (extension[0]!=0) {
01335                 strcat(find_name, ".");
01336                 strcat(find_name, extension);
01337         }
01338 
01339         /* Compare attributes to search attributes */
01340 
01341         //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
01342         if (attrs == DOS_ATTR_VOLUME) {
01343                 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
01344                 DOS_Drive_Cache dirCache;
01345                 dirCache.SetLabel(find_name, false, true);
01346         } else {
01347                 if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile;
01348         }
01349 
01350 
01351         /* Compare name to search pattern */
01352         if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
01353 
01354         //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
01355 
01356         dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib);
01357 
01358         memcpy(foundEntry, &sectbuf[entryoffset], sizeof(direntry));
01359 
01360         return true;
01361 }
01362 
01363 bool fatDrive::FindNext(DOS_DTA &dta) {
01364         direntry dummyClust;
01365 
01366         return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
01367 }
01368 
01369 bool fatDrive::GetFileAttr(const char *name, Bit16u *attr) {
01370         direntry fileEntry;
01371         Bit32u dirClust, subEntry;
01372         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01373                 char dirName[DOS_NAMELENGTH_ASCII];
01374                 char pathName[11];
01375 
01376                 /* Can we even get the name of the directory itself? */
01377                 if(!getEntryName(name, &dirName[0])) return false;
01378                 convToDirFile(&dirName[0], &pathName[0]);
01379 
01380                 /* Get parent directory starting cluster */
01381                 if(!getDirClustNum(name, &dirClust, true)) return false;
01382 
01383                 /* Find directory entry in parent directory */
01384                 Bit32s fileidx = 2;
01385                 if (dirClust==0) fileidx = 0;   // root directory
01386                 Bit32s last_idx=0; 
01387                 while(directoryBrowse(dirClust, &fileEntry, fileidx, last_idx)) {
01388                         if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
01389                                 *attr=fileEntry.attrib;
01390                                 return true;
01391                         }
01392                         fileidx++;
01393                 }
01394                 return false;
01395         } else *attr=fileEntry.attrib;
01396         return true;
01397 }
01398 
01399 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
01400         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01401         Bit32u logentsector;    /* Logical entry sector */
01402         Bit32u entryoffset = 0; /* Index offset within sector */
01403         Bit32u tmpsector;
01404         Bit16u dirPos = 0;
01405 
01406     (void)start;//UNUSED
01407 
01408     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01409     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01410     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01411 
01412         while(entNum>=0) {
01413                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01414                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01415 
01416                 if(dirClustNumber==0) {
01417                         if(dirPos >= bootbuffer.rootdirentries) return false;
01418                         tmpsector = firstRootDirSect+logentsector;
01419                         Read_AbsoluteSector(tmpsector,sectbuf);
01420                 } else {
01421                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01422                         /* A zero sector number can't happen */
01423                         if(tmpsector == 0) return false;
01424                         Read_AbsoluteSector(tmpsector,sectbuf);
01425                 }
01426                 dirPos++;
01427 
01428 
01429                 /* End of directory list */
01430                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01431                 --entNum;
01432         }
01433 
01434         memcpy(useEntry, &sectbuf[entryoffset],sizeof(direntry));
01435         return true;
01436 }
01437 
01438 bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
01439         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01440         Bit32u logentsector;    /* Logical entry sector */
01441         Bit32u entryoffset = 0; /* Index offset within sector */
01442         Bit32u tmpsector = 0;
01443         Bit16u dirPos = 0;
01444         
01445     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01446     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01447     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01448 
01449         while(entNum>=0) {              
01450                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01451                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01452 
01453                 if(dirClustNumber==0) {
01454                         if(dirPos >= bootbuffer.rootdirentries) return false;
01455                         tmpsector = firstRootDirSect+logentsector;
01456                         Read_AbsoluteSector(tmpsector,sectbuf);
01457                 } else {
01458                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01459                         /* A zero sector number can't happen */
01460                         if(tmpsector == 0) return false;
01461                         Read_AbsoluteSector(tmpsector,sectbuf);
01462                 }
01463                 dirPos++;
01464 
01465 
01466                 /* End of directory list */
01467                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01468                 --entNum;
01469         }
01470         if(tmpsector != 0) {
01471         memcpy(&sectbuf[entryoffset], useEntry, sizeof(direntry));
01472                 Write_AbsoluteSector(tmpsector, sectbuf);
01473         return true;
01474         } else {
01475                 return false;
01476         }
01477 }
01478 
01479 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
01480         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01481         Bit32u logentsector; /* Logical entry sector */
01482         Bit32u entryoffset;  /* Index offset within sector */
01483         Bit32u tmpsector;
01484         Bit16u dirPos = 0;
01485         
01486     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01487     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01488     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01489 
01490         for(;;) {               
01491                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01492                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01493 
01494                 if(dirClustNumber==0) {
01495                         if(dirPos >= bootbuffer.rootdirentries) return false;
01496                         tmpsector = firstRootDirSect+logentsector;
01497                         Read_AbsoluteSector(tmpsector,sectbuf);
01498                 } else {
01499                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01500                         /* A zero sector number can't happen - we need to allocate more room for this directory*/
01501                         if(tmpsector == 0) {
01502                                 Bit32u newClust;
01503                                 newClust = appendCluster(dirClustNumber);
01504                                 if(newClust == 0) return false;
01505                                 /* Try again to get tmpsector */
01506                                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01507                                 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
01508                         }
01509                         Read_AbsoluteSector(tmpsector,sectbuf);
01510                 }
01511                 dirPos++;
01512 
01513                 /* Deleted file entry or end of directory list */
01514                 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
01515                         sectbuf[entryoffset] = useEntry;
01516                         Write_AbsoluteSector(tmpsector,sectbuf);
01517                         break;
01518                 }
01519         }
01520 
01521         return true;
01522 }
01523 
01524 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
01525         Bit8u secBuffer[SECTOR_SIZE_MAX];
01526 
01527         memset(&secBuffer[0], 0, SECTOR_SIZE_MAX);
01528 
01529         unsigned int i;
01530         for(i=0;i<bootbuffer.sectorspercluster;i++) {
01531                 Write_AbsoluteSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
01532         }
01533 }
01534 
01535 bool fatDrive::MakeDir(const char *dir) {
01536         Bit32u dummyClust, dirClust;
01537         direntry tmpentry;
01538         char dirName[DOS_NAMELENGTH_ASCII];
01539         char pathName[11];
01540 
01541         /* Can we even get the name of the directory itself? */
01542         if(!getEntryName(dir, &dirName[0])) return false;
01543         convToDirFile(&dirName[0], &pathName[0]);
01544 
01545         /* Fail to make directory if already exists */
01546         if(getDirClustNum(dir, &dummyClust, false)) return false;
01547 
01548         dummyClust = getFirstFreeClust();
01549         /* No more space */
01550         if(dummyClust == 0) return false;
01551         
01552         if(!allocateCluster(dummyClust, 0)) return false;
01553 
01554         zeroOutCluster(dummyClust);
01555 
01556         /* Can we find the base directory? */
01557         if(!getDirClustNum(dir, &dirClust, true)) return false;
01558         
01559         /* Add the new directory to the base directory */
01560         memset(&tmpentry,0, sizeof(direntry));
01561         memcpy(&tmpentry.entryname, &pathName[0], 11);
01562         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01563         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01564         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01565         addDirectoryEntry(dirClust, tmpentry);
01566 
01567         /* Add the [.] and [..] entries to our new directory*/
01568         /* [.] entry */
01569         memset(&tmpentry,0, sizeof(direntry));
01570         memcpy(&tmpentry.entryname, ".          ", 11);
01571         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01572         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01573         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01574         addDirectoryEntry(dummyClust, tmpentry);
01575 
01576         /* [..] entry */
01577         memset(&tmpentry,0, sizeof(direntry));
01578         memcpy(&tmpentry.entryname, "..         ", 11);
01579         tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
01580         tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
01581         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01582         addDirectoryEntry(dummyClust, tmpentry);
01583 
01584         return true;
01585 }
01586 
01587 bool fatDrive::RemoveDir(const char *dir) {
01588         Bit32u dummyClust, dirClust;
01589         direntry tmpentry;
01590         char dirName[DOS_NAMELENGTH_ASCII];
01591         char pathName[11];
01592 
01593         /* Can we even get the name of the directory itself? */
01594         if(!getEntryName(dir, &dirName[0])) return false;
01595         convToDirFile(&dirName[0], &pathName[0]);
01596 
01597         /* Get directory starting cluster */
01598         if(!getDirClustNum(dir, &dummyClust, false)) return false;
01599 
01600         /* Can't remove root directory */
01601         if(dummyClust == 0) return false;
01602 
01603         /* Get parent directory starting cluster */
01604         if(!getDirClustNum(dir, &dirClust, true)) return false;
01605 
01606         /* Check to make sure directory is empty */
01607         Bit32u filecount = 0;
01608         /* Set to 2 to skip first 2 entries, [.] and [..] */
01609         Bit32s fileidx = 2;
01610         while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
01611                 /* Check for non-deleted files */
01612                 if(tmpentry.entryname[0] != 0xe5) filecount++;
01613                 fileidx++;
01614         }
01615 
01616         /* Return if directory is not empty */
01617         if(filecount > 0) return false;
01618 
01619         /* Find directory entry in parent directory */
01620         if (dirClust==0) fileidx = 0;   // root directory
01621         else fileidx = 2;
01622         bool found = false;
01623         while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
01624                 if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
01625                         found = true;
01626                         tmpentry.entryname[0] = 0xe5;
01627                         directoryChange(dirClust, &tmpentry, fileidx);
01628                         deleteClustChain(dummyClust);
01629 
01630                         break;
01631                 }
01632                 fileidx++;
01633         }
01634 
01635         if(!found) return false;
01636 
01637         return true;
01638 }
01639 
01640 bool fatDrive::Rename(const char * oldname, const char * newname) {
01641         direntry fileEntry1;
01642         Bit32u dirClust1, subEntry1;
01643         if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
01644         /* File to be renamed really exists */
01645 
01646         direntry fileEntry2;
01647         Bit32u dirClust2, subEntry2;
01648 
01649         /* Check if file already exists */
01650         if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
01651                 /* Target doesn't exist, can rename */
01652 
01653                 char dirName2[DOS_NAMELENGTH_ASCII];
01654                 char pathName2[11];
01655                 /* Can we even get the name of the file itself? */
01656                 if(!getEntryName(newname, &dirName2[0])) return false;
01657                 convToDirFile(&dirName2[0], &pathName2[0]);
01658 
01659                 /* Can we find the base directory? */
01660                 if(!getDirClustNum(newname, &dirClust2, true)) return false;
01661                 memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
01662                 memcpy(&fileEntry2.entryname, &pathName2[0], 11);
01663                 addDirectoryEntry(dirClust2, fileEntry2);
01664 
01665                 /* Check if file exists now */
01666                 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
01667 
01668                 /* Remove old entry */
01669                 fileEntry1.entryname[0] = 0xe5;
01670                 directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
01671 
01672                 return true;
01673         }
01674 
01675         /* Target already exists, fail */
01676         return false;
01677 }
01678 
01679 bool fatDrive::TestDir(const char *dir) {
01680         Bit32u dummyClust;
01681         return getDirClustNum(dir, &dummyClust, false);
01682 }
01683