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 * Heavy improvements by the DOSBox-X Team 00019 * With major works from joncampbell123 and Wengier 00020 */ 00021 00022 00023 #include <stdio.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <time.h> 00027 #include <errno.h> 00028 #include <limits.h> 00029 00030 #include "dosbox.h" 00031 #include "dos_inc.h" 00032 #include "drives.h" 00033 #include "support.h" 00034 #include "cross.h" 00035 #include "inout.h" 00036 #include "regs.h" 00037 #include "timer.h" 00038 #ifndef WIN32 00039 #include <utime.h> 00040 #else 00041 #include <sys/utime.h> 00042 #include <sys/locking.h> 00043 #endif 00044 00045 #include "cp437_uni.h" 00046 #include "cp866_uni.h" 00047 #include "cp932_uni.h" 00048 00049 #if defined(PATH_MAX) && !defined(MAX_PATH) 00050 #define MAX_PATH PATH_MAX 00051 #endif 00052 00053 #if defined(WIN32) 00054 // Windows: Use UTF-16 (wide char) 00055 // TODO: Offer an option to NOT use wide char on Windows if directed by config.h 00056 // for people who compile this code for Windows 95 or earlier where some 00057 // widechar functions are missing. 00058 typedef wchar_t host_cnv_char_t; 00059 # define host_cnv_use_wchar 00060 # define _HT(x) L##x 00061 # if defined(__MINGW32__) /* TODO: Get MinGW to support 64-bit file offsets, at least targeting Windows XP! */ 00062 # define ht_stat_t struct _stat 00063 # define ht_stat(x,y) _wstat(x,y) 00064 # else 00065 # define ht_stat_t struct _stat64 /* WTF Microsoft?? Why aren't _stat and _wstat() consistent on stat struct type? */ 00066 # define ht_stat(x,y) _wstat64(x,y) 00067 # endif 00068 # define ht_access(x,y) _waccess(x,y) 00069 # define ht_strdup(x) _wcsdup(x) 00070 # define ht_unlink(x) _wunlink(x) 00071 #else 00072 // Linux: Use UTF-8 00073 typedef char host_cnv_char_t; 00074 # define _HT(x) x 00075 # define ht_stat_t struct stat 00076 # define ht_stat(x,y) stat(x,y) 00077 # define ht_access(x,y) access(x,y) 00078 # define ht_strdup(x) strdup(x) 00079 # define ht_unlink(x) unlink(x) 00080 #endif 00081 00082 static host_cnv_char_t cpcnv_temp[4096]; 00083 static host_cnv_char_t cpcnv_ltemp[4096]; 00084 static Bit16u ldid[256]; 00085 static std::string ldir[256]; 00086 extern bool rsize, freesizecap, force_sfn; 00087 extern int lfn_filefind_handle; 00088 extern unsigned long totalc, freec; 00089 00090 bool String_ASCII_TO_HOST(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/) { 00091 const host_cnv_char_t* df = d + CROSS_LEN - 1; 00092 const char *sf = s + CROSS_LEN - 1; 00093 00094 while (*s != 0 && s < sf) { 00095 unsigned char ic = (unsigned char)(*s++); 00096 if (ic < 32 || ic > 127) return false; // non-representable 00097 00098 #if defined(host_cnv_use_wchar) 00099 *d++ = (host_cnv_char_t)ic; 00100 #else 00101 if (utf8_encode(&d,df,(uint32_t)ic) < 0) // will advance d by however many UTF-8 bytes are needed 00102 return false; // non-representable, or probably just out of room 00103 #endif 00104 } 00105 00106 assert(d <= df); 00107 *d = 0; 00108 00109 return true; 00110 } 00111 00112 template <class MT> bool String_SBCS_TO_HOST(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/,const MT *map,const size_t map_max) { 00113 const host_cnv_char_t* df = d + CROSS_LEN - 1; 00114 const char *sf = s + CROSS_LEN - 1; 00115 00116 while (*s != 0 && s < sf) { 00117 unsigned char ic = (unsigned char)(*s++); 00118 if (ic >= map_max) return false; // non-representable 00119 MT wc = map[ic]; // output: unicode character 00120 00121 #if defined(host_cnv_use_wchar) 00122 *d++ = (host_cnv_char_t)wc; 00123 #else 00124 if (utf8_encode(&d,df,(uint32_t)wc) < 0) // will advance d by however many UTF-8 bytes are needed 00125 return false; // non-representable, or probably just out of room 00126 #endif 00127 } 00128 00129 assert(d <= df); 00130 *d = 0; 00131 00132 return true; 00133 } 00134 00135 template <class MT> bool String_DBCS_TO_HOST_SHIFTJIS(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/,const MT *hitbl,const MT *rawtbl,const size_t rawtbl_max) { 00136 const host_cnv_char_t* df = d + CROSS_LEN - 1; 00137 const char *sf = s + CROSS_LEN - 1; 00138 00139 while (*s != 0 && s < sf) { 00140 uint16_t ic = (unsigned char)(*s++); 00141 if ((ic & 0xE0) == 0x80 || (ic & 0xE0) == 0xE0) { 00142 if (*s == 0) return false; 00143 ic <<= 8U; 00144 ic += (unsigned char)(*s++); 00145 } 00146 00147 MT rawofs = hitbl[ic >> 6]; 00148 if (rawofs == 0xFFFF) 00149 return false; 00150 00151 assert((size_t)(rawofs+ (Bitu)0x40) <= rawtbl_max); 00152 MT wc = rawtbl[rawofs + (ic & 0x3F)]; 00153 if (wc == 0x0000) 00154 return false; 00155 00156 #if defined(host_cnv_use_wchar) 00157 *d++ = (host_cnv_char_t)wc; 00158 #else 00159 if (utf8_encode(&d,df,(uint32_t)wc) < 0) // will advance d by however many UTF-8 bytes are needed 00160 return false; // non-representable, or probably just out of room 00161 #endif 00162 } 00163 00164 assert(d <= df); 00165 *d = 0; 00166 00167 return true; 00168 } 00169 00170 // TODO: This is SLOW. Optimize. 00171 template <class MT> int SBCS_From_Host_Find(int c,const MT *map,const size_t map_max) { 00172 for (size_t i=0;i < map_max;i++) { 00173 if ((MT)c == map[i]) 00174 return (int)i; 00175 } 00176 00177 return -1; 00178 } 00179 00180 // TODO: This is SLOW. Optimize. 00181 template <class MT> int DBCS_SHIFTJIS_From_Host_Find(int c,const MT *hitbl,const MT *rawtbl,const size_t rawtbl_max) { 00182 for (size_t h=0;h < 1024;h++) { 00183 MT ofs = hitbl[h]; 00184 00185 if (ofs == 0xFFFF) continue; 00186 assert((size_t)(ofs+ (Bitu)0x40) <= rawtbl_max); 00187 00188 for (size_t l=0;l < 0x40;l++) { 00189 if ((MT)c == rawtbl[ofs+l]) 00190 return (int)((h << 6) + l); 00191 } 00192 } 00193 00194 return -1; 00195 } 00196 00197 template <class MT> bool String_HOST_TO_DBCS_SHIFTJIS(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/,const MT *hitbl,const MT *rawtbl,const size_t rawtbl_max) { 00198 const host_cnv_char_t *sf = s + CROSS_LEN - 1; 00199 const char* df = d + CROSS_LEN - 1; 00200 00201 while (*s != 0 && s < sf) { 00202 int ic; 00203 #if defined(host_cnv_use_wchar) 00204 ic = (int)(*s++); 00205 #else 00206 if ((ic=utf8_decode(&s,sf)) < 0) 00207 return false; // non-representable 00208 #endif 00209 00210 int oc = DBCS_SHIFTJIS_From_Host_Find<MT>(ic,hitbl,rawtbl,rawtbl_max); 00211 if (oc < 0) 00212 return false; // non-representable 00213 00214 if (oc >= 0x100) { 00215 if ((d+1) >= df) return false; 00216 *d++ = (char)(oc >> 8U); 00217 *d++ = (char)oc; 00218 } 00219 else { 00220 if (d >= df) return false; 00221 *d++ = (char)oc; 00222 } 00223 } 00224 00225 assert(d <= df); 00226 *d = 0; 00227 00228 return true; 00229 } 00230 00231 template <class MT> bool String_HOST_TO_SBCS(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/,const MT *map,const size_t map_max) { 00232 const host_cnv_char_t *sf = s + CROSS_LEN - 1; 00233 const char* df = d + CROSS_LEN - 1; 00234 00235 while (*s != 0 && s < sf) { 00236 int ic; 00237 #if defined(host_cnv_use_wchar) 00238 ic = (int)(*s++); 00239 #else 00240 if ((ic=utf8_decode(&s,sf)) < 0) 00241 return false; // non-representable 00242 #endif 00243 00244 int oc = SBCS_From_Host_Find<MT>(ic,map,map_max); 00245 if (oc < 0) 00246 return false; // non-representable 00247 00248 if (d >= df) return false; 00249 *d++ = (char)oc; 00250 } 00251 00252 assert(d <= df); 00253 *d = 0; 00254 00255 return true; 00256 } 00257 00258 bool String_HOST_TO_ASCII(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/) { 00259 const host_cnv_char_t *sf = s + CROSS_LEN - 1; 00260 const char* df = d + CROSS_LEN - 1; 00261 00262 while (*s != 0 && s < sf) { 00263 int ic; 00264 #if defined(host_cnv_use_wchar) 00265 ic = (int)(*s++); 00266 #else 00267 if ((ic=utf8_decode(&s,sf)) < 0) 00268 return false; // non-representable 00269 #endif 00270 00271 if (ic < 32 || ic > 127) 00272 return false; // non-representable 00273 00274 if (d >= df) return false; 00275 *d++ = (char)ic; 00276 } 00277 00278 assert(d <= df); 00279 *d = 0; 00280 00281 return true; 00282 } 00283 00284 bool cpwarn_once = false; 00285 00286 bool CodePageHostToGuest(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/) { 00287 switch (dos.loaded_codepage) { 00288 case 437: 00289 return String_HOST_TO_SBCS<uint16_t>(d,s,cp437_to_unicode,sizeof(cp437_to_unicode)/sizeof(cp437_to_unicode[0])); 00290 case 866: 00291 return String_HOST_TO_SBCS<uint16_t>(d,s,cp866_to_unicode,sizeof(cp866_to_unicode)/sizeof(cp866_to_unicode[0])); 00292 case 932: 00293 return String_HOST_TO_DBCS_SHIFTJIS<uint16_t>(d,s,cp932_to_unicode_hitbl,cp932_to_unicode_raw,sizeof(cp932_to_unicode_raw)/sizeof(cp932_to_unicode_raw[0])); 00294 default: 00295 /* at this time, it would be cruel and unusual to not allow any file I/O just because 00296 * our code page support is so limited. */ 00297 if (!cpwarn_once) { 00298 cpwarn_once = true; 00299 LOG_MSG("WARNING: No translation support (to guest) for code page %u",dos.loaded_codepage); 00300 } 00301 return String_HOST_TO_ASCII(d,s); 00302 } 00303 00304 return false; 00305 } 00306 00307 bool CodePageGuestToHost(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/) { 00308 switch (dos.loaded_codepage) { 00309 case 437: 00310 return String_SBCS_TO_HOST<uint16_t>(d,s,cp437_to_unicode,sizeof(cp437_to_unicode)/sizeof(cp437_to_unicode[0])); 00311 case 866: 00312 return String_SBCS_TO_HOST<uint16_t>(d,s,cp866_to_unicode,sizeof(cp866_to_unicode)/sizeof(cp866_to_unicode[0])); 00313 case 932: 00314 return String_DBCS_TO_HOST_SHIFTJIS<uint16_t>(d,s,cp932_to_unicode_hitbl,cp932_to_unicode_raw,sizeof(cp932_to_unicode_raw)/sizeof(cp932_to_unicode_raw[0])); 00315 default: 00316 /* at this time, it would be cruel and unusual to not allow any file I/O just because 00317 * our code page support is so limited. */ 00318 if (!cpwarn_once) { 00319 cpwarn_once = true; 00320 LOG_MSG("WARNING: No translation support (to host) for code page %u",dos.loaded_codepage); 00321 } 00322 return String_ASCII_TO_HOST(d,s); 00323 } 00324 00325 return false; 00326 } 00327 00328 host_cnv_char_t *CodePageGuestToHost(const char *s) { 00329 if (!CodePageGuestToHost(cpcnv_temp,s)) 00330 return NULL; 00331 00332 return cpcnv_temp; 00333 } 00334 00335 char *CodePageHostToGuest(const host_cnv_char_t *s) { 00336 if (!CodePageHostToGuest((char*)cpcnv_temp,s)) 00337 return NULL; 00338 00339 return (char*)cpcnv_temp; 00340 } 00341 00342 char *CodePageHostToGuestL(const host_cnv_char_t *s) { 00343 if (!CodePageHostToGuest((char*)cpcnv_ltemp,s)) 00344 return NULL; 00345 00346 return (char*)cpcnv_ltemp; 00347 } 00348 00349 bool localDrive::FileCreate(DOS_File * * file,const char * name,Bit16u attributes) { 00350 if (nocachedir) EmptyCache(); 00351 00352 if (readonly) { 00353 DOS_SetError(DOSERR_WRITE_PROTECTED); 00354 return false; 00355 } 00356 00357 if (attributes & DOS_ATTR_VOLUME) { 00358 // Real MS-DOS 6.22 and earlier behavior says that creating a volume label 00359 // without first deleting the existing volume label does nothing but add 00360 // yet another volume label entry to the root directory and the reported 00361 // volume label does not change. MS-DOS 7.0 and later appear to have fixed 00362 // this considering the re-use of bits [3:0] == 0xF to carry LFN entries. 00363 // 00364 // Emulate this behavior by setting the volume label ONLY IF there is no 00365 // volume label. If the DOS application knows how to do it properly it will 00366 // first issue an FCB delete with attr = DOS_ATTR_VOLUME and ????????.??? 00367 // OR (more common in MS-DOS 6.22 and later) an FCB delete with 00368 // attr = DOS_ATTR_VOLUME and an explicit copy of the volume label obtained 00369 // from FCB FindFirst. 00370 // 00371 // Volume label handling always affects the root directory. 00372 // 00373 // This function is called with file == NULL for volume labels. 00374 if (*GetLabel() == 0) 00375 SetLabel(name,false,true); 00376 00377 return true; 00378 } 00379 00380 assert(file != NULL); 00381 00382 //TODO Maybe care for attributes but not likely 00383 char newname[CROSS_LEN]; 00384 strcpy(newname,basedir); 00385 strcat(newname,name); 00386 CROSS_FILENAME(newname); 00387 const char* temp_name = dirCache.GetExpandName(newname); // Can only be used until a new drive_cache action is performed 00388 /* Test if file exists (so we need to truncate it). don't add to dirCache then */ 00389 bool existing_file=false; 00390 00391 // guest to host code page translation 00392 const host_cnv_char_t* host_name = CodePageGuestToHost(temp_name); 00393 if (host_name == NULL) { 00394 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 00395 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME 00396 return false; 00397 } 00398 00399 #ifdef host_cnv_use_wchar 00400 FILE * test=_wfopen(host_name,L"rb+"); 00401 #else 00402 FILE * test=fopen(host_name,"rb+"); 00403 #endif 00404 if(test) { 00405 fclose(test); 00406 existing_file=true; 00407 } 00408 00409 #ifdef host_cnv_use_wchar 00410 FILE * hand=_wfopen(host_name,L"wb+"); 00411 #else 00412 FILE * hand=fopen(host_name,"wb+"); 00413 #endif 00414 if (!hand){ 00415 LOG_MSG("Warning: file creation failed: %s",newname); 00416 return false; 00417 } 00418 00419 if(!existing_file) { 00420 strcpy(newname,basedir); 00421 strcat(newname,name); 00422 CROSS_FILENAME(newname); 00423 dirCache.AddEntry(newname, true); 00424 } 00425 00426 /* Make the 16 bit device information */ 00427 *file=new localFile(name,hand); 00428 (*file)->flags=OPEN_READWRITE; 00429 00430 return true; 00431 } 00432 00433 bool localDrive::FileOpen(DOS_File * * file,const char * name,Bit32u flags) { 00434 if (nocachedir) EmptyCache(); 00435 00436 if (readonly) { 00437 if ((flags&0xf) == OPEN_WRITE || (flags&0xf) == OPEN_READWRITE) { 00438 DOS_SetError(DOSERR_WRITE_PROTECTED); 00439 return false; 00440 } 00441 } 00442 00443 const host_cnv_char_t * type; 00444 switch (flags&0xf) { 00445 case OPEN_READ: type = _HT("rb"); break; 00446 case OPEN_WRITE: type = _HT("rb+"); break; 00447 case OPEN_READWRITE: type = _HT("rb+"); break; 00448 case OPEN_READ_NO_MOD: type = _HT("rb"); break; //No modification of dates. LORD4.07 uses this 00449 default: 00450 DOS_SetError(DOSERR_ACCESS_CODE_INVALID); 00451 return false; 00452 } 00453 char newname[CROSS_LEN]; 00454 strcpy(newname,basedir); 00455 strcat(newname,name); 00456 CROSS_FILENAME(newname); 00457 dirCache.ExpandName(newname); 00458 00459 //Flush the buffer of handles for the same file. (Betrayal in Antara) 00460 Bit8u i,drive=DOS_DRIVES; 00461 localFile *lfp; 00462 for (i=0;i<DOS_DRIVES;i++) { 00463 if (Drives[i]==this) { 00464 drive=i; 00465 break; 00466 } 00467 } 00468 for (i=0;i<DOS_FILES;i++) { 00469 if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { 00470 lfp=dynamic_cast<localFile*>(Files[i]); 00471 if (lfp) lfp->Flush(); 00472 } 00473 } 00474 00475 // guest to host code page translation 00476 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 00477 if (host_name == NULL) { 00478 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 00479 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00480 return false; 00481 } 00482 00483 #ifdef host_cnv_use_wchar 00484 FILE * hand=_wfopen(host_name,type); 00485 #else 00486 FILE * hand=fopen(host_name,type); 00487 #endif 00488 // Bit32u err=errno; 00489 if (!hand) { 00490 if((flags&0xf) != OPEN_READ) { 00491 #ifdef host_cnv_use_wchar 00492 FILE * hmm=_wfopen(host_name,L"rb"); 00493 #else 00494 FILE * hmm=fopen(host_name,"rb"); 00495 #endif 00496 if (hmm) { 00497 fclose(hmm); 00498 #ifdef host_cnv_use_wchar 00499 LOG_MSG("Warning: file %ls exists and failed to open in write mode.\nPlease Remove write-protection",host_name); 00500 #else 00501 LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",host_name); 00502 #endif 00503 } 00504 } 00505 return false; 00506 } 00507 00508 *file=new localFile(name,hand); 00509 (*file)->flags=flags; //for the inheritance flag and maybe check for others. 00510 // (*file)->SetFileName(host_name); 00511 return true; 00512 } 00513 00514 FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const type) { 00515 00516 char newname[CROSS_LEN]; 00517 strcpy(newname,basedir); 00518 strcat(newname,name); 00519 CROSS_FILENAME(newname); 00520 dirCache.ExpandName(newname); 00521 00522 // guest to host code page translation 00523 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 00524 if (host_name == NULL) { 00525 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 00526 return NULL; 00527 } 00528 00529 #ifdef host_cnv_use_wchar 00530 wchar_t wtype[8]; 00531 unsigned int tis; 00532 00533 // "type" always has ANSI chars (like "rb"), nothing fancy 00534 for (tis=0;tis < 7 && type[tis] != 0;tis++) wtype[tis] = (wchar_t)type[tis]; 00535 assert(tis <= 7); // guard 00536 wtype[tis] = 0; 00537 00538 return _wfopen(host_name,wtype); 00539 #else 00540 return fopen(host_name,type); 00541 #endif 00542 } 00543 00544 bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) { 00545 00546 strcpy(sysName, basedir); 00547 strcat(sysName, dosName); 00548 CROSS_FILENAME(sysName); 00549 dirCache.ExpandName(sysName); 00550 00551 // guest to host code page translation 00552 const host_cnv_char_t* host_name = CodePageGuestToHost(sysName); 00553 if (host_name == NULL) { 00554 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,sysName); 00555 return false; 00556 } 00557 00558 #ifdef host_cnv_use_wchar 00559 // FIXME: GetSystemFilename as implemented cannot return the wide char filename 00560 return false; 00561 #else 00562 strcpy(sysName,host_name); 00563 return true; 00564 #endif 00565 } 00566 00567 #if defined (WIN32) 00568 #include <Shellapi.h> 00569 #else 00570 #include <glob.h> 00571 #endif 00572 bool localDrive::FileUnlink(const char * name) { 00573 if (readonly) { 00574 DOS_SetError(DOSERR_WRITE_PROTECTED); 00575 return false; 00576 } 00577 00578 char newname[CROSS_LEN]; 00579 strcpy(newname,basedir); 00580 strcat(newname,name); 00581 CROSS_FILENAME(newname); 00582 char *fullname = dirCache.GetExpandName(newname); 00583 00584 // guest to host code page translation 00585 const host_cnv_char_t* host_name = CodePageGuestToHost(fullname); 00586 if (host_name == NULL) { 00587 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,fullname); 00588 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00589 return false; 00590 } 00591 00592 if (ht_unlink(host_name)) { 00593 //Unlink failed for some reason try finding it. 00594 ht_stat_t buffer; 00595 if(ht_stat(host_name,&buffer)) return false; // File not found. 00596 00597 #ifdef host_cnv_use_wchar 00598 FILE* file_writable = _wfopen(host_name,L"rb+"); 00599 #else 00600 FILE* file_writable = fopen(host_name,"rb+"); 00601 #endif 00602 if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ? 00603 fclose(file_writable); 00604 00605 //File exists and can technically be deleted, nevertheless it failed. 00606 //This means that the file is probably open by some process. 00607 //See if We have it open. 00608 bool found_file = false; 00609 for(Bitu i = 0;i < DOS_FILES;i++){ 00610 if(Files[i] && Files[i]->IsName(name)) { 00611 Bitu max = DOS_FILES; 00612 while(Files[i]->IsOpen() && max--) { 00613 Files[i]->Close(); 00614 if (Files[i]->RemoveRef()<=0) break; 00615 } 00616 found_file=true; 00617 } 00618 } 00619 if(!found_file) return false; 00620 if (!ht_unlink(host_name)) { 00621 dirCache.DeleteEntry(newname); 00622 return true; 00623 } 00624 return false; 00625 } else { 00626 dirCache.DeleteEntry(newname); 00627 return true; 00628 } 00629 } 00630 00631 bool localDrive::FindFirst(const char * _dir,DOS_DTA & dta,bool fcb_findfirst) { 00632 char tempDir[CROSS_LEN]; 00633 strcpy(tempDir,basedir); 00634 strcat(tempDir,_dir); 00635 CROSS_FILENAME(tempDir); 00636 00637 for (unsigned int i=0;i<strlen(tempDir);i++) tempDir[i]=toupper(tempDir[i]); 00638 if (nocachedir) EmptyCache(); 00639 00640 if (allocation.mediaid==0xF0 ) { 00641 EmptyCache(); //rescan floppie-content on each findfirst 00642 } 00643 00644 00645 if (tempDir[strlen(tempDir)-1]!=CROSS_FILESPLIT) { 00646 char end[2]={CROSS_FILESPLIT,0}; 00647 strcat(tempDir,end); 00648 } 00649 00650 Bit16u id; 00651 if (!dirCache.FindFirst(tempDir,id)) { 00652 DOS_SetError(DOSERR_PATH_NOT_FOUND); 00653 return false; 00654 } 00655 if (lfn_filefind_handle>=LFN_FILEFIND_MAX) { 00656 dta.SetDirID(id); 00657 strcpy(srchInfo[id].srch_dir,tempDir); 00658 } else { 00659 ldid[lfn_filefind_handle]=id; 00660 ldir[lfn_filefind_handle]=tempDir; 00661 } 00662 00663 Bit8u sAttr; 00664 dta.GetSearchParams(sAttr,tempDir,uselfn); 00665 00666 if (this->isRemote() && this->isRemovable()) { 00667 // cdroms behave a bit different than regular drives 00668 if (sAttr == DOS_ATTR_VOLUME) { 00669 dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); 00670 return true; 00671 } 00672 } else { 00673 if (sAttr == DOS_ATTR_VOLUME) { 00674 if ( strcmp(dirCache.GetLabel(), "") == 0 ) { 00675 // LOG(LOG_DOSMISC,LOG_ERROR)("DRIVELABEL REQUESTED: none present, returned NOLABEL"); 00676 // dta.SetResult("NO_LABEL",0,0,0,DOS_ATTR_VOLUME); 00677 // return true; 00678 DOS_SetError(DOSERR_NO_MORE_FILES); 00679 return false; 00680 } 00681 dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); 00682 return true; 00683 } else if ((sAttr & DOS_ATTR_VOLUME) && (*_dir == 0) && !fcb_findfirst) { 00684 //should check for a valid leading directory instead of 0 00685 //exists==true if the volume label matches the searchmask and the path is valid 00686 if (WildFileCmp(dirCache.GetLabel(),tempDir)) { 00687 dta.SetResult(dirCache.GetLabel(),dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME); 00688 return true; 00689 } 00690 } 00691 } 00692 return FindNext(dta); 00693 } 00694 00695 char * shiftjis_upcase(char * str); 00696 00697 bool localDrive::FindNext(DOS_DTA & dta) { 00698 00699 char * dir_ent, *ldir_ent; 00700 ht_stat_t stat_block; 00701 char full_name[CROSS_LEN], lfull_name[LFN_NAMELENGTH+1]; 00702 char dir_entcopy[CROSS_LEN], ldir_entcopy[CROSS_LEN]; 00703 00704 Bit8u srch_attr;char srch_pattern[LFN_NAMELENGTH]; 00705 Bit8u find_attr; 00706 00707 dta.GetSearchParams(srch_attr,srch_pattern,uselfn); 00708 Bit16u id = lfn_filefind_handle>=LFN_FILEFIND_MAX?dta.GetDirID():ldid[lfn_filefind_handle]; 00709 00710 again: 00711 if (!dirCache.FindNext(id,dir_ent,ldir_ent)) { 00712 if (lfn_filefind_handle<LFN_FILEFIND_MAX) { 00713 ldid[lfn_filefind_handle]=0; 00714 ldir[lfn_filefind_handle]=""; 00715 } 00716 DOS_SetError(DOSERR_NO_MORE_FILES); 00717 return false; 00718 } 00719 if(!WildFileCmp(dir_ent,srch_pattern)&&!LWildFileCmp(ldir_ent,srch_pattern)) goto again; 00720 00721 strcpy(full_name,lfn_filefind_handle>=LFN_FILEFIND_MAX?srchInfo[id].srch_dir:(ldir[lfn_filefind_handle]!=""?ldir[lfn_filefind_handle].c_str():"\\")); 00722 strcpy(lfull_name,full_name); 00723 00724 strcat(full_name,dir_ent); 00725 strcat(lfull_name,ldir_ent); 00726 00727 //GetExpandName might indirectly destroy dir_ent (by caching in a new directory 00728 //and due to its design dir_ent might be lost.) 00729 //Copying dir_ent first 00730 strcpy(dir_entcopy,dir_ent); 00731 strcpy(ldir_entcopy,ldir_ent); 00732 00733 char *temp_name = dirCache.GetExpandName(full_name); 00734 00735 // guest to host code page translation 00736 const host_cnv_char_t* host_name = CodePageGuestToHost(temp_name); 00737 if (host_name == NULL) { 00738 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,temp_name); 00739 goto again;//No symlinks and such 00740 } 00741 00742 if (ht_stat(host_name,&stat_block)!=0) 00743 goto again;//No symlinks and such 00744 00745 if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY; 00746 else find_attr=0; 00747 #if defined (WIN32) 00748 Bitu attribs = GetFileAttributesW(host_name); 00749 if (attribs != INVALID_FILE_ATTRIBUTES) 00750 find_attr|=attribs&0x3f; 00751 #else 00752 find_attr|=DOS_ATTR_ARCHIVE; 00753 if(!(stat_block.st_mode & S_IWUSR)) find_attr|=DOS_ATTR_READ_ONLY; 00754 #endif 00755 if (~srch_attr & find_attr & DOS_ATTR_DIRECTORY) goto again; 00756 00757 /*file is okay, setup everything to be copied in DTA Block */ 00758 char find_name[DOS_NAMELENGTH_ASCII], lfind_name[LFN_NAMELENGTH+1]; 00759 Bit16u find_date,find_time;Bit32u find_size; 00760 00761 if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){ 00762 strcpy(find_name,dir_entcopy); 00763 if (IS_PC98_ARCH) 00764 shiftjis_upcase(find_name); 00765 else 00766 upcase(find_name); 00767 } 00768 strcpy(lfind_name,ldir_entcopy); 00769 lfind_name[LFN_NAMELENGTH]=0; 00770 00771 find_size=(Bit32u) stat_block.st_size; 00772 const struct tm* time; 00773 if((time=localtime(&stat_block.st_mtime))!=0){ 00774 find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); 00775 find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); 00776 } else { 00777 find_time=6; 00778 find_date=4; 00779 } 00780 dta.SetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr); 00781 return true; 00782 } 00783 00784 bool localDrive::SetFileAttr(const char * name,Bit16u attr) { 00785 char newname[CROSS_LEN]; 00786 strcpy(newname,basedir); 00787 strcat(newname,name); 00788 CROSS_FILENAME(newname); 00789 dirCache.ExpandName(newname); 00790 00791 // guest to host code page translation 00792 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 00793 if (host_name == NULL) { 00794 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 00795 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00796 return false; 00797 } 00798 00799 #if defined (WIN32) 00800 if (!SetFileAttributesW(host_name, attr)) 00801 { 00802 DOS_SetError((Bit16u)GetLastError()); 00803 return false; 00804 } 00805 dirCache.EmptyCache(); 00806 return true; 00807 #else 00808 ht_stat_t status; 00809 if (ht_stat(host_name,&status)==0) { 00810 if (attr & (DOS_ATTR_SYSTEM|DOS_ATTR_HIDDEN)) 00811 LOG(LOG_DOSMISC,LOG_WARN)("%s: Application attempted to set system or hidden attributes for '%s' which is ignored for local drives",__FUNCTION__,newname); 00812 00813 if (attr & DOS_ATTR_READ_ONLY) 00814 status.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 00815 else 00816 status.st_mode |= S_IWUSR; 00817 00818 if (chmod(host_name,status.st_mode) < 0) { 00819 DOS_SetError(DOSERR_ACCESS_DENIED); 00820 return false; 00821 } 00822 00823 return true; 00824 } 00825 00826 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00827 return false; 00828 #endif 00829 } 00830 00831 bool localDrive::GetFileAttr(const char * name,Bit16u * attr) { 00832 if (nocachedir) EmptyCache(); 00833 00834 char newname[CROSS_LEN]; 00835 strcpy(newname,basedir); 00836 strcat(newname,name); 00837 CROSS_FILENAME(newname); 00838 dirCache.ExpandName(newname); 00839 00840 // guest to host code page translation 00841 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 00842 if (host_name == NULL) { 00843 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 00844 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00845 return false; 00846 } 00847 00848 #if defined (WIN32) 00849 Bitu attribs = GetFileAttributesW(host_name); 00850 if (attribs == INVALID_FILE_ATTRIBUTES) 00851 { 00852 DOS_SetError((Bit16u)GetLastError()); 00853 return false; 00854 } 00855 *attr = attribs&0x3f; 00856 return true; 00857 #else 00858 ht_stat_t status; 00859 if (ht_stat(host_name,&status)==0) { 00860 *attr=DOS_ATTR_ARCHIVE; 00861 if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY; 00862 if(!(status.st_mode & S_IWUSR)) *attr|=DOS_ATTR_READ_ONLY; 00863 return true; 00864 } 00865 *attr=0; 00866 return false; 00867 #endif 00868 } 00869 00870 bool localDrive::GetFileAttrEx(char* name, struct stat *status) { 00871 char newname[CROSS_LEN]; 00872 strcpy(newname,basedir); 00873 strcat(newname,name); 00874 CROSS_FILENAME(newname); 00875 dirCache.ExpandName(newname); 00876 return !stat(newname,status); 00877 } 00878 00879 unsigned long localDrive::GetCompressedSize(char* name) { 00880 (void)name; 00881 #if !defined (WIN32) 00882 return 0; 00883 #else 00884 char newname[CROSS_LEN]; 00885 strcpy(newname,basedir); 00886 strcat(newname,name); 00887 CROSS_FILENAME(newname); 00888 dirCache.ExpandName(newname); 00889 DWORD size = GetCompressedFileSize(newname, NULL); 00890 if (size != INVALID_FILE_SIZE) { 00891 if (size != 0 && size == GetFileSize(newname, NULL)) { 00892 DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters; 00893 if (GetDiskFreeSpace(newname, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters)) { 00894 size = ((size - 1) | (sectors_per_cluster * bytes_per_sector - 1)) + 1; 00895 } 00896 } 00897 return size; 00898 } else { 00899 DOS_SetError((Bit16u)GetLastError()); 00900 return -1; 00901 } 00902 #endif 00903 } 00904 00905 #if defined (WIN32) 00906 HANDLE localDrive::CreateOpenFile(const char* name) { 00907 char newname[CROSS_LEN]; 00908 strcpy(newname,basedir); 00909 strcat(newname,name); 00910 CROSS_FILENAME(newname); 00911 dirCache.ExpandName(newname); 00912 HANDLE handle=CreateFile(newname, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 00913 if (handle==INVALID_HANDLE_VALUE) 00914 DOS_SetError((Bit16u)GetLastError()); 00915 return handle; 00916 } 00917 00918 unsigned long localDrive::GetSerial() { 00919 char newname[CROSS_LEN]; 00920 strcpy(newname,basedir); 00921 CROSS_FILENAME(newname); 00922 dirCache.ExpandName(newname); 00923 if (strlen(newname)>2&&newname[1]==':') { 00924 unsigned long serial_number=0x1234; 00925 char volume[] = "A:\\"; 00926 volume[0]=newname[0]; 00927 GetVolumeInformation(volume, NULL, 0, &serial_number, NULL, NULL, NULL, 0); 00928 return serial_number; 00929 } 00930 00931 return 0x1234; 00932 } 00933 #endif 00934 00935 bool localDrive::MakeDir(const char * dir) { 00936 if (nocachedir) EmptyCache(); 00937 00938 if (readonly) { 00939 DOS_SetError(DOSERR_WRITE_PROTECTED); 00940 return false; 00941 } 00942 00943 char newdir[CROSS_LEN]; 00944 strcpy(newdir,basedir); 00945 strcat(newdir,dir); 00946 CROSS_FILENAME(newdir); 00947 00948 const char* temp_name = dirCache.GetExpandName(newdir); 00949 00950 // guest to host code page translation 00951 const host_cnv_char_t* host_name = CodePageGuestToHost(temp_name); 00952 if (host_name == NULL) { 00953 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir); 00954 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME 00955 return false; 00956 } 00957 00958 #if defined (WIN32) /* MS Visual C++ */ 00959 int temp=_wmkdir(host_name); 00960 #else 00961 int temp=mkdir(host_name,0775); 00962 #endif 00963 if (temp==0) dirCache.CacheOut(newdir,true); 00964 00965 return (temp==0);// || ((temp!=0) && (errno==EEXIST)); 00966 } 00967 00968 bool localDrive::RemoveDir(const char * dir) { 00969 if (nocachedir) EmptyCache(); 00970 00971 if (readonly) { 00972 DOS_SetError(DOSERR_WRITE_PROTECTED); 00973 return false; 00974 } 00975 00976 char newdir[CROSS_LEN]; 00977 strcpy(newdir,basedir); 00978 strcat(newdir,dir); 00979 CROSS_FILENAME(newdir); 00980 00981 const char* temp_name = dirCache.GetExpandName(newdir); 00982 00983 // guest to host code page translation 00984 const host_cnv_char_t* host_name = CodePageGuestToHost(temp_name); 00985 if (host_name == NULL) { 00986 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir); 00987 DOS_SetError(DOSERR_FILE_NOT_FOUND); 00988 return false; 00989 } 00990 00991 #if defined (WIN32) /* MS Visual C++ */ 00992 int temp=_wrmdir(host_name); 00993 #else 00994 int temp=rmdir(host_name); 00995 #endif 00996 if (temp==0) dirCache.DeleteEntry(newdir,true); 00997 return (temp==0); 00998 } 00999 01000 bool localDrive::TestDir(const char * dir) { 01001 if (nocachedir) EmptyCache(); 01002 01003 char newdir[CROSS_LEN]; 01004 strcpy(newdir,basedir); 01005 strcat(newdir,dir); 01006 CROSS_FILENAME(newdir); 01007 dirCache.ExpandName(newdir); 01008 01009 // guest to host code page translation 01010 const host_cnv_char_t* host_name = CodePageGuestToHost(newdir); 01011 if (host_name == NULL) { 01012 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir); 01013 return false; 01014 } 01015 01016 std::string temp_line=std::string(newdir); 01017 if(temp_line.size() > 4 && temp_line[0]=='\\' && temp_line[1]=='\\' && temp_line[2]!='\\' && std::count(temp_line.begin()+3, temp_line.end(), '\\')==1) strcat(newdir,"\\"); 01018 01019 // Skip directory test, if "\" 01020 size_t len = strlen(newdir); 01021 if (len && (newdir[len-1]!='\\')) { 01022 // It has to be a directory ! 01023 ht_stat_t test; 01024 if (ht_stat(host_name,&test)) return false; 01025 if ((test.st_mode & S_IFDIR)==0) return false; 01026 } 01027 int temp=ht_access(host_name,F_OK); 01028 return (temp==0); 01029 } 01030 01031 bool localDrive::Rename(const char * oldname,const char * newname) { 01032 if (readonly) { 01033 DOS_SetError(DOSERR_WRITE_PROTECTED); 01034 return false; 01035 } 01036 01037 const host_cnv_char_t* ht; 01038 01039 char newold[CROSS_LEN]; 01040 strcpy(newold,basedir); 01041 strcat(newold,oldname); 01042 CROSS_FILENAME(newold); 01043 dirCache.ExpandName(newold); 01044 01045 char newnew[CROSS_LEN]; 01046 strcpy(newnew,basedir); 01047 strcat(newnew,newname); 01048 CROSS_FILENAME(newnew); 01049 dirCache.ExpandName(newnew); 01050 01051 // guest to host code page translation 01052 ht = CodePageGuestToHost(newold); 01053 if (ht == NULL) { 01054 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newold); 01055 DOS_SetError(DOSERR_FILE_NOT_FOUND); 01056 return false; 01057 } 01058 host_cnv_char_t *o_temp_name = ht_strdup(ht); 01059 01060 // guest to host code page translation 01061 ht = CodePageGuestToHost(newnew); 01062 if (ht == NULL) { 01063 free(o_temp_name); 01064 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newnew); 01065 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME 01066 return false; 01067 } 01068 host_cnv_char_t *n_temp_name = ht_strdup(ht); 01069 01070 #ifdef host_cnv_use_wchar 01071 int temp=_wrename(o_temp_name,n_temp_name); 01072 #else 01073 int temp=rename(o_temp_name,n_temp_name); 01074 #endif 01075 01076 if (temp==0) dirCache.CacheOut(newnew); 01077 01078 free(o_temp_name); 01079 free(n_temp_name); 01080 01081 return (temp==0); 01082 01083 } 01084 01085 #if !defined(WIN32) 01086 #include <sys/statvfs.h> 01087 #endif 01088 bool localDrive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) { 01089 *_bytes_sector=allocation.bytes_sector; 01090 *_sectors_cluster=allocation.sectors_cluster; 01091 *_total_clusters=allocation.total_clusters; 01092 *_free_clusters=allocation.free_clusters; 01093 if ((!allocation.total_clusters && !allocation.free_clusters) || freesizecap) { 01094 bool res=false; 01095 #if defined(WIN32) 01096 long unsigned int dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; 01097 Bit8u drive=strlen(basedir)>1&&basedir[1]==':'?toupper(basedir[0])-'A'+1:0; 01098 if (drive>26) drive=0; 01099 char root[4]="A:\\"; 01100 root[0]='A'+drive-1; 01101 res = GetDiskFreeSpace(drive?root:NULL, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); 01102 if (res) { 01103 unsigned long total = dwTotalClusters * dwSectPerClust; 01104 int ratio = total > 2097120 ? 64 : (total > 1048560 ? 32 : (total > 524280 ? 16 : (total > 262140 ? 8 : (total > 131070 ? 4 : (total > 65535 ? 2 : 1))))); 01105 *_bytes_sector = (Bit16u)dwBytesPerSect; 01106 *_sectors_cluster = ratio; 01107 *_total_clusters = total > 4194240? 65535 : (Bit16u)(dwTotalClusters * dwSectPerClust / ratio); 01108 *_free_clusters = total > 4194240? 61440 : (Bit16u)(dwFreeClusters * dwSectPerClust / ratio); 01109 if (rsize) { 01110 totalc=dwTotalClusters * dwSectPerClust / ratio; 01111 freec=dwFreeClusters * dwSectPerClust / ratio; 01112 } 01113 #else 01114 struct statvfs stat; 01115 res = statvfs(basedir, &stat) == 0; 01116 if (res) { 01117 int ratio = stat.f_blocks / 65536, tmp=ratio; 01118 *_bytes_sector = 512; 01119 *_sectors_cluster = stat.f_bsize/512 > 64? 64 : stat.f_bsize/512; 01120 if (ratio>1) { 01121 if (ratio * (*_sectors_cluster) > 64) tmp = (*_sectors_cluster+63)/(*_sectors_cluster); 01122 *_sectors_cluster = ratio * (*_sectors_cluster) > 64? 64 : ratio * (*_sectors_cluster); 01123 ratio = tmp; 01124 } 01125 *_total_clusters = stat.f_blocks > 65535? 65535 : stat.f_blocks; 01126 *_free_clusters = stat.f_bavail > 61440? 61440 : stat.f_bavail; 01127 if (rsize) { 01128 totalc=stat.f_blocks; 01129 freec=stat.f_bavail; 01130 if (ratio>1) { 01131 totalc/=ratio; 01132 freec/=ratio; 01133 } 01134 } 01135 #endif 01136 if (allocation.total_clusters || allocation.free_clusters) { 01137 bool g1=*_bytes_sector * *_sectors_cluster * *_total_clusters > allocation.bytes_sector * allocation.sectors_cluster * allocation.total_clusters; 01138 bool g2=*_bytes_sector * *_sectors_cluster * *_free_clusters > allocation.bytes_sector * allocation.sectors_cluster * allocation.free_clusters; 01139 if (g1||g2) { 01140 *_bytes_sector = allocation.bytes_sector; 01141 *_sectors_cluster = allocation.sectors_cluster; 01142 if (g1) *_total_clusters = allocation.total_clusters; 01143 if (g2) *_free_clusters = allocation.free_clusters; 01144 if (*_total_clusters<*_free_clusters) { 01145 if (*_free_clusters>65525) 01146 *_total_clusters=65535; 01147 else 01148 *_total_clusters=*_free_clusters+10; 01149 } 01150 if (rsize) { 01151 if (g1) totalc=*_total_clusters; 01152 if (g2) freec=*_free_clusters; 01153 } 01154 } 01155 } 01156 } else if (!allocation.total_clusters && !allocation.free_clusters) { 01157 if (allocation.mediaid==0xF0) { 01158 *_bytes_sector = 512; 01159 *_sectors_cluster = 1; 01160 *_total_clusters = 2880; 01161 *_free_clusters = 2880; 01162 } else if (allocation.bytes_sector==2048) { 01163 *_bytes_sector = 2048; 01164 *_sectors_cluster = 1; 01165 *_total_clusters = 65535; 01166 *_free_clusters = 0; 01167 } else { 01168 // 512*32*32765==~500MB total size 01169 // 512*32*16000==~250MB total free size 01170 *_bytes_sector = 512; 01171 *_sectors_cluster = 32; 01172 *_total_clusters = 32765; 01173 *_free_clusters = 16000; 01174 } 01175 } 01176 } 01177 return true; 01178 } 01179 01180 bool localDrive::FileExists(const char* name) { 01181 if (nocachedir) EmptyCache(); 01182 01183 char newname[CROSS_LEN]; 01184 strcpy(newname,basedir); 01185 strcat(newname,name); 01186 CROSS_FILENAME(newname); 01187 dirCache.ExpandName(newname); 01188 01189 // guest to host code page translation 01190 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 01191 if (host_name == NULL) { 01192 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 01193 return false; 01194 } 01195 01196 ht_stat_t temp_stat; 01197 if(ht_stat(host_name,&temp_stat)!=0) return false; 01198 if(temp_stat.st_mode & S_IFDIR) return false; 01199 return true; 01200 } 01201 01202 bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) { 01203 if (nocachedir) EmptyCache(); 01204 01205 char newname[CROSS_LEN]; 01206 strcpy(newname,basedir); 01207 strcat(newname,name); 01208 CROSS_FILENAME(newname); 01209 dirCache.ExpandName(newname); 01210 01211 // guest to host code page translation 01212 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 01213 if (host_name == NULL) { 01214 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname); 01215 return false; 01216 } 01217 01218 ht_stat_t temp_stat; 01219 if(ht_stat(host_name,&temp_stat)!=0) return false; 01220 01221 /* Convert the stat to a FileStat */ 01222 const struct tm* time; 01223 if((time=localtime(&temp_stat.st_mtime))!=0) { 01224 stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); 01225 stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); 01226 } 01227 stat_block->size=(Bit32u)temp_stat.st_size; 01228 return true; 01229 } 01230 01231 01232 Bit8u localDrive::GetMediaByte(void) { 01233 return allocation.mediaid; 01234 } 01235 01236 bool localDrive::isRemote(void) { 01237 if (remote==1) return true; 01238 if (remote==0) return false; 01239 char psp_name[9]; 01240 DOS_MCB psp_mcb(dos.psp()-1); 01241 psp_mcb.GetFileName(psp_name); 01242 if (strcmp(psp_name, "SCANDISK") == 0) { 01243 /* Check for SCANDISK.EXE and return true (Wengier) */ 01244 return true; 01245 } 01246 /* Automatically detect if called by SCANDISK.EXE even if it is renamed (tested with the program from MS-DOS 6.20 to Windows ME) */ 01247 if (dos.version.major >= 5 && reg_sp >=0x4000 && mem_readw(SegPhys(ss)+reg_sp)/0x100 == 0x1 && mem_readw(SegPhys(ss)+reg_sp+2)/0x100 >= 0xB && mem_readw(SegPhys(ss)+reg_sp+2)/0x100 <= 0x12) 01248 return true; 01249 return false; 01250 } 01251 01252 bool localDrive::isRemovable(void) { 01253 return false; 01254 } 01255 01256 Bits localDrive::UnMount(void) { 01257 delete this; 01258 return 0; 01259 } 01260 01261 /* helper functions for drive cache */ 01262 void *localDrive::opendir(const char *name) { 01263 // guest to host code page translation 01264 const host_cnv_char_t* host_name = CodePageGuestToHost(name); 01265 if (host_name == NULL) { 01266 LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,name); 01267 return NULL; 01268 } 01269 01270 return open_directoryw(host_name); 01271 } 01272 01273 void localDrive::closedir(void *handle) { 01274 close_directory((dir_information*)handle); 01275 } 01276 01277 bool localDrive::read_directory_first(void *handle, char* entry_name, char* entry_sname, bool& is_directory) { 01278 host_cnv_char_t tmp[MAX_PATH+1], stmp[MAX_PATH+1]; 01279 01280 if (::read_directory_firstw((dir_information*)handle, tmp, stmp, is_directory)) { 01281 // guest to host code page translation 01282 const char* n_stemp_name = CodePageHostToGuest(stmp); 01283 if (n_stemp_name == NULL) { 01284 #ifdef host_cnv_use_wchar 01285 LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,stmp); 01286 #else 01287 LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,stmp); 01288 #endif 01289 return false; 01290 } 01291 { 01292 const char* n_temp_name = CodePageHostToGuestL(tmp); 01293 if (n_temp_name == NULL) { 01294 #ifdef host_cnv_use_wchar 01295 LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp); 01296 #else 01297 LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp); 01298 #endif 01299 strcpy(entry_name,n_stemp_name); 01300 } else { 01301 strcpy(entry_name,n_temp_name); 01302 } 01303 } 01304 strcpy(entry_sname,n_stemp_name); 01305 return true; 01306 } 01307 01308 return false; 01309 } 01310 01311 bool localDrive::read_directory_next(void *handle, char* entry_name, char* entry_sname, bool& is_directory) { 01312 host_cnv_char_t tmp[MAX_PATH+1], stmp[MAX_PATH+1]; 01313 01314 next: 01315 if (::read_directory_nextw((dir_information*)handle, tmp, stmp, is_directory)) { 01316 // guest to host code page translation 01317 const char* n_stemp_name = CodePageHostToGuest(stmp); 01318 if (n_stemp_name == NULL) { 01319 #ifdef host_cnv_use_wchar 01320 LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,stmp); 01321 #else 01322 LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,stmp); 01323 #endif 01324 goto next; 01325 } 01326 { 01327 const char* n_temp_name = CodePageHostToGuestL(tmp); 01328 if (n_temp_name == NULL) { 01329 #ifdef host_cnv_use_wchar 01330 LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp); 01331 #else 01332 LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp); 01333 #endif 01334 strcpy(entry_name,n_stemp_name); 01335 } else { 01336 strcpy(entry_name,n_temp_name); 01337 } 01338 } 01339 strcpy(entry_sname,n_stemp_name); 01340 return true; 01341 } 01342 01343 return false; 01344 } 01345 01346 localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, std::vector<std::string> &options) { 01347 strcpy(basedir,startdir); 01348 sprintf(info,"local directory %s",startdir); 01349 allocation.bytes_sector=_bytes_sector; 01350 allocation.sectors_cluster=_sectors_cluster; 01351 allocation.total_clusters=_total_clusters; 01352 allocation.free_clusters=_free_clusters; 01353 allocation.mediaid=_mediaid; 01354 01355 for (const auto &opt : options) { 01356 size_t equ = opt.find_first_of('='); 01357 std::string name,value; 01358 01359 if (equ != std::string::npos) { 01360 name = opt.substr(0,equ); 01361 value = opt.substr(equ+1); 01362 } 01363 else { 01364 name = opt; 01365 value.clear(); 01366 } 01367 01368 for (char & c: name) c=tolower(c); 01369 if (name == "remote") 01370 remote = 1; 01371 else if (name == "local") 01372 remote = 0; 01373 } 01374 01375 dirCache.SetBaseDir(basedir,this); 01376 } 01377 01378 01379 //TODO Maybe use fflush, but that seemed to fuck up in visual c 01380 bool localFile::Read(Bit8u * data,Bit16u * size) { 01381 if ((this->flags & 0xf) == OPEN_WRITE) { // check if file opened in write-only mode 01382 DOS_SetError(DOSERR_ACCESS_DENIED); 01383 return false; 01384 } 01385 if (last_action==WRITE) fseek(fhandle,ftell(fhandle),SEEK_SET); 01386 last_action=READ; 01387 *size=(Bit16u)fread(data,1,*size,fhandle); 01388 /* Fake harddrive motion. Inspector Gadget with soundblaster compatible */ 01389 /* Same for Igor */ 01390 /* hardrive motion => unmask irq 2. Only do it when it's masked as unmasking is realitively heavy to emulate */ 01391 if (!IS_PC98_ARCH) { 01392 Bit8u mask = IO_Read(0x21); 01393 if(mask & 0x4 ) IO_Write(0x21,mask&0xfb); 01394 } 01395 01396 return true; 01397 } 01398 01399 bool localFile::Write(const Bit8u * data,Bit16u * size) { 01400 Bit32u lastflags = this->flags & 0xf; 01401 if (lastflags == OPEN_READ || lastflags == OPEN_READ_NO_MOD) { // check if file opened in read-only mode 01402 DOS_SetError(DOSERR_ACCESS_DENIED); 01403 return false; 01404 } 01405 if (last_action==READ) fseek(fhandle,ftell(fhandle),SEEK_SET); 01406 last_action=WRITE; 01407 if(*size==0){ 01408 return (!ftruncate(fileno(fhandle),ftell(fhandle))); 01409 } 01410 else 01411 { 01412 *size=(Bit16u)fwrite(data,1,*size,fhandle); 01413 return true; 01414 } 01415 } 01416 01417 /* ert, 20100711: Locking extensions */ 01418 #ifdef WIN32 01419 #include <sys/locking.h> 01420 bool localFile::LockFile(Bit8u mode, Bit32u pos, Bit16u size) { 01421 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fhandle)); 01422 BOOL bRet; 01423 01424 switch (mode) 01425 { 01426 case 0: bRet = ::LockFile (hFile, pos, 0, size, 0); break; 01427 case 1: bRet = ::UnlockFile(hFile, pos, 0, size, 0); break; 01428 default: 01429 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 01430 return false; 01431 } 01432 //LOG_MSG("::LockFile %s", name); 01433 01434 if (!bRet) 01435 { 01436 switch (GetLastError()) 01437 { 01438 case ERROR_ACCESS_DENIED: 01439 case ERROR_LOCK_VIOLATION: 01440 case ERROR_NETWORK_ACCESS_DENIED: 01441 case ERROR_DRIVE_LOCKED: 01442 case ERROR_SEEK_ON_DEVICE: 01443 case ERROR_NOT_LOCKED: 01444 case ERROR_LOCK_FAILED: 01445 DOS_SetError(0x21); 01446 break; 01447 case ERROR_INVALID_HANDLE: 01448 DOS_SetError(DOSERR_INVALID_HANDLE); 01449 break; 01450 case ERROR_INVALID_FUNCTION: 01451 default: 01452 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 01453 break; 01454 } 01455 } 01456 return bRet; 01457 } 01458 #endif 01459 01460 bool localFile::Seek(Bit32u * pos,Bit32u type) { 01461 int seektype; 01462 switch (type) { 01463 case DOS_SEEK_SET:seektype=SEEK_SET;break; 01464 case DOS_SEEK_CUR:seektype=SEEK_CUR;break; 01465 case DOS_SEEK_END:seektype=SEEK_END;break; 01466 default: 01467 //TODO Give some doserrorcode; 01468 return false;//ERROR 01469 } 01470 int ret=fseek(fhandle,*reinterpret_cast<Bit32s*>(pos),seektype); 01471 if (ret!=0) { 01472 // Out of file range, pretend everythings ok 01473 // and move file pointer top end of file... ?! (Black Thorne) 01474 fseek(fhandle,0,SEEK_END); 01475 } 01476 #if 0 01477 fpos_t temppos; 01478 fgetpos(fhandle,&temppos); 01479 Bit32u * fake_pos=(Bit32u*)&temppos; 01480 *pos=*fake_pos; 01481 #endif 01482 *pos=(Bit32u)ftell(fhandle); 01483 last_action=NONE; 01484 return true; 01485 } 01486 01487 bool localFile::Close() { 01488 if (newtime && fhandle) { 01489 // force STDIO to flush buffers on this file handle, or else fclose() will write buffered data 01490 // and cause mtime to reset back to current time. 01491 fflush(fhandle); 01492 01493 // backport from DOS_PackDate() and DOS_PackTime() 01494 struct tm tim = { 0 }; 01495 tim.tm_sec = (time&0x1f)*2; 01496 tim.tm_min = (time>>5)&0x3f; 01497 tim.tm_hour = (time>>11)&0x1f; 01498 tim.tm_mday = date&0x1f; 01499 tim.tm_mon = ((date>>5)&0x0f)-1; 01500 tim.tm_year = (date>>9)+1980-1900; 01501 // sanitize the date in case of invalid timestamps (such as 0x0000 date/time fields) 01502 if (tim.tm_mon < 0) tim.tm_mon = 0; 01503 if (tim.tm_mday == 0) tim.tm_mday = 1; 01504 // have the C run-time library code compute whether standard time or daylight saving time is in effect. 01505 tim.tm_isdst = -1; 01506 // serialize time 01507 mktime(&tim); 01508 01509 // change file time by file handle (while we still have it open) 01510 // so that we do not have to duplicate guest to host filename conversion here. 01511 // This should help Yksoft1 with file date/time, PC-98, and Shift-JIS Japanese filenames as well on Windows. 01512 01513 #if defined(WIN32) /* TODO: What about MinGW? */ 01514 struct _utimbuf ftim; 01515 ftim.actime = ftim.modtime = mktime(&tim); 01516 01517 if (_futime(fileno(fhandle), &ftim)) { 01518 extern int errno; 01519 LOG_MSG("Set time failed (%s)", strerror(errno)); 01520 } 01521 #elif !defined(RISCOS) // Linux (TODO: What about Mac OS X/Darwin?) 01522 // NTS: Do not attempt futime, Linux doesn't have it. 01523 // Do not attempt futimes, Linux man pages LIE about having it. It's even there in the freaking header, but not recognized! 01524 // Use futimens. Modern stuff should have it. [https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html] 01525 struct timespec ftsp[2]; 01526 ftsp[0].tv_sec = ftsp[1].tv_sec = mktime(&tim); 01527 ftsp[0].tv_nsec = ftsp[1].tv_nsec = 0; 01528 01529 if (futimens(fileno(fhandle), ftsp)) { 01530 extern int errno; 01531 LOG_MSG("Set time failed (%s)", strerror(errno)); 01532 } 01533 #endif 01534 } 01535 01536 // only close if one reference left 01537 if (refCtr==1) { 01538 if(fhandle) fclose(fhandle); 01539 fhandle = 0; 01540 open = false; 01541 } 01542 01543 return true; 01544 } 01545 01546 Bit16u localFile::GetInformation(void) { 01547 return read_only_medium?0x40:0; 01548 } 01549 01550 01551 Bit32u localFile::GetSeekPos() { 01552 return (Bit32u)ftell( fhandle ); 01553 } 01554 01555 localFile::localFile() {} 01556 01557 localFile::localFile(const char* _name, FILE * handle) { 01558 fhandle=handle; 01559 open=true; 01560 localFile::UpdateDateTimeFromHost(); 01561 01562 attr=DOS_ATTR_ARCHIVE; 01563 last_action=NONE; 01564 read_only_medium=false; 01565 01566 name=0; 01567 SetName(_name); 01568 } 01569 01570 void localFile::FlagReadOnlyMedium(void) { 01571 read_only_medium = true; 01572 } 01573 01574 bool localFile::UpdateDateTimeFromHost(void) { 01575 if(!open) return false; 01576 struct stat temp_stat; 01577 fstat(fileno(fhandle),&temp_stat); 01578 const struct tm* ltime; 01579 if((ltime=localtime(&temp_stat.st_mtime))!=0) { 01580 time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); 01581 date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); 01582 } else { 01583 time=1;date=1; 01584 } 01585 return true; 01586 } 01587 01588 void localFile::Flush(void) { 01589 if (last_action==WRITE) { 01590 fseek(fhandle,ftell(fhandle),SEEK_SET); 01591 fflush(fhandle); 01592 last_action=NONE; 01593 } 01594 } 01595 01596 01597 // ******************************************** 01598 // CDROM DRIVE 01599 // ******************************************** 01600 01601 int MSCDEX_RemoveDrive(char driveLetter); 01602 int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit); 01603 bool MSCDEX_HasMediaChanged(Bit8u subUnit); 01604 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name); 01605 01606 01607 cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error, std::vector<std::string> &options) 01608 :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid,options), 01609 subUnit(0), 01610 driveLetter('\0') 01611 { 01612 // Init mscdex 01613 error = MSCDEX_AddDrive(driveLetter,startdir,subUnit); 01614 strcpy(info, "CDRom "); 01615 strcat(info, startdir); 01616 this->driveLetter = driveLetter; 01617 // Get Volume Label 01618 char name[32]; 01619 if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); 01620 } 01621 01622 bool cdromDrive::FileOpen(DOS_File * * file,const char * name,Bit32u flags) { 01623 if ((flags&0xf)==OPEN_READWRITE) { 01624 flags &= ~((unsigned int)OPEN_READWRITE); 01625 } else if ((flags&0xf)==OPEN_WRITE) { 01626 DOS_SetError(DOSERR_ACCESS_DENIED); 01627 return false; 01628 } 01629 bool retcode = localDrive::FileOpen(file,name,flags); 01630 if(retcode) (dynamic_cast<localFile*>(*file))->FlagReadOnlyMedium(); 01631 return retcode; 01632 } 01633 01634 bool cdromDrive::FileCreate(DOS_File * * /*file*/,const char * /*name*/,Bit16u /*attributes*/) { 01635 DOS_SetError(DOSERR_ACCESS_DENIED); 01636 return false; 01637 } 01638 01639 bool cdromDrive::FileUnlink(const char * /*name*/) { 01640 DOS_SetError(DOSERR_ACCESS_DENIED); 01641 return false; 01642 } 01643 01644 bool cdromDrive::RemoveDir(const char * /*dir*/) { 01645 DOS_SetError(DOSERR_ACCESS_DENIED); 01646 return false; 01647 } 01648 01649 bool cdromDrive::MakeDir(const char * /*dir*/) { 01650 DOS_SetError(DOSERR_ACCESS_DENIED); 01651 return false; 01652 } 01653 01654 bool cdromDrive::Rename(const char * /*oldname*/,const char * /*newname*/) { 01655 DOS_SetError(DOSERR_ACCESS_DENIED); 01656 return false; 01657 } 01658 01659 bool cdromDrive::GetFileAttr(const char * name,Bit16u * attr) { 01660 bool result = localDrive::GetFileAttr(name,attr); 01661 if (result) *attr |= DOS_ATTR_READ_ONLY; 01662 return result; 01663 } 01664 01665 bool cdromDrive::GetFileAttrEx(char* name, struct stat *status) { 01666 return localDrive::GetFileAttrEx(name,status); 01667 } 01668 01669 unsigned long cdromDrive::GetCompressedSize(char* name) { 01670 return localDrive::GetCompressedSize(name); 01671 } 01672 01673 #if defined (WIN32) 01674 HANDLE cdromDrive::CreateOpenFile(const char* name) { 01675 return localDrive::CreateOpenFile(name); 01676 } 01677 unsigned long cdromDrive::GetSerial() { 01678 return localDrive::GetSerial(); 01679 } 01680 #endif 01681 01682 bool cdromDrive::FindFirst(const char * _dir,DOS_DTA & dta,bool /*fcb_findfirst*/) { 01683 // If media has changed, reInit drivecache. 01684 if (MSCDEX_HasMediaChanged(subUnit)) { 01685 dirCache.EmptyCache(); 01686 // Get Volume Label 01687 char name[32]; 01688 if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); 01689 } 01690 return localDrive::FindFirst(_dir,dta); 01691 } 01692 01693 void cdromDrive::SetDir(const char* path) { 01694 // If media has changed, reInit drivecache. 01695 if (MSCDEX_HasMediaChanged(subUnit)) { 01696 dirCache.EmptyCache(); 01697 // Get Volume Label 01698 char name[32]; 01699 if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true); 01700 } 01701 localDrive::SetDir(path); 01702 } 01703 01704 bool cdromDrive::isRemote(void) { 01705 return localDrive::isRemote(); 01706 } 01707 01708 bool cdromDrive::isRemovable(void) { 01709 return true; 01710 } 01711 01712 Bits cdromDrive::UnMount(void) { 01713 if(MSCDEX_RemoveDrive(driveLetter)) { 01714 delete this; 01715 return 0; 01716 } 01717 return 2; 01718 } 01719 01720 #define OVERLAY_DIR 1 01721 bool logoverlay = false; 01722 using namespace std; 01723 01724 #if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/ 01725 #define CROSS_DOSFILENAME(blah) 01726 #else 01727 //Convert back to DOS PATH 01728 #define CROSS_DOSFILENAME(blah) strreplace(blah,'/','\\') 01729 #endif 01730 01731 char* GetCrossedName(const char *basedir, const char *dir) { 01732 static char crossname[CROSS_LEN]; 01733 strcpy(crossname, basedir); 01734 strcat(crossname, dir); 01735 CROSS_FILENAME(crossname); 01736 return crossname; 01737 } 01738 01739 /* 01740 * Wengier: Long filenames are supported in all including overlay drives. 01741 * Shift-JIS characters (Kana, Kanji, etc) are also supported in PC-98 mode. 01742 * 01743 * New rename for base directories (not yet supported): 01744 * Alter shortname in the drive_cache: take care of order and long names. 01745 * update stored deleted files list in overlay. 01746 */ 01747 01748 //TODO recheck directories under linux with the filename_cache (as one adds the dos name (and runs cross_filename on the other)) 01749 01750 01751 //TODO Check: Maybe handle file redirection in ccc (opening the new file), (call update datetime host there ?) 01752 01753 01754 /* For rename/delete(unlink)/makedir/removedir we need to rebuild the cache. (shouldn't be needed, 01755 * but cacheout/delete entry currently throw away the cached folder and rebuild it on read. 01756 * so we have to ensure the rebuilding is controlled through the overlay. 01757 * In order to not reread the overlay directory contents, the information in there is cached and updated when 01758 * it changes (when deleting a file or adding one) 01759 */ 01760 01761 01762 //directories that exist only in overlay can not be added to the drive_cache currently. 01763 //Either upgrade addentry to support directories. (without actually caching stuff in! (code in testing)) 01764 //Or create an empty directory in local drive base. 01765 01766 bool Overlay_Drive::RemoveDir(const char * dir) { 01767 if (ovlnocachedir) { 01768 dirCache.EmptyCache(); 01769 update_cache(true); 01770 } 01771 01772 if (ovlreadonly) { 01773 DOS_SetError(DOSERR_WRITE_PROTECTED); 01774 return false; 01775 } 01776 01777 //DOS_RemoveDir checks if directory exists. 01778 #if OVERLAY_DIR 01779 if (logoverlay) LOG_MSG("Overlay: trying to remove directory: %s",dir); 01780 #else 01781 E_Exit("Overlay: trying to remove directory: %s",dir); 01782 #endif 01783 /* Overlay: Check if folder is empty (findfirst/next, skipping . and .. and breaking on first file found ?), if so, then it is not too tricky. */ 01784 if (is_dir_only_in_overlay(dir)) { 01785 //The simple case 01786 char sdir[CROSS_LEN],odir[CROSS_LEN]; 01787 strcpy(sdir,dir); 01788 strcpy(odir,overlaydir); 01789 strcat(odir,sdir); 01790 CROSS_FILENAME(odir); 01791 const host_cnv_char_t* host_name = CodePageGuestToHost(odir); 01792 int temp=-1; 01793 if (host_name!=NULL) { 01794 #if defined (WIN32) 01795 temp=_wrmdir(host_name); 01796 #else 01797 temp=rmdir(host_name); 01798 #endif 01799 } 01800 if (temp) { 01801 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,dir)); 01802 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 01803 strcpy(sdir,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 01804 strcpy(odir,overlaydir); 01805 strcat(odir,sdir); 01806 CROSS_FILENAME(odir); 01807 host_name = CodePageGuestToHost(odir); 01808 if (host_name!=NULL) { 01809 #if defined (WIN32) 01810 temp=_wrmdir(host_name); 01811 #else 01812 temp=rmdir(host_name); 01813 #endif 01814 } 01815 } 01816 } 01817 if (temp == 0) { 01818 remove_DOSdir_from_cache(dir); 01819 char newdir[CROSS_LEN]; 01820 strcpy(newdir,basedir); 01821 strcat(newdir,sdir); 01822 CROSS_FILENAME(newdir); 01823 dirCache.DeleteEntry(newdir,true); 01824 dirCache.EmptyCache(); 01825 update_cache(false); 01826 } 01827 return (temp == 0); 01828 } else { 01829 Bit16u olderror = dos.errorcode; //FindFirst/Next always set an errorcode, while RemoveDir itself shouldn't touch it if successful 01830 DOS_DTA dta(dos.tables.tempdta); 01831 char stardotstar[4] = {'*', '.', '*', 0}; 01832 dta.SetupSearch(0,(0xff & ~DOS_ATTR_VOLUME),stardotstar); //Fake drive as we don't use it. 01833 bool ret = this->FindFirst(dir,dta,false);// DOS_FindFirst(args,0xffff & ~DOS_ATTR_VOLUME); 01834 if (!ret) { 01835 //Path not found. Should not be possible due to removedir doing a testdir, but lets be correct 01836 DOS_SetError(DOSERR_PATH_NOT_FOUND); 01837 return false; 01838 } 01839 bool empty = true; 01840 do { 01841 char name[DOS_NAMELENGTH_ASCII],lname[LFN_NAMELENGTH];Bit32u size;Bit16u date;Bit16u time;Bit8u attr; 01842 dta.GetResult(name,lname,size,date,time,attr); 01843 if (logoverlay) LOG_MSG("RemoveDir found %s",name); 01844 if (empty && strcmp(".",name ) && strcmp("..",name)) 01845 empty = false; //Neither . or .. so directory not empty. 01846 } while ( (ret=this->FindNext(dta)) ); 01847 //Always exhaust list, so drive_cache entry gets invalidated/reused. 01848 //FindNext is done, restore error code to old value. DOS_RemoveDir will set the right one if needed. 01849 dos.errorcode = olderror; 01850 01851 if (!empty) return false; 01852 if (logoverlay) LOG_MSG("directory empty! Hide it."); 01853 //Directory is empty, mark it as deleted and create $DBOVERLAY file. 01854 //Ensure that overlap folder can not be created. 01855 char odir[CROSS_LEN]; 01856 strcpy(odir,overlaydir); 01857 strcat(odir,dir); 01858 CROSS_FILENAME(odir); 01859 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,dir)); 01860 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 01861 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 01862 strcpy(odir,overlaydir); 01863 strcat(odir,temp_name); 01864 CROSS_FILENAME(odir); 01865 rmdir(odir); 01866 } 01867 add_deleted_path(dir,true); 01868 return true; 01869 } 01870 } 01871 01872 bool Overlay_Drive::MakeDir(const char * dir) { 01873 if (ovlnocachedir) { 01874 dirCache.EmptyCache(); 01875 update_cache(true); 01876 } 01877 01878 if (ovlreadonly) { 01879 DOS_SetError(DOSERR_WRITE_PROTECTED); 01880 return false; 01881 } 01882 //DOS_MakeDir tries first, before checking if the directory already exists, so doing it here as well, so that case is handled. 01883 if (TestDir(dir)) return false; 01884 if (overlap_folder == dir) return false; //TODO Test 01885 #if OVERLAY_DIR 01886 if (logoverlay) LOG_MSG("Overlay trying to make directory: %s",dir); 01887 #else 01888 E_Exit("Overlay trying to make directory: %s",dir); 01889 #endif 01890 /* Overlay: Create in Overlay only and add it to drive_cache + some entries else the drive_cache will try to access it. Needs an AddEntry for directories. */ 01891 01892 //Check if leading dir is marked as deleted. 01893 if (check_if_leading_is_deleted(dir)) return false; 01894 01895 //Check if directory itself is marked as deleted 01896 if (is_deleted_path(dir) && localDrive::TestDir(dir)) { 01897 //Was deleted before and exists (last one is safety check) 01898 remove_deleted_path(dir,true); 01899 return true; 01900 } 01901 char newdir[CROSS_LEN],sdir[CROSS_LEN],pdir[CROSS_LEN]; 01902 strcpy(sdir,dir); 01903 char *p=strrchr(sdir,'\\'); 01904 if (p!=NULL) { 01905 *p=0; 01906 char *temp_name=dirCache.GetExpandName(GetCrossedName(basedir,sdir)); 01907 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 01908 strcpy(newdir,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 01909 strcat(newdir,"\\"); 01910 strcat(newdir,p+1); 01911 strcpy(sdir,newdir); 01912 } 01913 } 01914 strcpy(newdir,overlaydir); 01915 strcat(newdir,sdir); 01916 CROSS_FILENAME(newdir); 01917 p=strrchr(sdir,'\\'); 01918 int temp=-1; 01919 bool madepdir=false; 01920 const host_cnv_char_t* host_name; 01921 if (p!=NULL) { 01922 *p=0; 01923 if (strlen(sdir)) { 01924 strcpy(pdir,overlaydir); 01925 strcat(pdir,sdir); 01926 CROSS_FILENAME(pdir); 01927 if (!is_deleted_path(sdir) && localDrive::TestDir(sdir)) { 01928 host_name = CodePageGuestToHost(pdir); 01929 if (host_name!=NULL) { 01930 #if defined (WIN32) 01931 temp=_wmkdir(host_name); 01932 #else 01933 temp=mkdir(host_name,0775); 01934 #endif 01935 if (temp==0) madepdir=true; 01936 } 01937 } 01938 } 01939 } 01940 temp=-1; 01941 host_name = CodePageGuestToHost(newdir); 01942 if (host_name!=NULL) { 01943 #if defined (WIN32) 01944 temp=_wmkdir(host_name); 01945 #else 01946 temp=mkdir(host_name,0775); 01947 #endif 01948 } 01949 if (temp==0) { 01950 char fakename[CROSS_LEN]; 01951 strcpy(fakename,basedir); 01952 strcat(fakename,dir); 01953 CROSS_FILENAME(fakename); 01954 strcpy(sdir, dir); 01955 upcase(sdir); 01956 dirCache.AddEntryDirOverlay(fakename,sdir,true); 01957 add_DOSdir_to_cache(dir,sdir); 01958 dirCache.EmptyCache(); 01959 update_cache(true); 01960 } else if (madepdir) { 01961 host_name = CodePageGuestToHost(pdir); 01962 if (host_name!=NULL) { 01963 #if defined (WIN32) 01964 _wrmdir(host_name); 01965 #else 01966 rmdir(host_name); 01967 #endif 01968 } 01969 } 01970 01971 return (temp == 0);// || ((temp!=0) && (errno==EEXIST)); 01972 } 01973 01974 bool Overlay_Drive::TestDir(const char * dir) { 01975 if (ovlnocachedir) { 01976 dirCache.EmptyCache(); 01977 update_cache(true); 01978 } 01979 01980 //First check if directory exist exclusively in the overlay. 01981 //Currently using the update_cache cache, alternatively access the directory itself. 01982 01983 //Directories are stored without a trailing backslash 01984 char tempdir[CROSS_LEN]; 01985 strcpy(tempdir,dir); 01986 size_t templen = strlen(dir); 01987 if (templen && tempdir[templen-1] == '\\') tempdir[templen-1] = 0; 01988 01989 #if OVERLAY_DIR 01990 if (is_dir_only_in_overlay(tempdir)) return true; 01991 #endif 01992 01993 //Next Check if the directory is marked as deleted or one of its leading directories is. 01994 //(it still might exists in the localDrive) 01995 01996 if (is_deleted_path(tempdir)) return false; 01997 01998 // Not exclusive to overlay nor marked as deleted. Pass on to LocalDrive 01999 return localDrive::TestDir(dir); 02000 } 02001 02002 02003 class OverlayFile: public localFile { 02004 public: 02005 OverlayFile(const char* name, FILE * handle):localFile(name,handle){ 02006 overlay_active = false; 02007 if (logoverlay) LOG_MSG("constructing OverlayFile: %s",name); 02008 } 02009 bool Write(const Bit8u * data,Bit16u * size) { 02010 Bit32u f = flags&0xf; 02011 if (!overlay_active && (f == OPEN_READWRITE || f == OPEN_WRITE)) { 02012 if (logoverlay) LOG_MSG("write detected, switching file for %s",GetName()); 02013 if (*data == 0) { 02014 if (logoverlay) LOG_MSG("OPTIMISE: truncate on switch!!!!"); 02015 } 02016 Bit32u a = GetTicks(); 02017 bool r = create_copy(); 02018 if (GetTicks() - a > 2) { 02019 if (logoverlay) LOG_MSG("OPTIMISE: switching took %d",GetTicks() - a); 02020 } 02021 if (!r) return false; 02022 overlay_active = true; 02023 02024 } 02025 return localFile::Write(data,size); 02026 } 02027 bool create_copy(); 02028 //private: 02029 bool overlay_active; 02030 }; 02031 02032 //Create leading directories of a file being overlayed if they exist in the original (localDrive). 02033 //This function is used to create copies of existing files, so all leading directories exist in the original. 02034 02035 FILE* Overlay_Drive::create_file_in_overlay(const char* dos_filename, char const* mode) { 02036 char newname[CROSS_LEN]; 02037 strcpy(newname,overlaydir); //TODO GOG make part of class and join in 02038 strcat(newname,dos_filename); //HERE we need to convert it to Linux TODO 02039 CROSS_FILENAME(newname); 02040 02041 #ifdef host_cnv_use_wchar 02042 wchar_t wmode[8]; 02043 unsigned int tis; 02044 for (tis=0;tis < 7 && mode[tis] != 0;tis++) wmode[tis] = (wchar_t)mode[tis]; 02045 assert(tis < 7); // guard 02046 wmode[tis] = 0; 02047 #endif 02048 FILE* f; 02049 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 02050 if (host_name!=NULL) { 02051 #ifdef host_cnv_use_wchar 02052 f = _wfopen(host_name,wmode); 02053 #else 02054 f = fopen_wrap(host_name,mode); 02055 #endif 02056 } 02057 else { 02058 f = fopen_wrap(newname,mode); 02059 } 02060 //Check if a directories are part of the name: 02061 char* dir = strrchr((char *)dos_filename,'\\'); 02062 if (!f && dir && *dir) { 02063 if (logoverlay) LOG_MSG("Overlay: warning creating a file inside a directory %s",dos_filename); 02064 //ensure they exist, else make them in the overlay if they exist in the original.... 02065 Sync_leading_dirs(dos_filename); 02066 //try again 02067 char temp_name[CROSS_LEN],tmp[CROSS_LEN]; 02068 strcpy(tmp, dos_filename); 02069 char *p=strrchr(tmp, '\\'); 02070 assert(p!=NULL); 02071 *p=0; 02072 bool found=false; 02073 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it+=2) 02074 if ((*(it+1)).length()&&!strcasecmp((*(it+1)).c_str(), tmp)) { 02075 found=true; 02076 strcpy(tmp, (*it).c_str()); 02077 break; 02078 } 02079 if (found) { 02080 strcpy(temp_name,overlaydir); 02081 strcat(temp_name,tmp); 02082 strcat(temp_name,dir); 02083 CROSS_FILENAME(temp_name); 02084 const host_cnv_char_t* host_name = CodePageGuestToHost(temp_name); 02085 if (host_name!=NULL) { 02086 #ifdef host_cnv_use_wchar 02087 f = _wfopen(host_name,wmode); 02088 #else 02089 f = fopen_wrap(host_name,mode); 02090 #endif 02091 } 02092 else { 02093 f = fopen_wrap(temp_name,mode); 02094 } 02095 } 02096 if (!f) { 02097 strcpy(temp_name,dirCache.GetExpandName(GetCrossedName(basedir,dos_filename))); 02098 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 02099 strcpy(newname,overlaydir); 02100 strcat(newname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 02101 CROSS_FILENAME(newname); 02102 } 02103 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 02104 if (host_name!=NULL) { 02105 #ifdef host_cnv_use_wchar 02106 f = _wfopen(host_name,wmode); 02107 #else 02108 f = fopen_wrap(host_name,mode); 02109 #endif 02110 } 02111 else { 02112 f = fopen_wrap(newname,mode); 02113 } 02114 } 02115 } 02116 02117 return f; 02118 } 02119 02120 #ifndef BUFSIZ 02121 #define BUFSIZ 2048 02122 #endif 02123 02124 bool OverlayFile::create_copy() { 02125 //test if open/valid/etc 02126 //ensure file position 02127 FILE* lhandle = this->fhandle; 02128 fseek(lhandle,ftell(lhandle),SEEK_SET); 02129 int location_in_old_file = ftell(lhandle); 02130 fseek(lhandle,0L,SEEK_SET); 02131 02132 FILE* newhandle = NULL; 02133 Bit8u drive_set = GetDrive(); 02134 if (drive_set != 0xff && drive_set < DOS_DRIVES && Drives[drive_set]){ 02135 Overlay_Drive* od = dynamic_cast<Overlay_Drive*>(Drives[drive_set]); 02136 if (od) { 02137 newhandle = od->create_file_in_overlay(GetName(),"wb+"); //todo check wb+ 02138 } 02139 } 02140 02141 if (!newhandle) return false; 02142 char buffer[BUFSIZ]; 02143 size_t s; 02144 while ( (s = fread(buffer,1,BUFSIZ,lhandle)) != 0 ) fwrite(buffer, 1, s, newhandle); 02145 fclose(lhandle); 02146 //Set copied file handle to position of the old one 02147 fseek(newhandle,location_in_old_file,SEEK_SET); 02148 this->fhandle = newhandle; 02149 //Flags ? 02150 return true; 02151 } 02152 02153 02154 02155 static OverlayFile* ccc(DOS_File* file) { 02156 localFile* l = dynamic_cast<localFile*>(file); 02157 if (!l) E_Exit("overlay input file is not a localFile"); 02158 //Create an overlayFile 02159 OverlayFile* ret = new OverlayFile(l->GetName(),l->fhandle); 02160 ret->flags = l->flags; 02161 ret->refCtr = l->refCtr; 02162 delete l; 02163 return ret; 02164 } 02165 02166 Overlay_Drive::Overlay_Drive(const char * startdir,const char* overlay, Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid,Bit8u &error,std::vector<std::string> &options) 02167 :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid,options),special_prefix("$DBOVERLAY") { 02168 optimize_cache_v1 = true; //Try to not reread overlay files on deletes. Ideally drive_cache should be improved to handle deletes properly. 02169 //Currently this flag does nothing, as the current behavior is to not reread due to caching everything. 02170 #if defined (WIN32) 02171 if (strcasecmp(startdir,overlay) == 0) { 02172 #else 02173 if (strcmp(startdir,overlay) == 0) { 02174 #endif 02175 //overlay directory can not be the base directory 02176 error = 2; 02177 return; 02178 } 02179 02180 std::string s(startdir); 02181 std::string o(overlay); 02182 bool s_absolute = Cross::IsPathAbsolute(s); 02183 bool o_absolute = Cross::IsPathAbsolute(o); 02184 error = 0; 02185 if (s_absolute != o_absolute) { 02186 error = 1; 02187 return; 02188 } 02189 strcpy(overlaydir,overlay); 02190 char dirname[CROSS_LEN] = { 0 }; 02191 //Determine if overlaydir is part of the startdir. 02192 convert_overlay_to_DOSname_in_base(dirname); 02193 02194 size_t dirlen = strlen(dirname); 02195 if(dirlen && dirname[dirlen - 1] == '\\') dirname[dirlen - 1] = 0; 02196 02197 //add_deleted_path(dirname); //update_cache will add the overlap_folder 02198 overlap_folder = dirname; 02199 02200 update_cache(true); 02201 } 02202 02203 void Overlay_Drive::convert_overlay_to_DOSname_in_base(char* dirname ) 02204 { 02205 dirname[0] = 0;//ensure good return string 02206 if (strlen(overlaydir) >= strlen(basedir) ) { 02207 //Needs to be longer at least. 02208 #if defined (WIN32) 02209 //OS2 ? 02210 if (strncasecmp(overlaydir,basedir,strlen(basedir)) == 0) { 02211 #else 02212 if (strncmp(overlaydir,basedir,strlen(basedir)) == 0) { 02213 #endif 02214 //Beginning is the same. 02215 char t[CROSS_LEN]; 02216 strcpy(t,overlaydir+strlen(basedir)); 02217 02218 char* p = t; 02219 char* b = t; 02220 02221 while ( (p =strchr(p,CROSS_FILESPLIT)) ) { 02222 char directoryname[CROSS_LEN]={0}; 02223 char dosboxdirname[CROSS_LEN]={0}; 02224 strcpy(directoryname,dirname); 02225 strncat(directoryname,b,p-b); 02226 02227 char d[CROSS_LEN]; 02228 strcpy(d,basedir); 02229 strcat(d,directoryname); 02230 CROSS_FILENAME(d); 02231 //Try to find the corresponding directoryname in DOSBox. 02232 if(!dirCache.GetShortName(d,dosboxdirname) ) { 02233 //Not a long name, assume it is a short name instead 02234 strncpy(dosboxdirname,b,p-b); 02235 upcase(dosboxdirname); 02236 } 02237 02238 02239 strcat(dirname,dosboxdirname); 02240 strcat(dirname,"\\"); 02241 02242 if (logoverlay) LOG_MSG("HIDE directory: %s",dirname); 02243 02244 b=++p; 02245 02246 } 02247 } 02248 } 02249 } 02250 02251 bool Overlay_Drive::FileOpen(DOS_File * * file,const char * name,Bit32u flags) { 02252 if (ovlnocachedir) { 02253 dirCache.EmptyCache(); 02254 update_cache(true); 02255 } 02256 02257 if (ovlreadonly) { 02258 if ((flags&0xf) == OPEN_WRITE || (flags&0xf) == OPEN_READWRITE) { 02259 DOS_SetError(DOSERR_WRITE_PROTECTED); 02260 return false; 02261 } 02262 } 02263 const host_cnv_char_t * type; 02264 switch (flags&0xf) { 02265 case OPEN_READ: type = _HT("rb"); break; 02266 case OPEN_WRITE: type = _HT("rb+"); break; 02267 case OPEN_READWRITE: type = _HT("rb+"); break; 02268 case OPEN_READ_NO_MOD: type = _HT("rb"); break; //No modification of dates. LORD4.07 uses this 02269 default: 02270 DOS_SetError(DOSERR_ACCESS_CODE_INVALID); 02271 return false; 02272 } 02273 02274 //Flush the buffer of handles for the same file. (Betrayal in Antara) 02275 Bit8u i,drive = DOS_DRIVES; 02276 localFile *lfp; 02277 for (i=0;i<DOS_DRIVES;i++) { 02278 if (Drives[i]==this) { 02279 drive=i; 02280 break; 02281 } 02282 } 02283 for (i=0;i<DOS_FILES;i++) { 02284 if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) { 02285 lfp=dynamic_cast<localFile*>(Files[i]); 02286 if (lfp) lfp->Flush(); 02287 } 02288 } 02289 02290 02291 //Todo check name first against local tree 02292 //if name exists, use that one instead! 02293 //overlay file. 02294 char newname[CROSS_LEN]; 02295 strcpy(newname,overlaydir); 02296 strcat(newname,name); 02297 CROSS_FILENAME(newname); 02298 const host_cnv_char_t* host_name = CodePageGuestToHost(newname); 02299 #ifdef host_cnv_use_wchar 02300 FILE * hand = _wfopen(host_name,type); 02301 #else 02302 FILE * hand = fopen_wrap(newname,type); 02303 #endif 02304 if (!hand) { 02305 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 02306 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 02307 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 02308 strcpy(newname,overlaydir); 02309 strcat(newname,temp_name); 02310 CROSS_FILENAME(newname); 02311 host_name = CodePageGuestToHost(newname); 02312 if (host_name != NULL) { 02313 #ifdef host_cnv_use_wchar 02314 hand = _wfopen(host_name,type); 02315 #else 02316 hand = fopen_wrap(newname,type); 02317 #endif 02318 } 02319 } 02320 } 02321 bool fileopened = false; 02322 if (hand) { 02323 if (logoverlay) LOG_MSG("overlay file opened %s",newname); 02324 *file=new localFile(name,hand); 02325 (*file)->flags=flags; 02326 fileopened = true; 02327 } else { 02328 ; //TODO error handling!!!! (maybe check if it exists and read only (should not happen with overlays) 02329 } 02330 bool overlayed = fileopened; 02331 02332 //File not present in overlay, try normal drive 02333 //TODO take care of file being marked deleted. 02334 02335 if (!fileopened && !is_deleted_file(name)) fileopened = localDrive::FileOpen(file,name, OPEN_READ); 02336 02337 02338 if (fileopened) { 02339 if (logoverlay) LOG_MSG("file opened %s",name); 02340 //Convert file to OverlayFile 02341 OverlayFile* f = ccc(*file); 02342 f->flags = flags; //ccc copies the flags of the localfile, which were not correct in this case 02343 f->overlay_active = overlayed; //No need to switch if already in overlayed. 02344 *file = f; 02345 } 02346 return fileopened; 02347 } 02348 02349 bool Overlay_Drive::FileCreate(DOS_File * * file,const char * name,Bit16u /*attributes*/) { 02350 if (ovlnocachedir) { 02351 dirCache.EmptyCache(); 02352 update_cache(true); 02353 } 02354 02355 if (ovlreadonly) { 02356 DOS_SetError(DOSERR_WRITE_PROTECTED); 02357 return false; 02358 } 02359 //TODO Check if it exists in the dirCache ? // fix addentry ? or just double check (ld and overlay) 02360 //AddEntry looks sound to me.. 02361 02362 //check if leading part of filename is a deleted directory 02363 if (check_if_leading_is_deleted(name)) return false; 02364 02365 FILE* f = create_file_in_overlay(name,"wb+"); 02366 if(!f) { 02367 if (logoverlay) LOG_MSG("File creation in overlay system failed %s",name); 02368 return false; 02369 } 02370 *file = new localFile(name,f); 02371 (*file)->flags = OPEN_READWRITE; 02372 OverlayFile* of = ccc(*file); 02373 of->overlay_active = true; 02374 of->flags = OPEN_READWRITE; 02375 *file = of; 02376 //create fake name for the drive cache 02377 char fakename[CROSS_LEN]; 02378 strcpy(fakename,basedir); 02379 strcat(fakename,name); 02380 CROSS_FILENAME(fakename); 02381 dirCache.AddEntry(fakename,true); //add it. 02382 add_DOSname_to_cache(name); 02383 remove_deleted_file(name,true); 02384 return true; 02385 } 02386 02387 void Overlay_Drive::add_DOSname_to_cache(const char* name) { 02388 for (std::vector<std::string>::const_iterator itc = DOSnames_cache.begin(); itc != DOSnames_cache.end(); ++itc){ 02389 if (!strcasecmp((*itc).c_str(), name)) return; 02390 } 02391 DOSnames_cache.push_back(name); 02392 } 02393 02394 void Overlay_Drive::remove_DOSname_from_cache(const char* name) { 02395 for (std::vector<std::string>::iterator it = DOSnames_cache.begin(); it != DOSnames_cache.end(); ++it) { 02396 if (!strcasecmp((*it).c_str(), name)) { DOSnames_cache.erase(it); return;} 02397 } 02398 02399 } 02400 02401 bool Overlay_Drive::Sync_leading_dirs(const char* dos_filename){ 02402 const char* lastdir = strrchr(dos_filename,'\\'); 02403 //If there are no directories, return success. 02404 if (!lastdir) return true; 02405 02406 const char* leaddir = dos_filename; 02407 while ( (leaddir=strchr(leaddir,'\\')) != 0) { 02408 char dirname[CROSS_LEN] = {0}; 02409 strncpy(dirname,dos_filename,leaddir-dos_filename); 02410 02411 if (logoverlay) LOG_MSG("syncdir: %s",dirname); 02412 //Test if directory exist in base. 02413 char dirnamebase[CROSS_LEN] ={0}; 02414 strcpy(dirnamebase,basedir); 02415 strcat(dirnamebase,dirname); 02416 CROSS_FILENAME(dirnamebase); 02417 struct stat basetest; 02418 if (stat(dirCache.GetExpandName(dirnamebase),&basetest) == 0 && basetest.st_mode & S_IFDIR) { 02419 if (logoverlay) LOG_MSG("base exists: %s",dirnamebase); 02420 //Directory exists in base folder. 02421 //Ensure it exists in overlay as well 02422 02423 struct stat overlaytest; 02424 char dirnameoverlay[CROSS_LEN] ={0}; 02425 strcpy(dirnameoverlay,overlaydir); 02426 strcat(dirnameoverlay,dirname); 02427 CROSS_FILENAME(dirnameoverlay); 02428 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,dirname)); 02429 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 02430 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 02431 strcpy(dirnameoverlay,overlaydir); 02432 strcat(dirnameoverlay,temp_name); 02433 CROSS_FILENAME(dirnameoverlay); 02434 } 02435 if (stat(dirnameoverlay,&overlaytest) == 0 ) { 02436 //item exist. Check if it is a folder, if not a folder =>fail! 02437 if ((overlaytest.st_mode & S_IFDIR) ==0) return false; 02438 } else { 02439 //folder does not exist, make it 02440 if (logoverlay) LOG_MSG("creating %s",dirnameoverlay); 02441 #if defined (WIN32) /* MS Visual C++ */ 02442 int temp = mkdir(dirnameoverlay); 02443 #else 02444 int temp = mkdir(dirnameoverlay,0775); 02445 #endif 02446 if (temp != 0) return false; 02447 } 02448 } 02449 leaddir = leaddir + 1; //Move to next 02450 } 02451 02452 return true; 02453 } 02454 02455 void Overlay_Drive::update_cache(bool read_directory_contents) { 02456 Bit32u a = GetTicks(); 02457 std::vector<std::string> specials; 02458 std::vector<std::string> dirnames; 02459 std::vector<std::string> filenames; 02460 if (read_directory_contents) { 02461 //Clear all lists 02462 DOSnames_cache.clear(); 02463 DOSdirs_cache.clear(); 02464 deleted_files_in_base.clear(); 02465 deleted_paths_in_base.clear(); 02466 //Ensure hiding of the folder that contains the overlay, if it is part of the base folder. 02467 add_deleted_path(overlap_folder.c_str(), false); 02468 } 02469 02470 //Needs later to support stored renames and removals of files existing in the localDrive plane. 02471 //and by taking in account if the file names are actually already renamed. 02472 //and taking in account that a file could have gotten an overlay version and then both need to be removed. 02473 // 02474 //Also what about sequences were a base file gets copied to a working save game and then removed/renamed... 02475 //copy should be safe as then the link with the original doesn't exist. 02476 //however the working safe can be rather complicated after a rename and delete.. 02477 02478 //Currently directories existing only in the overlay can not be added to drive cache: 02479 //1. possible workaround create empty directory in base. Drawback would break the no-touching-of-base. 02480 //2. double up Addentry to support directories, (and adding . and .. to the newly directory so it counts as cachedin.. and won't be recached, as otherwise 02481 // cache will realize we are faking it. 02482 //Working on solution 2. 02483 02484 //Random TODO: Does the root drive under DOS have . and .. ? 02485 02486 //This function needs to be called after any localDrive function calling cacheout/deleteentry, as those throw away directories. 02487 //either do this with a parameter stating the part that needs to be rebuild,(directory) or clear the cache by default and do it all. 02488 02489 std::vector<std::string>::iterator i; 02490 std::string::size_type const prefix_lengh = special_prefix.length(); 02491 if (read_directory_contents) { 02492 void* dirp = opendir(overlaydir); 02493 if (dirp == NULL) return; 02494 // Read complete directory 02495 char dir_name[CROSS_LEN], dir_sname[CROSS_LEN]; 02496 bool is_directory; 02497 if (read_directory_first(dirp, dir_name, dir_sname, is_directory)) { 02498 if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name); 02499 else if (is_directory) { 02500 dirnames.push_back(dir_name); 02501 if (!strlen(dir_sname)) { 02502 strcpy(dir_sname, dir_name); 02503 upcase(dir_sname); 02504 } 02505 dirnames.push_back(dir_sname); 02506 } else filenames.push_back(dir_name); 02507 while (read_directory_next(dirp, dir_name, dir_sname, is_directory)) { 02508 if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(dir_name); 02509 else if (is_directory) { 02510 dirnames.push_back(dir_name); 02511 if (!strlen(dir_sname)) { 02512 strcpy(dir_sname, dir_name); 02513 upcase(dir_sname); 02514 } 02515 dirnames.push_back(dir_sname); 02516 } else filenames.push_back(dir_name); 02517 } 02518 } 02519 closedir(dirp); 02520 //parse directories to add them. 02521 02522 for (i = dirnames.begin(); i != dirnames.end(); i+=2) { 02523 if ((*i) == ".") continue; 02524 if ((*i) == "..") continue; 02525 std::string testi(*i); 02526 std::string::size_type ll = testi.length(); 02527 //TODO: Use the dirname\. and dirname\.. for creating fake directories in the driveCache. 02528 if( ll >2 && testi[ll-1] == '.' && testi[ll-2] == CROSS_FILESPLIT) continue; 02529 if( ll >3 && testi[ll-1] == '.' && testi[ll-2] == '.' && testi[ll-3] == CROSS_FILESPLIT) continue; 02530 02531 #if OVERLAY_DIR 02532 char tdir[CROSS_LEN],sdir[CROSS_LEN]; 02533 strcpy(tdir,(*i).c_str()); 02534 strcpy(sdir,(*(i+1)).c_str()); 02535 CROSS_DOSFILENAME(tdir); 02536 bool dir_exists_in_base = localDrive::TestDir(tdir); 02537 #endif 02538 02539 char dir[CROSS_LEN]; 02540 strcpy(dir,overlaydir); 02541 strcat(dir,(*i).c_str()); 02542 char dirpush[CROSS_LEN],dirspush[CROSS_LEN]; 02543 strcpy(dirpush,(*i).c_str()); 02544 strcpy(dirspush,(*(i+1)).c_str()); 02545 if (!strlen(dirspush)) { 02546 strcpy(dirspush, dirpush); 02547 upcase(dirspush); 02548 } 02549 static char end[2] = {CROSS_FILESPLIT,0}; 02550 strcat(dirpush,end); //Linux ? 02551 strcat(dirspush,end); 02552 void* dirp = opendir(dir); 02553 if (dirp == NULL) continue; 02554 02555 #if OVERLAY_DIR 02556 //Good directory, add to DOSdirs_cache if not existing in localDrive. tested earlier to prevent problems with opendir 02557 if (!dir_exists_in_base) { 02558 if (!strlen(sdir)) { 02559 strcpy(sdir, tdir); 02560 upcase(sdir); 02561 } 02562 add_DOSdir_to_cache(tdir,sdir); 02563 } 02564 #endif 02565 02566 std::string backupi(*i); 02567 // Read complete directory 02568 char dir_name[CROSS_LEN], dir_sname[CROSS_LEN]; 02569 bool is_directory; 02570 if (read_directory_first(dirp, dir_name, dir_sname, is_directory)) { 02571 if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name); 02572 else if (is_directory) { 02573 dirnames.push_back(string(dirpush)+dir_name); 02574 if (!strlen(dir_sname)) { 02575 strcpy(dir_sname, dir_name); 02576 upcase(dir_sname); 02577 } 02578 dirnames.push_back(string(dirspush)+dir_sname); 02579 } else filenames.push_back(string(dirpush)+dir_name); 02580 while (read_directory_next(dirp, dir_name, dir_sname, is_directory)) { 02581 if ((strlen(dir_name) > prefix_lengh+5) && strncmp(dir_name,special_prefix.c_str(),prefix_lengh) == 0) specials.push_back(string(dirpush)+dir_name); 02582 else if (is_directory) { 02583 dirnames.push_back(string(dirpush)+dir_name); 02584 if (!strlen(dir_sname)) { 02585 strcpy(dir_sname, dir_name); 02586 upcase(dir_sname); 02587 } 02588 dirnames.push_back(string(dirspush)+dir_sname); 02589 } else filenames.push_back(string(dirspush)+dir_name); 02590 } 02591 } 02592 closedir(dirp); 02593 for(i = dirnames.begin(); i != dirnames.end(); i+=2) { 02594 if ( (*i) == backupi) break; //find current directory again, for the next round. 02595 } 02596 } 02597 } 02598 02599 if (read_directory_contents) { 02600 for( i = filenames.begin(); i != filenames.end(); ++i) { 02601 char dosname[CROSS_LEN]; 02602 strcpy(dosname,(*i).c_str()); 02603 //upcase(dosname); //Should not be really needed, as uppercase in the overlay is a requirement... 02604 CROSS_DOSFILENAME(dosname); 02605 if (logoverlay) LOG_MSG("update cache add dosname %s",dosname); 02606 DOSnames_cache.push_back(dosname); 02607 } 02608 } 02609 02610 #if OVERLAY_DIR 02611 for (i = DOSdirs_cache.begin(); i !=DOSdirs_cache.end(); i+=2) { 02612 char fakename[CROSS_LEN],sdir[CROSS_LEN],tmp[CROSS_LEN],*p; 02613 strcpy(fakename,basedir); 02614 strcat(fakename,(*i).c_str()); 02615 CROSS_FILENAME(fakename); 02616 strcpy(sdir,(*(i+1)).c_str()); 02617 dirCache.AddEntryDirOverlay(fakename,sdir,true); 02618 if (strlen(sdir)) { 02619 strcpy(tmp,(*(i+1)).c_str()); 02620 p=strrchr(tmp, '\\'); 02621 if (p==NULL) *(i+1)=std::string(sdir); 02622 else { 02623 *p=0; 02624 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it<i && it != DOSdirs_cache.end(); it+=2) { 02625 if (!strcasecmp((*it).c_str(), tmp)) { 02626 strcpy(tmp, (*(it+1)).c_str()); 02627 break; 02628 } 02629 } 02630 strcat(tmp,"\\"); 02631 strcat(tmp,sdir); 02632 *(i+1)=std::string(tmp); 02633 } 02634 } 02635 } 02636 #endif 02637 02638 for (i = DOSnames_cache.begin(); i != DOSnames_cache.end(); ++i) { 02639 char fakename[CROSS_LEN]; 02640 strcpy(fakename,basedir); 02641 strcat(fakename,(*i).c_str()); 02642 CROSS_FILENAME(fakename); 02643 dirCache.AddEntry(fakename,true); 02644 } 02645 02646 if (read_directory_contents) { 02647 for (i = specials.begin(); i != specials.end(); ++i) { 02648 //Specials look like this $DBOVERLAY_YYY_FILENAME.EXT or DIRNAME[\/]$DBOVERLAY_YYY_FILENAME.EXT where 02649 //YYY is the operation involved. Currently only DEL is supported. 02650 //DEL = file marked as deleted, (but exists in localDrive!) 02651 std::string name(*i); 02652 std::string special_dir(""); 02653 std::string special_file(""); 02654 std::string special_operation(""); 02655 std::string::size_type s = name.find(special_prefix); 02656 if (s == std::string::npos) continue; 02657 if (s) { 02658 special_dir = name.substr(0,s); 02659 name.erase(0,s); 02660 } 02661 name.erase(0,special_prefix.length()+1); //Erase $DBOVERLAY_ 02662 s = name.find('_'); 02663 if (s == std::string::npos || s == 0) continue; 02664 special_operation = name.substr(0,s); 02665 name.erase(0,s + 1); 02666 special_file = name; 02667 if (special_file.length() == 0) continue; 02668 if (special_operation == "DEL") { 02669 name = special_dir + special_file; 02670 //CROSS_DOSFILENAME for strings: 02671 while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\"); 02672 02673 add_deleted_file(name.c_str(),false); 02674 } else if (special_operation == "RMD") { 02675 name = special_dir + special_file; 02676 //CROSS_DOSFILENAME for strings: 02677 while ( (s = name.find('/')) != std::string::npos) name.replace(s,1,"\\"); 02678 add_deleted_path(name.c_str(),false); 02679 02680 } else { 02681 if (logoverlay) LOG_MSG("unsupported operation %s on %s",special_operation.c_str(),(*i).c_str()); 02682 } 02683 02684 } 02685 } 02686 if (logoverlay) LOG_MSG("OPTIMISE: update cache took %d",GetTicks()-a); 02687 } 02688 02689 bool Overlay_Drive::FindNext(DOS_DTA & dta) { 02690 02691 char * dir_ent, *ldir_ent; 02692 ht_stat_t stat_block; 02693 char full_name[CROSS_LEN], lfull_name[LFN_NAMELENGTH+1]; 02694 char dir_entcopy[CROSS_LEN], ldir_entcopy[CROSS_LEN]; 02695 02696 Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII]; 02697 Bit8u find_attr; 02698 02699 dta.GetSearchParams(srch_attr,srch_pattern,uselfn); 02700 Bit16u id = lfn_filefind_handle>=LFN_FILEFIND_MAX?dta.GetDirID():ldid[lfn_filefind_handle]; 02701 02702 again: 02703 if (!dirCache.FindNext(id,dir_ent,ldir_ent)) { 02704 if (lfn_filefind_handle<LFN_FILEFIND_MAX) { 02705 ldid[lfn_filefind_handle]=0; 02706 ldir[lfn_filefind_handle]=""; 02707 } 02708 DOS_SetError(DOSERR_NO_MORE_FILES); 02709 return false; 02710 } 02711 if(!WildFileCmp(dir_ent,srch_pattern)&&!LWildFileCmp(ldir_ent,srch_pattern)) goto again; 02712 02713 strcpy(full_name,lfn_filefind_handle>=LFN_FILEFIND_MAX?srchInfo[id].srch_dir:(ldir[lfn_filefind_handle]!=""?ldir[lfn_filefind_handle].c_str():"\\")); 02714 strcpy(lfull_name,full_name); 02715 02716 strcat(full_name,dir_ent); 02717 strcat(lfull_name,ldir_ent); 02718 02719 //GetExpandName might indirectly destroy dir_ent (by caching in a new directory 02720 //and due to its design dir_ent might be lost.) 02721 //Copying dir_ent first 02722 strcpy(dir_entcopy,dir_ent); 02723 strcpy(ldir_entcopy,ldir_ent); 02724 02725 //First try overlay: 02726 char ovname[CROSS_LEN]; 02727 char relativename[CROSS_LEN]; 02728 strcpy(relativename,srchInfo[id].srch_dir); 02729 //strip off basedir: //TODO cleanup 02730 strcpy(ovname,overlaydir); 02731 char* prel = lfull_name + strlen(basedir); 02732 02733 char preldos[CROSS_LEN]; 02734 strcpy(preldos,prel); 02735 CROSS_DOSFILENAME(preldos); 02736 strcat(ovname,prel); 02737 bool statok = false; 02738 const host_cnv_char_t* host_name=NULL; 02739 if (!is_deleted_file(preldos)) { 02740 host_name = CodePageGuestToHost(ovname); 02741 if (host_name != NULL) statok = ht_stat(host_name,&stat_block)==0; 02742 if (!statok) { 02743 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,prel)); 02744 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 02745 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 02746 strcpy(ovname,GetCrossedName(overlaydir,temp_name)); 02747 host_name = CodePageGuestToHost(ovname); 02748 if (host_name != NULL) statok = ht_stat(host_name,&stat_block)==0; 02749 } 02750 } 02751 } 02752 02753 if (statok) { 02754 if (logoverlay) LOG_MSG("using overlay data for %s : %s",uselfn?lfull_name:full_name, ovname); 02755 } else { 02756 strcpy(preldos,prel); 02757 CROSS_DOSFILENAME(preldos); 02758 if (is_deleted_file(preldos)) { //dir.. maybe lower or keep it as is TODO 02759 if (logoverlay) LOG_MSG("skipping deleted file %s %s %s",preldos,uselfn?lfull_name:full_name,ovname); 02760 goto again; 02761 } 02762 const host_cnv_char_t* host_name = CodePageGuestToHost(dirCache.GetExpandName(lfull_name)); 02763 if (ht_stat(host_name,&stat_block)!=0) { 02764 if (logoverlay) LOG_MSG("stat failed for %s . This should not happen.",dirCache.GetExpandName(uselfn?lfull_name:full_name)); 02765 goto again;//No symlinks and such 02766 } 02767 } 02768 02769 if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY; 02770 else find_attr=0; 02771 #if defined (WIN32) 02772 Bitu attribs = host_name==NULL?INVALID_FILE_ATTRIBUTES:GetFileAttributesW(host_name); 02773 if (attribs != INVALID_FILE_ATTRIBUTES) 02774 find_attr|=attribs&0x3f; 02775 #else 02776 find_attr|=DOS_ATTR_ARCHIVE; 02777 if(!(stat_block.st_mode & S_IWUSR)) find_attr|=DOS_ATTR_READ_ONLY; 02778 #endif 02779 if (~srch_attr & find_attr & DOS_ATTR_DIRECTORY) goto again; 02780 02781 /* file is okay, setup everything to be copied in DTA Block */ 02782 char find_name[DOS_NAMELENGTH_ASCII], lfind_name[LFN_NAMELENGTH+1]; 02783 Bit16u find_date,find_time;Bit32u find_size; 02784 02785 if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){ 02786 strcpy(find_name,dir_entcopy); 02787 upcase(find_name); 02788 } 02789 strcpy(lfind_name,ldir_entcopy); 02790 lfind_name[LFN_NAMELENGTH]=0; 02791 02792 find_size=(Bit32u) stat_block.st_size; 02793 struct tm *time; 02794 if((time=localtime(&stat_block.st_mtime))!=0){ 02795 find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); 02796 find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); 02797 } else { 02798 find_time=6; 02799 find_date=4; 02800 } 02801 dta.SetResult(find_name,lfind_name,find_size,find_date,find_time,find_attr); 02802 return true; 02803 } 02804 02805 bool Overlay_Drive::FileUnlink(const char * name) { 02806 if (ovlreadonly) { 02807 DOS_SetError(DOSERR_WRITE_PROTECTED); 02808 return false; 02809 } 02810 02811 //TODO check the basedir for file existence in order if we need to add the file to deleted file list. 02812 Bit32u a = GetTicks(); 02813 if (logoverlay) LOG_MSG("calling unlink on %s",name); 02814 char basename[CROSS_LEN]; 02815 strcpy(basename,basedir); 02816 strcat(basename,name); 02817 CROSS_FILENAME(basename); 02818 02819 char overlayname[CROSS_LEN]; 02820 strcpy(overlayname,overlaydir); 02821 strcat(overlayname,name); 02822 CROSS_FILENAME(overlayname); 02823 02824 bool removed=false; 02825 const host_cnv_char_t* host_name; 02826 struct stat temp_stat; 02827 if (stat(overlayname,&temp_stat)) { 02828 char* temp_name = dirCache.GetExpandName(basename); 02829 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 02830 char overtmpname[CROSS_LEN]; 02831 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 02832 strcpy(overtmpname,overlaydir); 02833 strcat(overtmpname,temp_name); 02834 CROSS_FILENAME(overtmpname); 02835 host_name = CodePageGuestToHost(overtmpname); 02836 if (host_name != NULL && ht_unlink(host_name)==0) removed=true; 02837 } 02838 } 02839 // char *fullname = dirCache.GetExpandName(newname); 02840 02841 if (!removed&&unlink(overlayname)) { 02842 //Unlink failed for some reason try finding it. 02843 ht_stat_t status; 02844 host_name = CodePageGuestToHost(overlayname); 02845 if(host_name==NULL || ht_stat(host_name,&status)) { 02846 //file not found in overlay, check the basedrive 02847 //Check if file not already deleted 02848 if (is_deleted_file(name)) return false; 02849 02850 char *fullname = dirCache.GetExpandName(basename); 02851 host_name = CodePageGuestToHost(fullname); 02852 if (host_name==NULL || ht_stat(host_name,&status)) return false; // File not found in either, return file false. 02853 //File does exist in normal drive. 02854 //Maybe do something with the drive_cache. 02855 add_deleted_file(name,true); 02856 return true; 02857 // E_Exit("trying to remove existing non-overlay file %s",name); 02858 } 02859 FILE* file_writable = fopen_wrap(overlayname,"rb+"); 02860 if(!file_writable) return false; //No access ? ERROR MESSAGE NOT SET. FIXME ? 02861 fclose(file_writable); 02862 02863 //File exists and can technically be deleted, nevertheless it failed. 02864 //This means that the file is probably open by some process. 02865 //See if We have it open. 02866 bool found_file = false; 02867 for(Bitu i = 0;i < DOS_FILES;i++){ 02868 if(Files[i] && Files[i]->IsName(name)) { 02869 Bitu max = DOS_FILES; 02870 while(Files[i]->IsOpen() && max--) { 02871 Files[i]->Close(); 02872 if (Files[i]->RemoveRef()<=0) break; 02873 } 02874 found_file=true; 02875 } 02876 } 02877 if(!found_file) return false; 02878 if (unlink(overlayname) == 0) { //Overlay file removed 02879 //Mark basefile as deleted if it exists: 02880 if (localDrive::FileExists(name)) add_deleted_file(name,true); 02881 remove_DOSname_from_cache(name); //Should be an else ? although better safe than sorry. 02882 //Handle this better 02883 dirCache.DeleteEntry(basename); 02884 dirCache.EmptyCache(); 02885 update_cache(false); 02886 //Check if it exists in the base dir as well 02887 02888 return true; 02889 } 02890 return false; 02891 } else { //Removed from overlay. 02892 //TODO IF it exists in the basedir: and more locations above. 02893 if (localDrive::FileExists(name)) add_deleted_file(name,true); 02894 remove_DOSname_from_cache(name); 02895 //TODODO remove from the update_cache cache as well 02896 //Handle this better 02897 //Check if it exists in the base dir as well 02898 dirCache.DeleteEntry(basename); 02899 02900 dirCache.EmptyCache(); 02901 update_cache(false); 02902 if (logoverlay) LOG_MSG("OPTIMISE: unlink took %d",GetTicks()-a); 02903 return true; 02904 } 02905 } 02906 02907 bool Overlay_Drive::SetFileAttr(const char * name,Bit16u attr) { 02908 char overlayname[CROSS_LEN], tmp[CROSS_LEN], overtmpname[CROSS_LEN]; 02909 strcpy(overlayname,overlaydir); 02910 strcat(overlayname,name); 02911 CROSS_FILENAME(overlayname); 02912 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 02913 strcpy(tmp, name); 02914 char *q=strrchr(tmp, '\\'); 02915 if (q!=NULL) *(q+1)=0; 02916 else *tmp=0; 02917 char *p=strrchr(temp_name, '\\'); 02918 if (p!=NULL) 02919 strcat(tmp,p+1); 02920 else 02921 strcat(tmp,temp_name); 02922 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) 02923 strcpy(tmp,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 02924 strcpy(overtmpname,overlaydir); 02925 strcat(overtmpname,tmp); 02926 CROSS_FILENAME(overtmpname); 02927 02928 ht_stat_t status; 02929 const host_cnv_char_t* host_name; 02930 bool success=false; 02931 #if defined (WIN32) 02932 host_name = CodePageGuestToHost(overtmpname); 02933 if (host_name != NULL&&SetFileAttributesW(host_name, attr)) success=true; 02934 if (!success) { 02935 host_name = CodePageGuestToHost(overlayname); 02936 if (host_name != NULL&&SetFileAttributesW(host_name, attr)) success=true; 02937 } 02938 #else 02939 if (ht_stat(overtmpname,&status)==0 || ht_stat(overlayname,&status)==0) { 02940 if (attr & (DOS_ATTR_SYSTEM|DOS_ATTR_HIDDEN)) 02941 LOG(LOG_DOSMISC,LOG_WARN)("%s: Application attempted to set system or hidden attributes for '%s' which is ignored for local drives",__FUNCTION__,overlayname); 02942 02943 if (attr & DOS_ATTR_READ_ONLY) 02944 status.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 02945 else 02946 status.st_mode |= S_IWUSR; 02947 if (chmod(overtmpname,status.st_mode) >= 0 || chmod(overlayname,status.st_mode) >= 0) 02948 success=true; 02949 } 02950 #endif 02951 if (success) { 02952 dirCache.EmptyCache(); 02953 update_cache(false); 02954 return true; 02955 } 02956 02957 char newname[CROSS_LEN]; 02958 strcpy(newname,basedir); 02959 strcat(newname,name); 02960 CROSS_FILENAME(newname); 02961 temp_name = dirCache.GetExpandName(newname); 02962 host_name = CodePageGuestToHost(temp_name); 02963 02964 bool created=false; 02965 if (host_name != NULL && ht_stat(host_name,&status)==0 && (status.st_mode & S_IFDIR)) { 02966 host_name = CodePageGuestToHost(overtmpname); 02967 int temp=-1; 02968 if (host_name!=NULL) { 02969 #if defined (WIN32) 02970 temp=_wmkdir(host_name); 02971 #else 02972 temp=mkdir(host_name,0775); 02973 #endif 02974 } 02975 if (temp==0) created=true; 02976 } else { 02977 FILE * hand; 02978 host_name = CodePageGuestToHost(temp_name); 02979 #ifdef host_cnv_use_wchar 02980 if (host_name!=NULL) 02981 hand = _wfopen(host_name,_HT("rb")); 02982 else 02983 #endif 02984 hand = fopen_wrap(temp_name,"rb"); 02985 if (hand) { 02986 if (logoverlay) LOG_MSG("overlay file opened %s",temp_name); 02987 FILE * layfile=NULL; 02988 host_name = CodePageGuestToHost(overtmpname); 02989 #ifdef host_cnv_use_wchar 02990 if (host_name!=NULL) layfile=_wfopen(host_name,_HT("wb")); 02991 #endif 02992 if (layfile==NULL) layfile=fopen_wrap(overlayname,"wb"); 02993 int numr,numw; 02994 char buffer[1000]; 02995 while(feof(hand)==0) { 02996 if((numr=fread(buffer,1,1000,hand))!=1000){ 02997 if(ferror(hand)!=0){ 02998 fclose(hand); 02999 fclose(layfile); 03000 return false; 03001 } else if(feof(hand)!=0) { } 03002 } 03003 if((numw=fwrite(buffer,1,numr,layfile))!=numr){ 03004 fclose(hand); 03005 fclose(layfile); 03006 return false; 03007 } 03008 } 03009 fclose(hand); 03010 fclose(layfile); 03011 created=true; 03012 } 03013 } 03014 if (created) { 03015 #if defined (WIN32) 03016 host_name = CodePageGuestToHost(overtmpname); 03017 if (host_name != NULL&&SetFileAttributesW(host_name, attr)) success=true; 03018 if (!success) { 03019 host_name = CodePageGuestToHost(overlayname); 03020 if (host_name != NULL&&SetFileAttributesW(host_name, attr)) success=true; 03021 } 03022 #else 03023 if (ht_stat(overtmpname,&status)==0 || ht_stat(overlayname,&status)==0) { 03024 if (attr & (DOS_ATTR_SYSTEM|DOS_ATTR_HIDDEN)) 03025 LOG(LOG_DOSMISC,LOG_WARN)("%s: Application attempted to set system or hidden attributes for '%s' which is ignored for local drives",__FUNCTION__,overlayname); 03026 03027 if (attr & DOS_ATTR_READ_ONLY) 03028 status.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 03029 else 03030 status.st_mode |= S_IWUSR; 03031 if (chmod(overtmpname,status.st_mode) >= 0 || chmod(overlayname,status.st_mode) >= 0) 03032 success=true; 03033 } 03034 #endif 03035 if (success) { 03036 dirCache.EmptyCache(); 03037 update_cache(false); 03038 return true; 03039 } 03040 } 03041 return false; 03042 } 03043 03044 bool Overlay_Drive::GetFileAttr(const char * name,Bit16u * attr) { 03045 if (ovlnocachedir) { 03046 dirCache.EmptyCache(); 03047 update_cache(true); 03048 } 03049 03050 char overlayname[CROSS_LEN], tmp[CROSS_LEN], overtmpname[CROSS_LEN]; 03051 strcpy(overlayname,overlaydir); 03052 strcat(overlayname,name); 03053 CROSS_FILENAME(overlayname); 03054 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 03055 strcpy(tmp, name); 03056 char *q=strrchr(tmp, '\\'); 03057 if (q!=NULL) *(q+1)=0; 03058 else *tmp=0; 03059 char *p=strrchr(temp_name, '\\'); 03060 if (p!=NULL) 03061 strcat(tmp,p+1); 03062 else 03063 strcat(tmp,temp_name); 03064 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) 03065 strcpy(tmp,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03066 strcpy(overtmpname,overlaydir); 03067 strcat(overtmpname,tmp); 03068 CROSS_FILENAME(overtmpname); 03069 03070 #if defined (WIN32) 03071 Bitu attribs = INVALID_FILE_ATTRIBUTES; 03072 const host_cnv_char_t* host_name = CodePageGuestToHost(overtmpname); 03073 if (host_name != NULL) attribs = GetFileAttributesW(host_name); 03074 if (attribs == INVALID_FILE_ATTRIBUTES) { 03075 host_name = CodePageGuestToHost(overlayname); 03076 if (host_name != NULL) attribs = GetFileAttributesW(host_name); 03077 } 03078 if (attribs != INVALID_FILE_ATTRIBUTES) { 03079 *attr = attribs&0x3f; 03080 return true; 03081 } 03082 #else 03083 ht_stat_t status; 03084 if (ht_stat(overtmpname,&status)==0 || ht_stat(overlayname,&status)==0) { 03085 *attr=DOS_ATTR_ARCHIVE; 03086 if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY; 03087 if(!(status.st_mode & S_IWUSR)) *attr|=DOS_ATTR_READ_ONLY; 03088 return true; 03089 } 03090 #endif 03091 //Maybe check for deleted path as well 03092 if (is_deleted_file(name)) { 03093 *attr = 0; 03094 return false; 03095 } 03096 return localDrive::GetFileAttr(name,attr); 03097 } 03098 03099 03100 void Overlay_Drive::add_deleted_file(const char* name,bool create_on_disk) { 03101 char tname[CROSS_LEN]; 03102 strcpy(tname,basedir); 03103 strcat(tname,name); 03104 CROSS_FILENAME(tname); 03105 char* temp_name = dirCache.GetExpandName(tname); 03106 strcpy(tname, name); 03107 char *q=strrchr(tname, '\\'); 03108 if (q!=NULL) *(q+1)=0; 03109 else *tname=0; 03110 char *p=strrchr(temp_name, '\\'); 03111 if (p!=NULL) 03112 strcat(tname,p+1); 03113 else 03114 strcat(tname,temp_name); 03115 if (!is_deleted_file(tname)) { 03116 deleted_files_in_base.push_back(tname); 03117 if (create_on_disk) add_special_file_to_disk(tname, "DEL"); 03118 } 03119 } 03120 03121 void Overlay_Drive::add_special_file_to_disk(const char* dosname, const char* operation) { 03122 std::string name = create_filename_of_special_operation(dosname, operation); 03123 char overlayname[CROSS_LEN]; 03124 strcpy(overlayname,overlaydir); 03125 strcat(overlayname,name.c_str()); 03126 CROSS_FILENAME(overlayname); 03127 const host_cnv_char_t* host_name = CodePageGuestToHost(overlayname); 03128 FILE* f; 03129 if (host_name!=NULL) { 03130 #ifdef host_cnv_use_wchar 03131 f = _wfopen(host_name,_HT("wb+")); 03132 #else 03133 f = fopen_wrap(host_name,"wb+"); 03134 #endif 03135 } 03136 else { 03137 f = fopen_wrap(overlayname,"wb+"); 03138 } 03139 03140 if (!f) { 03141 Sync_leading_dirs(dosname); 03142 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name.c_str())); 03143 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03144 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 03145 strcpy(overlayname,overlaydir); 03146 strcat(overlayname,temp_name); 03147 CROSS_FILENAME(overlayname); 03148 } 03149 host_name = CodePageGuestToHost(overlayname); 03150 if (host_name!=NULL) { 03151 #ifdef host_cnv_use_wchar 03152 f = _wfopen(host_name,_HT("wb+")); 03153 #else 03154 f = fopen_wrap(host_name,"wb+"); 03155 #endif 03156 } 03157 else { 03158 f = fopen_wrap(overlayname,"wb+"); 03159 } 03160 } 03161 if (!f) E_Exit("Failed creation of %s",overlayname); 03162 char buf[5] = {'e','m','p','t','y'}; 03163 fwrite(buf,5,1,f); 03164 fclose(f); 03165 } 03166 03167 void Overlay_Drive::remove_special_file_from_disk(const char* dosname, const char* operation) { 03168 std::string name = create_filename_of_special_operation(dosname,operation); 03169 char overlayname[CROSS_LEN]; 03170 strcpy(overlayname,overlaydir); 03171 strcat(overlayname,name.c_str()); 03172 CROSS_FILENAME(overlayname); 03173 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name.c_str())); 03174 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03175 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 03176 strcpy(overlayname,overlaydir); 03177 strcat(overlayname,temp_name); 03178 CROSS_FILENAME(overlayname); 03179 } 03180 const host_cnv_char_t* host_name = CodePageGuestToHost(overlayname); 03181 if ((host_name == NULL || ht_unlink(host_name) != 0) && unlink(overlayname) != 0) 03182 E_Exit("Failed removal of %s",overlayname); 03183 } 03184 03185 std::string Overlay_Drive::create_filename_of_special_operation(const char* dosname, const char* operation) { 03186 std::string res(dosname); 03187 std::string::size_type s = res.rfind('\\'); //CHECK DOS or host endings.... on update_cache 03188 if (s == std::string::npos) s = 0; else s++; 03189 std::string oper = special_prefix + "_" + operation + "_"; 03190 res.insert(s,oper); 03191 return res; 03192 } 03193 03194 03195 bool Overlay_Drive::is_dir_only_in_overlay(const char* name) { 03196 if (!name || !*name) return false; 03197 if (DOSdirs_cache.empty()) return false; 03198 char fname[CROSS_LEN]; 03199 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 03200 strcpy(fname, ""); 03201 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03202 strcpy(fname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03203 CROSS_DOSFILENAME(fname); 03204 } 03205 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it+=2) { 03206 if (!strcasecmp((*it).c_str(), name)||(strlen(fname)&&!strcasecmp((*it).c_str(), fname))||((*(it+1)).length()&&!strcasecmp((*(it+1)).c_str(), name))) return true; 03207 } 03208 return false; 03209 } 03210 03211 bool Overlay_Drive::is_deleted_file(const char* name) { 03212 if (!name || !*name) return false; 03213 if (deleted_files_in_base.empty()) return false; 03214 char tname[CROSS_LEN],fname[CROSS_LEN]; 03215 strcpy(tname,basedir); 03216 strcat(tname,name); 03217 CROSS_FILENAME(tname); 03218 char* temp_name = dirCache.GetExpandName(tname); 03219 strcpy(tname, name); 03220 char *q=strrchr(tname, '\\'); 03221 if (q!=NULL) *(q+1)=0; 03222 else *tname=0; 03223 char *p=strrchr(temp_name, '\\'); 03224 if (p!=NULL) 03225 strcat(tname,p+1); 03226 else 03227 strcat(tname,temp_name); 03228 strcpy(fname, ""); 03229 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03230 strcpy(fname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03231 CROSS_DOSFILENAME(fname); 03232 } 03233 for(std::vector<std::string>::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); it++) { 03234 if (!strcasecmp((*it).c_str(), name)||!strcasecmp((*it).c_str(), tname)||(strlen(fname)&&!strcasecmp((*it).c_str(), fname))) return true; 03235 } 03236 return false; 03237 } 03238 03239 void Overlay_Drive::add_DOSdir_to_cache(const char* name, const char *sname) { 03240 if (!name || !*name ) return; //Skip empty file. 03241 if (logoverlay) LOG_MSG("Adding name to overlay_only_dir_cache %s",name); 03242 if (!is_dir_only_in_overlay(name)) { 03243 DOSdirs_cache.push_back(name); 03244 DOSdirs_cache.push_back(sname); 03245 } 03246 } 03247 03248 void Overlay_Drive::remove_DOSdir_from_cache(const char* name) { 03249 char fname[CROSS_LEN]; 03250 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 03251 strcpy(fname, ""); 03252 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03253 strcpy(fname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03254 CROSS_DOSFILENAME(fname); 03255 } 03256 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it+=2) { 03257 if (!strcasecmp((*it).c_str(), name)||(strlen(fname)&&!strcasecmp((*it).c_str(), fname))||((*(it+1)).length()&&!strcasecmp((*(it+1)).c_str(), name))) { 03258 DOSdirs_cache.erase(it+1); 03259 DOSdirs_cache.erase(it); 03260 return; 03261 } 03262 } 03263 } 03264 03265 void Overlay_Drive::remove_deleted_file(const char* name,bool create_on_disk) { 03266 char tname[CROSS_LEN],fname[CROSS_LEN]; 03267 strcpy(tname,basedir); 03268 strcat(tname,name); 03269 CROSS_FILENAME(tname); 03270 char* temp_name = dirCache.GetExpandName(tname); 03271 strcpy(tname, name); 03272 char *q=strrchr(tname, '\\'); 03273 if (q!=NULL) *(q+1)=0; 03274 else *tname=0; 03275 char *p=strrchr(temp_name, '\\'); 03276 if (p!=NULL) 03277 strcat(tname,p+1); 03278 else 03279 strcat(tname,temp_name); 03280 strcpy(fname, ""); 03281 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03282 strcpy(fname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03283 CROSS_DOSFILENAME(fname); 03284 } 03285 for(std::vector<std::string>::iterator it = deleted_files_in_base.begin(); it != deleted_files_in_base.end(); ++it) { 03286 if (!strcasecmp((*it).c_str(), name)||!strcasecmp((*it).c_str(), tname)||(strlen(fname)&&!strcasecmp((*it).c_str(), fname))) { 03287 deleted_files_in_base.erase(it); 03288 if (create_on_disk) remove_special_file_from_disk(name, "DEL"); 03289 return; 03290 } 03291 } 03292 } 03293 void Overlay_Drive::add_deleted_path(const char* name, bool create_on_disk) { 03294 if (!name || !*name ) return; //Skip empty file. 03295 if (!is_deleted_path(name)) { 03296 deleted_paths_in_base.push_back(name); 03297 //Add it to deleted files as well, so it gets skipped in FindNext. 03298 //Maybe revise that. 03299 if (create_on_disk) add_special_file_to_disk(name,"RMD"); 03300 add_deleted_file(name,false); 03301 } 03302 } 03303 bool Overlay_Drive::is_deleted_path(const char* name) { 03304 if (!name || !*name) return false; 03305 if (deleted_paths_in_base.empty()) return false; 03306 std::string sname(name); 03307 std::string::size_type namelen = sname.length();; 03308 for(std::vector<std::string>::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); ++it) { 03309 std::string::size_type blockedlen = (*it).length(); 03310 if (namelen < blockedlen) continue; 03311 //See if input starts with name. 03312 std::string::size_type n = sname.find(*it); 03313 if (n == 0 && ((namelen == blockedlen) || *(name + blockedlen) == '\\' )) return true; 03314 } 03315 return false; 03316 } 03317 03318 void Overlay_Drive::remove_deleted_path(const char* name, bool create_on_disk) { 03319 for(std::vector<std::string>::iterator it = deleted_paths_in_base.begin(); it != deleted_paths_in_base.end(); ++it) { 03320 if (!strcasecmp((*it).c_str(), name)) { 03321 deleted_paths_in_base.erase(it); 03322 remove_deleted_file(name,false); //Rethink maybe. 03323 if (create_on_disk) remove_special_file_from_disk(name,"RMD"); 03324 break; 03325 } 03326 } 03327 } 03328 bool Overlay_Drive::check_if_leading_is_deleted(const char* name){ 03329 const char* dname = strrchr(name,'\\'); 03330 if (dname != NULL) { 03331 char dirname[CROSS_LEN]; 03332 strncpy(dirname,name,dname - name); 03333 dirname[dname - name] = 0; 03334 if (is_deleted_path(dirname)) return true; 03335 } 03336 return false; 03337 } 03338 03339 bool Overlay_Drive::FileExists(const char* name) { 03340 if (ovlnocachedir) { 03341 dirCache.EmptyCache(); 03342 update_cache(true); 03343 } 03344 03345 char overlayname[CROSS_LEN]; 03346 strcpy(overlayname,overlaydir); 03347 strcat(overlayname,name); 03348 CROSS_FILENAME(overlayname); 03349 ht_stat_t temp_stat; 03350 const host_cnv_char_t* host_name = CodePageGuestToHost(overlayname); 03351 if (host_name != NULL && ht_stat(host_name ,&temp_stat)==0 && (temp_stat.st_mode & S_IFDIR)==0) return true; 03352 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 03353 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03354 strcpy(overlayname,overlaydir); 03355 strcat(overlayname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03356 CROSS_FILENAME(overlayname); 03357 host_name = CodePageGuestToHost(overlayname); 03358 if(host_name != NULL && ht_stat(host_name,&temp_stat)==0 && (temp_stat.st_mode & S_IFDIR)==0) return true; 03359 } 03360 03361 if (is_deleted_file(name)) return false; 03362 03363 return localDrive::FileExists(name); 03364 } 03365 03366 bool Overlay_Drive::Rename(const char * oldname,const char * newname) { 03367 if (ovlreadonly) { 03368 DOS_SetError(DOSERR_WRITE_PROTECTED); 03369 return false; 03370 } 03371 03372 //TODO with cache function! 03373 //Tricky function. 03374 //Renaming directories is currently not fully supported, due the drive_cache not handling that smoothly. 03375 //So oldname is directory => exit unless it is only in overlay. 03376 //If oldname is on overlay => simple rename. 03377 //if oldname is on base => copy file to overlay with new name and mark old file as deleted. 03378 //More advanced version. keep track of the file being renamed in order to detect that the file is being renamed back. 03379 03380 char tmp[CROSS_LEN]; 03381 host_cnv_char_t host_nameold[CROSS_LEN], host_namenew[CROSS_LEN]; 03382 Bit16u attr = 0; 03383 if (!GetFileAttr(oldname,&attr)) E_Exit("rename, but source doesn't exist, should not happen %s",oldname); 03384 ht_stat_t temp_stat; 03385 if (attr&DOS_ATTR_DIRECTORY) { 03386 //See if the directory exists only in the overlay, then it should be possible. 03387 #if OVERLAY_DIR 03388 if (localDrive::TestDir(oldname)) { 03389 LOG_MSG("Overlay: renaming base directory %s to %s not yet supported", oldname,newname); 03390 DOS_SetError(DOSERR_ACCESS_DENIED); 03391 return false; 03392 } 03393 #endif 03394 char overlaynameold[CROSS_LEN]; 03395 strcpy(overlaynameold,overlaydir); 03396 strcat(overlaynameold,oldname); 03397 CROSS_FILENAME(overlaynameold); 03398 #ifdef host_cnv_use_wchar 03399 wcscpy 03400 #else 03401 strcpy 03402 #endif 03403 (host_nameold, CodePageGuestToHost(overlaynameold)); 03404 03405 char overlaynamenew[CROSS_LEN]; 03406 strcpy(overlaynamenew,overlaydir); 03407 strcat(overlaynamenew,newname); 03408 CROSS_FILENAME(overlaynamenew); 03409 #ifdef host_cnv_use_wchar 03410 wcscpy 03411 #else 03412 strcpy 03413 #endif 03414 (host_namenew, CodePageGuestToHost(overlaynamenew)); 03415 03416 if (ht_stat(host_nameold,&temp_stat)) { 03417 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,oldname)); 03418 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03419 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 03420 strcpy(overlaynameold,GetCrossedName(overlaydir,temp_name)); 03421 #ifdef host_cnv_use_wchar 03422 wcscpy 03423 #else 03424 strcpy 03425 #endif 03426 (host_nameold, CodePageGuestToHost(overlaynameold)); 03427 } 03428 strcpy(tmp,newname); 03429 char *p=strrchr(tmp,'\\'), ndir[CROSS_LEN]; 03430 if (p!=NULL) { 03431 *p=0; 03432 temp_name=dirCache.GetExpandName(GetCrossedName(basedir,tmp)); 03433 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03434 strcpy(ndir,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03435 strcat(ndir,"\\"); 03436 strcat(ndir,p+1); 03437 strcpy(overlaynamenew,GetCrossedName(overlaydir,ndir)); 03438 #ifdef host_cnv_use_wchar 03439 wcscpy 03440 #else 03441 strcpy 03442 #endif 03443 (host_namenew, CodePageGuestToHost(overlaynamenew)); 03444 } 03445 } 03446 } 03447 int temp=-1; 03448 #ifdef host_cnv_use_wchar 03449 temp = _wrename(host_nameold,host_namenew); 03450 #else 03451 temp = rename(host_nameold,host_namenew); 03452 #endif 03453 if (temp==0) { 03454 dirCache.EmptyCache(); 03455 update_cache(true); 03456 return true; 03457 } 03458 LOG_MSG("Overlay: renaming overlay directory %s to %s not yet supported",oldname,newname); //TODO 03459 DOS_SetError(DOSERR_ACCESS_DENIED); 03460 return false; 03461 } 03462 03463 Bit32u a = GetTicks(); 03464 //First generate overlay names. 03465 char overlaynameold[CROSS_LEN]; 03466 strcpy(overlaynameold,overlaydir); 03467 strcat(overlaynameold,oldname); 03468 CROSS_FILENAME(overlaynameold); 03469 #ifdef host_cnv_use_wchar 03470 wcscpy 03471 #else 03472 strcpy 03473 #endif 03474 (host_nameold, CodePageGuestToHost(overlaynameold)); 03475 03476 char overlaynamenew[CROSS_LEN]; 03477 strcpy(overlaynamenew,overlaydir); 03478 strcat(overlaynamenew,newname); 03479 CROSS_FILENAME(overlaynamenew); 03480 #ifdef host_cnv_use_wchar 03481 wcscpy 03482 #else 03483 strcpy 03484 #endif 03485 (host_namenew, CodePageGuestToHost(overlaynamenew)); 03486 if (ht_stat(host_nameold,&temp_stat)) { 03487 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,oldname)); 03488 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03489 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 03490 strcpy(overlaynameold,GetCrossedName(overlaydir,temp_name)); 03491 #ifdef host_cnv_use_wchar 03492 wcscpy 03493 #else 03494 strcpy 03495 #endif 03496 (host_nameold, CodePageGuestToHost(overlaynameold)); 03497 } 03498 strcpy(tmp,newname); 03499 char *p=strrchr(tmp,'\\'), ndir[CROSS_LEN]; 03500 if (p!=NULL) { 03501 *p=0; 03502 temp_name=dirCache.GetExpandName(GetCrossedName(basedir,tmp)); 03503 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03504 strcpy(ndir,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03505 strcat(ndir,"\\"); 03506 strcat(ndir,p+1); 03507 strcpy(overlaynamenew,GetCrossedName(overlaydir,ndir)); 03508 #ifdef host_cnv_use_wchar 03509 wcscpy 03510 #else 03511 strcpy 03512 #endif 03513 (host_namenew, CodePageGuestToHost(overlaynamenew)); 03514 } 03515 } 03516 } 03517 03518 //No need to check if the original is marked as deleted, as GetFileAttr would fail if it did. 03519 03520 //Check if overlay source file exists 03521 int temp = -1; 03522 if (ht_stat(host_nameold,&temp_stat) == 0) { 03523 //Simple rename 03524 #ifdef host_cnv_use_wchar 03525 temp = _wrename(host_nameold,host_namenew); 03526 #else 03527 temp = rename(host_nameold,host_namenew); 03528 #endif 03529 //TODO CHECK if base has a file with same oldname!!!!! if it does mark it as deleted!! 03530 if (localDrive::FileExists(oldname)) add_deleted_file(oldname,true); 03531 } else { 03532 Bit32u aa = GetTicks(); 03533 //File exists in the basedrive. Make a copy and mark old one as deleted. 03534 char newold[CROSS_LEN]; 03535 strcpy(newold,basedir); 03536 strcat(newold,oldname); 03537 CROSS_FILENAME(newold); 03538 dirCache.ExpandName(newold); 03539 const host_cnv_char_t* host_name = CodePageGuestToHost(newold); 03540 FILE* o; 03541 if (host_name!=NULL) { 03542 #ifdef host_cnv_use_wchar 03543 o = _wfopen(host_name,_HT("rb")); 03544 #else 03545 o = fopen_wrap(host_name,"rb"); 03546 #endif 03547 } 03548 else { 03549 o = fopen_wrap(newold,"rb"); 03550 } 03551 if (!o) return false; 03552 FILE* n = create_file_in_overlay(newname,"wb+"); 03553 if (!n) {fclose(o); return false;} 03554 char buffer[BUFSIZ]; 03555 size_t s; 03556 while ( (s = fread(buffer,1,BUFSIZ,o)) ) fwrite(buffer, 1, s, n); 03557 fclose(o); fclose(n); 03558 03559 //File copied. 03560 //Mark old file as deleted 03561 add_deleted_file(oldname,true); 03562 temp =0; //success 03563 if (logoverlay) LOG_MSG("OPTIMISE: update rename with copy took %d",GetTicks()-aa); 03564 03565 } 03566 if (temp ==0) { 03567 //handle the drive_cache (a bit better) 03568 //Ensure that the file is not marked as deleted anymore. 03569 if (is_deleted_file(newname)) remove_deleted_file(newname,true); 03570 dirCache.EmptyCache(); 03571 update_cache(true); 03572 if (logoverlay) LOG_MSG("OPTIMISE: rename took %d",GetTicks()-a); 03573 03574 } 03575 return (temp==0); 03576 03577 } 03578 03579 bool Overlay_Drive::FindFirst(const char * _dir,DOS_DTA & dta,bool fcb_findfirst) { 03580 if (logoverlay) LOG_MSG("FindFirst in %s",_dir); 03581 03582 if (is_deleted_path(_dir)) { 03583 //No accidental listing of files in there. 03584 DOS_SetError(DOSERR_PATH_NOT_FOUND); 03585 return false; 03586 } 03587 03588 if (ovlnocachedir) { 03589 dirCache.EmptyCache(); 03590 update_cache(true); 03591 } 03592 03593 if (*_dir) { 03594 char newname[CROSS_LEN], tmp[CROSS_LEN]; 03595 strcpy(newname,overlaydir); 03596 strcat(newname,_dir); 03597 CROSS_FILENAME(newname); 03598 struct stat temp_stat; 03599 if (stat(newname,&temp_stat)) { 03600 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,_dir)); 03601 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03602 temp_name+=strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0); 03603 return localDrive::FindFirst(temp_name,dta,fcb_findfirst); 03604 } 03605 strcpy(tmp, _dir); 03606 bool found=false; 03607 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it+=2) { 03608 if ((*(it+1)).length()&&!strcasecmp((*(it+1)).c_str(), tmp)) { 03609 found=true; 03610 strcpy(tmp, (*it).c_str()); 03611 break; 03612 } 03613 } 03614 if (found) { 03615 return localDrive::FindFirst(tmp,dta,fcb_findfirst); 03616 } 03617 } 03618 } 03619 return localDrive::FindFirst(_dir,dta,fcb_findfirst); 03620 } 03621 03622 bool Overlay_Drive::FileStat(const char* name, FileStat_Block * const stat_block) { 03623 if (ovlnocachedir) { 03624 dirCache.EmptyCache(); 03625 update_cache(true); 03626 } 03627 03628 char overlayname[CROSS_LEN], tmp[CROSS_LEN], overtmpname[CROSS_LEN]; 03629 strcpy(overlayname,overlaydir); 03630 strcat(overlayname,name); 03631 CROSS_FILENAME(overlayname); 03632 char* temp_name = dirCache.GetExpandName(GetCrossedName(basedir,name)); 03633 const host_cnv_char_t* host_name; 03634 ht_stat_t temp_stat; 03635 bool success=false; 03636 if (strlen(temp_name)>strlen(basedir)&&!strncasecmp(temp_name, basedir, strlen(basedir))) { 03637 strcpy(overtmpname,overlaydir); 03638 strcat(overtmpname,temp_name+strlen(basedir)+(*(temp_name+strlen(basedir))=='\\'?1:0)); 03639 CROSS_FILENAME(overtmpname); 03640 host_name = CodePageGuestToHost(overtmpname); 03641 if (host_name != NULL && ht_stat(host_name,&temp_stat)==0) success=true; 03642 } 03643 if (!success) { 03644 host_name = CodePageGuestToHost(overlayname); 03645 if (host_name != NULL && ht_stat(host_name,&temp_stat) == 0) success=true; 03646 } 03647 if (!success) { 03648 strcpy(tmp,name); 03649 char *p=strrchr(tmp, '\\'), *q=strrchr(temp_name, '\\'); 03650 if (p!=NULL&&q!=NULL) { 03651 *p=0; 03652 for(std::vector<std::string>::iterator it = DOSdirs_cache.begin(); it != DOSdirs_cache.end(); it+=2) 03653 if ((*(it+1)).length()&&!strcasecmp((*(it+1)).c_str(), tmp)) { 03654 strcpy(tmp, (*it).c_str()); 03655 break; 03656 } 03657 strcat(tmp, "\\"); 03658 strcat(tmp, q+1); 03659 } 03660 strcpy(overtmpname,overlaydir); 03661 strcat(overtmpname,tmp); 03662 CROSS_FILENAME(overtmpname); 03663 host_name = CodePageGuestToHost(overtmpname); 03664 if (host_name != NULL && ht_stat(host_name,&temp_stat)==0) success=true; 03665 } 03666 if(!success) { 03667 if (is_deleted_file(name)) return false; 03668 return localDrive::FileStat(name,stat_block); 03669 } 03670 03671 /* Convert the stat to a FileStat */ 03672 struct tm *time; 03673 if((time=localtime(&temp_stat.st_mtime))!=0) { 03674 stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec); 03675 stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday); 03676 } else { 03677 // ... But this function is not used at the moment. 03678 } 03679 stat_block->size=(Bit32u)temp_stat.st_size; 03680 return true; 03681 } 03682 03683 Bits Overlay_Drive::UnMount(void) { 03684 delete this; 03685 return 0; 03686 } 03687 void Overlay_Drive::EmptyCache(void){ 03688 localDrive::EmptyCache(); 03689 update_cache(true);//lets rebuild it. 03690 }