DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/drive_fat.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, 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 class fatFile : public DOS_File {
00045 public:
00046         fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive);
00047         bool Read(Bit8u * data,Bit16u * size);
00048         bool Write(const Bit8u * data,Bit16u * size);
00049         bool Seek(Bit32u * pos,Bit32u type);
00050         bool Close();
00051         Bit16u GetInformation(void);
00052     void Flush(void);
00053         bool UpdateDateTimeFromHost(void);   
00054         Bit32u GetSeekPos(void);
00055 public:
00056         Bit32u firstCluster;
00057         Bit32u seekpos;
00058         Bit32u filelength;
00059         Bit32u currentSector;
00060         Bit32u curSectOff;
00061         Bit8u sectorBuffer[SECTOR_SIZE_MAX];
00062         /* Record of where in the directory structure this file is located */
00063         Bit32u dirCluster;
00064         Bit32u dirIndex;
00065 
00066     bool modified;
00067         bool loadedSector;
00068         fatDrive *myDrive;
00069 private:
00070 #if 0/*unused*/
00071     enum { NONE,READ,WRITE } last_action;
00072         Bit16u info;
00073 #endif
00074 };
00075 
00076 void time_t_to_DOS_DateTime(Bit16u &t,Bit16u &d,time_t unix_time) {
00077     struct tm *tm = localtime(&unix_time);
00078     if (tm == NULL) return;
00079 
00080     /* NTS: tm->tm_year = years since 1900,
00081      *      tm->tm_mon = months since January therefore January == 0
00082      *      tm->tm_mday = day of the month, starting with 1 */
00083 
00084     t = ((unsigned int)tm->tm_hour << 11u) + ((unsigned int)tm->tm_min << 5u) + ((unsigned int)tm->tm_sec >> 1u);
00085     d = (((unsigned int)tm->tm_year - 80u) << 9u) + (((unsigned int)tm->tm_mon + 1u) << 5u) + (unsigned int)tm->tm_mday;
00086 }
00087 
00088 /* IN - char * filename: Name in regular filename format, e.g. bob.txt */
00089 /* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob     txt */
00090 static void convToDirFile(const char *filename, char *filearray) {
00091         Bit32u charidx = 0;
00092         Bit32u flen,i;
00093         flen = (Bit32u)strlen(filename);
00094         memset(filearray, 32, 11);
00095         for(i=0;i<flen;i++) {
00096                 if(charidx >= 11) break;
00097                 if(filename[i] != '.') {
00098                         filearray[charidx] = filename[i];
00099                         charidx++;
00100                 } else {
00101                         charidx = 8;
00102                 }
00103         }
00104 }
00105 
00106 fatFile::fatFile(const char* /*name*/, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) {
00107         Bit32u seekto = 0;
00108         firstCluster = startCluster;
00109         myDrive = useDrive;
00110         filelength = fileLen;
00111     modified = false;
00112         open = true;
00113         loadedSector = false;
00114         curSectOff = 0;
00115         seekpos = 0;
00116         memset(&sectorBuffer[0], 0, sizeof(sectorBuffer));
00117         
00118         if(filelength > 0) {
00119                 Seek(&seekto, DOS_SEEK_SET);
00120         }
00121 }
00122 
00123 void fatFile::Flush(void) {
00124 #if 0//UNTESTED: THIS MAY CAUSE FURTHER PROBLEMS
00125     // FIXME: Copy-pasta from Close
00126         if (loadedSector) {
00127         myDrive->writeSector(currentSector, sectorBuffer);
00128         loadedSector = false;
00129     }
00130 #endif
00131 
00132     if (modified || newtime) {
00133         direntry tmpentry;
00134 
00135         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00136 
00137         if (newtime) {
00138             tmpentry.modTime = time;
00139             tmpentry.modDate = date;
00140         }
00141         else {
00142             Bit16u ct,cd;
00143 
00144             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
00145 
00146             tmpentry.modTime = ct;
00147             tmpentry.modDate = cd;
00148         }
00149 
00150         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00151         modified = false;
00152         newtime = false;
00153     }
00154 }
00155 
00156 bool fatFile::Read(Bit8u * data, Bit16u *size) {
00157         if ((this->flags & 0xf) == OPEN_WRITE) {        // check if file opened in write-only mode
00158                 DOS_SetError(DOSERR_ACCESS_DENIED);
00159                 return false;
00160         }
00161         Bit16u sizedec, sizecount;
00162         if(seekpos >= filelength) {
00163                 *size = 0;
00164                 return true;
00165         }
00166 
00167         if (!loadedSector) {
00168                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00169                 if(currentSector == 0) {
00170                         /* EOC reached before EOF */
00171                         *size = 0;
00172                         loadedSector = false;
00173                         return true;
00174                 }
00175                 curSectOff = seekpos % myDrive->getSectorSize();
00176                 myDrive->readSector(currentSector, sectorBuffer);
00177                 loadedSector = true;
00178         }
00179 
00180         sizedec = *size;
00181         sizecount = 0;
00182         while(sizedec != 0) {
00183                 if(seekpos >= filelength) {
00184                         *size = sizecount;
00185                         return true; 
00186                 }
00187                 data[sizecount++] = sectorBuffer[curSectOff++];
00188                 seekpos++;
00189                 if(curSectOff >= myDrive->getSectorSize()) {
00190                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00191                         if(currentSector == 0) {
00192                                 /* EOC reached before EOF */
00193                                 //LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength);
00194                                 *size = sizecount;
00195                                 loadedSector = false;
00196                                 return true;
00197                         }
00198                         curSectOff = 0;
00199                         myDrive->readSector(currentSector, sectorBuffer);
00200                         loadedSector = true;
00201                         //LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos);
00202                 }
00203                 --sizedec;
00204         }
00205         *size =sizecount;
00206         return true;
00207 }
00208 
00209 bool fatFile::Write(const Bit8u * data, Bit16u *size) {
00210         if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
00211                 DOS_SetError(DOSERR_ACCESS_DENIED);
00212                 return false;
00213         }
00214 
00215         direntry tmpentry;
00216         Bit16u sizedec, sizecount;
00217         sizedec = *size;
00218         sizecount = 0;
00219         
00220         if(seekpos < filelength && *size == 0) {
00221                 /* Truncate file to current position */
00222                 myDrive->deleteClustChain(firstCluster, seekpos);
00223                 filelength = seekpos;
00224                 goto finalizeWrite;
00225         }
00226 
00227         if(seekpos > filelength) {
00228                 /* Extend file to current position */
00229                 Bit32u clustSize = myDrive->getClusterSize();
00230                 if(filelength == 0) {
00231                         firstCluster = myDrive->getFirstFreeClust();
00232                         if(firstCluster == 0) goto finalizeWrite; // out of space
00233                         myDrive->allocateCluster(firstCluster, 0);
00234                         filelength = clustSize;
00235                 }
00236                 filelength = ((filelength - 1) / clustSize + 1) * clustSize;
00237                 while(filelength < seekpos) {
00238                         if(myDrive->appendCluster(firstCluster) == 0) goto finalizeWrite; // out of space
00239                         filelength += clustSize;
00240                 }
00241                 if(filelength > seekpos) filelength = seekpos;
00242                 if(*size == 0) goto finalizeWrite;
00243         }
00244 
00245         while(sizedec != 0) {
00246                 /* Increase filesize if necessary */
00247                 if(seekpos >= filelength) {
00248                         if(filelength == 0) {
00249                                 firstCluster = myDrive->getFirstFreeClust();
00250                                 if(firstCluster == 0) goto finalizeWrite; // out of space
00251                                 myDrive->allocateCluster(firstCluster, 0);
00252                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00253                                 myDrive->readSector(currentSector, sectorBuffer);
00254                                 loadedSector = true;
00255                         }
00256                         if (!loadedSector) {
00257                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00258                                 if(currentSector == 0) {
00259                                         /* EOC reached before EOF - try to increase file allocation */
00260                                         myDrive->appendCluster(firstCluster);
00261                                         /* Try getting sector again */
00262                                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00263                                         if(currentSector == 0) {
00264                                                 /* No can do. lets give up and go home.  We must be out of room */
00265                                                 goto finalizeWrite;
00266                                         }
00267                                 }
00268                                 curSectOff = seekpos % myDrive->getSectorSize();
00269                                 myDrive->readSector(currentSector, sectorBuffer);
00270                                 loadedSector = true;
00271                         }
00272                         filelength = seekpos+1;
00273                 }
00274                 sectorBuffer[curSectOff++] = data[sizecount++];
00275                 seekpos++;
00276                 if(curSectOff >= myDrive->getSectorSize()) {
00277                         if(loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00278             loadedSector = false;
00279 
00280             if (sizedec <= 1) goto finalizeWrite; // --sizedec == 0
00281 
00282                         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00283                         if(currentSector == 0) {
00284                                 /* EOC reached before EOF - try to increase file allocation */
00285                                 myDrive->appendCluster(firstCluster);
00286                                 /* Try getting sector again */
00287                                 currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00288                                 if(currentSector == 0) {
00289                                         /* No can do. lets give up and go home.  We must be out of room */
00290                                         goto finalizeWrite;
00291                                 }
00292                         }
00293                         curSectOff = 0;
00294                         myDrive->readSector(currentSector, sectorBuffer);
00295                         loadedSector = true;
00296                 }
00297         modified = true;
00298                 --sizedec;
00299         }
00300         if(curSectOff>0 && loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00301 
00302 finalizeWrite:
00303         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00304         tmpentry.entrysize = filelength;
00305         tmpentry.loFirstClust = (Bit16u)firstCluster;
00306         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00307 
00308         *size =sizecount;
00309         return true;
00310 }
00311 
00312 bool fatFile::Seek(Bit32u *pos, Bit32u type) {
00313         Bit32s seekto=0;
00314         
00315         switch(type) {
00316                 case DOS_SEEK_SET:
00317                         seekto = (Bit32s)*pos;
00318                         break;
00319                 case DOS_SEEK_CUR:
00320                         /* Is this relative seek signed? */
00321                         seekto = (Bit32s)*pos + (Bit32s)seekpos;
00322                         break;
00323                 case DOS_SEEK_END:
00324                         seekto = (Bit32s)filelength + (Bit32s)*pos;
00325                         break;
00326         }
00327 //      LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto);
00328 
00329         if(seekto<0) seekto = 0;
00330         seekpos = (Bit32u)seekto;
00331         currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos);
00332         if (currentSector == 0) {
00333                 /* not within file size, thus no sector is available */
00334                 loadedSector = false;
00335         } else {
00336                 curSectOff = seekpos % myDrive->getSectorSize();
00337                 myDrive->readSector(currentSector, sectorBuffer);
00338                 loadedSector = true;
00339         }
00340         *pos = seekpos;
00341         return true;
00342 }
00343 
00344 bool fatFile::Close() {
00345         /* Flush buffer */
00346         if (loadedSector) myDrive->writeSector(currentSector, sectorBuffer);
00347 
00348     if (modified || newtime) {
00349         direntry tmpentry;
00350 
00351         myDrive->directoryBrowse(dirCluster, &tmpentry, (Bit32s)dirIndex);
00352 
00353         if (newtime) {
00354             tmpentry.modTime = time;
00355             tmpentry.modDate = date;
00356         }
00357         else {
00358             Bit16u ct,cd;
00359 
00360             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
00361 
00362             tmpentry.modTime = ct;
00363             tmpentry.modDate = cd;
00364         }
00365 
00366         myDrive->directoryChange(dirCluster, &tmpentry, (Bit32s)dirIndex);
00367     }
00368 
00369         return false;
00370 }
00371 
00372 Bit16u fatFile::GetInformation(void) {
00373         return 0;
00374 }
00375 
00376 bool fatFile::UpdateDateTimeFromHost(void) {
00377         return true;
00378 }
00379 
00380 Bit32u fatFile::GetSeekPos() {
00381         return seekpos;
00382 }
00383 
00384 Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) {
00385         return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector;
00386 }
00387 
00388 Bit32u fatDrive::getClusterValue(Bit32u clustNum) {
00389         Bit32u fatoffset=0;
00390         Bit32u fatsectnum;
00391         Bit32u fatentoff;
00392         Bit32u clustValue=0;
00393 
00394         switch(fattype) {
00395                 case FAT12:
00396                         fatoffset = clustNum + (clustNum / 2);
00397                         break;
00398                 case FAT16:
00399                         fatoffset = clustNum * 2;
00400                         break;
00401                 case FAT32:
00402                         fatoffset = clustNum * 4;
00403                         break;
00404         }
00405         fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
00406         fatentoff = fatoffset % bootbuffer.bytespersector;
00407 
00408     assert((bootbuffer.bytespersector * (Bitu)2) <= sizeof(fatSectBuffer));
00409 
00410         if(curFatSect != fatsectnum) {
00411                 /* Load two sectors at once for FAT12 */
00412                 readSector(fatsectnum, &fatSectBuffer[0]);
00413                 if (fattype==FAT12)
00414                         readSector(fatsectnum+1, &fatSectBuffer[bootbuffer.bytespersector]);
00415                 curFatSect = fatsectnum;
00416         }
00417 
00418         switch(fattype) {
00419                 case FAT12:
00420                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00421                         if(clustNum & 0x1) {
00422                                 clustValue >>= 4;
00423                         } else {
00424                                 clustValue &= 0xfff;
00425                         }
00426                         break;
00427                 case FAT16:
00428                         clustValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00429                         break;
00430                 case FAT32:
00431                         clustValue = *((Bit32u *)&fatSectBuffer[fatentoff]);
00432                         break;
00433         }
00434 
00435         return clustValue;
00436 }
00437 
00438 void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) {
00439         Bit32u fatoffset=0;
00440         Bit32u fatsectnum;
00441         Bit32u fatentoff;
00442 
00443         switch(fattype) {
00444                 case FAT12:
00445                         fatoffset = clustNum + (clustNum / 2);
00446                         break;
00447                 case FAT16:
00448                         fatoffset = clustNum * 2;
00449                         break;
00450                 case FAT32:
00451                         fatoffset = clustNum * 4;
00452                         break;
00453         }
00454         fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff;
00455         fatentoff = fatoffset % bootbuffer.bytespersector;
00456 
00457     assert((bootbuffer.bytespersector * (Bitu)2) <= sizeof(fatSectBuffer));
00458 
00459         if(curFatSect != fatsectnum) {
00460                 /* Load two sectors at once for FAT12 */
00461                 readSector(fatsectnum, &fatSectBuffer[0]);
00462                 if (fattype==FAT12)
00463                         readSector(fatsectnum+1, &fatSectBuffer[bootbuffer.bytespersector]);
00464                 curFatSect = fatsectnum;
00465         }
00466 
00467         switch(fattype) {
00468                 case FAT12: {
00469                         Bit16u tmpValue = *((Bit16u *)&fatSectBuffer[fatentoff]);
00470                         if(clustNum & 0x1) {
00471                                 clustValue &= 0xfff;
00472                                 clustValue <<= 4;
00473                                 tmpValue &= 0xf;
00474                                 tmpValue |= (Bit16u)clustValue;
00475 
00476                         } else {
00477                                 clustValue &= 0xfff;
00478                                 tmpValue &= 0xf000;
00479                                 tmpValue |= (Bit16u)clustValue;
00480                         }
00481                         *((Bit16u *)&fatSectBuffer[fatentoff]) = tmpValue;
00482                         break;
00483                         }
00484                 case FAT16:
00485                         *((Bit16u *)&fatSectBuffer[fatentoff]) = (Bit16u)clustValue;
00486                         break;
00487                 case FAT32:
00488                         *((Bit32u *)&fatSectBuffer[fatentoff]) = clustValue;
00489                         break;
00490         }
00491         for(unsigned int fc=0;fc<bootbuffer.fatcopies;fc++) {
00492                 writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]);
00493                 if (fattype==FAT12) {
00494                         if (fatentoff >= (bootbuffer.bytespersector-1U))
00495                                 writeSector(fatsectnum+1u+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[bootbuffer.bytespersector]);
00496                 }
00497         }
00498 }
00499 
00500 bool fatDrive::getEntryName(const char *fullname, char *entname) {
00501         char dirtoken[DOS_PATHLENGTH];
00502 
00503         char * findDir;
00504         char * findFile;
00505         strcpy(dirtoken,fullname);
00506 
00507         //LOG_MSG("Testing for filename %s", fullname);
00508         findDir = strtok(dirtoken,"\\");
00509         if (findDir==NULL) {
00510                 return true;    // root always exists
00511         }
00512         findFile = findDir;
00513         while(findDir != NULL) {
00514                 findFile = findDir;
00515                 findDir = strtok(NULL,"\\");
00516         }
00517         strcpy(entname, findFile);
00518         return true;
00519 }
00520 
00521 void fatDrive::UpdateBootVolumeLabel(const char *label) {
00522     /* if the extended boot signature is 0x29 there is a copy of the volume label at 0x2B */
00523     unsigned char *p = (unsigned char*)(&bootbuffer);
00524 
00525     if (p[0x26] == 0x29) {
00526         unsigned int i = 0;
00527 
00528         while (i < 11 && *label != 0) p[0x2B+(i++)] = toupper(*label++);
00529         while (i < 11)                p[0x2B+(i++)] = ' ';
00530 
00531         loadedDisk->Write_AbsoluteSector(0+partSectOff,&bootbuffer);
00532     }
00533 }
00534 
00535 void fatDrive::SetLabel(const char *label, bool /*iscdrom*/, bool /*updatable*/) {
00536     direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
00537     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
00538     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
00539     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
00540 
00541     if (fattype == FAT32) return;
00542 
00543     if (*label != 0) {
00544         /* Add a volume label entry, by appending to the root directory.
00545          * The DOS program calling this entry is supposed to delete the
00546          * existing volume label. MS-DOS 7.0 and higher appear to automatically
00547          * rewrite the volume label and manage them tighter obviously due
00548          * to the way LFNs are stored in the filesystem. */
00549         for (unsigned int i=0;i < bootbuffer.rootdirentries;i++) {
00550             unsigned int di = i % dirent_per_sector;
00551 
00552             if (di == 0) {
00553                 memset(sectbuf,0,sizeof(sectbuf));
00554                         readSector((Bit32u)(firstRootDirSect+(i/dirent_per_sector)),sectbuf);
00555             }
00556 
00557             if (sectbuf[di].entryname[0] == 0x00 ||
00558                 sectbuf[di].entryname[0] == 0xe5) {
00559                 memset(&sectbuf[di],0,sizeof(sectbuf[di]));
00560                 sectbuf[di].attrib = DOS_ATTR_VOLUME;
00561                 {
00562                     unsigned int i = 0;
00563                     const char *s = label;
00564                     while (i < 11 && *s != 0) sectbuf[di].entryname[i++] = toupper(*s++);
00565                     while (i < 11)            sectbuf[di].entryname[i++] = ' ';
00566                 }
00567                 writeSector((Bit32u)(firstRootDirSect+(i/dirent_per_sector)),sectbuf);
00568                         labelCache.SetLabel(label, false, true);
00569                 UpdateBootVolumeLabel(label);
00570                 break;
00571             }
00572         }
00573     }
00574     else {
00575         /* erase ONE volume label from the root directory */
00576         for (unsigned int i=0;i < bootbuffer.rootdirentries;i++) {
00577             unsigned int di = i % dirent_per_sector;
00578 
00579             if (di == 0) {
00580                 memset(sectbuf,0,sizeof(sectbuf));
00581                         readSector((Bit32u)(firstRootDirSect+(i/dirent_per_sector)),sectbuf);
00582             }
00583 
00584             if (sectbuf[di].entryname[0] == 0x00 ||
00585                 sectbuf[di].entryname[0] == 0xe5)
00586                 continue;
00587 
00588             // TODO: If MS-DOS 7.0 or higher skip anything with attrib == 0x0F to avoid erasing LFNs
00589             if (sectbuf[di].attrib & DOS_ATTR_VOLUME) {
00590                 /* TODO: There needs to be a way for FCB delete to erase the volume label by name instead
00591                  *       of just picking the first one */
00592                 /* found one */
00593                 sectbuf[di].entryname[0] = 0xe5;
00594                 writeSector((Bit32u)(firstRootDirSect+(i/dirent_per_sector)),sectbuf);
00595                         labelCache.SetLabel("", false, true);
00596                 UpdateBootVolumeLabel("NO NAME");
00597                 break;
00598             }
00599         }
00600     }
00601 }
00602 
00603 bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) {
00604         size_t len = strlen(filename);
00605         char dirtoken[DOS_PATHLENGTH];
00606         Bit32u currentClust = 0;
00607 
00608         direntry foundEntry;
00609         char * findDir;
00610         char * findFile;
00611         strcpy(dirtoken,filename);
00612         findFile=dirtoken;
00613 
00614         /* Skip if testing in root directory */
00615         if ((len>0) && (filename[len-1]!='\\')) {
00616                 //LOG_MSG("Testing for filename %s", filename);
00617                 findDir = strtok(dirtoken,"\\");
00618                 findFile = findDir;
00619                 while(findDir != NULL) {
00620                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00621                         imgDTA->SetDirID(0);
00622                         
00623                         findFile = findDir;
00624                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break;
00625                         else {
00626                                 //Found something. See if it's a directory (findfirst always finds regular files)
00627                                 char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00628                                 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
00629                                 if(!(find_attr & DOS_ATTR_DIRECTORY)) break;
00630                         }
00631 
00632                         currentClust = foundEntry.loFirstClust;
00633                         findDir = strtok(NULL,"\\");
00634                 }
00635         } else {
00636                 /* Set to root directory */
00637         }
00638 
00639         /* Search found directory for our file */
00640         imgDTA->SetupSearch(0,0x7,findFile);
00641         imgDTA->SetDirID(0);
00642         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false;
00643 
00644         memcpy(useEntry, &foundEntry, sizeof(direntry));
00645         *dirClust = (Bit32u)currentClust;
00646         *subEntry = ((Bit32u)imgDTA->GetDirID()-1);
00647         return true;
00648 }
00649 
00650 bool fatDrive::getDirClustNum(const char *dir, Bit32u *clustNum, bool parDir) {
00651         Bit32u len = (Bit32u)strlen(dir);
00652         char dirtoken[DOS_PATHLENGTH];
00653         Bit32u currentClust = 0;
00654         direntry foundEntry;
00655         char * findDir;
00656         strcpy(dirtoken,dir);
00657 
00658         /* Skip if testing for root directory */
00659         if ((len>0) && (dir[len-1]!='\\')) {
00660                 //LOG_MSG("Testing for dir %s", dir);
00661                 findDir = strtok(dirtoken,"\\");
00662                 while(findDir != NULL) {
00663                         imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir);
00664                         imgDTA->SetDirID(0);
00665                         findDir = strtok(NULL,"\\");
00666                         if(parDir && (findDir == NULL)) break;
00667 
00668                         char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr;
00669                         if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) {
00670                                 return false;
00671                         } else {
00672                                 imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr);
00673                                 if(!(find_attr &DOS_ATTR_DIRECTORY)) return false;
00674                         }
00675                         currentClust = foundEntry.loFirstClust;
00676 
00677                 }
00678                 *clustNum = currentClust;
00679         } else {
00680                 /* Set to root directory */
00681                 *clustNum = 0;
00682         }
00683         return true;
00684 }
00685 
00686 Bit8u fatDrive::readSector(Bit32u sectnum, void * data) {
00687         if (absolute) return Read_AbsoluteSector(sectnum, data);
00688     assert(!IS_PC98_ARCH);
00689         Bit32u cylindersize = (unsigned int)bootbuffer.headcount * (unsigned int)bootbuffer.sectorspertrack;
00690         Bit32u cylinder = sectnum / cylindersize;
00691         sectnum %= cylindersize;
00692         Bit32u head = sectnum / bootbuffer.sectorspertrack;
00693         Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
00694         return loadedDisk->Read_Sector(head, cylinder, sector, data);
00695 }       
00696 
00697 Bit8u fatDrive::writeSector(Bit32u sectnum, void * data) {
00698         if (absolute) return Write_AbsoluteSector(sectnum, data);
00699     assert(!IS_PC98_ARCH);
00700         Bit32u cylindersize = (unsigned int)bootbuffer.headcount * (unsigned int)bootbuffer.sectorspertrack;
00701         Bit32u cylinder = sectnum / cylindersize;
00702         sectnum %= cylindersize;
00703         Bit32u head = sectnum / bootbuffer.sectorspertrack;
00704         Bit32u sector = sectnum % bootbuffer.sectorspertrack + 1L;
00705         return loadedDisk->Write_Sector(head, cylinder, sector, data);
00706 }
00707 
00708 Bit32u fatDrive::getSectorSize(void) {
00709         return bootbuffer.bytespersector;
00710 }
00711 
00712 Bit32u fatDrive::getClusterSize(void) {
00713         return (unsigned int)bootbuffer.sectorspercluster * (unsigned int)bootbuffer.bytespersector;
00714 }
00715 
00716 Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) {
00717         return  getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector);
00718 }
00719 
00720 Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) {
00721         Bit32s skipClust = (Bit32s)(logicalSector / bootbuffer.sectorspercluster);
00722         Bit32u sectClust = (Bit32u)(logicalSector % bootbuffer.sectorspercluster);
00723 
00724         Bit32u currentClust = startClustNum;
00725         Bit32u testvalue;
00726 
00727         while(skipClust!=0) {
00728                 bool isEOF = false;
00729                 testvalue = getClusterValue(currentClust);
00730                 switch(fattype) {
00731                         case FAT12:
00732                                 if(testvalue >= 0xff8) isEOF = true;
00733                                 break;
00734                         case FAT16:
00735                                 if(testvalue >= 0xfff8) isEOF = true;
00736                                 break;
00737                         case FAT32:
00738                                 if(testvalue >= 0xfffffff8) isEOF = true;
00739                                 break;
00740                 }
00741                 if((isEOF) && (skipClust>=1)) {
00742                         //LOG_MSG("End of cluster chain reached before end of logical sector seek!");
00743                         if (skipClust == 1 && fattype == FAT12) {
00744                                 //break;
00745                                 LOG(LOG_DOSMISC,LOG_ERROR)("End of cluster chain reached, but maybe good afterall ?");
00746                         }
00747                         return 0;
00748                 }
00749                 currentClust = testvalue;
00750                 --skipClust;
00751         }
00752 
00753         return (getClustFirstSect(currentClust) + sectClust);
00754 }
00755 
00756 void fatDrive::deleteClustChain(Bit32u startCluster, Bit32u bytePos) {
00757         Bit32u clustSize = getClusterSize();
00758         Bit32u endClust = (bytePos + clustSize - 1) / clustSize;
00759         Bit32u countClust = 1;
00760 
00761         Bit32u testvalue;
00762         Bit32u currentClust = startCluster;
00763         bool isEOF = false;
00764         while(!isEOF) {
00765                 testvalue = getClusterValue(currentClust);
00766                 if(testvalue == 0) {
00767                         /* What the crap?  Cluster is already empty - BAIL! */
00768                         break;
00769                 }
00770                 switch(fattype) {
00771                         case FAT12:
00772                                 if(testvalue >= 0xff8) isEOF = true;
00773                                 break;
00774                         case FAT16:
00775                                 if(testvalue >= 0xfff8) isEOF = true;
00776                                 break;
00777                         case FAT32:
00778                                 if(testvalue >= 0xfffffff8) isEOF = true;
00779                                 break;
00780                 }
00781                 if(countClust == endClust && !isEOF) {
00782                         /* Mark cluster as end */
00783                         switch(fattype) {
00784                                 case FAT12:
00785                                         setClusterValue(currentClust, 0xfff);
00786                                         break;
00787                                 case FAT16:
00788                                         setClusterValue(currentClust, 0xffff);
00789                                         break;
00790                                 case FAT32:
00791                                         setClusterValue(currentClust, 0xffffffff);
00792                                         break;
00793                         }
00794                 } else if(countClust > endClust) {
00795                         /* Mark cluster as empty */
00796                         setClusterValue(currentClust, 0);
00797                 }
00798                 if(isEOF) break;
00799                 currentClust = testvalue;
00800                 countClust++;
00801         }
00802 }
00803 
00804 Bit32u fatDrive::appendCluster(Bit32u startCluster) {
00805         Bit32u testvalue;
00806         Bit32u currentClust = startCluster;
00807         bool isEOF = false;
00808         
00809         while(!isEOF) {
00810                 testvalue = getClusterValue(currentClust);
00811                 switch(fattype) {
00812                         case FAT12:
00813                                 if(testvalue >= 0xff8) isEOF = true;
00814                                 break;
00815                         case FAT16:
00816                                 if(testvalue >= 0xfff8) isEOF = true;
00817                                 break;
00818                         case FAT32:
00819                                 if(testvalue >= 0xfffffff8) isEOF = true;
00820                                 break;
00821                 }
00822                 if(isEOF) break;
00823                 currentClust = testvalue;
00824         }
00825 
00826         Bit32u newClust = getFirstFreeClust();
00827         /* Drive is full */
00828         if(newClust == 0) return 0;
00829 
00830         if(!allocateCluster(newClust, currentClust)) return 0;
00831 
00832         zeroOutCluster(newClust);
00833 
00834         return newClust;
00835 }
00836 
00837 bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) {
00838 
00839         /* Can't allocate cluster #0 */
00840         if(useCluster == 0) return false;
00841 
00842         if(prevCluster != 0) {
00843                 /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */
00844                 if(!getClusterValue(prevCluster)) return false;
00845 
00846                 /* Point cluster to new cluster in chain */
00847                 setClusterValue(prevCluster, useCluster);
00848                 //LOG_MSG("Chaining cluser %d to %d", prevCluster, useCluster);
00849         } 
00850 
00851         switch(fattype) {
00852                 case FAT12:
00853                         setClusterValue(useCluster, 0xfff);
00854                         break;
00855                 case FAT16:
00856                         setClusterValue(useCluster, 0xffff);
00857                         break;
00858                 case FAT32:
00859                         setClusterValue(useCluster, 0xffffffff);
00860                         break;
00861         }
00862         return true;
00863 }
00864 
00865 fatDrive::~fatDrive() {
00866         if (loadedDisk) {
00867                 loadedDisk->Release();
00868                 loadedDisk = NULL;
00869         }
00870 }
00871 
00872 /* PC-98 IPL1 partition table entry.
00873  * Taken from GNU Parted source code.
00874  * Maximum 16 entries. */
00875 #pragma pack(push,1)
00876 struct _PC98RawPartition {
00877         uint8_t         mid;            /* 0x80 - boot */
00878         uint8_t         sid;            /* 0x80 - active */
00879         uint8_t         dum1;           /* dummy for padding */
00880         uint8_t         dum2;           /* dummy for padding */
00881         uint8_t         ipl_sect;       /* IPL sector */
00882         uint8_t         ipl_head;       /* IPL head */
00883         uint16_t        ipl_cyl;        /* IPL cylinder */
00884         uint8_t         sector;         /* starting sector */
00885         uint8_t         head;           /* starting head */
00886         uint16_t        cyl;            /* starting cylinder */
00887         uint8_t         end_sector;     /* end sector */
00888         uint8_t         end_head;       /* end head */
00889         uint16_t        end_cyl;        /* end cylinder */
00890         char            name[16];
00891 };
00892 #pragma pack(pop)
00893 
00894 fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, std::vector<std::string> &options) : loadedDisk(NULL) {
00895         created_successfully = true;
00896         FILE *diskfile;
00897         Bit32u filesize;
00898         
00899         if(imgDTASeg == 0) {
00900                 imgDTASeg = DOS_GetMemory(4,"imgDTASeg");
00901                 imgDTAPtr = RealMake(imgDTASeg, 0);
00902                 imgDTA    = new DOS_DTA(imgDTAPtr);
00903         }
00904 
00905         diskfile = fopen64(sysFilename, "rb+");
00906         if(!diskfile) {created_successfully = false;return;}
00907 
00908     // all disk I/O is in sector-sized blocks.
00909     // modern OSes have good caching.
00910     // there are plenty of cases where this code aborts, exits, or re-execs itself (such as reboot)
00911     // where stdio buffering can cause loss of data.
00912     setbuf(diskfile,NULL);
00913 
00914         QCow2Image::QCow2Header qcow2_header = QCow2Image::read_header(diskfile);
00915 
00916         if (qcow2_header.magic == QCow2Image::magic && (qcow2_header.version == 2 || qcow2_header.version == 3)){
00917                 Bit32u cluster_size = 1u << qcow2_header.cluster_bits;
00918                 if ((bytesector < 512) || ((cluster_size % bytesector) != 0)){
00919                         created_successfully = false;
00920                         return;
00921                 }
00922                 filesize = (Bit32u)(qcow2_header.size / 1024L);
00923                 loadedDisk = new QCow2Disk(qcow2_header, diskfile, (Bit8u *)sysFilename, filesize, bytesector, (filesize > 2880));
00924         }
00925         else{
00926                 fseeko64(diskfile, 0L, SEEK_SET);
00927         assert(sizeof(bootbuffer.bootcode) >= 256);
00928         size_t readResult = fread(bootbuffer.bootcode,256,1,diskfile); // look for magic signatures
00929         if (readResult != 1) {
00930             LOG(LOG_IO, LOG_ERROR) ("Reading error in fatDrive constructor\n");
00931             return;
00932         }
00933 
00934         const char *ext = strrchr(sysFilename,'.');
00935 
00936         if (ext != NULL && !strcasecmp(ext, ".d88")) {
00937             fseeko64(diskfile, 0L, SEEK_END);
00938             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00939             loadedDisk = new imageDiskD88(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00940         }
00941         else if (!memcmp(bootbuffer.bootcode,"VFD1.",5)) { /* FDD files */
00942             fseeko64(diskfile, 0L, SEEK_END);
00943             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00944             loadedDisk = new imageDiskVFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00945         }
00946         else if (!memcmp(bootbuffer.bootcode,"T98FDDIMAGE.R0\0\0",16)) {
00947             fseeko64(diskfile, 0L, SEEK_END);
00948             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00949             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880), 0);
00950         }
00951         else if (!memcmp(bootbuffer.bootcode,"T98FDDIMAGE.R1\0\0",16)) {
00952             fseeko64(diskfile, 0L, SEEK_END);
00953             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00954             loadedDisk = new imageDiskNFD(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880), 1);
00955         }
00956         else {
00957             fseeko64(diskfile, 0L, SEEK_END);
00958             filesize = (Bit32u)(ftello64(diskfile) / 1024L);
00959             loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880));
00960         }
00961         }
00962 
00963     fatDriveInit(sysFilename, bytesector, cylsector, headscyl, cylinders, filesize, options);
00964 }
00965 
00966 fatDrive::fatDrive(imageDisk *sourceLoadedDisk, std::vector<std::string> &options) : loadedDisk(NULL) {
00967         if (sourceLoadedDisk == 0) {
00968                 created_successfully = false;
00969                 return;
00970         }
00971         created_successfully = true;
00972         
00973         if(imgDTASeg == 0) {
00974                 imgDTASeg = DOS_GetMemory(4,"imgDTASeg");
00975                 imgDTAPtr = RealMake(imgDTASeg, 0);
00976                 imgDTA    = new DOS_DTA(imgDTAPtr);
00977         }
00978 
00979     loadedDisk = sourceLoadedDisk;
00980 
00981     fatDriveInit("", loadedDisk->sector_size, loadedDisk->sectors, loadedDisk->heads, loadedDisk->cylinders, loadedDisk->diskSizeK, options);
00982 }
00983 
00984 Bit8u fatDrive::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00985     if (loadedDisk != NULL) {
00986         /* this will only work if the logical sector size is larger than the disk sector size */
00987         const unsigned int lsz = loadedDisk->getSectSize();
00988         unsigned int c = sector_size / lsz;
00989 
00990         if (c != 0 && (sector_size % lsz) == 0) {
00991             Bit32u ssect = sectnum * c;
00992 
00993             while (c-- != 0) {
00994                 if (loadedDisk->Read_AbsoluteSector(ssect++,data) != 0)
00995                     return 0x05;
00996 
00997                 data = (void*)((char*)data + lsz);
00998             }
00999 
01000             return 0;
01001         }
01002     }
01003 
01004     return 0x05;
01005 }
01006 
01007 Bit8u fatDrive::Write_AbsoluteSector(Bit32u sectnum, void * data) {
01008     if (loadedDisk != NULL) {
01009         /* this will only work if the logical sector size is larger than the disk sector size */
01010         const unsigned int lsz = loadedDisk->getSectSize();
01011         unsigned int c = sector_size / lsz;
01012 
01013         if (c != 0 && (sector_size % lsz) == 0) {
01014             Bit32u ssect = sectnum * c;
01015 
01016             while (c-- != 0) {
01017                 if (loadedDisk->Write_AbsoluteSector(ssect++,data) != 0)
01018                     return 0x05;
01019 
01020                 data = (void*)((char*)data + lsz);
01021             }
01022 
01023             return 0;
01024         }
01025     }
01026 
01027     return 0x05;
01028 }
01029 
01030 Bit32u fatDrive::getSectSize(void) {
01031     return sector_size;
01032 }
01033 
01034 void fatDrive::UpdateDPB(unsigned char dos_drive) {
01035     PhysPt ptr = DOS_Get_DPB(dos_drive);
01036     if (ptr != PhysPt(0)) {
01037         mem_writew(ptr+0x02,bootbuffer.bytespersector);             // +2 = bytes per sector
01038         mem_writeb(ptr+0x04,bootbuffer.sectorspercluster - 1);      // +4 = highest sector within a cluster
01039         mem_writeb(ptr+0x05,bitop::log2(bootbuffer.sectorspercluster));// +5 = shift count to convert clusters to sectors
01040         mem_writew(ptr+0x06,bootbuffer.reservedsectors);            // +6 = number of reserved sectors at start of partition
01041         mem_writeb(ptr+0x08,bootbuffer.fatcopies);                  // +8 = number of FATs (file allocation tables)
01042         mem_writew(ptr+0x09,bootbuffer.rootdirentries);             // +9 = number of root directory entries
01043         mem_writew(ptr+0x0B,(uint16_t)(firstDataSector-partSectOff));// +11 = number of first sector containing user data
01044         mem_writew(ptr+0x0D,(uint16_t)CountOfClusters + 1);         // +13 = highest cluster number
01045         mem_writew(ptr+0x0F,(uint16_t)bootbuffer.sectorsperfat);    // +15 = sectors per FAT
01046         mem_writew(ptr+0x11,(uint16_t)(firstRootDirSect-partSectOff));// +17 = sector number of first directory sector
01047         mem_writed(ptr+0x13,0);                                     // +19 = address of device driver header (NOT IMPLEMENTED)
01048         mem_writeb(ptr+0x17,GetMediaByte());                        // +23 = media ID byte
01049         mem_writeb(ptr+0x18,0x00);                                  // +24 = disk accessed
01050         mem_writew(ptr+0x1F,0xFFFF);                                // +31 = number of free clusters or 0xFFFF if unknown
01051         // other fields, not implemented
01052     }
01053 }
01054 
01055 void fatDrive::fatDriveInit(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit64u filesize, std::vector<std::string> &options) {
01056         Bit32u startSector;
01057         bool pc98_512_to_1024_allow = false;
01058     int opt_partition_index = -1;
01059         bool is_hdd = (filesize > 2880);
01060         struct partTable mbrData;
01061 
01062         if(!loadedDisk) {
01063                 created_successfully = false;
01064                 return;
01065         }
01066 
01067     for (const auto &opt : options) {
01068         size_t equ = opt.find_first_of('=');
01069         std::string name,value;
01070 
01071         if (equ != std::string::npos) {
01072             name = opt.substr(0,equ);
01073             value = opt.substr(equ+1);
01074         }
01075         else {
01076             name = opt;
01077             value.clear();
01078         }
01079 
01080         if (name == "partidx") {
01081             if (!value.empty())
01082                 opt_partition_index = (int)atol(value.c_str());
01083         }
01084         else {
01085             LOG(LOG_MISC,LOG_DEBUG)("FAT: option '%s' = '%s' ignored, unknown",name.c_str(),value.c_str());
01086         }
01087 
01088 //        LOG_MSG("'%s' = '%s'",name.c_str(),value.c_str());
01089     }
01090 
01091         loadedDisk->Addref();
01092 
01093     if (loadedDisk->getSectSize() > sizeof(bootbuffer)) {
01094         LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize());
01095                 created_successfully = false;
01096         return;
01097     }
01098 
01099         if(is_hdd) {
01100                 /* Set user specified harddrive parameters */
01101         if (headscyl > 0 && cylinders > 0 && cylsector > 0 && bytesector > 0)
01102                 loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector);
01103 
01104         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
01105             created_successfully = false;
01106             LOG_MSG("No geometry");
01107             return;
01108         }
01109 
01110                 loadedDisk->Read_Sector(0,0,1,&mbrData);
01111 
01112                 if(mbrData.magic1!= 0x55 ||     mbrData.magic2!= 0xaa) LOG_MSG("Possibly invalid partition table in disk image.");
01113 
01114         startSector = 63;
01115 
01116         /* PC-98 bootloader support.
01117          * These can be identified by the "IPL1" in the boot sector.
01118          * These boot sectors do not have a valid partition table though the code below might
01119          * pick up a false partition #3 with a zero offset. Partition table is in sector 1 */
01120         if (!memcmp(mbrData.booter+4,"IPL1",4)) {
01121             unsigned char ipltable[SECTOR_SIZE_MAX];
01122             unsigned int max_entries = (std::min)(16UL,(unsigned long)(loadedDisk->getSectSize() / sizeof(_PC98RawPartition)));
01123             Bitu i;
01124 
01125             LOG_MSG("PC-98 IPL1 signature detected");
01126 
01127             assert(sizeof(_PC98RawPartition) == 32);
01128 
01129             memset(ipltable,0,sizeof(ipltable));
01130             loadedDisk->Read_Sector(0,0,2,ipltable);
01131 
01132             if (opt_partition_index >= 0) {
01133                 /* user knows best! */
01134                 if ((unsigned int)opt_partition_index >= max_entries) {
01135                     LOG_MSG("Partition index out of range");
01136                     created_successfully = false;
01137                     return;
01138                 }
01139 
01140                 i = (unsigned int)opt_partition_index;
01141                 _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
01142 
01143                 /* unfortunately start and end are in C/H/S geometry, so we have to translate.
01144                  * this is why it matters so much to read the geometry from the HDI header.
01145                  *
01146                  * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
01147                 startSector =
01148                     (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
01149                     (pe->head * loadedDisk->sectors) +
01150                     pe->sector;
01151 
01152                 /* Many HDI images I've encountered so far indicate 512 bytes/sector,
01153                  * but then the FAT filesystem itself indicates 1024 bytes per sector. */
01154                 pc98_512_to_1024_allow = true;
01155 
01156                 {
01157                     /* FIXME: What if the label contains SHIFT-JIS? */
01158                     std::string name = std::string(pe->name,sizeof(pe->name));
01159 
01160                     LOG_MSG("Using IPL1 entry %lu name '%s' which starts at sector %lu",
01161                         (unsigned long)i,name.c_str(),(unsigned long)startSector);
01162                 }
01163             }
01164             else {
01165                 for (i=0;i < max_entries;i++) {
01166                     _PC98RawPartition *pe = (_PC98RawPartition*)(ipltable+(i * 32));
01167 
01168                     if (pe->mid == 0 && pe->sid == 0 &&
01169                             pe->ipl_sect == 0 && pe->ipl_head == 0 && pe->ipl_cyl == 0 &&
01170                             pe->sector == 0 && pe->head == 0 && pe->cyl == 0 &&
01171                             pe->end_sector == 0 && pe->end_head == 0 && pe->end_cyl == 0)
01172                         continue; /* unused */
01173 
01174                     /* We're looking for MS-DOS partitions.
01175                      * I've heard that some other OSes were once ported to PC-98, including Windows NT and OS/2,
01176                      * so I would rather not mistake NTFS or HPFS as FAT and cause damage. --J.C.
01177                      * FIXME: Is there a better way? */
01178                     if (!strncasecmp(pe->name,"MS-DOS",6) ||
01179                         !strncasecmp(pe->name,"MSDOS",5) ||
01180                         !strncasecmp(pe->name,"Windows",7)) {
01181                         /* unfortunately start and end are in C/H/S geometry, so we have to translate.
01182                          * this is why it matters so much to read the geometry from the HDI header.
01183                          *
01184                          * NOTE: C/H/S values in the IPL1 table are similar to IBM PC except that sectors are counted from 0, not 1 */
01185                         startSector =
01186                             (pe->cyl * loadedDisk->sectors * loadedDisk->heads) +
01187                             (pe->head * loadedDisk->sectors) +
01188                             pe->sector;
01189 
01190                         /* Many HDI images I've encountered so far indicate 512 bytes/sector,
01191                          * but then the FAT filesystem itself indicates 1024 bytes per sector. */
01192                         pc98_512_to_1024_allow = true;
01193 
01194                         {
01195                             /* FIXME: What if the label contains SHIFT-JIS? */
01196                             std::string name = std::string(pe->name,sizeof(pe->name));
01197 
01198                             LOG_MSG("Using IPL1 entry %lu name '%s' which starts at sector %lu",
01199                                 (unsigned long)i,name.c_str(),(unsigned long)startSector);
01200                             break;
01201                         }
01202                     }
01203                 }
01204             }
01205 
01206             if (i == max_entries)
01207                 LOG_MSG("No partitions found in the IPL1 table");
01208         }
01209         else {
01210             /* IBM PC master boot record search */
01211             int m;
01212 
01213             if (opt_partition_index >= 0) {
01214                 /* user knows best! */
01215                 if (opt_partition_index >= 4) {
01216                     LOG_MSG("Partition index out of range");
01217                     created_successfully = false;
01218                     return;
01219                 }
01220 
01221                 startSector = mbrData.pentry[m=opt_partition_index].absSectStart;
01222             }
01223             else {
01224                 for(m=0;m<4;m++) {
01225                     /* Pick the first available partition */
01226                     if(mbrData.pentry[m].partSize != 0x00 &&
01227                             (mbrData.pentry[m].parttype == 0x01 || mbrData.pentry[m].parttype == 0x04 ||
01228                              mbrData.pentry[m].parttype == 0x06 || mbrData.pentry[m].parttype == 0x0B ||
01229                              mbrData.pentry[m].parttype == 0x0C || mbrData.pentry[m].parttype == 0x0D ||
01230                              mbrData.pentry[m].parttype == 0x0E || mbrData.pentry[m].parttype == 0x0F)) {
01231                         LOG_MSG("Using partition %d on drive (type 0x%02x); skipping %d sectors", m, mbrData.pentry[m].parttype, mbrData.pentry[m].absSectStart);
01232                         startSector = mbrData.pentry[m].absSectStart;
01233                         break;
01234                     }
01235                 }
01236             }
01237 
01238             if(m==4) LOG_MSG("No good partition found in image.");
01239         }
01240 
01241                 partSectOff = startSector;
01242         } else {
01243                 /* Get floppy disk parameters based on image size */
01244                 loadedDisk->Get_Geometry(&headscyl, &cylinders, &cylsector, &bytesector);
01245                 /* Floppy disks don't have partitions */
01246                 partSectOff = 0;
01247 
01248         if (loadedDisk->heads == 0 || loadedDisk->sectors == 0 || loadedDisk->cylinders == 0) {
01249             created_successfully = false;
01250             LOG_MSG("No geometry");
01251             return;
01252         }
01253         }
01254 
01255         loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer);
01256 
01257         if (!is_hdd) {
01258                 /* Identify floppy format */
01259                 if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 ||
01260                         (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) &&
01261                         (bootbuffer.mediadescriptor & 0xf0) == 0xf0) {
01262                         /* DOS 2.x or later format, BPB assumed valid */
01263 
01264                         if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) &&
01265                                 (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) {
01266                                 /* Fix pre-DOS 3.2 single-sided floppy */
01267                                 bootbuffer.sectorspercluster = 1;
01268                         }
01269                 } else {
01270                         /* Read media descriptor in FAT */
01271                         Bit8u sectorBuffer[512];
01272                         loadedDisk->Read_AbsoluteSector(1,&sectorBuffer);
01273                         Bit8u mdesc = sectorBuffer[0];
01274 
01275                         if (mdesc >= 0xf8) {
01276                                 /* DOS 1.x format, create BPB for 160kB floppy */
01277                                 bootbuffer.bytespersector = 512;
01278                                 bootbuffer.sectorspercluster = 1;
01279                                 bootbuffer.reservedsectors = 1;
01280                                 bootbuffer.fatcopies = 2;
01281                                 bootbuffer.rootdirentries = 64;
01282                                 bootbuffer.totalsectorcount = 320;
01283                                 bootbuffer.mediadescriptor = mdesc;
01284                                 bootbuffer.sectorsperfat = 1;
01285                                 bootbuffer.sectorspertrack = 8;
01286                                 bootbuffer.headcount = 1;
01287                                 bootbuffer.magic1 = 0x55;       // to silence warning
01288                                 bootbuffer.magic2 = 0xaa;
01289                                 if (!(mdesc & 0x2)) {
01290                                         /* Adjust for 9 sectors per track */
01291                                         bootbuffer.totalsectorcount = 360;
01292                                         bootbuffer.sectorsperfat = 2;
01293                                         bootbuffer.sectorspertrack = 9;
01294                                 }
01295                                 if (mdesc & 0x1) {
01296                                         /* Adjust for 2 sides */
01297                                         bootbuffer.sectorspercluster = 2;
01298                                         bootbuffer.rootdirentries = 112;
01299                                         bootbuffer.totalsectorcount *= 2;
01300                                         bootbuffer.headcount = 2;
01301                                 }
01302                         } else {
01303                                 /* Unknown format */
01304                                 created_successfully = false;
01305                                 return;
01306                         }
01307                 }
01308         }
01309 
01310     LOG_MSG("FAT: BPB says %u sectors/track %u heads %u bytes/sector",
01311         bootbuffer.sectorspertrack,
01312         bootbuffer.headcount,
01313         bootbuffer.bytespersector);
01314 
01315     /* NTS: Some HDI images of PC-98 games do in fact have headcount == 0. Some like "Amaranth 5" have sectorspertrack == 0 too! */
01316     if (!IS_PC98_ARCH) {
01317         /* a clue that we're not really looking at FAT is invalid or weird values in the boot sector */
01318         if (bootbuffer.sectorspertrack == 0 || (bootbuffer.sectorspertrack > ((filesize <= 3000) ? 40 : 255)) ||
01319             (bootbuffer.headcount > ((filesize <= 3000) ? 64 : 255))) {
01320             LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem");
01321             created_successfully = false;
01322             return;
01323         }
01324     }
01325 
01326     /* work at this point in logical sectors */
01327         sector_size = loadedDisk->getSectSize();
01328 
01329     /* Many HDI images indicate a disk format of 256 or 512 bytes per sector combined with a FAT filesystem
01330      * that indicates 1024 bytes per sector. */
01331     if (pc98_512_to_1024_allow &&
01332          bootbuffer.bytespersector != getSectSize() &&
01333          bootbuffer.bytespersector >  getSectSize() &&
01334         (bootbuffer.bytespersector %  getSectSize()) == 0) {
01335         unsigned int ratioshift = 1;
01336 
01337         while ((unsigned int)(bootbuffer.bytespersector >> ratioshift) > getSectSize())
01338             ratioshift++;
01339 
01340         unsigned int ratio = 1u << ratioshift;
01341 
01342         LOG_MSG("Disk indicates %u bytes/sector, FAT filesystem indicates %u bytes/sector. Ratio=%u:1 shift=%u",
01343                 getSectSize(),bootbuffer.bytespersector,ratio,ratioshift);
01344 
01345         if ((unsigned int)(bootbuffer.bytespersector >> ratioshift) == getSectSize()) {
01346             assert(ratio >= 2);
01347 
01348             /* we can hack things in place IF the starting sector is an even number */
01349             if ((partSectOff & (ratio - 1)) == 0) {
01350                 partSectOff >>= ratioshift;
01351                 startSector >>= ratioshift;
01352                 sector_size = bootbuffer.bytespersector;
01353                 LOG_MSG("Using logical sector size %u",sector_size);
01354             }
01355             else {
01356                 LOG_MSG("However there's nothing I can do, because the partition starts on an odd sector");
01357             }
01358         }
01359     }
01360 
01361     /* NTS: PC-98 floppies (the 1024 byte/sector format) do not have magic bytes */
01362     if (getSectSize() == 512 && !IS_PC98_ARCH) {
01363         if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) {
01364             /* Not a FAT filesystem */
01365             LOG_MSG("Loaded image has no valid magicnumbers at the end!");
01366             created_successfully = false;
01367             return;
01368         }
01369     }
01370 
01371         if (bootbuffer.sectorsperfat == 0) {
01372                 /* FAT32 not implemented yet */
01373                 LOG_MSG("FAT32 not implemented yet, mounting image only");
01374                 fattype = FAT32;        // Avoid parsing dir entries, see fatDrive::FindFirst()...should work for unformatted images as well
01375                 created_successfully = false;
01376                 return;
01377         }
01378 
01379         /* Sanity checks */
01380     /* NTS: DOSBox-X *does* support non-standard sector sizes, though not in IBM PC mode and not through INT 13h.
01381      *      In DOSBox-X INT 13h emulation will enforce the standard (512 byte) sector size.
01382      *      In PC-98 mode mounting disk images requires "non-standard" sector sizes because PC-98 floppies (other
01383      *      than ones formatted 1.44MB) generally use 1024 bytes/sector and MAY use 128 or 256 bytes per sector. */
01384     /* NTS: Loosen geometry checks for PC-98 mode, for two reasons. One, is that the geometry check will fail
01385      *      when logical vs physical sector translation is involved, since it is apparently common for PC-98 HDI
01386      *      images to be formatted with 256, 512, 1024, or in rare cases even 2048 bytes per sector, yet the FAT
01387      *      file format will report a sector size that is a power of 2 multiple of the disk sector size. The
01388      *      most common appears to be 512 byte/sector HDI images formatted with 1024 byte/sector FAT filesystems.
01389      *
01390      *      Second, there are some HDI images that are valid yet the FAT filesystem reports a head count of 0
01391      *      for some reason (Touhou Project) */
01392         if ((bootbuffer.sectorspercluster == 0) ||
01393                 (bootbuffer.rootdirentries == 0) ||
01394                 (bootbuffer.fatcopies == 0) ||
01395                 (bootbuffer.headcount == 0 && !IS_PC98_ARCH) ||
01396                 (bootbuffer.headcount > headscyl && !IS_PC98_ARCH) ||
01397                 (bootbuffer.sectorspertrack == 0 && !IS_PC98_ARCH) ||
01398                 (bootbuffer.sectorspertrack > cylsector && !IS_PC98_ARCH)) {
01399                 LOG_MSG("Sanity checks failed");
01400                 created_successfully = false;
01401                 return;
01402         }
01403 
01404     /* too much of this code assumes 512 bytes per sector or more.
01405      * MS-DOS itself as I understand it relies on bytes per sector being a power of 2.
01406      * this is to protect against errant FAT structures and to help prep this code
01407      * later to work with the 1024 bytes/sector used by PC-98 floppy formats.
01408      * When done, this code should be able to then handle the FDI/FDD images
01409      * PC-98 games are normally distributed in on the internet.
01410      *
01411      * The value "128" comes from the smallest sector size possible on the floppy
01412      * controller of MS-DOS based systems. */
01413     /* NTS: Power of 2 test: A number is a power of 2 if (x & (x - 1)) == 0
01414      *
01415      * 15        15 & 14       01111 AND 01110     RESULT: 01110 (15)
01416      * 16        16 & 15       10000 AND 01111     RESULT: 00000 (0)
01417      * 17        17 & 16       10001 AND 10000     RESULT: 10000 (16) */
01418     if (bootbuffer.bytespersector < 128 || bootbuffer.bytespersector > sizeof(bootbuffer) ||
01419         (bootbuffer.bytespersector & (bootbuffer.bytespersector - 1)) != 0/*not a power of 2*/) {
01420         LOG_MSG("FAT bytes/sector value %u not supported",bootbuffer.bytespersector);
01421                 created_successfully = false;
01422         return;
01423     }
01424 
01425     /* another fault of this code is that it assumes the sector size of the medium matches
01426      * the bytespersector value of the MS-DOS filesystem. if they don't match, problems
01427      * will result. */
01428     if (bootbuffer.bytespersector != getSectSize()) {
01429         LOG_MSG("FAT bytes/sector %u does not match disk image bytes/sector %u",
01430             (unsigned int)bootbuffer.bytespersector,
01431             (unsigned int)getSectSize());
01432                 created_successfully = false;
01433         return;
01434     }
01435 
01436         /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */
01437         absolute = IS_PC98_ARCH || ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector));
01438 
01439         /* Determine FAT format, 12, 16 or 32 */
01440 
01441         /* Get size of root dir in sectors */
01442         Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32u) + (bootbuffer.bytespersector - 1u)) / bootbuffer.bytespersector;
01443         Bit32u DataSectors;
01444         if(bootbuffer.totalsectorcount != 0) {
01445                 DataSectors = (Bitu)bootbuffer.totalsectorcount - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01446         } else {
01447                 DataSectors = (Bitu)bootbuffer.totalsecdword - ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors);
01448 
01449         }
01450         CountOfClusters = DataSectors / bootbuffer.sectorspercluster;
01451 
01452         firstDataSector = ((Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)RootDirSectors) + (Bitu)partSectOff;
01453         firstRootDirSect = (Bitu)bootbuffer.reservedsectors + ((Bitu)bootbuffer.fatcopies * (Bitu)bootbuffer.sectorsperfat) + (Bitu)partSectOff;
01454 
01455         if(CountOfClusters < 4085) {
01456                 /* Volume is FAT12 */
01457                 LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters);
01458                 fattype = FAT12;
01459         } else if (CountOfClusters < 65525) {
01460                 LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters);
01461                 fattype = FAT16;
01462         } else {
01463                 LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters);
01464                 fattype = FAT32;
01465         }
01466 
01467         /* There is no cluster 0, this means we are in the root directory */
01468         cwdDirCluster = 0;
01469 
01470         memset(fatSectBuffer,0,1024);
01471         curFatSect = 0xffffffff;
01472 
01473         strcpy(info, "fatDrive ");
01474         strcat(info, sysFilename);
01475 }
01476 
01477 bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) {
01478         Bit32u countFree = 0;
01479         Bit32u i;
01480         
01481         *_bytes_sector = (Bit16u)getSectSize();
01482         *_sectors_cluster = bootbuffer.sectorspercluster;
01483         if (CountOfClusters<65536) *_total_clusters = (Bit16u)CountOfClusters;
01484         else {
01485                 // maybe some special handling needed for fat32
01486                 *_total_clusters = 65535;
01487         }
01488         for(i=0;i<CountOfClusters;i++) if(!getClusterValue(i+2)) countFree++;
01489         if (countFree<65536) *_free_clusters = (Bit16u)countFree;
01490         else {
01491                 // maybe some special handling needed for fat32
01492                 *_free_clusters = 65535;
01493         }
01494         
01495         return true;
01496 }
01497 
01498 Bit32u fatDrive::getFirstFreeClust(void) {
01499         Bit32u i;
01500         for(i=0;i<CountOfClusters;i++) {
01501                 if(!getClusterValue(i+2)) return (i+2);
01502         }
01503 
01504         /* No free cluster found */
01505         return 0;
01506 }
01507 
01508 bool fatDrive::isRemote(void) { return false; }
01509 bool fatDrive::isRemovable(void) { return false; }
01510 
01511 Bits fatDrive::UnMount(void) {
01512         delete this;
01513         return 0;
01514 }
01515 
01516 Bit8u fatDrive::GetMediaByte(void) { return loadedDisk->GetBiosType(); }
01517 
01518 bool fatDrive::FileCreate(DOS_File **file, const char *name, Bit16u attributes) {
01519         direntry fileEntry;
01520         Bit32u dirClust, subEntry;
01521         char dirName[DOS_NAMELENGTH_ASCII];
01522         char pathName[11];
01523 
01524         Bit16u save_errorcode=dos.errorcode;
01525 
01526     if (attributes & DOS_ATTR_VOLUME) {
01527         SetLabel(name,false,true);
01528         return true;
01529     }
01530 
01531         /* Check if file already exists */
01532         if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01533                 /* Truncate file */
01534                 fileEntry.entrysize=0;
01535                 directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01536                 if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
01537         } else {
01538                 /* Can we even get the name of the file itself? */
01539                 if(!getEntryName(name, &dirName[0])) return false;
01540                 convToDirFile(&dirName[0], &pathName[0]);
01541 
01542                 /* Can we find the base directory? */
01543                 if(!getDirClustNum(name, &dirClust, true)) return false;
01544                 memset(&fileEntry, 0, sizeof(direntry));
01545                 memcpy(&fileEntry.entryname, &pathName[0], 11);
01546         {
01547             Bit16u ct,cd;
01548             time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,time(NULL));
01549             fileEntry.modTime = ct;
01550             fileEntry.modDate = cd;
01551         }
01552         fileEntry.attrib = (Bit8u)(attributes & 0xff);
01553                 addDirectoryEntry(dirClust, fileEntry);
01554 
01555                 /* Check if file exists now */
01556                 if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01557         }
01558 
01559         /* Empty file created, now lets open it */
01560         /* TODO: check for read-only flag and requested write access */
01561         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01562         (*file)->flags=OPEN_READWRITE;
01563         ((fatFile *)(*file))->dirCluster = dirClust;
01564         ((fatFile *)(*file))->dirIndex = subEntry;
01565         /* Maybe modTime and date should be used ? (crt matches findnext) */
01566         ((fatFile *)(*file))->time = fileEntry.modTime;
01567         ((fatFile *)(*file))->date = fileEntry.modDate;
01568 
01569         dos.errorcode=save_errorcode;
01570         return true;
01571 }
01572 
01573 bool fatDrive::FileExists(const char *name) {
01574         direntry fileEntry;
01575         Bit32u dummy1, dummy2;
01576         if(!getFileDirEntry(name, &fileEntry, &dummy1, &dummy2)) return false;
01577         return true;
01578 }
01579 
01580 bool fatDrive::FileOpen(DOS_File **file, const char *name, Bit32u flags) {
01581         direntry fileEntry;
01582         Bit32u dirClust, subEntry;
01583         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01584         /* TODO: check for read-only flag and requested write access */
01585         *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this);
01586         (*file)->flags = flags;
01587         ((fatFile *)(*file))->dirCluster = dirClust;
01588         ((fatFile *)(*file))->dirIndex = subEntry;
01589         /* Maybe modTime and date should be used ? (crt matches findnext) */
01590         ((fatFile *)(*file))->time = fileEntry.modTime;
01591         ((fatFile *)(*file))->date = fileEntry.modDate;
01592         return true;
01593 }
01594 
01595 bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_block*/) {
01596         /* TODO: Stub */
01597         return false;
01598 }
01599 
01600 bool fatDrive::FileUnlink(const char * name) {
01601         direntry fileEntry;
01602         Bit32u dirClust, subEntry;
01603 
01604         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false;
01605 
01606         fileEntry.entryname[0] = 0xe5;
01607         directoryChange(dirClust, &fileEntry, (Bit32s)subEntry);
01608 
01609         if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0);
01610 
01611         return true;
01612 }
01613 
01614 bool fatDrive::FindFirst(const char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) {
01615         direntry dummyClust;
01616         if(fattype==FAT32) return false;
01617 
01618     // volume label searches always affect root directory, no matter the current directory, at least with FCBs
01619     if (dta.GetAttr() & DOS_ATTR_VOLUME) {
01620         if(!getDirClustNum("\\", &cwdDirCluster, false)) {
01621             DOS_SetError(DOSERR_PATH_NOT_FOUND);
01622             return false;
01623         }
01624     }
01625     else {
01626         if(!getDirClustNum(_dir, &cwdDirCluster, false)) {
01627             DOS_SetError(DOSERR_PATH_NOT_FOUND);
01628             return false;
01629         }
01630     }
01631 
01632         dta.SetDirID(0);
01633         dta.SetDirIDCluster((Bit16u)(cwdDirCluster&0xffff));
01634         return FindNextInternal(cwdDirCluster, dta, &dummyClust);
01635 }
01636 
01637 char* removeTrailingSpaces(char* str) {
01638         char* end = str + strlen(str) - 1;
01639         while (end >= str && *end == ' ') end--;
01640     /* NTS: The loop will exit with 'end' one char behind the last ' ' space character.
01641      *      So to ASCIIZ snip off the space, step forward one and overwrite with NUL.
01642      *      The loop may end with 'end' one char behind 'ptr' if the string was empty ""
01643      *      or nothing but spaces. This is OK because after the step forward, end >= str
01644      *      in all cases. */
01645         *(++end) = '\0';
01646         return str;
01647 }
01648 
01649 char* removeLeadingSpaces(char* str) {
01650         size_t len = strlen(str);
01651         size_t pos = strspn(str," ");
01652         memmove(str,str + pos,len - pos + 1);
01653         return str;
01654 }
01655 
01656 char* trimString(char* str) {
01657         return removeTrailingSpaces(removeLeadingSpaces(str));
01658 }
01659 
01660 Bit32u fatDrive::GetSectorCount(void) {
01661     return (loadedDisk->heads * loadedDisk->sectors * loadedDisk->cylinders) - partSectOff;
01662 }
01663 
01664 Bit32u fatDrive::GetSectorSize(void) {
01665     return getSectorSize();
01666 }
01667 
01668 Bit8u fatDrive::Read_AbsoluteSector_INT25(Bit32u sectnum, void * data) {
01669     return readSector(sectnum+partSectOff,data);
01670 }
01671 
01672 Bit8u fatDrive::Write_AbsoluteSector_INT25(Bit32u sectnum, void * data) {
01673     return writeSector(sectnum+partSectOff,data);
01674 }
01675 
01676 bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) {
01677         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01678         Bit32u logentsector; /* Logical entry sector */
01679         Bit32u entryoffset;  /* Index offset within sector */
01680         Bit32u tmpsector;
01681         Bit8u attrs;
01682         Bit16u dirPos;
01683         char srch_pattern[DOS_NAMELENGTH_ASCII];
01684         char find_name[DOS_NAMELENGTH_ASCII];
01685         char extension[4];
01686 
01687     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01688     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01689     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01690 
01691         dta.GetSearchParams(attrs, srch_pattern);
01692         dirPos = dta.GetDirID();
01693 
01694 nextfile:
01695         logentsector = (Bit32u)((size_t)dirPos / dirent_per_sector);
01696         entryoffset = (Bit32u)((size_t)dirPos % dirent_per_sector);
01697 
01698         if(dirClustNumber==0) {
01699                 if(dirPos >= bootbuffer.rootdirentries) {
01700                         DOS_SetError(DOSERR_NO_MORE_FILES);
01701                         return false;
01702                 }
01703                 readSector(firstRootDirSect+logentsector,sectbuf);
01704         } else {
01705                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01706                 /* A zero sector number can't happen */
01707                 if(tmpsector == 0) {
01708                         DOS_SetError(DOSERR_NO_MORE_FILES);
01709                         return false;
01710                 }
01711                 readSector(tmpsector,sectbuf);
01712         }
01713         dirPos++;
01714         dta.SetDirID(dirPos);
01715 
01716         /* Deleted file entry */
01717         if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile;
01718 
01719         /* End of directory list */
01720         if (sectbuf[entryoffset].entryname[0] == 0x00) {
01721                 DOS_SetError(DOSERR_NO_MORE_FILES);
01722                 return false;
01723         }
01724         memset(find_name,0,DOS_NAMELENGTH_ASCII);
01725         memset(extension,0,4);
01726         memcpy(find_name,&sectbuf[entryoffset].entryname[0],8);
01727     memcpy(extension,&sectbuf[entryoffset].entryname[8],3);
01728 
01729     if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) {
01730         trimString(&find_name[0]);
01731         trimString(&extension[0]);
01732     }
01733 
01734     //if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY))
01735     if (extension[0]!=0) {
01736         if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME))
01737             strcat(find_name, ".");
01738 
01739         strcat(find_name, extension);
01740     }
01741 
01742     if (sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)
01743         trimString(find_name);
01744 
01745     /* Compare attributes to search attributes */
01746 
01747     //TODO What about attrs = DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY ?
01748     if (attrs == DOS_ATTR_VOLUME) {
01749                 if (!(sectbuf[entryoffset].attrib & DOS_ATTR_VOLUME)) goto nextfile;
01750                 labelCache.SetLabel(find_name, false, true);
01751         } else {
01752                 if (~attrs & sectbuf[entryoffset].attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOLUME | DOS_ATTR_SYSTEM | DOS_ATTR_HIDDEN) ) goto nextfile;
01753         }
01754 
01755 
01756         /* Compare name to search pattern */
01757         if(!WildFileCmp(find_name,srch_pattern)) goto nextfile;
01758 
01759         //dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib);
01760 
01761         dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].modDate, sectbuf[entryoffset].modTime, sectbuf[entryoffset].attrib);
01762 
01763         memcpy(foundEntry, &sectbuf[entryoffset], sizeof(direntry));
01764 
01765         return true;
01766 }
01767 
01768 bool fatDrive::FindNext(DOS_DTA &dta) {
01769         direntry dummyClust;
01770 
01771         return FindNextInternal(dta.GetDirIDCluster(), dta, &dummyClust);
01772 }
01773 
01774 bool fatDrive::GetFileAttr(const char *name, Bit16u *attr) {
01775         direntry fileEntry;
01776         Bit32u dirClust, subEntry;
01777         if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) {
01778                 char dirName[DOS_NAMELENGTH_ASCII];
01779                 char pathName[11];
01780 
01781                 /* Can we even get the name of the directory itself? */
01782                 if(!getEntryName(name, &dirName[0])) return false;
01783                 convToDirFile(&dirName[0], &pathName[0]);
01784 
01785                 /* Get parent directory starting cluster */
01786                 if(!getDirClustNum(name, &dirClust, true)) return false;
01787 
01788                 /* Find directory entry in parent directory */
01789                 Bit32s fileidx = 2;
01790                 if (dirClust==0) fileidx = 0;   // root directory
01791                 Bit32s last_idx=0; 
01792                 while(directoryBrowse(dirClust, &fileEntry, fileidx, last_idx)) {
01793                         if(memcmp(&fileEntry.entryname, &pathName[0], 11) == 0) {
01794                                 *attr=fileEntry.attrib;
01795                                 return true;
01796                         }
01797                         fileidx++;
01798                 }
01799                 return false;
01800         } else *attr=fileEntry.attrib;
01801         return true;
01802 }
01803 
01804 bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum, Bit32s start/*=0*/) {
01805         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01806         Bit32u logentsector;    /* Logical entry sector */
01807         Bit32u entryoffset = 0; /* Index offset within sector */
01808         Bit32u tmpsector;
01809         Bit16u dirPos = 0;
01810 
01811     (void)start;//UNUSED
01812 
01813     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01814     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01815     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01816 
01817         while(entNum>=0) {
01818                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01819                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01820 
01821                 if(dirClustNumber==0) {
01822                         if(dirPos >= bootbuffer.rootdirentries) return false;
01823                         tmpsector = firstRootDirSect+logentsector;
01824                         readSector(tmpsector,sectbuf);
01825                 } else {
01826                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01827                         /* A zero sector number can't happen */
01828                         if(tmpsector == 0) return false;
01829                         readSector(tmpsector,sectbuf);
01830                 }
01831                 dirPos++;
01832 
01833 
01834                 /* End of directory list */
01835                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01836                 --entNum;
01837         }
01838 
01839         memcpy(useEntry, &sectbuf[entryoffset],sizeof(direntry));
01840         return true;
01841 }
01842 
01843 bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) {
01844         direntry sectbuf[MAX_DIRENTS_PER_SECTOR];       /* 16 directory entries per 512 byte sector */
01845         Bit32u logentsector;    /* Logical entry sector */
01846         Bit32u entryoffset = 0; /* Index offset within sector */
01847         Bit32u tmpsector = 0;
01848         Bit16u dirPos = 0;
01849         
01850     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01851     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01852     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01853 
01854         while(entNum>=0) {              
01855                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01856                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01857 
01858                 if(dirClustNumber==0) {
01859                         if(dirPos >= bootbuffer.rootdirentries) return false;
01860                         tmpsector = firstRootDirSect+logentsector;
01861                         readSector(tmpsector,sectbuf);
01862                 } else {
01863                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01864                         /* A zero sector number can't happen */
01865                         if(tmpsector == 0) return false;
01866                         readSector(tmpsector,sectbuf);
01867                 }
01868                 dirPos++;
01869 
01870 
01871                 /* End of directory list */
01872                 if (sectbuf[entryoffset].entryname[0] == 0x00) return false;
01873                 --entNum;
01874         }
01875         if(tmpsector != 0) {
01876         memcpy(&sectbuf[entryoffset], useEntry, sizeof(direntry));
01877                 writeSector(tmpsector, sectbuf);
01878         return true;
01879         } else {
01880                 return false;
01881         }
01882 }
01883 
01884 bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) {
01885         direntry sectbuf[MAX_DIRENTS_PER_SECTOR]; /* 16 directory entries per 512 byte sector */
01886         Bit32u logentsector; /* Logical entry sector */
01887         Bit32u entryoffset;  /* Index offset within sector */
01888         Bit32u tmpsector;
01889         Bit16u dirPos = 0;
01890         
01891     size_t dirent_per_sector = getSectSize() / sizeof(direntry);
01892     assert(dirent_per_sector <= MAX_DIRENTS_PER_SECTOR);
01893     assert((dirent_per_sector * sizeof(direntry)) <= SECTOR_SIZE_MAX);
01894 
01895         for(;;) {               
01896                 logentsector = ((Bit32u)((size_t)dirPos / dirent_per_sector));
01897                 entryoffset = ((Bit32u)((size_t)dirPos % dirent_per_sector));
01898 
01899                 if(dirClustNumber==0) {
01900                         if(dirPos >= bootbuffer.rootdirentries) return false;
01901                         tmpsector = firstRootDirSect+logentsector;
01902                         readSector(tmpsector,sectbuf);
01903                 } else {
01904                         tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01905                         /* A zero sector number can't happen - we need to allocate more room for this directory*/
01906                         if(tmpsector == 0) {
01907                                 Bit32u newClust;
01908                                 newClust = appendCluster(dirClustNumber);
01909                                 if(newClust == 0) return false;
01910                                 /* Try again to get tmpsector */
01911                                 tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector);
01912                                 if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */
01913                         }
01914                         readSector(tmpsector,sectbuf);
01915                 }
01916                 dirPos++;
01917 
01918                 /* Deleted file entry or end of directory list */
01919                 if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) {
01920                         sectbuf[entryoffset] = useEntry;
01921                         writeSector(tmpsector,sectbuf);
01922                         break;
01923                 }
01924         }
01925 
01926         return true;
01927 }
01928 
01929 void fatDrive::zeroOutCluster(Bit32u clustNumber) {
01930         Bit8u secBuffer[SECTOR_SIZE_MAX];
01931 
01932         memset(&secBuffer[0], 0, SECTOR_SIZE_MAX);
01933 
01934         unsigned int i;
01935         for(i=0;i<bootbuffer.sectorspercluster;i++) {
01936                 writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]);
01937         }
01938 }
01939 
01940 bool fatDrive::MakeDir(const char *dir) {
01941         Bit32u dummyClust, dirClust;
01942         direntry tmpentry;
01943         char dirName[DOS_NAMELENGTH_ASCII];
01944     char pathName[11];
01945     Bit16u ct,cd;
01946 
01947         /* Can we even get the name of the directory itself? */
01948         if(!getEntryName(dir, &dirName[0])) return false;
01949         convToDirFile(&dirName[0], &pathName[0]);
01950 
01951         /* Fail to make directory if already exists */
01952         if(getDirClustNum(dir, &dummyClust, false)) return false;
01953 
01954         dummyClust = getFirstFreeClust();
01955         /* No more space */
01956         if(dummyClust == 0) return false;
01957         
01958         if(!allocateCluster(dummyClust, 0)) return false;
01959 
01960         zeroOutCluster(dummyClust);
01961 
01962         /* Can we find the base directory? */
01963         if(!getDirClustNum(dir, &dirClust, true)) return false;
01964 
01965     time_t_to_DOS_DateTime(/*&*/ct,/*&*/cd,::time(NULL));
01966 
01967         /* Add the new directory to the base directory */
01968         memset(&tmpentry,0, sizeof(direntry));
01969         memcpy(&tmpentry.entryname, &pathName[0], 11);
01970         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01971         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01972         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01973     tmpentry.modTime = ct;
01974     tmpentry.modDate = cd;
01975     addDirectoryEntry(dirClust, tmpentry);
01976 
01977         /* Add the [.] and [..] entries to our new directory*/
01978         /* [.] entry */
01979         memset(&tmpentry,0, sizeof(direntry));
01980         memcpy(&tmpentry.entryname, ".          ", 11);
01981         tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff);
01982         tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16);
01983         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01984     tmpentry.modTime = ct;
01985     tmpentry.modDate = cd;
01986         addDirectoryEntry(dummyClust, tmpentry);
01987 
01988         /* [..] entry */
01989         memset(&tmpentry,0, sizeof(direntry));
01990         memcpy(&tmpentry.entryname, "..         ", 11);
01991         tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff);
01992         tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16);
01993         tmpentry.attrib = DOS_ATTR_DIRECTORY;
01994     tmpentry.modTime = ct;
01995     tmpentry.modDate = cd;
01996         addDirectoryEntry(dummyClust, tmpentry);
01997 
01998         return true;
01999 }
02000 
02001 bool fatDrive::RemoveDir(const char *dir) {
02002         Bit32u dummyClust, dirClust;
02003         direntry tmpentry;
02004         char dirName[DOS_NAMELENGTH_ASCII];
02005         char pathName[11];
02006 
02007         /* Can we even get the name of the directory itself? */
02008         if(!getEntryName(dir, &dirName[0])) return false;
02009         convToDirFile(&dirName[0], &pathName[0]);
02010 
02011         /* Get directory starting cluster */
02012         if(!getDirClustNum(dir, &dummyClust, false)) return false;
02013 
02014         /* Can't remove root directory */
02015         if(dummyClust == 0) return false;
02016 
02017         /* Get parent directory starting cluster */
02018         if(!getDirClustNum(dir, &dirClust, true)) return false;
02019 
02020         /* Check to make sure directory is empty */
02021         Bit32u filecount = 0;
02022         /* Set to 2 to skip first 2 entries, [.] and [..] */
02023         Bit32s fileidx = 2;
02024         while(directoryBrowse(dummyClust, &tmpentry, fileidx)) {
02025                 /* Check for non-deleted files */
02026                 if(tmpentry.entryname[0] != 0xe5) filecount++;
02027                 fileidx++;
02028         }
02029 
02030         /* Return if directory is not empty */
02031         if(filecount > 0) return false;
02032 
02033         /* Find directory entry in parent directory */
02034         if (dirClust==0) fileidx = 0;   // root directory
02035         else fileidx = 2;
02036         bool found = false;
02037         while(directoryBrowse(dirClust, &tmpentry, fileidx)) {
02038                 if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) {
02039                         found = true;
02040                         tmpentry.entryname[0] = 0xe5;
02041                         directoryChange(dirClust, &tmpentry, fileidx);
02042                         deleteClustChain(dummyClust, 0);
02043 
02044                         break;
02045                 }
02046                 fileidx++;
02047         }
02048 
02049         if(!found) return false;
02050 
02051         return true;
02052 }
02053 
02054 bool fatDrive::Rename(const char * oldname, const char * newname) {
02055         direntry fileEntry1;
02056         Bit32u dirClust1, subEntry1;
02057         if(!getFileDirEntry(oldname, &fileEntry1, &dirClust1, &subEntry1)) return false;
02058         /* File to be renamed really exists */
02059 
02060         direntry fileEntry2;
02061         Bit32u dirClust2, subEntry2;
02062 
02063         /* Check if file already exists */
02064         if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) {
02065                 /* Target doesn't exist, can rename */
02066 
02067                 char dirName2[DOS_NAMELENGTH_ASCII];
02068                 char pathName2[11];
02069                 /* Can we even get the name of the file itself? */
02070                 if(!getEntryName(newname, &dirName2[0])) return false;
02071                 convToDirFile(&dirName2[0], &pathName2[0]);
02072 
02073                 /* Can we find the base directory? */
02074                 if(!getDirClustNum(newname, &dirClust2, true)) return false;
02075                 memcpy(&fileEntry2, &fileEntry1, sizeof(direntry));
02076                 memcpy(&fileEntry2.entryname, &pathName2[0], 11);
02077                 addDirectoryEntry(dirClust2, fileEntry2);
02078 
02079                 /* Check if file exists now */
02080                 if(!getFileDirEntry(newname, &fileEntry2, &dirClust2, &subEntry2)) return false;
02081 
02082                 /* Remove old entry */
02083                 fileEntry1.entryname[0] = 0xe5;
02084                 directoryChange(dirClust1, &fileEntry1, (Bit32s)subEntry1);
02085 
02086                 return true;
02087         }
02088 
02089         /* Target already exists, fail */
02090         return false;
02091 }
02092 
02093 bool fatDrive::TestDir(const char *dir) {
02094         Bit32u dummyClust;
02095         return getDirClustNum(dir, &dummyClust, false);
02096 }
02097