khanat-opennel-code/code/ryzom/common/src/game_share/bnp_patch.cpp
Guillaume Dupuy 6f1197d633 Allow target to work with sheetName and a quiet option
HG : Enter commit message.  Lines beginning with 'HG:' are removed.
2016-11-28 00:54:33 +01:00

1162 lines
31 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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/>.
//-----------------------------------------------------------------------------
// includes
//-----------------------------------------------------------------------------
#include "stdpch.h"
#include "nel/misc/path.h"
#include "nel/misc/sha1.h"
#include "bnp_patch.h"
#define PERSISTENT_TOKEN_FAMILY RyzomTokenFamily
//-----------------------------------------------------------------------------
// Handy utility routines
//-----------------------------------------------------------------------------
void normaliseBnpFileName(std::string& fileName)
{
BOMB_IF(fileName.empty(),"Can't normalise an empty bnp file name",return);
if (NLMISC::CFile::getExtension(fileName).empty() && fileName[fileName.size()-1]!='.')
fileName+=".bnp";
}
void applyDate (const std::string &sFilename, uint32 nDate)
{
// change the file time
if(nDate != 0)
{
// _utimbuf utb;
// utb.actime = utb.modtime = nDate;
NLMISC::CFile::setRWAccess(sFilename);
NLMISC::CFile::setFileModificationDate(sFilename, nDate);
// _utime (sFilename.c_str (), &utb);
}
}
//-----------------------------------------------------------------------------
// class CBNPFileVersion
//-----------------------------------------------------------------------------
CBNPFileVersion::CBNPFileVersion()
{
_FileTime= 0;
_FileSize= 0;
_7ZFileSize=0;
_PatchSize= 0;
_VersionNumber= std::numeric_limits<uint32>::max();
}
// setup record contents from a file name and version number
// returns false if the file didn't exist
bool CBNPFileVersion::setup(const std::string &fileName, uint32 versionNumber)
{
// make sure the file exists...
BOMB_IF(!NLMISC::CFile::fileExists(fileName),("File not found: "+fileName).c_str(),return false);
// generate a hash key for the file and store it in a vector of uint32
CHashKey hashKey= getSHA1(fileName);
nlassert(hashKey.HashKeyString.size()==20);
_HashKey.clear();
for (uint32 i=0;i<5;++i)
_HashKey.push_back(*(uint32*)&hashKey.HashKeyString[4*i]);
// get the other file properties
_FileTime= NLMISC::CFile::getFileModificationDate(fileName);
_FileSize= NLMISC::CFile::getFileSize(fileName);
// setup the version number
_VersionNumber= versionNumber;
return true;
}
void CBNPFileVersion::setVersionNumber(uint32 nVersionNumber)
{
_VersionNumber = nVersionNumber;
}
void CBNPFileVersion::set7ZipFileSize(uint32 n7ZFileSize)
{
_7ZFileSize = n7ZFileSize;
}
void CBNPFileVersion::setPatchSize(uint32 nPatchSize)
{
_PatchSize = nPatchSize;
}
void CBNPFileVersion::setTimeStamp(uint32 nTimeStamp)
{
_FileTime = nTimeStamp;
}
// accessors
uint32 CBNPFileVersion::getVersionNumber() const
{
return _VersionNumber;
}
uint32 CBNPFileVersion::getTimeStamp() const
{
return _FileTime;
}
uint32 CBNPFileVersion::getFileSize() const
{
return _FileSize;
}
uint32 CBNPFileVersion::get7ZFileSize() const
{
return _7ZFileSize;
}
uint32 CBNPFileVersion::getPatchSize() const
{
return _PatchSize;
}
CHashKey CBNPFileVersion::getHashKey() const
{
nlassert(_HashKey.size()==5);
CHashKey hashKey;
for (uint32 i=0;i<5;++i)
{
*(uint32*)&hashKey.HashKeyString[4*i]=_HashKey[i];
}
return hashKey;
}
// == operator
bool CBNPFileVersion::operator==(const CBNPFileVersion& other) const
{
// make sure the file sizes match
if (_FileSize!=other._FileSize)
return false;
// make sure the hash keys match
if (_HashKey!=other._HashKey)
return false;
// we don't compare version numbers or file dates as they're not interesting
return true;
}
// != operator
bool CBNPFileVersion::operator!=(const CBNPFileVersion& other) const
{
return !operator==(other);
}
//-----------------------------------------------------------------------------
// Persistent data for CBNPFileVersion
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CBNPFileVersion
#define PERSISTENT_DATA \
PROP(uint32,_VersionNumber) \
PROP(uint32,_FileSize) \
PROP(uint32,_7ZFileSize) \
PROP(uint32,_FileTime) \
PROP(uint32,_PatchSize) \
PROP_VECT(uint32,_HashKey)
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//-----------------------------------------------------------------------------
// class CBNPFile
//-----------------------------------------------------------------------------
CBNPFile::CBNPFile()
{
_IsIncremental= true;
}
bool CBNPFile::addVersion(const std::string& bnpDirectory, const std::string& /* refDirectory */, IVersionNumberGenerator& version)
{
nlinfo("Checking need to add new version to file: %s",_FileName.c_str());
// perform a quick check to see if the time stamp and file size of the new BNP file match the last version in the index
std::string fullFileName= bnpDirectory+_FileName;
if (!NLMISC::CFile::fileExists(fullFileName))
return false;
if (!_Versions.empty())
{
if ((NLMISC::CFile::getFileSize(fullFileName)==(uint32)_Versions.back().getFileSize())
&& (NLMISC::CFile::getFileModificationDate(fullFileName)==(uint32)_Versions.back().getTimeStamp()))
return true;
NLMISC::InfoLog->displayNL("File: %s\n size(%d != %d) || time(%d != %d)",
fullFileName.c_str(),
NLMISC::CFile::getFileSize(fullFileName),
(uint32)_Versions.back().getFileSize(),
NLMISC::CFile::getFileModificationDate(fullFileName),
(uint32)_Versions.back().getTimeStamp()
);
}
// create a new record for the BNP file that we have on the disk at the moment
// if no file was found then give up (return)
CBNPFileVersion fileVersion;
bool result= fileVersion.setup(fullFileName,~0u);
if (result==false)
return false;
// compare the fileVersion record to the last record in the history.
// If they don't match then append it
if (_Versions.empty() || _Versions.back()!=fileVersion)
{
// if we haven't yet generated the version number for this version then go for it now
version.grabVersionNumber();
fileVersion.setVersionNumber(version.getPackageVersionNumber());
// make sure that our version numbers are ever increasing... it would be fatal to have an out-of-order version
if (!_Versions.empty())
nlassert(_Versions.back().getVersionNumber()<version.getPackageVersionNumber());
// the file's current checksum doesn't match the previous checksum so add the new version
nlinfo("- Adding version %05u to file: %s",version.getPackageVersionNumber(),_FileName.c_str());
_Versions.push_back(fileVersion);
// copy the file to create a new reference file...
// NLMISC::CSString refFileName= NLMISC::CSString(refDirectory+_FileName).replace(".",NLMISC::toString("_%05u.",version.getPackageVersionNumber()).c_str());
// NLMISC::CFile::copyFile(refFileName, fullFileName);
}
else
{
// the file's size & current checksum match the previous version so just fix the file's timestamp
nlinfo("Files contents matches previous version but time stamp is different: %s",fullFileName.c_str());
applyDate(fullFileName,_Versions.back().getTimeStamp());
}
// if we're flagged as non-incremental then we don't need a version history
if (!_IsIncremental && _Versions.size()>1)
{
_Versions[0]= _Versions.back();
_Versions.resize(1);
}
return true;
}
uint32 CBNPFile::getLatestVersionNumber(uint32 max) const
{
if (_Versions.empty())
return 0;
uint32 i=0;
for (i=(uint32)_Versions.size();i--;)
if (_Versions[i].getVersionNumber()<=max)
return _Versions[i].getVersionNumber();
nlinfo("File %s didn't exist before version %d",_FileName.c_str(),max);
return 0;
}
uint32 CBNPFile::versionCount() const
{
return (uint32)_Versions.size();
}
const CBNPFileVersion& CBNPFile::getVersion(uint32 idx) const
{
nlassert(idx<versionCount());
return _Versions[idx];
}
CBNPFileVersion& CBNPFile::getVersion(uint32 idx)
{
nlassert(idx<versionCount());
return _Versions[idx];
}
void CBNPFile::setFileName(const std::string& fileName)
{
_FileName= fileName;
}
const std::string& CBNPFile::getFileName() const
{
return _FileName;
}
void CBNPFile::setIncremental(bool value)
{
_IsIncremental=value;
// if we're flagged as non-incremental then we don't need a version history
if (!_IsIncremental && _Versions.size()>1)
{
_Versions[0]= _Versions.back();
_Versions.resize(1);
}
}
bool CBNPFile::isIncremental()
{
return _IsIncremental;
}
//-----------------------------------------------------------------------------
// Persistent data for CBNPFile
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CBNPFile
#define PERSISTENT_DATA\
PROP(std::string,_FileName)\
STRUCT_VECT(_Versions)
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//-----------------------------------------------------------------------------
// class CBNPFileSet
//-----------------------------------------------------------------------------
void CBNPFileSet::removeFile(const std::string &filename)
{
for( uint k = 0; k < _Files.size(); ++k)
{
if (_Files[k].getFileName() == filename)
{
_Files.erase(_Files.begin() + k);
}
}
}
// add a version to the file
// returns highest version number in files after operation
uint32 CBNPFileSet::addVersion(const std::string& bnpDirectory, const std::string& refDirectory, IVersionNumberGenerator& version)
{
nlinfo("Updating package index...");
uint32 result=0;
// add versions to different files
for (uint32 i=(uint32)_Files.size();i--;)
if (_Files[i].addVersion(bnpDirectory,refDirectory,version)!=false)
result= std::max(result,_Files[i].getLatestVersionNumber());
return result;
}
// look through the referenced files for the highest version number
uint32 CBNPFileSet::getVersionNumber() const
{
uint32 result=0;
for (uint32 i=(uint32)_Files.size();i--;)
result= std::max(result,_Files[i].getLatestVersionNumber());
return result;
}
void CBNPFileSet::clear()
{
_Files.clear();
}
uint32 CBNPFileSet::fileCount() const
{
return (uint32)_Files.size();
}
const CBNPFile& CBNPFileSet::getFile(uint32 idx) const
{
return const_cast<CBNPFileSet*>(this)->getFile(idx);
}
const CBNPFile* CBNPFileSet::getFileByName(const std::string& fileName) const
{
return const_cast<CBNPFileSet*>(this)->getFileByName(fileName);
}
CBNPFile& CBNPFileSet::getFile(uint32 idx)
{
nlassert(idx<fileCount());
return _Files[idx];
}
CBNPFile* CBNPFileSet::getFileByName(const std::string& fileName)
{
// look for the file by name
for (uint32 i=0;i<fileCount();++i)
if (getFile(i).getFileName()==fileName)
return &getFile(i);
// file not found so return NULL
return NULL;
}
void CBNPFileSet::addFile(const std::string& fileName,bool isIncremental)
{
// see if the file already exists in the files container
if (getFileByName(fileName)!=NULL)
{
if (!isIncremental)
getFileByName(fileName)->setIncremental(false);
return;
}
// file is new so need to add it
std::string s= fileName;
normaliseBnpFileName(s);
nlinfo("- adding file: %s",s.c_str());
_Files.resize(_Files.size()+1);
_Files.back().setFileName(s);
_Files.back().setIncremental(isIncremental);
}
//-----------------------------------------------------------------------------
// Persistent data for CBNPFileSet
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CBNPFileSet
#define PERSISTENT_DATA\
STRUCT_VECT(_Files)
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//-----------------------------------------------------------------------------
// class CBNPCategory
//-----------------------------------------------------------------------------
CBNPCategory::CBNPCategory()
{
_IsOptional=true;
_IsIncremental=true;
_Hidden=false;
}
bool CBNPCategory::hasFile(const std::string &fileName) const
{
return std::find(_Files.begin(), _Files.end(), fileName) != _Files.end();
}
const std::string& CBNPCategory::getName() const
{
return _Name;
}
void CBNPCategory::setName(const std::string& name)
{
_Name=name;
}
void CBNPCategory::setOptional(bool value)
{
_IsOptional= value;
}
bool CBNPCategory::isOptional() const
{
return _IsOptional;
}
void CBNPCategory::setUnpackTo(const std::string &n)
{
_UnpackTo = n;
}
const std::string &CBNPCategory::getUnpackTo() const
{
return _UnpackTo;
}
void CBNPCategory::setIncremental(bool value)
{
_IsIncremental= value;
}
bool CBNPCategory::isIncremental() const
{
return _IsIncremental;
}
void CBNPCategory::setCatRequired(const std::string &cat)
{
_CatRequired = cat;
}
const std::string &CBNPCategory::getCatRequired() const
{
return _CatRequired;
}
void CBNPCategory::setHidden(bool value)
{
_Hidden = value;
}
bool CBNPCategory::isHidden() const
{
return _Hidden;
}
uint32 CBNPCategory::fileCount() const
{
return (uint32)_Files.size();
}
const std::string& CBNPCategory::getFile(uint32 idx) const
{
nlassert(idx<fileCount());
return _Files[idx];
}
void CBNPCategory::addFile(const std::string& fileName)
{
// make sure file doesn't already exist
for (uint32 i=0;i<_Files.size();++i)
if (_Files[i]==fileName)
return;
// add the new file
_Files.push_back(fileName);
}
//-----------------------------------------------------------------------------
// Persistent data for CBNPCategory
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CBNPCategory
#define PERSISTENT_DATA\
PROP(std::string, _Name)\
LPROP(bool, _IsOptional, if(!_IsOptional))\
LPROP(std::string, _UnpackTo, if(!_UnpackTo.empty()))\
LPROP(bool, _IsIncremental, if(!_IsIncremental))\
LPROP(std::string, _CatRequired, if(!_CatRequired.empty()))\
LPROP(bool, _Hidden, if(_Hidden))\
PROP_VECT(std::string, _Files)\
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//-----------------------------------------------------------------------------
// class CBNPCategorySet
//-----------------------------------------------------------------------------
void CBNPCategorySet::clear()
{
_Category.clear();
}
const CBNPCategory* CBNPCategorySet::getCategoryFromFile(const std::string &fileName) const
{
for(std::vector<CBNPCategory>::const_iterator it = _Category.begin(); it != _Category.end(); ++it)
{
if (it->hasFile(fileName))
{
return &(*it);
}
}
return NULL;
}
void CBNPCategorySet::deleteCategory(uint32 index)
{
nlassert(index < _Category.size());
_Category.erase(_Category.begin() + index);
}
uint32 CBNPCategorySet::fileCount() const
{
uint32 result=0;
for (uint32 i=0;i<_Category.size();++i)
result+=_Category[i].fileCount();
return result;
}
const std::string& CBNPCategorySet::getFile(uint32 idx) const
{
uint32 i=0;
for (;;++i)
{
nlassert(i<_Category.size());
if (_Category[i].fileCount()>idx)
break;
idx-=_Category[i].fileCount();
}
return _Category[i].getFile(idx);
}
uint32 CBNPCategorySet::categoryCount() const
{
return (uint32)_Category.size();
}
CBNPCategory& CBNPCategorySet::getCategory(uint32 idx)
{
nlassert(idx<categoryCount());
return _Category[idx];
}
const CBNPCategory& CBNPCategorySet::getCategory(uint32 idx) const
{
return const_cast<CBNPCategorySet*>(this)->getCategory(idx);
}
const CBNPCategory* CBNPCategorySet::getCategory(const std::string& categoryName) const
{
// look for a category with matching name
for (uint32 i=0;i<categoryCount();++i)
if (getCategory(i).getName()==categoryName)
return &(getCategory(i));
return NULL;
}
// check whether a named category exists and add a new one if need be
CBNPCategory* CBNPCategorySet::getCategory(const std::string& categoryName, bool addIfNotExist)
{
// look for a category with matching name
for (uint32 i=0;i<categoryCount();++i)
if (getCategory(i).getName()==categoryName)
return &(getCategory(i));
// the category wasn't found so return NULL if need be
if (!addIfNotExist)
return NULL;
// create a new category if need be
_Category.resize(_Category.size()+1);
_Category.back().setName(categoryName);
nlinfo("- New category created: %s",categoryName.c_str());
return &_Category.back();
}
void CBNPCategorySet::addFile(const std::string& categoryName,const std::string& fileName)
{
// make sure the category exists
CBNPCategory* theCategory= getCategory(categoryName,true);
// look to see if the file already exists in the category
for (uint32 i=0;i<theCategory->fileCount();++i)
if (theCategory->getFile(i)==fileName)
return;
// the file doesn't already exist so add it
theCategory->addFile(fileName);
nlinfo("- File added to category %s::%s",categoryName.c_str(),fileName.c_str());
}
bool CBNPCategorySet::isFileIncremental(const std::string& fileName) const
{
// for each category
for (uint32 i=0;i<categoryCount();++i)
{
const CBNPCategory& theCategory= getCategory(i);
// if the category is incremental then skip it
if (theCategory.isIncremental())
continue;
// if the file exists in this category then return 'false' meaning non-incremental
for (uint32 i=0;i<theCategory.fileCount();++i)
if (theCategory.getFile(i)==fileName)
return false;
}
// the file wasn't found in a non-incremental category so it must be incremental
return true;
}
//-----------------------------------------------------------------------------
// Persistent data for CBNPCategorySet
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CBNPCategorySet
#define PERSISTENT_DATA\
STRUCT_VECT(_Category)
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//-----------------------------------------------------------------------------
// class CProductDescriptionForClient
//-----------------------------------------------------------------------------
void CProductDescriptionForClient::clear()
{
_Categories.clear();
_Files.clear();
}
void CProductDescriptionForClient::setCategories(CPersistentDataRecord &pdr)
{
_Categories.clear();
_Categories.apply(pdr);
}
void CProductDescriptionForClient::setFiles(CPersistentDataRecord &pdr)
{
_Files.clear();
_Files.apply(pdr);
}
void CProductDescriptionForClient::getCategories(CPersistentDataRecord &pdr)
{
pdr.clear();
_Categories.store(pdr);
}
void CProductDescriptionForClient::getFiles(CPersistentDataRecord &pdr)
{
pdr.clear();
_Files.store(pdr);
}
bool CProductDescriptionForClient::load(const std::string& filePath)
{
// read new contents from input file
if (!NLMISC::CFile::fileExists(filePath))
return false;
clear();
static CPersistentDataRecord pdr;
pdr.clear();
pdr.readFromBinFile(filePath);
apply(pdr);
return true;
}
void CProductDescriptionForClient::serial(NLMISC::IStream &f)
{
static CPersistentDataRecord pdr("RyzomTokenFamily");
pdr.clear();
if (f.isReading())
{
bool ok = pdr.fromBuffer(f);
if (ok)
{
apply(pdr);
}
else
{
throw NLMISC::Exception("Can't retrieve file desc");
}
}
else
{
store(pdr);
pdr.toStream(f);
}
}
//-----------------------------------------------------------------------------
// Persistent data for CProductDescriptionForClient
//-----------------------------------------------------------------------------
#define PERSISTENT_CLASS CProductDescriptionForClient
#define PERSISTENT_DATA\
STRUCT(_Files)\
STRUCT(_Categories)
//# pragma message( PERSISTENT_GENERATION_MESSAGE )
#include "persistent_data_template.h"
#undef PERSISTENT_CLASS
#undef PERSISTENT_DATA
//================================================================================================
//================================================================================================
//================================================================================================
//================================================================================================
//#if 0
//
////-----------------------------------------------------------------------------
//// class CBNPPatchDescription
////-----------------------------------------------------------------------------
//// a little object used to describe patches in CBNPUnpatcher class
//
//std::string CBNPPatchDescription::getPatchFileName() const
//{
// return NLMISC::CFile::getFilenameWithoutExtension(getTargetFileName())+NLMISC::toString("_%05d.patch",getVersion());
//}
//
//
////-----------------------------------------------------------------------------
//// class CBNPUnpatcher
////-----------------------------------------------------------------------------
//
////-----------------------------------------------------------------------------
//// initialisation
//
//CBNPUnpatcher::CBNPUnpatcher(const std::string& productName,uint32 version,const std::string& appRootDirectory,const std::string& patchDirectory)
//{
// _AppRootDirectory= NLMISC::CPath::standardizePath(appRootDirectory,true);
// _PatchDirectory= NLMISC::CPath::standardizePath(patchDirectory,true);
// _ProductName= productName;
// _Version= version;
//}
//
//bool CBNPUnpatcher::isIndexUpToDate()
//{
// return (_Files.getVersionNumber()==_Version);
//}
//
//CBNPPatchDescription CBNPUnpatcher::getIndexFileDownloadDescription()
//{
// return CBNPPatchDescription(productName+"_patch_index",_Version,0,!isIndexUpToDate())
//}
//
//
////-----------------------------------------------------------------------------
//// accessors for directories
//
//std::string getRootDirectory() const
//{
// return _AppRootDirectory;
//}
//
//std::string getDataDirectory() const
//{
// return _AppRootDirectory+"data/";
//}
//
//std::string getPatchDirectory() const
//{
// return _PatchDirectory;
//}
//
//
////-----------------------------------------------------------------------------
//// accessors for retrieving info on required patches
//
//bool CBNPUnpatcher::isUpToDate()
//{
// return (isIndexUpToDate() && !isPatchMandatory() && !isPatchOptional())
//}
//
//bool CBNPUnpatcher::_isPatch(bool isBySelectionFlag,bool isOptionalFlag=false)
//{
// nlassert(isIndexUpToDate()==true);
//
// // scan all categories for a mandatoy category with non-up-to-date files
// for (uint32 i=0;i<_Categories.categoryCount();++i)
// {
// CBNPCategory* theCategory= _Categories.getCategory(i);
//
// // decide whether or not to skip this category
// if (isBySelectionFlag==true)
// {
// if (theCategory->isOptional() &&
// _SelectedCategories.find(theCategory->getName())==_SelectedCategories.end()))
// continue;
// }
// else
// {
// if (theCategory->isOptional()!=isOptionalFlag)
// continue;
// }
//
// // if one of the files is not up to date then we return 'true' as we need to patch
// for (uint32 j=0;j<theCategory->fileCount();++j)
// if (!isFileUpToDate(theCategory->getFile(j)))
// return true;
// }
//
// return false;
//}
//
//bool CBNPUnpatcher::isPatchMandatory()
//{
// return _isPatch(false,false);
//}
//
//bool CBNPUnpatcher::isPatchOptional()
//{
// return _isPatch(false,true);
//}
//
//bool CBNPUnpatcher::isPatchRequired()
//{
// return _isPatch(true);
//}
//
//// return true if it's necessary to download patches for the selected options
//bool CBNPUnpatcher::isDownloadRequired()
//{
// if (!isPatchRequired())
// return false;
//
// std::vector<CBNPPatchDescription> hold;
// getDownloadPatches(hold);
// return !hold.empty();
//}
//
//// return true if it's necessary to download the next patch that needs to be applied (in order)
//bool CBNPUnpatcher::isNextPatchDownloadRequired()
//{
// if (!isPatchRequired())
// return false;
//
// return getNextPatch().getRequiresDownload();
//}
//
//
////-----------------------------------------------------------------------------
//// crunching routines
//
//// scan the directories for files - identifies the set of required patches
//// and also the set of these patches that is missing from the patch directory
//void CBNPUnpatcher::scanForFiles()
//{
// std::vector<std::string> patchFiles;
// std::vector<std::string> patchFiles;
// std::vector<std::string> dataFiles;
//
// // get the list of files in the patch directory
// NLMISC::CPath::getPathContent(getPatchDirectory(),false,false,true,patchFiles);
//
// // get the list of files in the data directory
// NLMISC::CPath::getPathContent(getDataDirectory(),false,false,true,dataFiles);
//
// for (uint32 i=0;i<_Categories.fileCount();++i)
// {
// _Categories.getFile()
// result.push_back(_Categories.getCategory(i).getName());
// }
// xxx
//}
//
//// apply the mandatory and selected optional patches
//// nlerror if isDownloadRequired() is not false
//void CBNPUnpatcher::applyPatches()
//{
// nlassert(isIndexUpToDate()==true);
// nlassert(!isDownloadRequired());
// xxx
//}
//
//// apply the next patch (in order)
//// nlerror if isNextPatchDownloadRequired() is not false
//void CBNPUnpatcher::applyNextPatch()
//{
// // note that if the index isn't up to date then it is classed as the next patch
// nlassert(!isNextPatchDownloadRequired());
// xxx
//}
//
//
////-----------------------------------------------------------------------------
//// managing the set of selected optional patch categories
//
//// get the names of all optional categories
//void CBNPUnpatcher::getAllOptionalCategories(std::vector<std::string>& result)
//{
// nlassert(isIndexUpToDate()==true);
//
// result.clear();
// for (uint32 i=0;i<_Categories.categoryCount();++i)
// {
// result.push_back(_Categories.getCategory(i).getName());
// }
//}
//
//// get the names of the optional categories that require patching
//void CBNPUnpatcher::getPatchableOptionalCategories(std::vector<std::string>& result)
//{
// nlassert(isIndexUpToDate()==true);
//
// result.clear();
// for (uint32 i=0;i<_Categories.categoryCount();++i)
// {
// CBNPCategory* theCategory= _Categories.getCategory(i);
// uint32 j;
//
// // if one of the files is not up to date then we return 'true' as we need to patch
// for (j=0;j<theCategory->fileCount();++j)
// if (!isFileUpToDate(theCategory->getFile(j)))
// break;
//
// // if we broke out before the end of the for loop then we need to add this category
// if (j<theCategory->fileCount())
// result.push_back(_Categories.getCategory(i).getName());
// }
//}
//
//// select or unselect an optional package
//void CBNPUnpatcher::setOptionalCategorySelectFlag(const std::string& categoryName, bool value)
//{
// nlassert(isIndexUpToDate()==true);
// _SelectedCategories.insert(categoryName);
//}
//
//// select or unselect all optional packages
//void CBNPUnpatcher::setAllOptionalCategorySelectFlags(bool value)
//{
// nlassert(isIndexUpToDate()==true);
//
// _SelectedCategories.clear();
// for (uint32 i=0;i<_Categories.categoryCount();++i)
// {
// _SelectedCategories.insert(_Categories.getCategory(i).getName());
// }
//}
//
//
////-----------------------------------------------------------------------------
//// getting lists of applicable patches
//
//// get the ordered list of mandatory + optional patches that need to be applied to update selected packages
//void CBNPUnpatcher::getSelectedPatches(std::vector<CBNPPatchDescription>& result)
//{
// nlassert(isIndexUpToDate()==true);
//
// std::vector<CBNPPatchDescription> mandatoryPatches;
// getMandatoryPatches(mandatoryPatches);
//
// std::vector<CBNPPatchDescription> optionalPatches;
// getSelectedOptionalPatches(optionalPatches);
//
// result= mandatoryPatches+ optionalPatches;
//}
//
//// get the ordered list of optional patches that need to be applied to update selected packages
//void CBNPUnpatcher::getSelectedOptionalPatches(std::vector<CBNPPatchDescription>& result)
//{
// nlassert(isIndexUpToDate()==true);
// xxx
//}
//
//// get the ordered list of patches that need to be applied for a minimum update
//void CBNPUnpatcher::getMandatoryPatches(std::vector<CBNPPatchDescription>& result)
//{
// nlassert(isIndexUpToDate()==true);
// xxx
//}
//
//// get an ordered list of the patches that need to be applied for a full update
//void CBNPUnpatcher::getAllPatches(std::vector<CBNPPatchDescription>& result)
//{
// // store the selected category set in temporary variable
// std::set<std::string> selectedCategories= _SelectedCategories;
//
// // select all of the categories and delegate to getSelectedPatches()
// setAllOptionalCategorySelectFlags();
// getSelectedPatches(result);
//
// // restore the _SelectedCategories set from temp variable
// _SelectedCategories= selectedCategories;
//}
//
//// get the name of the next patch that needs to be applied (for progress display)
//const std::string& CBNPUnpatcher::getNextPatchName()
//{
// CBNPPatchDescription patch= getNextPatch();
// return patch.getTargetFileName()+NLMISC::toString(":%d",patch.getVersion());
//}
//
//// get the patch description for the next patch to apply
//CBNPPatchDescription CBNPUnpatcher::getNextPatch()
//{
// // make sure that index is up to date and patching is required
// nlassert(isIndexUpToDate());
// nlassert(isPatchRequired());
//
// // treat the case of !uptodate() here and get the index file as the next patch
// if (!isIndexUpToDate())
// {
// return getIndexFileDownloadDescription();
// }
//
// for (uint32 i=0;i<_Categories.categoryCount();++i)
// {
// CBNPCategory* theCategory= _Categories.getCategory(i);
// uint32 j;
//
// // if one of the files is not up to date then we return 'true' as we need to patch
// for (j=0;j<theCategory->fileCount();++j)
// if (!isFileUpToDate(theCategory->getFile(j)))
// break;
//
// // if we broke out before the end of the for loop then we need to add this category
// if (j<theCategory->fileCount())
// result.push_back(_Categories.getCategory(i).getName());
// }
//
// xxx
//}
//
//// get the list of patches that need to be downloaded
//void CBNPUnpatcher::getSelectedDownloadPatches(std::vector<CBNPPatchDescription>& result)
//{
// nlassert(isIndexUpToDate()==true);
// xxx
//}
//
//// get the list of patches that need to be downloaded
//void CBNPUnpatcher::getAllDownloadPatches(std::vector<CBNPPatchDescription>& result)
//{
// // store the selected category set in temporary variable
// std::set<std::string> selectedCategories= _SelectedCategories;
//
// // select all of the categories and delegate to getSelectedDownloadPatches()
// setAllOptionalCategorySelectFlags();
// getSelectedDownloadPatches(result);
//
// // restore the _SelectedCategories set from temp variable
// _SelectedCategories= selectedCategories;
//}
//
//
////-----------------------------------------------------------------------------
//
//
//#endif