DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include <string.h> 00021 #include <stdlib.h> 00022 #include <time.h> 00023 #include <ctype.h> 00024 #if defined(WIN32) && defined(__MINGW32__) 00025 # include <malloc.h> 00026 #endif 00027 00028 #include "dosbox.h" 00029 #include "bios.h" 00030 #include "mem.h" 00031 #include "regs.h" 00032 #include "dos_inc.h" 00033 #include "drives.h" 00034 #include "cross.h" 00035 #include "control.h" 00036 #include "dos_network2.h" 00037 00038 #define DOS_FILESTART 4 00039 00040 #define FCB_SUCCESS 0 00041 #define FCB_READ_NODATA 1 00042 #define FCB_READ_PARTIAL 3 00043 #define FCB_ERR_NODATA 1 00044 #define FCB_ERR_EOF 3 00045 #define FCB_ERR_WRITE 1 00046 00047 extern bool log_int21; 00048 extern bool log_fileio; 00049 extern int dos_clipboard_device_access; 00050 extern char *dos_clipboard_device_name; 00051 00052 Bitu DOS_FILES = 127; 00053 DOS_File ** Files = NULL; 00054 DOS_Drive * Drives[DOS_DRIVES] = {NULL}; 00055 bool force_sfn = false; 00056 int sdrive = 0; 00057 00058 /* This is the LFN filefind handle that is currently being used, with normal values between 00059 * 0 and 254 for LFN calls. The value LFN_FILEFIND_INTERNAL and LFN_FILEFIND_IMG are used 00060 * internally by LFN and image handling functions. For non-LFN calls the value is fixed to 00061 * be LFN_FILEFIND_NONE as defined in drives.h. */ 00062 int lfn_filefind_handle = LFN_FILEFIND_NONE; 00063 00064 bool shiftjis_lead_byte(int c); 00065 00066 Bit8u DOS_GetDefaultDrive(void) { 00067 // return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive(); 00068 Bit8u d = DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDrive(); 00069 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); 00070 return dos.current_drive; 00071 } 00072 00073 void DOS_SetDefaultDrive(Bit8u drive) { 00074 // if (drive<=DOS_DRIVES && ((drive<2) || Drives[drive])) DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive); 00075 if (drive<DOS_DRIVES && ((drive<2) || Drives[drive])) {dos.current_drive = drive; DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(drive);} 00076 } 00077 00078 bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive) { 00079 if(!name || *name == 0 || *name == ' ') { 00080 /* Both \0 and space are seperators and 00081 * empty filenames report file not found */ 00082 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00083 return false; 00084 } 00085 char names[LFN_NAMELENGTH]; 00086 strcpy(names,name); 00087 char * name_int = names; 00088 if (strlen(names)==14 && name_int[1]==':' && name_int[2]!='\\' && name_int[9]==' ' && name_int[10]=='.') { 00089 for (unsigned int i=0;i<strlen(names);i++) 00090 if (i<10 && name_int[i]==32) { 00091 name_int[i]='.'; 00092 name_int[i+1]=name_int[11]==32?0:toupper(name_int[11]); 00093 name_int[i+2]=name_int[12]==32?0:toupper(name_int[12]); 00094 name_int[i+3]=name_int[13]==32?0:toupper(name_int[13]); 00095 name_int[i+4]=0; 00096 break; 00097 } else if (i<10) name_int[i]=toupper(name_int[i]); 00098 } 00099 char tempdir[DOS_PATHLENGTH]; 00100 char upname[DOS_PATHLENGTH]; 00101 Bitu r,w, q=0; 00102 /* First get the drive */ 00103 *drive = DOS_GetDefaultDrive(); 00104 while (name_int[0]=='"') { 00105 q++; 00106 name_int++; 00107 } 00108 if (name_int[1]==':') { 00109 *drive=(name_int[0] | 0x20)-'a'; 00110 name_int+=2; 00111 } 00112 if (*drive>=DOS_DRIVES || !Drives[*drive]) { 00113 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00114 return false; 00115 } 00116 r=0;w=0; 00117 while (r<DOS_PATHLENGTH && name_int[r]!=0) { 00118 Bit8u c=(Bit8u)name_int[r++]; 00119 if (c=='/') c='\\'; 00120 else if (c=='"') {q++;continue;} 00121 else if (uselfn&&!force_sfn) { 00122 if (c==' ' && q/2*2 == q) continue; 00123 } else { 00124 if ((c>='a') && (c<='z')) c-=32; 00125 else if (c==' ') continue; /* should be separator */ 00126 } 00127 upname[w++]=(char)c; 00128 if (IS_PC98_ARCH && shiftjis_lead_byte(c) && r<DOS_PATHLENGTH) { 00129 /* The trailing byte is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */ 00130 upname[w++]=name_int[r++]; 00131 } 00132 } 00133 while (r>0 && name_int[r-1]==' ') r--; 00134 if (r>=DOS_PATHLENGTH) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; } 00135 upname[w]=0; 00136 00137 /* Now parse the new file name to make the final filename */ 00138 if (upname[0]!='\\') strcpy(fullname,Drives[*drive]->curdir); 00139 else fullname[0]=0; 00140 Bit32u lastdir=0;Bit32u t=0; 00141 while (fullname[t]!=0) { 00142 if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t; 00143 t++; 00144 } 00145 r=0;w=0; 00146 tempdir[0]=0; 00147 bool stop=false; 00148 while (!stop) { 00149 if (upname[r]==0) stop=true; 00150 if ((upname[r]=='\\') || (upname[r]==0)){ 00151 tempdir[w]=0; 00152 if (tempdir[0]==0) { w=0;r++;continue;} 00153 if (strcmp(tempdir,".")==0) { 00154 tempdir[0]=0; 00155 w=0;r++; 00156 continue; 00157 } 00158 00159 Bit32s iDown; 00160 bool dots = true; 00161 Bit32s templen=(Bit32s)strlen(tempdir); 00162 for(iDown=0;(iDown < templen) && dots;iDown++) 00163 if(tempdir[iDown] != '.') 00164 dots = false; 00165 00166 // only dots? 00167 if (dots && (templen > 1)) { 00168 Bit32s cDots = templen - 1; 00169 for(iDown=(Bit32s)strlen(fullname)-1;iDown>=0;iDown--) { 00170 if(fullname[iDown]=='\\' || iDown==0) { 00171 lastdir = (Bit32u)iDown; 00172 cDots--; 00173 if(cDots==0) 00174 break; 00175 } 00176 } 00177 fullname[lastdir]=0; 00178 t=0;lastdir=0; 00179 while (fullname[t]!=0) { 00180 if ((fullname[t]=='\\') && (fullname[t+1]!=0)) lastdir=t; 00181 t++; 00182 } 00183 tempdir[0]=0; 00184 w=0;r++; 00185 continue; 00186 } 00187 00188 00189 lastdir=(Bit32u)strlen(fullname); 00190 00191 if (lastdir!=0) strcat(fullname,"\\"); 00192 if (!uselfn||force_sfn) { 00193 char * ext=strchr(tempdir,'.'); 00194 if (ext) { 00195 if(strchr(ext+1,'.')) { 00196 //another dot in the extension =>file not found 00197 //Or path not found depending on wether 00198 //we are still in dir check stage or file stage 00199 if(stop) 00200 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00201 else 00202 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00203 return false; 00204 } 00205 00206 ext[4] = 0; 00207 if((strlen(tempdir) - strlen(ext)) > 8) memmove(tempdir + 8, ext, 5); 00208 } else { 00209 tempdir[8] = 0; 00210 } 00211 } 00212 00213 if (strlen(fullname)+strlen(tempdir)>=DOS_PATHLENGTH) { 00214 DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; 00215 } 00216 00217 strcat(fullname,tempdir); 00218 tempdir[0]=0; 00219 w=0;r++; 00220 continue; 00221 } 00222 tempdir[w++]=upname[r++]; 00223 } 00224 return true; 00225 } 00226 00227 bool DOS_GetSFNPath(char const * const path,char * SFNPath,bool LFN) { 00228 char pdir[LFN_NAMELENGTH+4], *p; 00229 Bit8u drive;char fulldir[DOS_PATHLENGTH],LFNPath[CROSS_LEN]; 00230 char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH]; 00231 Bit32u size;Bit16u date;Bit16u time;Bit8u attr; 00232 if (!DOS_MakeName(path,fulldir,&drive)) return false; 00233 sprintf(SFNPath,"%c:\\",drive+'A'); 00234 strcpy(LFNPath,SFNPath); 00235 p = fulldir; 00236 if (*p==0) return true; 00237 RealPt save_dta=dos.dta(); 00238 dos.dta(dos.tables.tempdta); 00239 DOS_DTA dta(dos.dta()); 00240 int fbak=lfn_filefind_handle; 00241 for (char *s = strchr(p,'\\'); s != NULL; s = strchr(p,'\\')) { 00242 *s = 0; 00243 if (SFNPath[strlen(SFNPath)-1]=='\\') 00244 sprintf(pdir,"\"%s%s\"",SFNPath,p); 00245 else 00246 sprintf(pdir,"\"%s\\%s\"",SFNPath,p); 00247 if (!strrchr(p,'*') && !strrchr(p,'?')) { 00248 *s = '\\'; 00249 p = s + 1; 00250 lfn_filefind_handle=LFN_FILEFIND_INTERNAL; 00251 if (DOS_FindFirst(pdir,0xffff & DOS_ATTR_DIRECTORY & ~DOS_ATTR_VOLUME,false)) { 00252 lfn_filefind_handle=fbak; 00253 dta.GetResult(name,lname,size,date,time,attr); 00254 strcat(SFNPath,name); 00255 strcat(LFNPath,lname); 00256 strcat(SFNPath,"\\"); 00257 strcat(LFNPath,"\\"); 00258 } 00259 else { 00260 lfn_filefind_handle=fbak; 00261 dos.dta(save_dta); 00262 return false; 00263 } 00264 } else { 00265 strcat(SFNPath,p); 00266 strcat(LFNPath,p); 00267 strcat(SFNPath,"\\"); 00268 strcat(LFNPath,"\\"); 00269 *s = '\\'; 00270 p = s + 1; 00271 break; 00272 } 00273 } 00274 if (p != 0) { 00275 sprintf(pdir,"\"%s%s\"",SFNPath,p); 00276 lfn_filefind_handle=LFN_FILEFIND_INTERNAL; 00277 if (!strrchr(p,'*')&&!strrchr(p,'?')&&DOS_FindFirst(pdir,0xffff & ~DOS_ATTR_VOLUME,false)) { 00278 dta.GetResult(name,lname,size,date,time,attr); 00279 strcat(SFNPath,name); 00280 strcat(LFNPath,lname); 00281 } else { 00282 strcat(SFNPath,p); 00283 strcat(LFNPath,p); 00284 } 00285 lfn_filefind_handle=fbak; 00286 } 00287 dos.dta(save_dta); 00288 if (LFN) strcpy(SFNPath,LFNPath); 00289 return true; 00290 } 00291 00292 bool DOS_GetCurrentDir(Bit8u drive,char * const buffer, bool LFN) { 00293 if (drive==0) drive=DOS_GetDefaultDrive(); 00294 else drive--; 00295 00296 if ((drive>=DOS_DRIVES) || (!Drives[drive])) { 00297 DOS_SetError(DOSERR_INVALID_DRIVE); 00298 return false; 00299 } 00300 00301 if (LFN && uselfn) { 00302 char cdir[DOS_PATHLENGTH+8],ldir[DOS_PATHLENGTH]; 00303 00304 if (strchr(Drives[drive]->curdir,' ')) 00305 sprintf(cdir,"\"%c:\\%s\"",drive+'A',Drives[drive]->curdir); 00306 else 00307 sprintf(cdir,"%c:\\%s",drive+'A',Drives[drive]->curdir); 00308 00309 if (!DOS_GetSFNPath(cdir,ldir,true)) 00310 return false; 00311 00312 strcpy(buffer,ldir+3); 00313 if (DOS_GetSFNPath(cdir,ldir,false)) 00314 strcpy(Drives[drive]->curdir,ldir+3); 00315 } else { 00316 strcpy(buffer,Drives[drive]->curdir); 00317 } 00318 00319 return true; 00320 } 00321 00322 bool DOS_ChangeDir(char const * const dir) { 00323 Bit8u drive;char fulldir[DOS_PATHLENGTH]; 00324 const char * testdir=dir; 00325 if (strlen(testdir) && testdir[1]==':') testdir+=2; 00326 size_t len=strlen(testdir); 00327 if (!len) { 00328 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00329 return false; 00330 } 00331 if (!DOS_MakeName(dir,fulldir,&drive)) return false; 00332 if (strlen(fulldir) && testdir[len-1]=='\\') { 00333 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00334 return false; 00335 } 00336 00337 if (Drives[drive]->TestDir(fulldir)) { 00338 strcpy(Drives[drive]->curdir,fulldir); 00339 return true; 00340 } else { 00341 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00342 } 00343 return false; 00344 } 00345 00346 bool DOS_MakeDir(char const * const dir) { 00347 Bit8u drive;char fulldir[DOS_PATHLENGTH]; 00348 size_t len = strlen(dir); 00349 if(!len || dir[len-1] == '\\') { 00350 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00351 return false; 00352 } 00353 if (!DOS_MakeName(dir,fulldir,&drive)) return false; 00354 while (strlen(fulldir)&&(*(fulldir+strlen(fulldir)-1)=='.'||*(fulldir+strlen(fulldir)-1)==' ')) *(fulldir+strlen(fulldir)-1)=0; 00355 if(Drives[drive]->MakeDir(fulldir)) return true; 00356 00357 /* Determine reason for failing */ 00358 if(Drives[drive]->TestDir(fulldir)) 00359 DOS_SetError(DOSERR_ACCESS_DENIED); 00360 else 00361 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00362 return false; 00363 } 00364 00365 bool DOS_RemoveDir(char const * const dir) { 00366 /* We need to do the test before the removal as can not rely on 00367 * the host to forbid removal of the current directory. 00368 * We never change directory. Everything happens in the drives. 00369 */ 00370 Bit8u drive;char fulldir[DOS_PATHLENGTH]; 00371 if (!DOS_MakeName(dir,fulldir,&drive)) return false; 00372 /* Check if exists */ 00373 if(!Drives[drive]->TestDir(fulldir)) { 00374 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00375 return false; 00376 } 00377 /* See if it's current directory */ 00378 char currdir[DOS_PATHLENGTH]= { 0 }, lcurrdir[DOS_PATHLENGTH]= { 0 }; 00379 DOS_GetCurrentDir(drive + 1 ,currdir, false); 00380 DOS_GetCurrentDir(drive + 1 ,lcurrdir, true); 00381 if(strcasecmp(currdir,fulldir) == 0 || (uselfn && strcasecmp(lcurrdir,fulldir) == 0)) { 00382 DOS_SetError(DOSERR_REMOVE_CURRENT_DIRECTORY); 00383 return false; 00384 } 00385 00386 if(Drives[drive]->RemoveDir(fulldir)) return true; 00387 00388 /* Failed. We know it exists and it's not the current dir */ 00389 /* Assume non empty */ 00390 DOS_SetError(DOSERR_ACCESS_DENIED); 00391 return false; 00392 } 00393 00394 bool DOS_Rename(char const * const oldname,char const * const newname) { 00395 Bit8u driveold;char fullold[DOS_PATHLENGTH]; 00396 Bit8u drivenew;char fullnew[DOS_PATHLENGTH]; 00397 if (!DOS_MakeName(oldname,fullold,&driveold)) return false; 00398 if (!DOS_MakeName(newname,fullnew,&drivenew)) return false; 00399 while (strlen(fullnew)&&(*(fullnew+strlen(fullnew)-1)=='.'||*(fullnew+strlen(fullnew)-1)==' ')) *(fullnew+strlen(fullnew)-1)=0; 00400 00401 /* No tricks with devices */ 00402 bool clip=false; 00403 if ( (DOS_FindDevice(oldname) != DOS_DEVICES) || 00404 (DOS_FindDevice(newname) != DOS_DEVICES) ) { 00405 #if defined (WIN32) 00406 if (!control->SecureMode()&&(dos_clipboard_device_access==3||dos_clipboard_device_access==4)) { 00407 if (DOS_FindDevice(oldname) == DOS_DEVICES) { 00408 const char* find_last; 00409 find_last=strrchr(fullnew,'\\'); 00410 if (find_last==NULL) find_last=fullnew; 00411 else find_last++; 00412 if (!strcasecmp(find_last, *dos_clipboard_device_name?dos_clipboard_device_name:"CLIP$")) 00413 clip=true; 00414 } 00415 } 00416 #endif 00417 if (!clip) { 00418 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00419 return false; 00420 } 00421 } 00422 /* Must be on the same drive */ 00423 if(driveold != drivenew) { 00424 DOS_SetError(DOSERR_NOT_SAME_DEVICE); 00425 return false; 00426 } 00427 Bit16u attr; 00428 /* Source must exist, check for path ? */ 00429 if (!Drives[driveold]->GetFileAttr( fullold, &attr ) ) { 00430 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00431 return false; 00432 } 00433 /*Test if target exists => no access */ 00434 if(Drives[drivenew]->GetFileAttr(fullnew,&attr)&&!(uselfn&&!force_sfn&&strcmp(fullold, fullnew)&&!strcasecmp(fullold, fullnew))) { 00435 DOS_SetError(DOSERR_ACCESS_DENIED); 00436 return false; 00437 } 00438 00439 #if defined (WIN32) 00440 if (clip) { 00441 Bit16u sourceHandle, targetHandle, toread = 0x8000; 00442 static Bit8u buffer[0x8000]; 00443 bool failed = false; 00444 if (DOS_OpenFile(oldname,OPEN_READ,&sourceHandle) && DOS_OpenFile(newname,OPEN_WRITE,&targetHandle)) { 00445 do { 00446 if (!DOS_ReadFile(sourceHandle,buffer,&toread) || !DOS_WriteFile(targetHandle,buffer,&toread)) failed=true; 00447 } while (toread == 0x8000); 00448 if (!DOS_CloseFile(sourceHandle)||!DOS_CloseFile(targetHandle)) failed=true; 00449 } else failed=true; 00450 if (!failed&&Drives[drivenew]->FileUnlink(fullold)) return true; 00451 } else 00452 #endif 00453 if (Drives[drivenew]->Rename(fullold,fullnew)) return true; 00454 /* If it still fails, which error should we give ? PATH NOT FOUND or EACCESS */ 00455 if (dos.errorcode!=DOSERR_ACCESS_DENIED&&dos.errorcode!=DOSERR_WRITE_PROTECTED) { 00456 LOG(LOG_FILES,LOG_NORMAL)("Rename fails for %s to %s, no proper errorcode returned.",oldname,newname); 00457 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00458 } 00459 return false; 00460 } 00461 00462 bool DOS_FindFirst(char * search,Bit16u attr,bool fcb_findfirst) { 00463 LOG(LOG_FILES,LOG_NORMAL)("file search attributes %X name %s",attr,search); 00464 DOS_DTA dta(dos.dta()); 00465 Bit8u drive;char fullsearch[DOS_PATHLENGTH]; 00466 char dir[DOS_PATHLENGTH];char pattern[DOS_PATHLENGTH]; 00467 size_t len = strlen(search); 00468 if(len && search[len - 1] == '\\' && !( (len > 2) && (search[len - 2] == ':') && (attr == DOS_ATTR_VOLUME) )) { 00469 //Dark Forces installer, but c:\ is allright for volume labels(exclusively set) 00470 DOS_SetError(DOSERR_NO_MORE_FILES); 00471 return false; 00472 } 00473 if (!DOS_MakeName(search,fullsearch,&drive)) return false; 00474 //Check for devices. FindDevice checks for leading subdir as well 00475 bool device = (DOS_FindDevice(search) != DOS_DEVICES); 00476 00477 /* Split the search in dir and pattern */ 00478 char * find_last; 00479 find_last=strrchr(fullsearch,'\\'); 00480 if (!find_last) { /*No dir */ 00481 strcpy(pattern,fullsearch); 00482 dir[0]=0; 00483 } else { 00484 *find_last=0; 00485 strcpy(pattern,find_last+1); 00486 strcpy(dir,fullsearch); 00487 } 00488 00489 sdrive=drive; 00490 dta.SetupSearch(drive,(Bit8u)attr,pattern); 00491 00492 if(device) { 00493 find_last = strrchr(pattern,'.'); 00494 if(find_last) *find_last = 0; 00495 //TODO use current date and time 00496 dta.SetResult(pattern,pattern,0,0,0,DOS_ATTR_DEVICE); 00497 LOG(LOG_DOSMISC,LOG_WARN)("finding device %s",pattern); 00498 return true; 00499 } 00500 00501 if (Drives[drive]->FindFirst(dir,dta,fcb_findfirst)) return true; 00502 00503 return false; 00504 } 00505 00506 bool DOS_FindNext(void) { 00507 DOS_DTA dta(dos.dta()); 00508 Bit8u i = dta.GetSearchDrive(); 00509 if(uselfn && (i >= DOS_DRIVES || !Drives[i])) i=sdrive; 00510 if(i >= DOS_DRIVES || !Drives[i]) { 00511 /* Corrupt search. */ 00512 LOG(LOG_FILES,LOG_ERROR)("Corrupt search!!!!"); 00513 DOS_SetError(DOSERR_NO_MORE_FILES); 00514 return false; 00515 } 00516 if (Drives[i]->FindNext(dta)) return true; 00517 return false; 00518 } 00519 00520 00521 bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) { 00522 #if defined(WIN32) && !defined(__MINGW32__) 00523 if(Network_IsActiveResource(entry)) 00524 return Network_ReadFile(entry,data,amount); 00525 #endif 00526 Bit32u handle = fcb?entry:RealHandle(entry); 00527 if (handle>=DOS_FILES) { 00528 DOS_SetError(DOSERR_INVALID_HANDLE); 00529 return false; 00530 } 00531 if (!Files[handle] || !Files[handle]->IsOpen()) { 00532 DOS_SetError(DOSERR_INVALID_HANDLE); 00533 return false; 00534 } 00535 00536 if (log_fileio) { 00537 LOG(LOG_FILES, LOG_DEBUG)("Reading %d bytes from %s ", *amount, Files[handle]->name); 00538 } 00539 /* 00540 if ((Files[handle]->flags & 0x0f) == OPEN_WRITE)) { 00541 DOS_SetError(DOSERR_INVALID_HANDLE); 00542 return false; 00543 } 00544 */ 00545 Bit16u toread=*amount; 00546 bool ret=Files[handle]->Read(data,&toread); 00547 *amount=toread; 00548 return ret; 00549 } 00550 00551 bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount,bool fcb) { 00552 #if defined(WIN32) && !defined(__MINGW32__) 00553 if(Network_IsActiveResource(entry)) 00554 return Network_WriteFile(entry,data,amount); 00555 #endif 00556 Bit32u handle = fcb?entry:RealHandle(entry); 00557 if (handle>=DOS_FILES) { 00558 DOS_SetError(DOSERR_INVALID_HANDLE); 00559 return false; 00560 } 00561 if (!Files[handle] || !Files[handle]->IsOpen()) { 00562 DOS_SetError(DOSERR_INVALID_HANDLE); 00563 return false; 00564 } 00565 00566 if (log_fileio) { 00567 LOG(LOG_FILES, LOG_DEBUG)("Writing %d bytes to %s", *amount, Files[handle]->name); 00568 } 00569 /* 00570 if ((Files[handle]->flags & 0x0f) == OPEN_READ)) { 00571 DOS_SetError(DOSERR_INVALID_HANDLE); 00572 return false; 00573 } 00574 */ 00575 Bit16u towrite=*amount; 00576 bool ret=Files[handle]->Write(data,&towrite); 00577 *amount=towrite; 00578 return ret; 00579 } 00580 00581 bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type,bool fcb) { 00582 Bit32u handle = fcb?entry:RealHandle(entry); 00583 if (handle>=DOS_FILES) { 00584 DOS_SetError(DOSERR_INVALID_HANDLE); 00585 return false; 00586 } 00587 if (!Files[handle] || !Files[handle]->IsOpen()) { 00588 DOS_SetError(DOSERR_INVALID_HANDLE); 00589 return false; 00590 } 00591 return Files[handle]->Seek(pos,type); 00592 } 00593 00594 /* ert, 20100711: Locking extensions */ 00595 bool DOS_LockFile(Bit16u entry,Bit8u mode,Bit32u pos,Bit32u size) { 00596 Bit32u handle=RealHandle(entry); 00597 if (handle>=DOS_FILES) { 00598 DOS_SetError(DOSERR_INVALID_HANDLE); 00599 return false; 00600 } 00601 if (!Files[handle] || !Files[handle]->IsOpen()) { 00602 DOS_SetError(DOSERR_INVALID_HANDLE); 00603 return false; 00604 } 00605 #ifdef WIN32 00606 return Files[handle]->LockFile(mode,pos,size); 00607 #else 00608 (void)mode;//UNUSED 00609 (void)size;//UNUSED 00610 (void)pos;//UNUSED 00611 return true; 00612 #endif 00613 } 00614 00615 bool DOS_CloseFile(Bit16u entry, bool fcb) { 00616 #if defined(WIN32) && !defined(__MINGW32__) 00617 if(Network_IsActiveResource(entry)) 00618 return Network_CloseFile(entry); 00619 #endif 00620 Bit32u handle = fcb?entry:RealHandle(entry); 00621 if (handle>=DOS_FILES) { 00622 DOS_SetError(DOSERR_INVALID_HANDLE); 00623 return false; 00624 } 00625 if (!Files[handle]) { 00626 DOS_SetError(DOSERR_INVALID_HANDLE); 00627 return false; 00628 } 00629 if (Files[handle]->IsOpen()) { 00630 if (log_fileio) { 00631 LOG(LOG_FILES, LOG_NORMAL)("Closing file %s", Files[handle]->name); 00632 } 00633 Files[handle]->Close(); 00634 } 00635 00636 DOS_PSP psp(dos.psp()); 00637 if (!fcb) psp.SetFileHandle(entry,0xff); 00638 00639 if (Files[handle]->RemoveRef()<=0) { 00640 delete Files[handle]; 00641 Files[handle]=0; 00642 } 00643 return true; 00644 } 00645 00646 bool DOS_FlushFile(Bit16u entry) { 00647 Bit32u handle=RealHandle(entry); 00648 if (handle>=DOS_FILES) { 00649 DOS_SetError(DOSERR_INVALID_HANDLE); 00650 return false; 00651 } 00652 if (!Files[handle] || !Files[handle]->IsOpen()) { 00653 DOS_SetError(DOSERR_INVALID_HANDLE); 00654 return false; 00655 } 00656 00657 LOG(LOG_DOSMISC,LOG_DEBUG)("FFlush used."); 00658 00659 Files[handle]->Flush(); 00660 return true; 00661 } 00662 00663 static bool PathExists(char const * const name) { 00664 const char* leading = strrchr(name,'\\'); 00665 if(!leading) return true; 00666 char temp[CROSS_LEN]; 00667 strcpy(temp,name); 00668 char * lead = strrchr(temp,'\\'); 00669 if (lead == temp) return true; 00670 *lead = 0; 00671 Bit8u drive;char fulldir[DOS_PATHLENGTH]; 00672 if (!DOS_MakeName(temp,fulldir,&drive)) return false; 00673 if(!Drives[drive]->TestDir(fulldir)) return false; 00674 return true; 00675 } 00676 00677 00678 bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry,bool fcb) { 00679 // Creation of a device is the same as opening it 00680 // Tc201 installer 00681 if (DOS_FindDevice(name) != DOS_DEVICES) 00682 return DOS_OpenFile(name, OPEN_READ, entry, fcb); 00683 00684 LOG(LOG_FILES,LOG_NORMAL)("file create attributes %X file %s",attributes,name); 00685 char fullname[DOS_PATHLENGTH];Bit8u drive; 00686 DOS_PSP psp(dos.psp()); 00687 if (!DOS_MakeName(name,fullname,&drive)) return false; 00688 while (strlen(fullname)&&(*(fullname+strlen(fullname)-1)=='.'||*(fullname+strlen(fullname)-1)==' ')) *(fullname+strlen(fullname)-1)=0; 00689 00690 /* Check for a free file handle */ 00691 Bit8u handle=(Bit8u)DOS_FILES;Bit8u i; 00692 for (i=0;i<DOS_FILES;i++) { 00693 if (!Files[i]) { 00694 handle=i; 00695 break; 00696 } 00697 } 00698 if (handle==DOS_FILES) { 00699 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); 00700 return false; 00701 } 00702 /* We have a position in the main table now find one in the psp table */ 00703 *entry = fcb?handle:psp.FindFreeFileEntry(); 00704 if (*entry==0xff) { 00705 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); 00706 return false; 00707 } 00708 /* Don't allow directories to be created */ 00709 if (attributes&DOS_ATTR_DIRECTORY) { 00710 DOS_SetError(DOSERR_ACCESS_DENIED); 00711 return false; 00712 } 00713 bool foundit=Drives[drive]->FileCreate(&Files[handle],fullname,attributes); 00714 if (foundit) { 00715 if (Files[handle]) { 00716 Files[handle]->SetDrive(drive); 00717 Files[handle]->AddRef(); 00718 Files[handle]->drive = drive; 00719 } 00720 if (!fcb) psp.SetFileHandle(*entry,handle); 00721 if (Files[handle]) Drives[drive]->EmptyCache(); 00722 return true; 00723 } else { 00724 if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); 00725 else DOS_SetError(DOSERR_FILE_NOT_FOUND); 00726 return false; 00727 } 00728 } 00729 00730 bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb) { 00731 #if defined(WIN32) && !defined(__MINGW32__) 00732 if(Network_IsNetworkResource(const_cast<char *>(name))) 00733 return Network_OpenFile(const_cast<char *>(name),flags,entry); 00734 #endif 00735 /* First check for devices */ 00736 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? 00737 else LOG(LOG_FILES,LOG_NORMAL)("file open command %X file %s",flags,name); 00738 00739 DOS_PSP psp(dos.psp()); 00740 Bit16u attr = 0; 00741 Bit8u devnum = DOS_FindDevice(name); 00742 bool device = (devnum != DOS_DEVICES); 00743 if(!device && DOS_GetFileAttr(name,&attr)) { 00744 //DON'T ALLOW directories to be openened.(skip test if file is device). 00745 if((attr & DOS_ATTR_DIRECTORY) || (attr & DOS_ATTR_VOLUME)){ 00746 DOS_SetError(DOSERR_ACCESS_DENIED); 00747 return false; 00748 } 00749 } 00750 00751 char fullname[DOS_PATHLENGTH];Bit8u drive;Bit8u i; 00752 /* First check if the name is correct */ 00753 if (!DOS_MakeName(name,fullname,&drive)) return false; 00754 Bit8u handle=255; 00755 /* Check for a free file handle */ 00756 for (i=0;i<DOS_FILES;i++) { 00757 if (!Files[i]) { 00758 handle=i; 00759 break; 00760 } 00761 } 00762 if (handle==255) { 00763 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); 00764 return false; 00765 } 00766 /* We have a position in the main table now find one in the psp table */ 00767 *entry = fcb?handle:psp.FindFreeFileEntry(); 00768 00769 if (*entry==0xff) { 00770 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); 00771 return false; 00772 } 00773 bool exists=false; 00774 if (device) { 00775 Files[handle]=new DOS_Device(*Devices[devnum]); 00776 } else { 00777 exists=Drives[drive]->FileOpen(&Files[handle],fullname,flags) || Drives[drive]->FileOpen(&Files[handle],upcase(fullname),flags); 00778 if (exists) Files[handle]->SetDrive(drive); 00779 } 00780 if (exists || device ) { 00781 Files[handle]->AddRef(); 00782 psp.SetFileHandle(*entry,handle); 00783 Files[handle]->drive = drive; 00784 return true; 00785 } else { 00786 //Test if file exists, but opened in read-write mode (and writeprotected) 00787 if(((flags&3) != OPEN_READ) && Drives[drive]->FileExists(fullname)) 00788 DOS_SetError(DOSERR_ACCESS_DENIED); 00789 else { 00790 if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); 00791 else DOS_SetError(DOSERR_FILE_NOT_FOUND); 00792 } 00793 return false; 00794 } 00795 } 00796 00797 bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status) { 00798 // FIXME: Not yet supported : Bit 13 of flags (int 0x24 on critical error) 00799 Bit16u result = 0; 00800 if (action==0) { 00801 // always fail setting 00802 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 00803 return false; 00804 } else { 00805 if (((action & 0x0f)>2) || ((action & 0xf0)>0x10)) { 00806 // invalid action parameter 00807 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 00808 return false; 00809 } 00810 } 00811 if (DOS_OpenFile(name, (Bit8u)(flags&0xff), entry)) { 00812 // File already exists 00813 switch (action & 0x0f) { 00814 case 0x00: // failed 00815 DOS_SetError(DOSERR_FILE_ALREADY_EXISTS); 00816 return false; 00817 case 0x01: // file open (already done) 00818 result = 1; 00819 break; 00820 case 0x02: // replace 00821 DOS_CloseFile(*entry); 00822 if (!DOS_CreateFile(name, createAttr, entry)) return false; 00823 result = 3; 00824 break; 00825 default: 00826 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 00827 E_Exit("DOS: OpenFileExtended: Unknown action."); 00828 break; 00829 } 00830 } else { 00831 // File doesn't exist 00832 if ((action & 0xf0)==0) { 00833 // uses error code from failed open 00834 return false; 00835 } 00836 // Create File 00837 if (!DOS_CreateFile(name, createAttr, entry)) { 00838 // uses error code from failed create 00839 return false; 00840 } 00841 result = 2; 00842 } 00843 // success 00844 *status = result; 00845 return true; 00846 } 00847 00848 bool DOS_UnlinkFile(char const * const name) { 00849 char fullname[DOS_PATHLENGTH];Bit8u drive; 00850 // An existing device returns an access denied error 00851 if (log_fileio) { 00852 LOG(LOG_FILES, LOG_NORMAL)("Deleting file %s", name); 00853 } 00854 if (DOS_FindDevice(name) != DOS_DEVICES) { 00855 DOS_SetError(DOSERR_ACCESS_DENIED); 00856 return false; 00857 } 00858 if (!DOS_MakeName(name,fullname,&drive)) return false; 00859 if(Drives[drive]->FileUnlink(fullname)){ 00860 return true; 00861 } else if(uselfn&&!force_sfn&&(strchr(fullname, '*')||strchr(fullname, '?'))) { // Wildcard delete as used by MS-DOS 7+ "DEL *.*" in LFN mode 00862 char dir[DOS_PATHLENGTH], temp[DOS_PATHLENGTH], fname[DOS_PATHLENGTH], spath[DOS_PATHLENGTH], pattern[DOS_PATHLENGTH]; 00863 if (!DOS_Canonicalize(name,fullname)||!strlen(fullname)) { 00864 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00865 return false; 00866 } 00867 if (!strchr(name,'\"')||!DOS_GetSFNPath(("\""+std::string(fullname)+"\"").c_str(), fname, false)) 00868 strcpy(fname, fullname); 00869 char * find_last=strrchr(fname,'\\'); 00870 if (!find_last) { 00871 dir[0]=0; 00872 strcpy(pattern, fname); 00873 } else { 00874 *find_last=0; 00875 strcpy(dir,fname); 00876 strcpy(pattern, find_last+1); 00877 } 00878 int k=0; 00879 for (int i=0;i<(int)strlen(pattern);i++) 00880 if (pattern[i]!='\"') 00881 pattern[k++]=pattern[i]; 00882 pattern[k]=0; 00883 RealPt save_dta=dos.dta(); 00884 dos.dta(dos.tables.tempdta); 00885 DOS_DTA dta(dos.dta()); 00886 std::vector<std::string> cdirs; 00887 cdirs.clear(); 00888 strcpy(spath, dir); 00889 if (!DOS_GetSFNPath(dir, spath, false)) strcpy(spath, dir); 00890 if (!strlen(spath)||spath[strlen(spath)-1]!='\\') strcat(spath, "\\"); 00891 std::string pfull=std::string(spath)+std::string(pattern); 00892 int fbak=lfn_filefind_handle; 00893 lfn_filefind_handle=LFN_FILEFIND_INTERNAL; 00894 bool ret=DOS_FindFirst((char *)((pfull.length()&&pfull[0]=='"'?"":"\"")+pfull+(pfull.length()&&pfull[pfull.length()-1]=='"'?"":"\"")).c_str(),0xffu & ~DOS_ATTR_VOLUME & ~DOS_ATTR_DIRECTORY); 00895 if (ret) do { 00896 char find_name[DOS_NAMELENGTH_ASCII],lfind_name[LFN_NAMELENGTH]; 00897 Bit16u find_date,find_time;Bit32u find_size;Bit8u find_attr; 00898 dta.GetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr); 00899 if (!(find_attr & DOS_ATTR_DIRECTORY)&&strlen(find_name)&&!strchr(find_name, '*')&&!strchr(find_name, '?')) { 00900 strcpy(temp, dir); 00901 if (strlen(temp)&&temp[strlen(temp)-1]!='\\') strcat(temp, "\\"); 00902 strcat(temp, find_name); 00903 cdirs.push_back(std::string(temp)); 00904 } 00905 } while ((ret=DOS_FindNext())==true); 00906 lfn_filefind_handle=fbak; 00907 bool removed=false; 00908 while (!cdirs.empty()) { 00909 if (DOS_UnlinkFile(cdirs.begin()->c_str())) 00910 removed=true; 00911 cdirs.erase(cdirs.begin()); 00912 } 00913 dos.dta(save_dta); 00914 if (removed) 00915 return true; 00916 else { 00917 if (dos.errorcode!=DOSERR_ACCESS_DENIED&&dos.errorcode!=DOSERR_WRITE_PROTECTED) DOS_SetError(DOSERR_FILE_NOT_FOUND); 00918 return false; 00919 } 00920 } else { 00921 if (dos.errorcode!=DOSERR_ACCESS_DENIED&&dos.errorcode!=DOSERR_WRITE_PROTECTED) DOS_SetError(DOSERR_FILE_NOT_FOUND); 00922 return false; 00923 } 00924 } 00925 00926 bool DOS_GetFileAttr(char const * const name,Bit16u * attr) { 00927 char fullname[DOS_PATHLENGTH];Bit8u drive; 00928 if (!DOS_MakeName(name,fullname,&drive)) return false; 00929 #if defined (WIN32) 00930 if (!control->SecureMode()&&dos_clipboard_device_access) { 00931 const char* find_last; 00932 find_last=strrchr(fullname,'\\'); 00933 if (find_last==NULL) find_last=fullname; 00934 else find_last++; 00935 if (!strcasecmp(find_last, *dos_clipboard_device_name?dos_clipboard_device_name:"CLIP$")) 00936 return true; 00937 } 00938 #endif 00939 if (Drives[drive]->GetFileAttr(fullname,attr)) { 00940 return true; 00941 } else { 00942 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00943 return false; 00944 } 00945 } 00946 00947 bool DOS_GetFileAttrEx(char const* const name, struct stat *status, Bit8u hdrive) 00948 { 00949 char fullname[DOS_PATHLENGTH]; 00950 Bit8u drive; 00951 bool usehdrive=/*hdrive>=0&&(always true)*/hdrive<DOS_FILES; 00952 if (usehdrive) 00953 strcpy(fullname,name); 00954 else if (!DOS_MakeName(name, fullname, &drive)) 00955 return false; 00956 return Drives[usehdrive?hdrive:drive]->GetFileAttrEx(fullname, status); 00957 } 00958 00959 unsigned long DOS_GetCompressedFileSize(char const* const name) 00960 { 00961 char fullname[DOS_PATHLENGTH]; 00962 Bit8u drive; 00963 if (!DOS_MakeName(name, fullname, &drive)) 00964 return false; 00965 return Drives[drive]->GetCompressedSize(fullname); 00966 } 00967 00968 #if defined (WIN32) 00969 HANDLE DOS_CreateOpenFile(char const* const name) 00970 { 00971 char fullname[DOS_PATHLENGTH]; 00972 Bit8u drive; 00973 if (!DOS_MakeName(name, fullname, &drive)) 00974 return INVALID_HANDLE_VALUE; 00975 return Drives[drive]->CreateOpenFile(fullname); 00976 } 00977 #endif 00978 00979 bool DOS_SetFileAttr(char const * const name,Bit16u attr) 00980 // returns false when using on cdrom (stonekeep) 00981 { 00982 char fullname[DOS_PATHLENGTH];Bit8u drive; 00983 if (!DOS_MakeName(name,fullname,&drive)) return false; 00984 if (strncmp(Drives[drive]->GetInfo(),"CDRom ",6)==0 || strncmp(Drives[drive]->GetInfo(),"isoDrive ",9)==0) { 00985 DOS_SetError(DOSERR_ACCESS_DENIED); 00986 return false; 00987 } 00988 00989 /* This function must prevent changing a file into a directory, volume label into a file, etc. 00990 * Also Windows 95 setup likes to create WINBOOT.INI as a file and then chattr it into a directory for some stupid reason. */ 00991 Bit16u old_attr; 00992 if (!Drives[drive]->GetFileAttr(fullname,&old_attr)) { 00993 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00994 return false; 00995 } 00996 00997 if ((old_attr ^ attr) & DOS_ATTR_VOLUME) { /* change in volume label attribute */ 00998 LOG(LOG_DOSMISC,LOG_WARN)("Attempted to change volume label attribute of '%s' with SetFileAttr",name); 00999 return false; 01000 } 01001 01002 if ((old_attr ^ attr) & DOS_ATTR_DIRECTORY) /* change in directory attribute (ex: Windows 95 SETUP.EXE vs WINBOOT.INI) */ 01003 LOG(LOG_DOSMISC,LOG_WARN)("Attempted to change directory attribute of '%s' with SetFileAttr",name); 01004 01005 /* define what cannot be changed */ 01006 const Bit16u attr_mask = (DOS_ATTR_VOLUME|DOS_ATTR_DIRECTORY); 01007 01008 attr = (attr & ~attr_mask) | (old_attr & attr_mask); 01009 01010 return Drives[drive]->SetFileAttr(fullname,attr); 01011 } 01012 01013 bool DOS_Canonicalize(char const * const name,char * const big) { 01014 //TODO Add Better support for devices and shit but will it be needed i doubt it :) 01015 Bit8u drive; 01016 char fullname[DOS_PATHLENGTH]; 01017 if (!DOS_MakeName(name,fullname,&drive)) return false; 01018 big[0]=drive+'A'; 01019 big[1]=':'; 01020 big[2]='\\'; 01021 strcpy(&big[3],fullname); 01022 return true; 01023 } 01024 01025 #ifdef _MSC_VER 01026 # define MIN(a,b) ((a) < (b) ? (a) : (b)) 01027 # define MAX(a,b) ((a) > (b) ? (a) : (b)) 01028 #else 01029 # define MIN(a,b) std::min(a,b) 01030 # define MAX(a,b) std::max(a,b) 01031 #endif 01032 01033 /* Common routine to take larger allocation information (such as FAT32) and convert it to values 01034 * that are suitable for use with older DOS programs that pre-date FAT32 and partitions 2GB or larger. 01035 * This is what Windows 95 OSR2 and higher do with FAT32 partitions anyway, as documented by Microsoft. */ 01036 bool DOS_CommonFAT32FAT16DiskSpaceConv( 01037 Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free, 01038 const Bit32u bytes32,const Bit32u sectors32,const Bit32u clusters32,const Bit32u free32) { 01039 Bit32u cdiv = 1; 01040 01041 if (sectors32 > 128 || bytes32 > 0x8000) 01042 return false; 01043 01044 /* This function is for the old API. It is necessary to adjust the values so that they never overflow 01045 * 16-bit unsigned integers and never multiply out to a number greater than just under 2GB. Because 01046 * old DOS programs use 32-bit signed integers for disk total/free and FAT12/FAT16 filesystem limitations. */ 01047 /* NTS: Make sure (bytes per sector * sectors per cluster) is less than 0x10000, or else FORMAT.COM will 01048 * crash with divide by zero or produce incorrect results when run with "FORMAT /S" */ 01049 while ((clusters32 > 0xFFFFu || free32 > 0xFFFFu) && (sectors32 * cdiv) <= 64u && (bytes32 * sectors32 * cdiv) < 0x8000/*Needed for FORMAT.COM*/) 01050 cdiv *= 2u; 01051 01052 /* The old API must never report more than just under 2GB for total and free */ 01053 const Bit32u clust2gb = (Bit32u)0x7FFF8000ul / (Bit32u)bytes32 / (sectors32 * cdiv); 01054 01055 *bytes = bytes32; 01056 *sectors = sectors32 * cdiv; 01057 *clusters = (Bit16u)MIN(MIN(clusters32 / cdiv,clust2gb),0xFFFFu); 01058 *free = (Bit16u)MIN(MIN(free32 / cdiv,clust2gb),0xFFFFu); 01059 return true; 01060 } 01061 01062 bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free) { 01063 if (drive==0) drive=DOS_GetDefaultDrive(); 01064 else drive--; 01065 if ((drive>=DOS_DRIVES) || (!Drives[drive])) { 01066 DOS_SetError(DOSERR_INVALID_DRIVE); 01067 return false; 01068 } 01069 01070 { 01071 Bit32u bytes32,sectors32,clusters32,free32; 01072 if ((dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) && 01073 Drives[drive]->AllocationInfo32(&bytes32,§ors32,&clusters32,&free32) && 01074 DOS_CommonFAT32FAT16DiskSpaceConv(bytes,sectors,clusters,free,bytes32,sectors32,clusters32,free32)) 01075 return true; 01076 } 01077 01078 if (Drives[drive]->AllocationInfo(bytes,sectors,clusters,free)) 01079 return true; 01080 01081 return false; 01082 } 01083 01084 bool DOS_GetFreeDiskSpace32(Bit8u drive,Bit32u * bytes,Bit32u * sectors,Bit32u * clusters,Bit32u * free) { 01085 if (drive==0) drive=DOS_GetDefaultDrive(); 01086 else drive--; 01087 if ((drive>=DOS_DRIVES) || (!Drives[drive])) { 01088 DOS_SetError(DOSERR_INVALID_DRIVE); 01089 return false; 01090 } 01091 01092 if ((dos.version.major > 7 || (dos.version.major == 7 && dos.version.minor >= 10)) && Drives[drive]->AllocationInfo32(bytes,sectors,clusters,free)) 01093 return true; 01094 01095 { 01096 Bit8u sectors8; 01097 Bit16u bytes16,clusters16,free16; 01098 if (Drives[drive]->AllocationInfo(&bytes16,§ors8,&clusters16,&free16)) { 01099 *free = free16; 01100 *bytes = bytes16; 01101 *sectors = sectors8; 01102 *clusters = clusters16; 01103 return true; 01104 } 01105 } 01106 01107 return false; 01108 } 01109 01110 bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry) { 01111 // Dont duplicate console handles 01112 /* if (entry<=STDPRN) { 01113 *newentry = entry; 01114 return true; 01115 }; 01116 */ 01117 Bit8u handle=RealHandle(entry); 01118 if (handle>=DOS_FILES) { 01119 DOS_SetError(DOSERR_INVALID_HANDLE); 01120 return false; 01121 } 01122 if (!Files[handle] || !Files[handle]->IsOpen()) { 01123 DOS_SetError(DOSERR_INVALID_HANDLE); 01124 return false; 01125 } 01126 DOS_PSP psp(dos.psp()); 01127 *newentry = psp.FindFreeFileEntry(); 01128 if (*newentry==0xff) { 01129 DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); 01130 return false; 01131 } 01132 Files[handle]->AddRef(); 01133 psp.SetFileHandle(*newentry,handle); 01134 return true; 01135 } 01136 01137 bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry) { 01138 if(entry == newentry) { 01139 DOS_SetError(DOSERR_INVALID_HANDLE); 01140 return false; 01141 } 01142 Bit8u orig = RealHandle(entry); 01143 if (orig >= DOS_FILES) { 01144 DOS_SetError(DOSERR_INVALID_HANDLE); 01145 return false; 01146 } 01147 if (!Files[orig] || !Files[orig]->IsOpen()) { 01148 DOS_SetError(DOSERR_INVALID_HANDLE); 01149 return false; 01150 } 01151 Bit8u newone = RealHandle(newentry); 01152 if (newone < DOS_FILES && Files[newone]) { 01153 DOS_CloseFile(newentry); 01154 } 01155 DOS_PSP psp(dos.psp()); 01156 Files[orig]->AddRef(); 01157 psp.SetFileHandle(newentry,orig); 01158 return true; 01159 } 01160 01161 01162 bool DOS_CreateTempFile(char * const name,Bit16u * entry) { 01163 size_t namelen=strlen(name); 01164 char * tempname=name+namelen; 01165 if (namelen==0) { 01166 // temp file created in root directory 01167 tempname[0]='\\'; 01168 tempname++; 01169 } else { 01170 if ((name[namelen-1]!='\\') && (name[namelen-1]!='/')) { 01171 tempname[0]='\\'; 01172 tempname++; 01173 } 01174 } 01175 dos.errorcode=0; 01176 /* add random crap to the end of the name and try to open */ 01177 do { 01178 Bit32u i; 01179 for (i=0;i<8;i++) { 01180 tempname[i]=(rand()%26)+'A'; 01181 } 01182 tempname[8]=0; 01183 } while ((!DOS_CreateFile(name,0,entry)) && (dos.errorcode==DOSERR_FILE_ALREADY_EXISTS)); 01184 if (dos.errorcode) return false; 01185 return true; 01186 } 01187 01188 char DOS_ToUpper(char c) { 01189 unsigned char uc = *reinterpret_cast<unsigned char*>(&c); 01190 if (uc > 0x60 && uc < 0x7B) uc -= 0x20; 01191 else if (uc > 0x7F && uc < 0xA5) { 01192 const unsigned char t[0x25] = { 01193 0x00, 0x9a, 0x45, 0x41, 0x8E, 0x41, 0x8F, 0x80, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x00, 0x00, 01194 0x00, 0x92, 0x00, 0x4F, 0x99, 0x4F, 0x55, 0x55, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 01195 0x41, 0x49, 0x4F, 0x55, 0xA5}; 01196 if (t[uc - 0x80]) uc = t[uc-0x80]; 01197 } 01198 char sc = *reinterpret_cast<char*>(&uc); 01199 return sc; 01200 } 01201 01202 #define FCB_SEP ":;,=+" 01203 #define ILLEGAL ":.;,=+ \t/\"[]<>|" 01204 01205 static bool isvalid(const char in){ 01206 const char ill[]=ILLEGAL; 01207 return (Bit8u(in)>0x1F) && (!strchr(ill,in)); 01208 } 01209 01210 #define PARSE_SEP_STOP 0x01 01211 #define PARSE_DFLT_DRIVE 0x02 01212 #define PARSE_BLNK_FNAME 0x04 01213 #define PARSE_BLNK_FEXT 0x08 01214 01215 #define PARSE_RET_NOWILD 0 01216 #define PARSE_RET_WILD 1 01217 #define PARSE_RET_BADDRIVE 0xff 01218 01219 Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change) { 01220 const char* string_begin = string; 01221 Bit8u ret=0; 01222 if (!(parser & PARSE_DFLT_DRIVE)) { 01223 // default drive forced, this intentionally invalidates an extended FCB 01224 mem_writeb(PhysMake(seg,offset),0); 01225 } 01226 DOS_FCB fcb(seg,offset,false); // always a non-extended FCB 01227 bool hasdrive,hasname,hasext,finished; 01228 hasdrive=hasname=hasext=finished=false; 01229 Bitu index=0; 01230 Bit8u fill=' '; 01231 /* First get the old data from the fcb */ 01232 #ifdef _MSC_VER 01233 #pragma pack (1) 01234 #endif 01235 union { 01236 struct { 01237 char drive[2]; 01238 char name[9]; 01239 char ext[4]; 01240 } GCC_ATTRIBUTE (packed) part; 01241 char full[DOS_FCBNAME]; 01242 } fcb_name; 01243 #ifdef _MSC_VER 01244 #pragma pack() 01245 #endif 01246 /* Get the old information from the previous fcb */ 01247 fcb.GetName(fcb_name.full); 01248 fcb_name.part.drive[0]-='A'-1;fcb_name.part.drive[1]=0; 01249 fcb_name.part.name[8]=0;fcb_name.part.ext[3]=0; 01250 /* strip leading spaces */ 01251 while((*string==' ')||(*string=='\t')) string++; 01252 01253 /* Strip of the leading separator */ 01254 if((parser & PARSE_SEP_STOP) && *string) { 01255 char sep[] = FCB_SEP;char a[2]; 01256 a[0] = *string;a[1] = '\0'; 01257 if (strcspn(a,sep) == 0) string++; 01258 } 01259 01260 /* Skip following spaces as well */ 01261 while((*string==' ')||(*string=='\t')) string++; 01262 01263 /* Check for a drive */ 01264 if (string[1]==':') { 01265 unsigned char d = *reinterpret_cast<unsigned char*>(&string[0]); 01266 if (!isvalid(ascii_toupper(d))) {string += 2; goto savefcb;} //TODO check (for ret value) 01267 fcb_name.part.drive[0]=0; 01268 hasdrive=true; 01269 if (isalpha(d) && Drives[ascii_toupper(d)-'A']) { //Floppies under dos always exist, but don't bother with that at this level 01270 ; //THIS* was here 01271 } else ret=0xff; 01272 fcb_name.part.drive[0]=DOS_ToUpper(string[0])-'A'+1; //Always do THIS* and continue parsing, just return the right code 01273 string+=2; 01274 } 01275 01276 /* Check for extension only file names */ 01277 if (string[0] == '.') {string++;goto checkext;} 01278 01279 /* do nothing if not a valid name */ 01280 if(!isvalid(string[0])) goto savefcb; 01281 01282 hasname=true;finished=false;fill=' ';index=0; 01283 /* Copy the name */ 01284 while (true) { 01285 unsigned char nc = *reinterpret_cast<unsigned char*>(&string[0]); 01286 if (IS_PC98_ARCH && shiftjis_lead_byte(nc)) { 01287 /* Shift-JIS is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */ 01288 fcb_name.part.name[index]=(char)nc; 01289 string++; 01290 index++; 01291 if (index >= 8) break; 01292 01293 /* should be trailing byte of Shift-JIS */ 01294 if (nc < 32 || nc >= 127) continue; 01295 01296 fcb_name.part.name[index]=(char)nc; 01297 } 01298 else 01299 { 01300 char ncs = (char)ascii_toupper(nc); //Should use DOS_ToUpper, but then more calls need to be changed. 01301 if (ncs == '*') { //Handle * 01302 fill = '?'; 01303 ncs = '?'; 01304 } 01305 if (ncs == '?' && !ret && index < 8) ret = 1; //Don't override bad drive 01306 if (!isvalid(ncs)) { //Fill up the name. 01307 while(index < 8) 01308 fcb_name.part.name[index++] = (char)fill; 01309 break; 01310 } 01311 if (index < 8) { 01312 fcb_name.part.name[index++] = (char)((fill == '?')?fill:ncs); 01313 } 01314 } 01315 string++; 01316 } 01317 if (!(string[0]=='.')) goto savefcb; 01318 string++; 01319 checkext: 01320 /* Copy the extension */ 01321 hasext=true;finished=false;fill=' ';index=0; 01322 while (true) { 01323 unsigned char nc = *reinterpret_cast<unsigned char*>(&string[0]); 01324 if (IS_PC98_ARCH && shiftjis_lead_byte(nc)) { 01325 /* Shift-JIS is NOT ASCII and SHOULD NOT be converted to uppercase like ASCII */ 01326 fcb_name.part.ext[index]=(char)nc; 01327 string++; 01328 index++; 01329 if (index >= 3) break; 01330 01331 /* should be trailing byte of Shift-JIS */ 01332 if (nc < 32u || nc >= 127u) continue; 01333 01334 fcb_name.part.ext[index]=(char)nc; 01335 } 01336 else 01337 { 01338 char ncs = (char)ascii_toupper(nc); 01339 if (ncs == '*') { //Handle * 01340 fill = '?'; 01341 ncs = '?'; 01342 } 01343 if (ncs == '?' && !ret && index < 3) ret = 1; 01344 if (!isvalid(ncs)) { //Fill up the name. 01345 while(index < 3) 01346 fcb_name.part.ext[index++] = (char)fill; 01347 break; 01348 } 01349 if (index < 3) { 01350 fcb_name.part.ext[index++] = (char)((fill=='?')?fill:ncs); 01351 } 01352 } 01353 string++; 01354 } 01355 savefcb: 01356 if (!hasdrive && !(parser & PARSE_DFLT_DRIVE)) fcb_name.part.drive[0] = 0; 01357 if (!hasname && !(parser & PARSE_BLNK_FNAME)) strcpy(fcb_name.part.name," "); 01358 if (!hasext && !(parser & PARSE_BLNK_FEXT)) strcpy(fcb_name.part.ext," "); 01359 fcb.SetName((unsigned char)fcb_name.part.drive[0],fcb_name.part.name,fcb_name.part.ext); 01360 fcb.ClearBlockRecsize(); //Undocumented bonus work. 01361 *change=(Bit8u)(string-string_begin); 01362 return ret; 01363 } 01364 01365 static void DTAExtendNameVolumeLabel(const char* const name, char* const filename, char* const ext) { 01366 size_t i,s; 01367 01368 i=0; 01369 s=0; 01370 while (i < 8 && name[s] != 0) filename[i++] = name[s++]; 01371 while (i < 8) filename[i++] = ' '; 01372 01373 i=0; 01374 while (i < 3 && name[s] != 0) ext[i++] = name[s++]; 01375 while (i < 3) ext[i++] = ' '; 01376 } 01377 01378 static void DTAExtendName(char * const name,char * const filename,char * const ext) { 01379 char * find=strchr(name,'.'); 01380 if (find && find!=name) { 01381 strcpy(ext,find+1); 01382 *find=0; 01383 } else ext[0]=0; 01384 strcpy(filename,name); 01385 size_t i; 01386 for (i=strlen(name);i<8;i++) filename[i]=' '; 01387 filename[8]=0; 01388 for (i=strlen(ext);i<3;i++) ext[i]=' '; 01389 ext[3]=0; 01390 } 01391 01392 static void SaveFindResult(DOS_FCB & find_fcb) { 01393 DOS_DTA find_dta(dos.tables.tempdta); 01394 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH]; 01395 Bit32u size;Bit16u date;Bit16u time;Bit8u attr;Bit8u drive; 01396 char file_name[9];char ext[4]; 01397 find_dta.GetResult(name,lname,size,date,time,attr); 01398 drive=find_fcb.GetDrive()+1; 01399 Bit8u find_attr = DOS_ATTR_ARCHIVE; 01400 find_fcb.GetAttr(find_attr); /* Gets search attributes if extended */ 01401 /* Create a correct file and extention */ 01402 if (attr & DOS_ATTR_VOLUME) 01403 DTAExtendNameVolumeLabel(name,file_name,ext); 01404 else 01405 DTAExtendName(name,file_name,ext); 01406 01407 DOS_FCB fcb(RealSeg(dos.dta()),RealOff(dos.dta()));//TODO 01408 fcb.Create(find_fcb.Extended()); 01409 fcb.SetName(drive,file_name,ext); 01410 fcb.SetAttr(find_attr); /* Only adds attribute if fcb is extended */ 01411 fcb.SetResult(size,date,time,attr); 01412 } 01413 01414 bool DOS_FCBCreate(Bit16u seg,Bit16u offset) { 01415 DOS_FCB fcb(seg,offset); 01416 char shortname[DOS_FCBNAME];Bit16u handle; 01417 Bit8u attr = DOS_ATTR_ARCHIVE; 01418 fcb.GetAttr(attr); 01419 if (!attr) attr = DOS_ATTR_ARCHIVE; //Better safe than sorry 01420 01421 if (attr & DOS_ATTR_VOLUME) { 01422 fcb.GetVolumeName(shortname); 01423 return Drives[fcb.GetDrive()]->FileCreate(NULL,shortname,attr); 01424 } 01425 01426 fcb.GetName(shortname); 01427 if (!DOS_CreateFile(shortname,attr,&handle,true)) return false; 01428 fcb.FileOpen((Bit8u)handle); 01429 return true; 01430 } 01431 01432 bool DOS_FCBOpen(Bit16u seg,Bit16u offset) { 01433 DOS_FCB fcb(seg,offset); 01434 char shortname[DOS_FCBNAME];Bit16u handle; 01435 fcb.GetName(shortname); 01436 01437 /* Search for file if name has wildcards */ 01438 if (strpbrk(shortname,"*?")) { 01439 LOG(LOG_FCB,LOG_WARN)("Wildcards in filename"); 01440 if (!DOS_FCBFindFirst(seg,offset)) return false; 01441 DOS_DTA find_dta(dos.tables.tempdta); 01442 DOS_FCB find_fcb(RealSeg(dos.tables.tempdta),RealOff(dos.tables.tempdta)); 01443 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH],file_name[9],ext[4]; 01444 Bit32u size;Bit16u date,time;Bit8u attr; 01445 find_dta.GetResult(name,lname,size,date,time,attr); 01446 DTAExtendName(name,file_name,ext); 01447 find_fcb.SetName(fcb.GetDrive()+1,file_name,ext); 01448 find_fcb.GetName(shortname); 01449 } 01450 01451 /* First check if the name is correct */ 01452 Bit8u drive; 01453 char fullname[DOS_PATHLENGTH]; 01454 if (!DOS_MakeName(shortname,fullname,&drive)) return false; 01455 01456 /* Check, if file is already opened */ 01457 for (Bit8u i = 0;i < DOS_FILES;i++) { 01458 if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) { 01459 Files[i]->AddRef(); 01460 fcb.FileOpen(i); 01461 return true; 01462 } 01463 } 01464 01465 if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle,true)) return false; 01466 fcb.FileOpen((Bit8u)handle); 01467 return true; 01468 } 01469 01470 bool DOS_FCBClose(Bit16u seg,Bit16u offset) { 01471 DOS_FCB fcb(seg,offset); 01472 if(!fcb.Valid()) return false; 01473 Bit8u fhandle; 01474 fcb.FileClose(fhandle); 01475 DOS_CloseFile(fhandle,true); 01476 return true; 01477 } 01478 01479 bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset) { 01480 DOS_FCB fcb(seg,offset); 01481 RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta); 01482 char name[DOS_FCBNAME];fcb.GetName(name); 01483 Bit8u attr = DOS_ATTR_ARCHIVE; 01484 fcb.GetAttr(attr); /* Gets search attributes if extended */ 01485 bool ret=DOS_FindFirst(name,attr,true); 01486 dos.dta(old_dta); 01487 if (ret) SaveFindResult(fcb); 01488 return ret; 01489 } 01490 01491 bool DOS_FCBFindNext(Bit16u seg,Bit16u offset) { 01492 DOS_FCB fcb(seg,offset); 01493 RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta); 01494 bool ret=DOS_FindNext(); 01495 dos.dta(old_dta); 01496 if (ret) SaveFindResult(fcb); 01497 return ret; 01498 } 01499 01500 Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset,Bit16u recno) { 01501 DOS_FCB fcb(seg,offset); 01502 Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size; 01503 fcb.GetSeqData(fhandle,rec_size); 01504 if (fhandle==0xff && rec_size!=0) { 01505 if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA; 01506 LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB"); 01507 fcb.GetSeqData(fhandle,rec_size); 01508 } 01509 if (rec_size == 0) { 01510 rec_size = 128; 01511 fcb.SetSeqData(fhandle,rec_size); 01512 } 01513 fcb.GetRecord(cur_block,cur_rec); 01514 Bit32u pos=((cur_block*128u)+cur_rec)*rec_size; 01515 if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_READ_NODATA; 01516 Bit16u toread=rec_size; 01517 if (!DOS_ReadFile(fhandle,dos_copybuf,&toread,true)) return FCB_READ_NODATA; 01518 if (toread==0) return FCB_READ_NODATA; 01519 if (toread < rec_size) { //Zero pad copybuffer to rec_size 01520 Bitu i = toread; 01521 while(i < rec_size) dos_copybuf[i++] = 0; 01522 } 01523 MEM_BlockWrite(Real2Phys(dos.dta())+(PhysPt)(recno*rec_size),dos_copybuf,rec_size); 01524 if (++cur_rec>127u) { cur_block++;cur_rec=0; } 01525 fcb.SetRecord(cur_block,cur_rec); 01526 if (toread==rec_size) return FCB_SUCCESS; 01527 return FCB_READ_PARTIAL; 01528 } 01529 01530 Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u recno) { 01531 DOS_FCB fcb(seg,offset); 01532 Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size; 01533 fcb.GetSeqData(fhandle,rec_size); 01534 if (fhandle==0xffu && rec_size!=0u) { 01535 if (!DOS_FCBOpen(seg,offset)) return FCB_READ_NODATA; 01536 LOG(LOG_FCB,LOG_WARN)("Reopened closed FCB"); 01537 fcb.GetSeqData(fhandle,rec_size); 01538 } 01539 if (rec_size == 0) { 01540 rec_size = 128; 01541 fcb.SetSeqData(fhandle,rec_size); 01542 } 01543 fcb.GetRecord(cur_block,cur_rec); 01544 Bit32u pos=((cur_block*128u)+cur_rec)*rec_size; 01545 if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE; 01546 MEM_BlockRead(Real2Phys(dos.dta())+(PhysPt)(recno*rec_size),dos_copybuf,rec_size); 01547 Bit16u towrite=rec_size; 01548 if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE; 01549 Bit32u size;Bit16u date,time; 01550 fcb.GetSizeDateTime(size,date,time); 01551 if (pos+towrite>size) size=pos+towrite; 01552 //time doesn't keep track of endofday 01553 date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day); 01554 Bit32u ticks = mem_readd(BIOS_TIMER); 01555 Bit32u seconds = (ticks*10u)/182u; 01556 Bit16u hour = (Bit16u)(seconds/3600u); 01557 Bit16u min = (Bit16u)((seconds % 3600u)/60u); 01558 Bit16u sec = (Bit16u)(seconds % 60u); 01559 time = DOS_PackTime(hour,min,sec); 01560 Files[fhandle]->time = time; 01561 Files[fhandle]->date = date; 01562 fcb.SetSizeDateTime(size,date,time); 01563 if (++cur_rec>127u) { cur_block++;cur_rec=0; } 01564 fcb.SetRecord(cur_block,cur_rec); 01565 return FCB_SUCCESS; 01566 } 01567 01568 Bit8u DOS_FCBIncreaseSize(Bit16u seg,Bit16u offset) { 01569 DOS_FCB fcb(seg,offset); 01570 Bit8u fhandle,cur_rec;Bit16u cur_block,rec_size; 01571 fcb.GetSeqData(fhandle,rec_size); 01572 fcb.GetRecord(cur_block,cur_rec); 01573 Bit32u pos=((cur_block*128u)+cur_rec)*rec_size; 01574 if (!DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET,true)) return FCB_ERR_WRITE; 01575 Bit16u towrite=0; 01576 if (!DOS_WriteFile(fhandle,dos_copybuf,&towrite,true)) return FCB_ERR_WRITE; 01577 Bit32u size;Bit16u date,time; 01578 fcb.GetSizeDateTime(size,date,time); 01579 if (pos+towrite>size) size=pos+towrite; 01580 //time doesn't keep track of endofday 01581 date = DOS_PackDate(dos.date.year,dos.date.month,dos.date.day); 01582 Bit32u ticks = mem_readd(BIOS_TIMER); 01583 Bit32u seconds = (ticks*10u)/182u; 01584 Bit16u hour = (Bit16u)(seconds/3600u); 01585 Bit16u min = (Bit16u)((seconds % 3600u)/60u); 01586 Bit16u sec = (Bit16u)(seconds % 60u); 01587 time = DOS_PackTime(hour,min,sec); 01588 Files[fhandle]->time = time; 01589 Files[fhandle]->date = date; 01590 fcb.SetSizeDateTime(size,date,time); 01591 fcb.SetRecord(cur_block,cur_rec); 01592 return FCB_SUCCESS; 01593 } 01594 01595 Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) { 01596 /* if restore is true :random read else random blok read. 01597 * random read updates old block and old record to reflect the random data 01598 * before the read!!!!!!!!! and the random data is not updated! (user must do this) 01599 * Random block read updates these fields to reflect the state after the read! 01600 */ 01601 DOS_FCB fcb(seg,offset); 01602 Bit32u random; 01603 Bit16u old_block=0; 01604 Bit8u old_rec=0; 01605 Bit8u error=0; 01606 Bit16u count; 01607 01608 /* Set the correct record from the random data */ 01609 fcb.GetRandom(random); 01610 fcb.SetRecord((Bit16u)(random / 128u),(Bit8u)(random & 127u)); 01611 if (restore) fcb.GetRecord(old_block,old_rec);//store this for after the read. 01612 // Read records 01613 for (count=0; count<*numRec; count++) { 01614 error = DOS_FCBRead(seg,offset,count); 01615 if (error!=FCB_SUCCESS) break; 01616 } 01617 if (error==FCB_READ_PARTIAL) count++; //partial read counts 01618 *numRec=count; 01619 Bit16u new_block;Bit8u new_rec; 01620 fcb.GetRecord(new_block,new_rec); 01621 if (restore) fcb.SetRecord(old_block,old_rec); 01622 /* Update the random record pointer with new position only when restore is false*/ 01623 if(!restore) fcb.SetRandom(new_block*128u+new_rec); 01624 return error; 01625 } 01626 01627 Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore) { 01628 /* see FCB_RandomRead */ 01629 DOS_FCB fcb(seg,offset); 01630 Bit32u random; 01631 Bit16u old_block=0; 01632 Bit8u old_rec=0; 01633 Bit8u error=0; 01634 01635 /* Set the correct record from the random data */ 01636 fcb.GetRandom(random); 01637 fcb.SetRecord((Bit16u)(random / 128u),(Bit8u)(random & 127u)); 01638 if (restore) fcb.GetRecord(old_block,old_rec); 01639 if (*numRec > 0) { 01640 Bit16u count; 01641 /* Write records */ 01642 for (count=0; count<*numRec; count++) { 01643 error = DOS_FCBWrite(seg,offset,count);// dos_fcbwrite return 0 false when true... 01644 if (error!=FCB_SUCCESS) break; 01645 } 01646 *numRec=count; 01647 } else { 01648 DOS_FCBIncreaseSize(seg,offset); 01649 } 01650 Bit16u new_block;Bit8u new_rec; 01651 fcb.GetRecord(new_block,new_rec); 01652 if (restore) fcb.SetRecord(old_block,old_rec); 01653 /* Update the random record pointer with new position only when restore is false */ 01654 if (!restore) fcb.SetRandom(new_block*128u+new_rec); 01655 return error; 01656 } 01657 01658 bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset) { 01659 char shortname[DOS_PATHLENGTH];Bit16u entry; 01660 DOS_FCB fcb(seg,offset); 01661 fcb.GetName(shortname); 01662 if (!DOS_OpenFile(shortname,OPEN_READ,&entry,true)) return false; 01663 Bit32u size = 0; 01664 Files[entry]->Seek(&size,DOS_SEEK_END); 01665 DOS_CloseFile(entry,true); 01666 01667 Bit8u handle; Bit16u rec_size; 01668 fcb.GetSeqData(handle,rec_size); 01669 if (rec_size == 0) rec_size = 128; //Use default if missing. 01670 01671 Bit32u random=(size/rec_size); 01672 if (size % rec_size) random++; 01673 fcb.SetRandom(random); 01674 return true; 01675 } 01676 01677 bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset){ 01678 /* Special case: ????????.??? and DOS_ATTR_VOLUME */ 01679 { 01680 DOS_FCB fcb(seg,offset); 01681 Bit8u attr = 0; 01682 fcb.GetAttr(attr); 01683 Bit8u drive = fcb.GetDrive(); 01684 std::string label = Drives[drive]->GetLabel(); 01685 01686 if (attr & DOS_ATTR_VOLUME) { 01687 char shortname[DOS_FCBNAME]; 01688 fcb.GetVolumeName(shortname); 01689 01690 if (!strcmp(shortname,"???????????")) { 01691 if (!label.empty()) { 01692 Drives[drive]->SetLabel("",false,true); 01693 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB delete volume label"); 01694 return true; 01695 } 01696 } 01697 else { 01698 /* MS-DOS 6.22 LABEL.EXE will explicitly request to delete the volume label by the volume label not ????????.???? */ 01699 if (!label.empty()) { 01700 while (label.length() < 11) label += ' '; 01701 if (!memcmp(label.c_str(),shortname,11)) { 01702 Drives[drive]->SetLabel("",false,true); 01703 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB delete volume label deleted"); 01704 return true; 01705 } 01706 } 01707 } 01708 01709 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB delete volume label not found (current='%s' asking='%s')",label.c_str(),shortname); 01710 DOS_SetError(DOSERR_FILE_NOT_FOUND); // right? 01711 return false; 01712 } 01713 } 01714 01715 /* FCB DELETE honours wildcards. it will return true if one or more 01716 * files get deleted. 01717 * To get this: the dta is set to temporary dta in which found files are 01718 * stored. This can not be the tempdta as that one is used by fcbfindfirst 01719 */ 01720 RealPt old_dta=dos.dta();dos.dta(dos.tables.tempdta_fcbdelete); 01721 RealPt new_dta=dos.dta(); 01722 bool nextfile = false; 01723 bool return_value = false; 01724 nextfile = DOS_FCBFindFirst(seg,offset); 01725 DOS_FCB fcb(RealSeg(new_dta),RealOff(new_dta)); 01726 while(nextfile) { 01727 char shortname[DOS_FCBNAME] = { 0 }; 01728 fcb.GetName(shortname); 01729 bool res=DOS_UnlinkFile(shortname); 01730 if(!return_value && res) return_value = true; //at least one file deleted 01731 nextfile = DOS_FCBFindNext(seg,offset); 01732 } 01733 dos.dta(old_dta); /*Restore dta */ 01734 return return_value; 01735 } 01736 01737 char* trimString(char* str); 01738 01739 bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset){ 01740 DOS_FCB fcbold(seg,offset); 01741 DOS_FCB fcbnew(seg,offset); 01742 fcbnew.SetPtPhys(fcbnew.GetPtPhys()+0x10u);//HACK: FCB NEW memory offset is affected by whether FCB OLD is extended 01743 if(!fcbold.Valid()) return false; 01744 char oldname[DOS_FCBNAME]; 01745 char newname[DOS_FCBNAME]; 01746 fcbold.GetName(oldname);fcbnew.GetName(newname); 01747 01748 { 01749 Bit8u drive = fcbold.GetDrive(); 01750 std::string label = Drives[drive]->GetLabel(); 01751 Bit8u attr = 0; 01752 01753 fcbold.GetAttr(attr); 01754 /* According to RBIL and confirmed with SETLABEL.ASM in DOSLIB2, you can rename a volume label dirent as well with this function */ 01755 if (attr & DOS_ATTR_VOLUME) { 01756 fcbold.GetVolumeName(oldname); 01757 fcbnew.GetVolumeName(newname); 01758 01759 for (unsigned int i=0;i < 11;i++) 01760 oldname[i] = toupper(oldname[i]); 01761 01762 trimString(oldname); 01763 trimString(newname); 01764 01765 if (!label.empty()) { 01766 if (!strcmp(oldname,"???????????") || label == oldname) { 01767 Drives[drive]->SetLabel(newname,false,true); 01768 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB rename volume label to '%s' from '%s'",newname,oldname); 01769 return true; 01770 } 01771 else { 01772 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB rename volume label rejected, does not match current label '%s' from '%s'",newname,oldname); 01773 DOS_SetError(DOSERR_FILE_NOT_FOUND); // right? 01774 return false; 01775 } 01776 } 01777 else { 01778 LOG(LOG_DOSMISC,LOG_NORMAL)("FCB rename volume label rejected, no label set"); 01779 DOS_SetError(DOSERR_FILE_NOT_FOUND); // right? 01780 return false; 01781 } 01782 } 01783 } 01784 01785 /* Check, if sourcefile is still open. This was possible in DOS, but modern oses don't like this */ 01786 Bit8u drive; char fullname[DOS_PATHLENGTH]; 01787 if (!DOS_MakeName(oldname,fullname,&drive)) return false; 01788 01789 DOS_PSP psp(dos.psp()); 01790 for (Bit8u i=0;i<DOS_FILES;i++) { 01791 if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) { 01792 Bit16u handle = psp.FindEntryByHandle(i); 01793 //(more than once maybe) 01794 if (handle == 0xFFu) { 01795 DOS_CloseFile(i,true); 01796 } else { 01797 DOS_CloseFile(handle); 01798 } 01799 } 01800 } 01801 01802 /* Rename the file */ 01803 return DOS_Rename(oldname,newname); 01804 } 01805 01806 void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset) { 01807 DOS_FCB fcb(seg,offset); 01808 Bit16u block;Bit8u rec; 01809 fcb.GetRecord(block,rec); 01810 fcb.SetRandom(block*128u+rec); 01811 } 01812 01813 01814 bool DOS_FileExists(char const * const name) { 01815 char fullname[DOS_PATHLENGTH];Bit8u drive; 01816 if (!DOS_MakeName(name,fullname,&drive)) return false; 01817 return Drives[drive]->FileExists(fullname); 01818 } 01819 01820 bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters) { 01821 if (!drive) drive = DOS_GetDefaultDrive(); 01822 else drive--; 01823 if (drive >= DOS_DRIVES || !Drives[drive]) { 01824 DOS_SetError(DOSERR_INVALID_DRIVE); 01825 return false; 01826 } 01827 Bit16u _free_clusters; 01828 Drives[drive]->AllocationInfo(_bytes_sector,_sectors_cluster,_total_clusters,&_free_clusters); 01829 SegSet16(ds,RealSeg(dos.tables.mediaid)); 01830 reg_bx=RealOff(dos.tables.mediaid+drive*dos.tables.dpb_size); 01831 return true; 01832 } 01833 01834 bool DOS_SetDrive(Bit8u drive) { 01835 if (Drives[drive]) { 01836 DOS_SetDefaultDrive(drive); 01837 return true; 01838 } else { 01839 return false; 01840 } 01841 } 01842 01843 bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate) { 01844 Bit32u handle=RealHandle(entry); 01845 if (handle>=DOS_FILES) { 01846 DOS_SetError(DOSERR_INVALID_HANDLE); 01847 return false; 01848 } 01849 if (!Files[handle] || !Files[handle]->IsOpen()) { 01850 DOS_SetError(DOSERR_INVALID_HANDLE); 01851 return false; 01852 } 01853 if (!Files[handle]->UpdateDateTimeFromHost()) { 01854 DOS_SetError(DOSERR_INVALID_HANDLE); 01855 return false; 01856 } 01857 *otime = Files[handle]->time; 01858 *odate = Files[handle]->date; 01859 return true; 01860 } 01861 01862 bool DOS_SetFileDate(Bit16u entry, Bit16u ntime, Bit16u ndate) 01863 { 01864 Bit32u handle=RealHandle(entry); 01865 if (handle>=DOS_FILES) { 01866 DOS_SetError(DOSERR_INVALID_HANDLE); 01867 return false; 01868 } 01869 if (!Files[handle]) { 01870 DOS_SetError(DOSERR_INVALID_HANDLE); 01871 return false; 01872 } 01873 Files[handle]->time = ntime; 01874 Files[handle]->date = ndate; 01875 Files[handle]->newtime = true; 01876 01877 return true; 01878 } 01879 01880 void DOS_SetupFiles (void) { 01881 /* Setup the File Handles */ 01882 Files = new DOS_File * [DOS_FILES]; 01883 Bit32u i; 01884 for (i=0;i<DOS_FILES;i++) { 01885 Files[i]=0; 01886 } 01887 /* Setup the Virtual Disk System */ 01888 for (i=0;i<DOS_DRIVES;i++) { 01889 Drives[i]=0; 01890 } 01891 Drives[25]=new Virtual_Drive(); 01892 } 01893 01894 // save state support 01895 void DOS_File::SaveState( std::ostream& stream ) 01896 { 01897 Bit32u file_namelen, seek_pos; 01898 01899 01900 file_namelen = strlen( name ); 01901 seek_pos = GetSeekPos(); 01902 01903 //****************************************** 01904 //****************************************** 01905 //****************************************** 01906 01907 // - pure data 01908 WRITE_POD( &file_namelen, file_namelen ); 01909 WRITE_POD_SIZE( name, file_namelen ); 01910 01911 WRITE_POD( &flags, flags ); 01912 WRITE_POD( &open, open ); 01913 01914 WRITE_POD( &attr, attr ); 01915 WRITE_POD( &time, time ); 01916 WRITE_POD( &date, date ); 01917 WRITE_POD( &refCtr, refCtr ); 01918 WRITE_POD( &hdrive, hdrive ); 01919 01920 //****************************************** 01921 //****************************************** 01922 //****************************************** 01923 01924 // - reloc ptr 01925 WRITE_POD( &seek_pos, seek_pos ); 01926 } 01927 01928 01929 void DOS_File::LoadState( std::istream& stream, bool pop ) 01930 { 01931 Bit32u file_namelen, seek_pos; 01932 char *file_name; 01933 01934 //****************************************** 01935 //****************************************** 01936 //****************************************** 01937 01938 // - pure data 01939 READ_POD( &file_namelen, file_namelen ); 01940 file_name = (char*)alloca( file_namelen * sizeof(char) ); 01941 READ_POD_SIZE( file_name, file_namelen ); 01942 01943 READ_POD( &flags, flags ); 01944 READ_POD( &open, open ); 01945 01946 READ_POD( &attr, attr ); 01947 READ_POD( &time, time ); 01948 READ_POD( &date, date ); 01949 READ_POD( &refCtr, refCtr ); 01950 READ_POD( &hdrive, hdrive ); 01951 01952 //****************************************** 01953 //****************************************** 01954 //****************************************** 01955 01956 // - reloc ptr 01957 READ_POD( &seek_pos, seek_pos ); 01958 01959 if (pop) 01960 return; 01961 01962 if( open ) Seek( &seek_pos, DOS_SEEK_SET ); 01963 else Close(); 01964 } 01965 01966 extern bool dos_kernel_disabled; 01967 01968 void POD_Save_DOS_Files( std::ostream& stream ) 01969 { 01970 if (!dos_kernel_disabled) { 01971 // 1. Do drives first (directories -> files) 01972 // 2. Then files next 01973 01974 for( int lcv=0; lcv<DOS_DRIVES; lcv++ ) { 01975 Bit8u drive_valid; 01976 01977 drive_valid = 0; 01978 if( Drives[lcv] == 0 ) drive_valid = 0xff; 01979 01980 //********************************************** 01981 //********************************************** 01982 //********************************************** 01983 01984 // - reloc ptr 01985 WRITE_POD( &drive_valid, drive_valid ); 01986 01987 if( drive_valid == 0xff ) continue; 01988 Drives[lcv]->SaveState(stream); 01989 } 01990 01991 for( unsigned int lcv=0; lcv<DOS_FILES; lcv++ ) { 01992 Bit8u file_valid; 01993 char *file_name; 01994 Bit8u file_namelen, file_drive, file_flags; 01995 01996 file_valid = 0; 01997 if( !Files[lcv] || Files[lcv]->GetName() == NULL ) file_valid = 0xff; 01998 else { 01999 if( strcmp( Files[lcv]->GetName(), "NUL" ) == 0 ) file_valid = 0xfe;//earth 2140 needs this 02000 if( strcmp( Files[lcv]->GetName(), "CON" ) == 0 ) file_valid = 0xfe; 02001 if( strcmp( Files[lcv]->GetName(), "LPT1" ) == 0 ) file_valid = 0xfe; 02002 if( strcmp( Files[lcv]->GetName(), "PRN" ) == 0 ) file_valid = 0xfe; 02003 if( strcmp( Files[lcv]->GetName(), "AUX" ) == 0 ) file_valid = 0xfe; 02004 if( strcmp( Files[lcv]->GetName(), "EMMXXXX0" ) == 0 ) file_valid = 0xfe;//raiden needs this 02005 } 02006 02007 // - reloc ptr 02008 WRITE_POD( &file_valid, file_valid ); 02009 // system files 02010 if( file_valid == 0xff ) continue; 02011 if( file_valid == 0xfe ) { 02012 WRITE_POD( &Files[lcv]->refCtr, Files[lcv]->refCtr ); 02013 continue; 02014 } 02015 02016 //********************************************** 02017 //********************************************** 02018 //********************************************** 02019 02020 file_namelen = strlen( Files[lcv]->name ); 02021 file_name = (char *) alloca( file_namelen ); 02022 strcpy( file_name, Files[lcv]->name ); 02023 02024 file_drive = Files[lcv]->GetDrive(); 02025 file_flags = Files[lcv]->flags; 02026 02027 // - Drives->FileOpen vars (repeat copy) 02028 WRITE_POD( &file_namelen, file_namelen ); 02029 WRITE_POD_SIZE( file_name, file_namelen ); 02030 02031 WRITE_POD( &file_drive, file_drive ); 02032 WRITE_POD( &file_flags, file_flags ); 02033 02034 02035 Files[lcv]->SaveState(stream); 02036 } 02037 } 02038 } 02039 02040 02041 void POD_Load_DOS_Files( std::istream& stream ) 02042 { 02043 if (!dos_kernel_disabled) { 02044 // 1. Do drives first (directories -> files) 02045 // 2. Then files next 02046 02047 for( int lcv=0; lcv<DOS_DRIVES; lcv++ ) { 02048 Bit8u drive_valid; 02049 02050 // - reloc ptr 02051 READ_POD( &drive_valid, drive_valid ); 02052 if( drive_valid == 0xff ) continue; 02053 02054 if( Drives[lcv] ) Drives[lcv]->LoadState(stream); 02055 } 02056 02057 //Alien Carnage - game creates and unlinks temp files 02058 //Here are two situations 02059 //1. Game already unlinked temp file, but information about file is still in Files[] and we saved it. In this case we must only pop old data from stream by loading. This is fixed. 02060 //2. Game still did not unlink file, We saved this information. Then was game restarted and temp files were removed. Then we try load save state, but we don't have temp file. This is not fixed 02061 DOS_File *dummy = NULL; 02062 02063 for( unsigned int lcv=0; lcv<DOS_FILES; lcv++ ) { 02064 Bit8u file_valid; 02065 char *file_name; 02066 Bit8u file_namelen, file_drive, file_flags; 02067 02068 // - reloc ptr 02069 READ_POD( &file_valid, file_valid ); 02070 02071 // ignore system files 02072 if( file_valid == 0xfe ) { 02073 READ_POD( &Files[lcv]->refCtr, Files[lcv]->refCtr ); 02074 continue; 02075 } 02076 02077 // shutdown old file 02078 if( Files[lcv] && Files[lcv]->GetName() != NULL ) { 02079 // invalid file state - abort 02080 if( strcmp( Files[lcv]->GetName(), "NUL" ) == 0 ) break; 02081 if( strcmp( Files[lcv]->GetName(), "CON" ) == 0 ) break; 02082 if( strcmp( Files[lcv]->GetName(), "LPT1" ) == 0 ) break; 02083 if( strcmp( Files[lcv]->GetName(), "PRN" ) == 0 ) break; 02084 if( strcmp( Files[lcv]->GetName(), "AUX" ) == 0 ) break; 02085 if( strcmp( Files[lcv]->GetName(), "EMMXXXX0" ) == 0 ) break;//raiden needs this 02086 02087 02088 if( Files[lcv]->IsOpen() ) Files[lcv]->Close(); 02089 if (Files[lcv]->RemoveRef()<=0) { 02090 delete Files[lcv]; 02091 } 02092 Files[lcv]=0; 02093 } 02094 02095 // ignore NULL file 02096 if( file_valid == 0xff ) continue; 02097 02098 //********************************************** 02099 //********************************************** 02100 //********************************************** 02101 02102 // - Drives->FileOpen vars (repeat copy) 02103 02104 READ_POD( &file_namelen, file_namelen ); 02105 file_name = (char *) alloca( file_namelen ); 02106 READ_POD_SIZE( file_name, file_namelen ); 02107 02108 READ_POD( &file_drive, file_drive ); 02109 READ_POD( &file_flags, file_flags ); 02110 02111 02112 // NOTE: Must open regardless to get 'new' DOS_File class 02113 Drives[file_drive]->FileOpen( &Files[lcv], file_name, file_flags ); 02114 02115 if( Files[lcv] ) { 02116 Files[lcv]->LoadState(stream, false); 02117 } else { 02118 //Alien carnage ->pop data for invalid file from stream 02119 if (dummy == NULL) { 02120 dummy = new localFile(); 02121 } 02122 dummy->LoadState(stream, true); 02123 }; 02124 } 02125 02126 if (dummy != NULL) { 02127 delete dummy; 02128 } 02129 } 02130 }