diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index d6782a9a7..370b6e3a9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -33,6 +33,12 @@ using namespace std; namespace BNPManager { +PackedFile::PackedFile() +{ + m_size = 0; + m_pos = 0; +} + NLMISC_SAFE_SINGLETON_IMPL(BNPFileHandle); BNPFileHandle::BNPFileHandle() @@ -58,7 +64,7 @@ void BNPFileHandle::releaseInstance() // *************************************************************************** bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { - FILE *bnp = fopen (m_activeBNPFile.c_str(), "rb"); + FILE *bnp = fopen (m_openedBNPFile.c_str(), "rb"); FILE *out; if (bnp == NULL) return false; @@ -97,13 +103,13 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList } // *************************************************************************** // Read the header from a big file -bool BNPFileHandle::readHeader(const std::string &filename) +bool BNPFileHandle::readHeader(const std::string &filePath) { m_packedFiles.clear(); - m_activeBNPFile = filename; + m_openedBNPFile = filePath; - FILE *f = fopen (filename.c_str(), "rb"); + FILE *f = fopen (filePath.c_str(), "rb"); if (f == NULL) { nlwarning("Could not open file!"); @@ -111,7 +117,7 @@ bool BNPFileHandle::readHeader(const std::string &filename) } nlfseek64 (f, 0, SEEK_END); - uint32 nFileSize=CFile::getFileSize (filename ); + uint32 nFileSize=CFile::getFileSize (filePath ); nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); uint32 nOffsetFromBegining; @@ -162,6 +168,7 @@ bool BNPFileHandle::readHeader(const std::string &filename) sName[nStringSize] = 0; PackedFile tmpPackedFile; tmpPackedFile.m_name = sName; + tmpPackedFile.m_path = m_openedBNPFile; if (fread (&tmpPackedFile.m_size, sizeof(uint32), 1, f) != 1) { nlwarning("Error reading packed file size!"); @@ -196,9 +203,186 @@ void BNPFileHandle::list(TPackedFilesList& FileList) tmpFile.m_name = it->m_name; tmpFile.m_pos = it->m_pos; tmpFile.m_size = it->m_size; + tmpFile.m_path = it->m_path; FileList.push_back(tmpFile); it++; } } // *************************************************************************** +bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) +{ + FILE *f = fopen (filePath.c_str(), "ab"); + if (f == NULL) return false; + + uint32 nNbFile = (uint32)m_packedFiles.size(); + if (fwrite (&nNbFile, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + for (uint32 i = 0; i < nNbFile; ++i) + { + uint8 nStringSize = (uint8)m_packedFiles[i].m_name.size(); + if (fwrite (&nStringSize, 1, 1, f) != 1) + { + fclose(f); + return false; + } + + if (fwrite (m_packedFiles[i].m_name.c_str(), 1, nStringSize, f) != nStringSize) + { + fclose(f); + return false; + } + + if (fwrite (&m_packedFiles[i].m_size, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + if (fwrite (&m_packedFiles[i].m_pos, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + } + + if (fwrite (&offset, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + fclose (f); + return true; +} +// *************************************************************************** +void BNPFileHandle::fileNames(std::vector &fileNames) +{ + TPackedFilesList::iterator it = m_packedFiles.begin(); + while (it != m_packedFiles.end() ) + { + fileNames.push_back(it->m_name); + it++; + } +} +// *************************************************************************** +void BNPFileHandle::addFiles( const vector &filePathes) +{ + uint32 OffsetFromBegining = 0; + + // create packed files and add them to the private vector + vector::const_iterator it_vec = filePathes.begin(); + while (it_vec != filePathes.end() ) + { + PackedFile tmpFile; + tmpFile.m_name = CFile::getFilename (*it_vec); + // Leave position to 0 and set the value during the new bnp file is creating + // We need the position only for the header at the end + tmpFile.m_pos = 0; + tmpFile.m_size = CFile::getFileSize(*it_vec); + tmpFile.m_path = *it_vec; + m_packedFiles.push_back( tmpFile ); + + it_vec++; + } + + // sort packed files alphabetic + std::sort ( m_packedFiles.begin(), m_packedFiles.end(), compare ); + + // create a new temporary bnp file with extension *.tmp + TPackedFilesList::iterator it_packed = m_packedFiles.begin(); + while (it_packed != m_packedFiles.end() ) + { + append(m_openedBNPFile + ".tmp", *it_packed); + // Set now the new offset for the new header + it_packed->m_pos = OffsetFromBegining; + OffsetFromBegining += it_packed->m_size; + + it_packed++; + } + + writeHeader(m_openedBNPFile + ".tmp", OffsetFromBegining); + + CFile::deleteFile( m_openedBNPFile ); + string src = m_openedBNPFile + ".tmp"; + CFile::moveFile( m_openedBNPFile.c_str(), src.c_str() ); +} +// *************************************************************************** +void BNPFileHandle::deleteFiles( const vector& fileNames) +{ + vector::const_iterator it_vec; + TPackedFilesList::iterator it_packed; + uint32 OffsetFromBegining = 0; + string tmpFile = m_openedBNPFile + ".tmp"; + + // create a new temporary bnp file with extension *.tmp + it_packed = m_packedFiles.begin(); + while (it_packed != m_packedFiles.end() ) + { + // check each packed file if it should be deleted + it_vec = find (fileNames.begin(), fileNames.end(), it_packed->m_name ); + if ( it_vec != fileNames.end() ) + { + nlinfo("Deleting file %s.", it_packed->m_name.c_str() ); + it_packed = m_packedFiles.erase(it_packed); + } + else + { + append(tmpFile, *it_packed); + // Set now the new offset for the new header + it_packed->m_pos = OffsetFromBegining; + OffsetFromBegining += it_packed->m_size; + + it_packed++; + } + } + nldebug("Writing header..."); + + writeHeader(tmpFile, OffsetFromBegining); + + CFile::deleteFile( m_openedBNPFile ); + string src = m_openedBNPFile + ".tmp"; + CFile::moveFile( m_openedBNPFile.c_str(), src.c_str() ); +} +// *************************************************************************** +void BNPFileHandle::append(const string &destination, const PackedFile &source) +{ + // check if the file exists and create one if not + if ( !CFile::fileExists(destination) ) + CFile::createEmptyFile( destination ); + + FILE *bnpfile = fopen(destination.c_str(), "ab"); + FILE *packedfile = fopen(source.m_path.c_str(), "rb"); + if (bnpfile == NULL) return; + if (packedfile == NULL) { fclose(bnpfile); return; } + + uint8 *ptr = new uint8[source.m_size]; + + // check if the source is a bnp file. + if ( nlstricmp( CFile::getExtension(source.m_path), "bnp" ) == 0 ) + { + // Jump to the file position inside the bnp + nlfseek64(packedfile, source.m_pos, SEEK_SET); + } + // Read the source + if (fread (ptr, source.m_size, 1, packedfile) != 1) + nlwarning("%s read error", source.m_path.c_str()); + + // Append the data to the destination + if (fwrite (ptr, source.m_size, 1, bnpfile) != 1) + nlwarning("%s write error", destination.c_str()); + + delete [] ptr; + + fclose(packedfile); + fclose(bnpfile); +} +// *************************************************************************** +bool BNPFileHandle::compare(const PackedFile &left, const PackedFile &right) +{ + return nlstricmp (left.m_name.c_str(), right.m_name.c_str()) < 0; +} } // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h index d1c642e3d..e03d0e664 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -32,9 +32,11 @@ namespace BNPManager struct PackedFile { + PackedFile(); std::string m_name; uint32 m_size; uint32 m_pos; + std::string m_path; }; typedef std::vector TPackedFilesList; @@ -64,7 +66,9 @@ public: * Read the header from the bnp file and create a filelist * \param filename (consisting the whole path) */ - bool readHeader (const std::string &filename); + bool readHeader (const std::string &filePath); + + bool writeHeader (const std::string &filePath, uint32 offset); /** * Append the header to a created bnp file @@ -73,10 +77,28 @@ public: void appendHeader (const std::string &filename) {}; /** - * Create a list of all packed files inside the bnp file - * \param reference to the list, which has to be filled + * Create a vector of all packed files inside the bnp file + * \param reference to the vector, which has to be filled */ void list (TPackedFilesList& FileList); + + /** + * Create a vector of all file names inside the bnp file + * \param reference to the vector, which has to be filled + */ + void fileNames( std::vector& fileNames ); + + /** + * Add files to the current aktive bnp file + * \param vector of file pathes to add + */ + void addFiles( const std::vector& filePathes ); + + /** + * Delete files from the current aktive bnp file + * \param vector of files names + */ + void deleteFiles (const std::vector& fileNames); /** * Unpack the selected packed files into user defined dir @@ -85,19 +107,33 @@ public: */ bool unpack (const std::string &dirName, const std::vector& fileList); + /** + * Compares two filenames + * \param left: left packed file + * \param right: right packed file + * \return: TODO + */ + static bool compare(const PackedFile &left, const PackedFile &right); + private: + /** + * Append one file to an existing bnp file + * \param destination: the active bnp file to append the file + * \param source: the source file to pack + */ + void append( const std::string& destination, const PackedFile& source ); + TPackedFilesList m_packedFiles; // currently opened and displayed bnp file - std::string m_activeBNPFile; + std::string m_openedBNPFile; // offset where the header of the bnp file begins uint32 m_offsetFromBeginning; }; - } #endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp index 3ed36d181..33157733e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -28,6 +28,7 @@ // NeL includes #include +#include // Qt includes #include @@ -153,9 +154,11 @@ void BNPManagerWindow::open() fileName = QFileDialog::getOpenFileName(this, tr("Open BNP file"), tr(m_DataPath.toStdString().c_str()), tr("BNP Files (*.bnp)")); - // check if there is a filename + // Check if filename is empty if (fileName.isNull()) return; + + m_openedBNPFile = fileName; loadFile(fileName); } // *************************************************************************** @@ -166,12 +169,73 @@ void BNPManagerWindow::close() // *************************************************************************** void BNPManagerWindow::addFiles() { - //TODO + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + + // vector of all current packed filenames + vector currentFiles; + + // vector of files to add + vector addFiles; + + // open a file dialog and to add files + QStringList FileList; + + FileList = QFileDialog::getOpenFileNames(this,tr("Add Files..."), + QDir::currentPath(), tr("All Files (*.*)") ); + + // get all current filenames from the opened bnp file + myBNPFileHandle.fileNames(currentFiles); + + QStringList::iterator it_list = FileList.begin(); + while (it_list != FileList.end() ) + { + string fileName = CFile::getFilename (it_list->toStdString() ); + if ( std::find(currentFiles.begin(), currentFiles.end(), fileName ) != currentFiles.end() ) + { + // Ask the user if he wants to override the existing file + // atm only warn the user and do not override + QMessageBox::warning(this, tr("BNP Manager"), + tr("File is already in the list!"), + QMessageBox::Ok, + QMessageBox::Ok); + } + else + { + addFiles.push_back( it_list->toStdString() ); + // log it + nlinfo("Add file %s", fileName.c_str() ); + } + it_list++; + } + + if ( !addFiles.empty() ) + { + myBNPFileHandle.addFiles( addFiles ); + } + loadFile(m_openedBNPFile); } // *************************************************************************** void BNPManagerWindow::deleteFiles() { - //TODO + QFileDialog filedialog(this); + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + vector selectedRows; + + m_BnpFileListDialog->getSelections(selectedRows); + + // Check if files were selected. If not, inform the user. + if (selectedRows.empty()) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("No files selected!"), + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + myBNPFileHandle.deleteFiles(selectedRows); + loadFile(m_openedBNPFile); } // *************************************************************************** void BNPManagerWindow::unpackFiles() @@ -188,7 +252,7 @@ void BNPManagerWindow::unpackFiles() if (selectedrows.empty()) { QMessageBox::information(this, tr("BNP Manager"), - tr("No files were selected to unpack!"), + tr("No files selected!"), QMessageBox::Ok, QMessageBox::Ok); return; @@ -198,6 +262,10 @@ void BNPManagerWindow::unpackFiles() tr(m_DataPath.toStdString().c_str()), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + // If anything went wrong or the user pressed "cancel" + if ( dir.isEmpty() ) + return; if (myBNPFileHandle.unpack(dir.toStdString(),selectedrows)) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h index b38e2b7be..b31d17a09 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h @@ -101,11 +101,11 @@ private: /** * Read plugin settings and set the window accordingly */ - void readSettings(); - + void readSettings(); + /** * Write plugin settings - */ + */ void writeSettings(); /** @@ -136,8 +136,7 @@ private: BnpFileListDialog *m_BnpFileListDialog; QString m_DataPath; - - BNPFileHandle *m_BNPFileHandle; + QString m_openedBNPFile; }; /* class BNPManagerWindow */