Mercurial > fife-parpg
diff engine/core/vfs/dat/dat2.cpp @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children | 90005975cdbb |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/core/vfs/dat/dat2.cpp Sun Jun 29 18:44:17 2008 +0000 @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright (C) 2005-2008 by the FIFE team * + * http://www.fifengine.de * + * This file is part of FIFE. * + * * + * FIFE is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +// Standard C++ library includes + +// 3rd party library includes +#include <boost/bind.hpp> + +// FIFE includes +// These includes are split up in two parts, separated by one empty line +// First block: files included from the FIFE root src directory +// Second block: files included from the same folder +#include "vfs/raw/rawdata.h" +#include "util/base/exception.h" +#include "util/log/logger.h" + +#include "dat2.h" + +namespace FIFE { + static Logger _log(LM_FO_LOADERS); + + DAT2::DAT2(VFS* vfs, const std::string& file) + : VFSSource(vfs), m_datpath(file), m_data(vfs->open(file)), m_filelist() { + + FL_LOG(_log, LMsg("MFFalloutDAT2") + << "loading: " << file + << " filesize: " << m_data->getDataLength()); + + m_data->setIndex(m_data->getDataLength() - 8); + uint32_t fileListLength = m_data->read32Little(); + uint32_t archiveSize = m_data->read32Little(); + + FL_LOG(_log, LMsg("MFFalloutDAT2") + << "FileListLength: " << fileListLength + << " ArchiveSize: " << archiveSize); + + if (archiveSize != m_data->getDataLength()) + throw InvalidFormat("size mismatch"); + + m_data->setIndex( archiveSize - fileListLength - 8); + m_filecount = m_data->read32Little(); + m_currentIndex = m_data->getCurrentIndex(); + + FL_LOG(_log, LMsg("MFFalloutDAT2 FileCount: ") << m_filecount); + + // Do not read the complete file list at startup. + // Instead read a chunk each frame. + m_timer.setInterval(0); + m_timer.setCallback( boost::bind( &DAT2::readFileEntry, this) ); + m_timer.start(); + } + + void DAT2::readFileEntry() const { + assert( m_filecount != 0); + + // Load more items per call, + // otherwise it takes _ages_ until everything is in. + unsigned int load_per_cycle = 50; + if( load_per_cycle > m_filecount ) + load_per_cycle = m_filecount; + m_filecount -= load_per_cycle; + + // Save the old index in an exception save way. + IndexSaver isaver(m_data.get()); + + // Move index to file list and read the entries. + m_data->setIndex(m_currentIndex); + RawDataDAT2::s_info info; + while( load_per_cycle-- ) { + uint32_t namelen = m_data->read32Little(); + info.name = fixPath(m_data->readString(namelen)); + + info.type = m_data->read8(); + info.unpackedLength = m_data->read32Little(); + info.packedLength = m_data->read32Little(); + info.offset = m_data->read32Little(); + + m_filelist.insert(std::make_pair(info.name, info)); + } + m_currentIndex = m_data->getCurrentIndex(); + + // Finally log on completion and stop the timer. + if( m_filecount == 0 ) { + FL_LOG(_log, LMsg("MFFalloutDAT2, All file entries in '") << m_datpath << "' loaded."); + m_timer.stop(); + } + } + + RawData* DAT2::open(const std::string& file) const { + const RawDataDAT2::s_info& info = getInfo(file); + return new RawData(new RawDataDAT2(getVFS(), m_datpath, info)); + } + + bool DAT2::fileExists(const std::string& name) const { + return findFileEntry(name) != m_filelist.end(); + } + + const RawDataDAT2::s_info& DAT2::getInfo(const std::string& name) const { + type_filelist::const_iterator i = findFileEntry(name); + if (i == m_filelist.end()) { + throw NotFound(name); + } + return i->second; + } + + DAT2::type_filelist::const_iterator DAT2::findFileEntry(const std::string& path) const { + + // Either the normalization is bogus, or we have to do + // it here, too. Otherwise we can't load the files returned + // by listFiles. + + std::string name = path; + + // Normalize the path + if (name.find("./") == 0) { + name.erase(0, 2); + } + + type_filelist::const_iterator i = m_filelist.find(name); + + // We might have another chance to find the file + // if the number of file entries not zero. + if ( m_filecount && i == m_filelist.end()) { + FL_LOG(_log, LMsg("MFFalloutDAT2") + << "Missing '" << name + << "' in partially(" << m_filecount <<") loaded "<< m_datpath); + while( m_filecount && i == m_filelist.end()) { + readFileEntry(); + i = m_filelist.find(name); + } + } + return i; + } + + + std::set<std::string> DAT2::listFiles(const std::string& pathstr) const { + return list(pathstr, false); + } + + std::set<std::string> DAT2::listDirectories(const std::string& pathstr) const { + return list(pathstr, true); + } + + std::set<std::string> DAT2::list(const std::string& pathstr, bool dirs) const { + std::set<std::string> list; + std::string path = pathstr; + + // Force loading the complete file entries + // This is a costly operation... right after startup. + // Later this should do nothing. + while( m_filecount ) { + readFileEntry(); + } + + // Normalize the path + if (path.find("./") == 0) { + path.erase(0, 2); + } + + int lastIndex = path.size() - 1; + if ((lastIndex != -1) && path[lastIndex] != '/') { + path += '/'; + } + + type_filelist::const_iterator end = m_filelist.end(); + for (type_filelist::const_iterator i = m_filelist.begin(); i != end; ++i) { + const std::string& file = i->first; + if (file.find(path) == 0) { + std::string cleanedfile = file.substr(path.size(), file.size()); // strip the pathstr + bool isdir = cleanedfile.find('/') != std::string::npos; // if we still have a / it's a subdir + + if (isdir) { + cleanedfile = cleanedfile.substr(0, cleanedfile.find('/')); + if (cleanedfile.find('/') != cleanedfile.rfind('/')) { + // check if this is a direct subdir + continue; + } + } + + if (isdir == dirs) { + list.insert(cleanedfile); + } + } + } + + return list; + } +} // FIFE