DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/dos/drive_local.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <time.h>
00024 #include <errno.h>
00025 
00026 #include "dosbox.h"
00027 #include "dos_inc.h"
00028 #include "drives.h"
00029 #include "support.h"
00030 #include "cross.h"
00031 #include "inout.h"
00032 #ifndef WIN32
00033 #include <utime.h>
00034 #else
00035 #include <sys/utime.h>
00036 #include <sys/locking.h>
00037 #endif
00038 
00039 class localFile : public DOS_File {
00040 public:
00041         localFile(const char* name, FILE * handle);
00042         bool Read(Bit8u * data,Bit16u * size);
00043         bool Write(const Bit8u * data,Bit16u * size);
00044         bool Seek(Bit32u * pos,Bit32u type);
00045         bool Close();
00046 #ifdef WIN32
00047         bool LockFile(Bit8u mode, Bit32u pos, Bit16u size);
00048 #endif
00049         Bit16u GetInformation(void);
00050         bool UpdateDateTimeFromHost(void);   
00051         void FlagReadOnlyMedium(void);
00052         void Flush(void);
00053         Bit32u GetSeekPos(void);
00054 private:
00055         FILE * fhandle;
00056         bool read_only_medium;
00057         enum { NONE,READ,WRITE } last_action;
00058 };
00059 
00060 #include "cp437_uni.h"
00061 #include "cp932_uni.h"
00062 
00063 #if defined(PATH_MAX) && !defined(MAX_PATH)
00064 #define MAX_PATH PATH_MAX
00065 #endif
00066 
00067 #if defined(WIN32)
00068 // Windows: Use UTF-16 (wide char)
00069 // TODO: Offer an option to NOT use wide char on Windows if directed by config.h
00070 //       for people who compile this code for Windows 95 or earlier where some
00071 //       widechar functions are missing.
00072 typedef wchar_t host_cnv_char_t;
00073 # define host_cnv_use_wchar
00074 # define _HT(x) L##x
00075 # if defined(__MINGW32__) /* TODO: Get MinGW to support 64-bit file offsets, at least targeting Windows XP! */
00076 #  define ht_stat_t struct _stat
00077 #  define ht_stat(x,y) _wstat(x,y)
00078 # else
00079 #  define ht_stat_t struct _stat64i32 /* WTF Microsoft?? Why aren't _stat and _wstat() consistent on stat struct type? */
00080 #  define ht_stat(x,y) _wstat64i32(x,y)
00081 # endif
00082 # define ht_access(x,y) _waccess(x,y)
00083 # define ht_strdup(x) _wcsdup(x)
00084 # define ht_unlink(x) _wunlink(x)
00085 #else
00086 // Linux: Use UTF-8
00087 typedef char host_cnv_char_t;
00088 # define _HT(x) x
00089 # define ht_stat_t struct stat
00090 # define ht_stat(x,y) stat(x,y)
00091 # define ht_access(x,y) access(x,y)
00092 # define ht_strdup(x) strdup(x)
00093 # define ht_unlink(x) unlink(x)
00094 #endif
00095 
00096 static host_cnv_char_t cpcnv_temp[4096];
00097 
00098 bool String_ASCII_TO_HOST(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/) {
00099         host_cnv_char_t *df = d + CROSS_LEN - 1;
00100         const char *sf = s + CROSS_LEN - 1;
00101     unsigned char ic;
00102 
00103     while (*s != 0 && s < sf) {
00104         ic = (unsigned char)(*s++);
00105         if (ic < 32 || ic > 127) return false; // non-representable
00106 
00107 #if defined(host_cnv_use_wchar)
00108         *d++ = (host_cnv_char_t)ic;
00109 #else
00110         if (utf8_encode(&d,df,(uint32_t)ic) < 0) // will advance d by however many UTF-8 bytes are needed
00111             return false; // non-representable, or probably just out of room
00112 #endif
00113     }
00114 
00115     assert(d <= df);
00116     *d = 0;
00117 
00118     return true;
00119 }
00120 
00121 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) {
00122         host_cnv_char_t *df = d + CROSS_LEN - 1;
00123         const char *sf = s + CROSS_LEN - 1;
00124     unsigned char ic;
00125     MT wc;
00126 
00127     while (*s != 0 && s < sf) {
00128         ic = (unsigned char)(*s++);
00129         if (ic >= map_max) return false; // non-representable
00130         wc = map[ic]; // output: unicode character
00131 
00132 #if defined(host_cnv_use_wchar)
00133         *d++ = (host_cnv_char_t)wc;
00134 #else
00135         if (utf8_encode(&d,df,(uint32_t)wc) < 0) // will advance d by however many UTF-8 bytes are needed
00136             return false; // non-representable, or probably just out of room
00137 #endif
00138     }
00139 
00140     assert(d <= df);
00141     *d = 0;
00142 
00143     return true;
00144 }
00145 
00146 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) {
00147         host_cnv_char_t *df = d + CROSS_LEN - 1;
00148         const char *sf = s + CROSS_LEN - 1;
00149     uint16_t ic;
00150     MT rawofs;
00151     MT wc;
00152 
00153     while (*s != 0 && s < sf) {
00154         ic = (unsigned char)(*s++);
00155         if ((ic & 0xE0) == 0x80 || (ic & 0xE0) == 0xE0) {
00156             if (*s == 0) return false;
00157             ic <<= 8U;
00158             ic += (unsigned char)(*s++);
00159         }
00160 
00161         rawofs = hitbl[ic >> 6];
00162         if (rawofs == 0xFFFF)
00163             return false;
00164 
00165         assert((size_t)(rawofs+0x40) <= rawtbl_max);
00166         wc = rawtbl[rawofs + (ic & 0x3F)];
00167         if (wc == 0x0000)
00168             return false;
00169 
00170 #if defined(host_cnv_use_wchar)
00171         *d++ = (host_cnv_char_t)wc;
00172 #else
00173         if (utf8_encode(&d,df,(uint32_t)wc) < 0) // will advance d by however many UTF-8 bytes are needed
00174             return false; // non-representable, or probably just out of room
00175 #endif
00176     }
00177 
00178     assert(d <= df);
00179     *d = 0;
00180 
00181     return true;
00182 }
00183 
00184 // TODO: This is SLOW. Optimize.
00185 template <class MT> int SBCS_From_Host_Find(int c,const MT *map,const size_t map_max) {
00186     for (size_t i=0;i < map_max;i++) {
00187         if ((MT)c == map[i])
00188             return (int)i;
00189     }
00190 
00191     return -1;
00192 }
00193 
00194 // TODO: This is SLOW. Optimize.
00195 template <class MT> int DBCS_SHIFTJIS_From_Host_Find(int c,const MT *hitbl,const MT *rawtbl,const size_t rawtbl_max) {
00196     for (size_t h=0;h < 1024;h++) {
00197         MT ofs = hitbl[h];
00198 
00199         if (ofs == 0xFFFF) continue;
00200         assert((size_t)(ofs+0x40) <= rawtbl_max);
00201 
00202         for (size_t l=0;l < 0x40;l++) {
00203             if ((MT)c == rawtbl[ofs+l])
00204                 return (int)((h << 6) + l);
00205         }
00206     }
00207 
00208     return -1;
00209 }
00210 
00211 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) {
00212     const host_cnv_char_t *sf = s + CROSS_LEN - 1;
00213     char *df = d + CROSS_LEN - 1;
00214     int ic;
00215     int oc;
00216 
00217     while (*s != 0 && s < sf) {
00218 #if defined(host_cnv_use_wchar)
00219         ic = (int)(*s++);
00220 #else
00221         if ((ic=utf8_decode(&s,sf)) < 0)
00222             return false; // non-representable
00223 #endif
00224 
00225         oc = DBCS_SHIFTJIS_From_Host_Find<MT>(ic,hitbl,rawtbl,rawtbl_max);
00226         if (oc < 0)
00227             return false; // non-representable
00228 
00229         if (oc >= 0x100) {
00230             if ((d+1) >= df) return false;
00231             *d++ = (char)(oc >> 8U);
00232             *d++ = (char)oc;
00233         }
00234         else {
00235             if (d >= df) return false;
00236             *d++ = (char)oc;
00237         }
00238     }
00239 
00240     assert(d <= df);
00241     *d = 0;
00242 
00243     return true;
00244 }
00245 
00246 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) {
00247     const host_cnv_char_t *sf = s + CROSS_LEN - 1;
00248     char *df = d + CROSS_LEN - 1;
00249     int ic;
00250     int oc;
00251 
00252     while (*s != 0 && s < sf) {
00253 #if defined(host_cnv_use_wchar)
00254         ic = (int)(*s++);
00255 #else
00256         if ((ic=utf8_decode(&s,sf)) < 0)
00257             return false; // non-representable
00258 #endif
00259 
00260         oc = SBCS_From_Host_Find<MT>(ic,map,map_max);
00261         if (oc < 0)
00262             return false; // non-representable
00263 
00264         if (d >= df) return false;
00265         *d++ = (char)oc;
00266     }
00267 
00268     assert(d <= df);
00269     *d = 0;
00270 
00271     return true;
00272 }
00273 
00274 bool String_HOST_TO_ASCII(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/) {
00275     const host_cnv_char_t *sf = s + CROSS_LEN - 1;
00276     char *df = d + CROSS_LEN - 1;
00277     int ic;
00278 
00279     while (*s != 0 && s < sf) {
00280 #if defined(host_cnv_use_wchar)
00281         ic = (int)(*s++);
00282 #else
00283         if ((ic=utf8_decode(&s,sf)) < 0)
00284             return false; // non-representable
00285 #endif
00286 
00287         if (ic < 32 || ic > 127)
00288             return false; // non-representable
00289 
00290         if (d >= df) return false;
00291         *d++ = (char)ic;
00292     }
00293 
00294     assert(d <= df);
00295     *d = 0;
00296 
00297     return true;
00298 }
00299 
00300 bool cpwarn_once = false;
00301 
00302 bool CodePageHostToGuest(char *d/*CROSS_LEN*/,const host_cnv_char_t *s/*CROSS_LEN*/) {
00303     switch (dos.loaded_codepage) {
00304         case 437:
00305             return String_HOST_TO_SBCS<uint16_t>(d,s,cp437_to_unicode,sizeof(cp437_to_unicode)/sizeof(cp437_to_unicode[0]));
00306         case 932:
00307             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]));
00308         default:
00309             /* at this time, it would be cruel and unusual to not allow any file I/O just because
00310              * our code page support is so limited. */
00311             if (!cpwarn_once) {
00312                 cpwarn_once = true;
00313                 LOG_MSG("WARNING: No translation support (to guest) for code page %u",dos.loaded_codepage);
00314             }
00315             return String_HOST_TO_ASCII(d,s);
00316     }
00317 
00318     return false;
00319 }
00320 
00321 bool CodePageGuestToHost(host_cnv_char_t *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/) {
00322     switch (dos.loaded_codepage) {
00323         case 437:
00324             return String_SBCS_TO_HOST<uint16_t>(d,s,cp437_to_unicode,sizeof(cp437_to_unicode)/sizeof(cp437_to_unicode[0]));
00325         case 932:
00326             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]));
00327         default:
00328             /* at this time, it would be cruel and unusual to not allow any file I/O just because
00329              * our code page support is so limited. */
00330             if (!cpwarn_once) {
00331                 cpwarn_once = true;
00332                 LOG_MSG("WARNING: No translation support (to host) for code page %u",dos.loaded_codepage);
00333             }
00334             return String_ASCII_TO_HOST(d,s);
00335     }
00336 
00337     return false;
00338 }
00339 
00340 host_cnv_char_t *CodePageGuestToHost(const char *s) {
00341     if (!CodePageGuestToHost(cpcnv_temp,s))
00342         return NULL;
00343 
00344     return cpcnv_temp;
00345 }
00346 
00347 char *CodePageHostToGuest(const host_cnv_char_t *s) {
00348     if (!CodePageHostToGuest((char*)cpcnv_temp,s))
00349         return NULL;
00350 
00351     return (char*)cpcnv_temp;
00352 }
00353 
00354 bool localDrive::FileCreate(DOS_File * * file,const char * name,Bit16u /*attributes*/) {
00355     if (nocachedir) EmptyCache();
00356 
00357     if (readonly) {
00358                 DOS_SetError(DOSERR_WRITE_PROTECTED);
00359         return false;
00360     }
00361 
00362 //TODO Maybe care for attributes but not likely
00363         char newname[CROSS_LEN];
00364         strcpy(newname,basedir);
00365         strcat(newname,name);
00366         CROSS_FILENAME(newname);
00367         char* temp_name = dirCache.GetExpandName(newname); //Can only be used in till a new drive_cache action is preformed */
00368         /* Test if file exists (so we need to truncate it). don't add to dirCache then */
00369         bool existing_file=false;
00370 
00371     // guest to host code page translation
00372     host_cnv_char_t *host_name = CodePageGuestToHost(temp_name);
00373     if (host_name == NULL) {
00374         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00375                 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME
00376         return false;
00377     }
00378 
00379 #ifdef host_cnv_use_wchar
00380     FILE * test=_wfopen(host_name,L"rb+");
00381 #else
00382     FILE * test=fopen(host_name,"rb+");
00383 #endif
00384         if(test) {
00385                 fclose(test);
00386                 existing_file=true;
00387         }
00388         
00389 #ifdef host_cnv_use_wchar
00390         FILE * hand=_wfopen(host_name,L"wb+");
00391 #else
00392         FILE * hand=fopen(host_name,"wb+");
00393 #endif
00394         if (!hand){
00395                 LOG_MSG("Warning: file creation failed: %s",newname);
00396                 return false;
00397         }
00398    
00399         if(!existing_file) {
00400                 strcpy(newname,basedir);
00401                 strcat(newname,name);
00402                 CROSS_FILENAME(newname);
00403                 dirCache.AddEntry(newname, true);
00404         }
00405 
00406         /* Make the 16 bit device information */
00407         *file=new localFile(name,hand);
00408         (*file)->flags=OPEN_READWRITE;
00409 
00410         return true;
00411 }
00412 
00413 bool localDrive::FileOpen(DOS_File * * file,const char * name,Bit32u flags) {
00414     if (nocachedir) EmptyCache();
00415 
00416     if (readonly) {
00417         if ((flags&0xf) == OPEN_WRITE || (flags&0xf) == OPEN_READWRITE) {
00418             DOS_SetError(DOSERR_WRITE_PROTECTED);
00419             return false;
00420         }
00421     }
00422 
00423         const host_cnv_char_t * type;
00424         switch (flags&0xf) {
00425         case OPEN_READ:        type = _HT("rb");  break;
00426         case OPEN_WRITE:       type = _HT("rb+"); break;
00427         case OPEN_READWRITE:   type = _HT("rb+"); break;
00428         case OPEN_READ_NO_MOD: type = _HT("rb");  break; //No modification of dates. LORD4.07 uses this
00429         default:
00430                 DOS_SetError(DOSERR_ACCESS_CODE_INVALID);
00431                 return false;
00432         }
00433         char newname[CROSS_LEN];
00434         strcpy(newname,basedir);
00435         strcat(newname,name);
00436         CROSS_FILENAME(newname);
00437         dirCache.ExpandName(newname);
00438 
00439         //Flush the buffer of handles for the same file. (Betrayal in Antara)
00440         Bit8u i,drive=DOS_DRIVES;
00441         localFile *lfp;
00442         for (i=0;i<DOS_DRIVES;i++) {
00443                 if (Drives[i]==this) {
00444                         drive=i;
00445                         break;
00446                 }
00447         }
00448         for (i=0;i<DOS_FILES;i++) {
00449                 if (Files[i] && Files[i]->IsOpen() && Files[i]->GetDrive()==drive && Files[i]->IsName(name)) {
00450                         lfp=dynamic_cast<localFile*>(Files[i]);
00451                         if (lfp) lfp->Flush();
00452                 }
00453         }
00454 
00455     // guest to host code page translation
00456     host_cnv_char_t *host_name = CodePageGuestToHost(newname);
00457     if (host_name == NULL) {
00458         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00459                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00460         return false;
00461     }
00462 
00463 #ifdef host_cnv_use_wchar
00464         FILE * hand=_wfopen(host_name,type);
00465 #else
00466         FILE * hand=fopen(host_name,type);
00467 #endif
00468 //      Bit32u err=errno;
00469         if (!hand) { 
00470                 if((flags&0xf) != OPEN_READ) {
00471 #ifdef host_cnv_use_wchar
00472                         FILE * hmm=_wfopen(host_name,L"rb");
00473 #else
00474                         FILE * hmm=fopen(host_name,"rb");
00475 #endif
00476                         if (hmm) {
00477                                 fclose(hmm);
00478 #ifdef host_cnv_use_wchar
00479                                 LOG_MSG("Warning: file %ls exists and failed to open in write mode.\nPlease Remove write-protection",host_name);
00480 #else
00481                                 LOG_MSG("Warning: file %s exists and failed to open in write mode.\nPlease Remove write-protection",host_name);
00482 #endif
00483                         }
00484                 }
00485                 return false;
00486         }
00487 
00488         *file=new localFile(name,hand);
00489         (*file)->flags=flags;  //for the inheritance flag and maybe check for others.
00490 //      (*file)->SetFileName(host_name);
00491         return true;
00492 }
00493 
00494 FILE * localDrive::GetSystemFilePtr(char const * const name, char const * const type) {
00495 
00496         char newname[CROSS_LEN];
00497         strcpy(newname,basedir);
00498         strcat(newname,name);
00499         CROSS_FILENAME(newname);
00500         dirCache.ExpandName(newname);
00501 
00502     // guest to host code page translation
00503     host_cnv_char_t *host_name = CodePageGuestToHost(newname);
00504     if (host_name == NULL) {
00505         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00506         return NULL;
00507     }
00508 
00509 #ifdef host_cnv_use_wchar
00510     wchar_t wtype[8];
00511     unsigned int tis;
00512 
00513     // "type" always has ANSI chars (like "rb"), nothing fancy
00514     for (tis=0;type[tis] != 0 && tis < 7;tis++) wtype[tis] = (wchar_t)type[tis];
00515     assert(tis < 7); // guard
00516     wtype[tis] = 0;
00517 
00518         return _wfopen(host_name,wtype);
00519 #else
00520         return fopen(host_name,type);
00521 #endif
00522 }
00523 
00524 bool localDrive::GetSystemFilename(char *sysName, char const * const dosName) {
00525 
00526         strcpy(sysName, basedir);
00527         strcat(sysName, dosName);
00528         CROSS_FILENAME(sysName);
00529         dirCache.ExpandName(sysName);
00530 
00531     // guest to host code page translation
00532     host_cnv_char_t *host_name = CodePageGuestToHost(sysName);
00533     if (host_name == NULL) {
00534         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,sysName);
00535         return false;
00536     }
00537 
00538 #ifdef host_cnv_use_wchar
00539     // FIXME: GetSystemFilename as implemented cannot return the wide char filename
00540     return false;
00541 #else
00542     strcpy(sysName,host_name);
00543 #endif
00544 
00545         return true;
00546 }
00547 
00548 bool localDrive::FileUnlink(const char * name) {
00549     if (readonly) {
00550         DOS_SetError(DOSERR_WRITE_PROTECTED);
00551         return false;
00552     }
00553 
00554         char newname[CROSS_LEN];
00555         strcpy(newname,basedir);
00556         strcat(newname,name);
00557         CROSS_FILENAME(newname);
00558         char *fullname = dirCache.GetExpandName(newname);
00559 
00560     // guest to host code page translation
00561     host_cnv_char_t *host_name = CodePageGuestToHost(fullname);
00562     if (host_name == NULL) {
00563         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,fullname);
00564                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00565         return false;
00566     }
00567 
00568         if (ht_unlink(host_name)) {
00569                 //Unlink failed for some reason try finding it.
00570                 ht_stat_t buffer;
00571                 if(ht_stat(host_name,&buffer)) return false; // File not found.
00572 
00573 #ifdef host_cnv_use_wchar
00574                 FILE* file_writable = _wfopen(host_name,L"rb+");
00575 #else
00576                 FILE* file_writable = fopen(host_name,"rb+");
00577 #endif
00578                 if(!file_writable) return false; //No acces ? ERROR MESSAGE NOT SET. FIXME ?
00579                 fclose(file_writable);
00580 
00581                 //File exists and can technically be deleted, nevertheless it failed.
00582                 //This means that the file is probably open by some process.
00583                 //See if We have it open.
00584                 bool found_file = false;
00585                 for(Bitu i = 0;i < DOS_FILES;i++){
00586                         if(Files[i] && Files[i]->IsName(name)) {
00587                                 Bitu max = DOS_FILES;
00588                                 while(Files[i]->IsOpen() && max--) {
00589                                         Files[i]->Close();
00590                                         if (Files[i]->RemoveRef()<=0) break;
00591                                 }
00592                                 found_file=true;
00593                         }
00594                 }
00595                 if(!found_file) return false;
00596                 if (!ht_unlink(host_name)) {
00597                         dirCache.DeleteEntry(newname);
00598                         return true;
00599                 }
00600                 return false;
00601         } else {
00602                 dirCache.DeleteEntry(newname);
00603                 return true;
00604         }
00605 }
00606 
00607 bool localDrive::FindFirst(const char * _dir,DOS_DTA & dta,bool fcb_findfirst) {
00608         char tempDir[CROSS_LEN];
00609         strcpy(tempDir,basedir);
00610         strcat(tempDir,_dir);
00611         CROSS_FILENAME(tempDir);
00612 
00613     if (nocachedir) EmptyCache();
00614 
00615         if (allocation.mediaid==0xF0 ) {
00616                 EmptyCache(); //rescan floppie-content on each findfirst
00617         }
00618     
00619         char end[2]={CROSS_FILESPLIT,0};
00620         if (tempDir[strlen(tempDir)-1]!=CROSS_FILESPLIT) strcat(tempDir,end);
00621         
00622         Bit16u id;
00623         if (!dirCache.FindFirst(tempDir,id)) {
00624                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00625                 return false;
00626         }
00627         strcpy(srchInfo[id].srch_dir,tempDir);
00628         dta.SetDirID(id);
00629         
00630         Bit8u sAttr;
00631         dta.GetSearchParams(sAttr,tempDir);
00632 
00633         if (this->isRemote() && this->isRemovable()) {
00634                 // cdroms behave a bit different than regular drives
00635                 if (sAttr == DOS_ATTR_VOLUME) {
00636                         dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
00637                         return true;
00638                 }
00639         } else {
00640                 if (sAttr == DOS_ATTR_VOLUME) {
00641                         if ( strcmp(dirCache.GetLabel(), "") == 0 ) {
00642 //                              LOG(LOG_DOSMISC,LOG_ERROR)("DRIVELABEL REQUESTED: none present, returned  NOLABEL");
00643 //                              dta.SetResult("NO_LABEL",0,0,0,DOS_ATTR_VOLUME);
00644 //                              return true;
00645                                 DOS_SetError(DOSERR_NO_MORE_FILES);
00646                                 return false;
00647                         }
00648                         dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
00649                         return true;
00650                 } else if ((sAttr & DOS_ATTR_VOLUME)  && (*_dir == 0) && !fcb_findfirst) { 
00651                 //should check for a valid leading directory instead of 0
00652                 //exists==true if the volume label matches the searchmask and the path is valid
00653                         if (WildFileCmp(dirCache.GetLabel(),tempDir)) {
00654                                 dta.SetResult(dirCache.GetLabel(),0,0,0,DOS_ATTR_VOLUME);
00655                                 return true;
00656                         }
00657                 }
00658         }
00659         return FindNext(dta);
00660 }
00661 
00662 char * shiftjis_upcase(char * str);
00663 
00664 bool localDrive::FindNext(DOS_DTA & dta) {
00665 
00666         char * dir_ent;
00667         ht_stat_t stat_block;
00668         char full_name[CROSS_LEN];
00669         char dir_entcopy[CROSS_LEN];
00670 
00671         Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII];
00672         Bit8u find_attr;
00673 
00674         dta.GetSearchParams(srch_attr,srch_pattern);
00675         Bit16u id = dta.GetDirID();
00676 
00677 again:
00678         if (!dirCache.FindNext(id,dir_ent)) {
00679                 DOS_SetError(DOSERR_NO_MORE_FILES);
00680                 return false;
00681         }
00682         if(!WildFileCmp(dir_ent,srch_pattern)) goto again;
00683 
00684         strcpy(full_name,srchInfo[id].srch_dir);
00685         strcat(full_name,dir_ent);
00686         
00687         //GetExpandName might indirectly destroy dir_ent (by caching in a new directory 
00688         //and due to its design dir_ent might be lost.)
00689         //Copying dir_ent first
00690         strcpy(dir_entcopy,dir_ent);
00691 
00692     char *temp_name = dirCache.GetExpandName(full_name);
00693 
00694     // guest to host code page translation
00695     host_cnv_char_t *host_name = CodePageGuestToHost(temp_name);
00696     if (host_name == NULL) {
00697         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,temp_name);
00698                 goto again;//No symlinks and such
00699     }
00700 
00701         if (ht_stat(host_name,&stat_block)!=0) { 
00702                 goto again;//No symlinks and such
00703         }       
00704 
00705         if(stat_block.st_mode & S_IFDIR) find_attr=DOS_ATTR_DIRECTORY;
00706         else find_attr=DOS_ATTR_ARCHIVE;
00707         if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again;
00708         
00709         /*file is okay, setup everything to be copied in DTA Block */
00710         char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size;
00711 
00712         if(strlen(dir_entcopy)<DOS_NAMELENGTH_ASCII){
00713                 strcpy(find_name,dir_entcopy);
00714         if (IS_PC98_ARCH)
00715             shiftjis_upcase(find_name);
00716         else
00717             upcase(find_name);
00718     } 
00719 
00720         find_size=(Bit32u) stat_block.st_size;
00721         struct tm *time;
00722         if((time=localtime(&stat_block.st_mtime))!=0){
00723                 find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
00724                 find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
00725         } else {
00726                 find_time=6; 
00727                 find_date=4;
00728         }
00729         dta.SetResult(find_name,find_size,find_date,find_time,find_attr);
00730         return true;
00731 }
00732 
00733 bool localDrive::GetFileAttr(const char * name,Bit16u * attr) {
00734     if (nocachedir) EmptyCache();
00735 
00736         char newname[CROSS_LEN];
00737         strcpy(newname,basedir);
00738         strcat(newname,name);
00739         CROSS_FILENAME(newname);
00740         dirCache.ExpandName(newname);
00741 
00742     // guest to host code page translation
00743     host_cnv_char_t *host_name = CodePageGuestToHost(newname);
00744     if (host_name == NULL) {
00745         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00746                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00747         return false;
00748     }
00749 
00750         ht_stat_t status;
00751         if (ht_stat(host_name,&status)==0) {
00752                 *attr=DOS_ATTR_ARCHIVE;
00753                 if(status.st_mode & S_IFDIR) *attr|=DOS_ATTR_DIRECTORY;
00754                 return true;
00755         }
00756         *attr=0;
00757         return false; 
00758 }
00759 
00760 bool localDrive::MakeDir(const char * dir) {
00761     if (nocachedir) EmptyCache();
00762 
00763     if (readonly) {
00764         DOS_SetError(DOSERR_WRITE_PROTECTED);
00765         return false;
00766     }
00767 
00768         char newdir[CROSS_LEN];
00769         strcpy(newdir,basedir);
00770         strcat(newdir,dir);
00771         CROSS_FILENAME(newdir);
00772 
00773     char *temp_name = dirCache.GetExpandName(newdir);
00774 
00775     // guest to host code page translation
00776     host_cnv_char_t *host_name = CodePageGuestToHost(temp_name);
00777     if (host_name == NULL) {
00778         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir);
00779                 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME
00780         return false;
00781     }
00782 
00783 #if defined (WIN32)                                             /* MS Visual C++ */
00784         int temp=_wmkdir(host_name);
00785 #else
00786         int temp=mkdir(host_name,0700);
00787 #endif
00788         if (temp==0) dirCache.CacheOut(newdir,true);
00789 
00790         return (temp==0);// || ((temp!=0) && (errno==EEXIST));
00791 }
00792 
00793 bool localDrive::RemoveDir(const char * dir) {
00794     if (nocachedir) EmptyCache();
00795 
00796     if (readonly) {
00797         DOS_SetError(DOSERR_WRITE_PROTECTED);
00798         return false;
00799     }
00800 
00801         char newdir[CROSS_LEN];
00802         strcpy(newdir,basedir);
00803         strcat(newdir,dir);
00804         CROSS_FILENAME(newdir);
00805 
00806     char *temp_name = dirCache.GetExpandName(newdir);
00807 
00808     // guest to host code page translation
00809     host_cnv_char_t *host_name = CodePageGuestToHost(temp_name);
00810     if (host_name == NULL) {
00811         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir);
00812                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00813         return false;
00814     }
00815 
00816 #if defined (WIN32)                                             /* MS Visual C++ */
00817         int temp=_wrmdir(host_name);
00818 #else
00819         int temp=rmdir(host_name);
00820 #endif
00821         if (temp==0) dirCache.DeleteEntry(newdir,true);
00822         return (temp==0);
00823 }
00824 
00825 bool localDrive::TestDir(const char * dir) {
00826     if (nocachedir) EmptyCache();
00827 
00828         char newdir[CROSS_LEN];
00829         strcpy(newdir,basedir);
00830         strcat(newdir,dir);
00831         CROSS_FILENAME(newdir);
00832         dirCache.ExpandName(newdir);
00833 
00834     // guest to host code page translation
00835     host_cnv_char_t *host_name = CodePageGuestToHost(newdir);
00836     if (host_name == NULL) {
00837         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newdir);
00838         return false;
00839     }
00840 
00841         // Skip directory test, if "\"
00842         size_t len = strlen(newdir);
00843         if (len && (newdir[len-1]!='\\')) {
00844                 // It has to be a directory !
00845                 ht_stat_t test;
00846                 if (ht_stat(host_name,&test))           return false;
00847                 if ((test.st_mode & S_IFDIR)==0)        return false;
00848         };
00849         int temp=ht_access(host_name,F_OK);
00850         return (temp==0);
00851 }
00852 
00853 bool localDrive::Rename(const char * oldname,const char * newname) {
00854     if (readonly) {
00855         DOS_SetError(DOSERR_WRITE_PROTECTED);
00856         return false;
00857     }
00858 
00859     host_cnv_char_t *ht;
00860 
00861         char newold[CROSS_LEN];
00862         strcpy(newold,basedir);
00863         strcat(newold,oldname);
00864         CROSS_FILENAME(newold);
00865         dirCache.ExpandName(newold);
00866         
00867         char newnew[CROSS_LEN];
00868         strcpy(newnew,basedir);
00869         strcat(newnew,newname);
00870         CROSS_FILENAME(newnew);
00871     dirCache.ExpandName(newnew);
00872 
00873     // guest to host code page translation
00874     ht = CodePageGuestToHost(newold);
00875     if (ht == NULL) {
00876         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newold);
00877                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00878         return false;
00879     }
00880     host_cnv_char_t *o_temp_name = ht_strdup(ht);
00881 
00882     // guest to host code page translation
00883     ht = CodePageGuestToHost(newnew);
00884     if (ht == NULL) {
00885         free(o_temp_name);
00886         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newnew);
00887                 DOS_SetError(DOSERR_FILE_NOT_FOUND); // FIXME
00888         return false;
00889     }
00890     host_cnv_char_t *n_temp_name = ht_strdup(ht);
00891 
00892 #ifdef host_cnv_use_wchar
00893         int temp=_wrename(o_temp_name,n_temp_name);
00894 #else
00895         int temp=rename(o_temp_name,n_temp_name);
00896 #endif
00897 
00898         if (temp==0) dirCache.CacheOut(newnew);
00899 
00900     free(o_temp_name);
00901     free(n_temp_name);
00902 
00903         return (temp==0);
00904 
00905 }
00906 
00907 bool localDrive::AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters) {
00908         *_bytes_sector=allocation.bytes_sector;
00909         *_sectors_cluster=allocation.sectors_cluster;
00910         *_total_clusters=allocation.total_clusters;
00911         *_free_clusters=allocation.free_clusters;
00912         return true;
00913 }
00914 
00915 bool localDrive::FileExists(const char* name) {
00916     if (nocachedir) EmptyCache();
00917 
00918         char newname[CROSS_LEN];
00919         strcpy(newname,basedir);
00920         strcat(newname,name);
00921         CROSS_FILENAME(newname);
00922         dirCache.ExpandName(newname);
00923 
00924     // guest to host code page translation
00925     host_cnv_char_t *host_name = CodePageGuestToHost(newname);
00926     if (host_name == NULL) {
00927         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00928         return false;
00929     }
00930 
00931         ht_stat_t temp_stat;
00932         if(ht_stat(host_name,&temp_stat)!=0) return false;
00933         if(temp_stat.st_mode & S_IFDIR) return false;
00934         return true;
00935 }
00936 
00937 bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) {
00938     if (nocachedir) EmptyCache();
00939 
00940         char newname[CROSS_LEN];
00941         strcpy(newname,basedir);
00942         strcat(newname,name);
00943         CROSS_FILENAME(newname);
00944         dirCache.ExpandName(newname);
00945 
00946     // guest to host code page translation
00947     host_cnv_char_t *host_name = CodePageGuestToHost(newname);
00948     if (host_name == NULL) {
00949         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,newname);
00950         return false;
00951     }
00952 
00953         ht_stat_t temp_stat;
00954         if(ht_stat(host_name,&temp_stat)!=0) return false;
00955 
00956         /* Convert the stat to a FileStat */
00957         struct tm *time;
00958         if((time=localtime(&temp_stat.st_mtime))!=0) {
00959                 stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
00960                 stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
00961         } else {
00962 
00963         }
00964         stat_block->size=(Bit32u)temp_stat.st_size;
00965         return true;
00966 }
00967 
00968 
00969 Bit8u localDrive::GetMediaByte(void) {
00970         return allocation.mediaid;
00971 }
00972 
00973 bool localDrive::isRemote(void) {
00974         return false;
00975 }
00976 
00977 bool localDrive::isRemovable(void) {
00978         return false;
00979 }
00980 
00981 Bits localDrive::UnMount(void) { 
00982         delete this;
00983         return 0; 
00984 }
00985 
00986 /* helper functions for drive cache */
00987 void *localDrive::opendir(const char *name) {
00988     // guest to host code page translation
00989     host_cnv_char_t *host_name = CodePageGuestToHost(name);
00990     if (host_name == NULL) {
00991         LOG_MSG("%s: Filename '%s' from guest is non-representable on the host filesystem through code page conversion",__FUNCTION__,name);
00992         return NULL;
00993     }
00994 
00995         return open_directoryw(host_name);
00996 }
00997 
00998 void localDrive::closedir(void *handle) {
00999         close_directory((dir_information*)handle);
01000 }
01001 
01002 bool localDrive::read_directory_first(void *handle, char* entry_name, bool& is_directory) {
01003     host_cnv_char_t tmp[MAX_PATH+1];
01004 
01005     if (::read_directory_firstw((dir_information*)handle, tmp, is_directory)) {
01006         // guest to host code page translation
01007         char *n_temp_name = CodePageHostToGuest(tmp);
01008         if (n_temp_name == NULL) {
01009 #ifdef host_cnv_use_wchar
01010             LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp);
01011 #else
01012             LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp);
01013 #endif
01014             return false;
01015         }
01016         strcpy(entry_name,n_temp_name);
01017         return true;
01018     }
01019 
01020     return false;
01021 }
01022 
01023 bool localDrive::read_directory_next(void *handle, char* entry_name, bool& is_directory) {
01024     host_cnv_char_t tmp[MAX_PATH+1];
01025 
01026 next:
01027     if (::read_directory_nextw((dir_information*)handle, tmp, is_directory)) {
01028         // guest to host code page translation
01029         char *n_temp_name = CodePageHostToGuest(tmp);
01030         if (n_temp_name == NULL) {
01031 #ifdef host_cnv_use_wchar
01032             LOG_MSG("%s: Filename '%ls' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp);
01033 #else
01034             LOG_MSG("%s: Filename '%s' from host is non-representable on the guest filesystem through code page conversion",__FUNCTION__,tmp);
01035 #endif
01036             goto next;
01037         }
01038         strcpy(entry_name,n_temp_name);
01039         return true;
01040     }
01041 
01042     return false;
01043 }
01044 
01045 localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid) {
01046         strcpy(basedir,startdir);
01047         sprintf(info,"local directory %s",startdir);
01048         allocation.bytes_sector=_bytes_sector;
01049         allocation.sectors_cluster=_sectors_cluster;
01050         allocation.total_clusters=_total_clusters;
01051         allocation.free_clusters=_free_clusters;
01052         allocation.mediaid=_mediaid;
01053 
01054         dirCache.SetBaseDir(basedir,this);
01055 }
01056 
01057 
01058 //TODO Maybe use fflush, but that seemed to fuck up in visual c
01059 bool localFile::Read(Bit8u * data,Bit16u * size) {
01060         if ((this->flags & 0xf) == OPEN_WRITE) {        // check if file opened in write-only mode
01061                 DOS_SetError(DOSERR_ACCESS_DENIED);
01062                 return false;
01063         }
01064         if (last_action==WRITE) fseek(fhandle,ftell(fhandle),SEEK_SET);
01065         last_action=READ;
01066         *size=(Bit16u)fread(data,1,*size,fhandle);
01067         /* Fake harddrive motion. Inspector Gadget with soundblaster compatible */
01068         /* Same for Igor */
01069         /* hardrive motion => unmask irq 2. Only do it when it's masked as unmasking is realitively heavy to emulate */
01070     if (!IS_PC98_ARCH) {
01071         Bit8u mask = IO_Read(0x21);
01072         if(mask & 0x4 ) IO_Write(0x21,mask&0xfb);
01073     }
01074 
01075         return true;
01076 }
01077 
01078 bool localFile::Write(const Bit8u * data,Bit16u * size) {
01079         if ((this->flags & 0xf) == OPEN_READ) { // check if file opened in read-only mode
01080                 DOS_SetError(DOSERR_ACCESS_DENIED);
01081                 return false;
01082         }
01083         if (last_action==READ) fseek(fhandle,ftell(fhandle),SEEK_SET);
01084         last_action=WRITE;
01085         if(*size==0){  
01086         return (!ftruncate(fileno(fhandle),ftell(fhandle)));
01087     }
01088     else 
01089     {
01090                 *size=(Bit16u)fwrite(data,1,*size,fhandle);
01091                 return true;
01092     }
01093 }
01094 
01095 /* ert, 20100711: Locking extensions */
01096 #ifdef WIN32
01097 #include <sys/locking.h>
01098 bool localFile::LockFile(Bit8u mode, Bit32u pos, Bit16u size) {
01099         HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fhandle));
01100         BOOL bRet;
01101 
01102         switch (mode)
01103         {
01104         case 0: bRet = ::LockFile (hFile, pos, 0, size, 0); break;
01105         case 1: bRet = ::UnlockFile(hFile, pos, 0, size, 0); break;
01106         default: 
01107                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
01108                 return false;
01109         }
01110         //LOG_MSG("::LockFile %s", name);
01111 
01112         if (!bRet)
01113         {
01114                 switch (GetLastError())
01115                 {
01116                 case ERROR_ACCESS_DENIED:
01117                 case ERROR_LOCK_VIOLATION:
01118                 case ERROR_NETWORK_ACCESS_DENIED:
01119                 case ERROR_DRIVE_LOCKED:
01120                 case ERROR_SEEK_ON_DEVICE:
01121                 case ERROR_NOT_LOCKED:
01122                 case ERROR_LOCK_FAILED:
01123                         DOS_SetError(0x21);
01124                         break;
01125                 case ERROR_INVALID_HANDLE:
01126                         DOS_SetError(DOSERR_INVALID_HANDLE);
01127                         break;
01128                 case ERROR_INVALID_FUNCTION:
01129                 default:
01130                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
01131                         break;
01132                 }
01133         }
01134         return bRet;
01135 }
01136 #endif
01137 
01138 bool localFile::Seek(Bit32u * pos,Bit32u type) {
01139         int seektype;
01140         switch (type) {
01141         case DOS_SEEK_SET:seektype=SEEK_SET;break;
01142         case DOS_SEEK_CUR:seektype=SEEK_CUR;break;
01143         case DOS_SEEK_END:seektype=SEEK_END;break;
01144         default:
01145         //TODO Give some doserrorcode;
01146                 return false;//ERROR
01147         }
01148         int ret=fseek(fhandle,*reinterpret_cast<Bit32s*>(pos),seektype);
01149         if (ret!=0) {
01150                 // Out of file range, pretend everythings ok 
01151                 // and move file pointer top end of file... ?! (Black Thorne)
01152                 fseek(fhandle,0,SEEK_END);
01153         };
01154 #if 0
01155         fpos_t temppos;
01156         fgetpos(fhandle,&temppos);
01157         Bit32u * fake_pos=(Bit32u*)&temppos;
01158         *pos=*fake_pos;
01159 #endif
01160         *pos=(Bit32u)ftell(fhandle);
01161         last_action=NONE;
01162         return true;
01163 }
01164 
01165 bool localFile::Close() {
01166         // only close if one reference left
01167         if (refCtr==1) {
01168                 if(fhandle) fclose(fhandle); 
01169                 fhandle = 0;
01170                 open = false;
01171         };
01172 
01173         if (newtime) {
01174                 // backport from DOS_PackDate() and DOS_PackTime()
01175                 struct tm tim = { 0 };
01176                 tim.tm_sec  = (time&0x1f)*2;
01177                 tim.tm_min  = (time>>5)&0x3f;
01178                 tim.tm_hour = (time>>11)&0x1f;
01179                 tim.tm_mday = date&0x1f;
01180                 tim.tm_mon  = ((date>>5)&0x0f)-1;
01181                 tim.tm_year = (date>>9)+1980-1900;
01182                 //  have the C run-time library code compute whether standard time or daylight saving time is in effect.
01183                 tim.tm_isdst = -1;
01184                 // serialize time
01185                 mktime(&tim);
01186 
01187                 struct utimbuf ftim;
01188                 ftim.actime = ftim.modtime = mktime(&tim);
01189         
01190                 char fullname[DOS_PATHLENGTH];
01191                 strcpy(fullname, Drives[drive]->GetBaseDir());
01192                 strcat(fullname, name);
01193 //              Dos_SpecoalChar(fullname, true);
01194                 CROSS_FILENAME(fullname);
01195                 if (utime(fullname, &ftim)) {
01196 //                      extern int errno; 
01197 //                      LOG_MSG("Set time failed for %s (%s)", fullname, strerror(errno));
01198                         return false;
01199                 }
01200         }
01201 
01202         return true;
01203 }
01204 
01205 Bit16u localFile::GetInformation(void) {
01206         return read_only_medium?0x40:0;
01207 }
01208         
01209 
01210 Bit32u localFile::GetSeekPos() {
01211         return ftell( fhandle );
01212 }
01213 
01214 
01215 localFile::localFile(const char* _name, FILE * handle) {
01216         fhandle=handle;
01217         open=true;
01218         UpdateDateTimeFromHost();
01219 
01220         attr=DOS_ATTR_ARCHIVE;
01221         last_action=NONE;
01222         read_only_medium=false;
01223 
01224         name=0;
01225         SetName(_name);
01226 }
01227 
01228 void localFile::FlagReadOnlyMedium(void) {
01229         read_only_medium = true;
01230 }
01231 
01232 bool localFile::UpdateDateTimeFromHost(void) {
01233         if(!open) return false;
01234         struct stat temp_stat;
01235         fstat(fileno(fhandle),&temp_stat);
01236         struct tm * ltime;
01237         if((ltime=localtime(&temp_stat.st_mtime))!=0) {
01238                 time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec);
01239                 date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday);
01240         } else {
01241                 time=1;date=1;
01242         }
01243         return true;
01244 }
01245 
01246 void localFile::Flush(void) {
01247         if (last_action==WRITE) {
01248                 fseek(fhandle,ftell(fhandle),SEEK_SET);
01249                 last_action=NONE;
01250         }
01251 }
01252 
01253 
01254 // ********************************************
01255 // CDROM DRIVE
01256 // ********************************************
01257 
01258 int  MSCDEX_RemoveDrive(char driveLetter);
01259 int  MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit);
01260 bool MSCDEX_HasMediaChanged(Bit8u subUnit);
01261 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name);
01262 
01263 
01264 cdromDrive::cdromDrive(const char driveLetter, const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid, int& error)
01265                    :localDrive(startdir,_bytes_sector,_sectors_cluster,_total_clusters,_free_clusters,_mediaid) {
01266         // Init mscdex
01267         error = MSCDEX_AddDrive(driveLetter,startdir,subUnit);
01268         strcpy(info, "CDRom ");
01269         strcat(info, startdir);
01270         this->driveLetter = driveLetter;
01271         // Get Volume Label
01272         char name[32];
01273         if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
01274 }
01275 
01276 bool cdromDrive::FileOpen(DOS_File * * file,const char * name,Bit32u flags) {
01277         if ((flags&0xf)==OPEN_READWRITE) {
01278                 flags &= ~((unsigned int)OPEN_READWRITE);
01279         } else if ((flags&0xf)==OPEN_WRITE) {
01280                 DOS_SetError(DOSERR_ACCESS_DENIED);
01281                 return false;
01282         }
01283         bool retcode = localDrive::FileOpen(file,name,flags);
01284         if(retcode) (dynamic_cast<localFile*>(*file))->FlagReadOnlyMedium();
01285         return retcode;
01286 }
01287 
01288 bool cdromDrive::FileCreate(DOS_File * * /*file*/,const char * /*name*/,Bit16u /*attributes*/) {
01289         DOS_SetError(DOSERR_ACCESS_DENIED);
01290         return false;
01291 }
01292 
01293 bool cdromDrive::FileUnlink(const char * /*name*/) {
01294         DOS_SetError(DOSERR_ACCESS_DENIED);
01295         return false;
01296 }
01297 
01298 bool cdromDrive::RemoveDir(const char * /*dir*/) {
01299         DOS_SetError(DOSERR_ACCESS_DENIED);
01300         return false;
01301 }
01302 
01303 bool cdromDrive::MakeDir(const char * /*dir*/) {
01304         DOS_SetError(DOSERR_ACCESS_DENIED);
01305         return false;
01306 }
01307 
01308 bool cdromDrive::Rename(const char * /*oldname*/,const char * /*newname*/) {
01309         DOS_SetError(DOSERR_ACCESS_DENIED);
01310         return false;
01311 }
01312 
01313 bool cdromDrive::GetFileAttr(const char * name,Bit16u * attr) {
01314         bool result = localDrive::GetFileAttr(name,attr);
01315         if (result) *attr |= DOS_ATTR_READ_ONLY;
01316         return result;
01317 }
01318 
01319 bool cdromDrive::FindFirst(const char * _dir,DOS_DTA & dta,bool /*fcb_findfirst*/) {
01320         // If media has changed, reInit drivecache.
01321         if (MSCDEX_HasMediaChanged(subUnit)) {
01322                 dirCache.EmptyCache();
01323                 // Get Volume Label
01324                 char name[32];
01325                 if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
01326         }
01327         return localDrive::FindFirst(_dir,dta);
01328 }
01329 
01330 void cdromDrive::SetDir(const char* path) {
01331         // If media has changed, reInit drivecache.
01332         if (MSCDEX_HasMediaChanged(subUnit)) {
01333                 dirCache.EmptyCache();
01334                 // Get Volume Label
01335                 char name[32];
01336                 if (MSCDEX_GetVolumeName(subUnit,name)) dirCache.SetLabel(name,true,true);
01337         }
01338         localDrive::SetDir(path);
01339 }
01340 
01341 bool cdromDrive::isRemote(void) {
01342         return true;
01343 }
01344 
01345 bool cdromDrive::isRemovable(void) {
01346         return true;
01347 }
01348 
01349 Bits cdromDrive::UnMount(void) {
01350         if(MSCDEX_RemoveDrive(driveLetter)) {
01351                 delete this;
01352                 return 0;
01353         }
01354         return 2;
01355 }