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, std::vector<std::string> &options) : 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 if (!memcmp(bootbuffer.bootcode,"T98FDDIMAGE.R0\0\0",16)) {
00723             fseeko64(diskfile, 0L, SEEK_END);
00724             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00725             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880), 0);
00726         }
00727         else if (!memcmp(bootbuffer.bootcode,"T98FDDIMAGE.R1\0\0",16)) {
00728             fseeko64(diskfile, 0L, SEEK_END);
00729             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00730             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880), 1);
00731         }
00732         else {
00733             fseeko64(diskfile, 0L, SEEK_END);
00734             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00735             loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00736         }
00737         }
00738 
00739     fatDriveInit(sysFilename, bytesector, cylsector, headscyl, cylinders, filesize, options);
00740 }
00741 
00742 fatDrive::fatDrive(imageDisk *sourceLoadedDisk, std::vector<std::string> &options) : loadedDisk(NULL) {
00743         if (sourceLoadedDisk == 0) {
00744                 created_successfully = false;
00745                 return;
00746         }
00747         created_successfully = true;
00748         
00749         if(imgDTASeg == 0) {
00750                 imgDTASeg = DOS_GetMemory(2,"imgDTASeg");
00751                 imgDTAPtr = RealMake(imgDTASeg, 0);
00752                 imgDTA    = new DOS_DTA(imgDTAPtr);
00753         }
00754 
00755     loadedDisk = sourceLoadedDisk;
00756 
00757     fatDriveInit("", loadedDisk->sector_size, loadedDisk->sectors, loadedDisk->heads, loadedDisk->cylinders, loadedDisk->diskSizeK, options);
00758 }
00759 
00760 Bit8u fatDrive::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00761     if (loadedDisk != NULL) {
00762         /* this will only work if the logical sector size is larger than the disk sector size */
00763         const unsigned int lsz = loadedDisk->getSectSize();
00764         unsigned int c = sector_size / lsz;
00765 
00766         if (c != 0 && (sector_size % lsz) == 0) {
00767             Bit32u ssect = sectnum * c;
00768 
00769             while (c-- != 0) {
00770                 if (loadedDisk->Read_AbsoluteSector(ssect++,data) != 0)
00771                     return 0x05;
00772 
00773                 data = (void*)((char*)data + lsz);
00774             }
00775 
00776             return 0;
00777         }
00778     }
00779 
00780     return 0x05;
00781 }
00782 
00783 Bit8u fatDrive::Write_AbsoluteSector(Bit32u sectnum, void * data) {
00784     if (loadedDisk != NULL) {
00785         /* this will only work if the logical sector size is larger than the disk sector size */
00786         const unsigned int lsz = loadedDisk->getSectSize();
00787         unsigned int c = sector_size / lsz;
00788 
00789         if (c != 0 && (sector_size % lsz) == 0) {
00790             Bit32u ssect = sectnum * c;
00791 
00792             while (c-- != 0) {
00793                 if (loadedDisk->Write_AbsoluteSector(ssect++,data) != 0)
00794                     return 0x05;
00795 
00796                 data = (void*)((char*)data + lsz);
00797             }
00798 
00799             return 0;
00800         }
00801     }
00802 
00803     return 0x05;
00804 }
00805 
00806 Bit32u fatDrive::getSectSize(void) {
00807     return sector_size;
00808 }
00809 
00810 void fatDrive::fatDriveInit(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit64u filesize, std::vector<std::string> &options) {
00811         Bit32u startSector;
00812         bool pc98_512_to_1024_allow = false;
00813     int opt_partition_index = -1;
00814         struct partTable mbrData;
00815 
00816         if(!loadedDisk) {
00817                 created_successfully = false;
00818                 return;
00819         }
00820 
00821     for (const auto &opt : options) {
00822         size_t equ = opt.find_first_of('=');
00823         std::string name,value;
00824 
00825         if (equ != std::string::npos) {
00826             name = opt.substr(0,equ);
00827             value = opt.substr(equ+1);
00828         }
00829         else {
00830             name = opt;
00831             value.clear();
00832         }
00833 
00834         if (name == "partidx") {
00835             if (!value.empty())
00836                 opt_partition_index = (int)atol(value.c_str());
00837         }
00838         else {
00839             LOG(LOG_MISC,LOG_DEBUG)("FAT: option '%s' = '%s' ignored, unknown",name.c_str(),value.c_str());
00840         }
00841 
00842 //        LOG_MSG("'%s' = '%s'",name.c_str(),value.c_str());
00843     }
00844 
00845         loadedDisk->Addref();
00846 
00847     if (loadedDisk->getSectSize() > sizeof(bootbuffer)) {
00848         LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize());
00849                 created_successfully = false;
00850         return;
00851     }
00852 
00853         if(filesize > 2880) {
00854                 /* Set user specified harddrive parameters */
00855         if (headscyl > 0 && cylinders > 0 && cylsector > 0 && bytesector > 0)
00856                 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
00857 
00858         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
00859             created_successfully = false;
00860             LOG_MSG("No geometry");
00861             return;
00862         }
00863 
00864                 loadedDisk->Read_Sector(0,0,1,&mbrData);
00865 
00866                 if(mbrData.magic1!= 0x55 ||     mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
00867 
00868         startSector = 63;
00869 
00870         /* PC-98 bootloader support.
00871          * These can be identified by the "IPL1" in the boot sector.
00872          * These boot sectors do not have a valid partition table though the code below might
00873          * pick up a false partition #3 with a zero offset. Partition table is in sector 1 */
00874         if (!memcmp(mbrData.booter+4,"IPL1",4)) {
00875             unsigned char ipltable[SECTOR_SIZE_MAX];
00876             unsigned int max_entries = (std::min)(16UL,(unsigned long)(loadedDisk->getSectSize() / sizeof(_PC98RawPartition)));
00877             unsigned int i;
00878 
00879             LOG_MSG("PC-98 IPL1 signature detected");
00880 
00881             assert(sizeof(_PC98RawPartition) == 32);
00882 
00883             memset(ipltable,0,sizeof(ipltable));
00884             loadedDisk->Read_Sector(0,0,2,ipltable);
00885 
00886             if (opt_partition_index >= 0) {
00887                 /* user knows best! */
00888                 if ((unsigned int)opt_partition_index >= max_entries) {
00889                     LOG_MSG("Partition index out of range");
00890                     created_successfully = false;
00891                     return;
00892                 }
00893 
00894                 i = opt_partition_index;
00895                 _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
00896 
00897                 /* unfortunately start and end are in C/H/S geometry, so we have to translate.
00898                  * this is why it matters so much to read the geometry from the HDI header.
00899                  *
00900                  * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
00901                 startSector =
00902                     (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
00903                     (pe->head * loadedDisk->sectors) +
00904                     pe->sector;
00905 
00906                 /* Many HDI images I've encountered so far indicate 512 bytes/sector,
00907                  * but then the FAT filesystem itself indicates 1024 bytes per sector. */
00908                 pc98_512_to_1024_allow = true;
00909 
00910                 {
00911                     /* FIXME: What if the label contains SHIFT-JIS? */
00912                     std::string name = std::string(pe->name,sizeof(pe->name));
00913 
00914                     LOG_MSG("Using IPL1 entry %u name '%s' which starts at sector %lu",
00915                         i,name.c_str(),(unsigned long)startSector);
00916                 }
00917             }
00918             else {
00919                 for (i=0;i < max_entries;i++) {
00920                     _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
00921 
00922                     if (pe->mid == 0 && pe->sid == 0 &&
00923                             pe->ipl_sect == 0 && pe->ipl_head == 0 && pe->ipl_cyl == 0 &&
00924                             pe->sector == 0 && pe->head == 0 && pe->cyl == 0 &&
00925                             pe->end_sector == 0 && pe->end_head == 0 && pe->end_cyl == 0)
00926                         continue; /* unused */
00927 
00928                     /* We're looking for MS-DOS partitions.
00929                      * I've heard that some other OSes were once ported to PC-98, including Windows NT and OS/2,
00930                      * so I would rather not mistake NTFS or HPFS as FAT and cause damage. --J.C.
00931                      * FIXME: Is there a better way? */
00932                     if (!strncasecmp(pe->name,"MS-DOS",6) ||
00933                         !strncasecmp(pe->name,"MSDOS",5) ||
00934                         !strncasecmp(pe->name,"Windows",7)) {
00935                         /* unfortunately start and end are in C/H/S geometry, so we have to translate.
00936                          * this is why it matters so much to read the geometry from the HDI header.
00937                          *
00938                          * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
00939                         startSector =
00940                             (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
00941                             (pe->head * loadedDisk->sectors) +
00942                             pe->sector;
00943 
00944                         /* Many HDI images I've encountered so far indicate 512 bytes/sector,
00945                          * but then the FAT filesystem itself indicates 1024 bytes per sector. */
00946                         pc98_512_to_1024_allow = true;
00947 
00948                         {
00949                             /* FIXME: What if the label contains SHIFT-JIS? */
00950                             std::string name = std::string(pe->name,sizeof(pe->name));
00951 
00952                             LOG_MSG("Using IPL1 entry %u name '%s' which starts at sector %lu",
00953                                 i,name.c_str(),(unsigned long)startSector);
00954                             break;
00955                         }
00956                     }
00957                 }
00958             }
00959 
00960             if (i == max_entries)
00961                 LOG_MSG("No partitions found in the IPL1 table");
00962         }
00963         else {
00964             /* IBM PC master boot record search */
00965             int m;
00966 
00967             if (opt_partition_index >= 0) {
00968                 /* user knows best! */
00969                 if (opt_partition_index >= 4) {
00970                     LOG_MSG("Partition index out of range");
00971                     created_successfully = false;
00972                     return;
00973                 }
00974 
00975                 startSector = mbrData.pentry[m=opt_partition_index].absSectStart;
00976             }
00977             else {
00978                 for(m=0;m<4;m++) {
00979                     /* Pick the first available partition */
00980                     if(mbrData.pentry[m].partSize != 0x00 &&
00981                             (mbrData.pentry[m].parttype == 0x01 || mbrData.pentry[m].parttype == 0x04 ||
00982                              mbrData.pentry[m].parttype == 0x06 || mbrData.pentry[m].parttype == 0x0B ||
00983                              mbrData.pentry[m].parttype == 0x0C || mbrData.pentry[m].parttype == 0x0D ||
00984                              mbrData.pentry[m].parttype == 0x0E || mbrData.pentry[m].parttype == 0x0F)) {
00985                         LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
00986                         startSector = mbrData.pentry[m].absSectStart;
00987                         break;
00988                     }
00989                 }
00990             }
00991 
00992             if(m==4) LOG_MSG("No good partiton found in image.");
00993         }
00994 
00995                 partSectOff = startSector;
00996         } else {
00997                 /* Floppy disks don't have partitions */
00998                 partSectOff = 0;
00999 
01000         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
01001             created_successfully = false;
01002             LOG_MSG("No geometry");
01003             return;
01004         }
01005         }
01006 
01007         loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
01008 
01009         /* Check for DOS 1.x format floppy */
01010         if ((bootbuffer.mediadescriptor & 0xf0) != 0xf0 && filesize <= 360 && loadedDisk->getSectSize() == 512) {
01011 
01012                 Bit8u sectorBuffer[512];
01013                 loadedDisk->Read_AbsoluteSector(1,&sectorBuffer);
01014                 Bit8u mdesc = sectorBuffer[0];
01015 
01016                 /* Allowed if media descriptor in FAT matches image size  */
01017                 if ((mdesc == 0xfc && filesize == 180) ||
01018                         (mdesc == 0xfd && filesize == 360) ||
01019                         (mdesc == 0xfe && filesize == 160) ||
01020                         (mdesc == 0xff && filesize == 320)) {
01021 
01022                         /* Create parameters for a 160kB floppy */
01023                         bootbuffer.bytespersector = 512;
01024                         bootbuffer.sectorspercluster = 1;
01025                         bootbuffer.reservedsectors = 1;
01026                         bootbuffer.fatcopies = 2;
01027                         bootbuffer.rootdirentries = 64;
01028                         bootbuffer.totalsectorcount = 320;
01029                         bootbuffer.mediadescriptor = mdesc;
01030                         bootbuffer.sectorsperfat = 1;
01031                         bootbuffer.sectorspertrack = 8;
01032                         bootbuffer.headcount = 1;
01033                         bootbuffer.magic1 = 0x55;       // to silence warning
01034                         bootbuffer.magic2 = 0xaa;
01035                         if (!(mdesc & 0x2)) {
01036                                 /* Adjust for 9 sectors per track */
01037                                 bootbuffer.totalsectorcount = 360;
01038                                 bootbuffer.sectorsperfat = 2;
01039                                 bootbuffer.sectorspertrack = 9;
01040                         }
01041                         if (mdesc & 0x1) {
01042                                 /* Adjust for 2 sides */
01043                                 bootbuffer.sectorspercluster = 2;
01044                                 bootbuffer.rootdirentries = 112;
01045                                 bootbuffer.totalsectorcount *= 2;
01046                                 bootbuffer.headcount = 2;
01047                         }
01048                 }
01049         }
01050 
01051     LOG_MSG("FAT: BPB says %u sectors/track %u heads %u bytes/sector",
01052         bootbuffer.sectorspertrack,
01053         bootbuffer.headcount,
01054         bootbuffer.bytespersector);
01055 
01056     /* NTS: Some HDI images of PC-98 games do in fact have headcount == 0 */
01057     /* a clue that we're not really looking at FAT is invalid or weird values in the boot sector */
01058     if (bootbuffer.sectorspertrack == 0 || (bootbuffer.sectorspertrack > ((filesize <= 3000) ? 40 : 255)) ||
01059         (bootbuffer.headcount > ((filesize <= 3000) ? 64 : 255))) {
01060         LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem");
01061                 created_successfully = false;
01062         return;
01063     }
01064 
01065     /* work at this point in logical sectors */
01066         sector_size = loadedDisk->getSectSize();
01067 
01068     /* Many HDI images indicate a disk format of 256 or 512 bytes per sector combined with a FAT filesystem
01069      * that indicates 1024 bytes per sector. */
01070     if (pc98_512_to_1024_allow &&
01071          bootbuffer.bytespersector != getSectSize() &&
01072          bootbuffer.bytespersector >  getSectSize() &&
01073         (bootbuffer.bytespersector %  getSectSize()) == 0) {
01074         unsigned int ratioshift = 1;
01075 
01076         while ((unsigned int)(bootbuffer.bytespersector >> ratioshift) > getSectSize())
01077             ratioshift++;
01078 
01079         unsigned int ratio = 1u << ratioshift;
01080 
01081         LOG_MSG("Disk indicates %u bytes/sector, FAT filesystem indicates %u bytes/sector. Ratio=%u:1 shift=%u",
01082                 getSectSize(),bootbuffer.bytespersector,ratio,ratioshift);
01083 
01084         if ((unsigned int)(bootbuffer.bytespersector >> ratioshift) == getSectSize()) {
01085             assert(ratio >= 2);
01086 
01087             /* we can hack things in place IF the starting sector is an even number */
01088             if ((partSectOff & (ratio - 1)) == 0) {
01089                 partSectOff >>= ratioshift;
01090                 startSector >>= ratioshift;
01091                 sector_size = bootbuffer.bytespersector;
01092                 LOG_MSG("Using logical sector size %u",sector_size);
01093             }
01094             else {
01095                 LOG_MSG("However there's nothing I can do, because the partition starts on an odd sector");
01096             }
01097         }
01098     }
01099 
01100     /* NTS: PC-98 floppies (the 1024 byte/sector format) do not have magic bytes */
01101     if (getSectSize() == 512 && !IS_PC98_ARCH) {
01102         if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
01103             /* Not a FAT filesystem */
01104             LOG_MSG("Loaded image has no valid magicnumbers at the end!");
01105             created_successfully = false;
01106             return;
01107         }
01108     }
01109 
01110         if(!bootbuffer.sectorsperfat) {
01111                 /* FAT32 not implemented yet */
01112                 LOG_MSG("FAT32 not implemented yet, mounting image only");
01113                 fattype = FAT32;        // Avoid parsing dir entries, see fatDrive::FindFirst()...should work for unformatted images as well
01114                 created_successfully = false;
01115                 return;
01116         }
01117 
01118     /* too much of this code assumes 512 bytes per sector or more.
01119      * MS-DOS itself as I understand it relies on bytes per sector being a power of 2.
01120      * this is to protect against errant FAT structures and to help prep this code
01121      * later to work with the 1024 bytes/sector used by PC-98 floppy formats.
01122      * When done, this code should be able to then handle the FDI/FDD images
01123      * PC-98 games are normally distributed in on the internet.
01124      *
01125      * The value "128" comes from the smallest sector size possible on the floppy
01126      * controller of MS-DOS based systems. */
01127     /* NTS: Power of 2 test: A number is a power of 2 if (x & (x - 1)) == 0
01128      *
01129      * 15        15 & 14       01111 AND 01110     RESULT: 01110 (15)
01130      * 16        16 & 15       10000 AND 01111     RESULT: 00000 (0)
01131      * 17        17 & 16       10001 AND 10000     RESULT: 10000 (16) */
01132     if (bootbuffer.bytespersector < 128 || bootbuffer.bytespersector > sizeof(bootbuffer) ||
01133         (bootbuffer.bytespersector & (bootbuffer.bytespersector - 1)) != 0/*not a power of 2*/) {
01134         LOG_MSG("FAT bytes/sector value %u not supported",bootbuffer.bytespersector);
01135                 created_successfully = false;
01136         return;
01137     }
01138 
01139     /* another fault of this code is that it assumes the sector size of the medium matches
01140      * the bytespersector value of the MS-DOS filesystem. if they don't match, problems
01141      * will result. */
01142     if (bootbuffer.bytespersector != getSectSize()) {
01143         LOG_MSG("FAT bytes/sector %u does not match disk image bytes/sector %u",
01144             (unsigned int)bootbuffer.bytespersector,
01145             (unsigned int)getSectSize());
01146                 created_successfully = false;
01147         return;
01148     }
01149 
01150         /* Determine FAT format, 12, 16 or 32 */
01151 
01152         /* Get size of root dir in sectors */
01153         /* TODO: Get 32-bit total sector count if needed */
01154         Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32u) + (bootbuffer.bytespersector - 1u)) / bootbuffer.bytespersector;
01155         Bit32u DataSectors;
01156         if(bootbuffer.totalsectorcount != 0) {
01157                 DataSectors = (Bitu)bootbuffer.totalsectorcount - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01158         } else {
01159                 DataSectors = (Bitu)bootbuffer.totalsecdword - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01160 
01161         }
01162         CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
01163 
01164         firstDataSector = ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
01165         firstRootDirSect = (Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)partSectOff;
01166 
01167         if(CountOfClusters < 4085) {
01168                 /* Volume is FAT12 */
01169                 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
01170                 fattype = FAT12;
01171         } else if (CountOfClusters < 65525) {
01172                 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
01173                 fattype = FAT16;
01174         } else {
01175                 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
01176                 fattype = FAT32;
01177         }
01178 
01179         /* There is no cluster 0, this means we are in the root directory */
01180         cwdDirCluster = 0;
01181 
01182         memset(fatSectBuffer,0,1024);
01183         curFatSect = 0xffffffff;
01184 
01185         strcpy(info, "fatDrive ");
01186         strcat(info, sysFilename);
01187 }
01188 
01189 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
01190         Bit32u countFree = 0;
01191         Bit32u i;
01192         
01193         *_bytes_sector = (Bit16u)getSectSize();
01194         *_sectors_cluster = bootbuffer.sectorspercluster;
01195         if (CountOfClusters<65536) *_total_clusters = (Bit16u)CountOfClusters;
01196         else {
01197                 // maybe some special handling needed for fat32
01198                 *_total_clusters = 65535;
01199         }
01200         for(i=0;i<CountOfClusters;i++) if(!getClusterValue(i+2)) countFree++;
01201         if (countFree<65536) *_free_clusters = (Bit16u)countFree;
01202         else {
01203                 // maybe some special handling needed for fat32
01204                 *_free_clusters = 65535;
01205         }
01206         
01207         return true;
01208 }
01209 
01210 Bit32u fatDrive::getFirstFreeClust(void) {
01211         Bit32u i;
01212         for(i=0;i<CountOfClusters;i++) {
01213                 if(!getClusterValue(i+2)) return (i+2);
01214         }
01215 
01216         /* No free cluster found */
01217         return 0;
01218 }
01219 
01220 bool fatDrive::isRemote(void) { return false; }
01221 bool fatDrive::isRemovable(void) { return false; }
01222 
01223 Bits fatDrive::UnMount(void) {
01224         delete this;
01225         return 0;
01226 }
01227 
01228 Bit8u fatDrive::GetMediaByte(void) { return loadedDisk->GetBiosType(); }
01229 
01230 bool fatDrive::FileCreate(DOS_File **file, const char *name, Bit16u attributes) {
01231         direntry fileEntry;
01232         Bit32u dirClust, subEntry;
01233         char dirName[DOS_NAMELENGTH_ASCII];
01234         char pathName[11];
01235 
01236         Bit16u save_errorcode=dos.errorcode;
01237 
01238         /* Check if file already exists */
01239         if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01240                 /* Truncate file */
01241                 fileEntry.entrysize=0;
01242                 directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01243         } else {
01244                 /* Can we even get the name of the file itself? */
01245                 if(!getEntryName(name, &dirName[0])) return false;
01246                 convToDirFile(&dirName[0], &pathName[0]);
01247 
01248                 /* Can we find the base directory? */
01249                 if(!getDirClustNum(name, &dirClust, true)) return false;
01250                 memset(&fileEntry, 0, sizeof(direntry));
01251                 memcpy(&fileEntry.entryname, &pathName[0], 11);
01252                 fileEntry.attrib = (Bit8u)(attributes & 0xff);
01253                 addDirectoryEntry(dirClust, fileEntry);
01254 
01255                 /* Check if file exists now */
01256                 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01257         }
01258 
01259         /* Empty file created, now lets open it */
01260         /* TODO: check for read-only flag and requested write access */
01261         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01262         (*file)->flags=OPEN_READWRITE;
01263         ((fatFile *)(*file))->dirCluster = dirClust;
01264         ((fatFile *)(*file))->dirIndex = subEntry;
01265         /* Maybe modTime and date should be used ? (crt matches findnext) */
01266         ((fatFile *)(*file))->time = fileEntry.crtTime;
01267         ((fatFile *)(*file))->date = fileEntry.crtDate;
01268 
01269         dos.errorcode=save_errorcode;
01270         return true;
01271 }
01272 
01273 bool fatDrive::FileExists(const char *name) {
01274         direntry fileEntry;
01275         Bit32u dummy1, dummy2;
01276         if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
01277         return true;
01278 }
01279 
01280 bool fatDrive::FileOpen(DOS_File **file, const char *name, Bit32u flags) {
01281         direntry fileEntry;
01282         Bit32u dirClust, subEntry;
01283         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01284         /* TODO: check for read-only flag and requested write access */
01285         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01286         (*file)->flags = flags;
01287         ((fatFile *)(*file))->dirCluster = dirClust;
01288         ((fatFile *)(*file))->dirIndex = subEntry;
01289         /* Maybe modTime and date should be used ? (crt matches findnext) */
01290         ((fatFile *)(*file))->time = fileEntry.crtTime;
01291         ((fatFile *)(*file))->date = fileEntry.crtDate;
01292         return true;
01293 }
01294 
01295 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
01296         /* TODO: Stub */
01297         return false;
01298 }
01299 
01300 bool fatDrive::FileUnlink(const char * name) {
01301         direntry fileEntry;
01302         Bit32u dirClust, subEntry;
01303 
01304         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01305 
01306         fileEntry.entryname[0] = 0xe5;
01307         directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01308 
01309         if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust);
01310 
01311         return true;
01312 }
01313 
01314 bool fatDrive::FindFirst(const char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
01315         direntry dummyClust;
01316         if(fattype==FAT32) return false;
01317 #if 0
01318         Bit8u attr;char pattern[DOS_NAMELENGTH_ASCII];
01319         dta.GetSearchParams(attr,pattern);
01320         if(attr==DOS_ATTR_VOLUME) {
01321                 if (strcmp(GetLabel(), "") == 0 ) {
01322                         DOS_SetError(DOSERR_NO_MORE_FILES);
01323                         return false;
01324                 }
01325                 dta.SetResult(GetLabel(),0,0,0,DOS_ATTR_VOLUME);
01326                 return true;
01327         }
01328         if(attr & DOS_ATTR_VOLUME) //check for root dir or fcb_findfirst
01329                 LOG(LOG_DOSMISC,LOG_WARN)("findfirst for volumelabel used on fatDrive. Unhandled!!!!!");
01330 #endif
01331         if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
01332                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
01333                 return false;
01334         }
01335         dta.SetDirID(0);
01336         dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
01337         return FindNextInternal(cwdDirCluster, dta, &dummyClust);
01338 }
01339 
01340 char* removeTrailingSpaces(char* str) {
01341         char* end = str + strlen(str) - 1;
01342         while (end >= str && *end == ' ') end--;
01343     /* NTS: The loop will exit with 'end' one char behind the last ' ' space character.
01344      *      So to ASCIIZ snip off the space, step forward one and overwrite with NUL.
01345      *      The loop may end with 'end' one char behind 'ptr' if the string was empty ""
01346      *      or nothing but spaces. This is OK because after the step forward, end >= str
01347      *      in all cases. */
01348         *(++end) = '\0';
01349         return str;
01350 }
01351 
01352 char* removeLeadingSpaces(char* str) {
01353         size_t len = strlen(str);
01354         size_t pos = strspn(str," ");
01355         memmove(str,str + pos,len - pos + 1);
01356         return str;
01357 }
01358 
01359 char* trimString(char* str) {
01360         return removeTrailingSpaces(removeLeadingSpaces(str));
01361 }
01362 
01363 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
01364         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01365         Bit32u logentsector; /* Logical entry sector */
01366         Bit32u entryoffset;  /* Index offset within sector */
01367         Bit32u tmpsector;
01368         Bit8u attrs;
01369         Bit16u dirPos;
01370         char srch_pattern[DOS_NAMELENGTH_ASCII];
01371         char find_name[DOS_NAMELENGTH_ASCII];
01372         char extension[4];
01373 
01374     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01375     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01376     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01377 
01378         dta.GetSearchParams(attrs, srch_pattern);
01379         dirPos = dta.GetDirID();
01380 
01381 nextfile:
01382         logentsector = (Bit32u)((size_t)dirPos / dirent_per_sector);
01383         entryoffset = (Bit32u)((size_t)dirPos % dirent_per_sector);
01384 
01385         if(dirClustNumber==0) {
01386                 if(dirPos >= bootbuffer.rootdirentries) {
01387                         DOS_SetError(DOSERR_NO_MORE_FILES);
01388                         return false;
01389                 }
01390                 Read_AbsoluteSector(firstRootDirSect+logentsector,sectbuf);
01391         } else {
01392                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01393                 /* A zero sector number can't happen */
01394                 if(tmpsector == 0) {
01395                         DOS_SetError(DOSERR_NO_MORE_FILES);
01396                         return false;
01397                 }
01398                 Read_AbsoluteSector(tmpsector,sectbuf);
01399         }
01400         dirPos++;
01401         dta.SetDirID(dirPos);
01402 
01403         /* Deleted file entry */
01404         if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
01405 
01406         /* End of directory list */
01407         if (sectbuf[entryoffset].entryname[0] == 0x00) {
01408                 DOS_SetError(DOSERR_NO_MORE_FILES);
01409                 return false;
01410         }
01411         memset(find_name,0,DOS_NAMELENGTH_ASCII);
01412         memset(extension,0,4);
01413         memcpy(find_name,&sectbuf[entryoffset].entryname[0],8);
01414         memcpy(extension,&sectbuf[entryoffset].entryname[8],3);
01415         trimString(&find_name[0]);
01416         trimString(&extension[0]);
01417         
01418         //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY))
01419         if (extension[0]!=0) {
01420                 strcat(find_name, ".");
01421                 strcat(find_name, extension);
01422         }
01423 
01424         /* Compare attributes to search attributes */
01425 
01426         //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
01427         if (attrs == DOS_ATTR_VOLUME) {
01428                 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
01429                 DOS_Drive_Cache dirCache;
01430                 dirCache.SetLabel(find_name, false, true);
01431         } else {
01432                 if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile;
01433         }
01434 
01435 
01436         /* Compare name to search pattern */
01437         if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
01438 
01439         //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
01440 
01441         dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib);
01442 
01443         memcpy(foundEntry, &sectbuf[entryoffset], sizeof(direntry));
01444 
01445         return true;
01446 }
01447 
01448 bool fatDrive::FindNext(DOS_DTA &dta) {
01449         direntry dummyClust;
01450 
01451         return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
01452 }
01453 
01454 bool fatDrive::GetFileAttr(const char *name, Bit16u *attr) {
01455         direntry fileEntry;
01456         Bit32u dirClust, subEntry;
01457         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01458                 char dirName[DOS_NAMELENGTH_ASCII];
01459                 char pathName[11];
01460 
01461                 /* Can we even get the name of the directory itself? */
01462                 if(!getEntryName(name, &dirName[0])) return false;
01463                 convToDirFile(&dirName[0], &pathName[0]);
01464 
01465                 /* Get parent directory starting cluster */
01466                 if(!getDirClustNum(name, &dirClust, true)) return false;
01467 
01468                 /* Find directory entry in parent directory */
01469                 Bit32s fileidx = 2;
01470                 if (dirClust==0) fileidx = 0;   // root directory
01471                 Bit32s last_idx=0; 
01472                 while(directoryBrowse(dirClust, &fileEntry, fileidx, last_idx)) {
01473                         if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
01474                                 *attr=fileEntry.attrib;
01475                                 return true;
01476                         }
01477                         fileidx++;
01478                 }
01479                 return false;
01480         } else *attr=fileEntry.attrib;
01481         return true;
01482 }
01483 
01484 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
01485         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01486         Bit32u logentsector;    /* Logical entry sector */
01487         Bit32u entryoffset = 0; /* Index offset within sector */
01488         Bit32u tmpsector;
01489         Bit16u dirPos = 0;
01490 
01491     (void)start;//UNUSED
01492 
01493     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01494     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01495     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01496 
01497         while(entNum>=0) {
01498                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01499                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01500 
01501                 if(dirClustNumber==0) {
01502                         if(dirPos >= bootbuffer.rootdirentries) return false;
01503                         tmpsector = firstRootDirSect+logentsector;
01504                         Read_AbsoluteSector(tmpsector,sectbuf);
01505                 } else {
01506                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01507                         /* A zero sector number can't happen */
01508                         if(tmpsector == 0) return false;
01509                         Read_AbsoluteSector(tmpsector,sectbuf);
01510                 }
01511                 dirPos++;
01512 
01513 
01514                 /* End of directory list */
01515                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01516                 --entNum;
01517         }
01518 
01519         memcpy(useEntry, &sectbuf[entryoffset],sizeof(direntry));
01520         return true;
01521 }
01522 
01523 bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
01524         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01525         Bit32u logentsector;    /* Logical entry sector */
01526         Bit32u entryoffset = 0; /* Index offset within sector */
01527         Bit32u tmpsector = 0;
01528         Bit16u dirPos = 0;
01529         
01530     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01531     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01532     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01533 
01534         while(entNum>=0) {              
01535                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01536                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01537 
01538                 if(dirClustNumber==0) {
01539                         if(dirPos >= bootbuffer.rootdirentries) return false;
01540                         tmpsector = firstRootDirSect+logentsector;
01541                         Read_AbsoluteSector(tmpsector,sectbuf);
01542                 } else {
01543                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01544                         /* A zero sector number can't happen */
01545                         if(tmpsector == 0) return false;
01546                         Read_AbsoluteSector(tmpsector,sectbuf);
01547                 }
01548                 dirPos++;
01549 
01550 
01551                 /* End of directory list */
01552                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01553                 --entNum;
01554         }
01555         if(tmpsector != 0) {
01556         memcpy(&sectbuf[entryoffset], useEntry, sizeof(direntry));
01557                 Write_AbsoluteSector(tmpsector, sectbuf);
01558         return true;
01559         } else {
01560                 return false;
01561         }
01562 }
01563 
01564 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
01565         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01566         Bit32u logentsector; /* Logical entry sector */
01567         Bit32u entryoffset;  /* Index offset within sector */
01568         Bit32u tmpsector;
01569         Bit16u dirPos = 0;
01570         
01571     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01572     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01573     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01574 
01575         for(;;) {               
01576                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01577                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01578 
01579                 if(dirClustNumber==0) {
01580                         if(dirPos >= bootbuffer.rootdirentries) return false;
01581                         tmpsector = firstRootDirSect+logentsector;
01582                         Read_AbsoluteSector(tmpsector,sectbuf);
01583                 } else {
01584                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01585                         /* A zero sector number can't happen - we need to allocate more room for this directory*/
01586                         if(tmpsector == 0) {
01587                                 Bit32u newClust;
01588                                 newClust = appendCluster(dirClustNumber);
01589                                 if(newClust == 0) return false;
01590                                 /* Try again to get tmpsector */
01591                                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01592                                 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
01593                         }
01594                         Read_AbsoluteSector(tmpsector,sectbuf);
01595                 }
01596                 dirPos++;
01597 
01598                 /* Deleted file entry or end of directory list */
01599                 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
01600                         sectbuf[entryoffset] = useEntry;
01601                         Write_AbsoluteSector(tmpsector,sectbuf);
01602                         break;
01603                 }
01604         }
01605 
01606         return true;
01607 }
01608 
01609 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
01610         Bit8u secBuffer[SECTOR_SIZE_MAX];
01611 
01612         memset(&secBuffer[0], 0, SECTOR_SIZE_MAX);
01613 
01614         unsigned int i;
01615         for(i=0;i<bootbuffer.sectorspercluster;i++) {
01616                 Write_AbsoluteSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
01617         }
01618 }
01619 
01620 bool fatDrive::MakeDir(const char *dir) {
01621         Bit32u dummyClust, dirClust;
01622         direntry tmpentry;
01623         char dirName[DOS_NAMELENGTH_ASCII];
01624         char pathName[11];
01625 
01626         /* Can we even get the name of the directory itself? */
01627         if(!getEntryName(dir, &dirName[0])) return false;
01628         convToDirFile(&dirName[0], &pathName[0]);
01629 
01630         /* Fail to make directory if already exists */
01631         if(getDirClustNum(dir, &dummyClust, false)) return false;
01632 
01633         dummyClust = getFirstFreeClust();
01634         /* No more space */
01635         if(dummyClust == 0) return false;
01636         
01637         if(!allocateCluster(dummyClust, 0)) return false;
01638 
01639         zeroOutCluster(dummyClust);
01640 
01641         /* Can we find the base directory? */
01642         if(!getDirClustNum(dir, &dirClust, true)) return false;
01643         
01644         /* Add the new directory to the base directory */
01645         memset(&tmpentry,0, sizeof(direntry));
01646         memcpy(&tmpentry.entryname, &pathName[0], 11);
01647         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01648         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01649         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01650         addDirectoryEntry(dirClust, tmpentry);
01651 
01652         /* Add the [.] and [..] entries to our new directory*/
01653         /* [.] entry */
01654         memset(&tmpentry,0, sizeof(direntry));
01655         memcpy(&tmpentry.entryname, ".          ", 11);
01656         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01657         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01658         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01659         addDirectoryEntry(dummyClust, tmpentry);
01660 
01661         /* [..] entry */
01662         memset(&tmpentry,0, sizeof(direntry));
01663         memcpy(&tmpentry.entryname, "..         ", 11);
01664         tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
01665         tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
01666         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01667         addDirectoryEntry(dummyClust, tmpentry);
01668 
01669         return true;
01670 }
01671 
01672 bool fatDrive::RemoveDir(const char *dir) {
01673         Bit32u dummyClust, dirClust;
01674         direntry tmpentry;
01675         char dirName[DOS_NAMELENGTH_ASCII];
01676         char pathName[11];
01677 
01678         /* Can we even get the name of the directory itself? */
01679         if(!getEntryName(dir, &dirName[0])) return false;
01680         convToDirFile(&dirName[0], &pathName[0]);
01681 
01682         /* Get directory starting cluster */
01683         if(!getDirClustNum(dir, &dummyClust, false)) return false;
01684 
01685         /* Can't remove root directory */
01686         if(dummyClust == 0) return false;
01687 
01688         /* Get parent directory starting cluster */
01689         if(!getDirClustNum(dir, &dirClust, true)) return false;
01690 
01691         /* Check to make sure directory is empty */
01692         Bit32u filecount = 0;
01693         /* Set to 2 to skip first 2 entries, [.] and [..] */
01694         Bit32s fileidx = 2;
01695         while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
01696                 /* Check for non-deleted files */
01697                 if(tmpentry.entryname[0] != 0xe5) filecount++;
01698                 fileidx++;
01699         }
01700 
01701         /* Return if directory is not empty */
01702         if(filecount > 0) return false;
01703 
01704         /* Find directory entry in parent directory */
01705         if (dirClust==0) fileidx = 0;   // root directory
01706         else fileidx = 2;
01707         bool found = false;
01708         while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
01709                 if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
01710                         found = true;
01711                         tmpentry.entryname[0] = 0xe5;
01712                         directoryChange(dirClust, &tmpentry, fileidx);
01713                         deleteClustChain(dummyClust);
01714 
01715                         break;
01716                 }
01717                 fileidx++;
01718         }
01719 
01720         if(!found) return false;
01721 
01722         return true;
01723 }
01724 
01725 bool fatDrive::Rename(const char * oldname, const char * newname) {
01726         direntry fileEntry1;
01727         Bit32u dirClust1, subEntry1;
01728         if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
01729         /* File to be renamed really exists */
01730 
01731         direntry fileEntry2;
01732         Bit32u dirClust2, subEntry2;
01733 
01734         /* Check if file already exists */
01735         if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
01736                 /* Target doesn't exist, can rename */
01737 
01738                 char dirName2[DOS_NAMELENGTH_ASCII];
01739                 char pathName2[11];
01740                 /* Can we even get the name of the file itself? */
01741                 if(!getEntryName(newname, &dirName2[0])) return false;
01742                 convToDirFile(&dirName2[0], &pathName2[0]);
01743 
01744                 /* Can we find the base directory? */
01745                 if(!getDirClustNum(newname, &dirClust2, true)) return false;
01746                 memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
01747                 memcpy(&fileEntry2.entryname, &pathName2[0], 11);
01748                 addDirectoryEntry(dirClust2, fileEntry2);
01749 
01750                 /* Check if file exists now */
01751                 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
01752 
01753                 /* Remove old entry */
01754                 fileEntry1.entryname[0] = 0xe5;
01755                 directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
01756 
01757                 return true;
01758         }
01759 
01760         /* Target already exists, fail */
01761         return false;
01762 }
01763 
01764 bool fatDrive::TestDir(const char *dir) {
01765         Bit32u dummyClust;
01766         return getDirClustNum(dir, &dummyClust, false);
01767 }
01768