DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/drive_fat.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 #include "bitop.h"
00033 
00034 #include <algorithm>
00035 
00036 #define IMGTYPE_FLOPPY 0
00037 #define IMGTYPE_ISO    1
00038 #define IMGTYPE_HDD        2
00039 
00040 #define FAT12              0
00041 #define FAT16              1
00042 #define FAT32              2
00043 
00044 static Bit16u dpos[256];
00045 static Bit32u dnum[256];
00046 extern bool wpcolon, force_sfn;
00047 extern int lfn_filefind_handle;
00048 
00049 bool filename_not_8x3(const char *n) {
00050         unsigned int i;
00051 
00052         i = 0;
00053         while (*n != 0) {
00054                 if (*n == '.') break;
00055                 if (*n<=32||*n==127||*n=='"'||*n=='+'||*n=='='||*n==','||*n==';'||*n==':'||*n=='<'||*n=='>'||*n=='['||*n==']'||*n=='|'||*n=='?'||*n=='*') return true;
00056                 i++;
00057                 n++;
00058         }
00059         if (i > 8) return true;
00060         if (*n == 0) return false; /* made it past 8 or less normal chars and end of string: normal */
00061 
00062         /* skip dot */
00063         assert(*n == '.');
00064         n++;
00065 
00066         i = 0;
00067         while (*n != 0) {
00068                 if (*n == '.') return true; /* another '.' means LFN */
00069                 if (*n<=32||*n==127||*n=='"'||*n=='+'||*n=='='||*n==','||*n==';'||*n==':'||*n=='<'||*n=='>'||*n=='['||*n==']'||*n=='|'||*n=='?'||*n=='*') return true;
00070                 i++;
00071                 n++;
00072         }
00073         if (i > 3) return true;
00074 
00075         return false; /* it is 8.3 case */
00076 }
00077 
00078 /* Assuming an LFN call, if the name is not strict 8.3 uppercase, return true.
00079  * If the name is strict 8.3 uppercase like "FILENAME.TXT" there is no point making an LFN because it is a waste of space */
00080 bool filename_not_strict_8x3(const char *n) {
00081         if (filename_not_8x3(n)) return true;
00082         for (unsigned int i=0; i<strlen(n); i++)
00083                 if (n[i]>='a' && n[i]<='z')
00084                         return true;
00085         return false; /* it is strict 8.3 upper case */
00086 }
00087 
00088 char sfn[DOS_NAMELENGTH_ASCII];
00089 /* Generate 8.3 names from LFNs, with tilde usage (from ~1 to ~9999). */
00090 char* fatDrive::Generate_SFN(const char *path, const char *name) {
00091         if (!filename_not_8x3(name)) {
00092                 strcpy(sfn, name);
00093                 upcase(sfn);
00094                 return sfn;
00095         }
00096         char lfn[LFN_NAMELENGTH+1], fullname[DOS_PATHLENGTH+DOS_NAMELENGTH_ASCII], *n;
00097         if (name==NULL||!*name) return NULL;
00098         if (strlen(name)>LFN_NAMELENGTH) {
00099                 strncpy(lfn, name, LFN_NAMELENGTH);
00100                 lfn[LFN_NAMELENGTH]=0;
00101         } else
00102                 strcpy(lfn, name);
00103         if (!strlen(lfn)) return NULL;
00104         direntry fileEntry = {};
00105         Bit32u dirClust, subEntry;
00106         unsigned int k=1, i, t=10000;
00107         while (k<10000) {
00108                 n=lfn;
00109                 if (t>strlen(n)||k==1||k==10||k==100||k==1000) {
00110                         i=0;
00111                         *sfn=0;
00112                         while (*n == '.'||*n == ' ') n++;
00113                         while (strlen(n)&&(*(n+strlen(n)-1)=='.'||*(n+strlen(n)-1)==' ')) *(n+strlen(n)-1)=0;
00114                         while (*n != 0 && *n != '.' && i<(k<10?6u:(k<100?5u:(k<1000?4:3u)))) {
00115                                 if (*n == ' ') {
00116                                         n++;
00117                                         continue;
00118                                 }
00119                                 if (*n=='"'||*n=='+'||*n=='='||*n==','||*n==';'||*n==':'||*n=='<'||*n=='>'||*n=='['||*n==']'||*n=='|'||*n=='?'||*n=='*') {
00120                                         sfn[i++]='_';
00121                                         n++;
00122                                 } else
00123                                         sfn[i++]=toupper(*(n++));
00124                         }
00125                         sfn[i++]='~';
00126                         t=i;
00127                 } else
00128                         i=t;
00129                 if (k<10)
00130                         sfn[i++]='0'+k;
00131                 else if (k<100) {
00132                         sfn[i++]='0'+(k/10);
00133                         sfn[i++]='0'+(k%10);
00134                 } else if (k<1000) {
00135                         sfn[i++]='0'+(k/100);
00136                         sfn[i++]='0'+((k%100)/10);
00137                         sfn[i++]='0'+(k%10);
00138                 } else {
00139                         sfn[i++]='0'+(k/1000);
00140                         sfn[i++]='0'+((k%1000)/100);
00141                         sfn[i++]='0'+((k%100)/10);
00142                         sfn[i++]='0'+(k%10);
00143                 }
00144                 if (t>strlen(n)||k==1||k==10||k==100||k==1000) {
00145                         char *p=strrchr(n, '.');
00146                         if (p!=NULL) {
00147                                 sfn[i++]='.';
00148                                 n=p+1;
00149                                 while (*n == '.') n++;
00150                                 int j=0;
00151                                 while (*n != 0 && j++<3) {
00152                                         if (*n == ' ') {
00153                                                 n++;
00154                                                 continue;
00155                                         }
00156                                         if (*n=='"'||*n=='+'||*n=='='||*n==','||*n==';'||*n==':'||*n=='<'||*n=='>'||*n=='['||*n==']'||*n=='|'||*n=='?'||*n=='*') {
00157                                                 sfn[i++]='_';
00158                                                 n++;
00159                                         } else
00160                                                 sfn[i++]=toupper(*(n++));
00161                                 }
00162                         }
00163                         sfn[i++]=0;
00164                 }
00165                 strcpy(fullname, path);
00166                 strcat(fullname, sfn);
00167                 if(!getFileDirEntry(fullname, &fileEntry, &dirClust, &subEntry,/*dirOk*/true)) return sfn;
00168                 k++;
00169         }
00170         return NULL;
00171 }
00172 
00173 class fatFile : public DOS_File {
00174 public:
00175         fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
00176         bool Read(Bit8u * data,Bit16u * size);
00177         bool Write(const Bit8u * data,Bit16u * size);
00178         bool Seek(Bit32u * pos,Bit32u type);
00179         bool Close();
00180         Bit16u GetInformation(void);
00181     void Flush(void);
00182         bool UpdateDateTimeFromHost(void);   
00183         Bit32u GetSeekPos(void);
00184         Bit32u firstCluster;
00185         Bit32u seekpos = 0;
00186         Bit32u filelength;
00187         Bit32u currentSector = 0;
00188         Bit32u curSectOff = 0;
00189         Bit8u sectorBuffer[SECTOR_SIZE_MAX];
00190         /* Record of where in the directory structure this file is located */
00191         Bit32u dirCluster = 0;
00192         Bit32u dirIndex = 0;
00193 
00194     bool modified = false;
00195         bool loadedSector = false;
00196         fatDrive *myDrive;
00197 
00198 #if 0/*unused*/
00199 private:
00200     enum { NONE,READ,WRITE } last_action;
00201         Bit16u info;
00202 #endif
00203 };
00204 
00205 void time_t_to_DOS_DateTime(Bit16u &t,Bit16u &d,time_t unix_time) {
00206     const struct tm *tm = localtime(&unix_time);
00207     if (tm == NULL) return;
00208 
00209     /* NTS: tm->tm_year = years since 1900,
00210      *      tm->tm_mon = months since January therefore January == 0
00211      *      tm->tm_mday = day of the month, starting with 1 */
00212 
00213     t = ((unsigned int)tm->tm_hour << 11u) + ((unsigned int)tm->tm_min << 5u) + ((unsigned int)tm->tm_sec >> 1u);
00214     d = (((unsigned int)tm->tm_year - 80u) << 9u) + (((unsigned int)tm->tm_mon + 1u) << 5u) + (unsigned int)tm->tm_mday;
00215 }
00216 
00217 /* IN - char * filename: Name in regular filename format, e.g. bob.txt */
00218 /* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob     txt */
00219 static void convToDirFile(const char *filename, char *filearray) {
00220         Bit32u charidx = 0;
00221         Bit32u flen,i;
00222         flen = (Bit32u)strlen(filename);
00223         memset(filearray, 32, 11);
00224         for(i=0;i<flen;i++) {
00225                 if(charidx >= 11) break;
00226                 if(filename[i] != '.') {
00227                         filearray[charidx] = filename[i];
00228                         charidx++;
00229                 } else {
00230                         charidx = 8;
00231                 }
00232         }
00233 }
00234 
00235 fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) : firstCluster(startCluster), filelength(fileLen), myDrive(useDrive) {
00236         Bit32u seekto = 0;
00237         open = true;
00238         memset(&sectorBuffer[0], 0, sizeof(sectorBuffer));
00239         
00240         if(filelength > 0) {
00241                 Seek(&seekto, DOS_SEEK_SET);
00242         }
00243 }
00244 
00245 void fatFile::Flush(void) {
00246         if (loadedSector) {
00247                 myDrive->writeSector(currentSector, sectorBuffer);
00248                 loadedSector = false;
00249         }
00250 
00251     if (modified || newtime) {
00252         direntry tmpentry = {};
00253 
00254         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00255 
00256         if (newtime) {
00257             tmpentry.modTime = time;
00258             tmpentry.modDate = date;
00259         }
00260         else {
00261             Bit16u ct,cd;
00262 
00263             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
00264 
00265             tmpentry.modTime = ct;
00266             tmpentry.modDate = cd;
00267         }
00268 
00269         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00270         modified = false;
00271         newtime = false;
00272     }
00273 }
00274 
00275 bool fatFile::Read(Bit8u * data, Bit16u *size) {
00276         if ((this->flags & 0xf) == OPEN_WRITE) {        // check if file opened in write-only mode
00277                 DOS_SetError(DOSERR_ACCESS_DENIED);
00278                 return false;
00279         }
00280         Bit16u sizedec, sizecount;
00281         if(seekpos >= filelength) {
00282                 *size = 0;
00283                 return true;
00284         }
00285 
00286         if (!loadedSector) {
00287                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00288                 if(currentSector == 0) {
00289                         /* EOC reached before EOF */
00290                         *size = 0;
00291                         loadedSector = false;
00292                         return true;
00293                 }
00294                 curSectOff = seekpos % myDrive->getSectorSize();
00295                 myDrive->readSector(currentSector, sectorBuffer);
00296                 loadedSector = true;
00297         }
00298 
00299         sizedec = *size;
00300         sizecount = 0;
00301         while(sizedec != 0) {
00302                 if(seekpos >= filelength) {
00303                         *size = sizecount;
00304                         return true; 
00305                 }
00306                 data[sizecount++] = sectorBuffer[curSectOff++];
00307                 seekpos++;
00308                 if(curSectOff >= myDrive->getSectorSize()) {
00309                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00310                         if(currentSector == 0) {
00311                                 /* EOC reached before EOF */
00312                                 //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
00313                                 *size = sizecount;
00314                                 loadedSector = false;
00315                                 return true;
00316                         }
00317                         curSectOff = 0;
00318                         myDrive->readSector(currentSector, sectorBuffer);
00319                         loadedSector = true;
00320                         //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
00321                 }
00322                 --sizedec;
00323         }
00324         *size =sizecount;
00325         return true;
00326 }
00327 
00328 bool fatFile::Write(const Bit8u * data, Bit16u *size) {
00329         if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
00330                 DOS_SetError(DOSERR_ACCESS_DENIED);
00331                 return false;
00332         }
00333 
00334     direntry tmpentry = {};
00335         Bit16u sizedec, sizecount;
00336         sizedec = *size;
00337         sizecount = 0;
00338 
00339         if(seekpos < filelength && *size == 0) {
00340                 /* Truncate file to current position */
00341                 myDrive->deleteClustChain(firstCluster, seekpos);
00342                 filelength = seekpos;
00343                 if (filelength == 0) firstCluster = 0; /* A file of length zero has a starting cluster of zero as well */
00344                 modified = true;
00345                 goto finalizeWrite;
00346         }
00347 
00348         if(seekpos > filelength) {
00349                 /* Extend file to current position */
00350                 Bit32u clustSize = myDrive->getClusterSize();
00351                 if(filelength == 0) {
00352                         firstCluster = myDrive->getFirstFreeClust();
00353                         if(firstCluster == 0) goto finalizeWrite; // out of space
00354                         myDrive->allocateCluster(firstCluster, 0);
00355                         filelength = clustSize;
00356                 }
00357 
00358                 /* round up */
00359                 filelength += clustSize - 1;
00360                 filelength -= filelength % clustSize;
00361 
00362                 /* add clusters until the file length is correct */
00363                 while(filelength < seekpos) {
00364                         if(myDrive->appendCluster(firstCluster) == 0) goto finalizeWrite; // out of space
00365                         filelength += clustSize;
00366                 }
00367                 assert(filelength < (seekpos+clustSize));
00368 
00369                 /* limit file length to seekpos, then bail out if write count is zero */
00370                 modified = true;
00371                 if(filelength > seekpos) filelength = seekpos;
00372                 if(*size == 0) goto finalizeWrite;
00373         }
00374 
00375         while(sizedec != 0) {
00376                 /* Increase filesize if necessary */
00377                 if(seekpos >= filelength) {
00378                         if(filelength == 0) {
00379                                 firstCluster = myDrive->getFirstFreeClust();
00380                                 if(firstCluster == 0) goto finalizeWrite; // out of space
00381                                 myDrive->allocateCluster(firstCluster, 0);
00382                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00383                                 if (currentSector == 0) {
00384                                         /* I guess allocateCluster() didn't work after all. This check is necessary to prevent
00385                                          * this conditon from treating the BOOT SECTOR as a file. */
00386                                         LOG(LOG_DOSMISC,LOG_WARN)("FAT file write: unable to allocate first cluster, erroring out");
00387                                         goto finalizeWrite;
00388                                 }
00389                                 myDrive->readSector(currentSector, sectorBuffer);
00390                                 loadedSector = true;
00391                         }
00392                         if (!loadedSector) {
00393                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00394                                 if(currentSector == 0) {
00395                                         /* EOC reached before EOF - try to increase file allocation */
00396                                         myDrive->appendCluster(firstCluster);
00397                                         /* Try getting sector again */
00398                                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00399                                         if(currentSector == 0) {
00400                                                 /* No can do. lets give up and go home.  We must be out of room */
00401                                                 goto finalizeWrite;
00402                                         }
00403                                 }
00404                                 curSectOff = seekpos % myDrive->getSectorSize();
00405                                 myDrive->readSector(currentSector, sectorBuffer);
00406                                 loadedSector = true;
00407                         }
00408                         filelength = seekpos+1;
00409                 }
00410                 --sizedec;
00411                 modified = true;
00412                 sectorBuffer[curSectOff++] = data[sizecount++];
00413                 seekpos++;
00414                 if(curSectOff >= myDrive->getSectorSize()) {
00415                         if(loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00416                         loadedSector = false;
00417 
00418                         if (sizedec == 0) goto finalizeWrite;
00419 
00420                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00421                         if(currentSector == 0) {
00422                                 /* EOC reached before EOF - try to increase file allocation */
00423                                 myDrive->appendCluster(firstCluster);
00424                                 /* Try getting sector again */
00425                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00426                                 if(currentSector == 0) {
00427                                         /* No can do. lets give up and go home.  We must be out of room */
00428                                         goto finalizeWrite;
00429                                 }
00430                         }
00431                         curSectOff = 0;
00432                         myDrive->readSector(currentSector, sectorBuffer);
00433                         loadedSector = true;
00434                 }
00435         }
00436         if(curSectOff>0 && loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00437 
00438 finalizeWrite:
00439         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00440         tmpentry.entrysize = filelength;
00441 
00442         if (myDrive->GetBPB().is_fat32())
00443                 tmpentry.SetCluster32(firstCluster);
00444         else
00445                 tmpentry.loFirstClust = (Bit16u)firstCluster;
00446 
00447         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00448 
00449         *size =sizecount;
00450         return true;
00451 }
00452 
00453 bool fatFile::Seek(Bit32u *pos, Bit32u type) {
00454         Bit32s seekto=0;
00455         
00456         switch(type) {
00457                 case DOS_SEEK_SET:
00458                         seekto = (Bit32s)*pos;
00459                         break;
00460                 case DOS_SEEK_CUR:
00461                         /* Is this relative seek signed? */
00462                         seekto = (Bit32s)*pos + (Bit32s)seekpos;
00463                         break;
00464                 case DOS_SEEK_END:
00465                         seekto = (Bit32s)filelength + (Bit32s)*pos;
00466                         break;
00467         }
00468 //      LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
00469 
00470         if(seekto<0) seekto = 0;
00471         seekpos = (Bit32u)seekto;
00472         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00473         if (currentSector == 0) {
00474                 /* not within file size, thus no sector is available */
00475                 loadedSector = false;
00476         } else {
00477                 curSectOff = seekpos % myDrive->getSectorSize();
00478                 myDrive->readSector(currentSector, sectorBuffer);
00479                 loadedSector = true;
00480         }
00481         *pos = seekpos;
00482         return true;
00483 }
00484 
00485 bool fatFile::Close() {
00486         /* Flush buffer */
00487         if (loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00488 
00489     if (modified || newtime) {
00490         direntry tmpentry = {};
00491 
00492         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00493 
00494         if (newtime) {
00495             tmpentry.modTime = time;
00496             tmpentry.modDate = date;
00497         }
00498         else {
00499             Bit16u ct,cd;
00500 
00501             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
00502 
00503             tmpentry.modTime = ct;
00504             tmpentry.modDate = cd;
00505         }
00506 
00507         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00508     }
00509 
00510         return false;
00511 }
00512 
00513 Bit16u fatFile::GetInformation(void) {
00514         return 0;
00515 }
00516 
00517 bool fatFile::UpdateDateTimeFromHost(void) {
00518         return true;
00519 }
00520 
00521 Bit32u fatFile::GetSeekPos() {
00522         return seekpos;
00523 }
00524 
00525 Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
00526         return ((clustNum - 2) * BPB.v.BPB_SecPerClus) + firstDataSector;
00527 }
00528 
00529 Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
00530         Bit32u fatoffset=0;
00531         Bit32u fatsectnum;
00532         Bit32u fatentoff;
00533         Bit32u clustValue=0;
00534 
00535         switch(fattype) {
00536                 case FAT12:
00537                         fatoffset = clustNum + (clustNum / 2);
00538                         break;
00539                 case FAT16:
00540                         fatoffset = clustNum * 2;
00541                         break;
00542                 case FAT32:
00543                         fatoffset = clustNum * 4;
00544                         break;
00545         }
00546         fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff;
00547         fatentoff = fatoffset % BPB.v.BPB_BytsPerSec;
00548 
00549     if (BPB.is_fat32()) {
00550         if (fatsectnum >= (BPB.v.BPB_RsvdSecCnt + BPB.v32.BPB_FATSz32 + partSectOff)) {
00551             LOG(LOG_DOSMISC,LOG_ERROR)("Attempt to read cluster entry from FAT that out of range (outside the FAT table) cluster %u",(unsigned int)clustNum);
00552             return 0;
00553         }
00554     }
00555     else {
00556         if (fatsectnum >= (BPB.v.BPB_RsvdSecCnt + BPB.v.BPB_FATSz16 + partSectOff)) {
00557             LOG(LOG_DOSMISC,LOG_ERROR)("Attempt to read cluster entry from FAT that out of range (outside the FAT table) cluster %u",(unsigned int)clustNum);
00558             return 0;
00559         }
00560     }
00561 
00562     assert((BPB.v.BPB_BytsPerSec * (Bitu)2) <= sizeof(fatSectBuffer));
00563 
00564         if(curFatSect != fatsectnum) {
00565                 /* Load two sectors at once for FAT12 */
00566                 readSector(fatsectnum, &fatSectBuffer[0]);
00567                 if (fattype==FAT12)
00568                         readSector(fatsectnum+1, &fatSectBuffer[BPB.v.BPB_BytsPerSec]);
00569                 curFatSect = fatsectnum;
00570         }
00571 
00572         switch(fattype) {
00573                 case FAT12:
00574                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00575                         if(clustNum & 0x1) {
00576                                 clustValue >>= 4;
00577                         } else {
00578                                 clustValue &= 0xfff;
00579                         }
00580                         break;
00581                 case FAT16:
00582                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00583                         break;
00584                 case FAT32:
00585                         clustValue = *((Bit32u *)&fatSectBuffer[fatentoff]) & 0x0FFFFFFFul; /* Well, actually it's FAT28. Upper 4 bits are "reserved". */
00586                         break;
00587         }
00588 
00589         return clustValue;
00590 }
00591 
00592 void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
00593         Bit32u fatoffset=0;
00594         Bit32u fatsectnum;
00595         Bit32u fatentoff;
00596 
00597         switch(fattype) {
00598                 case FAT12:
00599                         fatoffset = clustNum + (clustNum / 2);
00600                         break;
00601                 case FAT16:
00602                         fatoffset = clustNum * 2;
00603                         break;
00604                 case FAT32:
00605                         fatoffset = clustNum * 4;
00606                         break;
00607         }
00608         fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff;
00609         fatentoff = fatoffset % BPB.v.BPB_BytsPerSec;
00610 
00611     if (BPB.is_fat32()) {
00612         if (fatsectnum >= (BPB.v.BPB_RsvdSecCnt + BPB.v32.BPB_FATSz32 + partSectOff)) {
00613             LOG(LOG_DOSMISC,LOG_ERROR)("Attempt to write cluster entry from FAT that out of range (outside the FAT table) cluster %u",(unsigned int)clustNum);
00614             return;
00615         }
00616     }
00617     else {
00618         if (fatsectnum >= (BPB.v.BPB_RsvdSecCnt + BPB.v.BPB_FATSz16 + partSectOff)) {
00619             LOG(LOG_DOSMISC,LOG_ERROR)("Attempt to write cluster entry from FAT that out of range (outside the FAT table) cluster %u",(unsigned int)clustNum);
00620             return;
00621         }
00622     }
00623 
00624     assert((BPB.v.BPB_BytsPerSec * (Bitu)2) <= sizeof(fatSectBuffer));
00625 
00626         if(curFatSect != fatsectnum) {
00627                 /* Load two sectors at once for FAT12 */
00628                 readSector(fatsectnum, &fatSectBuffer[0]);
00629                 if (fattype==FAT12)
00630                         readSector(fatsectnum+1, &fatSectBuffer[BPB.v.BPB_BytsPerSec]);
00631                 curFatSect = fatsectnum;
00632         }
00633 
00634         switch(fattype) {
00635                 case FAT12: {
00636                         Bit16u tmpValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00637                         if(clustNum & 0x1) {
00638                                 clustValue &= 0xfff;
00639                                 clustValue <<= 4;
00640                                 tmpValue &= 0xf;
00641                                 tmpValue |= (Bit16u)clustValue;
00642 
00643                         } else {
00644                                 clustValue &= 0xfff;
00645                                 tmpValue &= 0xf000;
00646                                 tmpValue |= (Bit16u)clustValue;
00647                         }
00648                         *((Bit16u *)&fatSectBuffer[fatentoff]) = tmpValue;
00649                         break;
00650                         }
00651                 case FAT16:
00652                         *((Bit16u *)&fatSectBuffer[fatentoff]) = (Bit16u)clustValue;
00653                         break;
00654                 case FAT32:
00655                         *((Bit32u *)&fatSectBuffer[fatentoff]) = clustValue;
00656                         break;
00657         }
00658         for(unsigned int fc=0;fc<BPB.v.BPB_NumFATs;fc++) {
00659                 writeSector(fatsectnum + (fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[0]);
00660                 if (fattype==FAT12) {
00661                         if (fatentoff >= (BPB.v.BPB_BytsPerSec-1U))
00662                                 writeSector(fatsectnum+1u+(fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[BPB.v.BPB_BytsPerSec]);
00663                 }
00664         }
00665 }
00666 
00667 bool fatDrive::getEntryName(const char *fullname, char *entname) {
00668         char dirtoken[DOS_PATHLENGTH];
00669 
00670         char * findDir;
00671         char * findFile;
00672         strcpy(dirtoken,fullname);
00673 
00674         //LOG_MSG("Testing for filename %s", fullname);
00675         findDir = strtok(dirtoken,"\\");
00676         if (findDir==NULL) {
00677                 return true;    // root always exists
00678         }
00679         findFile = findDir;
00680         while(findDir != NULL) {
00681                 findFile = findDir;
00682                 findDir = strtok(NULL,"\\");
00683         }
00684         int j=0;
00685         for (int i=0; i<(int)strlen(findFile); i++)
00686                 if (findFile[i]!=' '&&findFile[i]!='"'&&findFile[i]!='+'&&findFile[i]!='='&&findFile[i]!=','&&findFile[i]!=';'&&findFile[i]!=':'&&findFile[i]!='<'&&findFile[i]!='>'&&findFile[i]!='['&&findFile[i]!=']'&&findFile[i]!='|'&&findFile[i]!='?'&&findFile[i]!='*') findFile[j++]=findFile[i];
00687         findFile[j]=0;
00688         if (strlen(findFile)>12)
00689                 strncpy(entname, findFile, 12);
00690         else
00691                 strcpy(entname, findFile);
00692         upcase(entname);
00693         return true;
00694 }
00695 
00696 void fatDrive::UpdateBootVolumeLabel(const char *label) {
00697     FAT_BootSector bootbuffer = {};
00698 
00699     if (BPB.v.BPB_BootSig == 0x28 || BPB.v.BPB_BootSig == 0x29) {
00700         unsigned int i = 0;
00701 
00702         loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
00703 
00704         while (i < 11 && *label != 0) bootbuffer.bpb.v.BPB_VolLab[i++] = toupper(*label++);
00705         while (i < 11)                bootbuffer.bpb.v.BPB_VolLab[i++] = ' ';
00706 
00707         loadedDisk->Write_AbsoluteSector(0+partSectOff,&bootbuffer);
00708     }
00709 }
00710 
00711 void fatDrive::SetLabel(const char *label, bool /*iscdrom*/, bool /*updatable*/) {
00712         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
00713         Bit32u dirClustNumber;
00714         Bit32u logentsector; /* Logical entry sector */
00715         Bit32u entryoffset;  /* Index offset within sector */
00716         Bit32u tmpsector;
00717         Bit16u dirPos = 0;
00718 
00719         size_t dirent_per_sector = getSectSize() / sizeof(direntry);
00720         assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
00721         assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
00722 
00723         if(!getDirClustNum("\\", &dirClustNumber, false))
00724                 return;
00725 
00726 nextfile:
00727         logentsector = (Bit32u)((size_t)dirPos / dirent_per_sector);
00728         entryoffset = (Bit32u)((size_t)dirPos % dirent_per_sector);
00729 
00730         if(dirClustNumber==0) {
00731                 assert(!BPB.is_fat32());
00732                 if(dirPos >= BPB.v.BPB_RootEntCnt) return;
00733                 tmpsector = firstRootDirSect+logentsector;
00734         } else {
00735                 /* A zero sector number can't happen */
00736                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
00737                 /* A zero sector number can't happen - we need to allocate more room for this directory*/
00738                 if(tmpsector == 0) {
00739                         if (*label == 0) return; // removing volume label, so stop now
00740                         Bit32u newClust;
00741                         newClust = appendCluster(dirClustNumber);
00742                         if(newClust == 0) return;
00743                         zeroOutCluster(newClust);
00744                         /* Try again to get tmpsector */
00745                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
00746                         if(tmpsector == 0) return; /* Give up if still can't get more room for directory */
00747                 }
00748                 readSector(tmpsector,sectbuf);
00749         }
00750         readSector(tmpsector,sectbuf);
00751         dirPos++;
00752 
00753         if (dos.version.major >= 7 || uselfn) {
00754                 /* skip LFN entries */
00755                 if ((sectbuf[entryoffset].attrib & 0x3F) == 0x0F)
00756                         goto nextfile;
00757         }
00758 
00759         if (*label != 0) {
00760                 /* adding a volume label */
00761                 if (sectbuf[entryoffset].entryname[0] == 0x00 ||
00762                         sectbuf[entryoffset].entryname[0] == 0xE5) {
00763                         memset(&sectbuf[entryoffset],0,sizeof(sectbuf[entryoffset]));
00764                         sectbuf[entryoffset].attrib = DOS_ATTR_VOLUME;
00765                         {
00766                                 unsigned int j = 0;
00767                                 const char *s = label;
00768                                 while (j < 11 && *s != 0) sectbuf[entryoffset].entryname[j++] = toupper(*s++);
00769                                 while (j < 11)            sectbuf[entryoffset].entryname[j++] = ' ';
00770                         }
00771                         writeSector(tmpsector,sectbuf);
00772                         labelCache.SetLabel(label, false, true);
00773                         UpdateBootVolumeLabel(label);
00774                         return;
00775                 }
00776         }
00777         else {
00778                 if (sectbuf[entryoffset].entryname[0] == 0x00)
00779                         return;
00780                 if (sectbuf[entryoffset].entryname[0] == 0xe5)
00781                         goto nextfile;
00782 
00783                 if (sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME) {
00784                         /* TODO: There needs to be a way for FCB delete to erase the volume label by name instead
00785                          *       of just picking the first one */
00786                         /* found one */
00787                         sectbuf[entryoffset].entryname[0] = 0xe5;
00788                         writeSector(tmpsector,sectbuf);
00789                         labelCache.SetLabel("", false, true);
00790                         UpdateBootVolumeLabel("NO NAME");
00791                         return;
00792                 }
00793         }
00794 
00795         goto nextfile;
00796 }
00797 
00798 /* NTS: This function normally will only return files. Every element of the path that is a directory is entered into.
00799  *      If every element is a directory, then this code will fail to locate anything.
00800  *
00801  *      If dirOk is set, and all path elements are directories, it will stop at the last one and look it up as if a file.
00802  *      The purpose is to clean up this FAT driver by eliminating all the ridiculous "look up getFileDirEntry but if it fails
00803  *      do a whole different code path that looks it up as if directory" copy-pasta in this code that complicates some functions
00804  *      like the Rename() method.
00805  *
00806  *      useEntry is filled with the SFN direntry of the first search result. dirClust is filled in with the starting cluster of
00807  *      the parent directory. Note that even if dirOk is set and the result is a directory, dirClust is the parent directory of
00808  *      that directory. subEntry is the dirent index into the directory.
00809  *
00810  *      As a side effect of using FindNextInternal, variable lfnRange will be either cleared or filled in with the subEntry range
00811  *      of dirents that contain the LFN entries (needed for deletion, renaming, rmdir, etc). Not all paths set or clear it, so
00812  *      first call the clear() method before calling. After the call, copy off the value because the next call to FindNextInternal
00813  *      by any part of this code will obliterate the result with a new result. */
00814 bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry,bool dirOk) {
00815         size_t len = strlen(filename);
00816         char dirtoken[DOS_PATHLENGTH];
00817         Bit32u currentClust = 0; /* FAT12/FAT16 root directory */
00818 
00819         direntry foundEntry;
00820         char * findDir;
00821         char * findFile;
00822         strcpy(dirtoken,filename);
00823         findFile=dirtoken;
00824 
00825         if (BPB.is_fat32()) {
00826                 /* Set to FAT32 root directory */
00827                 currentClust = BPB.v32.BPB_RootClus;
00828         }
00829 
00830         int fbak=lfn_filefind_handle;
00831         lfn_filefind_handle=uselfn?LFN_FILEFIND_IMG:LFN_FILEFIND_NONE;
00832         /* Skip if testing in root directory */
00833         if ((len>0) && (filename[len-1]!='\\')) {
00834                 //LOG_MSG("Testing for filename %s", filename);
00835                 findDir = strtok(dirtoken,"\\");
00836                 findFile = findDir;
00837                 while(findDir != NULL) {
00838                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00839                         imgDTA->SetDirID(0);
00840                         
00841                         findFile = findDir;
00842                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
00843                         else {
00844                                 //Found something. See if it's a directory (findfirst always finds regular files)
00845                 char find_name[DOS_NAMELENGTH_ASCII],lfind_name[LFN_NAMELENGTH];
00846                 Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00847                 imgDTA->GetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr);
00848                                 if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
00849 
00850                                 char * findNext;
00851                                 findNext = strtok(NULL,"\\");
00852                                 if (findNext == NULL && dirOk) break; /* dirOk means that if the last element is a directory, then refer to the directory itself */
00853                                 findDir = findNext;
00854                         }
00855 
00856                         if (BPB.is_fat32())
00857                                 currentClust = foundEntry.Cluster32();
00858                         else
00859                                 currentClust = foundEntry.loFirstClust;
00860                 }
00861         } else {
00862                 /* Set to root directory */
00863         }
00864 
00865         /* Search found directory for our file */
00866         imgDTA->SetupSearch(0,0x7 | (dirOk ? DOS_ATTR_DIRECTORY : 0),findFile);
00867         imgDTA->SetDirID(0);
00868         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {lfn_filefind_handle=fbak;return false;}
00869         lfn_filefind_handle=fbak;
00870 
00871         memcpy(useEntry, &foundEntry, sizeof(direntry));
00872         *dirClust = (Bit32u)currentClust;
00873         *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
00874         return true;
00875 }
00876 
00877 bool fatDrive::getDirClustNum(const char *dir, Bit32u *clustNum, bool parDir) {
00878         Bit32u len = (Bit32u)strlen(dir);
00879         char dirtoken[DOS_PATHLENGTH];
00880         direntry foundEntry;
00881         strcpy(dirtoken,dir);
00882 
00883         int fbak=lfn_filefind_handle;
00884         /* Skip if testing for root directory */
00885         if ((len>0) && (dir[len-1]!='\\')) {
00886                 Bit32u currentClust = 0;
00887 
00888         if (BPB.is_fat32()) {
00889             /* Set to FAT32 root directory */
00890             currentClust = BPB.v32.BPB_RootClus;
00891         }
00892 
00893                 //LOG_MSG("Testing for dir %s", dir);
00894                 char * findDir = strtok(dirtoken,"\\");
00895                 while(findDir != NULL) {
00896                         lfn_filefind_handle=uselfn?LFN_FILEFIND_IMG:LFN_FILEFIND_NONE;
00897                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00898                         imgDTA->SetDirID(0);
00899                         findDir = strtok(NULL,"\\");
00900                         if(parDir && (findDir == NULL)) {lfn_filefind_handle=fbak;break;}
00901 
00902                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
00903                                 lfn_filefind_handle=fbak;
00904                                 return false;
00905                         } else {
00906                 char find_name[DOS_NAMELENGTH_ASCII],lfind_name[LFN_NAMELENGTH];
00907                 Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00908                                 imgDTA->GetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr);
00909                                 lfn_filefind_handle=fbak;
00910                                 if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
00911                         }
00912                         if (BPB.is_fat32())
00913                                 currentClust = foundEntry.Cluster32();
00914                         else
00915                                 currentClust = foundEntry.loFirstClust;
00916                 }
00917                 *clustNum = currentClust;
00918         } else if (BPB.is_fat32()) {
00919                 /* Set to FAT32 root directory */
00920                 *clustNum = BPB.v32.BPB_RootClus;
00921         } else {
00922                 /* Set to root directory */
00923                 *clustNum = 0;
00924         }
00925         return true;
00926 }
00927 
00928 Bit8u fatDrive::readSector(Bit32u sectnum, void * data) {
00929         if (absolute) return Read_AbsoluteSector(sectnum, data);
00930     assert(!IS_PC98_ARCH);
00931         Bit32u cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk;
00932         Bit32u cylinder = sectnum / cylindersize;
00933         sectnum %= cylindersize;
00934         Bit32u head = sectnum / BPB.v.BPB_SecPerTrk;
00935         Bit32u sector = sectnum % BPB.v.BPB_SecPerTrk + 1L;
00936         return loadedDisk->Read_Sector(head, cylinder, sector, data);
00937 }       
00938 
00939 Bit8u fatDrive::writeSector(Bit32u sectnum, void * data) {
00940         if (absolute) return Write_AbsoluteSector(sectnum, data);
00941     assert(!IS_PC98_ARCH);
00942         Bit32u cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk;
00943         Bit32u cylinder = sectnum / cylindersize;
00944         sectnum %= cylindersize;
00945         Bit32u head = sectnum / BPB.v.BPB_SecPerTrk;
00946         Bit32u sector = sectnum % BPB.v.BPB_SecPerTrk + 1L;
00947         return loadedDisk->Write_Sector(head, cylinder, sector, data);
00948 }
00949 
00950 Bit32u fatDrive::getSectorSize(void) {
00951         return BPB.v.BPB_BytsPerSec;
00952 }
00953 
00954 Bit32u fatDrive::getClusterSize(void) {
00955         return (unsigned int)BPB.v.BPB_SecPerClus * (unsigned int)BPB.v.BPB_BytsPerSec;
00956 }
00957 
00958 Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
00959         return  getAbsoluteSectFromChain(startClustNum, bytePos / BPB.v.BPB_BytsPerSec);
00960 }
00961 
00962 Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
00963         Bit32s skipClust = (Bit32s)(logicalSector / BPB.v.BPB_SecPerClus);
00964         Bit32u sectClust = (Bit32u)(logicalSector % BPB.v.BPB_SecPerClus);
00965 
00966         /* startClustNum == 0 means the file is (likely) zero length and has no allocation chain yet.
00967          * Nothing to map. Without this check, this code would permit the FAT file reader/writer to
00968          * treat the ROOT DIRECTORY as a file (with disasterous results)
00969          *
00970          * [https://github.com/joncampbell123/dosbox-x/issues/1517] */
00971         if (startClustNum == 0) return 0;
00972 
00973         Bit32u currentClust = startClustNum;
00974 
00975         while(skipClust!=0) {
00976                 bool isEOF = false;
00977                 Bit32u testvalue = getClusterValue(currentClust);
00978                 if(testvalue == 0) {
00979                         /* What the crap?  Cluster is already empty - BAIL! */
00980                         LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain and cluster value at the end is zero.");
00981                         return 0;
00982                 }
00983                 switch(fattype) {
00984                         case FAT12:
00985                                 if(testvalue >= 0xff8) isEOF = true;
00986                                 break;
00987                         case FAT16:
00988                                 if(testvalue >= 0xfff8) isEOF = true;
00989                                 break;
00990                         case FAT32:
00991                                 if(testvalue >= 0x0ffffff8) isEOF = true; /* FAT32 is really FAT28 with 4 reserved bits */
00992                                 break;
00993                 }
00994                 if((isEOF) && (skipClust>=1)) {
00995                         //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
00996                         if (skipClust == 1 && fattype == FAT12) {
00997                                 //break;
00998                                 LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain reached, but maybe good afterall ?");
00999                         }
01000                         return 0;
01001                 }
01002                 currentClust = testvalue;
01003                 --skipClust;
01004         }
01005 
01006         /* this should not happen! */
01007         assert(currentClust != 0);
01008 
01009         return (getClustFirstSect(currentClust) + sectClust);
01010 }
01011 
01012 void fatDrive::deleteClustChain(Bit32u startCluster, Bit32u bytePos) {
01013         if (startCluster < 2) return; /* do not corrupt the FAT media ID. The file has no chain. Do nothing. */
01014 
01015         Bit32u clustSize = getClusterSize();
01016         Bit32u endClust = (bytePos + clustSize - 1) / clustSize;
01017         Bit32u countClust = 1;
01018 
01019         Bit32u currentClust = startCluster;
01020         Bit32u eofClust = 0;
01021 
01022         switch(fattype) {
01023                 case FAT12:
01024                         eofClust = 0xff8;
01025                         break;
01026                 case FAT16:
01027                         eofClust = 0xfff8;
01028                         break;
01029                 case FAT32:
01030                         eofClust = 0x0ffffff8;
01031                         break;
01032                 default:
01033                         abort();
01034                         break;
01035         }
01036 
01037         /* chain preservation */
01038         while (countClust < endClust) {
01039                 Bit32u testvalue = getClusterValue(currentClust);
01040                 if (testvalue == 0) {
01041                         LOG(LOG_DOSMISC,LOG_WARN)("deleteClusterChain startCluster=%u countClust=%u endClust=%u currentClust=%u testvalue=%u eof=%u unexpected zero cluster value in FAT table",
01042                                         (unsigned int)startCluster,(unsigned int)countClust,(unsigned int)endClust,(unsigned int)currentClust,(unsigned int)testvalue,(unsigned int)eofClust);
01043                         return;
01044                 }
01045                 else if (testvalue >= eofClust)
01046                         return; /* Allocation chain is already shorter than intended */
01047 
01048                 currentClust = testvalue;
01049                 countClust++;
01050         }
01051 
01052         /* cut the chain here, write EOF.
01053          * This condition will NOT occur if bytePos == 0 (i.e. complete file truncation)
01054          * because countClust == 1 and endClust == 0 */
01055         if (countClust == endClust) {
01056                 Bit32u testvalue = getClusterValue(currentClust);
01057                 if (testvalue == 0) {
01058                         LOG(LOG_DOSMISC,LOG_WARN)("deleteClusterChain startCluster=%u countClust=%u endClust=%u currentClust=%u testvalue=%u eof=%u unexpected zero cluster value in FAT table",
01059                                         (unsigned int)startCluster,(unsigned int)countClust,(unsigned int)endClust,(unsigned int)currentClust,(unsigned int)testvalue,(unsigned int)eofClust);
01060                         return;
01061                 }
01062                 else if (testvalue >= eofClust)
01063                         return; /* No need to write EOF because EOF is already there */
01064 
01065                 setClusterValue(currentClust,eofClust);
01066                 currentClust = testvalue;
01067                 countClust++;
01068         }
01069 
01070         /* then run the rest of the chain and zero it out */
01071         while (1) {
01072                 Bit32u testvalue = getClusterValue(currentClust);
01073                 if (testvalue == 0) {
01074                         LOG(LOG_DOSMISC,LOG_WARN)("deleteClusterChain startCluster=%u countClust=%u endClust=%u currentClust=%u testvalue=%u eof=%u unexpected zero cluster value in FAT table",
01075                                         (unsigned int)startCluster,(unsigned int)countClust,(unsigned int)endClust,(unsigned int)currentClust,(unsigned int)testvalue,(unsigned int)eofClust);
01076                         return;
01077                 }
01078 
01079                 setClusterValue(currentClust,0);
01080                 currentClust = testvalue;
01081                 countClust++;
01082 
01083                 /* this follows setClusterValue() to make sure the end of the chain is zeroed too */
01084                 if (testvalue >= eofClust)
01085                         return;
01086         }
01087 }
01088 
01089 Bit32u fatDrive::appendCluster(Bit32u startCluster) {
01090         if (startCluster < 2) return 0; /* do not corrupt the FAT media ID. The file has no chain. Do nothing. */
01091 
01092         Bit32u currentClust = startCluster;
01093         Bit32u eofClust = 0;
01094 
01095         switch(fattype) {
01096                 case FAT12:
01097                         eofClust = 0xff8;
01098                         break;
01099                 case FAT16:
01100                         eofClust = 0xfff8;
01101                         break;
01102                 case FAT32:
01103                         eofClust = 0x0ffffff8;
01104                         break;
01105                 default:
01106                         abort();
01107                         break;
01108         }
01109 
01110         while (1) {
01111                 Bit32u testvalue = getClusterValue(currentClust);
01112                 if (testvalue == 0) {
01113                         LOG(LOG_DOSMISC,LOG_WARN)("appendCluster currentClust=%u testvalue=%u eof=%u unexpected zero cluster value in FAT table",
01114                                         (unsigned int)currentClust,(unsigned int)testvalue,(unsigned int)eofClust);
01115                         return 0;
01116                 }
01117                 else if (testvalue >= eofClust) {
01118                         break; /* found it! */
01119                 }
01120 
01121                 currentClust = testvalue;
01122         }
01123 
01124         Bit32u newClust = getFirstFreeClust();
01125         if(newClust == 0) return 0; /* Drive is full */
01126 
01127         if(!allocateCluster(newClust, currentClust)) return 0;
01128 
01129         zeroOutCluster(newClust);
01130 
01131         return newClust;
01132 }
01133 
01134 bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
01135 
01136         /* Can't allocate cluster #0 */
01137         if(useCluster == 0) return false;
01138 
01139         if(prevCluster != 0) {
01140                 /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
01141                 if(!getClusterValue(prevCluster)) return false;
01142 
01143                 /* Point cluster to new cluster in chain */
01144                 setClusterValue(prevCluster, useCluster);
01145                 //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
01146         } 
01147 
01148         switch(fattype) {
01149                 case FAT12:
01150                         setClusterValue(useCluster, 0xfff);
01151                         break;
01152                 case FAT16:
01153                         setClusterValue(useCluster, 0xffff);
01154                         break;
01155                 case FAT32:
01156                         setClusterValue(useCluster, 0x0fffffff);
01157                         break;
01158         }
01159         return true;
01160 }
01161 
01162 fatDrive::~fatDrive() {
01163         if (loadedDisk) {
01164                 loadedDisk->Release();
01165                 loadedDisk = NULL;
01166         }
01167 }
01168 
01169 /* PC-98 IPL1 partition table entry.
01170  * Taken from GNU Parted source code.
01171  * Maximum 16 entries. */
01172 #pragma pack(push,1)
01173 struct _PC98RawPartition {
01174         uint8_t         mid;            /* 0x80 - boot */
01175         uint8_t         sid;            /* 0x80 - active */
01176         uint8_t         dum1;           /* dummy for padding */
01177         uint8_t         dum2;           /* dummy for padding */
01178         uint8_t         ipl_sect;       /* IPL sector */
01179         uint8_t         ipl_head;       /* IPL head */
01180         uint16_t        ipl_cyl;        /* IPL cylinder */
01181         uint8_t         sector;         /* starting sector */
01182         uint8_t         head;           /* starting head */
01183         uint16_t        cyl;            /* starting cylinder */
01184         uint8_t         end_sector;     /* end sector */
01185         uint8_t         end_head;       /* end head */
01186         uint16_t        end_cyl;        /* end cylinder */
01187         char            name[16];
01188 };
01189 #pragma pack(pop)
01190 
01191 fatDrive::fatDrive(const char* sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, std::vector<std::string>& options) {
01192         FILE *diskfile;
01193         Bit32u filesize;
01194         unsigned char bootcode[256];
01195 
01196         if(imgDTASeg == 0) {
01197                 imgDTASeg = DOS_GetMemory(4,"imgDTASeg");
01198                 imgDTAPtr = RealMake(imgDTASeg, 0);
01199                 imgDTA    = new DOS_DTA(imgDTAPtr);
01200         }
01201 
01202         readonly = wpcolon&&strlen(sysFilename)>1&&sysFilename[0]==':';
01203         diskfile = fopen64(readonly?sysFilename+1:sysFilename, readonly?"rb":"rb+");
01204         if(!diskfile) {created_successfully = false;return;}
01205 
01206     // all disk I/O is in sector-sized blocks.
01207     // modern OSes have good caching.
01208     // there are plenty of cases where this code aborts, exits, or re-execs itself (such as reboot)
01209     // where stdio buffering can cause loss of data.
01210     setbuf(diskfile,NULL);
01211 
01212         QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(diskfile);
01213 
01214         if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)){
01215                 Bit32u cluster_size = 1u << qcow2_header.cluster_bits;
01216                 if ((bytesector < 512) || ((cluster_size % bytesector) != 0)){
01217                         created_successfully = false;
01218                         return;
01219                 }
01220                 filesize = (Bit32u)(qcow2_header.size / 1024L);
01221                 loadedDisk = new QCow2Disk(qcow2_header, diskfile, (Bit8u *)sysFilename, filesize, bytesector, (filesize > 2880));
01222         }
01223         else{
01224                 fseeko64(diskfile, 0L, SEEK_SET);
01225         assert(sizeof(bootcode) >= 256);
01226         size_t readResult = fread(bootcode,256,1,diskfile); // look for magic signatures
01227         if (readResult != 1) {
01228             LOG(LOG_IO, LOG_ERROR) ("Reading error in fatDrive constructor\n");
01229             return;
01230         }
01231 
01232         const char *ext = strrchr(sysFilename,'.'), *fname=readonly?sysFilename+1:sysFilename;
01233 
01234         if (ext != NULL && !strcasecmp(ext, ".d88")) {
01235             fseeko64(diskfile, 0L, SEEK_END);
01236             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
01237             loadedDisk = new imageDiskD88(diskfile, (Bit8u *)fname, filesize, (filesize > 2880));
01238         }
01239         else if (!memcmp(bootcode,"VFD1.",5)) { /* FDD files */
01240             fseeko64(diskfile, 0L, SEEK_END);
01241             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
01242             loadedDisk = new imageDiskVFD(diskfile, (Bit8u *)fname, filesize, (filesize > 2880));
01243         }
01244         else if (!memcmp(bootcode,"T98FDDIMAGE.R0\0\0",16)) {
01245             fseeko64(diskfile, 0L, SEEK_END);
01246             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
01247             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)fname, filesize, (filesize > 2880), 0);
01248         }
01249         else if (!memcmp(bootcode,"T98FDDIMAGE.R1\0\0",16)) {
01250             fseeko64(diskfile, 0L, SEEK_END);
01251             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
01252             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)fname, filesize, (filesize > 2880), 1);
01253         }
01254         else {
01255             fseeko64(diskfile, 0L, SEEK_END);
01256             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
01257             loadedDisk = new imageDisk(diskfile, (Bit8u *)fname, filesize, (filesize > 2880));
01258         }
01259         }
01260 
01261     fatDriveInit(sysFilename, bytesector, cylsector, headscyl, cylinders, filesize, options);
01262 }
01263 
01264 fatDrive::fatDrive(imageDisk *sourceLoadedDisk, std::vector<std::string> &options) {
01265         if (sourceLoadedDisk == 0) {
01266                 created_successfully = false;
01267                 return;
01268         }
01269         created_successfully = true;
01270         
01271         if(imgDTASeg == 0) {
01272                 imgDTASeg = DOS_GetMemory(4,"imgDTASeg");
01273                 imgDTAPtr = RealMake(imgDTASeg, 0);
01274                 imgDTA    = new DOS_DTA(imgDTAPtr);
01275         }
01276 
01277     loadedDisk = sourceLoadedDisk;
01278 
01279     fatDriveInit("", loadedDisk->sector_size, loadedDisk->sectors, loadedDisk->heads, loadedDisk->cylinders, loadedDisk->diskSizeK, options);
01280 }
01281 
01282 Bit8u fatDrive::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01283     if (loadedDisk != NULL) {
01284         /* this will only work if the logical sector size is larger than the disk sector size */
01285         const unsigned int lsz = loadedDisk->getSectSize();
01286         unsigned int c = sector_size / lsz;
01287 
01288         if (c != 0 && (sector_size % lsz) == 0) {
01289             Bit32u ssect = sectnum * c;
01290 
01291             while (c-- != 0) {
01292                 if (loadedDisk->Read_AbsoluteSector(ssect++,data) != 0)
01293                     return 0x05;
01294 
01295                 data = (void*)((char*)data + lsz);
01296             }
01297 
01298             return 0;
01299         }
01300     }
01301 
01302     return 0x05;
01303 }
01304 
01305 Bit8u fatDrive::Write_AbsoluteSector(Bit32u sectnum, void * data) {
01306     if (loadedDisk != NULL) {
01307         /* this will only work if the logical sector size is larger than the disk sector size */
01308         const unsigned int lsz = loadedDisk->getSectSize();
01309         unsigned int c = sector_size / lsz;
01310 
01311         if (c != 0 && (sector_size % lsz) == 0) {
01312             Bit32u ssect = sectnum * c;
01313 
01314             while (c-- != 0) {
01315                 if (loadedDisk->Write_AbsoluteSector(ssect++,data) != 0)
01316                     return 0x05;
01317 
01318                 data = (void*)((char*)data + lsz);
01319             }
01320 
01321             return 0;
01322         }
01323     }
01324 
01325     return 0x05;
01326 }
01327 
01328 Bit32u fatDrive::getSectSize(void) {
01329     return sector_size;
01330 }
01331 
01332 void fatDrive::UpdateDPB(unsigned char dos_drive) {
01333     PhysPt ptr = DOS_Get_DPB(dos_drive);
01334     if (ptr != PhysPt(0)) {
01335         mem_writew(ptr+0x02,BPB.v.BPB_BytsPerSec);                  // +2 = bytes per sector
01336         mem_writeb(ptr+0x04,BPB.v.BPB_SecPerClus - 1);              // +4 = highest sector within a cluster
01337         mem_writeb(ptr+0x05,bitop::log2(BPB.v.BPB_SecPerClus));     // +5 = shift count to convert clusters to sectors
01338         mem_writew(ptr+0x06,BPB.v.BPB_RsvdSecCnt);                  // +6 = number of reserved sectors at start of partition
01339         mem_writeb(ptr+0x08,BPB.v.BPB_NumFATs);                     // +8 = number of FATs (file allocation tables)
01340         mem_writew(ptr+0x09,BPB.v.BPB_RootEntCnt);                  // +9 = number of root directory entries
01341         mem_writew(ptr+0x0B,(uint16_t)(firstDataSector-partSectOff));// +11 = number of first sector containing user data
01342 
01343         if (BPB.is_fat32())
01344             mem_writew(ptr+0x0D,0);                                 // Windows 98 behavior
01345         else
01346             mem_writew(ptr+0x0D,(uint16_t)CountOfClusters + 1);     // +13 = highest cluster number
01347 
01348         mem_writew(ptr+0x0F,(uint16_t)BPB.v.BPB_FATSz16);           // +15 = sectors per FAT
01349 
01350         if (BPB.is_fat32())
01351             mem_writew(ptr+0x11,0xFFFF);                            // Windows 98 behavior
01352         else
01353             mem_writew(ptr+0x11,(uint16_t)(firstRootDirSect-partSectOff));// +17 = sector number of first directory sector
01354 
01355         mem_writed(ptr+0x13,0xFFFFFFFF);                            // +19 = address of device driver header (NOT IMPLEMENTED) Windows 98 behavior
01356         mem_writeb(ptr+0x17,GetMediaByte());                        // +23 = media ID byte
01357         mem_writeb(ptr+0x18,0x00);                                  // +24 = disk accessed
01358         mem_writew(ptr+0x1F,0xFFFF);                                // +31 = number of free clusters or 0xFFFF if unknown
01359         // other fields, not implemented
01360     }
01361 }
01362 
01363 void fatDrive::fatDriveInit(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit64u filesize, const std::vector<std::string> &options) {
01364         Bit32u startSector = 0,countSector = 0;
01365         bool pc98_512_to_1024_allow = false;
01366     int opt_partition_index = -1;
01367         bool is_hdd = (filesize > 2880);
01368         struct partTable mbrData;
01369 
01370         req_ver_major = req_ver_minor = 0;
01371 
01372         if(!loadedDisk) {
01373                 created_successfully = false;
01374                 return;
01375         }
01376 
01377     for (const auto &opt : options) {
01378         size_t equ = opt.find_first_of('=');
01379         std::string name,value;
01380 
01381         if (equ != std::string::npos) {
01382             name = opt.substr(0,equ);
01383             value = opt.substr(equ+1);
01384         }
01385         else {
01386             name = opt;
01387             value.clear();
01388         }
01389 
01390         if (name == "partidx") {
01391             if (!value.empty())
01392                 opt_partition_index = (int)atol(value.c_str());
01393         }
01394         else {
01395             LOG(LOG_MISC,LOG_DEBUG)("FAT: option '%s' = '%s' ignored, unknown",name.c_str(),value.c_str());
01396         }
01397 
01398 //        LOG_MSG("'%s' = '%s'",name.c_str(),value.c_str());
01399     }
01400 
01401         loadedDisk->Addref();
01402 
01403     {
01404         FAT_BootSector bootbuffer = {};
01405 
01406         if (loadedDisk->getSectSize() > sizeof(bootbuffer)) {
01407             LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize());
01408             created_successfully = false;
01409             return;
01410         }
01411 
01412         if(is_hdd) {
01413             /* Set user specified harddrive parameters */
01414             if (headscyl > 0 && cylinders > 0 && cylsector > 0 && bytesector > 0)
01415                 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
01416 
01417             if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
01418                 created_successfully = false;
01419                 LOG_MSG("No geometry");
01420                 return;
01421             }
01422 
01423             loadedDisk->Read_Sector(0,0,1,&mbrData);
01424 
01425             if(mbrData.magic1!= 0x55 || mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
01426 
01427             startSector = 0;
01428 
01429             /* PC-98 bootloader support.
01430              * These can be identified by the "IPL1" in the boot sector.
01431              * These boot sectors do not have a valid partition table though the code below might
01432              * pick up a false partition #3 with a zero offset. Partition table is in sector 1 */
01433             if (!memcmp(mbrData.booter+4,"IPL1",4)) {
01434                 unsigned char ipltable[SECTOR_SIZE_MAX];
01435                 unsigned int max_entries = (std::min)(16UL,(unsigned long)(loadedDisk->getSectSize() / sizeof(_PC98RawPartition)));
01436                 Bitu i;
01437 
01438                 LOG_MSG("PC-98 IPL1 signature detected");
01439 
01440                 assert(sizeof(_PC98RawPartition) == 32);
01441 
01442                 memset(ipltable,0,sizeof(ipltable));
01443                 loadedDisk->Read_Sector(0,0,2,ipltable);
01444 
01445                 if (opt_partition_index >= 0) {
01446                     /* user knows best! */
01447                     if ((unsigned int)opt_partition_index >= max_entries) {
01448                         LOG_MSG("Partition index out of range");
01449                         created_successfully = false;
01450                         return;
01451                     }
01452 
01453                     i = (unsigned int)opt_partition_index;
01454                     const _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
01455 
01456                     /* unfortunately start and end are in C/H/S geometry, so we have to translate.
01457                      * this is why it matters so much to read the geometry from the HDI header.
01458                      *
01459                      * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
01460                     startSector =
01461                         (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
01462                         (pe->head * loadedDisk->sectors) +
01463                         pe->sector;
01464 
01465                     /* Many HDI images I've encountered so far indicate 512 bytes/sector,
01466                      * but then the FAT filesystem itself indicates 1024 bytes per sector. */
01467                     pc98_512_to_1024_allow = true;
01468 
01469                     {
01470                         /* FIXME: What if the label contains SHIFT-JIS? */
01471                         std::string name = std::string(pe->name,sizeof(pe->name));
01472 
01473                         LOG_MSG("Using IPL1 entry %lu name '%s' which starts at sector %lu",
01474                                 (unsigned long)i,name.c_str(),(unsigned long)startSector);
01475                     }
01476                 }
01477                 else {
01478                     for (i=0;i < max_entries;i++) {
01479                         const _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
01480 
01481                         if (pe->mid == 0 && pe->sid == 0 &&
01482                                 pe->ipl_sect == 0 && pe->ipl_head == 0 && pe->ipl_cyl == 0 &&
01483                                 pe->sector == 0 && pe->head == 0 && pe->cyl == 0 &&
01484                                 pe->end_sector == 0 && pe->end_head == 0 && pe->end_cyl == 0)
01485                             continue; /* unused */
01486 
01487                         /* We're looking for MS-DOS partitions.
01488                          * I've heard that some other OSes were once ported to PC-98, including Windows NT and OS/2,
01489                          * so I would rather not mistake NTFS or HPFS as FAT and cause damage. --J.C.
01490                          * FIXME: Is there a better way? */
01491                         if (!strncasecmp(pe->name,"MS-DOS",6) ||
01492                                 !strncasecmp(pe->name,"MSDOS",5) ||
01493                                 !strncasecmp(pe->name,"Windows",7)) {
01494                             /* unfortunately start and end are in C/H/S geometry, so we have to translate.
01495                              * this is why it matters so much to read the geometry from the HDI header.
01496                              *
01497                              * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
01498                             startSector =
01499                                 (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
01500                                 (pe->head * loadedDisk->sectors) +
01501                                 pe->sector;
01502 
01503                             /* Many HDI images I've encountered so far indicate 512 bytes/sector,
01504                              * but then the FAT filesystem itself indicates 1024 bytes per sector. */
01505                             pc98_512_to_1024_allow = true;
01506 
01507                             {
01508                                 /* FIXME: What if the label contains SHIFT-JIS? */
01509                                 std::string name = std::string(pe->name,sizeof(pe->name));
01510 
01511                                 LOG_MSG("Using IPL1 entry %lu name '%s' which starts at sector %lu",
01512                                         (unsigned long)i,name.c_str(),(unsigned long)startSector);
01513                                 break;
01514                             }
01515                         }
01516                     }
01517                 }
01518 
01519                 if (i == max_entries)
01520                     LOG_MSG("No partitions found in the IPL1 table");
01521             }
01522             else {
01523                 /* IBM PC master boot record search */
01524                 int m=4;
01525 
01526                 if (opt_partition_index >= 0) {
01527                     /* user knows best! */
01528                     if (opt_partition_index >= 4) {
01529                         LOG_MSG("Partition index out of range");
01530                         created_successfully = false;
01531                         return;
01532                     }
01533 
01534                     startSector = mbrData.pentry[m=opt_partition_index].absSectStart;
01535                     countSector = mbrData.pentry[m=opt_partition_index].partSize;
01536                 }
01537                 else {
01538                     for(m=0;m<4;m++) {
01539                         /* Pick the first available partition */
01540                         if (mbrData.pentry[m].parttype == 0x01 || mbrData.pentry[m].parttype == 0x04 || mbrData.pentry[m].parttype == 0x06) {
01541                             LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
01542                             startSector = mbrData.pentry[m].absSectStart;
01543                             countSector = mbrData.pentry[m].partSize;
01544                             break;
01545                         }
01546                         else if (mbrData.pentry[m].parttype == 0x0E/*FAT16B LBA*/) {
01547                                                         if (dos.version.major >= 7) { /* MS-DOS 7.0 or higher */
01548                                                                 LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
01549                                                                 startSector = mbrData.pentry[m].absSectStart;
01550                                                                 countSector = mbrData.pentry[m].partSize;
01551                                                         } else {
01552                                                                 req_ver_major = 7; req_ver_minor = 0;
01553                             }
01554                             break;
01555                         }
01556                         else if (mbrData.pentry[m].parttype == 0x0B || mbrData.pentry[m].parttype == 0x0C) { /* FAT32 types */
01557                                                         if (dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) { /* MS-DOS 7.10 or higher */
01558                                                                 LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
01559                                                                 startSector = mbrData.pentry[m].absSectStart;
01560                                                                 countSector = mbrData.pentry[m].partSize;
01561                                                         } else {
01562                                                                 req_ver_major = 7; req_ver_minor = 10;
01563                             }
01564                             break;
01565                         }
01566                     }
01567                 }
01568 
01569                 if(m==4) LOG_MSG("No good partition found in image.");
01570             }
01571 
01572             partSectOff = startSector;
01573             partSectSize = countSector;
01574         } else {
01575             /* Get floppy disk parameters based on image size */
01576             loadedDisk->Get_Geometry(&headscyl, &cylinders, &cylsector, &bytesector);
01577             /* Floppy disks don't have partitions */
01578             partSectOff = 0;
01579 
01580             if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
01581                 created_successfully = false;
01582                 LOG_MSG("No geometry");
01583                 return;
01584             }
01585         }
01586 
01587         BPB = {};
01588         loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
01589 
01590         /* If the sector is full of 0xF6, the partition is brand new and was just created with Microsoft FDISK.EXE (Windows 98 behavior)
01591          * and therefore there is NO FAT filesystem here. We'll go farther and check if all bytes are just the same. */
01592         {
01593             unsigned int i=1;
01594 
01595             while (i < 128 && ((Bit8u*)(&bootbuffer))[0] == ((Bit8u*)(&bootbuffer))[i]) i++;
01596 
01597             if (i == 128) {
01598                 LOG_MSG("Boot sector appears to have been created by FDISK.EXE but not formatted");
01599                 created_successfully = false;
01600                 return;
01601             }
01602         }
01603 
01604         if (!is_hdd) {
01605             /* Identify floppy format */
01606             if ((bootbuffer.BS_jmpBoot[0] == 0x69 || bootbuffer.BS_jmpBoot[0] == 0xe9 ||
01607                         (bootbuffer.BS_jmpBoot[0] == 0xeb && bootbuffer.BS_jmpBoot[2] == 0x90)) &&
01608                     (bootbuffer.bpb.v.BPB_Media & 0xf0) == 0xf0) {
01609                 /* DOS 2.x or later format, BPB assumed valid */
01610 
01611                 if ((bootbuffer.bpb.v.BPB_Media != 0xf0 && !(bootbuffer.bpb.v.BPB_Media & 0x1)) &&
01612                         (bootbuffer.BS_OEMName[5] != '3' || bootbuffer.BS_OEMName[6] != '.' || bootbuffer.BS_OEMName[7] < '2')) {
01613                     /* Fix pre-DOS 3.2 single-sided floppy */
01614                     bootbuffer.bpb.v.BPB_SecPerClus = 1;
01615                 }
01616             } else {
01617                 /* Read media descriptor in FAT */
01618                 Bit8u sectorBuffer[512];
01619                 loadedDisk->Read_AbsoluteSector(1,&sectorBuffer);
01620                 Bit8u mdesc = sectorBuffer[0];
01621 
01622                 if (mdesc >= 0xf8) {
01623                     /* DOS 1.x format, create BPB for 160kB floppy */
01624                     bootbuffer.bpb.v.BPB_BytsPerSec = 512;
01625                     bootbuffer.bpb.v.BPB_SecPerClus = 1;
01626                     bootbuffer.bpb.v.BPB_RsvdSecCnt = 1;
01627                     bootbuffer.bpb.v.BPB_NumFATs = 2;
01628                     bootbuffer.bpb.v.BPB_RootEntCnt = 64;
01629                     bootbuffer.bpb.v.BPB_TotSec16 = 320;
01630                     bootbuffer.bpb.v.BPB_Media = mdesc;
01631                     bootbuffer.bpb.v.BPB_FATSz16 = 1;
01632                     bootbuffer.bpb.v.BPB_SecPerTrk = 8;
01633                     bootbuffer.bpb.v.BPB_NumHeads = 1;
01634                     bootbuffer.magic1 = 0x55;   // to silence warning
01635                     bootbuffer.magic2 = 0xaa;
01636                     if (!(mdesc & 0x2)) {
01637                         /* Adjust for 9 sectors per track */
01638                         bootbuffer.bpb.v.BPB_TotSec16 = 360;
01639                         bootbuffer.bpb.v.BPB_FATSz16 = 2;
01640                         bootbuffer.bpb.v.BPB_SecPerTrk = 9;
01641                     }
01642                     if (mdesc & 0x1) {
01643                         /* Adjust for 2 sides */
01644                         bootbuffer.bpb.v.BPB_SecPerClus = 2;
01645                         bootbuffer.bpb.v.BPB_RootEntCnt = 112;
01646                         bootbuffer.bpb.v.BPB_TotSec16 *= 2;
01647                         bootbuffer.bpb.v.BPB_NumHeads = 2;
01648                     }
01649                 } else {
01650                     /* Unknown format */
01651                     created_successfully = false;
01652                     return;
01653                 }
01654             }
01655         }
01656 
01657         /* accept BPB.. so far */
01658         BPB = bootbuffer.bpb;
01659 
01660         /* DEBUG */
01661         LOG(LOG_DOSMISC,LOG_DEBUG)("FAT: BPB says %u sectors/track %u heads %u bytes/sector",
01662                 BPB.v.BPB_SecPerTrk,
01663                 BPB.v.BPB_NumHeads,
01664                 BPB.v.BPB_BytsPerSec);
01665 
01666         /* NTS: PC-98 floppies (the 1024 byte/sector format) do not have magic bytes */
01667         if (fatDrive::getSectSize() == 512 && !IS_PC98_ARCH) {
01668             if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
01669                 /* Not a FAT filesystem */
01670                 LOG_MSG("Loaded image has no valid magicnumbers at the end!");
01671                 created_successfully = false;
01672                 return;
01673             }
01674         }
01675     }
01676 
01677     /* NTS: Some HDI images of PC-98 games do in fact have BPB_NumHeads == 0. Some like "Amaranth 5" have BPB_SecPerTrk == 0 too! */
01678     if (!IS_PC98_ARCH) {
01679         /* a clue that we're not really looking at FAT is invalid or weird values in the boot sector */
01680         if (BPB.v.BPB_SecPerTrk == 0 || (BPB.v.BPB_SecPerTrk > ((filesize <= 3000) ? 40 : 255)) ||
01681             (BPB.v.BPB_NumHeads > ((filesize <= 3000) ? 64 : 255))) {
01682             LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem");
01683             created_successfully = false;
01684             return;
01685         }
01686     }
01687 
01688     /* work at this point in logical sectors */
01689         sector_size = loadedDisk->getSectSize();
01690 
01691     /* Many HDI images indicate a disk format of 256 or 512 bytes per sector combined with a FAT filesystem
01692      * that indicates 1024 bytes per sector. */
01693     if (pc98_512_to_1024_allow &&
01694          BPB.v.BPB_BytsPerSec != fatDrive::getSectSize() &&
01695          BPB.v.BPB_BytsPerSec >  fatDrive::getSectSize() &&
01696         (BPB.v.BPB_BytsPerSec %  fatDrive::getSectSize()) == 0) {
01697         unsigned int ratioshift = 1;
01698 
01699         while ((unsigned int)(BPB.v.BPB_BytsPerSec >> ratioshift) > fatDrive::getSectSize())
01700             ratioshift++;
01701 
01702         unsigned int ratio = 1u << ratioshift;
01703 
01704         LOG_MSG("Disk indicates %u bytes/sector, FAT filesystem indicates %u bytes/sector. Ratio=%u:1 shift=%u",
01705                 fatDrive::getSectSize(),BPB.v.BPB_BytsPerSec,ratio,ratioshift);
01706 
01707         if ((unsigned int)(BPB.v.BPB_BytsPerSec >> ratioshift) == fatDrive::getSectSize()) {
01708             assert(ratio >= 2);
01709 
01710             /* we can hack things in place IF the starting sector is an even number */
01711             if ((partSectOff & (ratio - 1)) == 0) {
01712                 partSectOff >>= ratioshift;
01713                 startSector >>= ratioshift;
01714                 sector_size = BPB.v.BPB_BytsPerSec;
01715                 LOG_MSG("Using logical sector size %u",sector_size);
01716             }
01717             else {
01718                 LOG_MSG("However there's nothing I can do, because the partition starts on an odd sector");
01719             }
01720         }
01721     }
01722 
01723         /* Sanity checks */
01724     /* NTS: DOSBox-X *does* support non-standard sector sizes, though not in IBM PC mode and not through INT 13h.
01725      *      In DOSBox-X INT 13h emulation will enforce the standard (512 byte) sector size.
01726      *      In PC-98 mode mounting disk images requires "non-standard" sector sizes because PC-98 floppies (other
01727      *      than ones formatted 1.44MB) generally use 1024 bytes/sector and MAY use 128 or 256 bytes per sector. */
01728     /* NTS: Loosen geometry checks for PC-98 mode, for two reasons. One, is that the geometry check will fail
01729      *      when logical vs physical sector translation is involved, since it is apparently common for PC-98 HDI
01730      *      images to be formatted with 256, 512, 1024, or in rare cases even 2048 bytes per sector, yet the FAT
01731      *      file format will report a sector size that is a power of 2 multiple of the disk sector size. The
01732      *      most common appears to be 512 byte/sector HDI images formatted with 1024 byte/sector FAT filesystems.
01733      *
01734      *      Second, there are some HDI images that are valid yet the FAT filesystem reports a head count of 0
01735      *      for some reason (Touhou Project) */
01736         if ((BPB.v.BPB_SecPerClus == 0) ||
01737                 (BPB.v.BPB_NumFATs == 0) ||
01738                 (BPB.v.BPB_NumHeads == 0 && !IS_PC98_ARCH) ||
01739                 (BPB.v.BPB_NumHeads > headscyl && !IS_PC98_ARCH) ||
01740                 (BPB.v.BPB_SecPerTrk == 0 && !IS_PC98_ARCH) ||
01741                 (BPB.v.BPB_SecPerTrk > cylsector && !IS_PC98_ARCH)) {
01742                 LOG_MSG("Sanity checks failed");
01743                 created_successfully = false;
01744                 return;
01745         }
01746 
01747     /* Sanity check: Root directory count is nonzero if FAT16/FAT12, or is zero if FAT32 */
01748     if (BPB.is_fat32()) {
01749         if (BPB.v.BPB_RootEntCnt != 0) {
01750             LOG_MSG("Sanity check fail: Root directory count != 0 and not FAT32");
01751             created_successfully = false;
01752             return;
01753         }
01754     }
01755     else {
01756         if (BPB.v.BPB_RootEntCnt == 0) {
01757             LOG_MSG("Sanity check fail: Root directory count == 0 and not FAT32");
01758             created_successfully = false;
01759             return;
01760         }
01761     }
01762 
01763     /* too much of this code assumes 512 bytes per sector or more.
01764      * MS-DOS itself as I understand it relies on bytes per sector being a power of 2.
01765      * this is to protect against errant FAT structures and to help prep this code
01766      * later to work with the 1024 bytes/sector used by PC-98 floppy formats.
01767      * When done, this code should be able to then handle the FDI/FDD images
01768      * PC-98 games are normally distributed in on the internet.
01769      *
01770      * The value "128" comes from the smallest sector size possible on the floppy
01771      * controller of MS-DOS based systems. */
01772     /* NTS: Power of 2 test: A number is a power of 2 if (x & (x - 1)) == 0
01773      *
01774      * 15        15 & 14       01111 AND 01110     RESULT: 01110 (15)
01775      * 16        16 & 15       10000 AND 01111     RESULT: 00000 (0)
01776      * 17        17 & 16       10001 AND 10000     RESULT: 10000 (16) */
01777     if (BPB.v.BPB_BytsPerSec < 128 || BPB.v.BPB_BytsPerSec > SECTOR_SIZE_MAX ||
01778         (BPB.v.BPB_BytsPerSec & (BPB.v.BPB_BytsPerSec - 1)) != 0/*not a power of 2*/) {
01779         LOG_MSG("FAT bytes/sector value %u not supported",BPB.v.BPB_BytsPerSec);
01780                 created_successfully = false;
01781         return;
01782     }
01783 
01784     /* another fault of this code is that it assumes the sector size of the medium matches
01785      * the BPB_BytsPerSec value of the MS-DOS filesystem. if they don't match, problems
01786      * will result. */
01787     if (BPB.v.BPB_BytsPerSec != fatDrive::getSectSize()) {
01788         LOG_MSG("FAT bytes/sector %u does not match disk image bytes/sector %u",
01789             (unsigned int)BPB.v.BPB_BytsPerSec,
01790             (unsigned int)fatDrive::getSectSize());
01791                 created_successfully = false;
01792         return;
01793     }
01794 
01795         /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */
01796         absolute = IS_PC98_ARCH || ((BPB.v.BPB_NumHeads == headscyl) && (BPB.v.BPB_SecPerTrk == cylsector));
01797         LOG(LOG_DOSMISC,LOG_DEBUG)("FAT driver: Using %s sector access",absolute ? "absolute" : "C/H/S");
01798 
01799         /* Determine FAT format, 12, 16 or 32 */
01800 
01801         /* Get size of root dir in sectors */
01802         Bit32u RootDirSectors;
01803         Bit32u DataSectors;
01804 
01805     if (BPB.is_fat32()) {
01806         /* FAT32 requires use of TotSec32, TotSec16 must be zero. */
01807         if (BPB.v.BPB_TotSec32 == 0) {
01808             LOG_MSG("BPB_TotSec32 == 0 and FAT32 BPB, not valid");
01809             created_successfully = false;
01810             return;
01811         }
01812         if (BPB.v32.BPB_RootClus < 2) {
01813             LOG_MSG("BPB_RootClus == 0 and FAT32 BPB, not valid");
01814             created_successfully = false;
01815             return;
01816         }
01817         if (BPB.v.BPB_FATSz16 != 0) {
01818             LOG_MSG("BPB_FATSz16 != 0 and FAT32 BPB, not valid");
01819             created_successfully = false;
01820             return;
01821         }
01822         if (BPB.v32.BPB_FATSz32 == 0) {
01823             LOG_MSG("BPB_FATSz32 == 0 and FAT32 BPB, not valid");
01824             created_successfully = false;
01825             return;
01826         }
01827 
01828         RootDirSectors = 0; /* FAT32 root directory has it's own allocation chain, instead of a fixed location */
01829         DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors);
01830         CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus;
01831         firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
01832         firstRootDirSect = 0;
01833     }
01834     else {
01835         if (BPB.v.BPB_FATSz16 == 0) {
01836             LOG_MSG("BPB_FATSz16 == 0 and not FAT32 BPB, not valid");
01837             created_successfully = false;
01838             return;
01839         }
01840 
01841         RootDirSectors = ((BPB.v.BPB_RootEntCnt * 32u) + (BPB.v.BPB_BytsPerSec - 1u)) / BPB.v.BPB_BytsPerSec;
01842 
01843         if (BPB.v.BPB_TotSec16 != 0)
01844             DataSectors = (Bitu)BPB.v.BPB_TotSec16 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors);
01845         else
01846             DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors);
01847 
01848         CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus;
01849         firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
01850         firstRootDirSect = (Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)partSectOff;
01851     }
01852 
01853         if(CountOfClusters < 4085) {
01854                 /* Volume is FAT12 */
01855                 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
01856                 fattype = FAT12;
01857         } else if (CountOfClusters < 65525) {
01858                 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
01859                 fattype = FAT16;
01860         } else {
01861                 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
01862                 fattype = FAT32;
01863         }
01864 
01865         /* just so you know....! */
01866         if (fattype == FAT32 && (dos.version.major < 7 || (dos.version.major == 7 && dos.version.minor < 10))) {
01867                 LOG_MSG("CAUTION: Mounting FAT32 partition when reported DOS version is less than 7.10. Disk formatting/repair utilities may mis-identify the partition.");
01868         }
01869 
01870         /* There is no cluster 0, this means we are in the root directory */
01871         cwdDirCluster = 0;
01872 
01873         memset(fatSectBuffer,0,1024);
01874         curFatSect = 0xffffffff;
01875 
01876         strcpy(info, "fatDrive ");
01877         strcat(info, wpcolon&&strlen(sysFilename)>1&&sysFilename[0]==':'?sysFilename+1:sysFilename);
01878 }
01879 
01880 #ifdef _MSC_VER
01881 # define MIN(a,b) ((a) < (b) ? (a) : (b))
01882 # define MAX(a,b) ((a) > (b) ? (a) : (b))
01883 #else
01884 # define MIN(a,b) std::min(a,b)
01885 # define MAX(a,b) std::max(a,b)
01886 #endif
01887 
01888 bool fatDrive::AllocationInfo32(Bit32u * _bytes_sector,Bit32u * _sectors_cluster,Bit32u * _total_clusters,Bit32u * _free_clusters) {
01889         Bit32u countFree = 0;
01890         Bit32u i;
01891 
01892         for(i=0;i<CountOfClusters;i++) {
01893                 if(!getClusterValue(i+2))
01894                         countFree++;
01895         }
01896 
01897         *_bytes_sector = getSectSize();
01898         *_sectors_cluster = BPB.v.BPB_SecPerClus;
01899         *_total_clusters = CountOfClusters;
01900         *_free_clusters = countFree;
01901 
01902         return true;
01903 }
01904 
01905 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
01906         if (BPB.is_fat32()) {
01907                 Bit32u bytes32,sectors32,clusters32,free32;
01908                 if (AllocationInfo32(&bytes32,&sectors32,&clusters32,&free32) &&
01909                         DOS_CommonFAT32FAT16DiskSpaceConv(_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,bytes32,sectors32,clusters32,free32))
01910                         return true;
01911 
01912                 return false;
01913         }
01914         else {
01915                 Bit32u countFree = 0;
01916                 Bit32u i;
01917 
01918                 for(i=0;i<CountOfClusters;i++) {
01919                         if(!getClusterValue(i+2))
01920                                 countFree++;
01921                 }
01922 
01923                 /* FAT12/FAT16 should never allow more than 0xFFF6 clusters and partitions larger than 2GB */
01924                 *_bytes_sector = (Bit16u)getSectSize();
01925                 *_sectors_cluster = BPB.v.BPB_SecPerClus;
01926                 *_total_clusters = (Bit16u)MIN(CountOfClusters,0xFFFFu);
01927                 *_free_clusters = (Bit16u)MIN(countFree,0xFFFFu);
01928         }
01929 
01930         return true;
01931 }
01932 
01933 Bit32u fatDrive::getFirstFreeClust(void) {
01934         Bit32u i;
01935         for(i=0;i<CountOfClusters;i++) {
01936                 if(!getClusterValue(i+2)) return (i+2);
01937         }
01938 
01939         /* No free cluster found */
01940         return 0;
01941 }
01942 
01943 bool fatDrive::isRemote(void) { return false; }
01944 bool fatDrive::isRemovable(void) { return false; }
01945 
01946 Bits fatDrive::UnMount(void) {
01947         delete this;
01948         return 0;
01949 }
01950 
01951 Bit8u fatDrive::GetMediaByte(void) { return BPB.v.BPB_Media; }
01952 const FAT_BootSector::bpb_union_t &fatDrive::GetBPB(void) { return BPB; }
01953 
01954 void fatDrive::SetBPB(const FAT_BootSector::bpb_union_t &bpb) {
01955         if (readonly) return;
01956         BPB.v.BPB_BytsPerSec = bpb.v.BPB_BytsPerSec;
01957         BPB.v.BPB_SecPerClus = bpb.v.BPB_SecPerClus;
01958         BPB.v.BPB_RsvdSecCnt = bpb.v.BPB_RsvdSecCnt;
01959         BPB.v.BPB_NumFATs = bpb.v.BPB_NumFATs;
01960         BPB.v.BPB_RootEntCnt = bpb.v.BPB_RootEntCnt;
01961         BPB.v.BPB_TotSec16 = bpb.v.BPB_TotSec16;
01962         BPB.v.BPB_Media = bpb.v.BPB_Media;
01963         BPB.v.BPB_FATSz16 = bpb.v.BPB_FATSz16;
01964         BPB.v.BPB_SecPerTrk = bpb.v.BPB_SecPerTrk;
01965         BPB.v.BPB_NumHeads = bpb.v.BPB_NumHeads;
01966         BPB.v.BPB_HiddSec = bpb.v.BPB_HiddSec;
01967         BPB.v.BPB_TotSec32 = bpb.v.BPB_TotSec32;
01968         if (!bpb.is_fat32() && (bpb.v.BPB_BootSig == 0x28 || bpb.v.BPB_BootSig == 0x29))
01969                 BPB.v.BPB_VolID = bpb.v.BPB_VolID;
01970         if (bpb.is_fat32() && (bpb.v32.BS_BootSig == 0x28 || bpb.v32.BS_BootSig == 0x29))
01971                 BPB.v32.BS_VolID = bpb.v32.BS_VolID;
01972         if (bpb.is_fat32()) {
01973                 BPB.v32.BPB_BytsPerSec = bpb.v32.BPB_BytsPerSec;
01974                 BPB.v32.BPB_SecPerClus = bpb.v32.BPB_SecPerClus;
01975                 BPB.v32.BPB_RsvdSecCnt = bpb.v32.BPB_RsvdSecCnt;
01976                 BPB.v32.BPB_NumFATs = bpb.v32.BPB_NumFATs;
01977                 BPB.v32.BPB_RootEntCnt = bpb.v32.BPB_RootEntCnt;
01978                 BPB.v32.BPB_TotSec16 = bpb.v32.BPB_TotSec16;
01979                 BPB.v32.BPB_Media = bpb.v32.BPB_Media;
01980                 BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32;
01981                 BPB.v32.BPB_SecPerTrk = bpb.v32.BPB_SecPerTrk;
01982                 BPB.v32.BPB_NumHeads = bpb.v32.BPB_NumHeads;
01983                 BPB.v32.BPB_HiddSec = bpb.v32.BPB_HiddSec;
01984                 BPB.v32.BPB_TotSec32 = bpb.v32.BPB_TotSec32;
01985                 BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32;
01986                 BPB.v32.BPB_ExtFlags = bpb.v32.BPB_ExtFlags;
01987                 BPB.v32.BPB_FSVer = bpb.v32.BPB_FSVer;
01988                 BPB.v32.BPB_RootClus = bpb.v32.BPB_RootClus;
01989                 BPB.v32.BPB_FSInfo = bpb.v32.BPB_FSInfo;
01990                 BPB.v32.BPB_BkBootSec = bpb.v32.BPB_BkBootSec;
01991         }
01992 
01993     FAT_BootSector bootbuffer = {};
01994     loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
01995         if (BPB.is_fat32()) bootbuffer.bpb.v32=BPB.v32;
01996         bootbuffer.bpb.v=BPB.v;
01997     loadedDisk->Write_AbsoluteSector(0+partSectOff,&bootbuffer);
01998 }
01999 
02000 bool fatDrive::FileCreate(DOS_File **file, const char *name, Bit16u attributes) {
02001         const char *lfn = NULL;
02002 
02003     if (readonly) {
02004                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02005         return false;
02006     }
02007     direntry fileEntry = {};
02008         Bit32u dirClust, subEntry;
02009         char dirName[DOS_NAMELENGTH_ASCII];
02010         char pathName[11], path[DOS_PATHLENGTH];
02011 
02012         Bit16u save_errorcode=dos.errorcode;
02013 
02014         if (attributes & DOS_ATTR_VOLUME) {
02015                 SetLabel(name,false,true);
02016                 return true;
02017         }
02018         if (attributes & DOS_ATTR_DIRECTORY) {
02019                 DOS_SetError(DOSERR_ACCESS_DENIED);
02020                 return false;
02021         }
02022 
02023         /* you cannot create root directory */
02024         if (*name == 0) {
02025                 DOS_SetError(DOSERR_ACCESS_DENIED);
02026                 return false;
02027         }
02028 
02029         /* Check if file already exists */
02030         if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry, true/*dirOk*/)) {
02031                 /* You can't create/truncate a directory! */
02032                 if (fileEntry.attrib & DOS_ATTR_DIRECTORY) {
02033                         DOS_SetError(DOSERR_ACCESS_DENIED);
02034                         return false;
02035                 }
02036 
02037                 /* Truncate file allocation chain */
02038                 {
02039                         const Bit32u chk = BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust;
02040                         if(chk != 0) deleteClustChain(chk, 0);
02041                 }
02042                 /* Update directory entry */
02043                 fileEntry.entrysize=0;
02044                 fileEntry.SetCluster32(0);
02045                 directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
02046         } else {
02047                 /* Can we even get the name of the file itself? */
02048                 if(!getEntryName(name, &dirName[0])||!strlen(trim(dirName))) return false;
02049                 convToDirFile(&dirName[0], &pathName[0]);
02050 
02051                 /* Can we find the base directory? */
02052                 if(!getDirClustNum(name, &dirClust, true)) return false;
02053 
02054                 /* NTS: "name" is the full relative path. For LFN creation to work we need only the final element of the path */
02055                 if (uselfn && !force_sfn) {
02056                         lfn = strrchr(name,'\\');
02057 
02058                         if (lfn != NULL) {
02059                                 lfn++; /* step past '\' */
02060                                 strcpy(path, name);
02061                                 *(strrchr(path,'\\')+1)=0;
02062                         } else {
02063                                 lfn = name; /* no path elements */
02064                                 *path=0;
02065                         }
02066 
02067                         if (filename_not_strict_8x3(lfn)) {
02068                                 char *sfn=Generate_SFN(path, lfn);
02069                                 if (sfn!=NULL) convToDirFile(sfn, &pathName[0]);
02070                         } else
02071                                 lfn = NULL;
02072                 }
02073 
02074                 memset(&fileEntry, 0, sizeof(direntry));
02075                 memcpy(&fileEntry.entryname, &pathName[0], 11);
02076         {
02077             Bit16u ct,cd;
02078             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,time(NULL));
02079             fileEntry.modTime = ct;
02080             fileEntry.modDate = cd;
02081         }
02082         fileEntry.attrib = (Bit8u)(attributes & 0xff);
02083                 addDirectoryEntry(dirClust, fileEntry, lfn);
02084 
02085                 /* Check if file exists now */
02086                 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
02087         }
02088 
02089         /* Empty file created, now lets open it */
02090         /* TODO: check for read-only flag and requested write access */
02091         *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this);
02092         (*file)->flags=OPEN_READWRITE;
02093         ((fatFile *)(*file))->dirCluster = dirClust;
02094         ((fatFile *)(*file))->dirIndex = subEntry;
02095         /* Maybe modTime and date should be used ? (crt matches findnext) */
02096         ((fatFile *)(*file))->time = fileEntry.modTime;
02097         ((fatFile *)(*file))->date = fileEntry.modDate;
02098 
02099         dos.errorcode=save_errorcode;
02100         return true;
02101 }
02102 
02103 bool fatDrive::FileExists(const char *name) {
02104     direntry fileEntry = {};
02105         Bit32u dummy1, dummy2;
02106         if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
02107         return true;
02108 }
02109 
02110 bool fatDrive::FileOpen(DOS_File **file, const char *name, Bit32u flags) {
02111     direntry fileEntry = {};
02112         Bit32u dirClust, subEntry;
02113 
02114         /* you cannot open root directory */
02115         if (*name == 0) {
02116                 DOS_SetError(DOSERR_ACCESS_DENIED);
02117                 return false;
02118         }
02119 
02120         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
02121         /* TODO: check for read-only flag and requested write access */
02122         *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this);
02123         (*file)->flags = flags;
02124         ((fatFile *)(*file))->dirCluster = dirClust;
02125         ((fatFile *)(*file))->dirIndex = subEntry;
02126         /* Maybe modTime and date should be used ? (crt matches findnext) */
02127         ((fatFile *)(*file))->time = fileEntry.modTime;
02128         ((fatFile *)(*file))->date = fileEntry.modDate;
02129         return true;
02130 }
02131 
02132 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
02133         /* TODO: Stub */
02134         return false;
02135 }
02136 
02137 bool fatDrive::FileUnlink(const char * name) {
02138     if (readonly) {
02139                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02140         return false;
02141     }
02142     direntry tmpentry = {};
02143     direntry fileEntry = {};
02144         Bit32u dirClust, subEntry;
02145 
02146         /* you cannot delete root directory */
02147         if (*name == 0) {
02148                 DOS_SetError(DOSERR_ACCESS_DENIED);
02149                 return false;
02150         }
02151 
02152         lfnRange.clear();
02153         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false; /* Do not use dirOk, DOS should never call this unless a file */
02154         lfnRange_t dir_lfn_range = lfnRange; /* copy down LFN results before they are obliterated by the next call to FindNextInternal. */
02155 
02156         /* delete LFNs */
02157         if (!dir_lfn_range.empty() && (dos.version.major >= 7 || uselfn)) {
02158                 /* last LFN entry should be fileidx */
02159                 assert(dir_lfn_range.dirPos_start < dir_lfn_range.dirPos_end);
02160                 if (dir_lfn_range.dirPos_end != subEntry) LOG_MSG("FAT warning: LFN dirPos_end=%u fileidx=%u (mismatch)",dir_lfn_range.dirPos_end,subEntry);
02161                 for (unsigned int didx=dir_lfn_range.dirPos_start;didx < dir_lfn_range.dirPos_end;didx++) {
02162                         if (directoryBrowse(dirClust,&tmpentry,didx)) {
02163                                 tmpentry.entryname[0] = 0xe5;
02164                                 directoryChange(dirClust,&tmpentry,didx);
02165                         }
02166                 }
02167         }
02168 
02169         /* remove primary 8.3 SFN */
02170         fileEntry.entryname[0] = 0xe5;
02171         directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
02172 
02173         /* delete allocation chain */
02174         {
02175                 const Bit32u chk = BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust;
02176                 if(chk != 0) deleteClustChain(chk, 0);
02177         }
02178 
02179         if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
02180 
02181         return true;
02182 }
02183 
02184 bool fatDrive::FindFirst(const char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
02185     direntry dummyClust = {};
02186 
02187     // volume label searches always affect root directory, no matter the current directory, at least with FCBs
02188     if (dta.GetAttr() & DOS_ATTR_VOLUME) {
02189         if(!getDirClustNum("\\", &cwdDirCluster, false)) {
02190             DOS_SetError(DOSERR_PATH_NOT_FOUND);
02191             return false;
02192         }
02193     }
02194     else {
02195         if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
02196             DOS_SetError(DOSERR_PATH_NOT_FOUND);
02197             return false;
02198         }
02199     }
02200 
02201         if (lfn_filefind_handle>=LFN_FILEFIND_MAX) {
02202                 dta.SetDirID(0);
02203                 dta.SetDirIDCluster(cwdDirCluster);
02204         } else {
02205                 dpos[lfn_filefind_handle]=0;
02206                 dnum[lfn_filefind_handle]=cwdDirCluster;
02207         }
02208 
02209         return FindNextInternal(cwdDirCluster, dta, &dummyClust);
02210 }
02211 
02212 char* removeTrailingSpaces(char* str) {
02213         char* end = str + strlen(str) - 1;
02214         while (end >= str && *end == ' ') end--;
02215     /* NTS: The loop will exit with 'end' one char behind the last ' ' space character.
02216      *      So to ASCIIZ snip off the space, step forward one and overwrite with NUL.
02217      *      The loop may end with 'end' one char behind 'ptr' if the string was empty ""
02218      *      or nothing but spaces. This is OK because after the step forward, end >= str
02219      *      in all cases. */
02220         *(++end) = '\0';
02221         return str;
02222 }
02223 
02224 char* removeLeadingSpaces(char* str) {
02225         size_t len = strlen(str);
02226         size_t pos = strspn(str," ");
02227         memmove(str,str + pos,len - pos + 1);
02228         return str;
02229 }
02230 
02231 char* trimString(char* str) {
02232         return removeTrailingSpaces(removeLeadingSpaces(str));
02233 }
02234 
02235 Bit32u fatDrive::GetSectorCount(void) {
02236     return (loadedDisk->heads * loadedDisk->sectors * loadedDisk->cylinders) - partSectOff;
02237 }
02238 
02239 Bit32u fatDrive::GetSectorSize(void) {
02240     return getSectorSize();
02241 }
02242 
02243 Bit8u fatDrive::Read_AbsoluteSector_INT25(Bit32u sectnum, void * data) {
02244     return readSector(sectnum+partSectOff,data);
02245 }
02246 
02247 Bit8u fatDrive::Write_AbsoluteSector_INT25(Bit32u sectnum, void * data) {
02248     return writeSector(sectnum+partSectOff,data);
02249 }
02250 
02251 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
02252         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
02253         Bit32u logentsector; /* Logical entry sector */
02254         Bit32u entryoffset;  /* Index offset within sector */
02255         Bit32u tmpsector;
02256         Bit8u attrs;
02257         Bit16u dirPos;
02258         char srch_pattern[CROSS_LEN];
02259         char find_name[DOS_NAMELENGTH_ASCII];
02260         char lfind_name[LFN_NAMELENGTH+1];
02261         unsigned int lfn_max_ord = 0;
02262         unsigned char lfn_checksum = 0;
02263         bool lfn_ord_found[0x40];
02264         char extension[4];
02265 
02266     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
02267     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
02268     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
02269 
02270         dta.GetSearchParams(attrs, srch_pattern,uselfn);
02271         dirPos = lfn_filefind_handle>=LFN_FILEFIND_MAX?dta.GetDirID():dpos[lfn_filefind_handle]; /* NTS: Windows 9x is said to have a 65536 dirent limit even for FAT32, so dirPos as 16-bit is acceptable */
02272 
02273         memset(lfind_name,0,LFN_NAMELENGTH);
02274         lfnRange.clear();
02275 
02276 nextfile:
02277         logentsector = (Bit32u)((size_t)dirPos / dirent_per_sector);
02278         entryoffset = (Bit32u)((size_t)dirPos % dirent_per_sector);
02279 
02280         if(dirClustNumber==0) {
02281         if (BPB.is_fat32()) return false;
02282 
02283                 if(dirPos >= BPB.v.BPB_RootEntCnt) {
02284                         if (lfn_filefind_handle<LFN_FILEFIND_MAX) {
02285                                 dpos[lfn_filefind_handle]=0;
02286                                 dnum[lfn_filefind_handle]=0;
02287                         }
02288                         DOS_SetError(DOSERR_NO_MORE_FILES);
02289                         return false;
02290                 }
02291                 readSector(firstRootDirSect+logentsector,sectbuf);
02292         } else {
02293                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02294                 /* A zero sector number can't happen */
02295                 if(tmpsector == 0) {
02296                         if (lfn_filefind_handle<LFN_FILEFIND_MAX) {
02297                                 dpos[lfn_filefind_handle]=0;
02298                                 dnum[lfn_filefind_handle]=0;
02299                         }
02300                         DOS_SetError(DOSERR_NO_MORE_FILES);
02301                         return false;
02302                 }
02303                 readSector(tmpsector,sectbuf);
02304         }
02305         dirPos++;
02306         if (lfn_filefind_handle>=LFN_FILEFIND_MAX) dta.SetDirID(dirPos);
02307         else dpos[lfn_filefind_handle]=dirPos;
02308 
02309     /* Deleted file entry */
02310     if (sectbuf[entryoffset].entryname[0] == 0xe5) {
02311         lfind_name[0] = 0; /* LFN code will memset() it in full upon next dirent */
02312         lfn_max_ord = 0;
02313         lfnRange.clear();
02314         goto nextfile;
02315     }
02316 
02317         /* End of directory list */
02318         if (sectbuf[entryoffset].entryname[0] == 0x00) {
02319                         if (lfn_filefind_handle<LFN_FILEFIND_MAX) {
02320                                 dpos[lfn_filefind_handle]=0;
02321                                 dnum[lfn_filefind_handle]=0;
02322                         }
02323                 DOS_SetError(DOSERR_NO_MORE_FILES);
02324                 return false;
02325         }
02326         memset(find_name,0,DOS_NAMELENGTH_ASCII);
02327         memset(extension,0,4);
02328         memcpy(find_name,&sectbuf[entryoffset].entryname[0],8);
02329     memcpy(extension,&sectbuf[entryoffset].entryname[8],3);
02330 
02331     if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) {
02332         trimString(&find_name[0]);
02333         trimString(&extension[0]);
02334     }
02335 
02336         if (extension[0]!=0) {
02337                 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) {
02338                         strcat(find_name, ".");
02339                 }
02340                 strcat(find_name, extension);
02341         }
02342 
02343         if (sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)
02344         trimString(find_name);
02345 
02346     /* Compare attributes to search attributes */
02347 
02348     //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
02349         if (attrs == DOS_ATTR_VOLUME) {
02350                 if (dos.version.major >= 7 || uselfn) {
02351                         /* skip LFN entries */
02352                         if ((sectbuf[entryoffset].attrib & 0x3F) == 0x0F)
02353                                 goto nextfile;
02354                 }
02355 
02356                 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
02357                 labelCache.SetLabel(find_name, false, true);
02358         } else if ((dos.version.major >= 7 || uselfn) && (sectbuf[entryoffset].attrib & 0x3F) == 0x0F) { /* long filename piece */
02359                 struct direntry_lfn *dlfn = (struct direntry_lfn*)(&sectbuf[entryoffset]);
02360 
02361                 /* assume last entry comes first, because that's how Windows 9x does it and that is how you're supposed to do it according to Microsoft */
02362                 if (dlfn->LDIR_Ord & 0x40) {
02363                         lfn_max_ord = (dlfn->LDIR_Ord & 0x3F); /* NTS: First entry has ordinal 1, this is the HIGHEST ordinal in the LFN. The other entries follow in descending ordinal. */
02364                         for (unsigned int i=0;i < 0x40;i++) lfn_ord_found[i] = false;
02365                         lfn_checksum = dlfn->LDIR_Chksum;
02366                         memset(lfind_name,0,LFN_NAMELENGTH);
02367                         lfnRange.clear();
02368                         lfnRange.dirPos_start = dirPos - 1; /* NTS: The code above has already incremented dirPos */
02369                 }
02370 
02371                 if (lfn_max_ord != 0 && (dlfn->LDIR_Ord & 0x3F) > 0 && (dlfn->LDIR_Ord & 0x3Fu) <= lfn_max_ord && dlfn->LDIR_Chksum == lfn_checksum) {
02372                         unsigned int oidx = (dlfn->LDIR_Ord & 0x3Fu) - 1u;
02373                         unsigned int stridx = oidx * 13u;
02374 
02375                         if ((stridx+13u) <= LFN_NAMELENGTH) {
02376                                 for (unsigned int i=0;i < 5;i++)
02377                                         lfind_name[stridx+i+0] = (char)(dlfn->LDIR_Name1[i] & 0xFF);
02378                                 for (unsigned int i=0;i < 6;i++)
02379                                         lfind_name[stridx+i+5] = (char)(dlfn->LDIR_Name2[i] & 0xFF);
02380                                 for (unsigned int i=0;i < 2;i++)
02381                                         lfind_name[stridx+i+11] = (char)(dlfn->LDIR_Name3[i] & 0xFF);
02382 
02383                                 lfn_ord_found[oidx] = true;
02384                         }
02385                 }
02386 
02387                 goto nextfile;
02388         } else {
02389         if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME) ) {
02390             lfind_name[0] = 0; /* LFN code will memset() it in full upon next dirent */
02391             lfn_max_ord = 0;
02392             lfnRange.clear();
02393             goto nextfile;
02394         }
02395         }
02396 
02397         if (lfn_max_ord != 0) {
02398                 bool ok = false;
02399                 unsigned int complete = 0;
02400                 for (unsigned int i=0;i < lfn_max_ord;i++) complete += lfn_ord_found[i]?1:0;
02401 
02402                 if (complete == lfn_max_ord) {
02403                         unsigned char chk = 0;
02404                         for (unsigned int i=0;i < 11;i++) {
02405                                 chk = ((chk & 1u) ? 0x80u : 0x00u) + (chk >> 1u) + sectbuf[entryoffset].entryname[i];
02406                         }
02407 
02408                         if (lfn_checksum == chk) {
02409                                 lfnRange.dirPos_end = dirPos - 1; /* NTS: The code above has already incremented dirPos */
02410                                 ok = true;
02411                         }
02412                 }
02413 
02414                 if (!ok) {
02415                         lfind_name[0] = 0; /* LFN code will memset() it in full upon next dirent */
02416                         lfn_max_ord = 0;
02417                         lfnRange.clear();
02418                 }
02419         }
02420         else {
02421                 lfind_name[0] = 0; /* LFN code will memset() it in full upon next dirent */
02422                 lfn_max_ord = 0;
02423                 lfnRange.clear();
02424         }
02425 
02426         /* Compare name to search pattern. Skip long filename match if no long filename given. */
02427         if (!(WildFileCmp(find_name,srch_pattern) || (lfn_max_ord != 0 && lfind_name[0] != 0 && LWildFileCmp(lfind_name,srch_pattern)))) {
02428                 lfind_name[0] = 0; /* LFN code will memset() it in full upon next dirent */
02429                 lfn_max_ord = 0;
02430                 lfnRange.clear();
02431                 goto nextfile;
02432         }
02433 
02434         // Drive emulation does not need to require a LFN in case there is no corresponding 8.3 names.
02435         if (lfind_name[0] == 0) strcpy(lfind_name,find_name);
02436 
02437         //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
02438 
02439         dta.SetResult(find_name, lfind_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib);
02440 
02441         memcpy(foundEntry, &sectbuf[entryoffset], sizeof(direntry));
02442 
02443         return true;
02444 }
02445 
02446 bool fatDrive::FindNext(DOS_DTA &dta) {
02447     direntry dummyClust = {};
02448 
02449         return FindNextInternal(lfn_filefind_handle>=LFN_FILEFIND_MAX?dta.GetDirIDCluster():(dnum[lfn_filefind_handle]?dnum[lfn_filefind_handle]:0), dta, &dummyClust);
02450 }
02451 
02452 
02453 bool fatDrive::SetFileAttr(const char *name, Bit16u attr) {
02454     if (readonly) {
02455                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02456         return false;
02457     }
02458     direntry fileEntry = {};
02459         Bit32u dirClust, subEntry;
02460 
02461         /* you cannot set file attr root directory (right?) */
02462         if (*name == 0) {
02463                 DOS_SetError(DOSERR_ACCESS_DENIED);
02464                 return false;
02465         }
02466 
02467         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry, /*dirOk*/true)) {
02468                 return false;
02469         } else {
02470                 fileEntry.attrib=(Bit8u)attr;
02471                 directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
02472         }
02473         return true;
02474 }
02475 
02476 bool fatDrive::GetFileAttr(const char *name, Bit16u *attr) {
02477     direntry fileEntry = {};
02478         Bit32u dirClust, subEntry;
02479 
02480         /* you CAN get file attr root directory */
02481         if (*name == 0) {
02482                 *attr=DOS_ATTR_DIRECTORY;
02483                 return true;
02484         }
02485 
02486         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry, /*dirOk*/true)) {
02487                 return false;
02488         } else *attr=fileEntry.attrib;
02489         return true;
02490 }
02491 
02492 bool fatDrive::GetFileAttrEx(char* name, struct stat *status) {
02493     (void)name;
02494     (void)status;
02495         return false;
02496 }
02497 
02498 unsigned long fatDrive::GetCompressedSize(char* name) {
02499     (void)name;
02500         return 0;
02501 }
02502 
02503 #if defined (WIN32)
02504 HANDLE fatDrive::CreateOpenFile(const char* name) {
02505     (void)name;
02506         DOS_SetError(1);
02507         return INVALID_HANDLE_VALUE;
02508 }
02509 #endif
02510 
02511 unsigned long fatDrive::GetSerial() {
02512         if (BPB.is_fat32())
02513                 return BPB.v32.BS_VolID?BPB.v32.BS_VolID:0x1234;
02514         else
02515                 return BPB.v.BPB_VolID?BPB.v.BPB_VolID:0x1234;
02516 }
02517 
02518 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
02519         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
02520         Bit32u entryoffset = 0; /* Index offset within sector */
02521         Bit32u tmpsector;
02522         Bit16u dirPos = 0;
02523 
02524     (void)start;//UNUSED
02525 
02526     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
02527     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
02528     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
02529 
02530         while(entNum>=0) {
02531                 Bit32u logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector)); /* Logical entry sector */
02532                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
02533 
02534                 if(dirClustNumber==0) {
02535             assert(!BPB.is_fat32());
02536             if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
02537                         tmpsector = firstRootDirSect+logentsector;
02538                         readSector(tmpsector,sectbuf);
02539                 } else {
02540                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02541                         /* A zero sector number can't happen */
02542                         if(tmpsector == 0) return false;
02543                         readSector(tmpsector,sectbuf);
02544                 }
02545                 dirPos++;
02546 
02547 
02548                 /* End of directory list */
02549                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
02550                 --entNum;
02551         }
02552 
02553         memcpy(useEntry, &sectbuf[entryoffset],sizeof(direntry));
02554         return true;
02555 }
02556 
02557 bool fatDrive::directoryChange(Bit32u dirClustNumber, const direntry *useEntry, Bit32s entNum) {
02558         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
02559         Bit32u entryoffset = 0; /* Index offset within sector */
02560         Bit32u tmpsector = 0;
02561         Bit16u dirPos = 0;
02562         
02563     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
02564     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
02565     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
02566 
02567         while(entNum>=0) {              
02568                 Bit32u logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector)); /* Logical entry sector */
02569                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
02570 
02571                 if(dirClustNumber==0) {
02572             assert(!BPB.is_fat32());
02573             if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
02574                         tmpsector = firstRootDirSect+logentsector;
02575                         readSector(tmpsector,sectbuf);
02576                 } else {
02577                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02578                         /* A zero sector number can't happen */
02579                         if(tmpsector == 0) return false;
02580                         readSector(tmpsector,sectbuf);
02581                 }
02582                 dirPos++;
02583 
02584 
02585                 /* End of directory list */
02586                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
02587                 --entNum;
02588         }
02589         if(tmpsector != 0) {
02590         memcpy(&sectbuf[entryoffset], useEntry, sizeof(direntry));
02591                 writeSector(tmpsector, sectbuf);
02592         return true;
02593         } else {
02594                 return false;
02595         }
02596 }
02597 
02598 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, const direntry& useEntry,const char *lfn) {
02599         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
02600         Bit32u tmpsector;
02601         Bit16u dirPos = 0;
02602         unsigned int need = 1;
02603         unsigned int found = 0;
02604         Bit16u dirPosFound = 0;
02605 
02606         if (lfn != NULL && *lfn != 0) {
02607                 /* 13 characters per LFN entry.
02608                  * FIXME: When we convert the LFN to wchar using code page, strlen() prior to conversion will not work,
02609                  *        convert first then count wchar_t characters. */
02610                 need = (unsigned int)(1 + ((strlen(lfn) + 12) / 13))/*round up*/;
02611         }
02612 
02613         size_t dirent_per_sector = getSectSize() / sizeof(direntry);
02614         assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
02615         assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
02616 
02617         for(;;) {               
02618                 Bit32u logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector)); /* Logical entry sector */
02619                 Bit32u entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector)); /* Index offset within sector */
02620 
02621                 if(dirClustNumber==0) {
02622                         assert(!BPB.is_fat32());
02623                         if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
02624                         tmpsector = firstRootDirSect+logentsector;
02625                 } else {
02626                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02627                         /* A zero sector number can't happen - we need to allocate more room for this directory*/
02628                         if(tmpsector == 0) {
02629                                 Bit32u newClust;
02630                                 newClust = appendCluster(dirClustNumber);
02631                                 if(newClust == 0) return false;
02632                                 zeroOutCluster(newClust);
02633                                 /* Try again to get tmpsector */
02634                                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02635                                 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
02636                         }
02637                 }
02638                 readSector(tmpsector,sectbuf);
02639 
02640                 /* Deleted file entry or end of directory list */
02641                 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
02642                         if (found == 0) dirPosFound = dirPos;
02643 
02644                         if ((++found) >= need) {
02645                                 sectbuf[entryoffset] = useEntry;
02646                                 writeSector(tmpsector,sectbuf);
02647 
02648                                 /* Add LFN entries */
02649                                 if (need != 1/*LFN*/) {
02650                                         Bit16u lfnbuf[LFN_NAMELENGTH+13]; /* on disk, LFNs are WCHAR unicode (UCS-16) */
02651 
02652                                         assert(lfn != NULL);
02653                                         assert(*lfn != 0);
02654 
02655                                         /* TODO: ANSI LFN convert to wchar here according to code page */
02656 
02657                                         unsigned int o = 0;
02658                                         const char *scan = lfn;
02659 
02660                                         while (*scan) {
02661                                                 if (o >= LFN_NAMELENGTH) return false; /* Nope! */
02662                                                 lfnbuf[o++] = (Bit16u)((unsigned char)(*scan++));
02663                                         }
02664 
02665                                         /* on disk, LFNs are padded with 0x0000 followed by a run of 0xFFFF to fill the dirent */
02666                                         lfnbuf[o++] = 0x0000;
02667                                         for (unsigned int i=0;i < 13;i++) lfnbuf[o++] = 0xFFFF;
02668                                         assert(o <= (LFN_NAMELENGTH+13));
02669 
02670                                         unsigned char chk = 0;
02671                                         for (unsigned int i=0;i < 11;i++) {
02672                                                 chk = ((chk & 1u) ? 0x80u : 0x00u) + (chk >> 1u) + useEntry.entryname[i];
02673                                         }
02674 
02675                                         dirPos = dirPosFound;
02676                                         for (unsigned int s=0;s < (need-1u);s++) {
02677                                                 unsigned int lfnsrci = (need-2u-s);
02678                                                 unsigned int lfnsrc = lfnsrci * 13;
02679 
02680                                                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector)); /* Logical entry sector */
02681                                                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector)); /* Index offset within sector */
02682 
02683                                                 if(dirClustNumber==0) {
02684                                                         assert(!BPB.is_fat32());
02685                                                         if(dirPos >= BPB.v.BPB_RootEntCnt) return false;
02686                                                         tmpsector = firstRootDirSect+logentsector;
02687                                                 } else {
02688                                                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
02689                                                         /* A zero sector number can't happen - we need to allocate more room for this directory*/
02690                                                         if(tmpsector == 0) return false;
02691                                                 }
02692                                                 readSector(tmpsector,sectbuf);
02693 
02694                                                 direntry_lfn *dlfn = (direntry_lfn*)(&sectbuf[entryoffset]);
02695 
02696                                                 memset(dlfn,0,sizeof(*dlfn));
02697 
02698                                                 dlfn->LDIR_Ord = (s == 0 ? 0x40 : 0x00) + lfnsrci + 1;
02699                                                 dlfn->LDIR_Chksum = chk;
02700                                                 dlfn->attrib = 0x0F;
02701 
02702                                                 for (unsigned int i=0;i < 5;i++) dlfn->LDIR_Name1[i] = lfnbuf[lfnsrc++];
02703                                                 for (unsigned int i=0;i < 6;i++) dlfn->LDIR_Name2[i] = lfnbuf[lfnsrc++];
02704                                                 for (unsigned int i=0;i < 2;i++) dlfn->LDIR_Name3[i] = lfnbuf[lfnsrc++];
02705 
02706                                                 writeSector(tmpsector,sectbuf);
02707                                                 dirPos++;
02708                                         }
02709                                 }
02710 
02711                                 break;
02712                         }
02713                 }
02714                 else {
02715                         found = 0;
02716                 }
02717 
02718                 dirPos++;
02719         }
02720 
02721         return true;
02722 }
02723 
02724 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
02725         Bit8u secBuffer[SECTOR_SIZE_MAX];
02726 
02727         memset(&secBuffer[0], 0, SECTOR_SIZE_MAX);
02728 
02729         unsigned int i;
02730         for(i=0;i<BPB.v.BPB_SecPerClus;i++) {
02731                 writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
02732         }
02733 }
02734 
02735 bool fatDrive::MakeDir(const char *dir) {
02736         const char *lfn = NULL;
02737 
02738     if (readonly) {
02739                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02740         return false;
02741     }
02742         Bit32u dummyClust, dirClust, subEntry;
02743         direntry tmpentry;
02744         char dirName[DOS_NAMELENGTH_ASCII];
02745     char pathName[11], path[DOS_PATHLENGTH];
02746     Bit16u ct,cd;
02747 
02748         /* you cannot mkdir root directory */
02749         if (*dir == 0) {
02750                 DOS_SetError(DOSERR_ACCESS_DENIED);
02751                 return false;
02752         }
02753 
02754         /* Can we even get the name of the directory itself? */
02755         if(!getEntryName(dir, &dirName[0])||!strlen(trim(dirName))) return false;
02756         convToDirFile(&dirName[0], &pathName[0]);
02757 
02758         /* Fail to make directory if something of that name already exists */
02759         if(getFileDirEntry(dir,&tmpentry,&dummyClust,&subEntry,/*dirOk*/true)) return false;
02760 
02761         /* Can we find the base directory? */
02762         if(!getDirClustNum(dir, &dirClust, true)) return false;
02763 
02764         dummyClust = getFirstFreeClust();
02765         /* No more space */
02766         if(dummyClust == 0) return false;
02767 
02768         if(!allocateCluster(dummyClust, 0)) return false;
02769 
02770         /* NTS: "dir" is the full relative path. For LFN creation to work we need only the final element of the path */
02771         if (uselfn && !force_sfn) {
02772                 lfn = strrchr(dir,'\\');
02773 
02774                 if (lfn != NULL) {
02775                         lfn++; /* step past '\' */
02776                         strcpy(path, dir);
02777                         *(strrchr(path,'\\')+1)=0;
02778                 } else {
02779                         lfn = dir; /* no path elements */
02780                         *path=0;
02781                 }
02782 
02783                 if (filename_not_strict_8x3(lfn)) {
02784                         char *sfn=Generate_SFN(path, lfn);
02785                         if (sfn!=NULL) convToDirFile(sfn, &pathName[0]);
02786                 } else
02787                         lfn = NULL;
02788         }
02789 
02790         zeroOutCluster(dummyClust);
02791 
02792         time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
02793 
02794         /* Add the new directory to the base directory */
02795         memset(&tmpentry,0, sizeof(direntry));
02796         memcpy(&tmpentry.entryname, &pathName[0], 11);
02797         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
02798         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
02799         tmpentry.attrib = DOS_ATTR_DIRECTORY;
02800     tmpentry.modTime = ct;
02801     tmpentry.modDate = cd;
02802     addDirectoryEntry(dirClust, tmpentry, lfn);
02803 
02804         /* Add the [.] and [..] entries to our new directory*/
02805         /* [.] entry */
02806         memset(&tmpentry,0, sizeof(direntry));
02807         memcpy(&tmpentry.entryname, ".          ", 11);
02808         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
02809         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
02810         tmpentry.attrib = DOS_ATTR_DIRECTORY;
02811     tmpentry.modTime = ct;
02812     tmpentry.modDate = cd;
02813         addDirectoryEntry(dummyClust, tmpentry);
02814 
02815         /* [..] entry */
02816         memset(&tmpentry,0, sizeof(direntry));
02817         memcpy(&tmpentry.entryname, "..         ", 11);
02818         if (BPB.is_fat32() && dirClust == BPB.v32.BPB_RootClus) {
02819                 /* Windows 98 SCANDISK.EXE considers it an error for the '..' entry of a top level
02820                  * directory to point at the actual cluster number of the root directory. The
02821                  * correct value is 0 apparently. */
02822                 tmpentry.loFirstClust = (Bit16u)0;
02823                 tmpentry.hiFirstClust = (Bit16u)0;
02824         }
02825         else {
02826                 tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
02827                 tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
02828         }
02829         tmpentry.attrib = DOS_ATTR_DIRECTORY;
02830     tmpentry.modTime = ct;
02831     tmpentry.modDate = cd;
02832         addDirectoryEntry(dummyClust, tmpentry);
02833         //if(!getDirClustNum(dir, &dummyClust, false)) return false;
02834 
02835         return true;
02836 }
02837 
02838 bool fatDrive::RemoveDir(const char *dir) {
02839     if (readonly) {
02840                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02841         return false;
02842     }
02843         Bit32u dummyClust, dirClust, subEntry;
02844     direntry tmpentry = {};
02845         char dirName[DOS_NAMELENGTH_ASCII];
02846         char pathName[11];
02847 
02848         /* you cannot rmdir root directory */
02849         if (*dir == 0) {
02850                 DOS_SetError(DOSERR_ACCESS_DENIED);
02851                 return false;
02852         }
02853 
02854         /* Can we even get the name of the directory itself? */
02855         if(!getEntryName(dir, &dirName[0])||!strlen(trim(dirName))) return false;
02856         convToDirFile(&dirName[0], &pathName[0]);
02857 
02858         /* directory must exist */
02859         lfnRange.clear();
02860         if(!getFileDirEntry(dir,&tmpentry,&dirClust,&subEntry,/*dirOk*/true)) return false; /* dirClust is parent dir of directory */
02861         if (!(tmpentry.attrib & DOS_ATTR_DIRECTORY)) return false;
02862         dummyClust = (BPB.is_fat32() ? tmpentry.Cluster32() : tmpentry.loFirstClust);
02863         lfnRange_t dir_lfn_range = lfnRange; /* copy down LFN results before they are obliterated by the next call to FindNextInternal. */
02864 
02865         /* Can't remove root directory */
02866         if(dummyClust == 0) return false;
02867         if(BPB.is_fat32() && dummyClust==BPB.v32.BPB_RootClus) return false;
02868 
02869         /* Check to make sure directory is empty */
02870         Bit32u filecount = 0;
02871         /* Set to 2 to skip first 2 entries, [.] and [..] */
02872         Bit32s fileidx = 2;
02873         while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
02874                 /* Check for non-deleted files */
02875                 if(tmpentry.entryname[0] != 0xe5) filecount++;
02876                 fileidx++;
02877         }
02878 
02879         /* Return if directory is not empty */
02880         if(filecount > 0) return false;
02881 
02882         /* delete LFNs */
02883         if (!dir_lfn_range.empty() && (dos.version.major >= 7 || uselfn)) {
02884                 /* last LFN entry should be fileidx */
02885                 assert(dir_lfn_range.dirPos_start < dir_lfn_range.dirPos_end);
02886                 if (dir_lfn_range.dirPos_end != subEntry) LOG_MSG("FAT warning: LFN dirPos_end=%u fileidx=%u (mismatch)",dir_lfn_range.dirPos_end,subEntry);
02887                 for (unsigned int didx=dir_lfn_range.dirPos_start;didx < dir_lfn_range.dirPos_end;didx++) {
02888                         if (directoryBrowse(dirClust,&tmpentry,didx)) {
02889                                 tmpentry.entryname[0] = 0xe5;
02890                                 directoryChange(dirClust,&tmpentry,didx);
02891                         }
02892                 }
02893         }
02894 
02895         /* remove primary 8.3 entry */
02896         if (!directoryBrowse(dirClust, &tmpentry, subEntry)) return false;
02897         tmpentry.entryname[0] = 0xe5;
02898         if (!directoryChange(dirClust, &tmpentry, subEntry)) return false;
02899 
02900         /* delete allocation chain */
02901         deleteClustChain(dummyClust, 0);
02902         return true;
02903 }
02904 
02905 bool fatDrive::Rename(const char * oldname, const char * newname) {
02906         const char *lfn = NULL;
02907 
02908     if (readonly) {
02909                 DOS_SetError(DOSERR_WRITE_PROTECTED);
02910         return false;
02911     }
02912 
02913         /* you cannot rename root directory */
02914         if (*oldname == 0 || *newname == 0) {
02915                 DOS_SetError(DOSERR_ACCESS_DENIED);
02916                 return false;
02917         }
02918 
02919     direntry fileEntry1 = {}, fileEntry2 = {};
02920         Bit32u dirClust1, subEntry1, dirClust2, subEntry2;
02921         char dirName2[DOS_NAMELENGTH_ASCII];
02922         char pathName2[11], path[DOS_PATHLENGTH];
02923         lfnRange_t dir_lfn_range;
02924 
02925         /* Check that old name exists (file or directory) */
02926         lfnRange.clear();
02927         if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1, /*dirOk*/true)) return false;
02928         dir_lfn_range = lfnRange;
02929 
02930         /* Check if new name (file or directory) already exists, fail if so */
02931         if(getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2, /*dirOk*/true)&&!(uselfn&&!force_sfn&&strcmp(oldname, newname)&&!strcasecmp(oldname, newname))) return false;
02932 
02933         /* Can we even get the name of the file itself? */
02934         if(!getEntryName(newname, &dirName2[0])||!strlen(trim(dirName2))) return false;
02935         convToDirFile(&dirName2[0], &pathName2[0]);
02936 
02937         /* Can we find the base directory of the new name? (we know the parent dir of oldname in dirClust1) */
02938         if(!getDirClustNum(newname, &dirClust2, true)) return false;
02939 
02940         /* NTS: "newname" is the full relative path. For LFN creation to work we need only the final element of the path */
02941         if (uselfn && !force_sfn) {
02942                 lfn = strrchr(newname,'\\');
02943 
02944                 if (lfn != NULL) {
02945                         lfn++; /* step past '\' */
02946                         strcpy(path, newname);
02947                         *(strrchr(path,'\\')+1)=0;
02948                 } else {
02949                         lfn = newname; /* no path elements */
02950                         *path=0;
02951                 }
02952 
02953                 if (filename_not_strict_8x3(lfn)) {
02954                         char oldchar=fileEntry1.entryname[0];
02955                         fileEntry1.entryname[0] = 0xe5;
02956                         directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
02957                         char *sfn=Generate_SFN(path, lfn);
02958                         if (sfn!=NULL) convToDirFile(sfn, &pathName2[0]);
02959                         fileEntry1.entryname[0] = oldchar;
02960                         directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
02961                 } else
02962                         lfn = NULL;
02963         }
02964 
02965         /* add new dirent */
02966         memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
02967         memcpy(&fileEntry2.entryname, &pathName2[0], 11);
02968         addDirectoryEntry(dirClust2, fileEntry2, lfn);
02969 
02970         /* Remove old 8.3 SFN entry */
02971         fileEntry1.entryname[0] = 0xe5;
02972         directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
02973 
02974         /* remove LFNs of old entry only if emulating LFNs or DOS version 7.0.
02975          * Earlier DOS versions ignore LFNs. */
02976         if (!dir_lfn_range.empty() && (dos.version.major >= 7 || uselfn)) {
02977                 /* last LFN entry should be fileidx */
02978                 assert(dir_lfn_range.dirPos_start < dir_lfn_range.dirPos_end);
02979                 if (dir_lfn_range.dirPos_end != subEntry1) LOG_MSG("FAT warning: LFN dirPos_end=%u fileidx=%u (mismatch)",dir_lfn_range.dirPos_end,subEntry1);
02980                 for (unsigned int didx=dir_lfn_range.dirPos_start;didx < dir_lfn_range.dirPos_end;didx++) {
02981                         if (directoryBrowse(dirClust1,&fileEntry1,didx)) {
02982                                 fileEntry1.entryname[0] = 0xe5;
02983                                 directoryChange(dirClust1,&fileEntry1,didx);
02984                         }
02985                 }
02986         }
02987 
02988         return true;
02989 }
02990 
02991 bool fatDrive::TestDir(const char *dir) {
02992         Bit32u dummyClust;
02993 
02994         /* root directory is directory */
02995         if (*dir == 0) return true;
02996 
02997         return getDirClustNum(dir, &dummyClust, false);
02998 }
02999 
03000 Bit32u fatDrive::GetPartitionOffset(void) {
03001         return partSectOff;
03002 }
03003 
03004 Bit32u fatDrive::GetFirstClusterOffset(void) {
03005     return firstDataSector - partSectOff;
03006 }
03007 
03008 Bit32u fatDrive::GetHighestClusterNumber(void) {
03009     return CountOfClusters + 1ul;
03010 }