dat2.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 
00024 // 3rd party library includes
00025 #include <boost/bind.hpp>
00026 
00027 // FIFE includes
00028 // These includes are split up in two parts, separated by one empty line
00029 // First block: files included from the FIFE root src directory
00030 // Second block: files included from the same folder
00031 #include "vfs/raw/rawdata.h"
00032 #include "util/base/exception.h"
00033 #include "util/log/logger.h"
00034 
00035 #include "dat2.h"
00036 
00037 namespace FIFE {
00038     static Logger _log(LM_FO_LOADERS);
00039 
00040     DAT2::DAT2(VFS* vfs, const std::string& file) 
00041         : VFSSource(vfs), m_datpath(file), m_data(vfs->open(file)), m_filelist() {
00042 
00043         FL_LOG(_log, LMsg("MFFalloutDAT2") 
00044             << "loading: " << file 
00045             << " filesize: " << m_data->getDataLength());
00046 
00047         m_data->setIndex(m_data->getDataLength() - 8);
00048         uint32_t fileListLength = m_data->read32Little();
00049         uint32_t archiveSize = m_data->read32Little();
00050 
00051         FL_LOG(_log, LMsg("MFFalloutDAT2") 
00052             << "FileListLength: " << fileListLength 
00053             << " ArchiveSize: " << archiveSize);
00054 
00055         if (archiveSize != m_data->getDataLength())
00056             throw InvalidFormat("size mismatch");
00057 
00058         m_data->setIndex( archiveSize - fileListLength - 8);
00059         m_filecount = m_data->read32Little();
00060         m_currentIndex = m_data->getCurrentIndex();
00061 
00062         FL_LOG(_log, LMsg("MFFalloutDAT2 FileCount: ") << m_filecount);
00063 
00064         // Do not read the complete file list at startup.
00065         // Instead read a chunk each frame.
00066         m_timer.setInterval(0);
00067         m_timer.setCallback( boost::bind( &DAT2::readFileEntry, this) );
00068         m_timer.start();
00069     }
00070 
00071     void DAT2::readFileEntry() const {
00072         assert( m_filecount != 0);
00073 
00074         // Load more items per call,
00075         // otherwise it takes _ages_ until everything is in.
00076         unsigned int load_per_cycle = 50;
00077         if( load_per_cycle > m_filecount )
00078             load_per_cycle = m_filecount;
00079         m_filecount -= load_per_cycle;
00080 
00081         // Save the old index in an exception save way.
00082         IndexSaver isaver(m_data.get());
00083 
00084         // Move index to file list and read the entries.
00085         m_data->setIndex(m_currentIndex);
00086         RawDataDAT2::s_info info;
00087         while( load_per_cycle-- ) {
00088             uint32_t namelen = m_data->read32Little();
00089             info.name = fixPath(m_data->readString(namelen));
00090     
00091             info.type = m_data->read8();
00092             info.unpackedLength = m_data->read32Little();
00093             info.packedLength = m_data->read32Little();
00094             info.offset = m_data->read32Little();
00095     
00096             m_filelist.insert(std::make_pair(info.name, info));
00097         }
00098         m_currentIndex = m_data->getCurrentIndex();
00099 
00100         // Finally log on completion and stop the timer.
00101         if( m_filecount == 0 ) {
00102             FL_LOG(_log, LMsg("MFFalloutDAT2, All file entries in '") << m_datpath << "' loaded.");
00103             m_timer.stop();
00104         }
00105     }
00106 
00107     RawData* DAT2::open(const std::string& file) const {
00108         const RawDataDAT2::s_info& info = getInfo(file);
00109         return new RawData(new RawDataDAT2(getVFS(), m_datpath, info));
00110     }
00111 
00112     bool DAT2::fileExists(const std::string& name) const {
00113         return findFileEntry(name) != m_filelist.end();
00114     }
00115 
00116     const RawDataDAT2::s_info& DAT2::getInfo(const std::string& name) const {
00117         type_filelist::const_iterator i = findFileEntry(name);
00118         if (i == m_filelist.end()) {
00119             throw NotFound(name);
00120         }
00121         return i->second;
00122     }
00123 
00124     DAT2::type_filelist::const_iterator DAT2::findFileEntry(const std::string& path) const {
00125 
00126         // Either the normalization is bogus, or we have to do
00127         // it here, too. Otherwise we can't load the files returned
00128         // by listFiles.
00129 
00130         std::string name = path;
00131 
00132         // Normalize the path
00133         if (name.find("./") == 0) {
00134             name.erase(0, 2);
00135         }
00136 
00137         type_filelist::const_iterator i = m_filelist.find(name);
00138 
00139         // We might have another chance to find the file
00140         // if the number of file entries not zero.
00141         if ( m_filecount && i == m_filelist.end()) {
00142             FL_LOG(_log, LMsg("MFFalloutDAT2")
00143                 << "Missing '" << name
00144                 << "' in partially(" << m_filecount <<") loaded "<< m_datpath);
00145             while( m_filecount && i == m_filelist.end()) {
00146                 readFileEntry();
00147                 i = m_filelist.find(name);
00148             }
00149         }
00150         return i;
00151     }
00152 
00153 
00154     std::set<std::string> DAT2::listFiles(const std::string& pathstr) const {
00155         return list(pathstr, false);
00156     }
00157 
00158     std::set<std::string> DAT2::listDirectories(const std::string& pathstr) const {
00159         return list(pathstr, true);
00160     }
00161 
00162     std::set<std::string> DAT2::list(const std::string& pathstr, bool dirs) const {
00163         std::set<std::string> list;
00164         std::string path = pathstr;
00165 
00166         // Force loading the complete file entries
00167         // This is a costly operation... right after startup.
00168         // Later this should do nothing.
00169         while( m_filecount ) {
00170             readFileEntry();
00171         }
00172 
00173         // Normalize the path
00174         if (path.find("./") == 0) {
00175             path.erase(0, 2);
00176         }
00177         
00178         int lastIndex = path.size() - 1;
00179         if ((lastIndex != -1) && path[lastIndex] != '/') {
00180             path += '/';
00181         }
00182 
00183         type_filelist::const_iterator end = m_filelist.end();
00184         for (type_filelist::const_iterator i = m_filelist.begin(); i != end; ++i) {
00185             const std::string& file = i->first;
00186             if (file.find(path) == 0) {
00187                 std::string cleanedfile = file.substr(path.size(), file.size()); // strip the pathstr
00188                 bool isdir = cleanedfile.find('/') != std::string::npos; // if we still have a / it's a subdir
00189 
00190                 if (isdir) {
00191                     cleanedfile = cleanedfile.substr(0, cleanedfile.find('/'));
00192                     if (cleanedfile.find('/') != cleanedfile.rfind('/')) {
00193                         // check if this is a direct subdir
00194                         continue;
00195                     }
00196                 }
00197 
00198                 if (isdir == dirs) {
00199                     list.insert(cleanedfile);
00200                 }
00201             }
00202         }
00203 
00204         return list;
00205     }
00206 } // FIFE