![[]](/images/special/trans.gif)
LibN2L-4 Library Code ReferenceClassesCompounds Files Members Method Index Full Reference vfsUtilities.cppGo to the documentation of this file.00001 /************************************************************************ 00002 Nova-2 Library (libN2L, or simply n2l) Game development C++ Library 00003 Copyright (C) 2003 Aaron Cameron 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free Software 00017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 00019 A copy of the GNU Lesser General Public License has been provided with 00020 this library in the file 'COPYING'. 00021 00022 Contact information for the author of this library has been provided 00023 with this library in the file 'AUTHOR'. 00024 ************************************************************************/ 00025 #include "vfsUtilities.h" 00026 00027 #include "n2l/n2l.h" 00028 #include "n2l/dynVars.h" 00029 00030 #include "vfsConstants.h" 00031 00032 #include "cVfsOpenException.h" 00033 #include "cVfsReadException.h" 00034 00035 #include "vfsFileTypes.h" 00036 00037 #include "cVfsNodeInterface.h" 00038 #include "cVfsDirectory.h" 00039 #include "cVfsFile.h" 00040 00041 #include <map> 00042 #include <vector> 00043 00044 #include <sys/stat.h> 00045 #include <stdio.h> 00046 00047 #include <dirent.h> 00048 #include <errno.h> 00049 00050 #ifdef WIN32 00051 # include <windows.h> 00052 # define SHGFP_TYPE_CURRENT 0 00053 # define CSIDL_APPDATA 0x0001a 00054 # define CSIDL_CREATE 0x8000 00055 # ifndef S_ISDIR 00056 # define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) 00057 # endif 00058 # ifndef S_ISREG 00059 # define S_ISREG(mode) (((mode) & _S_IFREG) != 0) 00060 # endif 00061 # define getcwd _getcwd 00062 #else 00063 # include <sys/types.h> 00064 # include <pwd.h> 00065 # include <unistd.h> 00066 # include <libgen.h> 00067 #endif 00068 00069 using namespace std; 00070 00071 /******************************************************************************/ 00072 namespace n2l 00073 { 00074 00075 /**************************************************************************/ 00076 const tString vfsPermissionsAsString( const tUint &iPerms ) 00077 { 00078 tString temp( "---" ); 00079 if (iPerms&VfsPermission_Readable) temp[0] = 'r'; 00080 if (iPerms&VfsPermission_Writeable) temp[1] = 'w'; 00081 if (iPerms&VfsPermission_Executable) temp[2] = 'x'; 00082 return temp; 00083 } 00084 00085 /**************************************************************************/ 00086 const tFsNodeType getFsNodeType( const tFsNodeName &iName ) 00087 { 00088 tFsNodeName name = cleanFsPath(iName); 00089 struct stat statBuffer; 00090 if (0!=stat(name.c_str(),&statBuffer)) 00091 throw cVfsOpenException("getFsNodeType","Stat failed",name); 00092 00093 if (S_ISDIR(statBuffer.st_mode)) return FsNodeType_Directory; 00094 if (S_ISREG(statBuffer.st_mode)) return FsNodeType_File; 00095 00096 return FsNodeType_Unknown; 00097 } 00098 00099 /**************************************************************************/ 00100 const tUint getVfsNodePermissions( const tVfsNodeName &iName ) 00101 { 00102 return 0; 00103 } 00104 00105 /**************************************************************************/ 00106 const tUint getFsNodePermissions( const tFsNodeName & ) // i_name 00107 { 00108 return 0; 00109 } 00110 00111 /**************************************************************************/ 00112 const tFsFileSize getFsNodeSize( const tFsNodeName &i_name ) 00113 { 00114 FILE * fp = fopen( i_name.c_str(), "rb" ); 00115 if (0==fp) 00116 throw cVfsOpenException("getFsNodeSize","Open failed",i_name); 00117 00118 const tSint OldFpPos = ftell(fp); 00119 if (-1==OldFpPos) { 00120 fclose(fp); 00121 throw cVfsReadException( "getFsNodeSize", 00122 "Tell failed getting origin", i_name); 00123 } 00124 if (-1==fseek(fp,0,SEEK_END)) { 00125 fclose(fp); 00126 throw cVfsReadException("getFsNodeSize", "Seek end failed", i_name); 00127 } 00128 const tSint TotalSize = ftell(fp); 00129 if (-1==TotalSize) { 00130 fclose(fp); 00131 throw cVfsReadException( "getFsNodeSize", 00132 "Tell failed getting size", i_name); 00133 } 00134 if (-1==fseek(fp,OldFpPos,SEEK_SET)) { 00135 fclose(fp); 00136 throw cVfsReadException( "getFsNodeSize", "Seek origin failed", 00137 i_name); 00138 } 00139 00140 fclose(fp); 00141 00142 return tFsFileSize(TotalSize); 00143 } 00144 00145 /**************************************************************************/ 00146 void getFsNodeDump( const tFsNodeName &i_name, tString &o_buffer ) 00147 { 00148 o_buffer.clear(); 00149 00150 const tFsFileSize FileSize = getFsNodeSize(i_name); 00151 if (0==FileSize) return; 00152 00153 o_buffer.reserve( FileSize ); 00154 FILE * fp = fopen( i_name.c_str(), "rb" ); 00155 if (0==fp) 00156 throw cVfsReadException("getFsNodeDump", "Open failed", i_name); 00157 00158 const tUint ReadBufferSize = 1024*64; // 64k read buffer 00159 tUint8 readBuffer[ReadBufferSize]; 00160 00161 tUint fileBytesLeftToRead = FileSize; 00162 tUint bytesToRead = 00163 (fileBytesLeftToRead > 00164 ReadBufferSize?ReadBufferSize:fileBytesLeftToRead); 00165 00166 while (!feof(fp) && bytesToRead) { 00167 if ( 1!=fread((void*)(readBuffer), bytesToRead,1, fp) && 00168 !feof(fp) ) 00169 { 00170 fclose(fp); 00171 throw cVfsReadException( "getFsNodeDump", 00172 "Out of bytes before eof", i_name); 00173 } 00174 00175 o_buffer.append( (char*)(readBuffer), 00176 tString::size_type(bytesToRead) ); 00177 00178 fileBytesLeftToRead -= bytesToRead; 00179 bytesToRead = (fileBytesLeftToRead > 00180 ReadBufferSize?ReadBufferSize:fileBytesLeftToRead); 00181 } 00182 fclose(fp); 00183 } 00184 00185 /**************************************************************************/ 00186 const tVfsNodeName convertFsLiteralNameToVfs( const tFsNodeName &i_name ) 00187 { 00188 const char FsPathSepArray[] = { _n2l::FsPathSeperatorSymbol,'\0' }; 00189 const char VfsPathSepArray[] = { VfsPathSeperatorSymbol,'\0' }; 00190 return cleanVfsPath( substringReplace( FsPathSepArray, 00191 VfsPathSepArray, i_name) ); 00192 } 00193 00194 /**************************************************************************/ 00195 const tVfsNodeName cleanVfsPath( const tVfsNodeName &iName ) 00196 { 00197 return _n2l::cleanPath(iName,VfsPathSeperatorSymbol); 00198 } 00199 00200 /**************************************************************************/ 00201 const tFsNodeName cleanFsPath( const tFsNodeName & iName ) 00202 { 00203 return _n2l::cleanPath( _n2l::cleanPath( iName, 00204 _n2l::FsPathSeperatorSymbol), _n2l::AltFsPathSeperatorSymbol ); 00205 } 00206 00207 /**************************************************************************/ 00208 const vector<tVfsNodeName> splitVfsPathBlocks( const tVfsNodeName &i_name ) 00209 { 00210 vector<tVfsNodeName> temp; 00211 00212 explode(i_name,temp,VfsPathSeperatorSymbol,'\0','\0'); 00213 00214 // Remove blank entries 00215 for (vector<tVfsNodeName>::iterator blockIt = temp.begin(); 00216 blockIt!=temp.end();) 00217 { 00218 if (!blockIt->size()) blockIt = temp.erase(blockIt); 00219 else ++blockIt; 00220 } 00221 return temp; 00222 } 00223 00224 /**************************************************************************/ 00225 const tVfsNodeName stripFrontLiteralVfsName( const tVfsNodeName &iName, 00226 tVfsNodeName &oStrippedName ) 00227 { 00228 const tVfsNodeName::size_type StartPos = 00229 iName.find_first_not_of(VfsPathSeperatorSymbol); 00230 00231 if (StartPos == tVfsNodeName::npos) { 00232 oStrippedName = ""; 00233 return ""; 00234 } 00235 tVfsNodeName::size_type breakPos = iName.find( 00236 VfsPathSeperatorSymbol,StartPos+1); 00237 00238 if (breakPos == tVfsNodeName::npos ) { 00239 oStrippedName = iName.substr(StartPos,iName.size()-StartPos); 00240 return ""; 00241 } 00242 00243 const tVfsNodeName::difference_type StrippedNameSize = 00244 breakPos-StartPos; 00245 00246 if (StrippedNameSize<=0) { 00247 oStrippedName = ""; 00248 return ""; 00249 } 00250 const tVfsNodeName::size_type RemainStrSize = iName.size()-breakPos; 00251 00252 oStrippedName = iName.substr(StartPos,StrippedNameSize); 00253 if (RemainStrSize==1) return ""; 00254 return iName.substr(breakPos+1,RemainStrSize); 00255 } 00256 00257 /**************************************************************************/ 00258 const tString & simpleGuessVfsFileType( const tVfsNodeName &iName ) 00259 { 00260 static map<tString,const tString*> extLookup; 00261 // Get an extension if there is one 00262 const tString::size_type LastDotPos = iName.find_last_of("."); 00263 if (LastDotPos==tString::npos) return VfsFileType_Unknown; 00264 00265 // Get the extension 00266 const tString extStr = iName.substr( (LastDotPos+1), 00267 iName.size()-(LastDotPos+1) ); 00268 00269 // Build the extension list if we haven't already 00270 if (extLookup.empty()) { 00271 extLookup["bmp"] = &VfsFileType_BMP; 00272 extLookup["gif"] = &VfsFileType_GIF; 00273 extLookup["jpeg"] = &VfsFileType_JPEG; 00274 extLookup["jpg"] = &VfsFileType_JPEG; 00275 extLookup["jpe"] = &VfsFileType_JPEG; 00276 extLookup["png"] = &VfsFileType_PNG; 00277 extLookup["tiff"] = &VfsFileType_TIFF; 00278 extLookup["tif"] = &VfsFileType_TIFF; 00279 extLookup["tga"] = &VfsFileType_TGA; 00280 extLookup["pcx"] = &VfsFileType_PCX; 00281 00282 extLookup["wav"] = &VfsFileType_WAV; /**************************************************************************/ 00283 extLookup["mp3"] = &VfsFileType_MP3; 00284 extLookup["ogg"] = &VfsFileType_OGG; 00285 00286 extLookup["gz"] = &VfsFileType_GZIP; 00287 extLookup["tgz"] = &VfsFileType_GZIP; 00288 extLookup["tar"] = &VfsFileType_TAR; 00289 extLookup["bz2"] = &VfsFileType_BZIP2; 00290 extLookup["zip"] = &VfsFileType_ZIP; 00291 } 00292 00293 // Do the lookup 00294 const map<tString,const tString*>::const_iterator ExtPos = 00295 extLookup.find( extStr ); 00296 00297 if (ExtPos == extLookup.end()) return VfsFileType_Unknown; 00298 00299 return *(ExtPos->second); 00300 } 00301 00302 /**************************************************************************/ 00303 const tUint vfsNodeFileWithHeader( const cVfsNodeInterface &iNode, 00304 const tString &iHeader, tVfsFileBuffer &oBuffer ) 00305 { 00306 oBuffer.clear(); 00307 if (!iNode.likeFile()) 00308 throw cBadDataUseException( "vfsNodeFileWithHeader", 00309 "Node is not like a file" ); 00310 if (iNode.firstLine()!=iHeader) 00311 throw cBadDataUseException( "vfsNodeFileWithHeader", 00312 tString("File header: \"") + iNode.firstLine() + "\" not " 00313 " the required header: \"" + iHeader + "\"" ); 00314 00315 iNode.getBuffer( oBuffer ); 00316 const tUint DataOffset = iNode.firstLine().size()+1; 00317 if (oBuffer.size()<=DataOffset) 00318 throw cBadDataUseException( "vfsNodeFileWithHeader", 00319 "No data after header" ); 00320 return DataOffset; 00321 } 00322 00323 /**************************************************************************/ 00324 const tUint vfsNodeFileWithHeader( const cVfsNodeInterface &iNode, 00325 const tString &iHeader ) 00326 { 00327 if (!iNode.likeFile()) 00328 throw cBadDataUseException( "vfsNodeFileWithHeader", 00329 "Node is not like a file" ); 00330 00331 if (iNode.firstLine()!=iHeader) 00332 throw cBadDataUseException( "vfsNodeFileWithHeader", 00333 tString("File header: \"") + iNode.firstLine() + 00334 "\" not the required header: \"" + iHeader + "\"" ); 00335 00336 const tUint DataOffset = iNode.firstLine().size()+1; 00337 if (iNode.size()<=DataOffset) 00338 throw cBadDataUseException( "vfsNodeFileWithHeader", 00339 "No data after header" ); 00340 return DataOffset; 00341 } 00342 00343 /**************************************************************************/ 00344 void vfsNodeFileWithHeader( const cVfsNodeInterface &iNode, 00345 const tString &iHeader, cDynVar &oDef ) 00346 { 00347 if (!iNode.likeFile()) 00348 throw cBadDataUseException( "vfsNodeFileWithHeader", 00349 "Provided node isn\'t like a file" ); 00350 // Load it. 00351 if (iNode.firstLine()!=iHeader) 00352 throw cParsingException( "vfsNodeFileWithHeader", 00353 tString("This file isn\'t the right type. " 00354 "Wanted: \"") + iHeader + "\" but got: \"" + 00355 iNode.firstLine() + "\"" ); 00356 00357 if (iNode.buffer().size()<=(iNode.firstLine().size()+1)) 00358 throw cParsingException( "vfsNodeFileWithHeader", 00359 "No data after header" ); 00360 00361 // The type is ok, unserialize it for the caller to work with. 00362 oDef.unserialize( iNode.buffer().c_str() + 00363 iNode.firstLine().size()+1 ); 00364 } 00365 00366 /**************************************************************************/ 00367 const tVfsNodeName vfsGetCWD() 00368 { 00369 # ifdef WIN32 00370 tUint bufSize = MAX_PATH; 00371 # else 00372 tUint bufSize = 1024; 00373 # endif 00374 for (;;) { 00375 char *buf = new char[bufSize]; 00376 if (NULL == getcwd(buf, bufSize)) { 00377 delete []buf; 00378 if (errno == ERANGE && (bufSize*2>bufSize)) { 00379 bufSize *= 2; 00380 } else throw cException( "vfsGetCWD", 00381 "Failed, though I have no clue why. Errno was: " + 00382 asString(errno) ); 00383 } else { 00384 tVfsNodeName tmp = buf; 00385 delete []buf; 00386 return tmp; 00387 } 00388 } 00389 } 00390 00391 /**************************************************************************/ 00392 const tVfsNodeName vfsGetHomeDir() 00393 { 00394 tFsNodeName temp; 00395 # ifdef WIN32 00396 static HMODULE hSHFolderDLL = NULL; 00397 hSHFolderDLL = LoadLibrary("SHFOLDER.DLL"); 00398 00399 typedef HRESULT (WINAPI *SHGetFolderPathADDR)(HWND, int, 00400 HANDLE, DWORD, LPTSTR); 00401 static SHGetFolderPathADDR SHGetFolderPath = 0; 00402 00403 SHGetFolderPath = (SHGetFolderPathADDR)GetProcAddress( 00404 hSHFolderDLL, "SHGetFolderPathA" ); 00405 if (!SHGetFolderPath) throw cVfsException("vfsGetHomeDir", 00406 "Could not get proc addr for SHGetFolderPathA" ); 00407 00408 char path[MAX_PATH]; 00409 HRESULT r = SHGetFolderPath(0, CSIDL_APPDATA|CSIDL_CREATE, 00410 0, SHGFP_TYPE_CURRENT, path); 00411 if (r == S_FALSE || r == E_FAIL || r == E_INVALIDARG) 00412 throw cVfsException( "vfsGetHomeDir", 00413 "Failed calling ShGetFolderPath" ); 00414 temp = path; 00415 # else 00416 struct passwd *passWDLine = getpwuid( getuid() ); 00417 if (!passWDLine) 00418 throw cVfsException("vfsGetHomeDir", 00419 "Cannot get user passwd line"); 00420 temp = tFsNodeName( passWDLine->pw_dir ); 00421 # endif 00422 00423 return convertFsLiteralNameToVfs( temp ); 00424 } 00425 00426 /**************************************************************************/ 00427 const tString vfsGetExecBinaryDir( const tString &iArgV0 ) 00428 { 00429 if (iArgV0.empty()) 00430 throw cBadDataUseException( "vfsGetExecBinaryDir", 00431 "Called without an argv0." ); 00432 00433 # ifdef WIN32 00434 throw cUnsupportedMethodException( "vfsGetExecBinaryDir", 00435 "No implementation exists for this method. Actually, " 00436 "this method may work exactly the same as in Linux, it just " 00437 "hasn\'t been tested." ); 00438 # else 00439 vector<tString> path; 00440 tString binName; 00441 00442 // Figure out the binary name and the path described by iArgV0 00443 { 00444 char *tmp = new char[iArgV0.size()+1]; 00445 strcpy( tmp, iArgV0.c_str() ); 00446 path.push_back( dirname( tmp ) ); 00447 00448 strcpy( tmp, iArgV0.c_str() ); 00449 binName = basename( tmp ); 00450 delete []tmp; 00451 } 00452 00453 try { 00454 explode( n2lGetEnv("PATH"), path, ':', '\\', '\0', true ); 00455 } 00456 catch ( const cOutOfBoundsException & ) 00457 { 00458 // This is ok. Continue with no path defined. 00459 } 00460 00461 for (tUint i=0; i!=path.size(); ++i) { 00462 cAutoPtr<cVfsDirectory> sDir = 0; 00463 try { 00464 sDir = new cVfsDirectory( path[i] ); 00465 00466 cVfsDirectory::tConstIterator f = sDir->find( binName ); 00467 if (f==sDir->end()) continue; 00468 00469 cAutoPtr<const cVfsFile> file = 00470 f->second.as<const cVfsFile>(); 00471 if (!file.isSet()) continue; 00472 00473 if (file->isSymLink()) { 00474 // Let's give them this and be done with it. 00475 tString retV; 00476 { 00477 retV = file->readLink(); 00478 char *tmp = new char[retV.size()+1]; 00479 strcpy( tmp, retV.c_str() ); 00480 retV = dirname(tmp); 00481 delete []tmp; 00482 return retV; 00483 } 00484 00485 } else return path[i]; 00486 } 00487 catch ( const cException & ) { } 00488 } 00489 return ""; 00490 # endif 00491 } 00492 00493 } // namespace n2l 00494 00495 namespace _n2l 00496 { 00497 /**************************************************************************/ 00498 const n2l::tString cleanPath(const n2l::tString &iName, 00499 const char iPathSplit ) 00500 { 00501 // This is a big hacky, but it sort of fits in the scope of 00502 // what we're supposed to do so I don't feel TOO terrible about it. 00503 if (iName==".") return _n2l::cleanPath( n2l::vfsGetCWD(), iPathSplit ); 00504 00505 vector<n2l::tString> parts; 00506 vector<n2l::tString> cleanParts; 00507 n2l::explode(iName,parts,iPathSplit,'\0','\0'); 00508 for(vector<n2l::tString>::const_iterator it = parts.begin(); 00509 it!=parts.end(); ++it) 00510 { 00511 if (!it->empty() && *it!="." && *it!="..") 00512 cleanParts.push_back(*it); 00513 } 00514 n2l::tString finalStr; 00515 if (!iName.empty() && iName[0]==iPathSplit) 00516 finalStr.assign( 1, iPathSplit ); 00517 finalStr += n2l::implode(cleanParts,iPathSplit,'\0','\0'); 00518 return finalStr; 00519 } 00520 00521 } |