DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/drive_local.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  *  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, &sectors_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 }