DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_files.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <stdlib.h>
00022 #include <time.h>
00023 #include <ctype.h>
00024 
00025 #include "dosbox.h"
00026 #include "bios.h"
00027 #include "mem.h"
00028 #include "regs.h"
00029 #include "dos_inc.h"
00030 #include "drives.h"
00031 #include "cross.h"
00032 #include "dos_network2.h"
00033 
00034 #define DOS_FILESTART 4
00035 
00036 #define FCB_SUCCESS     0
00037 #define FCB_READ_NODATA 1
00038 #define FCB_READ_PARTIAL 3
00039 #define FCB_ERR_NODATA  1
00040 #define FCB_ERR_EOF     3
00041 #define FCB_ERR_WRITE   1
00042 
00043 Bitu DOS_FILES = 127;
00044 DOS_File ** Files = NULL;
00045 DOS_Drive * Drives[DOS_DRIVES] = {NULL};
00046 
00047 bool shiftjis_lead_byte(int c);
00048 
00049 Bit8u DOS_GetDefaultDrive(void) {
00050 //      return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive();
00051         Bit8u d = DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive();
00052         if( d != dos.current_drive ) LOG(LOG_DOSMISC,LOG_ERROR)("SDA drive %d not the same as dos.current_drive %d",d,dos.current_drive);
00053         return dos.current_drive;
00054 }
00055 
00056 void DOS_SetDefaultDrive(Bit8u drive) {
00057 //      if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);
00058         if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) {dos.current_drive = drive; DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);}
00059 }
00060 
00061 bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive) {
00062         if(!name || *name == 0 || *name == ' ') {
00063                 /* Both \0 and space are seperators and
00064                  * empty filenames report file not found */
00065                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00066                 return false;
00067         }
00068         const char * name_int = name;
00069         char tempdir[DOS_PATHLENGTH];
00070         char upname[DOS_PATHLENGTH];
00071         Bitu r,w;
00072         *drive = DOS_GetDefaultDrive();
00073         /* First get the drive */
00074         if (name_int[1]==':') {
00075                 *drive=(name_int[0] | 0x20)-'a';
00076                 name_int+=2;
00077         }
00078         if (*drive>=DOS_DRIVES || !Drives[*drive]) { 
00079                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00080                 return false; 
00081         }
00082         r=0;w=0;
00083         while (name_int[r]!=0 && (r<DOS_PATHLENGTH)) {
00084                 Bit8u c=(Bit8u)name_int[r++];
00085                 if ((c>='a') && (c<='z')) {upname[w++]=(char)c-32;continue;}
00086                 if ((c>='A') && (c<='Z')) {upname[w++]=(char)c;continue;}
00087                 if ((c>='0') && (c<='9')) {upname[w++]=(char)c;continue;}
00088                 switch (c) {
00089                 case '/':
00090                         upname[w++]='\\';
00091                         break;
00092                 case ' ': /* should be seperator */
00093                         break;
00094                 default:
00095                         upname[w++]=(char)c;
00096             if (IS_PC98_ARCH && shiftjis_lead_byte(c) && r<DOS_PATHLENGTH) {
00097                 /* The trailing byte is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */
00098                 upname[w++]=name_int[r++];
00099             }
00100                         break;
00101                 }
00102         }
00103         while (r>0 && name_int[r-1]==' ') r--;
00104         if (r>=DOS_PATHLENGTH) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; }
00105         upname[w]=0;
00106 
00107         /* Now parse the new file name to make the final filename */
00108         if (upname[0]!='\\') strcpy(fullname,Drives[*drive]->curdir);
00109         else fullname[0]=0;
00110         Bit32u lastdir=0;Bit32u t=0;
00111         while (fullname[t]!=0) {
00112                 if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t;
00113                 t++;
00114         };
00115         r=0;w=0;
00116         tempdir[0]=0;
00117         bool stop=false;
00118         while (!stop) {
00119                 if (upname[r]==0) stop=true;
00120                 if ((upname[r]=='\\') || (upname[r]==0)){
00121                         tempdir[w]=0;
00122                         if (tempdir[0]==0) { w=0;r++;continue;}
00123                         if (strcmp(tempdir,".")==0) {
00124                                 tempdir[0]=0;                   
00125                                 w=0;r++;
00126                                 continue;
00127                         }
00128 
00129                         Bit32s iDown;
00130                         bool dots = true;
00131                         Bit32s templen=(Bit32s)strlen(tempdir);
00132                         for(iDown=0;(iDown < templen) && dots;iDown++)
00133                                 if(tempdir[iDown] != '.')
00134                                         dots = false;
00135 
00136                         // only dots?
00137                         if (dots && (templen > 1)) {
00138                                 Bit32s cDots = templen - 1;
00139                                 for(iDown=(Bit32s)strlen(fullname)-1;iDown>=0;iDown--) {
00140                                         if(fullname[iDown]=='\\' || iDown==0) {
00141                                                 lastdir = (Bit32u)iDown;
00142                                                 cDots--;
00143                                                 if(cDots==0)
00144                                                         break;
00145                                         }
00146                                 }
00147                                 fullname[lastdir]=0;
00148                                 t=0;lastdir=0;
00149                                 while (fullname[t]!=0) {
00150                                         if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t;
00151                                         t++;
00152                                 }
00153                                 tempdir[0]=0;
00154                                 w=0;r++;
00155                                 continue;
00156                         }
00157                         
00158 
00159                         lastdir=(Bit32u)strlen(fullname);
00160 
00161                         if (lastdir!=0) strcat(fullname,"\\");
00162                         char * ext=strchr(tempdir,'.');
00163                         if (ext) {
00164                                 if(strchr(ext+1,'.')) { 
00165                                 //another dot in the extension =>file not found
00166                                 //Or path not found depending on wether 
00167                                 //we are still in dir check stage or file stage
00168                                         if(stop)
00169                                                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00170                                         else
00171                                                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00172                                         return false;
00173                                 }
00174                                 
00175                                 ext[4] = 0;
00176                                 if((strlen(tempdir) - strlen(ext)) > 8) memmove(tempdir + 8, ext, 5);
00177                         } else tempdir[8]=0;
00178 
00179                         if (strlen(fullname)+strlen(tempdir)>=DOS_PATHLENGTH) {
00180                                 DOS_SetError(DOSERR_PATH_NOT_FOUND);return false;
00181                         }
00182                    
00183                         strcat(fullname,tempdir);
00184                         tempdir[0]=0;
00185                         w=0;r++;
00186                         continue;
00187                 }
00188                 tempdir[w++]=upname[r++];
00189         }
00190         return true;    
00191 }
00192 
00193 bool DOS_GetCurrentDir(Bit8u drive,char * const buffer) {
00194         if (drive==0) drive=DOS_GetDefaultDrive();
00195         else drive--;
00196         if ((drive>=DOS_DRIVES) || (!Drives[drive])) {
00197                 DOS_SetError(DOSERR_INVALID_DRIVE);
00198                 return false;
00199         }
00200         strcpy(buffer,Drives[drive]->curdir);
00201         return true;
00202 }
00203 
00204 bool DOS_ChangeDir(char const * const dir) {
00205         Bit8u drive;char fulldir[DOS_PATHLENGTH];
00206         const char * testdir=dir;
00207         if (strlen(testdir) && testdir[1]==':') testdir+=2;
00208         size_t len=strlen(testdir);
00209         if (!len) {
00210                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00211                 return false;
00212         }
00213         if (!DOS_MakeName(dir,fulldir,&drive)) return false;
00214         if (strlen(fulldir) && testdir[len-1]=='\\') {
00215                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00216                 return false;
00217         }
00218         
00219         if (Drives[drive]->TestDir(fulldir)) {
00220                 strcpy(Drives[drive]->curdir,fulldir);
00221                 return true;
00222         } else {
00223                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00224         }
00225         return false;
00226 }
00227 
00228 bool DOS_MakeDir(char const * const dir) {
00229         Bit8u drive;char fulldir[DOS_PATHLENGTH];
00230         size_t len = strlen(dir);
00231         if(!len || dir[len-1] == '\\') {
00232                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00233                 return false;
00234         }
00235         if (!DOS_MakeName(dir,fulldir,&drive)) return false;
00236         if(Drives[drive]->MakeDir(fulldir)) return true;
00237 
00238         /* Determine reason for failing */
00239         if(Drives[drive]->TestDir(fulldir)) 
00240                 DOS_SetError(DOSERR_ACCESS_DENIED);
00241         else
00242                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00243         return false;
00244 }
00245 
00246 bool DOS_RemoveDir(char const * const dir) {
00247 /* We need to do the test before the removal as can not rely on
00248  * the host to forbid removal of the current directory.
00249  * We never change directory. Everything happens in the drives.
00250  */
00251         Bit8u drive;char fulldir[DOS_PATHLENGTH];
00252         if (!DOS_MakeName(dir,fulldir,&drive)) return false;
00253         /* Check if exists */
00254         if(!Drives[drive]->TestDir(fulldir)) {
00255                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00256                 return false;
00257         }
00258         /* See if it's current directory */
00259         char currdir[DOS_PATHLENGTH]= { 0 };
00260         DOS_GetCurrentDir(drive + 1 ,currdir);
00261         if(strcmp(currdir,fulldir) == 0) {
00262                 DOS_SetError(DOSERR_REMOVE_CURRENT_DIRECTORY);
00263                 return false;
00264         }
00265 
00266         if(Drives[drive]->RemoveDir(fulldir)) return true;
00267 
00268         /* Failed. We know it exists and it's not the current dir */
00269         /* Assume non empty */
00270         DOS_SetError(DOSERR_ACCESS_DENIED);
00271         return false;
00272 }
00273 
00274 bool DOS_Rename(char const * const oldname,char const * const newname) {
00275         Bit8u driveold;char fullold[DOS_PATHLENGTH];
00276         Bit8u drivenew;char fullnew[DOS_PATHLENGTH];
00277         if (!DOS_MakeName(oldname,fullold,&driveold)) return false;
00278         if (!DOS_MakeName(newname,fullnew,&drivenew)) return false;
00279         /* No tricks with devices */
00280         if ( (DOS_FindDevice(oldname) != DOS_DEVICES) ||
00281              (DOS_FindDevice(newname) != DOS_DEVICES) ) {
00282                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00283                 return false;
00284         }
00285         /* Must be on the same drive */
00286         if(driveold != drivenew) {
00287                 DOS_SetError(DOSERR_NOT_SAME_DEVICE);
00288                 return false;
00289         }
00290         /*Test if target exists => no access */
00291         Bit16u attr;
00292         if(Drives[drivenew]->GetFileAttr(fullnew,&attr)) {
00293                 DOS_SetError(DOSERR_ACCESS_DENIED);
00294                 return false;
00295         }
00296         /* Source must exist, check for path ? */
00297         if (!Drives[driveold]->GetFileAttr( fullold, &attr ) ) {
00298                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00299                 return false;
00300         }
00301 
00302         if (Drives[drivenew]->Rename(fullold,fullnew)) return true;
00303         /* If it still fails, which error should we give ? PATH NOT FOUND or EACCESS */
00304         LOG(LOG_FILES,LOG_NORMAL)("Rename fails for %s to %s, no proper errorcode returned.",oldname,newname);
00305         DOS_SetError(DOSERR_FILE_NOT_FOUND);
00306         return false;
00307 }
00308 
00309 bool DOS_FindFirst(char * search,Bit16u attr,bool fcb_findfirst) {
00310         LOG(LOG_FILES,LOG_NORMAL)("file search attributes %X name %s",attr,search);
00311         DOS_DTA dta(dos.dta());
00312         Bit8u drive;char fullsearch[DOS_PATHLENGTH];
00313         char dir[DOS_PATHLENGTH];char pattern[DOS_PATHLENGTH];
00314         size_t len = strlen(search);
00315         if(len && search[len - 1] == '\\' && !( (len > 2) && (search[len - 2] == ':') && (attr == DOS_ATTR_VOLUME) )) { 
00316                 //Dark Forces installer, but c:\ is allright for volume labels(exclusively set)
00317                 DOS_SetError(DOSERR_NO_MORE_FILES);
00318                 return false;
00319         }
00320         if (!DOS_MakeName(search,fullsearch,&drive)) return false;
00321         //Check for devices. FindDevice checks for leading subdir as well
00322         bool device = (DOS_FindDevice(search) != DOS_DEVICES);
00323 
00324         /* Split the search in dir and pattern */
00325         char * find_last;
00326         find_last=strrchr(fullsearch,'\\');
00327         if (!find_last) {       /*No dir */
00328                 strcpy(pattern,fullsearch);
00329                 dir[0]=0;
00330         } else {
00331                 *find_last=0;
00332                 strcpy(pattern,find_last+1);
00333                 strcpy(dir,fullsearch);
00334         }
00335 
00336         dta.SetupSearch(drive,(Bit8u)attr,pattern);
00337 
00338         if(device) {
00339                 find_last = strrchr(pattern,'.');
00340                 if(find_last) *find_last = 0;
00341                 //TODO use current date and time
00342                 dta.SetResult(pattern,0,0,0,DOS_ATTR_DEVICE);
00343                 LOG(LOG_DOSMISC,LOG_WARN)("finding device %s",pattern);
00344                 return true;
00345         }
00346    
00347         if (Drives[drive]->FindFirst(dir,dta,fcb_findfirst)) return true;
00348         
00349         return false;
00350 }
00351 
00352 bool DOS_FindNext(void) {
00353         DOS_DTA dta(dos.dta());
00354         Bit8u i = dta.GetSearchDrive();
00355         if(i >= DOS_DRIVES || !Drives[i]) {
00356                 /* Corrupt search. */
00357                 LOG(LOG_FILES,LOG_ERROR)("Corrupt search!!!!");
00358                 DOS_SetError(DOSERR_NO_MORE_FILES); 
00359                 return false;
00360         } 
00361         if (Drives[i]->FindNext(dta)) return true;
00362         return false;
00363 }
00364 
00365 
00366 bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount) {
00367 #if defined(WIN32) && !defined(__MINGW32__)
00368         if(Network_IsActiveResource(entry))
00369                 return Network_ReadFile(entry,data,amount);
00370 #endif
00371         Bit32u handle=RealHandle(entry);
00372         if (handle>=DOS_FILES) {
00373                 DOS_SetError(DOSERR_INVALID_HANDLE);
00374                 return false;
00375         };
00376         if (!Files[handle] || !Files[handle]->IsOpen()) {
00377                 DOS_SetError(DOSERR_INVALID_HANDLE);
00378                 return false;
00379         };
00380 /*
00381         if ((Files[handle]->flags & 0x0f) == OPEN_WRITE)) {
00382                 DOS_SetError(DOSERR_INVALID_HANDLE);
00383                 return false;
00384         }
00385 */
00386         Bit16u toread=*amount;
00387         bool ret=Files[handle]->Read(data,&toread);
00388         *amount=toread;
00389         return ret;
00390 }
00391 
00392 bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount) {
00393 #if defined(WIN32) && !defined(__MINGW32__)
00394         if(Network_IsActiveResource(entry))
00395                 return Network_WriteFile(entry,data,amount);
00396 #endif
00397         Bit32u handle=RealHandle(entry);
00398         if (handle>=DOS_FILES) {
00399                 DOS_SetError(DOSERR_INVALID_HANDLE);
00400                 return false;
00401         };
00402         if (!Files[handle] || !Files[handle]->IsOpen()) {
00403                 DOS_SetError(DOSERR_INVALID_HANDLE);
00404                 return false;
00405         };
00406 /*
00407         if ((Files[handle]->flags & 0x0f) == OPEN_READ)) {
00408                 DOS_SetError(DOSERR_INVALID_HANDLE);
00409                 return false;
00410         }
00411 */
00412         Bit16u towrite=*amount;
00413         bool ret=Files[handle]->Write(data,&towrite);
00414         *amount=towrite;
00415         return ret;
00416 }
00417 
00418 bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type) {
00419         Bit32u handle=RealHandle(entry);
00420         if (handle>=DOS_FILES) {
00421                 DOS_SetError(DOSERR_INVALID_HANDLE);
00422                 return false;
00423         };
00424         if (!Files[handle] || !Files[handle]->IsOpen()) {
00425                 DOS_SetError(DOSERR_INVALID_HANDLE);
00426                 return false;
00427         };
00428         return Files[handle]->Seek(pos,type);
00429 }
00430 
00431 /* ert, 20100711: Locking extensions */
00432 bool DOS_LockFile(Bit16u entry,Bit8u mode,Bit32u pos,Bit32u size) {
00433         Bit32u handle=RealHandle(entry);
00434         if (handle>=DOS_FILES) {
00435                 DOS_SetError(DOSERR_INVALID_HANDLE);
00436                 return false;
00437         };
00438         if (!Files[handle] || !Files[handle]->IsOpen()) {
00439                 DOS_SetError(DOSERR_INVALID_HANDLE);
00440                 return false;
00441         };
00442 #ifdef WIN32
00443         return Files[handle]->LockFile(mode,pos,size);
00444 #else
00445     (void)mode;//UNUSED
00446     (void)size;//UNUSED
00447     (void)pos;//UNUSED
00448         return true;
00449 #endif
00450 }
00451 
00452 bool DOS_CloseFile(Bit16u entry) {
00453 #if defined(WIN32) && !defined(__MINGW32__)
00454         if(Network_IsActiveResource(entry))
00455                 return Network_CloseFile(entry);
00456 #endif
00457         Bit32u handle=RealHandle(entry);
00458         if (handle>=DOS_FILES) {
00459                 DOS_SetError(DOSERR_INVALID_HANDLE);
00460                 return false;
00461         };
00462         if (!Files[handle]) {
00463                 DOS_SetError(DOSERR_INVALID_HANDLE);
00464                 return false;
00465         };
00466         if (Files[handle]->IsOpen()) {
00467                 Files[handle]->Close();
00468         }
00469         DOS_PSP psp(dos.psp());
00470         psp.SetFileHandle(entry,0xff);
00471         if (Files[handle]->RemoveRef()<=0) {
00472                 delete Files[handle];
00473                 Files[handle]=0;
00474         }
00475         return true;
00476 }
00477 
00478 bool DOS_FlushFile(Bit16u entry) {
00479         Bit32u handle=RealHandle(entry);
00480         if (handle>=DOS_FILES) {
00481                 DOS_SetError(DOSERR_INVALID_HANDLE);
00482                 return false;
00483         };
00484         if (!Files[handle] || !Files[handle]->IsOpen()) {
00485                 DOS_SetError(DOSERR_INVALID_HANDLE);
00486                 return false;
00487         };
00488         LOG(LOG_DOSMISC,LOG_NORMAL)("FFlush used.");
00489         return true;
00490 }
00491 
00492 static bool PathExists(char const * const name) {
00493         const char* leading = strrchr(name,'\\');
00494         if(!leading) return true;
00495         char temp[CROSS_LEN];
00496         strcpy(temp,name);
00497         char * lead = strrchr(temp,'\\');
00498         if (lead == temp) return true;
00499         *lead = 0;
00500         Bit8u drive;char fulldir[DOS_PATHLENGTH];
00501         if (!DOS_MakeName(temp,fulldir,&drive)) return false;
00502         if(!Drives[drive]->TestDir(fulldir)) return false;
00503         return true;
00504 }
00505 
00506 
00507 bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry) {
00508         // Creation of a device is the same as opening it
00509         // Tc201 installer
00510         if (DOS_FindDevice(name) != DOS_DEVICES)
00511                 return DOS_OpenFile(name, OPEN_READ, entry);
00512 
00513         LOG(LOG_FILES,LOG_NORMAL)("file create attributes %X file %s",attributes,name);
00514         char fullname[DOS_PATHLENGTH];Bit8u drive;
00515         DOS_PSP psp(dos.psp());
00516         if (!DOS_MakeName(name,fullname,&drive)) return false;
00517         /* Check for a free file handle */
00518         Bit8u handle=DOS_FILES;Bit8u i;
00519         for (i=0;i<DOS_FILES;i++) {
00520                 if (!Files[i]) {
00521                         handle=i;
00522                         break;
00523                 }
00524         }
00525         if (handle==DOS_FILES) {
00526                 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
00527                 return false;
00528         }
00529         /* We have a position in the main table now find one in the psp table */
00530         *entry = psp.FindFreeFileEntry();
00531         if (*entry==0xff) {
00532                 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
00533                 return false;
00534         }
00535         /* Don't allow directories to be created */
00536         if (attributes&DOS_ATTR_DIRECTORY) {
00537                 DOS_SetError(DOSERR_ACCESS_DENIED);
00538                 return false;
00539         }
00540         bool foundit=Drives[drive]->FileCreate(&Files[handle],fullname,attributes);
00541         if (foundit) { 
00542                 Files[handle]->SetDrive(drive);
00543                 Files[handle]->AddRef();
00544                 Files[handle]->drive = drive;
00545                 psp.SetFileHandle(*entry,handle);
00546                 return true;
00547         } else {
00548                 if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); 
00549                 else DOS_SetError(DOSERR_FILE_NOT_FOUND);
00550                 return false;
00551         }
00552 }
00553 
00554 bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry) {
00555 #if defined(WIN32) && !defined(__MINGW32__)
00556         if(Network_IsNetworkResource(const_cast<char *>(name)))
00557                 return Network_OpenFile(const_cast<char *>(name),flags,entry);
00558 #endif
00559         /* First check for devices */
00560         if (flags>2) LOG(LOG_FILES,LOG_NORMAL)("Special file open command %X file %s",flags,name); // FIXME: Why? Is there something about special opens DOSBox doesn't handle properly?
00561         else LOG(LOG_FILES,LOG_NORMAL)("file open command %X file %s",flags,name);
00562 
00563         DOS_PSP psp(dos.psp());
00564         Bit16u attr = 0;
00565         Bit8u devnum = DOS_FindDevice(name);
00566         bool device = (devnum != DOS_DEVICES);
00567         if(!device && DOS_GetFileAttr(name,&attr)) {
00568         //DON'T ALLOW directories to be openened.(skip test if file is device).
00569                 if((attr & DOS_ATTR_DIRECTORY) || (attr & DOS_ATTR_VOLUME)){
00570                         DOS_SetError(DOSERR_ACCESS_DENIED);
00571                         return false;
00572                 }
00573         }
00574 
00575         char fullname[DOS_PATHLENGTH];Bit8u drive;Bit8u i;
00576         /* First check if the name is correct */
00577         if (!DOS_MakeName(name,fullname,&drive)) return false;
00578         Bit8u handle=255;               
00579         /* Check for a free file handle */
00580         for (i=0;i<DOS_FILES;i++) {
00581                 if (!Files[i]) {
00582                         handle=i;
00583                         break;
00584                 }
00585         }
00586         if (handle==255) {
00587                 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
00588                 return false;
00589         }
00590         /* We have a position in the main table now find one in the psp table */
00591         *entry = psp.FindFreeFileEntry();
00592 
00593         if (*entry==0xff) {
00594                 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
00595                 return false;
00596         }
00597         bool exists=false;
00598         if (device) {
00599                 Files[handle]=new DOS_Device(*Devices[devnum]);
00600         } else {
00601                 exists=Drives[drive]->FileOpen(&Files[handle],fullname,flags);
00602                 if (exists) Files[handle]->SetDrive(drive);
00603         }
00604         if (exists || device ) { 
00605                 Files[handle]->AddRef();
00606                 psp.SetFileHandle(*entry,handle);
00607                 Files[handle]->drive = drive;
00608                 return true;
00609         } else {
00610                 //Test if file exists, but opened in read-write mode (and writeprotected)
00611                 if(((flags&3) != OPEN_READ) && Drives[drive]->FileExists(fullname))
00612                         DOS_SetError(DOSERR_ACCESS_DENIED);
00613                 else {
00614                         if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); 
00615                         else DOS_SetError(DOSERR_FILE_NOT_FOUND);
00616                 }
00617                 return false;
00618         }
00619 }
00620 
00621 bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status) {
00622 // FIXME: Not yet supported : Bit 13 of flags (int 0x24 on critical error)
00623         Bit16u result = 0;
00624         if (action==0) {
00625                 // always fail setting
00626                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00627                 return false;
00628         } else {
00629                 if (((action & 0x0f)>2) || ((action & 0xf0)>0x10)) {
00630                         // invalid action parameter
00631                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00632                         return false;
00633                 }
00634         }
00635         if (DOS_OpenFile(name, (Bit8u)(flags&0xff), entry)) {
00636                 // File already exists
00637                 switch (action & 0x0f) {
00638                         case 0x00:              // failed
00639                                 DOS_SetError(DOSERR_FILE_ALREADY_EXISTS);
00640                                 return false;
00641                         case 0x01:              // file open (already done)
00642                                 result = 1;
00643                                 break;
00644                         case 0x02:              // replace
00645                                 DOS_CloseFile(*entry);
00646                                 if (!DOS_CreateFile(name, createAttr, entry)) return false;
00647                                 result = 3;
00648                                 break;
00649                         default:
00650                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00651                                 E_Exit("DOS: OpenFileExtended: Unknown action.");
00652                                 break;
00653                 }
00654         } else {
00655                 // File doesn't exist
00656                 if ((action & 0xf0)==0) {
00657                         // uses error code from failed open
00658                         return false;
00659                 }
00660                 // Create File
00661                 if (!DOS_CreateFile(name, createAttr, entry)) {
00662                         // uses error code from failed create
00663                         return false;
00664                 }
00665                 result = 2;
00666         }
00667         // success
00668         *status = result;
00669         return true;
00670 }
00671 
00672 bool DOS_UnlinkFile(char const * const name) {
00673         char fullname[DOS_PATHLENGTH];Bit8u drive;
00674         // An existing device returns an access denied error
00675         if (DOS_FindDevice(name) != DOS_DEVICES) {
00676                 DOS_SetError(DOSERR_ACCESS_DENIED);
00677                 return false;
00678         }
00679         if (!DOS_MakeName(name,fullname,&drive)) return false;
00680         if(Drives[drive]->FileUnlink(fullname)){
00681                 return true;
00682         } else {
00683                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00684                 return false;
00685         }
00686 }
00687 
00688 bool DOS_GetFileAttr(char const * const name,Bit16u * attr) {
00689         char fullname[DOS_PATHLENGTH];Bit8u drive;
00690         if (!DOS_MakeName(name,fullname,&drive)) return false;
00691         if (Drives[drive]->GetFileAttr(fullname,attr)) {
00692                 return true;
00693         } else {
00694                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00695                 return false;
00696         }
00697 }
00698 
00699 bool DOS_SetFileAttr(char const * const name,Bit16u /*attr*/) 
00700 // this function does not change the file attributs
00701 // it just does some tests if file is available 
00702 // returns false when using on cdrom (stonekeep)
00703 {
00704         Bit16u attrTemp;
00705         char fullname[DOS_PATHLENGTH];Bit8u drive;
00706         if (!DOS_MakeName(name,fullname,&drive)) return false;  
00707         if (strncmp(Drives[drive]->GetInfo(),"CDRom ",6)==0 || strncmp(Drives[drive]->GetInfo(),"isoDrive ",9)==0) {
00708                 DOS_SetError(DOSERR_ACCESS_DENIED);
00709                 return false;
00710         }
00711         return Drives[drive]->GetFileAttr(fullname,&attrTemp);
00712 }
00713 
00714 bool DOS_Canonicalize(char const * const name,char * const big) {
00715 //TODO Add Better support for devices and shit but will it be needed i doubt it :) 
00716         Bit8u drive;
00717         char fullname[DOS_PATHLENGTH];
00718         if (!DOS_MakeName(name,fullname,&drive)) return false;
00719         big[0]=drive+'A';
00720         big[1]=':';
00721         big[2]='\\';
00722         strcpy(&big[3],fullname);
00723         return true;
00724 }
00725 
00726 bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free) {
00727         if (drive==0) drive=DOS_GetDefaultDrive();
00728         else drive--;
00729         if ((drive>=DOS_DRIVES) || (!Drives[drive])) {
00730                 DOS_SetError(DOSERR_INVALID_DRIVE);
00731                 return false;
00732         }
00733         return Drives[drive]->AllocationInfo(bytes,sectors,clusters,free);
00734 }
00735 
00736 bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry) {
00737         // Dont duplicate console handles
00738 /*      if (entry<=STDPRN) {
00739                 *newentry = entry;
00740                 return true;
00741         };
00742 */      
00743         Bit8u handle=RealHandle(entry);
00744         if (handle>=DOS_FILES) {
00745                 DOS_SetError(DOSERR_INVALID_HANDLE);
00746                 return false;
00747         };
00748         if (!Files[handle] || !Files[handle]->IsOpen()) {
00749                 DOS_SetError(DOSERR_INVALID_HANDLE);
00750                 return false;
00751         };
00752         DOS_PSP psp(dos.psp());
00753         *newentry = psp.FindFreeFileEntry();
00754         if (*newentry==0xff) {
00755                 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
00756                 return false;
00757         }
00758         Files[handle]->AddRef();        
00759         psp.SetFileHandle(*newentry,handle);
00760         return true;
00761 }
00762 
00763 bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry) {
00764         if(entry == newentry) {
00765                 DOS_SetError(DOSERR_INVALID_HANDLE);
00766                 return false;
00767         }
00768         Bit8u orig = RealHandle(entry);
00769         if (orig >= DOS_FILES) {
00770                 DOS_SetError(DOSERR_INVALID_HANDLE);
00771                 return false;
00772         };
00773         if (!Files[orig] || !Files[orig]->IsOpen()) {
00774                 DOS_SetError(DOSERR_INVALID_HANDLE);
00775                 return false;
00776         };
00777         Bit8u newone = RealHandle(newentry);
00778         if (newone < DOS_FILES && Files[newone]) {
00779                 DOS_CloseFile(newentry);
00780         }
00781         DOS_PSP psp(dos.psp());
00782         Files[orig]->AddRef();
00783         psp.SetFileHandle(newentry,orig);
00784         return true;
00785 }
00786 
00787 
00788 bool DOS_CreateTempFile(char * const name,Bit16u * entry) {
00789         size_t namelen=strlen(name);
00790         char * tempname=name+namelen;
00791         if (namelen==0) {
00792                 // temp file created in root directory
00793                 tempname[0]='\\';
00794                 tempname++;
00795         } else {
00796                 if ((name[namelen-1]!='\\') && (name[namelen-1]!='/')) {
00797                         tempname[0]='\\';
00798                         tempname++;
00799                 }
00800         }
00801         dos.errorcode=0;
00802         /* add random crap to the end of the name and try to open */
00803         do {
00804                 Bit32u i;
00805                 for (i=0;i<8;i++) {
00806                         tempname[i]=(rand()%26)+'A';
00807                 }
00808                 tempname[8]=0;
00809         } while ((!DOS_CreateFile(name,0,entry)) && (dos.errorcode==DOSERR_FILE_ALREADY_EXISTS));
00810         if (dos.errorcode) return false;
00811         return true;
00812 }
00813 
00814 #define FCB_SEP ":.;,=+"
00815 #define ILLEGAL ":.;,=+ \t/\"[]<>|"
00816 
00817 static bool isvalid(const char in){
00818         const char ill[]=ILLEGAL;    
00819         return (Bit8u(in)>0x1F) && (!strchr(ill,in));
00820 }
00821 
00822 #define PARSE_SEP_STOP          0x01
00823 #define PARSE_DFLT_DRIVE        0x02
00824 #define PARSE_BLNK_FNAME        0x04
00825 #define PARSE_BLNK_FEXT         0x08
00826 
00827 #define PARSE_RET_NOWILD        0
00828 #define PARSE_RET_WILD          1
00829 #define PARSE_RET_BADDRIVE      0xff
00830 
00831 Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change) {
00832         char * string_begin=string;
00833         Bit8u ret=0;
00834         if (!(parser & PARSE_DFLT_DRIVE)) {
00835                 // default drive forced, this intentionally invalidates an extended FCB
00836                 mem_writeb(PhysMake(seg,offset),0);
00837         }
00838         DOS_FCB fcb(seg,offset,false);  // always a non-extended FCB
00839         bool hasdrive,hasname,hasext,finished;
00840         hasdrive=hasname=hasext=finished=false;
00841         Bitu index=0;
00842         Bit8u fill=' ';
00843 /* First get the old data from the fcb */
00844 #ifdef _MSC_VER
00845 #pragma pack (1)
00846 #endif
00847         union {
00848                 struct {
00849                         char drive[2];
00850                         char name[9];
00851                         char ext[4];
00852                 } GCC_ATTRIBUTE (packed) part;
00853                 char full[DOS_FCBNAME];
00854         } fcb_name;
00855 #ifdef _MSC_VER
00856 #pragma pack()
00857 #endif
00858         /* Get the old information from the previous fcb */
00859         fcb.GetName(fcb_name.full);
00860         fcb_name.part.drive[0]-='A'-1;fcb_name.part.drive[1]=0;
00861         fcb_name.part.name[8]=0;fcb_name.part.ext[3]=0;
00862         /* Strip of the leading sepetaror */
00863         if((parser & PARSE_SEP_STOP) && *string)  {       //ignore leading seperator
00864                 char sep[] = FCB_SEP;char a[2];
00865                 a[0]= *string;a[1]='\0';
00866                 if (strcspn(a,sep)==0) string++;
00867         } 
00868         /* strip leading spaces */
00869         while((*string==' ')||(*string=='\t')) string++;
00870         /* Check for a drive */
00871         if (string[1]==':') {
00872                 fcb_name.part.drive[0]=0;
00873                 hasdrive=true;
00874                 if (isalpha(string[0]) && Drives[ascii_toupper(string[0])-'A']) {
00875                         fcb_name.part.drive[0]=(char)(ascii_toupper(string[0])-'A'+1);
00876                 } else ret=0xff;
00877                 string+=2;
00878         }
00879         /* Special checks for . and .. */
00880         if (string[0]=='.') {
00881                 string++;
00882                 if (!string[0]) {
00883                         hasname=true;
00884                         ret=PARSE_RET_NOWILD;
00885                         strcpy(fcb_name.part.name,".       ");
00886                         goto savefcb;
00887                 }
00888                 if (string[1]=='.' && !string[1])       {
00889                         string++;
00890                         hasname=true;
00891                         ret=PARSE_RET_NOWILD;
00892                         strcpy(fcb_name.part.name,"..      ");
00893                         goto savefcb;
00894                 }
00895                 goto checkext;
00896         }
00897         /* Copy the name */     
00898         hasname=true;finished=false;fill=' ';index=0;
00899         while (index<8) {
00900                 if (!finished) {
00901                         if (string[0]=='*') {fill='?';fcb_name.part.name[index]='?';if (!ret) ret=1;finished=true;}
00902                         else if (string[0]=='?') {fcb_name.part.name[index]='?';if (!ret) ret=1;}
00903             else if (IS_PC98_ARCH && shiftjis_lead_byte(string[0])) {
00904                 /* Shift-JIS is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */
00905                 fcb_name.part.name[index]=string[0];
00906                 string++;
00907                 index++;
00908                 if (index >= 8) break;
00909 
00910                 /* should be trailing byte of Shift-JIS */
00911                 if ((unsigned char)string[0] < 32 || (unsigned char)string[0] >= 127) continue;
00912 
00913                 fcb_name.part.name[index]=string[0];
00914             }
00915                         else if (isvalid(string[0])) {fcb_name.part.name[index]=(char)(ascii_toupper(string[0]));}
00916                         else { finished=true;continue; }
00917                         string++;
00918                 } else {
00919                         fcb_name.part.name[index]=(char)fill;
00920                 }
00921                 index++;
00922         }
00923         if (!(string[0]=='.')) goto savefcb;
00924         string++;
00925 checkext:
00926         /* Copy the extension */
00927         hasext=true;finished=false;fill=' ';index=0;
00928         while (index<3) {
00929                 if (!finished) {
00930                         if (string[0]=='*') {fill='?';fcb_name.part.ext[index]='?';finished=true;}
00931                         else if (string[0]=='?') {fcb_name.part.ext[index]='?';if (!ret) ret=1;}
00932                         else if (isvalid(string[0])) {fcb_name.part.ext[index]=(char)(ascii_toupper(string[0]));}
00933             else if (IS_PC98_ARCH && shiftjis_lead_byte(string[0])) {
00934                 /* Shift-JIS is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */
00935                 fcb_name.part.ext[index]=string[0];
00936                 string++;
00937                 index++;
00938                 if (index >= 3) break;
00939 
00940                 /* should be trailing byte of Shift-JIS */
00941                 if ((unsigned char)string[0] < 32u || (unsigned char)string[0] >= 127u) continue;
00942 
00943                 fcb_name.part.ext[index]=string[0];
00944             }
00945                         else { finished=true;continue; }
00946                         string++;
00947                 } else {
00948                         fcb_name.part.ext[index]=(char)fill;
00949                 }
00950                 index++;
00951         }
00952 savefcb:
00953         if (!hasdrive & !(parser & PARSE_DFLT_DRIVE)) fcb_name.part.drive[0] = 0;
00954         if (!hasname & !(parser & PARSE_BLNK_FNAME)) strcpy(fcb_name.part.name,"        ");
00955         if (!hasext & !(parser & PARSE_BLNK_FEXT)) strcpy(fcb_name.part.ext,"   ");
00956         fcb.SetName((unsigned char)fcb_name.part.drive[0],fcb_name.part.name,fcb_name.part.ext);
00957         *change=(Bit8u)(string-string_begin);
00958         return ret;
00959 }
00960 
00961 static void DTAExtendName(char * const name,char * const filename,char * const ext) {
00962         char * find=strchr(name,'.');
00963         if (find && find!=name) {
00964                 strcpy(ext,find+1);
00965                 *find=0;
00966         } else ext[0]=0;
00967         strcpy(filename,name);
00968         size_t i;
00969         for (i=strlen(name);i<8;i++) filename[i]=' ';
00970         filename[8]=0;
00971         for (i=strlen(ext);i<3;i++) ext[i]=' ';
00972         ext[3]=0;
00973 }
00974 
00975 static void SaveFindResult(DOS_FCB & find_fcb) {
00976         DOS_DTA find_dta(dos.tables.tempdta);
00977         char name[DOS_NAMELENGTH_ASCII];Bit32u size;Bit16u date;Bit16u time;Bit8u attr;Bit8u drive;
00978         char file_name[9];char ext[4];
00979         find_dta.GetResult(name,size,date,time,attr);
00980         drive=find_fcb.GetDrive()+1;
00981         Bit8u find_attr = DOS_ATTR_ARCHIVE;
00982         find_fcb.GetAttr(find_attr); /* Gets search attributes if extended */
00983         /* Create a correct file and extention */
00984         DTAExtendName(name,file_name,ext);      
00985         DOS_FCB fcb(RealSeg(dos.dta()),RealOff(dos.dta()));//TODO
00986         fcb.Create(find_fcb.Extended());
00987         fcb.SetName(drive,file_name,ext);
00988         fcb.SetAttr(find_attr);      /* Only adds attribute if fcb is extended */
00989         fcb.SetResultAttr(attr);
00990         fcb.SetSizeDateTime(size,date,time);
00991 }
00992 
00993 bool DOS_FCBCreate(Bit16u seg,Bit16u offset) { 
00994         DOS_FCB fcb(seg,offset);
00995         char shortname[DOS_FCBNAME];Bit16u handle;
00996         fcb.GetName(shortname);
00997         Bit8u attr = DOS_ATTR_ARCHIVE;
00998         fcb.GetAttr(attr);
00999         if (!attr) attr = DOS_ATTR_ARCHIVE; //Better safe than sorry 
01000         if (!DOS_CreateFile(shortname,attr,&handle)) return false;
01001         fcb.FileOpen((Bit8u)handle);
01002         return true;
01003 }
01004 
01005 bool DOS_FCBOpen(Bit16u seg,Bit16u offset) { 
01006         DOS_FCB fcb(seg,offset);
01007         char shortname[DOS_FCBNAME];Bit16u handle;
01008         fcb.GetName(shortname);
01009 
01010         /* First check if the name is correct */
01011         Bit8u drive;
01012         char fullname[DOS_PATHLENGTH];
01013         if (!DOS_MakeName(shortname,fullname,&drive)) return false;
01014         
01015         /* Check, if file is already opened */
01016         for (Bit8u i=0;i<DOS_FILES;i++) {
01017                 DOS_PSP psp(dos.psp());
01018                 if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) {
01019                         handle = psp.FindEntryByHandle(i);
01020                         if (handle==0xFF) {
01021                                 // This shouldnt happen
01022                                 LOG(LOG_FILES,LOG_ERROR)("DOS: File %s is opened but has no psp entry.",shortname);
01023                                 return false;
01024                         }
01025                         fcb.FileOpen((Bit8u)handle);
01026                         return true;
01027                 }
01028         }
01029         
01030         if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle)) return false;
01031         fcb.FileOpen((Bit8u)handle);
01032         return true;
01033 }
01034 
01035 bool DOS_FCBClose(Bit16u seg,Bit16u offset) {
01036         DOS_FCB fcb(seg,offset);
01037         if(!fcb.Valid()) return false;
01038         Bit8u fhandle;
01039         fcb.FileClose(fhandle);
01040         DOS_CloseFile(fhandle);
01041         return true;
01042 }
01043 
01044 bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset) {
01045         DOS_FCB fcb(seg,offset);
01046         RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta);
01047         char name[DOS_FCBNAME];fcb.GetName(name);
01048         Bit8u attr = DOS_ATTR_ARCHIVE;
01049         fcb.GetAttr(attr); /* Gets search attributes if extended */
01050         bool ret=DOS_FindFirst(name,attr,true);
01051         dos.dta(old_dta);
01052         if (ret) SaveFindResult(fcb);
01053         return ret;
01054 }
01055 
01056 bool DOS_FCBFindNext(Bit16u seg,Bit16u offset) {
01057         DOS_FCB fcb(seg,offset);
01058         RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta);
01059         bool ret=DOS_FindNext();
01060         dos.dta(old_dta);
01061         if (ret) SaveFindResult(fcb);
01062         return ret;
01063 }
01064 
01065 Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset,Bit16u recno) {
01066         DOS_FCB fcb(seg,offset);
01067         Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
01068         fcb.GetSeqData(fhandle,rec_size);
01069         if (fhandle==0xff && rec_size!=0) {
01070                 if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA;
01071                 LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB");
01072                 fcb.GetSeqData(fhandle,rec_size);
01073         }
01074         fcb.GetRecord(cur_block,cur_rec);
01075         Bit32u pos=((cur_block*128u)+cur_rec)*rec_size;
01076         if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_READ_NODATA; 
01077         Bit16u toread=rec_size;
01078         if (!DOS_ReadFile(fhandle,dos_copybuf,&toread)) return FCB_READ_NODATA;
01079         if (toread==0) return FCB_READ_NODATA;
01080         if (toread < rec_size) { //Zero pad copybuffer to rec_size
01081                 Bitu i = toread;
01082                 while(i < rec_size) dos_copybuf[i++] = 0;
01083         }
01084         MEM_BlockWrite(Real2Phys(dos.dta())+(PhysPt)(recno*rec_size),dos_copybuf,rec_size);
01085         if (++cur_rec>127u) { cur_block++;cur_rec=0; }
01086         fcb.SetRecord(cur_block,cur_rec);
01087         if (toread==rec_size) return FCB_SUCCESS;
01088         if (toread==0) return FCB_READ_NODATA;
01089         return FCB_READ_PARTIAL;
01090 }
01091 
01092 Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u recno) {
01093         DOS_FCB fcb(seg,offset);
01094         Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
01095         fcb.GetSeqData(fhandle,rec_size);
01096         if (fhandle==0xffu && rec_size!=0u) {
01097                 if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA;
01098                 LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB");
01099                 fcb.GetSeqData(fhandle,rec_size);
01100         }
01101         fcb.GetRecord(cur_block,cur_rec);
01102         Bit32u pos=((cur_block*128u)+cur_rec)*rec_size;
01103         if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_ERR_WRITE; 
01104         MEM_BlockRead(Real2Phys(dos.dta())+(PhysPt)(recno*rec_size),dos_copybuf,rec_size);
01105         Bit16u towrite=rec_size;
01106         if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite)) return FCB_ERR_WRITE;
01107         Bit32u size;Bit16u date,time;
01108         fcb.GetSizeDateTime(size,date,time);
01109         if (pos+towrite>size) size=pos+towrite;
01110         //time doesn't keep track of endofday
01111         date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day);
01112         Bit32u ticks = mem_readd(BIOS_TIMER);
01113         Bit32u seconds = (ticks*10u)/182u;
01114         Bit16u hour = (Bit16u)(seconds/3600u);
01115         Bit16u min = (Bit16u)((seconds % 3600u)/60u);
01116         Bit16u sec = (Bit16u)(seconds % 60u);
01117         time = DOS_PackTime(hour,min,sec);
01118         Bit8u temp=RealHandle(fhandle);
01119         Files[temp]->time=time;
01120         Files[temp]->date=date;
01121         fcb.SetSizeDateTime(size,date,time);
01122         if (++cur_rec>127u) { cur_block++;cur_rec=0; }  
01123         fcb.SetRecord(cur_block,cur_rec);
01124         return FCB_SUCCESS;
01125 }
01126 
01127 Bit8u DOS_FCBIncreaseSize(Bit16u seg,Bit16u offset) {
01128         DOS_FCB fcb(seg,offset);
01129         Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size;
01130         fcb.GetSeqData(fhandle,rec_size);
01131         fcb.GetRecord(cur_block,cur_rec);
01132         Bit32u pos=((cur_block*128u)+cur_rec)*rec_size;
01133         if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET)) return FCB_ERR_WRITE; 
01134         Bit16u towrite=0;
01135         if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite)) return FCB_ERR_WRITE;
01136         Bit32u size;Bit16u date,time;
01137         fcb.GetSizeDateTime(size,date,time);
01138         if (pos+towrite>size) size=pos+towrite;
01139         //time doesn't keep track of endofday
01140         date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day);
01141         Bit32u ticks = mem_readd(BIOS_TIMER);
01142         Bit32u seconds = (ticks*10u)/182u;
01143         Bit16u hour = (Bit16u)(seconds/3600u);
01144         Bit16u min = (Bit16u)((seconds % 3600u)/60u);
01145         Bit16u sec = (Bit16u)(seconds % 60u);
01146         time = DOS_PackTime(hour,min,sec);
01147         Bit8u temp=RealHandle(fhandle);
01148         Files[temp]->time=time;
01149         Files[temp]->date=date;
01150         fcb.SetSizeDateTime(size,date,time);
01151         fcb.SetRecord(cur_block,cur_rec);
01152         return FCB_SUCCESS;
01153 }
01154 
01155 Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) {
01156 /* if restore is true :random read else random blok read. 
01157  * random read updates old block and old record to reflect the random data
01158  * before the read!!!!!!!!! and the random data is not updated! (user must do this)
01159  * Random block read updates these fields to reflect the state after the read!
01160  */
01161         DOS_FCB fcb(seg,offset);
01162         Bit32u random;
01163         Bit16u old_block=0;
01164         Bit8u old_rec=0;
01165         Bit8u error=0;
01166         Bit16u count;
01167 
01168         /* Set the correct record from the random data */
01169         fcb.GetRandom(random);
01170         fcb.SetRecord((Bit16u)(random / 128u),(Bit8u)(random & 127u));
01171         if (restore) fcb.GetRecord(old_block,old_rec);//store this for after the read.
01172         // Read records
01173         for (count=0; count<*numRec; count++) {
01174                 error = DOS_FCBRead(seg,offset,count);
01175                 if (error!=FCB_SUCCESS) break;
01176         }
01177         if (error==FCB_READ_PARTIAL) count++;   //partial read counts
01178         *numRec=count;
01179         Bit16u new_block;Bit8u new_rec;
01180         fcb.GetRecord(new_block,new_rec);
01181         if (restore) fcb.SetRecord(old_block,old_rec);
01182         /* Update the random record pointer with new position only when restore is false*/
01183         if(!restore) fcb.SetRandom(new_block*128u+new_rec); 
01184         return error;
01185 }
01186 
01187 Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) {
01188 /* see FCB_RandomRead */
01189         DOS_FCB fcb(seg,offset);
01190         Bit32u random;
01191         Bit16u old_block=0;
01192         Bit8u old_rec=0;
01193         Bit8u error=0;
01194         Bit16u count;
01195 
01196         /* Set the correct record from the random data */
01197         fcb.GetRandom(random);
01198         fcb.SetRecord((Bit16u)(random / 128u),(Bit8u)(random & 127u));
01199         if (restore) fcb.GetRecord(old_block,old_rec);
01200         if (*numRec > 0) {
01201                 /* Write records */
01202                 for (count=0; count<*numRec; count++) {
01203                         error = DOS_FCBWrite(seg,offset,count);// dos_fcbwrite return 0 false when true...
01204                         if (error!=FCB_SUCCESS) break;
01205                 }
01206                 *numRec=count;
01207         } else {
01208                 DOS_FCBIncreaseSize(seg,offset);
01209         }
01210         Bit16u new_block;Bit8u new_rec;
01211         fcb.GetRecord(new_block,new_rec);
01212         if (restore) fcb.SetRecord(old_block,old_rec);
01213         /* Update the random record pointer with new position only when restore is false */
01214         if (!restore) fcb.SetRandom(new_block*128u+new_rec); 
01215         return error;
01216 }
01217 
01218 bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset) {
01219         char shortname[DOS_PATHLENGTH];Bit16u entry;Bit8u handle;Bit16u rec_size;
01220         DOS_FCB fcb(seg,offset);
01221         fcb.GetName(shortname);
01222         if (!DOS_OpenFile(shortname,OPEN_READ,&entry)) return false;
01223         handle = RealHandle(entry);
01224         Bit32u size = 0;
01225         Files[handle]->Seek(&size,DOS_SEEK_END);
01226         DOS_CloseFile(entry);fcb.GetSeqData(handle,rec_size);
01227         if (rec_size == 0) rec_size = 128; //Use default if missing.
01228         Bit32u random=(size/rec_size);
01229         if (size % rec_size) random++;
01230         fcb.SetRandom(random);
01231         return true;
01232 }
01233 
01234 bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset){
01235 /* FCB DELETE honours wildcards. it will return true if one or more
01236  * files get deleted. 
01237  * To get this: the dta is set to temporary dta in which found files are
01238  * stored. This can not be the tempdta as that one is used by fcbfindfirst
01239  */
01240         RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta_fcbdelete);
01241         RealPt new_dta=dos.dta();
01242         bool nextfile = false;
01243         bool return_value = false;
01244         nextfile = DOS_FCBFindFirst(seg,offset);
01245         DOS_FCB fcb(RealSeg(new_dta),RealOff(new_dta));
01246         while(nextfile) {
01247                 char shortname[DOS_FCBNAME] = { 0 };
01248                 fcb.GetName(shortname);
01249                 bool res=DOS_UnlinkFile(shortname);
01250                 if(!return_value && res) return_value = true; //at least one file deleted
01251                 nextfile = DOS_FCBFindNext(seg,offset);
01252         }
01253         dos.dta(old_dta);  /*Restore dta */
01254         return  return_value;
01255 }
01256 
01257 bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset){
01258         DOS_FCB fcbold(seg,offset);
01259         DOS_FCB fcbnew(seg,offset+16u);
01260         if(!fcbold.Valid()) return false;
01261         char oldname[DOS_FCBNAME];
01262         char newname[DOS_FCBNAME];
01263         fcbold.GetName(oldname);fcbnew.GetName(newname);
01264 
01265         /* Check, if sourcefile is still open. This was possible in DOS, but modern oses don't like this */
01266         Bit8u drive; char fullname[DOS_PATHLENGTH];
01267         if (!DOS_MakeName(oldname,fullname,&drive)) return false;
01268         
01269         DOS_PSP psp(dos.psp());
01270         for (Bit8u i=0;i<DOS_FILES;i++) {
01271                 if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) {
01272                         Bit16u handle = psp.FindEntryByHandle(i);
01273                         if (handle == 0xFFu) {
01274                                 // This shouldnt happen
01275                                 LOG(LOG_FILES,LOG_ERROR)("DOS: File %s is opened but has no psp entry.",oldname);
01276                                 return false;
01277                         }
01278                         DOS_CloseFile(handle);
01279                 }
01280         }
01281 
01282         /* Rename the file */
01283         return DOS_Rename(oldname,newname);
01284 }
01285 
01286 void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset) {
01287         DOS_FCB fcb(seg,offset);
01288         Bit16u block;Bit8u rec;
01289         fcb.GetRecord(block,rec);
01290         fcb.SetRandom(block*128u+rec);
01291 }
01292 
01293 
01294 bool DOS_FileExists(char const * const name) {
01295         char fullname[DOS_PATHLENGTH];Bit8u drive;
01296         if (!DOS_MakeName(name,fullname,&drive)) return false;
01297         return Drives[drive]->FileExists(fullname);
01298 }
01299 
01300 bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters) {
01301         if (!drive) drive =  DOS_GetDefaultDrive();
01302         else drive--;
01303         if (drive >= DOS_DRIVES || !Drives[drive]) {
01304                 DOS_SetError(DOSERR_INVALID_DRIVE);
01305                 return false;
01306         }
01307         Bit16u _free_clusters;
01308         Drives[drive]->AllocationInfo(_bytes_sector,_sectors_cluster,_total_clusters,&_free_clusters);
01309         SegSet16(ds,RealSeg(dos.tables.mediaid));
01310         reg_bx=RealOff(dos.tables.mediaid+drive*2u);
01311         return true;
01312 }
01313 
01314 bool DOS_SetDrive(Bit8u drive) {
01315         if (Drives[drive]) {
01316                 DOS_SetDefaultDrive(drive);
01317                 return true;
01318         } else {
01319                 return false;
01320         }
01321 }
01322 
01323 bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate) {
01324         Bit32u handle=RealHandle(entry);
01325         if (handle>=DOS_FILES) {
01326                 DOS_SetError(DOSERR_INVALID_HANDLE);
01327                 return false;
01328         };
01329         if (!Files[handle] || !Files[handle]->IsOpen()) {
01330                 DOS_SetError(DOSERR_INVALID_HANDLE);
01331                 return false;
01332         };
01333         if (!Files[handle]->UpdateDateTimeFromHost()) {
01334                 DOS_SetError(DOSERR_INVALID_HANDLE);
01335                 return false; 
01336         }
01337         *otime = Files[handle]->time;
01338         *odate = Files[handle]->date;
01339         return true;
01340 }
01341 
01342 bool DOS_SetFileDate(Bit16u entry, Bit16u ntime, Bit16u ndate)
01343 {
01344         Bit32u handle=RealHandle(entry);
01345         if (handle>=DOS_FILES) {
01346                 DOS_SetError(DOSERR_INVALID_HANDLE);
01347                 return false;
01348         };
01349         if (!Files[handle]) {
01350                 DOS_SetError(DOSERR_INVALID_HANDLE);
01351                 return false;
01352         };
01353         Files[handle]->time = ntime;
01354         Files[handle]->date = ndate;
01355         Files[handle]->newtime = true;
01356 
01357         return true;
01358 }
01359 
01360 void DOS_SetupFiles (void) {
01361         /* Setup the File Handles */
01362         Files = new DOS_File * [DOS_FILES];
01363         Bit32u i;
01364         for (i=0;i<DOS_FILES;i++) {
01365                 Files[i]=0;
01366         }
01367         /* Setup the Virtual Disk System */
01368         for (i=0;i<DOS_DRIVES;i++) {
01369                 Drives[i]=0;
01370         }
01371         Drives[25]=new Virtual_Drive();
01372 }
01373