DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include "drives.h" 00021 #include "dos_inc.h" 00022 #include "support.h" 00023 #include "cross.h" 00024 00025 // STL stuff 00026 #include <vector> 00027 #include <iterator> 00028 #include <algorithm> 00029 00030 #if defined (WIN32) /* Win 32 */ 00031 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from 00032 #include <windows.h> 00033 #endif 00034 00035 #if defined (OS2) 00036 #define INCL_DOSERRORS 00037 #define INCL_DOSFILEMGR 00038 #include <os2.h> 00039 #endif 00040 00041 int fileInfoCounter = 0; 00042 00043 bool SortByName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) { 00044 return strcmp(a->shortname,b->shortname)<0; 00045 } 00046 00047 bool SortByNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) { 00048 return strcmp(a->shortname,b->shortname)>0; 00049 } 00050 00051 bool SortByDirName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) { 00052 // Directories first... 00053 if (a->isDir!=b->isDir) return (a->isDir>b->isDir); 00054 return strcmp(a->shortname,b->shortname)<0; 00055 } 00056 00057 bool SortByDirNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) { 00058 // Directories first... 00059 if (a->isDir!=b->isDir) return (a->isDir>b->isDir); 00060 return strcmp(a->shortname,b->shortname)>0; 00061 } 00062 00063 DOS_Drive_Cache::DOS_Drive_Cache(void) { 00064 dirBase = new CFileInfo; 00065 save_dir = 0; 00066 srchNr = 0; 00067 label[0] = 0; 00068 nextFreeFindFirst = 0; 00069 for (Bit32u i=0; i<MAX_OPENDIRS; i++) { dirSearch[i] = 0; dirFindFirst[i] = 0; } 00070 SetDirSort(DIRALPHABETICAL); 00071 updatelabel = true; 00072 } 00073 00074 DOS_Drive_Cache::DOS_Drive_Cache(const char* path, DOS_Drive *drive) { 00075 dirBase = new CFileInfo; 00076 save_dir = 0; 00077 srchNr = 0; 00078 label[0] = 0; 00079 nextFreeFindFirst = 0; 00080 for (Bit32u i=0; i<MAX_OPENDIRS; i++) { dirSearch[i] = 0; dirFindFirst[i] = 0; } 00081 SetDirSort(DIRALPHABETICAL); 00082 SetBaseDir(path,drive); 00083 updatelabel = true; 00084 } 00085 00086 DOS_Drive_Cache::~DOS_Drive_Cache(void) { 00087 Clear(); 00088 for (Bit32u i=0; i<MAX_OPENDIRS; i++) { DeleteFileInfo(dirFindFirst[i]); dirFindFirst[i]=0; } 00089 } 00090 00091 void DOS_Drive_Cache::Clear(void) { 00092 DeleteFileInfo(dirBase); dirBase = 0; 00093 nextFreeFindFirst = 0; 00094 for (Bit32u i=0; i<MAX_OPENDIRS; i++) dirSearch[i] = 0; 00095 } 00096 00097 void DOS_Drive_Cache::EmptyCache(void) { 00098 // Empty Cache and reinit 00099 Clear(); 00100 dirBase = new CFileInfo; 00101 save_dir = 0; 00102 srchNr = 0; 00103 SetBaseDir(basePath,drive); 00104 } 00105 00106 void DOS_Drive_Cache::SetLabel(const char* vname,bool cdrom,bool allowupdate) { 00107 /* allowupdate defaults to true. if mount sets a label then allowupdate is 00108 * false and will this function return at once after the first call. 00109 * The label will be set at the first call. */ 00110 00111 if(!this->updatelabel) return; 00112 this->updatelabel = allowupdate; 00113 Set_Label(vname,label,cdrom); 00114 LOG(LOG_DOSMISC,LOG_NORMAL)("DIRCACHE: Set volume label to %s",label); 00115 } 00116 00117 Bit16u DOS_Drive_Cache::GetFreeID(CFileInfo* dir) { 00118 if (dir->id != MAX_OPENDIRS) 00119 return dir->id; 00120 for (Bit16u i=0; i<MAX_OPENDIRS; i++) { 00121 if (!dirSearch[i]) { 00122 dir->id = i; 00123 return i; 00124 } 00125 } 00126 LOG(LOG_FILES,LOG_NORMAL)("DIRCACHE: Too many open directories!"); 00127 dir->id=0; 00128 return 0; 00129 } 00130 00131 void DOS_Drive_Cache::SetBaseDir(const char* baseDir, DOS_Drive *drive) { 00132 Bit16u id; 00133 strcpy(basePath,baseDir); 00134 this->drive = drive; 00135 if (OpenDir(baseDir,id)) { 00136 char* result = 0, *lresult = 0; 00137 ReadDir(id,result,lresult); 00138 } 00139 // Get Volume Label 00140 #if defined (WIN32) || defined (OS2) 00141 char labellocal[256]={ 0 }; 00142 char drives[4] = "C:\\"; 00143 drives[0] = basePath[0]; 00144 #if defined (WIN32) 00145 if (GetVolumeInformation(drives,labellocal,256,NULL,NULL,NULL,NULL,0)) { 00146 bool cdrom = false; 00147 UINT test = GetDriveType(drives); 00148 if(test == DRIVE_CDROM) cdrom = true; 00149 #else // OS2 00150 //TODO determine wether cdrom or not! 00151 FSINFO fsinfo; 00152 ULONG drivenumber = drive[0]; 00153 if (drivenumber > 26) { // drive letter was lowercase 00154 drivenumber = drive[0] - 'a' + 1; 00155 } 00156 APIRET rc = DosQueryFSInfo(drivenumber, FSIL_VOLSER, &fsinfo, sizeof(FSINFO)); 00157 if (rc == NO_ERROR) { 00158 bool cdrom = false; 00159 #endif 00160 /* Set label and allow being updated */ 00161 SetLabel(labellocal,cdrom,true); 00162 } 00163 #endif 00164 } 00165 00166 void DOS_Drive_Cache::ExpandName(char* path) { 00167 strcpy(path,GetExpandName(path)); 00168 } 00169 00170 char* DOS_Drive_Cache::GetExpandName(const char* path) { 00171 static char work [CROSS_LEN] = { 0 }; 00172 char dir [CROSS_LEN]; 00173 00174 work[0] = 0; 00175 strcpy (dir,path); 00176 00177 const char* pos = strrchr(path,CROSS_FILESPLIT); 00178 00179 if (pos) dir[pos-path+1] = 0; 00180 CFileInfo* dirInfo = FindDirInfo(dir, work); 00181 00182 if (pos) { 00183 // Last Entry = File 00184 strcpy(dir,pos+1); 00185 GetLongName(dirInfo, dir); 00186 strcat(work,dir); 00187 } 00188 00189 if (*work) { 00190 size_t len = strlen(work); 00191 #if defined (WIN32) 00192 //What about OS/2 00193 if((work[len-1] == CROSS_FILESPLIT ) && (len >= 2) && (work[len-2] != ':')) { 00194 #else 00195 if((len > 1) && (work[len-1] == CROSS_FILESPLIT )) { 00196 #endif 00197 work[len-1] = 0; // Remove trailing slashes except when in root 00198 } 00199 } 00200 return work; 00201 } 00202 00203 void DOS_Drive_Cache::AddEntry(const char* path, bool checkExists) { 00204 // Get Last part... 00205 char expand [CROSS_LEN]; 00206 00207 CFileInfo* dir = FindDirInfo(path,expand); 00208 const char* pos = strrchr(path,CROSS_FILESPLIT); 00209 00210 if (pos) { 00211 char file [CROSS_LEN]; 00212 strcpy(file,pos+1); 00213 // Check if file already exists, then don't add new entry... 00214 if (checkExists) { 00215 if (GetLongName(dir,file)>=0) return; 00216 } 00217 00218 char sfile[DOS_NAMELENGTH]; 00219 sfile[0]=0; 00220 CreateEntry(dir,file,sfile,false); 00221 00222 Bits index = GetLongName(dir,file); 00223 if (index>=0) { 00224 // Check if there are any open search dir that are affected by this... 00225 if (dir) for (Bit32u i=0; i<MAX_OPENDIRS; i++) { 00226 if ((dirSearch[i]==dir) && ((Bit32u)index<=dirSearch[i]->nextEntry)) 00227 dirSearch[i]->nextEntry++; 00228 } 00229 } 00230 // LOG_DEBUG("DIR: Added Entry %s",path); 00231 } else { 00232 // LOG_DEBUG("DIR: Error: Failed to add %s",path); 00233 } 00234 } 00235 00236 bool filename_not_strict_8x3(const char *n); 00237 void DOS_Drive_Cache::AddEntryDirOverlay(const char* path, char *sfile, bool checkExists) { 00238 // Get Last part... 00239 char file [CROSS_LEN]; 00240 char expand [CROSS_LEN]; 00241 char dironly[CROSS_LEN + 1]; 00242 00243 //When adding a directory, the directory we want to operate inside in is the above it. (which can go wrong if the directory already exists.) 00244 strcpy(dironly,path); 00245 char* post = strrchr(dironly,CROSS_FILESPLIT); 00246 00247 if (post) { 00248 #if defined (WIN32) 00249 //OS2 ? 00250 if (post > dironly && *(post - 1) == ':' && (post - dironly) == 2) 00251 post++; //move away from X: as need to end up with "x:\" 00252 #else 00253 //Lets hope this is not really used.. (root folder specified as overlay) 00254 if (post == dironly) 00255 post++; //move away from / 00256 #endif 00257 *post = 0; //TODO take care of AddEntryDIR D:\\piet) (so mount d d:\ as base) 00258 *(post + 1) = 0; //As FindDirInfo is skipping over the base directory 00259 } 00260 CFileInfo* dir = FindDirInfo(dironly,expand); 00261 const char* pos = strrchr(path,CROSS_FILESPLIT); 00262 00263 char sname[CROSS_LEN], *p=strrchr(sfile, '\\'); 00264 if (p!=NULL) 00265 strcpy(sname, p+1); 00266 else 00267 strcpy(sname, sfile); 00268 if (pos) { 00269 strcpy(file,pos + 1); 00270 // Check if directory already exists, then don't add new entry... 00271 if (checkExists) { 00272 Bits index = GetLongName(dir,(char *)(!strlen(sname)||filename_not_strict_8x3(sname)?file:sname)); 00273 if (index >= 0) { 00274 //directory already exists, but most likely empty. 00275 dir = dir->fileList[index]; 00276 if (dir->isOverlayDir && dir->fileList.empty()) { 00277 //maybe care about searches ? but this function should only run on cache inits/refreshes. 00278 //add dot entries 00279 CreateEntry(dir,".",".",true); 00280 CreateEntry(dir,"..","..",true); 00281 } 00282 return; 00283 } 00284 } 00285 00286 if (filename_not_strict_8x3(sname)) sname[0]=0; 00287 char* genname=CreateEntry(dir,file,sname,true); 00288 Bits index = GetLongName(dir,(char *)(!strlen(sname)||filename_not_strict_8x3(sname)?file:sname)); 00289 if (strlen(genname)) { 00290 strcpy(sfile, sname); 00291 p=strrchr(sfile, '\\'); 00292 if (p!=NULL) { 00293 *(p+1)=0; 00294 strcat(sfile, genname); 00295 } else 00296 strcpy(sfile, genname); 00297 } 00298 if (index>=0) { 00299 Bit32u i; 00300 // Check if there are any open search dir that are affected by this... 00301 if (dir) for (i=0; i<MAX_OPENDIRS; i++) { 00302 if ((dirSearch[i]==dir) && ((Bit32u)index<=dirSearch[i]->nextEntry)) 00303 dirSearch[i]->nextEntry++; 00304 } 00305 00306 dir = dir->fileList[index]; 00307 dir->isOverlayDir = true; 00308 CreateEntry(dir,".",".",true); 00309 CreateEntry(dir,"..","..",true); 00310 } 00311 // LOG_DEBUG("DIR: Added Entry %s",path); 00312 } else { 00313 // LOG_DEBUG("DIR: Error: Failed to add %s",path); 00314 } 00315 } 00316 00317 void DOS_Drive_Cache::DeleteEntry(const char* path, bool ignoreLastDir) { 00318 CacheOut(path,ignoreLastDir); 00319 if (dirSearch[srchNr] && (dirSearch[srchNr]->nextEntry>0)) dirSearch[srchNr]->nextEntry--; 00320 00321 if (!ignoreLastDir) { 00322 // Check if there are any open search dir that are affected by this... 00323 char expand [CROSS_LEN]; 00324 CFileInfo* dir = FindDirInfo(path,expand); 00325 if (dir) for (Bit32u i=0; i<MAX_OPENDIRS; i++) { 00326 if ((dirSearch[i]==dir) && (dirSearch[i]->nextEntry>0)) 00327 dirSearch[i]->nextEntry--; 00328 } 00329 } 00330 } 00331 00332 void DOS_Drive_Cache::CacheOut(const char* path, bool ignoreLastDir) { 00333 char expand[CROSS_LEN] = { 0 }; 00334 CFileInfo* dir; 00335 00336 if (ignoreLastDir) { 00337 char tmp[CROSS_LEN] = { 0 }; 00338 Bit32s len=0; 00339 const char* pos = strrchr(path,CROSS_FILESPLIT); 00340 if (pos) len = (Bit32s)(pos - path); 00341 if (len>0) { 00342 safe_strncpy(tmp,path,len+1); 00343 } else { 00344 strcpy(tmp,path); 00345 } 00346 dir = FindDirInfo(tmp,expand); 00347 } else { 00348 dir = FindDirInfo(path,expand); 00349 } 00350 00351 // LOG_DEBUG("DIR: Caching out %s : dir %s",expand,dir->orgname); 00352 // clear cache first? 00353 for (Bit32u i=0; i<MAX_OPENDIRS; i++) { 00354 dirSearch[i] = 0; //free[i] = true; 00355 } 00356 // delete file objects... 00357 //Maybe check if it is a file and then only delete the file and possibly the long name. instead of all objects in the dir. 00358 for(Bit32u i=0; i<dir->fileList.size(); i++) { 00359 if (dirSearch[srchNr]==dir->fileList[i]) dirSearch[srchNr] = 0; 00360 DeleteFileInfo(dir->fileList[i]); dir->fileList[i] = 0; 00361 } 00362 // clear lists 00363 dir->fileList.clear(); 00364 dir->longNameList.clear(); 00365 save_dir = 0; 00366 } 00367 00368 bool DOS_Drive_Cache::IsCachedIn(CFileInfo* curDir) { 00369 return (curDir->isOverlayDir || curDir->fileList.size()>0); 00370 } 00371 00372 00373 bool DOS_Drive_Cache::GetShortName(const char* fullname, char* shortname) { 00374 // Get Dir Info 00375 char expand[CROSS_LEN] = {0}; 00376 CFileInfo* curDir = FindDirInfo(fullname,expand); 00377 00378 const char* pos = strrchr(fullname,CROSS_FILESPLIT); 00379 if (pos) pos++; else return false; 00380 00381 std::vector<CFileInfo*>::size_type filelist_size = curDir->longNameList.size(); 00382 if (GCC_UNLIKELY(filelist_size<=0)) return false; 00383 00384 // The orgname part of the list is not sorted (shortname is)! So we can only walk through it. 00385 for(Bitu i = 0; i < filelist_size; i++) { 00386 #if defined (WIN32) || defined (OS2) /* Win 32 & OS/2*/ 00387 if (strcmp(pos,curDir->longNameList[i]->orgname) == 0) { 00388 #else 00389 if (strcmp(pos,curDir->longNameList[i]->orgname) == 0) { 00390 #endif 00391 strcpy(shortname,curDir->longNameList[i]->shortname); 00392 return true; 00393 } 00394 } 00395 return false; 00396 } 00397 00398 int DOS_Drive_Cache::CompareShortname(const char* compareName, const char* shortName) { 00399 char const* cpos = strchr(shortName,'~'); 00400 if (cpos) { 00401 /* the following code is replaced as it's not safe when char* is 64 bits */ 00402 /* Bits compareCount1 = (int)cpos - (int)shortName; 00403 char* endPos = strchr(cpos,'.'); 00404 Bitu numberSize = endPos ? int(endPos)-int(cpos) : strlen(cpos); 00405 00406 char* lpos = strchr(compareName,'.'); 00407 Bits compareCount2 = lpos ? int(lpos)-int(compareName) : strlen(compareName); 00408 if (compareCount2>8) compareCount2 = 8; 00409 00410 compareCount2 -= numberSize; 00411 if (compareCount2>compareCount1) compareCount1 = compareCount2; 00412 */ 00413 size_t compareCount1 = strcspn(shortName,"~"); 00414 size_t numberSize = strcspn(cpos,"."); 00415 size_t compareCount2 = strcspn(compareName,"."); 00416 if(compareCount2 > 8) compareCount2 = 8; 00417 /* We want 00418 * compareCount2 -= numberSize; 00419 * if (compareCount2>compareCount1) compareCount1 = compareCount2; 00420 * but to prevent negative numbers: 00421 */ 00422 if(compareCount2 > compareCount1 + numberSize) 00423 compareCount1 = compareCount2 - numberSize; 00424 return strncmp(compareName,shortName,compareCount1); 00425 } 00426 return strcmp(compareName,shortName); 00427 } 00428 00429 Bitu DOS_Drive_Cache::CreateShortNameID(CFileInfo* curDir, const char* name) { 00430 std::vector<CFileInfo*>::size_type filelist_size = curDir->longNameList.size(); 00431 if (GCC_UNLIKELY(filelist_size<=0)) return 1; // shortener IDs start with 1 00432 00433 Bitu foundNr = 0; 00434 Bits low = 0; 00435 Bits high = (Bits)(filelist_size-1); 00436 00437 while (low<=high) { 00438 Bits mid = (low+high)/2; 00439 Bits res = CompareShortname(name,curDir->longNameList[(size_t)mid]->shortname); 00440 00441 if (res>0) low = mid+1; else 00442 if (res<0) high = mid-1; 00443 else { 00444 // any more same x chars in next entries ? 00445 do { 00446 foundNr = curDir->longNameList[(size_t)mid]->shortNr; 00447 mid++; 00448 } while((Bitu)mid<curDir->longNameList.size() && (CompareShortname(name,curDir->longNameList[(size_t)mid]->shortname)==0)); 00449 break; 00450 } 00451 } 00452 return foundNr+1; 00453 } 00454 00455 bool DOS_Drive_Cache::RemoveTrailingDot(char* shortname) { 00456 // remove trailing '.' if no extension is available (Linux compatibility) 00457 size_t len = strlen(shortname); 00458 if (len && (shortname[len-1]=='.')) { 00459 if (len==1) return false; 00460 if ((len==2) && (shortname[0]=='.')) return false; 00461 shortname[len-1] = 0; 00462 return true; 00463 } 00464 return false; 00465 } 00466 00467 #define WINE_DRIVE_SUPPORT 1 00468 #if WINE_DRIVE_SUPPORT 00469 //Changes to interact with WINE by supporting their namemangling. 00470 //The code is rather slow, because orglist is unordered, so it needs to be avoided if possible. 00471 //Hence the tests in GetLongFileName 00472 00473 00474 // From the Wine project 00475 static Bits wine_hash_short_file_name( char* name, char* buffer ) 00476 { 00477 static const char hash_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"; 00478 static const char invalid_chars[] = { '*','?','<','>','|','"','+','=',',',';','[',']',' ','\345','~','.',0 }; 00479 char* p; 00480 char* ext; 00481 char* end = name + strlen(name); 00482 char* dst; 00483 unsigned short hash; 00484 int i; 00485 00486 // Compute the hash code of the file name 00487 for (p = name, hash = 0xbeef; p < end - 1; p++) 00488 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8); 00489 hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); // Last character 00490 00491 00492 // Find last dot for start of the extension 00493 for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p; 00494 00495 // Copy first 4 chars, replacing invalid chars with '_' 00496 for (i = 4, p = name, dst = buffer; i > 0; i--, p++) 00497 { 00498 if (p == end || p == ext) break; 00499 *dst++ = (*p < 0 || strchr( invalid_chars, *p ) != NULL) ? '_' : toupper(*p); 00500 } 00501 // Pad to 5 chars with '~' 00502 while (i-- >= 0) *dst++ = '~'; 00503 00504 // Insert hash code converted to 3 ASCII chars 00505 *dst++ = hash_chars[(hash >> 10) & 0x1f]; 00506 *dst++ = hash_chars[(hash >> 5) & 0x1f]; 00507 *dst++ = hash_chars[hash & 0x1f]; 00508 00509 // Copy the first 3 chars of the extension (if any) 00510 if (ext) 00511 { 00512 *dst++ = '.'; 00513 for (i = 3, ext++; (i > 0) && ext < end; i--, ext++) 00514 *dst++ = (*ext < 0 || strchr( invalid_chars, *ext ) != NULL) ? '_' : toupper(*ext); 00515 } 00516 00517 return (Bits)(dst - buffer); 00518 } 00519 #endif 00520 00521 Bits DOS_Drive_Cache::GetLongName(CFileInfo* curDir, char* shortName) { 00522 std::vector<CFileInfo*>::size_type filelist_size = curDir->fileList.size(); 00523 if (GCC_UNLIKELY(filelist_size<=0)) return -1; 00524 00525 // Remove dot, if no extension... 00526 RemoveTrailingDot(shortName); 00527 // Search long name and return array number of element 00528 Bits res; 00529 if (strlen(shortName)) 00530 for (Bitu i=0; i<filelist_size; i++) { 00531 if (!strcasecmp(shortName,curDir->fileList[i]->orgname) || !strcasecmp(shortName,curDir->fileList[i]->shortname)) { 00532 strcpy(shortName,curDir->fileList[i]->orgname); 00533 return (Bits)i; 00534 } 00535 } 00536 00537 #ifdef WINE_DRIVE_SUPPORT 00538 if (strlen(shortName) < 8 || shortName[4] != '~' || shortName[5] == '.' || shortName[6] == '.' || shortName[7] == '.') return -1; // not available 00539 // else it's most likely a Wine style short name ABCD~###, # = not dot (length at least 8) 00540 // The above test is rather strict as the following loop can be really slow if filelist_size is large. 00541 char buff[CROSS_LEN]; 00542 for (Bitu i = 0; i < filelist_size; i++) { 00543 res = wine_hash_short_file_name(curDir->fileList[i]->orgname,buff); 00544 buff[res] = 0; 00545 if (!strcmp(shortName,buff)) { 00546 // Found 00547 strcpy(shortName,curDir->fileList[i]->orgname); 00548 return (Bits)i; 00549 } 00550 } 00551 #endif 00552 // not available 00553 return -1; 00554 } 00555 00556 bool DOS_Drive_Cache::RemoveSpaces(char* str) { 00557 // Removes all spaces 00558 char* curpos = str; 00559 char* chkpos = str; 00560 while (*chkpos!=0) { 00561 if (*chkpos==' ') chkpos++; else *curpos++ = *chkpos++; 00562 } 00563 *curpos = 0; 00564 return (curpos!=chkpos); 00565 } 00566 00567 char * shiftjis_upcase(char * str); 00568 00569 void DOS_Drive_Cache::CreateShortName(CFileInfo* curDir, CFileInfo* info) { 00570 Bits len = 0; 00571 bool createShort = false; 00572 00573 char tmpNameBuffer[CROSS_LEN]; 00574 00575 char* tmpName = tmpNameBuffer; 00576 00577 // Remove Spaces 00578 strcpy(tmpName,info->orgname); 00579 if (IS_PC98_ARCH) 00580 shiftjis_upcase(tmpName); 00581 else 00582 upcase(tmpName); 00583 00584 createShort = RemoveSpaces(tmpName); 00585 00586 // Get Length of filename 00587 char* pos = strchr(tmpName,'.'); 00588 if (pos) { 00589 // ignore preceding '.' if extension is longer than "3" 00590 if (strlen(pos)>4) { 00591 while (*tmpName=='.') tmpName++; 00592 createShort = true; 00593 } 00594 pos = strchr(tmpName,'.'); 00595 if (pos) len = (Bits)(pos - tmpName); 00596 else len = (Bits)strlen(tmpName); 00597 } else { 00598 len = (Bits)strlen(tmpName); 00599 } 00600 00601 // Should shortname version be created ? 00602 createShort = createShort || (len>8); 00603 if (!createShort) { 00604 char buffer[CROSS_LEN]; 00605 strcpy(buffer,tmpName); 00606 createShort = (GetLongName(curDir,buffer)>=0); 00607 } 00608 00609 if (createShort) { 00610 // Create number 00611 char buffer[8]; 00612 info->shortNr = CreateShortNameID(curDir,tmpName); 00613 sprintf(buffer,"%d",(int)info->shortNr); 00614 // Copy first letters 00615 Bits tocopy = 0; 00616 size_t buflen = strlen(buffer); 00617 if ((size_t)len+buflen+1u>8u) tocopy = (Bits)(8u - buflen - 1u); 00618 else tocopy = len; 00619 safe_strncpy(info->shortname,tmpName,tocopy+1); 00620 // Copy number 00621 strcat(info->shortname,"~"); 00622 strcat(info->shortname,buffer); 00623 // Add (and cut) Extension, if available 00624 if (pos) { 00625 // Step to last extension... 00626 pos = strrchr(tmpName, '.'); 00627 // add extension 00628 strncat(info->shortname,pos,4); 00629 info->shortname[DOS_NAMELENGTH] = 0; 00630 } 00631 00632 // keep list sorted for CreateShortNameID to work correctly 00633 if (curDir->longNameList.size()>0) { 00634 if (!(strcmp(info->shortname,curDir->longNameList.back()->shortname)<0)) { 00635 // append at end of list 00636 curDir->longNameList.push_back(info); 00637 } else { 00638 // look for position where to insert this element 00639 bool found=false; 00640 std::vector<CFileInfo*>::iterator it; 00641 for (it=curDir->longNameList.begin(); it!=curDir->longNameList.end(); ++it) { 00642 if (strcmp(info->shortname,(*it)->shortname)<0) { 00643 found = true; 00644 break; 00645 } 00646 } 00647 // Put it in longname list... 00648 if (found) curDir->longNameList.insert(it,info); 00649 else curDir->longNameList.push_back(info); 00650 } 00651 } else { 00652 // empty file list, append 00653 curDir->longNameList.push_back(info); 00654 } 00655 } else { 00656 strcpy(info->shortname,tmpName); 00657 } 00658 RemoveTrailingDot(info->shortname); 00659 } 00660 00661 DOS_Drive_Cache::CFileInfo* DOS_Drive_Cache::FindDirInfo(const char* path, char* expandedPath) { 00662 // statics 00663 static char split[2] = { CROSS_FILESPLIT,0 }; 00664 char dir [CROSS_LEN]; 00665 const char* start = path; 00666 const char* pos; 00667 CFileInfo* curDir = dirBase; 00668 Bit16u id; 00669 00670 if (save_dir && (strcmp(path,save_path)==0)) { 00671 strcpy(expandedPath,save_expanded); 00672 return save_dir; 00673 } 00674 00675 // LOG_DEBUG("DIR: Find %s",path); 00676 00677 // Remove base dir path 00678 start += strlen(basePath); 00679 strcpy(expandedPath,basePath); 00680 00681 // hehe, baseDir should be cached in... 00682 if (!IsCachedIn(curDir)) { 00683 char work [CROSS_LEN]; 00684 strcpy(work,basePath); 00685 if (OpenDir(curDir,work,id)) { 00686 char buffer[CROSS_LEN]; 00687 char *result = 0, *lresult = 0; 00688 strcpy(buffer,dirPath); 00689 ReadDir(id,result,lresult); 00690 strcpy(dirPath,buffer); 00691 if (dirSearch[id]) { 00692 dirSearch[id]->id = MAX_OPENDIRS; 00693 dirSearch[id] = 0; 00694 } 00695 } 00696 } 00697 00698 do { 00699 // TODO: In PC-98 mode, use a Shift-JIS aware version of strchr() to find the path separator. 00700 // It's possible for the host path separator to appear in the trailing end of a double-byte character. 00701 // bool errorcheck = false; 00702 pos = strchr(start,CROSS_FILESPLIT); 00703 if (pos) { safe_strncpy(dir,start,pos-start+1); /*errorcheck = true;*/ } 00704 else { strcpy(dir,start); } 00705 00706 // Path found 00707 Bits nextDir = GetLongName(curDir,dir); 00708 strcat(expandedPath,dir); 00709 00710 // Error check 00711 /* if ((errorcheck) && (nextDir<0)) { 00712 LOG_DEBUG("DIR: Error: %s not found.",expandedPath); 00713 }; 00714 */ 00715 // Follow Directory 00716 if ((nextDir>=0) && curDir->fileList[(size_t)nextDir]->isDir) { 00717 curDir = curDir->fileList[(size_t)nextDir]; 00718 strcpy (curDir->orgname,dir); 00719 if (!IsCachedIn(curDir)) { 00720 if (OpenDir(curDir,expandedPath,id)) { 00721 char buffer[CROSS_LEN]; 00722 char *result = 0, *lresult = 0; 00723 strcpy(buffer,dirPath); 00724 ReadDir(id,result,lresult); 00725 strcpy(dirPath,buffer); 00726 if (dirSearch[id]) { 00727 dirSearch[id]->id = MAX_OPENDIRS; 00728 dirSearch[id] = 0; 00729 } 00730 } 00731 } 00732 } 00733 if (pos) { 00734 strcat(expandedPath,split); 00735 start = pos+1; 00736 } 00737 } while (pos); 00738 00739 // Save last result for faster access next time 00740 strcpy(save_path,path); 00741 strcpy(save_expanded,expandedPath); 00742 save_dir = curDir; 00743 00744 return curDir; 00745 } 00746 00747 bool DOS_Drive_Cache::OpenDir(const char* path, Bit16u& id) { 00748 char expand[CROSS_LEN] = {0}; 00749 CFileInfo* dir = FindDirInfo(path,expand); 00750 if (OpenDir(dir,expand,id)) { 00751 dirSearch[id]->nextEntry = 0; 00752 return true; 00753 } 00754 return false; 00755 } 00756 00757 bool DOS_Drive_Cache::OpenDir(CFileInfo* dir, const char* expand, Bit16u& id) { 00758 id = GetFreeID(dir); 00759 dirSearch[id] = dir; 00760 char expandcopy [CROSS_LEN]; 00761 strcpy(expandcopy,expand); 00762 // Add "/" 00763 if (expandcopy[strlen(expandcopy)-1]!=CROSS_FILESPLIT) { 00764 char end[2]={CROSS_FILESPLIT,0}; 00765 strcat(expandcopy,end); 00766 } 00767 // open dir 00768 if (dirSearch[id]) { 00769 // open dir 00770 void* dirp = drive->opendir(expandcopy); 00771 if (dirp || dir->isOverlayDir) { 00772 // Reset it.. 00773 if (dirp) drive->closedir(dirp); 00774 strcpy(dirPath,expandcopy); 00775 return true; 00776 } 00777 if (dirSearch[id]) { 00778 dirSearch[id]->id = MAX_OPENDIRS; 00779 dirSearch[id] = 0; 00780 } 00781 } 00782 return false; 00783 } 00784 00785 char* DOS_Drive_Cache::CreateEntry(CFileInfo* dir, const char* name, const char* sname, bool is_directory) { 00786 CFileInfo* info = new CFileInfo; 00787 strcpy(info->shortname, sname); 00788 strcpy(info->orgname, name); 00789 info->shortNr = 0; 00790 info->isDir = is_directory; 00791 00792 // Check for long filenames... 00793 if (sname[0]==0) CreateShortName(dir, info); 00794 00795 // keep list sorted (so GetLongName works correctly, used by CreateShortName in this routine) 00796 if (dir->fileList.size()>0) { 00797 if (!(strcmp(info->shortname,dir->fileList.back()->shortname)<0)) { 00798 // append at end of list 00799 dir->fileList.push_back(info); 00800 } else { 00801 bool found = false; 00802 // look for position where to insert this element 00803 std::vector<CFileInfo*>::iterator it; 00804 for (it=dir->fileList.begin(); it!=dir->fileList.end(); ++it) { 00805 if (strcmp(info->shortname,(*it)->shortname)<0) { 00806 found = true; 00807 break; 00808 } 00809 } 00810 // Put file in lists 00811 if (found) dir->fileList.insert(it,info); 00812 else dir->fileList.push_back(info); 00813 } 00814 } else { 00815 // empty file list, append 00816 dir->fileList.push_back(info); 00817 } 00818 static char sgenname[DOS_NAMELENGTH+1]; 00819 strcpy(sgenname, info->shortname); 00820 return sgenname; 00821 } 00822 00823 void DOS_Drive_Cache::CopyEntry(CFileInfo* dir, CFileInfo* from) { 00824 CFileInfo* info = new CFileInfo; 00825 // just copy things into new fileinfo 00826 strcpy(info->orgname, from->orgname); 00827 strcpy(info->shortname, from->shortname); 00828 info->shortNr = from->shortNr; 00829 info->isDir = from->isDir; 00830 00831 dir->fileList.push_back(info); 00832 } 00833 00834 bool DOS_Drive_Cache::ReadDir(Bit16u id, char* &result, char * &lresult) { 00835 // shouldnt happen... 00836 if (id>=MAX_OPENDIRS) return false; 00837 00838 if (!IsCachedIn(dirSearch[id])) { 00839 // Try to open directory 00840 void* dirp = drive->opendir(dirPath); 00841 if (!dirp) { 00842 if (dirSearch[id]) { 00843 dirSearch[id]->id = MAX_OPENDIRS; 00844 dirSearch[id] = 0; 00845 } 00846 return false; 00847 } 00848 // Read complete directory 00849 char dir_name[CROSS_LEN], dir_sname[DOS_NAMELENGTH+1]; 00850 bool is_directory; 00851 if (drive->read_directory_first(dirp, dir_name, dir_sname, is_directory)) { 00852 CreateEntry(dirSearch[id], dir_name, dir_sname, is_directory); 00853 while (drive->read_directory_next(dirp, dir_name, dir_sname, is_directory)) { 00854 CreateEntry(dirSearch[id], dir_name, dir_sname, is_directory); 00855 } 00856 } 00857 00858 // close dir 00859 drive->closedir(dirp); 00860 00861 // Info 00862 /* if (!dirp) { 00863 LOG_DEBUG("DIR: Error Caching in %s",dirPath); 00864 return false; 00865 } else { 00866 char buffer[128]; 00867 sprintf(buffer,"DIR: Caching in %s (%d Files)",dirPath,dirSearch[srchNr]->fileList.size()); 00868 LOG_DEBUG(buffer); 00869 };*/ 00870 } 00871 if (SetResult(dirSearch[id], result, lresult, dirSearch[id]->nextEntry)) return true; 00872 if (dirSearch[id]) { 00873 dirSearch[id]->id = MAX_OPENDIRS; 00874 dirSearch[id] = 0; 00875 } 00876 return false; 00877 } 00878 00879 bool DOS_Drive_Cache::SetResult(CFileInfo* dir, char* &result, char* &lresult, Bitu entryNr) 00880 { 00881 static char res[CROSS_LEN] = { 0 }; 00882 static char lres[CROSS_LEN] = { 0 }; 00883 00884 result = res; 00885 lresult = lres; 00886 00887 if (entryNr>=dir->fileList.size()) return false; 00888 CFileInfo* info = dir->fileList[entryNr]; 00889 // copy filename, short version 00890 strcpy(res,info->shortname); 00891 strcpy(lres,info->orgname); 00892 // Set to next Entry 00893 dir->nextEntry = entryNr+1; 00894 return true; 00895 } 00896 00897 // FindFirst / FindNext 00898 bool DOS_Drive_Cache::FindFirst(char* path, Bit16u& id) { 00899 Bit16u dirID; 00900 // Cache directory in 00901 if (!OpenDir(path,dirID)) return false; 00902 00903 //Find a free slot. 00904 //If the next one isn't free, move on to the next, if none is free => reset and assume the worst 00905 Bit16u local_findcounter = 0; 00906 while ( local_findcounter < MAX_OPENDIRS ) { 00907 if (dirFindFirst[this->nextFreeFindFirst] == 0) break; 00908 if (++this->nextFreeFindFirst >= MAX_OPENDIRS) this->nextFreeFindFirst = 0; //Wrap around 00909 local_findcounter++; 00910 } 00911 00912 Bit16u dirFindFirstID = this->nextFreeFindFirst++; 00913 if (this->nextFreeFindFirst >= MAX_OPENDIRS) this->nextFreeFindFirst = 0; //Increase and wrap around for the next search. 00914 00915 if (local_findcounter == MAX_OPENDIRS) { //Here is the reset from above. 00916 // no free slot found... 00917 LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst/Next: All slots full. Resetting"); 00918 // Clear the internal list then. 00919 dirFindFirstID = 0; 00920 this->nextFreeFindFirst = 1; //the next free one after this search 00921 for(Bitu n=0; n<MAX_OPENDIRS;n++) { 00922 // Clear and reuse slot 00923 DeleteFileInfo(dirFindFirst[n]); 00924 dirFindFirst[n]=0; 00925 } 00926 00927 } 00928 dirFindFirst[dirFindFirstID] = new CFileInfo(); 00929 dirFindFirst[dirFindFirstID]-> nextEntry = 0; 00930 00931 // Copy entries to use with FindNext 00932 for (Bitu i=0; i<dirSearch[dirID]->fileList.size(); i++) { 00933 CopyEntry(dirFindFirst[dirFindFirstID],dirSearch[dirID]->fileList[i]); 00934 } 00935 // Now re-sort the fileList accordingly to output 00936 switch (sortDirType) { 00937 case ALPHABETICAL : break; 00938 // case ALPHABETICAL : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByName); break; 00939 case DIRALPHABETICAL : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByDirName); break; 00940 case ALPHABETICALREV : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByNameRev); break; 00941 case DIRALPHABETICALREV : std::sort(dirFindFirst[dirFindFirstID]->fileList.begin(), dirFindFirst[dirFindFirstID]->fileList.end(), SortByDirNameRev); break; 00942 case NOSORT : break; 00943 } 00944 00945 // LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst : %s (ID:%02X)",path,dirFindFirstID); 00946 id = dirFindFirstID; 00947 return true; 00948 } 00949 00950 bool DOS_Drive_Cache::FindNext(Bit16u id, char* &result, char* &lresult) { 00951 // out of range ? 00952 if ((id>=MAX_OPENDIRS) || !dirFindFirst[id]) { 00953 LOG(LOG_MISC,LOG_ERROR)("DIRCACHE: FindFirst/Next failure : ID out of range: %04X",id); 00954 return false; 00955 } 00956 if (!SetResult(dirFindFirst[id], result, lresult, dirFindFirst[id]->nextEntry)) { 00957 // free slot 00958 DeleteFileInfo(dirFindFirst[id]); dirFindFirst[id] = 0; 00959 return false; 00960 } 00961 return true; 00962 } 00963 00964 void DOS_Drive_Cache::ClearFileInfo(CFileInfo *dir) { 00965 for(Bit32u i=0; i<dir->fileList.size(); i++) { 00966 if (CFileInfo *info = dir->fileList[i]) 00967 ClearFileInfo(info); 00968 } 00969 if (dir->id != MAX_OPENDIRS) { 00970 dirSearch[dir->id] = 0; 00971 dir->id = MAX_OPENDIRS; 00972 } 00973 } 00974 00975 void DOS_Drive_Cache::DeleteFileInfo(CFileInfo *dir) { 00976 if (dir) 00977 ClearFileInfo(dir); 00978 delete dir; 00979 }