// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdmisc.h" #include "nel/misc/big_file.h" #include "nel/misc/path.h" using namespace std; using namespace NLMISC; namespace NLMISC { //CBigFile *CBigFile::_Singleton = NULL; NLMISC_SAFE_SINGLETON_IMPL(CBigFile); // *************************************************************************** void CBigFile::releaseInstance() { if (_Instance) { NLMISC::INelContext::getInstance().releaseSingletonPointer("CBigFile", _Instance); delete _Instance; _Instance = NULL; } } // *************************************************************************** CBigFile::CThreadFileArray::CThreadFileArray() { _CurrentId= 0; } // *************************************************************************** uint32 CBigFile::CThreadFileArray::allocate() { return _CurrentId++; } // *************************************************************************** CBigFile::CHandleFile &CBigFile::CThreadFileArray::get(uint32 index) { // If the thread struct ptr is NULL, must allocate it. vector<CHandleFile> *ptr= (vector<CHandleFile>*)_TDS.getPointer(); if(ptr==NULL) { ptr= new vector<CHandleFile>; _TDS.setPointer(ptr); } // if the vector is not allocated, allocate it (empty entries filled with NULL => not opened FILE* in this thread) if(index>=ptr->size()) { ptr->resize(index+1); } return (*ptr)[index]; } // *************************************************************************** void CBigFile::currentThreadFinished() { _ThreadFileArray.currentThreadFinished(); } // *************************************************************************** void CBigFile::CThreadFileArray::currentThreadFinished() { vector<CHandleFile> *ptr= (vector<CHandleFile>*)_TDS.getPointer(); if (ptr==NULL) return; for (uint k = 0; k < ptr->size(); ++k) { if ((*ptr)[k].File) { fclose((*ptr)[k].File); (*ptr)[k].File = NULL; } } delete ptr; _TDS.setPointer(NULL); } // *************************************************************************** //CBigFile::CBigFile () //{ //} // //// *************************************************************************** //CBigFile &CBigFile::getInstance () //{ // if (_Singleton == NULL) // { // _Singleton = new CBigFile(); // } // return *_Singleton; //} // *************************************************************************** bool CBigFile::add (const std::string &sBigFileName, uint32 nOptions) { // Is already the same bigfile name ? string bigfilenamealone = toLower(CFile::getFilename (sBigFileName)); if (_BNPs.find(bigfilenamealone) != _BNPs.end()) { nlwarning ("CBigFile::add : bigfile %s already added.", bigfilenamealone.c_str()); return false; } // Create the new bnp entry BNP &bnp = _BNPs[bigfilenamealone]; bnp.BigFileName= sBigFileName; // Allocate a new ThreadSafe FileId for this bnp. bnp.ThreadFileId= _ThreadFileArray.allocate(); // Get a ThreadSafe handle on the file CHandleFile &handle= _ThreadFileArray.get(bnp.ThreadFileId); // Open the big file. handle.File = fopen (sBigFileName.c_str(), "rb"); if (handle.File == NULL) return false; uint32 nFileSize=CFile::getFileSize (handle.File); //nlfseek64 (handle.File, 0, SEEK_END); //uint32 nFileSize = ftell (handle.File); // Result if (nlfseek64 (handle.File, nFileSize-4, SEEK_SET) != 0) { fclose (handle.File); handle.File = NULL; return false; } uint32 nOffsetFromBeginning; if (fread (&nOffsetFromBeginning, sizeof(uint32), 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } if (nlfseek64 (handle.File, nOffsetFromBeginning, SEEK_SET) != 0) { fclose (handle.File); handle.File = NULL; return false; } // Read the file count uint32 nNbFile; if (fread (&nNbFile, sizeof(uint32), 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } map<string,BNPFile> tempMap; for (uint32 i = 0; i < nNbFile; ++i) { char FileName[256]; uint8 nStringSize; if (fread (&nStringSize, 1, 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } if (fread (FileName, nStringSize, 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } FileName[nStringSize] = 0; uint32 nFileSize2; if (fread (&nFileSize2, sizeof(uint32), 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } uint32 nFilePos; if (fread (&nFilePos, sizeof(uint32), 1, handle.File) != 1) { fclose (handle.File); handle.File = NULL; return false; } BNPFile bnpfTmp; bnpfTmp.Pos = nFilePos; bnpfTmp.Size = nFileSize2; tempMap.insert (make_pair(toLower(string(FileName)), bnpfTmp)); } if (nlfseek64 (handle.File, 0, SEEK_SET) != 0) { fclose (handle.File); handle.File = NULL; return false; } // Convert temp map if (nNbFile > 0) { uint nSize = 0, nNb = 0; map<string,BNPFile>::iterator it = tempMap.begin(); while (it != tempMap.end()) { nSize += (uint)it->first.size() + 1; nNb++; it++; } bnp.FileNames = new char[nSize]; memset(bnp.FileNames, 0, nSize); bnp.Files.resize(nNb); it = tempMap.begin(); nSize = 0; nNb = 0; while (it != tempMap.end()) { strcpy(bnp.FileNames+nSize, it->first.c_str()); bnp.Files[nNb].Name = bnp.FileNames+nSize; bnp.Files[nNb].Size = it->second.Size; bnp.Files[nNb].Pos = it->second.Pos; nSize += (uint)it->first.size() + 1; nNb++; it++; } } // End of temp map conversion if (nOptions&BF_CACHE_FILE_ON_OPEN) bnp.CacheFileOnOpen = true; else bnp.CacheFileOnOpen = false; if (!(nOptions&BF_ALWAYS_OPENED)) { fclose (handle.File); handle.File = NULL; bnp.AlwaysOpened = false; } else { bnp.AlwaysOpened = true; } nldebug("BigFile : added bnp '%s' to the collection", bigfilenamealone.c_str()); return true; } // *************************************************************************** void CBigFile::remove (const std::string &sBigFileName) { if (_BNPs.find (sBigFileName) != _BNPs.end()) { map<string, BNP>::iterator it = _BNPs.find (sBigFileName); BNP &rbnp = it->second; // Get a ThreadSafe handle on the file CHandleFile &handle= _ThreadFileArray.get(rbnp.ThreadFileId); // close it if needed if (handle.File != NULL) { fclose (handle.File); handle.File= NULL; } delete [] rbnp.FileNames; _BNPs.erase (it); } } // *************************************************************************** bool CBigFile::isBigFileAdded(const std::string &sBigFileName) { // Is already the same bigfile name ? string bigfilenamealone = CFile::getFilename (sBigFileName); return _BNPs.find(bigfilenamealone) != _BNPs.end(); } // *************************************************************************** void CBigFile::list (const std::string &sBigFileName, std::vector<std::string> &vAllFiles) { string lwrFileName = toLower(sBigFileName); if (_BNPs.find (lwrFileName) == _BNPs.end()) return; vAllFiles.clear (); BNP &rbnp = _BNPs.find (lwrFileName)->second; vector<BNPFile>::iterator it = rbnp.Files.begin(); while (it != rbnp.Files.end()) { vAllFiles.push_back (string(it->Name)); // Add the name of the file to the return vector ++it; } } // *************************************************************************** void CBigFile::removeAll () { while (_BNPs.begin() != _BNPs.end()) { remove (_BNPs.begin()->first); } } // *************************************************************************** bool CBigFile::getFileInternal (const std::string &sFileName, BNP *&zeBnp, BNPFile *&zeBnpFile) { string zeFileName, zeBigFileName, lwrFileName = toLower(sFileName); string::size_type i, nPos = sFileName.find ('@'); if (nPos == string::npos) { return false; } for (i = 0; i < nPos; ++i) zeBigFileName += lwrFileName[i]; ++i; // Skip @ for (; i < lwrFileName.size(); ++i) zeFileName += lwrFileName[i]; if (_BNPs.find (zeBigFileName) == _BNPs.end()) { return false; } BNP &rbnp = _BNPs.find (zeBigFileName)->second; if (rbnp.Files.size() == 0) { return false; } vector<BNPFile>::iterator itNBPFile; BNPFile temp_bnp_file; temp_bnp_file.Name = (char*)zeFileName.c_str(); itNBPFile = lower_bound(rbnp.Files.begin(), rbnp.Files.end(), temp_bnp_file, CBNPFileComp()); if (itNBPFile != rbnp.Files.end()) { if (strcmp(itNBPFile->Name, zeFileName.c_str()) != 0) { return false; } } else { return false; } BNPFile &rbnpfile = *itNBPFile; // set ptr on found bnp/bnpFile zeBnp= &rbnp; zeBnpFile= &rbnpfile; return true; } // *************************************************************************** FILE* CBigFile::getFile (const std::string &sFileName, uint32 &rFileSize, uint32 &rBigFileOffset, bool &rCacheFileOnOpen, bool &rAlwaysOpened) { BNP *bnp= NULL; BNPFile *bnpFile= NULL; if(!getFileInternal(sFileName, bnp, bnpFile)) { nlwarning ("BF: Couldn't load '%s'", sFileName.c_str()); return NULL; } nlassert(bnp && bnpFile); // Get a ThreadSafe handle on the file CHandleFile &handle= _ThreadFileArray.get(bnp->ThreadFileId); /* If not opened, open it now. There is 2 reason for it to be not opened: rbnp.AlwaysOpened==false, or it is a new thread which use it for the first time. */ if(handle.File== NULL) { handle.File = fopen (bnp->BigFileName.c_str(), "rb"); if (handle.File == NULL) return NULL; } rCacheFileOnOpen = bnp->CacheFileOnOpen; rAlwaysOpened = bnp->AlwaysOpened; rBigFileOffset = bnpFile->Pos; rFileSize = bnpFile->Size; return handle.File; } // *************************************************************************** bool CBigFile::getFileInfo (const std::string &sFileName, uint32 &rFileSize, uint32 &rBigFileOffset) { BNP *bnp= NULL; BNPFile *bnpFile= NULL; if(!getFileInternal(sFileName, bnp, bnpFile)) { nlwarning ("BF: Couldn't find '%s' for info", sFileName.c_str()); return false; } nlassert(bnp && bnpFile); // get infos rBigFileOffset = bnpFile->Pos; rFileSize = bnpFile->Size; return true; } // *************************************************************************** char *CBigFile::getFileNamePtr(const std::string &sFileName, const std::string &sBigFileName) { string bigfilenamealone = CFile::getFilename (sBigFileName); if (_BNPs.find(bigfilenamealone) != _BNPs.end()) { BNP &rbnp = _BNPs.find (bigfilenamealone)->second; vector<BNPFile>::iterator itNBPFile; if (rbnp.Files.size() == 0) return NULL; string lwrFileName = toLower(sFileName); BNPFile temp_bnp_file; temp_bnp_file.Name = (char*)lwrFileName.c_str(); itNBPFile = lower_bound(rbnp.Files.begin(), rbnp.Files.end(), temp_bnp_file, CBNPFileComp()); if (itNBPFile != rbnp.Files.end()) { if (strcmp(itNBPFile->Name, lwrFileName.c_str()) == 0) { return itNBPFile->Name; } } } return NULL; } // *************************************************************************** void CBigFile::getBigFilePaths(std::vector<std::string> &bigFilePaths) { bigFilePaths.clear(); for(std::map<std::string, BNP>::iterator it = _BNPs.begin(); it != _BNPs.end(); ++it) { bigFilePaths.push_back(it->second.BigFileName); } } } // namespace NLMISC