From e61e79eee3017c83a214670ff0f58cb89872f3f7 Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 4 Jan 2012 09:51:10 +0100 Subject: [PATCH 1/8] Added: Added bnp manager plugin basic layout, list and unpack for ovqt --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/CMakeLists.txt | 1 + .../src/plugins/bnp_manager/CMakeLists.txt | 46 ++++ .../bnp_manager/bnp_dirtree_dialog.cpp | 87 +++++++ .../plugins/bnp_manager/bnp_dirtree_dialog.h | 80 +++++++ .../plugins/bnp_manager/bnp_dirtree_form.ui | 58 +++++ .../src/plugins/bnp_manager/bnp_file.cpp | 204 ++++++++++++++++ .../src/plugins/bnp_manager/bnp_file.h | 103 ++++++++ .../bnp_manager/bnp_filelist_dialog.cpp | 122 ++++++++++ .../plugins/bnp_manager/bnp_filelist_dialog.h | 81 +++++++ .../bnp_manager/bnp_filelist_dialog.ui | 70 ++++++ .../bnp_manager/bnp_filesystem_model.cpp | 52 ++++ .../bnp_manager/bnp_filesystem_model.h | 49 ++++ .../src/plugins/bnp_manager/bnp_manager.qrc | 9 + .../bnp_manager/bnp_manager_constants.h | 37 +++ .../bnp_manager/bnp_manager_plugin.cpp | 89 +++++++ .../plugins/bnp_manager/bnp_manager_plugin.h | 130 ++++++++++ .../bnp_manager/bnp_manager_window.cpp | 223 ++++++++++++++++++ .../plugins/bnp_manager/bnp_manager_window.h | 146 ++++++++++++ .../plugins/bnp_manager/bnp_manager_window.ui | 50 ++++ .../bnp_manager/images/ic_nel_add_item.png | Bin 0 -> 3270 bytes .../bnp_manager/images/ic_nel_bnp_make.png | Bin 0 -> 25705 bytes .../bnp_manager/images/ic_nel_delete_item.png | Bin 0 -> 1496 bytes .../bnp_manager/images/ic_nel_export.png | Bin 0 -> 27878 bytes .../bnp_manager/images/ic_nel_reset_all.png | Bin 0 -> 30281 bytes .../bnp_manager/ovqt_plugin_bnp_manager.xml | 10 + 25 files changed, 1647 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/ovqt_plugin_bnp_manager.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index 426d3aa61..467b00876 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -7,6 +7,7 @@ ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(georges_editor) ADD_SUBDIRECTORY(translation_manager) +ADD_SUBDIRECTORY(bnp_manager) # Note: Temporarily disabled until development continues. #ADD_SUBDIRECTORY(zone_painter) # Ryzom Specific Plugins diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt new file mode 100644 index 000000000..e06c82c94 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt @@ -0,0 +1,46 @@ +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) + +FILE(GLOB SRC *.cpp *.h) +SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_manager.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) + +SET(OVQT_PLUG_BNP_MANAGER_HDR bnp_manager_plugin.h + bnp_manager_window.h + bnp_dirtree_dialog.h + bnp_filesystem_model.h + bnp_file.h + bnp_filelist_dialog.h + ) +SET(OVQT_PLUG_BNP_MANAGER_UIS bnp_dirtree_form.ui + bnp_filelist_dialog.ui + ) + +SET(OVQT_PLUGIN_BNP_MANAGER_RCS bnp_manager.qrc) + +SET(QT_USE_QTGUI TRUE) + +QT4_ADD_RESOURCES(OVQT_PLUGIN_BNP_MANAGER_RC_SRCS ${OVQT_PLUGIN_BNP_MANAGER_RCS}) +QT4_WRAP_CPP(OVQT_PLUG_BNP_MANAGER_MOC_SRC ${OVQT_PLUG_BNP_MANAGER_HDR}) +QT4_WRAP_UI(OVQT_PLUG_BNP_MANAGER_UI_HDRS ${OVQT_PLUG_BNP_MANAGER_UIS}) + +SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_BNP_MANAGER_UIS} ${OVQT_PLUGIN_BNP_MANAGER_RCS}) +SOURCE_GROUP(QtGeneratedUiHdr FILES ${OVQT_PLUG_BNP_MANAGER_UI_HDRS}) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_BNP_MANAGER_MOC_SRC}) +SOURCE_GROUP("BNP Manager Plugin" FILES ${SRC}) +SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) + +ADD_LIBRARY(ovqt_plugin_bnp_manager MODULE ${SRC} ${OVQT_PLUG_BNP_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUGIN_BNP_MANAGER_RC_SRCS} ${OVQT_PLUG_BNP_MANAGER_UI_HDRS}) + +TARGET_LINK_LIBRARIES(ovqt_plugin_bnp_manager ovqt_plugin_core nelmisc nelgeorges ${QT_LIBRARIES}) + +NL_DEFAULT_PROPS(ovqt_plugin_bnp_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: BNP Manager") +NL_ADD_RUNTIME_FLAGS(ovqt_plugin_bnp_manager) +NL_ADD_LIB_SUFFIX(ovqt_plugin_bnp_manager) + +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} -DQT_PLUGIN -DQT_SHARED ${QT_DEFINITIONS}) + +INSTALL(TARGETS ovqt_plugin_bnp_manager LIBRARY DESTINATION lib RUNTIME DESTINATION bin ARCHIVE DESTINATION lib COMPONENT tools3d) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp new file mode 100644 index 000000000..9487f66ae --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp @@ -0,0 +1,87 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +// Project includes +#include "bnp_dirtree_dialog.h" +#include "bnp_filesystem_model.h" + +// Qt includes +#include + +// NeL includes +#include + +namespace BNPManager +{ + +CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) + : QDockWidget(parent), + m_DataPath(bnpPath) +{ + // Setup the dialog + m_ui.setupUi(this); + + // Filter settings to only display files with bnp extension. + // Could be changed to display all files and react according to the extension: + // Bnp file: opened and displayed + // all other files: added to the currently opened bnp file + QStringList filter; + filter << tr("*.bnp"); + + // Setup the directory tree model + m_dirModel= new BNPFileSystemModel; + m_dirModel->setRootPath(m_DataPath); + m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::AllEntries); + m_dirModel->setNameFilters(filter); + m_dirModel->setNameFilterDisables(0); + + m_ui.dirTree->setModel(m_dirModel); + + m_ui.dirTree->setRootIndex(m_dirModel->index(m_DataPath)); + + // Trigger if one filename is activated + // In future drag&drop should be also possible + connect(m_ui.dirTree, SIGNAL(activated(QModelIndex)), + this, SLOT(fileSelected(QModelIndex))); +} +// *************************************************************************** +CBnpDirTreeDialog::~CBnpDirTreeDialog() +{ + +} +// *************************************************************************** +void CBnpDirTreeDialog::fileSelected(QModelIndex index) +{ + if (index.isValid() && !m_dirModel->isDir(index)) + { + // emit the according signal to BNPManagerWindow class + Q_EMIT selectedForm(m_dirModel->fileInfo(index).filePath()); + } +} +// *************************************************************************** +void CBnpDirTreeDialog::changeFile(QString file) +{ + +} +// *************************************************************************** +void CBnpDirTreeDialog::BnpPathChanged(QString path) +{ + +} +// *************************************************************************** +} + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h new file mode 100644 index 000000000..33b221a4d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h @@ -0,0 +1,80 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_DIRTREE_DIALOG_H +#define BNP_DIRTREE_DIALOG_H + +// Qt includes +#include + +// STL includes + +// NeL includes + +// Project includes +#include "ui_bnp_dirtree_form.h" + +namespace BNPManager +{ + +class BNPFileSystemModel; + +class CBnpDirTreeDialog : public QDockWidget +{ + Q_OBJECT +public: + + /** + * Constructor + * \param path to root directory, which should be displayed + */ + CBnpDirTreeDialog(QString bnpPath, QWidget *parent = 0); + + /** + * Destructor + */ + ~CBnpDirTreeDialog(); + + /** + * Change the root path for the dir tree view + * \param data path to the new directory + */ + void BnpPathChanged(QString); + +private: + + Ui::CBnpDirTreeDialog m_ui; + + // path ro data root directory + QString m_DataPath; + + BNPFileSystemModel *m_dirModel; + +Q_SIGNALS: + void selectedForm(const QString); + +private Q_SLOTS: + /** + * Triggered if the user activates (double klick on windows) + * a file name in the dir tree view + * \param selected ModelIndex (filename) + */ + void fileSelected(QModelIndex index); + + void changeFile(QString file); +}; +} +#endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui new file mode 100644 index 000000000..751c4f055 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui @@ -0,0 +1,58 @@ + + + CBnpDirTreeDialog + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 200 + 141 + + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + BNP Datapath + + + + + 50 + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..d6782a9a7 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -0,0 +1,204 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +// Project includes +#include "bnp_file.h" + +// Nel includes +#include +#include +#include +#include +#include + +// Qt includes + +using namespace NLMISC; +using namespace std; + + +namespace BNPManager +{ + +NLMISC_SAFE_SINGLETON_IMPL(BNPFileHandle); + +BNPFileHandle::BNPFileHandle() +{ + m_offsetFromBeginning = 0; +} +// *************************************************************************** +BNPFileHandle::~BNPFileHandle() +{ + // Erase the list + m_packedFiles.clear(); +} +// *************************************************************************** +void BNPFileHandle::releaseInstance() +{ + if (_Instance) + { + NLMISC::INelContext::getInstance().releaseSingletonPointer("BNPFileHandle", _Instance); + delete _Instance; + _Instance = NULL; + } +} +// *************************************************************************** +bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) +{ + FILE *bnp = fopen (m_activeBNPFile.c_str(), "rb"); + FILE *out; + if (bnp == NULL) + return false; + + TPackedFilesList::iterator it_files = m_packedFiles.begin(); + + for (it_files; it_files != m_packedFiles.end(); it_files++) + { + // Check if the file should be unpacked or not + if (find(fileList.begin(), fileList.end(), it_files->m_name) != fileList.end()) + { + string filename = dirName + "/" + it_files->m_name; + + out = fopen (filename.c_str(), "wb"); + if (out != NULL) + { + nlfseek64 (bnp, it_files->m_pos, SEEK_SET); + uint8 *ptr = new uint8[it_files->m_size]; + if (fread (ptr, it_files->m_size, 1, bnp) != 1) + { + nlwarning("%s read error", filename.c_str()); + return false; + } + if (fwrite (ptr, it_files->m_size, 1, out) != 1) + { + nlwarning("%s write error", filename.c_str()); + return false; + } + fclose (out); + delete [] ptr; + } + } + } + fclose (bnp); + return true; +} +// *************************************************************************** +// Read the header from a big file +bool BNPFileHandle::readHeader(const std::string &filename) +{ + m_packedFiles.clear(); + + m_activeBNPFile = filename; + + FILE *f = fopen (filename.c_str(), "rb"); + if (f == NULL) + { + nlwarning("Could not open file!"); + return false; + } + + nlfseek64 (f, 0, SEEK_END); + uint32 nFileSize=CFile::getFileSize (filename ); + nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); + + uint32 nOffsetFromBegining; + + if (fread (&nOffsetFromBegining, sizeof(uint32), 1, f) != 1) + { + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(nOffsetFromBegining); +#endif + + if (nlfseek64 (f, nOffsetFromBegining, SEEK_SET) != 0) + { + nlwarning("Could not read offset from begining"); + fclose (f); + return false; + } + + uint32 nNbFile; + if (fread (&nNbFile, sizeof(uint32), 1, f) != 1) + { + nlwarning("Could not read number of files!"); + fclose (f); + return false; + } + +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(nNbFile); +#endif + + for (uint32 i = 0; i < nNbFile; ++i) + { + uint8 nStringSize; + char sName[256]; + if (fread (&nStringSize, 1, 1, f) != 1) + { + nlwarning("Error reading packed filename!"); + fclose (f); + return false; + } + if (fread (sName, 1, nStringSize, f) != nStringSize) + { + fclose (f); + return false; + } + sName[nStringSize] = 0; + PackedFile tmpPackedFile; + tmpPackedFile.m_name = sName; + if (fread (&tmpPackedFile.m_size, sizeof(uint32), 1, f) != 1) + { + nlwarning("Error reading packed file size!"); + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(tmpBNPFile.Size); +#endif + if (fread (&tmpPackedFile.m_pos, sizeof(uint32), 1, f) != 1) + { + nlwarning("Error reading packed file position!"); + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(tmpBNPFile.Pos); +#endif + m_packedFiles.push_back (tmpPackedFile); + } + + fclose (f); + return true; +} +// *************************************************************************** +void BNPFileHandle::list(TPackedFilesList& FileList) +{ + PackedFile tmpFile; + TPackedFilesList::iterator it = m_packedFiles.begin(); + while (it != m_packedFiles.end() ) + { + tmpFile.m_name = it->m_name; + tmpFile.m_pos = it->m_pos; + tmpFile.m_size = it->m_size; + FileList.push_back(tmpFile); + it++; + } +} +// *************************************************************************** +} // 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 new file mode 100644 index 000000000..d1c642e3d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -0,0 +1,103 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_FILE_H +#define BNP_FILE_H + +// Project includes + +// Nel includes +#include "nel/misc/types_nl.h" +#include + +// Qt includes +#include + + +namespace BNPManager +{ + +struct PackedFile +{ + std::string m_name; + uint32 m_size; + uint32 m_pos; +}; + +typedef std::vector TPackedFilesList; + +class BNPFileHandle +{ + NLMISC_SAFE_SINGLETON_DECL(BNPFileHandle) + + /** + * Private constructor + */ + BNPFileHandle(); + + /** + * Private destructor + */ + ~BNPFileHandle(); + +public: + // release memory + static void releaseInstance(); + + /*void append (const QString destFilename, const QString origFilename, uint32 sizeToRead); + void packRecurse();*/ + + /** + * Read the header from the bnp file and create a filelist + * \param filename (consisting the whole path) + */ + bool readHeader (const std::string &filename); + + /** + * Append the header to a created bnp file + * \param filename (consisting the whole path) + */ + 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 + */ + void list (TPackedFilesList& FileList); + + /** + * Unpack the selected packed files into user defined dir + * \param directory path, where the files should be unpacked + * \param list of files, which has to be unpacked + */ + bool unpack (const std::string &dirName, const std::vector& fileList); + +private: + + TPackedFilesList m_packedFiles; + + // currently opened and displayed bnp file + std::string m_activeBNPFile; + + // 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_filelist_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp new file mode 100644 index 000000000..b78e6c0bf --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp @@ -0,0 +1,122 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +// Project includes +#include "bnp_filelist_dialog.h" +#include "bnp_file.h" + +// Qt includes +#include + +// NeL includes +#include + +using namespace std; + +namespace BNPManager +{ + +BnpFileListDialog::BnpFileListDialog(QString bnpPath, QWidget *parent) + : QDockWidget(parent), + m_DataPath(bnpPath) +{ + m_ui.setupUi(this); +} +// *************************************************************************** +BnpFileListDialog::~BnpFileListDialog() +{ + +} +// *************************************************************************** +void BnpFileListDialog::setupTable(int nbrows) +{ + // delete all old entries + m_ui.tableWidget->clear(); + + // set 2 colums: filename and size + m_ui.tableWidget->setColumnCount(2); + + // set number of rows according to the number of files in the bnp file + m_ui.tableWidget->setRowCount(nbrows); + + // only entire rows can be selected + m_ui.tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + + // set the horizontal headers + QStringList labels; + labels << tr("Filename") << tr("Size"); + m_ui.tableWidget->setHorizontalHeaderLabels(labels); + + m_ui.tableWidget->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive); + m_ui.tableWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch ); + m_ui.tableWidget->verticalHeader()->hide(); + + // set vertical size a little bit smaller + m_ui.tableWidget->verticalHeader()->setDefaultSectionSize(15); + m_ui.tableWidget->setShowGrid(false); + m_ui.tableWidget->setObjectName("tablewidget"); +} +// *************************************************************************** +bool BnpFileListDialog::loadTable(const QString fileName) +{ + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + // string vector of all packed files inside a bnp + TPackedFilesList filelist; + int row = 0; + + // read the header from the bnp file + if (!myBNPFileHandle.readHeader( fileName.toStdString()) ) + { + return false; + } + myBNPFileHandle.list( filelist ); + + // create table with number of rows + setupTable(filelist.size()); + + // fill the table items + TPackedFilesList::iterator it = filelist.begin(); + while (it != filelist.end() ) + { + QTableWidgetItem *nameItem = new QTableWidgetItem (it->m_name.c_str() ); + QTableWidgetItem *sizeItem = new QTableWidgetItem (tr("%1 KB").arg(it->m_size)); + m_ui.tableWidget->setItem(row, 0, nameItem); + m_ui.tableWidget->setItem(row, 1, sizeItem); + it++; + row++; + } + + return true; +} +// *************************************************************************** +void BnpFileListDialog::getSelections(TSelectionList& SelectionList) +{ + QModelIndex index; + QAbstractItemModel *model = m_ui.tableWidget->model(); + QItemSelectionModel *selection = m_ui.tableWidget->selectionModel(); + QModelIndexList indexes = selection->selectedRows(); + + Q_FOREACH(index, indexes) + { + QVariant data = model->data(index); + QString filename = data.toString(); + SelectionList.push_back( filename.toStdString() ); + } +} +// *************************************************************************** + +} // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h new file mode 100644 index 000000000..f2cc021a9 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h @@ -0,0 +1,81 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_FILELIST_DIALOG_H +#define BNP_FILELIST_DIALOG_H + +// Qt includes +#include + +// STL includes +#include +#include + +// NeL includes + +// Project includes +#include "ui_bnp_filelist_dialog.h" + +namespace BNPManager +{ + +typedef std::vector TSelectionList; + +class BnpFileListDialog : public QDockWidget +{ + Q_OBJECT + +public: + + // Constructor + BnpFileListDialog(QString bnpPath, QWidget *parent = 0); + + // Destructor + ~BnpFileListDialog(); + + /** + * Load the bnp file and setup the table view + * \param Filename + * \return true if everything went well + */ + bool loadTable(const QString filename); + + /** + * Set the dimension of the table + * \param number of rows + */ + void setupTable(int nbrows); + + /** + * Fill the files selected in the table view to + * unpack them. + * \param reference to a vector of filenames. + * \return true if everything went well + */ + void getSelections(TSelectionList& SelectionList); + +private: + + Ui::BnpFileListDialog m_ui; + + // common data path as root folder for the dirtree view + QString m_DataPath; + +}; + +} + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui new file mode 100644 index 000000000..9f62ff7c0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui @@ -0,0 +1,70 @@ + + + BnpFileListDialog + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 200 + 141 + + + + true + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + BNP File List + + + + + 50 + 0 + + + + + + + true + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + false + + + 15 + + + + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp new file mode 100644 index 000000000..eaf6389f5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp @@ -0,0 +1,52 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 + +#include "bnp_filesystem_model.h" + +#include +#include + +namespace BNPManager +{ + +BNPFileSystemModel::BNPFileSystemModel(QObject *parent) + : QFileSystemModel(parent) +{ + +} +// *************************************************************************** +BNPFileSystemModel::~BNPFileSystemModel() +{ + +} +// *************************************************************************** +int BNPFileSystemModel::columnCount(const QModelIndex &) const +{ + return 1; +} +// *************************************************************************** +QVariant BNPFileSystemModel::data(const QModelIndex& index, int role) const +{ + + if (role == Qt::DecorationRole) + { + if (isDir(index)) + return QApplication::style()->standardIcon(QStyle::SP_DirIcon); + } + return QFileSystemModel::data(index, role); +} +// *************************************************************************** +} // namespace BNPManager diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h new file mode 100644 index 000000000..2ca086b39 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h @@ -0,0 +1,49 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 + +#ifndef BNP_FILESYSTEM_MODEL_H +#define BNP_FILESYSTEM_MODEL_H + +#include + +namespace BNPManager +{ + +class BNPFileSystemModel : public QFileSystemModel +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + BNPFileSystemModel(QObject *parent = 0); + + /** + * Destructor + */ + ~BNPFileSystemModel(); + + int columnCount(const QModelIndex &) const; + + QVariant data(const QModelIndex& index, int role) const ; + +}; + +} + +#endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc new file mode 100644 index 000000000..bf32595d6 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc @@ -0,0 +1,9 @@ + + + images/ic_nel_bnp_make.png + images/ic_nel_delete_item.png + images/ic_nel_add_item.png + images/ic_nel_export.png + images/ic_nel_reset_all.png + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h new file mode 100644 index 000000000..7ec5eecd0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h @@ -0,0 +1,37 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_MANAGER_CONSTANTS_H +#define BNP_MANAGER_CONSTANTS_H + +namespace BNPManager +{ +namespace Constants +{ +//settings +const char * const BNP_MANAGER_SECTION = "BNPManager"; + +//resources +const char *const ICON_ADD = ":/images/ic_nel_add_item.png"; +const char *const ICON_DELETE = ":/images/ic_nel_delete_item.png"; +const char *const ICON_UNPACK = ":/images/ic_nel_export.png"; +const char *const ICON_CLOSE = ":/images/ic_nel_reset_all.png"; + + +} // namespace Constants +} // namespace Plugin + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp new file mode 100644 index 000000000..70015773b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp @@ -0,0 +1,89 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +// Project includes +#include "bnp_manager_plugin.h" +#include "bnp_manager_window.h" + +#include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/menu_manager.h" + +// NeL includes +#include "nel/misc/debug.h" + +// Qt includes +#include +#include + +namespace BNPManager +{ + + BNPManagerPlugin::BNPManagerPlugin() + { + } + + BNPManagerPlugin::~BNPManagerPlugin() + { + Q_FOREACH(QObject *obj, m_autoReleaseObjects) + { + m_plugMan->removeObject(obj); + } + qDeleteAll(m_autoReleaseObjects); + m_autoReleaseObjects.clear(); + } + +bool BNPManagerPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) +{ + Q_UNUSED(errorString); + m_plugMan = pluginManager; + + addAutoReleasedObject(new BNPManagerContext(this)); + return true; +} + +void BNPManagerPlugin::extensionsInitialized() +{ +} + +void BNPManagerPlugin::shutdown() +{ + +} + +void BNPManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) +{ +#ifdef NL_OS_WINDOWS + // Ensure that a context doesn't exist yet. + // This only applies to platforms without PIC, e.g. Windows. + nlassert(!NLMISC::INelContext::isContextInitialised()); +#endif // NL_OS_WINDOWS + m_libContext = new NLMISC::CLibraryContext(*nelContext); +} + +void BNPManagerPlugin::addAutoReleasedObject(QObject *obj) +{ + m_plugMan->addObject(obj); + m_autoReleaseObjects.prepend(obj); +} + +/*void BNPManagerContext::open() +{ + m_BnpManagerWindow->open(); +}*/ +} + +Q_EXPORT_PLUGIN(BNPManager::BNPManagerPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h new file mode 100644 index 000000000..55e2e8444 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h @@ -0,0 +1,130 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_MANAGER_PLUGIN_H +#define BNP_MANAGER_PLUGIN_H + +// Project includes +#include "../../extension_system/iplugin.h" +#include "../core/icontext.h" +#include "bnp_manager_window.h" + +// NeL includes +#include "nel/misc/app_context.h" +#include + +// Qt includes +#include +#include + +namespace NLMISC +{ +class CLibraryContext; +} + +namespace ExtensionSystem +{ +class IPluginSpec; +} + +namespace BNPManager +{ +class m_BnpManagerWindow; + +class BNPManagerPlugin : public QObject, public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_INTERFACES(ExtensionSystem::IPlugin) + +public: + BNPManagerPlugin(); + virtual ~BNPManagerPlugin(); + + virtual bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); + virtual void extensionsInitialized(); + virtual void shutdown(); + virtual void setNelContext(NLMISC::INelContext *nelContext); + + void addAutoReleasedObject(QObject *obj); + +protected: + + NLMISC::CLibraryContext *m_libContext; + +private: + + ExtensionSystem::IPluginManager *m_plugMan; + QList m_autoReleaseObjects; +}; + +/** + * Implementation of the IContext interface + * + * \date 2011 + */ + +class BNPManagerContext : public Core::IContext +{ + Q_OBJECT + +public: + // Constructor + BNPManagerContext(QObject *parent = 0) : IContext(parent) + { + // run new manager window app + m_BnpManagerWindow = new BNPManagerWindow(); + } + + // Destructor + virtual ~BNPManagerContext() {} + + virtual QString id() const + { + return QLatin1String("BNPManagerContext"); + } + virtual QString trName() const + { + return tr("BNP Manager"); + } + virtual QIcon icon() const + { + return QIcon(":/images/ic_nel_bnp_make.png"); + } + + virtual void open() + { + m_BnpManagerWindow->open(); + } + + virtual QUndoStack *undoStack() + { + return m_BnpManagerWindow->m_undoStack; + } + + virtual QWidget *widget() + { + return m_BnpManagerWindow; + } + + BNPManagerWindow *m_BnpManagerWindow; + +}; + +} // namespace Plugin + + + +#endif // BNP_MANAGER_PLUGIN_H 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 new file mode 100644 index 000000000..3ed36d181 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -0,0 +1,223 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +// Project includes +#include "bnp_manager_window.h" +#include "bnp_manager_constants.h" +#include "bnp_dirtree_dialog.h" +#include "bnp_filelist_dialog.h" +#include "bnp_file.h" + +#include "../core/icore.h" +#include "../core/menu_manager.h" +#include "../core/core_constants.h" +#include "../../extension_system/iplugin_spec.h" + +// NeL includes +#include + +// Qt includes +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace NLMISC; + + +namespace BNPManager +{ + +BNPManagerWindow::BNPManagerWindow(QWidget *parent) + : QMainWindow(parent) +{ + // add new mainwindow for sheet dockwidgets + QTableWidget* hideWidget = new QTableWidget(0,0,this); + setCentralWidget(hideWidget); + hideWidget->hide(); + + // Read the settings + readSettings(); + + // create main dialogs and display them + createDialogs(); + + // create actions like open, close, add etc. + createActions(); + + // create a toolbar with icons + createToolBars(); + + // this SLOT is triggered if the user activates a bnp files in the + // dirtree view + connect(m_BnpDirTreeDialog, SIGNAL(selectedForm(const QString)), + this, SLOT(loadFile(const QString))); + + // not used + m_undoStack = new QUndoStack(this); +} +// *************************************************************************** +BNPManagerWindow::~BNPManagerWindow() +{ + writeSettings(); +} +// *************************************************************************** +void BNPManagerWindow::createDialogs() +{ + // create dialog to list the contents of the specified + // bnp data file directory + m_BnpDirTreeDialog = new CBnpDirTreeDialog(tr(m_DataPath.toStdString().c_str()),this); + addDockWidget(Qt::LeftDockWidgetArea, m_BnpDirTreeDialog); + m_BnpDirTreeDialog->setVisible(true); + restoreDockWidget(m_BnpDirTreeDialog); + + // create dialog to list the packed file contents of bnp files on + // the right hand side + m_BnpFileListDialog = new BnpFileListDialog(m_DataPath,this); + addDockWidget(Qt::RightDockWidgetArea, m_BnpFileListDialog); + m_BnpFileListDialog->setVisible(true); + restoreDockWidget(m_BnpFileListDialog); +} +// *************************************************************************** +void BNPManagerWindow::createActions() +{ + // open action + m_openAction = new QAction(tr("&Open..."), this); + m_openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); + m_openAction->setStatusTip(tr("Open file")); + connect(m_openAction, SIGNAL(triggered()), this, SLOT( open() )); + + // close action + m_closeAction = new QAction(tr("&Close..."), this); + m_closeAction->setIcon(QIcon(Constants::ICON_CLOSE)); + m_closeAction->setStatusTip(tr("Close the BNP File")); + connect(m_closeAction, SIGNAL(triggered()), this, SLOT( close() )); + + // add files into the bnp file + m_addFilesAction = new QAction(tr("&Add..."), this); + m_addFilesAction->setIcon(QIcon(Constants::ICON_ADD)); + m_addFilesAction->setStatusTip(tr("Add Files to BNP")); + connect(m_addFilesAction, SIGNAL(triggered()), this, SLOT( addFiles() )); + + // delete files from the bnp file + m_deleteFilesAction = new QAction(tr("&Delete..."), this); + m_deleteFilesAction->setIcon(QIcon(Constants::ICON_DELETE)); + m_deleteFilesAction->setStatusTip(tr("Delete Files")); + connect(m_deleteFilesAction, SIGNAL(triggered()), this, SLOT( deleteFiles() )); + + // unpack selected files into user defined dir + m_unpackFilesAction = new QAction(tr("&Unpack..."), this); + m_unpackFilesAction->setIcon(QIcon(Constants::ICON_UNPACK)); + m_unpackFilesAction->setStatusTip(tr("Unpack Files")); + connect(m_unpackFilesAction, SIGNAL(triggered()), this, SLOT( unpackFiles() )); +} +// *************************************************************************** +void BNPManagerWindow::createToolBars() +{ + m_fileToolBar = addToolBar(tr("&File")); + m_fileToolBar->addAction(m_openAction); + m_fileToolBar->addAction(m_closeAction); + + m_toolsBar = addToolBar(tr("&Tools")); + m_toolsBar->addAction(m_addFilesAction); + m_toolsBar->addAction(m_deleteFilesAction); + m_toolsBar->addAction(m_unpackFilesAction); +} +// *************************************************************************** +bool BNPManagerWindow::loadFile(const QString fileName) +{ + m_BnpFileListDialog->loadTable(fileName); + return true; +} +// *************************************************************************** +void BNPManagerWindow::open() +{ + QString fileName; + // file dialog to select with file should be opened + fileName = QFileDialog::getOpenFileName(this, + tr("Open BNP file"), tr(m_DataPath.toStdString().c_str()), tr("BNP Files (*.bnp)")); + + // check if there is a filename + if (fileName.isNull()) + return; + loadFile(fileName); +} +// *************************************************************************** +void BNPManagerWindow::close() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::addFiles() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::deleteFiles() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::unpackFiles() +{ + QFileDialog filedialog(this); + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + vector selectedrows; + + m_BnpFileListDialog->getSelections(selectedrows); + + // Check if files were selected. If not, inform the user. + // TODO: Ask the user if nothing was selected, if he wants to unpack all + // files. This is more like Winzip. + if (selectedrows.empty()) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("No files were selected to unpack!"), + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), + tr(m_DataPath.toStdString().c_str()), + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks); + + if (myBNPFileHandle.unpack(dir.toStdString(),selectedrows)) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("All files has been exported successfully."), + QMessageBox::Ok, + QMessageBox::Ok); + } +} +// *************************************************************************** +void BNPManagerWindow::readSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + m_DataPath = settings->value(Core::Constants::ASSETS_PATH, "w:/database").toString(); + settings->endGroup(); +} +// *************************************************************************** +void BNPManagerWindow::writeSettings() +{ +} +} // namespace BNPManager 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 new file mode 100644 index 000000000..b38e2b7be --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h @@ -0,0 +1,146 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 . + +#ifndef BNP_MANAGER_WINDOW_H +#define BNP_MANAGER_WINDOW_H + +// Project includes +//#include "ui_bnp_manager_window.h" + +// Qt includes +#include +#include +#include +#include + + +namespace BNPManager +{ + +class CBnpDirTreeDialog; +class BnpFileListDialog; +class BNPFileHandle; + +/** + * Main window class. Derived from QMainWindow and implements + * the basic layout like menue, toolbars and dialogs. + * + * \date 2011 + */ + +class BNPManagerWindow : public QMainWindow +{ + Q_OBJECT + +public: + + // Constructor + BNPManagerWindow(QWidget *parent = 0); + + //Destructor + ~BNPManagerWindow(); + + + QUndoStack *m_undoStack; + +public Q_SLOTS: + + /** + * Open a file dialog to choose which file should be opened. + * \return Filename string + */ + void open(); + + /** + * Load a certain bnp file into the manager + * \param Filename + * \return true if everything went well + */ + bool loadFile(const QString fileName); + + /** + * close an opened bnp file and reset all views + */ + void close(); + + /** + * Add files into an opened bnp file. + * \param Filelist + */ + void addFiles(); + + /** + * Unpack the files marked in the filelist dialog into user defined + * directory. + * \param TBD + * \return true if everything went well + */ + void unpackFiles(); + + /** + * Delete marked files from the bnp file + * \param TBD + */ + void deleteFiles(); + +private: + + /** + * Read plugin settings and set the window accordingly + */ + void readSettings(); + + /** + * Write plugin settings + */ + void writeSettings(); + + /** + * Create all plugin dialogs + */ + void createDialogs(); + + /** + * Create all plugin actions + */ + void createActions(); + + /** + * Create the plugin toolbar + */ + void createToolBars(); + + QToolBar *m_fileToolBar; + QToolBar *m_toolsBar; + + QAction *m_openAction; + QAction *m_closeAction; + QAction *m_addFilesAction; + QAction *m_unpackFilesAction; + QAction *m_deleteFilesAction; + + CBnpDirTreeDialog *m_BnpDirTreeDialog; + BnpFileListDialog *m_BnpFileListDialog; + + QString m_DataPath; + + BNPFileHandle *m_BNPFileHandle; + +}; /* class BNPManagerWindow */ + +} /* namespace Plugin */ + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui new file mode 100644 index 000000000..4e695f72c --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui @@ -0,0 +1,50 @@ + + + BNPManagerWindow + + + + 0 + 0 + 800 + 600 + + + + BNP Manager + + + + + + + QWidget#centralwidget { + image: url(:/images/ic_nel_georges_editor.png); + } + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png new file mode 100644 index 0000000000000000000000000000000000000000..bde338f7851d2675b29272c7dcf9471196a8f6cc GIT binary patch literal 3270 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 Eg5p97)c^nh literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png new file mode 100644 index 0000000000000000000000000000000000000000..19b749b1da76ec5ae8a7f063c7127f5672915ef6 GIT binary patch literal 25705 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 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png new file mode 100644 index 0000000000000000000000000000000000000000..a5a1787d593591f855be9a7eb8da271cc8d8f214 GIT binary patch literal 1496 zcmaKs{XY{30LC|C-c2!W(h#B2QhA@kOt{brn;~tK$;-mn#DM?-zdb>=i zu*@RTd8@_pvUxpuxyoG1>Bsxw?uX}jKHujL_-0@|T~w8{l>h*Ms+%k3&``sHb<81=-~(e+|N^9C&_KHVw5X^3IM3J{S1(sAHJ}8!egC%W4+FY#L`JIX8~B! zIZ7C+< z6bcMhk7Jz$z zt<~^Npzeyeat^;b(tkMW#bRkwb5S`|-P(cZ{!1geIKC7Tzpi^Q9XhN7r*| zh)Tmw?h}4)r(e%EPx*~jbiDn>W9k_F^gyx#1J1b!&uZuD!J$Duh_I|$-B|3)pjcABT`DZr@@$gnJ?g*7niqC zPg~-q=_uM61|+bWZC}wlE4;C-L;LVrU6J(R@D0Z4=p`kt`4==LnW(d8wXiBZND)3NugXyk}j`B>QSp`rx>7I47#1U z_C?Zqn_!w_iq*Q=7#33$KOO+R;CW!bcdn#k;~tqqg>*o8s|#;{%M1@^2B=`1X*QOW z`~~EL<8YMd;Wk~hP%ln9+T{3weTCGPC>#3IdbbgtfSL(^;<-$(&+()%CWY(ZY}rUs zYZ^=Sws7E+dDpj6e_>>!qz;jFg;SMzWx6GvaM=_CJv#h+^qF(IggsR{8BYMQlBT|c zLR(J;=NJUkn>YRLcdDtY?zXt@zIEUuTe=1hEndEU6|7%)i#=`5cs5S}P9Mh=PcJfo z{ZbSwueq|yZyG~GMGk&1;XI&33fgfyu~6eIQgk!Fkv4<^W8F=|9U5oXINb+#fCen_ z{sB?u3Xfi@SxqMAC%ZI%1BJIf0uMiCx!yXz62rndMv z$=6toP8$LinV$s7C|ZjMS5+@W1rn4NxN=o7bmp|tGx-1vXy7V6(LK7Owl1b<1|o_O z>bY9rcsZ;+(mT(U8Vqy|@K*SC7Xeg!@WG?ntw{ZC`G3Vh1-90Bf1vJ=9f)jI$P4Q6YBQ@Z8;^^H#>9pe+1$q6DXU$-5VbBeJ(3kUiuh943T}3C$!G=4Zw&rX zIZx;etQ%$b10W;H`0fD|-g&n~Eq? zJZ zC}pc4u@0qD`7mQX=zMD+8o3Hi#&31p7{mxJ*6-KkigeYIgt}@Xn1a05YyE6U8VHU~ z8PL=CTn*0W$(#-|89Sz`9P!(hY?pOfm^ZQVXfjUeBhD+B#yUg0vkok?_%`$Xq0`=> g{}tB1bjDO$2a)_&SEAML{IoE@&Dj&vfF`E?18{b*hyVZp literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc71c09dab0615021bb5c813d47e3f5b2a72623 GIT binary patch literal 27878 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 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png new file mode 100644 index 0000000000000000000000000000000000000000..c6728740865db94a38a7d740accd930a9c8326d5 GIT binary patch literal 30281 zcmdRV_fr$h_jM8iBp}j3Kp=D#=}n~s=?VgZAVrFF=~bjA0R$8g5a}g|2-15miH`_K zN2;_$K#FuJ2_z(6Kkt9>&d$#6{bgtO&g{Lr=brP*)L5U1ftLXQ05IJ%&@lr5fd8gI z0EF(}@FeiF+yAT}Gkq;U&FIywe+Jn7?t{AkKz#}$`6=x`8|rUh9RvVe{`$WT?DzZZ z0sw?8-_yDK=!N}mH+|ca>99RG8~>x!II!o&RsIwE)KnI`;7eM^%Mtr97FalIF(gjM zkF7h_Fmkn5<+*QQkl!Hu`O2|V|0_S=tNpV~+>RGGT;autbz{@e@I+d0N#lChOr!hh z@?=jpj!?O6n{hu<2m1eabGheOxg?oEp|`m4^>WptKB>=--1u$EcY^#s1-2Cm)?T>R z{ci5-p?`FvbN_L<(Oj7s&+S?>o?E46Kc!zQ-G|*T%Db-#Te@3R5N+@&@Liu7iYVoS z`E)3f?7+C=8uNwWMM-D27{J2>X!U5wa6^*!WeRC+sc%W@T*oUUj*am#M?QgWlx==3dzRpDetrBOpd zK}KOvZi__OzPiRmmG0Jt;0&OA&To33QrM>&9#vrQ6#Xn?&^S-?{h{PXured`cphVx zBk4m$_Bp&n(vLs(xVcomlDR+AudeX?b41k4EyAy5g++bR@Z#syg7)+!Npt=7GQIsj z0s`~1ON~b}dzn6xd%5o)!FUI*k(pU?s^a{%sp@6BXAzHYq$?*$vLjsHW9i;wEkV83 zpk6oxX1u`bKi*4Vo*%6)(CB!IMAXd&6P63jmlh(70{wP0mb>pbzp+)QUg}nCpH_!I z)zP{NAbfAJTU-^PZ_|%IF@*d^*|nJTY=8NpDq6HYSMe}5{m3Evmi*ZzNWxeYg3=Tp z&Ak7s(RaDjGvI2$U50ZEwd$~K1uD;W!`4spc7MuSwoOYm0MY%=IyD06hwtz0c|wDK znal4_)90%FJh*k=Dqwc2D@kc|>Xz~9f%x!I!<^>NfW@~mB}MC<0ZRS~(&JdRHlKw1 zS~nzZ>BZKL_PVZrr=oOVSz-$oV+4$T0};Vn%RaaG(QIqBy2g?$_YhZV>r-#nPVC4d^%ewUY8LFzF8u+(t`otbxq)~8_N2VY@i1~q$>Hb>{i%KBw zs_9{vgLPd3Y3VB=0E0M2?WLGdVo0af{q2}`JO^P(ax{Rtah|k1=lGm@Hj14rgei`DVIm`#SBZvGcGnxx8g?@3A z;_#Ll)=}(9Im!Zg?*e0@CJg+`%wTW1>Dey;#yMYlq)jcO)G4 zG_on6iC;dIdyQAUbaX)~AzcAH8mhilP8V0VzUu^%C6c#GEpO1=3Anyu`_E4m;FgJz z4y)1~2|9=(C1^Ea7MWtg%mV-jD)xe3L^6A zV@$ZHgg!%dFVHq{JD$1`+xF&aDsA~aC;qm(R&;9m&+T6yrYq*bq!UxFp?t-GKmHD0o7d<&CTIc>rUbH&V(AS?# z>?HYRcxcn51q{Hz8bDbx(NFqGYWdbtQ()mbb9Jn|Iqt2WhuHyQHgr^74tRQUzE7s! zlvfPI-hd$uBEiC%nJmzs^s4m#O`8S^Yy5K*3LJb?P;uz&>*8yr$Qc&&I%J^J+>U#B zZNo#bynf#<%$NTl32EiB7uuWwkb9q|#nb-Yn&B$-O9;0Zsz2fR()Xu8*EH1b$A&a3 zU=7d(nTPULC*Zd$O7iQWz9?db`a=-7#3iQ3RplB=mcbuD88V-LxA@|Pw)s!}Iz~kM z@V+#S<1$Hwljr?~AlyDxSD=e(>n@Nt#mK@jB0{sm&_>UtD0++GwkG(Q-M68^tC#j) z5nRY)eD^>k%Nz8D+f+W4t1qQ=YJeIlVlVGJSD1fbbpGZM_m)cMnW|UK2C8h>ZUE%r z4tnjHi=!fv!CZ}_C>SPuysI~t!S$KEt3dBcwdB1UtUu{>4qnxaEjh0ouP4VP3j9^7 z&KNwszpwt~0p#-`Ze&ct;q7w!ECp?V@& zsHz=;QtmU4c$SX;?&A>`Tjlz{*?_Z&muN&acWI?r4~}jpTyJ>4n28#LFY{C{@MvA5!Ii?|^wu7|zaTbFa&_3YjN*5Ud?F60ye*weV-Mq&HL>aS)C%2n=$4-D47va0CZEh+g4#weN}Z2U1CUryiOC*l}IVdjSh$!L}rTHCAJ=EJMp zH6`<+<&)F}MiaZT=RJ}lJIUFruF|>+rQr|tK0MUg^fbHH=t#6<0nS((fO|2!l(^eW;G<7X2KK@=(K_5GXwjb%1W zvXB?mh6jg0p&yHe8dzrZ4OQ3-uaSI`qY*jE(lmiQ(3+GJ7fkZ8kb+dBJl(MnR;;M# z+6V{P-rHk7mIvvUUm-xce6*YsnDpIb6tf-c!F-kl2Zm4iv122;4 z9^_c%me5yTSV$5r;W&HgU3^8yC_Gh3wv7I3kvk;>MK!P^qglVu6SK*~`phVkqLjRH z*4<3*Ij>%D3!x^3%X^&^l~tUEdwfdeQ<~;G%dZiRN3{Rx5*0~R;$bXyx2Ll#s5z72 zAX_BDG{R%R;ag5PD9QG^O!BQ)ya{-TIvtTWYW~XMlN;Y?c$3+Kxy>N=KC;TH-1sQ3 zQu2o5bu?cTUE+S+PY^?5B44W5c>S27qJyMHixt$U_R;gq?^H3j-w$uQaYiY29e??^ zy&4s3?7ruNrNkk8?8nNbczK2H0kb(4jF{-R;$a=0>%@L)e{t;aTCJVeO_~{fn6zU6 z%@Vy~ixuE_4KALJc#WkpPo>!M!%xmX%#W*NfO$*J`t|o=?S2MHbB<+Tv6mhMkLd=TS`>J%XCd zU&gwEKvAwLh@VWj3~6lvR#zZq;L|l46CUF0X#Jml)(-D(3~4Ajf0I>UpM-ava)bvs zjC*G^37Pje=lUzS1D;*5f~Z;TZJ8@y_a*8!Z~$V03vf_)Qy1&ki}stje@&!VFDBHr zWE^rZH2Q2URQa!MXG{rifDloj^jU(aW<0V+cTuG6r7%h%6x-aUt>i4sb&UrnwS+xq$^V2HPY06Rz zg-S=5p_{RKO#u&!nk#8)tT9<^zYcmduaBn&hmcZ5BZ}rGUXO4xt8az+iQsWXNhgN6 zLI3Rm&>So;HGb)q^6og)w!>PPqqz(QV~mmk#Xn&Vk7*iMN3Ir5PGf|?&qXo~MKI+G z(%}7is4hjl;aUj&=DvZWDPj6T9cq*}iCB70z5iR-u4q3Hczp+(LPOSho-@ZDBN%vX zGSzN(GOL}*Rpo_9sVZZFtUHBIpC3TS8seOluL~8a~=Ff>b&*sL!1hVBLD1S-xfu$qQDt7Q)6>!E`58Xz1lHv%74F7dt}RRaQ+xQt`r)mDaO; zS@43_uee`|Vq>ZnO{Som7^>c1i_S#ioC}-d-+1Z&EIO{h>ymUi1??P%asA$nD+0b& ze5u+wr#BRi_~d@^P0WPlRt{V(Wu>DYH5}_LnL4Tnr}YI5mK2?NifpU4U*>rH@VVi` z2al`0AFZ2l0ySi!Q}sVbay_t1WPB+KAeQt0f*1RXrrRm=0K$K8E10KDJB82)l=1mx z^f{by(>tJTqNU%WaGsbbymL=f0A@)cFNn*UCfTDw5AtPJF*&jnk?vIZcVWVKoAHGk z_U;>SyFv1b^e^EL6W8OvI0-Wt=W<}U4BK+xzncUT54^0C>8U4w zZfMUk`MMbWzFFkUo~0(#H!2{5RKVx9oH3|m%QJv^z8PXxjw6WK5n z3(l^tYeyH)MqWo~Q78?kON;7Koo%kp@$F^L(VOhBm+cR#!IJA8*Sff>AFw2kD!(Tz^4Z;818>hh|Fu=0Nw z@t17iT? zvX(qDJCVnVr9KUB=WbUnpSsZwq(o;v7^`4~U4yjW&qTiq-233adyc8*JGu`x_J6gw z6BZub(?`pz)_;?RV=?%tqs-KK7v0qlP#X@W%gsiQ)9z-T4*s?KNpD?X2a3G24Dhz( z?Zd@iwJrlx4rGcbtgyfkC3){ZH7;{#I*h+~;@Is_)NKvJn5{1U%ox0vdkL}j=KgP( zkBRvk*ZR}ynIjoba(f}Qti?F3)&)RpjKAzp*jMQ94*YMz_ps!2E&K}fO1;%0OcZqU zR>cA3>~ACEtl_}xX^yjf-Psmvt-Af5N{9X77l7B-#HLa@1fc3i>=BGjRFJ>i@5aBU zLVWJ1<}%^d&8R!i+Juu+T*3aS%q5AIgfZ^%9*Oa&lKMxm zC)esyb}DS4+eToy=cP_^jXI*8ULm5a+A9qCs!oZWe*5Z+oYkG;mPl)kbpc1tY*#$h zJ{o?#ueiv4qF%4MLJ#yZ3`cYCrR(Z7m~C@*gjwJ$?sdw^JFHsumK$F83R3(Z0iB zPdWevFB)eI^2Ow>MC6H=7=Klz#V%ctMjl|_GNKHDP$EqwjEuHYc;tM3h!Re=E8R*0 z;BtyYYTRGQIOWI}s7XmuEhT!naVv(Jxh`*^Iep1Zfu6m?gzo^Biy2fPP)k!MV8hWM-%7;4H&UP)GRYh(zq6A#YXuD7OK zhWe*Lb6||aughPiLLonLBnk>FxDQ0Cb&d0jvL38T{r#O3_|;X#kn1yF0G}2&YIgAv zc>gsvrBDCp<1THu>-LR>;Eg27Bs_0BbZ;COwe&-q*`EdE8^e@lDb?n&)hnxkZFc$O zI3VX`x|Q9Ec2>Uj=Q}Uuc5=QSI&Gc0orv68YXg-Rzf0f}EXYJQEqsEoJs_;_1iUIj zd+=DPsB@S0HA&n_+9xg~44YUkDLKynSV+ze`@oY!zsGc!L5B#1aiXZ<{1UG8tWN8p zHg$Yw$?2hsl&|Hg-m5jY$&Ri2sVLw1mhPosB}deiHEB_oBAD}vfTI|mElw0r4p$1F z6*VAsOOE+?m}0-;)hWcXp-~%mhnd^?+?T0pp1hG>lU3%W+2(PjUibEq#LX&ju*hPH z=_~)Q6_7mM)8ejx#&!ENy0IfZKDpnw%Mv4pg@45tpZ45r?hklvyo`&mF?%o4Xnikr zWK}Nu0N*IV^z&Om7qw^aznA(Q_h?A%*HsAtlg}4t9^gu|@~$Ir5+DP>zppIBWBMC8 z_8*=wJYQtckKotXcrimHC7Fa$22^Dh{q}`w-g8F$M+>9*=ylpWvdR!L=}^vx=LB~d z&{+PyZPRn}$3oRTG&c=l2Uz$M>jEe=CR; zZ5yN)T1WTS0NbEt6|I4MHuZ-&Ced0%O=de}nKWl|Pa(F*feVPwEUt6;HXxFiVr-}h zBJ--iVM2di*FiTko>@9*laM2z>l%XCrs{nWkR>0T&BBVC>@~;wUwNf&(QY}FS3jnf z8*STi7JM3-TXF6Q8(7}6;anW>&g7fF5OmhR>~Op0(>J>?%Gn-y(j;V6?~VVe{^iis z1m=)cbNO4IqkDUQoVhzD3E3HKbS^9qE+?h~Orwh~6GU22CspNARoFr?fifK?ahvZ~ z7$$cf}{-)#SRG1}?Z ziHq81BH(6w)@E+nt-R%*%xt6WrAwvV(-e66qw#RToj(iw(1(a`-k=C7`}OsQ-_t}A?`V}$a>KUOBy=rH0{DP6s2Tcl=6N-A?SOlev##5< z_6x;Zk2-UFoS4LA)@#S}v;Q-1T^M|@{Is)It*|RSz9s$K8}02rkkCW@FKB6X_Q|hx zX2J7|rx}AqCv3-bkw=>?E!=%g&FzyW8b8&8sTaQIYv13_rUDjh{kkTiPF9(h5F~so zFO)o*o3%9S5IIvlj_74Wf?=92oOJZb9NDMCi!qp6Bw~D@Lt#)%W7>%T^~#!4Y*0=Z zPmCbp%?^hzD*B^m!-{ja7;J`mBgQR07g~U{E_^P;x4}J>!So3>~E=iaK5AC;omFnK( zRjChILni{yYg)0LM4Ea1xB2R&-6Okl5t$J#XMv$>tom(oySu)}pC1Y%AMX7bk_mM< zJNO#hwI99RO;~*F3MHQ|_;u8FALf#l9@_`X;GQ?;VWReRj9o~8iIk^(?O`j9*)0XOX7Rl!?~sjjH2k(%&2_VYLm z|D?A8_Gx~bf(qvEa%maY?gFx#z#XN7kMnPNAVE&ri)~LkEr2rqDXaq%Z%eOFWPZnb z8>54~G3tci6d8wg=?p*h@ZLl})7*vRy-a&&8(XT!%3S^4TU);e*g~=dD`>S!!8pj_ zoajv8OqO{CzT%L{dGLvV3vpnBM(T~0?qfCXb+D*hlM%<;(twkw z*2FQhdozspE+t~ z)BdQ^OaZDM(=HHHGB zny|j2EWWqyTc>MHi) zG)OS&ppdkpFDSa|jG`t%iMUWfh2@vv3Y`PjZ?rvs&nx$)Ia4|=IAvh2kT9*{ustLz zF#Y+a(G+ALYCJ&)xve*04TMt@ z$yD{AUId}g`jCynu+j)}n*JiR|LQ8ZixAA(^5+NR#9BggYtz-wxbvU?#pMfxBnU?O zJN;E~Gs;<+={bH!GFaCTJ@}xgu`0r<35sTV8^s#qNE%h$bGD{&;9%`KSyb4=Zkb!a z>!R3XgnZ37xRH=+xy3TQD%{cr=fdZ!CdRkrbFQv4zvbhpL-Rx@Rg^|a{Yk-?FzfyS zG*Qj%oVPv`Hv^#tZ8BDuuzvtA9z?y!EHB`kd+PAe5puB3PoRP4JJ(LI=xVG{@BBIt zo#Q?yc~D-_oOhPXAIYc_o)Ai7>^ypUj~_I{YH`sQA)y!3k2|lki!18Tt*$)|qA;L* zex>PmoX(_q1+IC$Ji%D)y+WaJ-+P!pEv2W$pm*u_yFbqE zONa>3GmV}ZfDcf834RzU;TH?tXDdm2sp@VLUra@S=|K-|IET`%|1R0r62>dJ;F&!Y z&5*OMf~Iu47bv-4UYJo>}Zd+!Od4ncyET*gr3*F$blT_`{0_F=9d zH$DyE6{sdy5JKK(B=3i9^vE1M?$6tkvSvvb%}ICBPp$k$yB3=@W0_jX z093c_qiP%`y70)}IBT2L(rULPB_2BiH#;b%GYS8(9oBylg$cp?Uc$oPUUvig7ub`K zqclPG`2HC9uK>Y;FceHxLedIMjB3PzqSqSTcjU*HqAr@{pW)axapgcv>8{ys|A-&S zn>1Wuv6rZQ`MW48?rMoOv?|T1(-s@?NYo!m_SRicaWnPYepp@Yz*k#@lS<<5jL4J@ zF_$o|dPa5TmNo+BgRChRBwh>bWBOX3E{WTB0m{n{p!n|ACMr`qjZ8QG^5ett5)D;x zInGM%Ye|g6qWDYWYa7p4+#8-v3mpWAT>EOQ3Si$MZOomx$A0NqjSZ0#Y{ClqRKEeV zc_hbh!!OsZ>&9u1Cun@=URU`0wq9`euJ&bzqoe$vz6s@@f4kED(TF~JkkjNB;(^V* z{zNGXh%o`U-N@_f+OO%nS{kAsU-akkW0CFqwaEg%-A1y1?O_PZqPs1vD~!UZ8U}bRxD~qAIi`M?udQPi0NiHZ=ID3i5hoT$DGCUEyl??vk7Y+@ezGg z5zA_XI*>2(VyJ@?=NlfS`~1C~ln?hYFNL$mOz!*w6Nt_!?H^O_RP41bN>zT-WPX7& zZi)uD?J~Z?`#d9_e;{b_x~Z3l0$5qhZ#&_-)9>H=moC^}1AdTBaCl?&b+hwkp~!skuC^yz0r_Ywppl6uLMugvOG-Pbi~rm7y& z1bEKSP+9>EaM?o(@LI|ZxkvEyu~tI26c6U`zaZaVT|e)sAuH};z8F001`w`XhregA zwcqBYDDJj3QT}_49={Duj7m`e6>((WyXyut&EW*WO>Ft(%jr?`+ zmHjAxQ|)26HORAe?yO(Bb4NaS>hnWfo`lM(P&-fS>nD9Ff-;YFuEUv}SZWPK60j=c zG*9SIUtCHYl@HP>E~2&SUI5{LQKWh6b5Com>`X`NY*m1t+R+e7?=k0?0LjU3k{iH6dRH59QY5E0(KQ6SEF;Uee)jV>CnH%RcwsC zpRgOwy0lSP++JawdFPam^;`10`}@ZC#CyuU#9&f)Mj%K!-YV;jESCnXK3D3|a42<= zWmsced;CaUnM|mckz%k;fF$bef3Ts3mO$$PFl1=SNF+-rvC-neW2KPF+=sZ%p$W%KKY>kR8ysgGxJ1zI=ok}D$zX`hO~TXpcW(+}!BQ3HHG|q>6Q8d&PtX{I_95_q_oG1I*j0p9};NGJTCBA3?CQn>{kN z``ecHu!MvtVOgo%)+h_)?YCjTXW`>GX|ubRMg8l=w>+q|HVMqLV}6?xhImBKeABbN z!ic>9=4&GcH4ZbH@&M}f+*I&pG5~N~=|KoSKt~D>2-7ap6gfov&X@;~PjferORaQ7 za+dvzFH&=%I8jNhR-sZ0b7{2)ll|9wW5Cj^LTVCls_>W(`vHbc@|?GW!bcUo{d9p3 zZ@v3Ctm+QPu^P#8EcEY0p)+9`n}_=sv%i8!i`4qfBA4!%R4@$r%JXGZu92USxdC44 ztd`DGA%KbFY2A>~YOYK^8)zeE>na=4kI8@Muc6F{D9|h<73-SyE~ZJql3X}2`VLIaVVeXtT|nCa zYd^y_u{4)I&uEQ%>okf=c!VpNS4&jR;!!lu0}1>MwVm~@kl8Ksb{ZVZrr)IHB8%XX z4XDErtqC%0sl4S`AH|3aoBMFDu&ny_7~B%PSLMv2HG^MlGC}Y|uKGaGGtlyCvC@zf)^IAT|#LMs%ro$|+dZ~b9(Fc4?9 z`>u9zdWufrmD6hUZWIJjL((7N5TxDK&V&rh#fbgxZ7e+G&m%$;#ksh9@>eWQ0$8Zc zaY^t%h#1yw+2BOPBP^_$=@b}vWH7`o)wLqpkYv4gg;38D`RG}@rQ4pm(HqYs+OOj-Y%qH6E@SFT!7$A7&pg3 zFxqeV)cQa15r;qBC9oz&v&|Nu&p8d(St^QlWqnj5pB#m%8g0!6+kd{N&9sdX*T?kl z{M=3j`~(`{c|RTKfZ+Vf-r0XV{`@=Hb#@(?3NA8;ZPB*Q@h(46?jitsX-eau5rR6J z*`zAiry0!^?_NAD+1QVTMB-^EO1V4(kDR^awGJl5jgub-Ol54Kw~Z}c2d`iY1ZObw zCFUg}HLBNvz-?P(q1l4}2P0m2qNBG?WsrvE=;US#T$BL~Aa4xcII@l6VNDG$9^u}t zE0dI`R47_xfGc=MKz0^p2hAVe1{ur#iLn;|0*-I)J|n$>9HcQ%#ubmV6~zPCV5&tN z%ZTf;R*Z4Y9`@AUSM2c4t+Sm_@9t$1CpK*>owalf? zl8QU6M~BnXZRujEuPI{>h3n>xzBf&>h+;)c_-ec7`8#mYn5#-J>KghF>wn(YyqjCA z4Y&!piydhNNKa_s9*<4#JU3?DjN09!$>&UHpxZFL6=)PsWrn6cFNS@ZKXfnYv^Rem zKvRL;Kky6l;X>tf@#mVX*DTUuhkI^Ze+1-;(U>ZpKH47fJ}oF;7ZHMLUV3y%+7gU7 zpDL&RRQ8^GVNJtuFh;zs0msAd2gI_FlipiEP~Mt^*03T1VHSzby%1-l=|KsCURdX% zZ^b`&d1{;dFxEePu!K;kO6Zj8+*N+o*nPOlTzg;hOKvDYJfg2c(d@Jn=-ZPS7YX0d)>-a4$u-^vViba zL9{8QW+U=x&%2^1?n^~s=Z|)ISUtqis(9qZJN$Ai^Rf*3yoj0&M^Knw9XHA-l7v9~ zp)w4Ky7<1{F<#yMU!kDC;LY4k=dqt!psNxE&oE08sEek9f#lpkb?5n}{kK;Y4MyIr zIyYt^vB#H+W`!BQHyNP;R`NIB%;dd7yEnM!P&E#s#!2|H&4kfjYxC&Fb)YHKfR#ZK z|48+$r9)D>!ytMmNjN5p4RNZ(3AJcFbHU0vF#f%oy3mpcZfN=(#nnjrX_p<=JQPVc zRdTY|=KSoP&c_cclkT8-2R2Ta)On(4HZg{u}9*3=ma|!wT zLS9fW9N&Gn2ZKv~I1!QW-aP%nZ zil^fVa+{{-q1KCg@(1+jhwgi2(Qph^ahfNtQ9uZA2WD6PzN`u*_~xqGi{Rc-E>R#W zwwdnyCWz>P&S}>OFQo3#py@s;!)P)EK3*s&(QiE(e_{@2Bxh5Nz{RYP=1p_X&!X(9 zP3JSW^tM`?18HH%$Dfuewr;9txnhGvfC#Vawc~6MhlCQ-=NkR!Sr@U$vx?_gVyR7g zHC(508t__~cbv}uhzCR4Y9oSs8&94<5L2v)?Z7+q2kf1YFJoTfsttagvmMfDoZiWd~>HY-7N|dxx!in*5-=o`1_Awd(WZK0K5(dYBo_hK7k}WonBX; zI_voVJ?x)V>;ejyIUeM8sayEct;9jh8rvWT)*CPPR~vzpr1MBb9Xc5A&B=5os(BOn z$dPALHAt=L>W+f_xL^D6O420EwRt+nT7p!$j$RRSm5=_=ekp);REm@QjMx7SkkZ0` zqj~*}2Glgkq+E9Qzn^A;$lEv`K@G5K4xPBrFJW1cv{9X=w|zb@BG-^EZakl`kp%W! z`}4*8?|bzZ>m5VlDbu;nZN2ntYI{LfKI&?|JGGm^n2h;7DoVa{dSje6Qe9;Pm&RbO zsGb-vF|R82>c+z<{h=fKpuHsFE1H6y)U}Xv>-Q%DRoL7#z|jS;1U`rrD8%hV$fP@< zjpXk)a;#JLE9KX`n(1P&)XElC5f0y1DN=Tz1;~4}trC73MeC?Do)~8T+!*rw?*#`5 z?`Hb?O`pslUG|yD;NKt5*cTfVj04=SVKg3A0`3By0!QNlR^R!mTWds2BlZ9-Kn-AB zeD|mb{es^{H|$8A=TEMA*-4a2kz3fK*r2k)@CsE2apKXr2A{5`^3gIJzTf?8<6wUD zQ>UpXr3(+b0vb;N!Sr zDlP}b2%DtFtiBB}^y~OC@+h4_D#s$3<^_Lbj>QIPW*TPuhZK?K@35+Jyl6yzFKcu} z6K#MMR7!$11Bhf{CSS+qpvcB0`al8JxDu=vscQRC$y%qNUamr!NvKMtXxM9Uu@DQ} zMgd@<)Grf;IE+s-z+-ik1ql=YedxY&T^AeOjU2qMah32&3rTTLZbsU%M%}+3p(HW{ zwUPHE7XcAk*AF7krc}233d>v@qCj<1d_l(}^cLk|2fba|yWoCEV4lp?nC~Ok=)RFZo)+<%cK(#Br zyoZX-H_6kwTKp%ihtF*A#)Bopbt=O6-Pl-oSVgl#7I=-9<)<-H!jva53W1~VI6LZb zH9a~yq1*seVo!nlu}g*UGK)|jwb~XNT~Ry@)w*DtS#Zu8?AZU6AZ6Xqn{3heqw7DB zJC|CUo5}Sx6PNk^-uX7RSIWjRKu`iXGO5_Y;;zBX15&1~-qX=*BOKNa8G#psPd^3k zb`I(LEpQ+jT?bO1hb*6Mzv`H{-fY)dmCCf<6eH)GQHPWfB# zLFo5ObuA7a(HpdsVJF#YChT8q!KcHhVtkmBoK+u8tmu#XIt>m`pnvHqLa&$2GkTq4 zT=$mY5#p}cqzh~+;efk3H+NCp_bX9SIR~r*U}jvTR_)d{U0~)Z^?ebba|R&gMsn1n z$i`K15-J&SI8!YpWis(?J&>~_-+wV+yM(^XvxT4bc8?f7@3*xth!oif2P@a2qfWQx z2kzJ}aN7VuvY9}bGHe4?fO~a<4N!$IYJr1U{~-%Hh`WZg6(VVGI*I?J75|BjFV6(3 z2K}_w@cOWbs?$T3cQ7v_{t<)!MqYsZdGLg401Z`POwB?c#G+*Y)?)({9#*}M@h0y5 zTb@le@IDgVGX{w*7Ut6fH?f!XJ7%tzF{CBJGf^?o$D1ktSfx_tph0)|{j13EP*AJ} zG*!qkEv0jMN}&ln3@1F;FUA+vo(ZA+*U|u30+63AhM(X}rxD;{4Im^U1e@h)z^dmUM#)VM!f%NvEo#QQcxiSAiNyD&-6$YtoucdJIE)o1jEDt`x zk?Vysa7o=Lgst5-^-CYPZHEBCIgzG`G!7cpL^SLhcR#mZm znHtb59tN*IJe+L?S8!=&_Nn;z_BRZmf-aGN&9u_71wsb!~5U1JyHA zgz@l`_nP@~^3^C%!{;cvIHVZf^|FpY=4T2E%EvL0nuv%^9=e zsnBfzTPp2=AEg7Veyp5`=#(%l3OT?py=NkFyJ5u49fBBKKj&fA-X+bP%ywTx6w=Ai zVEmZkTvFYorn)7PY1G7o9jj)%=*Aw`JlWC;MP?*y-mXiDDQW`>12aN<_1|kUX8@9= z4WRFMUBbO*M0xV=y*QrGZ!>Feqf89QT)9u@9L|pcvi*!aIi_?|M+P{ z7t{-`NmS8(uW6EjZTYw`PvP1fi{nvrR)+?&07PZ2ne;vWY-l28;CD?gNr$ZV&p;Dw*tCmjw0%w zfdE*ym7_&S$t7}E!j(8C5u{BNvMG`aI;XJ$yyOKMq#j8DXXHdBh1rO^Uo!5aUySKM zkFP*COW`~qMU$Z-{!mb*^Oo|e)X`B!}iBO(FJ@kkSrTS3TUr=X$mwqAPjz;@7)JW|6m1# zuvztZ=csCCq`n(s)=$3jq51Ku$bHA5_nNg2eS9z}r3plADKmEo6m`!iw$WR=O>tn$suzmoDF#`L=FD^d%Mx0m*z;3iq{&0F7_+~Vz^KqKn&(0nfb?+i09HM(lMlQf^`T#|gOVK2B#SfN`dCBPich^wQ5A6)Ua zv^}Q?Ez3A2m%6NVW~;{ctB*vRw15vAgxl!u#TQlke3C}AZe3)DNDeyil$@`tPWSC0 zo{_&&G6qAZC;#q9t+ga0J|>CQWem^z36vHKDlSdfyjAtmgSk<;d;DLNDZ({R_0vuX zP@zZ%soks(XUPyPP~EJ?L!zjoG|B{!&j{Ta31?_{ePw|DF@R^OG;?J*pW|+1h~uXp zwMjMFSy99G8TXU0lr~N3y}2o6A8TrOR+KflXv7xeAFCf@?kk?z$Xx_Ky*tL6{@7DZCAux*`C^^R z$)A6Mw!oXA%)re>%;ui-aOpeu6rz33h5k50{u31y`Dnx(j02SY@ghaz+`nd>zAbFN z=bwEH1#wugReA{$pw&$^*b}_$b&>7`>e`XiD|0(_&n$)~Z=L3)!?}4{_`BMz0 zk(yq(KK+z*?B`!!%dQVn86&NIoyZGyy0>cECj{Sp&nuheOP=AT1v250J&#BqtIgSQ zEo6#3HsKH$A#+z4}Jafx7M>iF8u?y9H{N(>&#GCWI+b) z0klZ(YB|Oa%wj{+l`!^qte@YiNC2b}fblmn3a+{76;=2zEKwqc2W3+Qn;h>ZgJm!N6=LIT;*mkw zM(^FA(L%yj%HHaCE_;=?cbDv@+2kT4yZ#*|S$`{__0ZheJPv{{ zt-i5W{QgpKWcoMh@XLQY`r*5u?obJ1KXx=Uomr{l#~bnXRThE&y_>%Q4YmSU=fcMj zV+;o`H~!`tYb$@tmck@kywQ^7g~lzYZDSR0+d8ars}sJ*@7IsXvfUzLX|_AAnXC1J zg-H+Iv&h)AjX$)kq8=)Iqc5~*Ok)q-p44Z*bR@X8M@!V6BwyfoK2Pk-dUeT%yeB!< zZtcCwM7@$h(}yE4KwL}NcGS%pnUn7k2t?J+CV6yuHJQos2aLQwxreWi ze1t=fRx`J1K-Y38x-*&&{r8V2pZ!Q1Rh}But+IDnW%T_0GOU0;a&p`nY}$Cm%*W3L zK1C7Yi379rh|9kCTxyiCeLdCeWs^YLe$N`=-P2!6UNWLN;EUSt3#aF&0vUsOKlZ$6 zkFyjD**9^SnN$hEwu;-vF)vCEmJ3wGc>YOHXRZg%vK%xv!HXm~4W7fqkb+x%Nh2o8 z*Ups>6NYx+DZ!l8wX&5GMpq<2S29uoY0;gp+8s&snu#alGedSx1Mp&PqePAvOWNtAnI6KE?)BfO6cn& zC~sj2v9Er0e(8I+in@ukSfK;lh9Tw>oCO^+Xx zh5pu!5E>nwbmc=SUQD$1y`t_-{GfSsmlp6Jf|2LDP z;J-IGbN8M%KJDoRn<2_b(}S|Go!43({XCbc%~5*^aTuiX0sQ~Sh3@Oa=)iP&gKaTv z*7ZNk_ajw{Pp4aK-Uw@bww!Ra0@^QkbFCf4gwly(U+Wpo`_YOcetN}Zg;9F`ue$U8 zXY2jncoK=dwUnyZ+S;qB)(A!IRn%xvwY^cbSH!4M6s2kvwW_w-qeQj#YEfI#+LXi& ziR7ElpYeU1AI~rMeIEC@?{lu#^SWN{2wMK1zkon|dABiR{(HrLeuuQkRXho7G@=7I zilhjV%Zxx{i!dY6NXIuNiHTdQn_Q5iaQ3^#o2sfWkuegZD)oC|VMYOrX3?yD&$u;_ zp6VSkB1pwN+?^EruL8nFPmYP7GsH;$>}rnn;jiiMc$-9rYlFH(Yb?aGJ~z)-wv2vh zKeoJ&HLgCOE0W^FT-}L9H2UbF{brvI>3w&0I7_uv2puUYR7UI!Kw`)K1Q<~!t3WafPGQtxu^5>jR zNP-z>eElz9a$$R-a8Ydwf#FHv5@J`<8`ADMQ%`_>Vgr<-44=~AY)X3;bj5u3!)bv2 zR-ii%>ZBqIatYnkij7JvM3)b92g(kn!G51;#}oPi9-17vB0~+Ji(WvZLJu3jp&=o7>oovh&zs|SX&oXkn81+MJ}CF|s429B;OVVdxo)@^Z#!NR zV8sTkn0cEg;~1OQa{1ec8}Ml`@&Se>AHSPDh_^62_|3FCjC2k|fiv_Pr@Lq0 zwM_2>9WxLaJ{e@=w)YNeK4aBzfOoa@=GuUA-Oq<51Vc-%8>6IBdZ`m^Qt{c1foy$y z$#uh1$a+WfFI(U`oXyN~-nR`)3mM%vn|wJ>#Ud=l*i2_Vl6`&4==bgZfQ5Tvd#;#? zsS6Uxgs{m5AP9Wa0I|{;XOdIT>vlThXpwCwO2`0*%pzc#qO1V0g;+H;(RvUq-^zW5 z>v=cu_zeX4YzwVcUbYU7BKx7p*{F%>8QI|~`EXGE?H=%!`UAu2sCw|7>c%r6tbkIi zDMAf1uqmN@IB15meJp76cC0{A9C)=S$K(DW5+rK-<1U!@fU%mVdt{U>;RTKExrvVt zq`_E8kp=~+bVkUARyJLH1N>xkPhH*~1ducu86Ghl)`)uILbt{S_^gSh=f8{pTgu4* zCb_3lPi_z?lFgLZ$dztG4l}-)wGWr?@~Dqz@=S#>*1+l7+2#p7#cl{O8F1(_zyA9# zp%E`90dN_8=Z=Vu_FqyWPClqQIrj3ZYi5S38c$%S@k|3lRgauFEdO%j2L0=|TrGE` zkI$fN_1rnJm~)afI8BS1^-9fm$vKZ6I?y$>@V2}bFYI^X0QvDB<64Fy#Ph@K!3#?TpU(@q09}>?BL?Tnj)PM~5=kBea*$(-rIJm^{7yxwcj`F!k0gwI@~|kd z<&q8@DT_EYUC(hRT%hdp?%TE>&8Rmfb;R+2&wlZCLw`tn)IaN0Mc<|!!sJ%f-iD}U zc-D3i4}~Rf7F(IU0EQ-kYbTY|XdGNUCDkOTAu3<5h`EpNIP@ATgqxP4b$TU$RA03S z4<4!cL%t*+e>oQd`crwg%a7+9rire1>>uY6!!6zDV!Z(;;44gJCp7x7yu6-I^ZcA_UY#h4glDap}#x4bG_G3EP z_9m2!Q&yTcK!DKKsiTS}27iNw%b*i;rR;MSH(a$@uAlChpA+@hkEA0ps!@?fOhf^? z$tmVE)mu%1>z}AT5K4b(Yt*6SYXivOj*Qq_{GJZSyjjHnJy50 zaX)LS!aryBRdfC7x!5!<&?89y^>jI;Qi>?y$7=&G&3T>_;wuFMt-Q9+Rf<0O$8KrY83*^8qnMIO_C6 zV6Gfuu&5`eT%_IGKD;DMC)cY$0jjz&_xtxfcFaaz?cXbZK^8JqAH}HTp&B|5Ei=)l zGd%8-1^3<3OcjpChut>Q zU3!fx=YL`pE|24Z*OO3C>)2;lA+Pg<{+}-)qD=E5x|e?lC6^D-h34pm?GkxIva-x3 z|P^sAtzmhgSy9NJTcpbWI2>L4OGfU`7yu2X3WUEl+rtN}Nk++6t^X3bV zg{~>?pl#zA-RR^+!(*!-E?oEZj0c)fPP>~Yt zqc>CVqR_M z9SrZ-y$6f4gv+Lp@_4=v{t9bc*V(dv!;lY{14llpdDAmQoFts9!4I?$`4Y50r(W4Y{zDx=VfWxaF~0Ob!He@a8#g}+(>I$yt2zK`dy?}iM$!G|2)l#siwkJ1 zFlz@O=Qr{%fJIH>v3lQ%4=2){JYZghn^t@*d`?oHDhwfQ5Z>pUd=Nq6BD6tbG~R%o zS9BVJaadTyR6ne`2VA;M*w(fdg&xdi$4|eOce`>hNjh4(6ysfea}CyS$TK%#m}am0 zY^W`ZbAtX_+3UtTFx_sP+;X@!K${}7I;2QF66bHGW;eqvMc>9(Spt4j1Uvjb>Xz6C zb6SKbpJ{AqHDzL-w|pq9Z&E0K0vMtB1bIR5t_K=w-#!|+%HL;EBvbwdtQ>@iJ{lJXtKGV{;F59mn&QjS@*W@j zK&?&9Lt)$|Ao64A(bJ(^gxwR=>wq~kN?I4lK8ed31U}aQRX`XH$lK-NGtT=>>Jb^0 zJ@sqvceiQQa|NO$-~92o@2rWA|Bbgud~{Gsi;$7=B-l=hYYE;jQtM(08Gj_b2RlF8#?uZv0O? zk`MO))B=S8B%^OxH-kdmjY0~AzbZVN^VXFBfcUQkH>0wVYKrR-8Ql^mu3!@eR{AAm z{zQdX)Q%?z(bQwqx#D62a0}nq6|34w<|zyNZ%TPO<&1*97IN?k;~&bWORzgYlS=-w z<8OV{T>Z4&D+mfc{z$XD8npa}@80G2wF~ES(vwX&ob99(xZ&&gFZ{D2>D{ja){}Ch zI!Nu3Q3f6RNav|1pZRb#_{Sj9EF;DrAtD)ri@+@U5+8aC+vu<{=6{q8__XH)`Im2= zZFtQx0Fppcou*xRLA>#~yE?Pb8n|0q^Z4XU_RQ0!V1>|Iai{IcKhY4cdZOj?A;!Dz z%X!{yy{4Sv41YEZBiyfY-PIAsJs(cFWvqI#mf@$1yscUtMX`)8*e}L|1FMm_Klt~^ z*-hjUz035K#yDs<*?8^p&&P;e!NU+I2M4cM@Zq?mAJbpk?kP@mHuZHD5v1<3NIsI^}{ZgbJBdW;GZ>OUu^Tx?fV*) zpjaYEU1!dFt{;GCrlOf^2Xuo-k^at+fJJu1!0%sRmW7mh9Z>m+{8fk1@k5}o|D6$b z8F6GW`xD^35d*WZTXG;U*w|u%Jo9TM`8J(-OwgThfS5}}?t*ybI;Hwo75-e-bT>MW zmGV~4@H;jCxa)T&-L6y9y|`Go=gpOB5qf;R$9+Rpo;AlH^C9TpCy#?SW%?O*RLuem zMV!|sNnt)+D|0Mi&Sx14dMc|E|}- zUKLRN2k4E`u|uoPdZqENn~zjO-5{~R-WR{^+D0vdd5hswvQJw}nyvHzd~CeMIT5hi z%P^PYEot50ud}wu>l;)p76yNvQDaVmqLM$IN)y1e?2bRD698O*$MAz_znK)b(|UoS zSZK8Z3d#qkBKaGT5m&wJ zMGF1Pihte^uCJvC%~TI0?ODQ;ATkWz?a$egL(`@?PzAY}n&%CURODZjsvUA4@mb3$ z>0IrY*Qw0OU(H@;W0%i@uAwr<=L2777O=O>t7nFKNqKBUR$hK`mz@xSq1RlDK3vSlRUR&~ay=P)_~vT8+Q-Xl z!tjZ2%IsV9;gvPrqBoD^+*<{7m8thq1Nu+s=#c$+T0kXvbfW=5u$AAqG2T*hi*t@( zm%C*15O7oIO&J)+F?3&w`}aRXF|tp-H?jw?Cb9lqPqO+#OF1E%jd_r>P>#6;f zdJW$3&3g{pjrcZEva?PRWAUMsU%bGkXy;is_DR0ng~gV6L(ylztH28~>s+ZrqPc?( zhz@^sN!abKT`9~CVuge<{1><3j>$4@xe;BcrDiobB|S{1zUR5&A`z7a7$!#AYUkU< zD1RDj^8w=Wm4Ks6I@lTCu@$n)MR?Q^+?@oaLfoqrbnBll_&qRD{63EbVSjX;F4hUz z9{68q%pZFjFF-Be>qhzU;!neCZH=9T9r5KydJk*fM!ptFd&g8^0V!AiRt=G^6vich zLI-L}_U=nWRWiI7(&7^4{FZI%h4re1p|bzg`144+xo zoUsFthNvz&a$ZjHJ9?g6Wk)^e+&7;hrBHA3h%i7W1M8gG5{zX6`D|STrPdlq%7EOb757Nv|%> zQNA~=T4KALq3`2oyd0ET4Lkvvcp=k~#?-o6_BQLTuT=hku5DJnQB^qUoBfpUAAR4b zrgb{}7`jNqxYcuR*Sq!}TPi99r3x37G+o!hv0%hfRNQeNb=0)1rl!|o;1Y*4#H_Qk zh)7WTnC59jO2Oknj*A0pOL&$@S^(eL6?O`eK2};b1;@M|BZhis7NTuY_UfwdJZ>A@ z%zCbow!}!w9>qS-(u_#onDC=nDO8ixov|w8h83ox7_C8%g68^fh(?B=EC+SWyf&$` zgj`qewQ#p#4MORK^?{&;fWdUv^9xlVZ<7PL^nIbiX5CD_xpI@1a_DZPT6Djw-pSwH zx}YhA6CRwHOAkuz=_@NMyYJ6*&aL0uLy6fWgH9&Jm-3eI-|YgrSJ;sD#;i6BrA4~1 zcrY?r-M35%MV=F^-HN;5I6S}5yP z*xhsUZ(RUQMiF$qo4)kcc(b!ge@pXI{u=W3w%+A(^O_6Hwlo(^yx2FPQfuaZ5+k$s zP$mVUGJJRvg85%Y#mb|=VuR@GsS4WZ-Mfl+u`QxKb;_h30OD7G zTQI%2)qhb$)oycn6pK3F+h_>QIbT0bm|?)#=Pc9{U?Uj!(Djy}VLLA5;f;OEp{v1i zB0#fPFm=R~S`%pe{XBMJdm`z=KH!IQk*%Og? zAKn0P(wF7TEkstoySP|@PAy1}4kdki4{S;2xBuyy_Dg7ebSYBetpv(-ukYovG58I_ zSXQXl>~3V@`B~-*1ju$_RBGY2I(2FIP5dCJr*L2V@Z@L^d1l_LroydkU#27V-xyP( zB5F)mGk5S<0YWXU68O6qW_he31pRPpr$UbzA-PH9W}S=Qp9{ z?N61XLFT(?-L!~V{p@Apm{$yS%IxS!WY>Pvqot|Gb~|sDx`PBg6(X~DHx&;Gm8X_L zFG9KzeA$v7XxeAE<$Ry35h>9HM{TLs40P_pB8(nadLt6=>`!p47Tzw3)YB{&Rx$HW zI?kV_OZMb%?Lriw?KzJ}?t610y%OoVgd^X~JWU4wHxAebkape;R2ksM%Pc%Gq_7x; zk2cog&+B!T!vWoD;ao*L5NOGIhs#Ypgko>6eJc~RmhgNevf}x6k@ce23C$UAQ(dR4 zneEs=7CK~G>iP0BmB{R9Pw;4+Xo=N9~ z2l@SUUnTbMKj)Z&{xR)gVSP|fF=I?L9ulny8X%c5$~j$!o(+EVSxYpJH7%Mp89c~3 zI@uK^f4VrJ5m}F?&&;U};_k`zlpuf8}`f z8fxMYn?x(1zsj{ofTJ5qed>ha&ObDkP`WQngnSvMZIazir-7HtPBRV0de@T-Sk+Bd z=(+Kn!0+{SJL^8bi3k<4;$BM&#@2E7*5a{hkapRj${>f5)Q$T~Ut1>j_lW@FO|V&) z)@K!TG7IqO+7XO0#JyBS8%=-~s`Ys|^-PkJyg%9}@ZE=}fj)Y>#0aT9^YJy+$ zB&Tf@ugd(8;@DQq4&=#|m?GQ#VCAe|3)Ua$dn$_AKT-cGcCs6^)8rL;slv%&{Z2n< zWu->N;Z2y+Xua_3{fQ4WPEHZSC^YR>UryL5-+p6px)ys|6h-%?2`6#6doJ0~zDWjp zLdU!|tBiHabykMGaApTRF?cS?ETC)xBO>^)DxxP$I_h-yd(NtuB^nM$GR z{Qj?)8AhN~FApTPt!RV&#UuGq#Uf;d<^7_&z!|S8#r8qkXJ1^nHR#1WrBH7@YhONp zn2a~|B$I5Pm9?JTOlXQ?O~xX7@&Bta*u^7TpQe0cZF!u)E`uG5TYjK_1AHSB>G{wL=s>VAdWe5 zHCVB($$Gj$7BIK^GD<&KwJWU;jN4X4&z?O(S_}93g5Ex{vLbq=h`;L;^@6ZjrLk6_ zT&_BD!@-Pb3;y7QSGYlEuJ%6BWIv9e{i?(i+wtdrr8;?K%g{!V{pNOugopp;7MJBc z-`e_3h6mNWb~b~yF%#XaTmtTJ{O~zmk{c_0$mk+c)N4rCbPsP*`Faze+-Pp<_NSed zSGG12;Nq?HxQm0ViJGT`UBJ=+5rlA#BnMrKDc4Sr)(;iuc7z(bPzw-307Txcr{3S!P66!m@I`7g^5uoyAk%elp z82&CH2(j}lf7?#QF{;xV^5lx&D;uaoI*+HQ$m7zXyav`Us$%|3SxoZ{ti<0zAg03K zv_wtvfoT+3c$p<^dxih&WoCa*&P>tfC?w%gG$e~nFbexeT1CaX)v19w7F^_D`h?=R z(kDz*=FQrdZyzm16=&MJ?GQ96RAeMHfZS(h+%L86X}ik>(0BN#LFPINsE*b?5gcy*_K`na{yTA%E}4yv zcwgkL@d#MCR|qKbed-<{p=jafr}QNRr@Fu8QQo$_HmHqyS~@#qWXwB)nx9Ii*JG)H zoxj~xk--%9qt!n>%*TZ*v}(xK;>}wog7XruBcGL$*a^CK85Z_B%<)t>D81<(`R5vj zaH0GulQZpI&H=(>qB>T)`W1!iV|+atalaqyxODSOgoOw`dtv>{x)i&^le#im`|Rwz z{a3;iI1AcWkp>`TT7&Qg*ndh&4d5@Yg8_looTx)W3N?V~*aeaqrPf3}Zq(}okYR<> zA{s_6cl0#*&DU^Uwf(=BH(ysD>4zHDn3vyP4rzvywW3I$+7zth@XfPKD?=(Oky)Kz zy>uMu<|7%^Cw3bZD*la9`QDXmdZq)Ya@ZUFsT%?3o;$jIse$g6*({_cYG7P{&=CD3;5un( zS+e-AkFK&!wI;aOJ|pL7 z2oFmt-%df!y?bx8hBdA7jnH#^ z>ac61#pMaqT(?TmRMu?dqR=W}?PSE@vidH(qBrSR1meW5F$6&lCn5%KgVko^hLjET zrpUzL+}!;CFrHKLYId*{S$5o}6=f8hQR#qFCV=(cG$J*40$~tRy!1p_V z9|S7yf%; zjBk6PVvC?{lil$rjNN%?lwUPFf@+~I9s4KpqrlHhq-uUAI^n7V%IEi)Ps^*4<5pb6$HJj`C%F@!t2?Lat8s$~UuG3>c8FJ5jQDLFL&0j<bLPZi2!5~;ylT( z6_DSsv~=un)>dRuI72BL=7Cu_SLsh~N~}jnql1+E`OzD{8~?8T)}uv`xH5v)NJO)< zd`&j!bqkQ3sm}m$fE|_WvWrd|6{?`7z8@W#qr;AiXTjjsi&xfHan~ zf~+*SsjRW}j6L^UpM!n+guN3JP8F5fgJmIIx5)&Uvt5(b6`)>&J1~z$zUe>bv7$CF zk?mxEamZVRn$T8Y@Q(yBA}$@%rFxnx?i&l0BdiaMg`|*$E%H!RpX=Z4ls1CzY>l(cnVPc?=T=jLP$61X)- z_<~{qW>Ck1h4mBpRXbw9p+nTRV=1ri^eHfWaxLCo$(2`i-?6?iGashKfw=c*Yn21Z zf|HYz;iA-5G)-C{%Eu48yy7dhX)>==WCGjfSC?>L+zZ_VE(No)_s$ov;`Tlpqy*?D zftM;Yt6%yh&A)J+v9oglWl%)RXg;b=MnBwNobIM5P8b#_mt?a2xt@Fvc96@x_nAVk z$ke1!D4vqC?hlCj{+@)VFBD1>9V=&NKKo%8D-btPHYEoi? z8el*XX)rkUKqC7H|Ls}To}5?VY+w~*oAp4WnV)kZhesqS=KPq#H;FMSs3Fg2dd;It z-50DKU#EKC0$PK_1%SU?!2L4h2(S28s8`h;RHTZQ%hOdb^^OZeRBj?ZelyFRD0jv$ z%uslRxM{t+c2%-;yLFjfF-*vbHTP1rVx;nN7!({rS?N7c21+Ja1BZgzJc~6%zjHkW z8fhL6)&v;?LW^hbTpt-#I?b@_OOB}9V9vHR!SX0@;28+OE9Yk+;B?5ooW>y4UBZzB z&6`)tek1Vw87~+^TS(!Q&Xpd6DpxJS5sH#2A|uRcRw97ctzyUN>qkNp?=Bh$^F#N1 zQR9#Zou^Lw<~^#O-y8_gIcGv8Lz)xqs7S?Ib$QI$qu38KBxm@<6j2LoQhs@a%5|p8 zWYe@xs@0}L)zSZ>pXwQ0f`N*<-ghLj5=%7_#r~M>6j*=FS-LoI&85si_XFU0B5@(! zVvKVfQ(RHrJtB<}i&j&bw;bpgQ~h#kz1G9c?gqwNZx$`j(hyU!2_&TtvV@u~vFTsd zSuys9iw4u^R{DPt&gDIkyg_OH?zD*E{02T>5oeX!W8WIjQ|J-#JVumed3_@o>#$bbDqps@;EPWyZZ}+$Hei zK~vBTuMgkBUzVffcKLm%9e&+am?4g(!Arob?3y`o6GsFl)Q4NEW0Y+e%$|!=bsGsk zcNs<8JCC)2dQHTNnSHv2ht6quU*cf~AP-AfGcfKwa^+WS&?%Od`AcC^Wa>Q@S0K!v zF5L6uD3(R?Rk6Pvn?N=e>v^VK6D9NBUYmZ*XleVuq`k@GGqFFJ*1$k-Zgrf}w6+5< z4$~SI#ht|(^#;vh+W1P(;Y_cr=U1$KV;-Qs5jAR*K4lICm@xup6ldC3QnIj29d++p{(6`EsJq-T}>lzHjlT96qnNI!hNI8U(%6Y zwE;4e?I!)_78O46;cROFu>Kpld<3IhI+@7~k>H?}(vES}5Kg2q=)*Z-avI zE@Ox(l0d}~GQYLUvLrpSip5Q|60MWY0tU3@&Mt3-hT zjW_u9F%j@oa!@M~WpSH?dZNINx%L}q36hG^!c~DMuhC5>2tBX#$@y(xh_ZKO2Jh8PN0$*VD94Cpv%S!74Xa&yin)dULQ$V{%pA*&I$G&V9y1uL{f({L$!-Tw z42eStMRuI{#7#JJK@a(cqV|VtKE#_*_vmiw*Oxqr=8_Z%uHx}YV}e&%Atyn4nMtA< z-a8Ue|F!bh<+n0Urg#u>T`mA#k0ZIg!pFmT7SoTepsK`>p_B30BRT=FYPa%KKqGQO zwf4r|_$Yew!dqZhQv2BZCfy*c69TQ_;-QO0Yxb_j4TA9R~Ohhnx_V^hy5 zmllj4XN5P)U+&N$?=0V_>9{TsqfLd_y&(MBxS%Xxd!aLNUQ&33C{|`G`MX9#_USWk zwBx;?<0@)G{ZerErURiW(ha){c(VpE&@1uHIO0exCG z=_uIdD1eJ#!2EZ^wfOz)+^TDb-R*<_HL8$G11M4v9^amrducr?8U`0B|4yTG!zOLg zTkkJ~70B6puckw;k%l9qzigNJbThS5yUydND}AAGNb->=KI3fnZ>zoQ@6ETqnmZfea(2~G{C`xXb2@Dsz@jk=*usQPwZFRGG*0zr&uvX&^&?#MGKoy>v zSdi>LR3ivOJpoYl0I#FYi&*H2sGUGT1(uubIa}2GYmH54F{=A4-`0puyKI0uZt1G1 z&9ArDdShX`^f{n#`&F^5ES?VKl5#%F{+c61Nkh27nU$83hATrB+~;-t8YeNk0JK!p z23hLLWG;C}41s9VmsasTYD|0wCJNw6 zmVn=Tb;dM?=WT|d{SN8^V05Du=F}rR!9Q$LePisZ&Nr6+=_~dnf6J^#AZM-Um%2vP zWU7kTFq>niRMXSq=JB-ZtA^HBR55$JEhm_|T*7ySYl$1Po5B-QUVGVg*KY}Hva-e+ zbIv^H0AJjGmfcT-liqEd9W7Y1`_({*KQ(@G(|*x5>xUSiK>h<)3c11&bR3}LOb8SF zGrzpIlF;*sasqhw7D!ZiiK^LbYp++#ZfU~=>z`Z0PE@E`jgV*Mxv>DpmR;uai^Mz= zNXI%M!>a=u@U`wI-r&{^HCFewGD>awC-`M6&tOR} zHS(b?ghVle73Jx`HQ|H?wU$K9(y|I@upxif)2Q2^nDXsp*3qt43Vs9>i?sBVy6{?MsSn1XQd)|-497tDeRupxlYtr?u+$<@|dwD@9GSKmU9 zh)DYIi+d~D?SWlrO1~x=I@X)YTD86C#j(_uPc)Ytw?%Dgjxo-99C5Yk*+y%MSybCH zfRhA;ZGAU~D0yDGTM4{#EL&clU<{RNxHnG^HSyT*_m0&7k;#ZmjNwlO%8lZah{da7^rbRY#Zt*rOps8d|aa;|kP+~GOZOAeGo80+8l~}Wg zg91qAFv2TP9a3Tbb-lB>$# zd$7ewS&#y#{*r6(d!$KGsOCQ_5 zeYl+&;_!VuQTk1b-RGw3S%dWN##AfmMHau*5$Tb@DxYgp* znnvKAGcN0pET-)L| zi~@o_Q)6~yg_gD-T(tK&FI34*4DI<|*c*Wdj_kL|5fwFmbKljhF0+cTilhkuPXw=_ zC;)cdYg*C_?5hAZ^0nuS&l0g^xw*+BxN@C8mqMpOP~PssuuaU@B~QZMzMbXdzEatm z<>y+A>G|Tw+Adm%y*)q&8Z?F6TRNd%lg!F%aGLG*-o^px^_X9t-QEi(8StC0S$}&FB80$dgK=^FibZB`;;vO@ z`o5=6nALfNE|uu>qW`yC_Fr!dJxWEi^{=iqi}AQg)EgwCFyT+&jZR(77byvUia&(5 z`$3$dBM00~1{~1NqRKqWNVU3&Q}6L2p!b + ovqt_plugin_bnp_manager + BNPManager + 0.1 + Krolock + Edit BNP Files + + + + \ No newline at end of file From ba66f5e07047e2d8eb85409da3d85ff146109cef Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 11 Jan 2012 20:54:54 +0100 Subject: [PATCH 2/8] Added: Implemented bnp_manager_plugin add and delete files --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 194 +++++++++++++++++- .../src/plugins/bnp_manager/bnp_file.h | 46 ++++- .../bnp_manager/bnp_manager_window.cpp | 76 ++++++- .../plugins/bnp_manager/bnp_manager_window.h | 9 +- 4 files changed, 306 insertions(+), 19 deletions(-) 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 */ From 22ff1f79dac876393a0f3becb488529d3957ce5e Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 11 Jan 2012 23:52:18 +0100 Subject: [PATCH 3/8] Changed: bnp_manager_plugin file handles to CIFile/COFile to use serial system --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 139 +++++------------- 1 file changed, 37 insertions(+), 102 deletions(-) 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 370b6e3a9..7c404b347 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 @@ -64,9 +64,8 @@ void BNPFileHandle::releaseInstance() // *************************************************************************** bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { - FILE *bnp = fopen (m_openedBNPFile.c_str(), "rb"); - FILE *out; - if (bnp == NULL) + CIFile bnp; + if ( !bnp.open(m_openedBNPFile) ) return false; TPackedFilesList::iterator it_files = m_packedFiles.begin(); @@ -78,27 +77,17 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList { string filename = dirName + "/" + it_files->m_name; - out = fopen (filename.c_str(), "wb"); - if (out != NULL) + COFile out; + if ( out.open(filename) ) { - nlfseek64 (bnp, it_files->m_pos, SEEK_SET); + bnp.seek(it_files->m_pos, IStream::begin); uint8 *ptr = new uint8[it_files->m_size]; - if (fread (ptr, it_files->m_size, 1, bnp) != 1) - { - nlwarning("%s read error", filename.c_str()); - return false; - } - if (fwrite (ptr, it_files->m_size, 1, out) != 1) - { - nlwarning("%s write error", filename.c_str()); - return false; - } - fclose (out); + bnp.serialBuffer(ptr,it_files->m_size); + out.serialBuffer(ptr,it_files->m_size); delete [] ptr; } } } - fclose (bnp); return true; } // *************************************************************************** @@ -109,42 +98,32 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_openedBNPFile = filePath; - FILE *f = fopen (filePath.c_str(), "rb"); - if (f == NULL) + CIFile bnp; + if ( !bnp.open (filePath) ) { nlwarning("Could not open file!"); return false; } - nlfseek64 (f, 0, SEEK_END); + bnp.seek(0, IStream::end); uint32 nFileSize=CFile::getFileSize (filePath ); - nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); + bnp.seek(nFileSize-sizeof(uint32), IStream::begin); uint32 nOffsetFromBegining; - if (fread (&nOffsetFromBegining, sizeof(uint32), 1, f) != 1) - { - fclose (f); - return false; - } + bnp.serial(nOffsetFromBegining); #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nOffsetFromBegining); #endif - if (nlfseek64 (f, nOffsetFromBegining, SEEK_SET) != 0) + if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { nlwarning("Could not read offset from begining"); - fclose (f); return false; } uint32 nNbFile; - if (fread (&nNbFile, sizeof(uint32), 1, f) != 1) - { - nlwarning("Could not read number of files!"); - fclose (f); - return false; - } + bnp.serial(nNbFile); #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nNbFile); @@ -153,44 +132,28 @@ bool BNPFileHandle::readHeader(const std::string &filePath) for (uint32 i = 0; i < nNbFile; ++i) { uint8 nStringSize; + uint32 fileSize; + uint32 filePos; char sName[256]; - if (fread (&nStringSize, 1, 1, f) != 1) - { - nlwarning("Error reading packed filename!"); - fclose (f); - return false; - } - if (fread (sName, 1, nStringSize, f) != nStringSize) - { - fclose (f); - return false; - } + bnp.serial(nStringSize); + bnp.serialBuffer( (uint8*)sName, nStringSize); 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!"); - fclose (f); - return false; - } + + bnp.serial(fileSize); + tmpPackedFile.m_size = fileSize; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(tmpBNPFile.Size); #endif - if (fread (&tmpPackedFile.m_pos, sizeof(uint32), 1, f) != 1) - { - nlwarning("Error reading packed file position!"); - fclose (f); - return false; - } + bnp.serial(filePos); + tmpPackedFile.m_pos = filePos; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(tmpBNPFile.Pos); #endif m_packedFiles.push_back (tmpPackedFile); } - - fclose (f); return true; } // *************************************************************************** @@ -211,52 +174,24 @@ void BNPFileHandle::list(TPackedFilesList& FileList) // *************************************************************************** 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; - } + COFile bnp; + if ( !bnp.open(filePath, true) ) 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; - } + uint32 nNbFile = (uint32)m_packedFiles.size(); + bnp.serial(nNbFile); - if (fwrite (&m_packedFiles[i].m_pos, 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(); + bnp.serial( nStringSize ); + bnp.serialBuffer( (uint8*)m_packedFiles[i].m_name.c_str(), nStringSize ); + bnp.serial(m_packedFiles[i].m_size); + bnp.serial(m_packedFiles[i].m_pos); + } - if (fwrite (&offset, sizeof(uint32), 1, f) != 1) - { - fclose(f); - return false; - } + bnp.serial(offset); - fclose (f); - return true; + return true; } // *************************************************************************** void BNPFileHandle::fileNames(std::vector &fileNames) From 215eb46cbcfd57b896ffcc4f5d3d0ec012eba115 Mon Sep 17 00:00:00 2001 From: Krolock Date: Thu, 12 Jan 2012 00:00:09 +0100 Subject: [PATCH 4/8] Changed: BIG_ENDIAN check is no longer needed --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) 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 7c404b347..cce2a022a 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 @@ -65,8 +65,7 @@ void BNPFileHandle::releaseInstance() bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { CIFile bnp; - if ( !bnp.open(m_openedBNPFile) ) - return false; + bnp.open(m_openedBNPFile); TPackedFilesList::iterator it_files = m_packedFiles.begin(); @@ -99,11 +98,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_openedBNPFile = filePath; CIFile bnp; - if ( !bnp.open (filePath) ) - { - nlwarning("Could not open file!"); - return false; - } + bnp.open (filePath); bnp.seek(0, IStream::end); uint32 nFileSize=CFile::getFileSize (filePath ); @@ -112,9 +107,6 @@ bool BNPFileHandle::readHeader(const std::string &filePath) uint32 nOffsetFromBegining; bnp.serial(nOffsetFromBegining); -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(nOffsetFromBegining); -#endif if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { @@ -125,33 +117,22 @@ bool BNPFileHandle::readHeader(const std::string &filePath) uint32 nNbFile; bnp.serial(nNbFile); -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(nNbFile); -#endif - for (uint32 i = 0; i < nNbFile; ++i) { uint8 nStringSize; - uint32 fileSize; - uint32 filePos; char sName[256]; + bnp.serial(nStringSize); bnp.serialBuffer( (uint8*)sName, nStringSize); sName[nStringSize] = 0; + PackedFile tmpPackedFile; tmpPackedFile.m_name = sName; tmpPackedFile.m_path = m_openedBNPFile; - bnp.serial(fileSize); - tmpPackedFile.m_size = fileSize; -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(tmpBNPFile.Size); -#endif - bnp.serial(filePos); - tmpPackedFile.m_pos = filePos; -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(tmpBNPFile.Pos); -#endif + bnp.serial(tmpPackedFile.m_size); + bnp.serial(tmpPackedFile.m_pos); + m_packedFiles.push_back (tmpPackedFile); } return true; From 2719c61efc6326f57a994f3b9f22d609bed4aae1 Mon Sep 17 00:00:00 2001 From: Krolock Date: Thu, 12 Jan 2012 00:18:09 +0100 Subject: [PATCH 5/8] Changed: Finished serial system --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) 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 cce2a022a..d9b2f45c1 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 @@ -85,8 +85,11 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList out.serialBuffer(ptr,it_files->m_size); delete [] ptr; } + out.close(); } } + + bnp.close(); return true; } // *************************************************************************** @@ -101,7 +104,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) bnp.open (filePath); bnp.seek(0, IStream::end); - uint32 nFileSize=CFile::getFileSize (filePath ); + uint32 nFileSize = bnp.getFileSize(); bnp.seek(nFileSize-sizeof(uint32), IStream::begin); uint32 nOffsetFromBegining; @@ -111,6 +114,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { nlwarning("Could not read offset from begining"); + bnp.close(); return false; } @@ -135,6 +139,8 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_packedFiles.push_back (tmpPackedFile); } + + bnp.close(); return true; } // *************************************************************************** @@ -156,7 +162,9 @@ void BNPFileHandle::list(TPackedFilesList& FileList) bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) { COFile bnp; - if ( !bnp.open(filePath, true) ) return false; + bnp.open(filePath, true); + if ( !bnp.isOpen() ) + return false; uint32 nNbFile = (uint32)m_packedFiles.size(); bnp.serial(nNbFile); @@ -172,6 +180,8 @@ bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) bnp.serial(offset); + bnp.close(); + return true; } // *************************************************************************** @@ -255,7 +265,6 @@ void BNPFileHandle::deleteFiles( const vector& fileNames) it_packed++; } } - nldebug("Writing header..."); writeHeader(tmpFile, OffsetFromBegining); @@ -270,10 +279,12 @@ void BNPFileHandle::append(const string &destination, const PackedFile &source) 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; } + COFile bnpfile; + CIFile packedfile; + bnpfile.open(destination, true); + packedfile.open(source.m_path); + if ( !bnpfile.isOpen() ) return; + uint8 *ptr = new uint8[source.m_size]; @@ -281,20 +292,18 @@ void BNPFileHandle::append(const string &destination, const PackedFile &source) if ( nlstricmp( CFile::getExtension(source.m_path), "bnp" ) == 0 ) { // Jump to the file position inside the bnp - nlfseek64(packedfile, source.m_pos, SEEK_SET); + packedfile.seek(source.m_pos, IStream::begin); } // Read the source - if (fread (ptr, source.m_size, 1, packedfile) != 1) - nlwarning("%s read error", source.m_path.c_str()); + packedfile.serialBuffer(ptr, source.m_size); // Append the data to the destination - if (fwrite (ptr, source.m_size, 1, bnpfile) != 1) - nlwarning("%s write error", destination.c_str()); + bnpfile.serialBuffer(ptr, source.m_size); delete [] ptr; - fclose(packedfile); - fclose(bnpfile); + packedfile.close(); + bnpfile.close(); } // *************************************************************************** bool BNPFileHandle::compare(const PackedFile &left, const PackedFile &right) From a178f13d618b75dfdc44892b08349b885654399e Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 17:45:47 +0100 Subject: [PATCH 6/8] Added: Sortproxymodel in order to sort DirTreeView (folders on top) --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/CMakeLists.txt | 1 + .../plugins/bnp_manager/bnp_dirtree_dialog.cpp | 18 ++++++++++++------ .../plugins/bnp_manager/bnp_dirtree_dialog.h | 5 ++++- .../bnp_manager/bnp_filesystem_model.cpp | 1 - .../plugins/bnp_manager/bnp_manager_window.cpp | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt index e06c82c94..7ecfd7396 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt @@ -14,6 +14,7 @@ SET(OVQT_PLUG_BNP_MANAGER_HDR bnp_manager_plugin.h bnp_filesystem_model.h bnp_file.h bnp_filelist_dialog.h + bnp_proxy_model.h ) SET(OVQT_PLUG_BNP_MANAGER_UIS bnp_dirtree_form.ui bnp_filelist_dialog.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp index 9487f66ae..78cc2f3cd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp @@ -17,6 +17,7 @@ // Project includes #include "bnp_dirtree_dialog.h" #include "bnp_filesystem_model.h" +#include "bnp_proxy_model.h" // Qt includes #include @@ -39,18 +40,22 @@ CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) // Bnp file: opened and displayed // all other files: added to the currently opened bnp file QStringList filter; - filter << tr("*.bnp"); + //filter << tr("*.bnp"); // Setup the directory tree model - m_dirModel= new BNPFileSystemModel; + m_dirModel= new BNPFileSystemModel(); + m_proxyModel = new BNPSortProxyModel(); m_dirModel->setRootPath(m_DataPath); m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::AllEntries); m_dirModel->setNameFilters(filter); m_dirModel->setNameFilterDisables(0); - m_ui.dirTree->setModel(m_dirModel); + m_proxyModel->setSourceModel(m_dirModel); - m_ui.dirTree->setRootIndex(m_dirModel->index(m_DataPath)); + m_ui.dirTree->setModel(m_proxyModel); + + m_ui.dirTree->setRootIndex( m_proxyModel->mapFromSource (m_dirModel->index(m_DataPath) ) ); + m_ui.dirTree->setSortingEnabled(true); // Trigger if one filename is activated // In future drag&drop should be also possible @@ -65,10 +70,11 @@ CBnpDirTreeDialog::~CBnpDirTreeDialog() // *************************************************************************** void CBnpDirTreeDialog::fileSelected(QModelIndex index) { - if (index.isValid() && !m_dirModel->isDir(index)) + QModelIndex source = m_proxyModel->mapToSource(index); + if (source.isValid() && !m_dirModel->isDir(source)) { // emit the according signal to BNPManagerWindow class - Q_EMIT selectedForm(m_dirModel->fileInfo(index).filePath()); + Q_EMIT selectedFile(m_dirModel->fileInfo(source).filePath()); } } // *************************************************************************** diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h index 33b221a4d..737085185 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h @@ -31,6 +31,7 @@ namespace BNPManager { class BNPFileSystemModel; +class BNPSortProxyModel; class CBnpDirTreeDialog : public QDockWidget { @@ -63,8 +64,10 @@ private: BNPFileSystemModel *m_dirModel; + BNPSortProxyModel *m_proxyModel; + Q_SIGNALS: - void selectedForm(const QString); + void selectedFile(const QString); private Q_SLOTS: /** diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp index eaf6389f5..75ef031ff 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp @@ -25,7 +25,6 @@ namespace BNPManager BNPFileSystemModel::BNPFileSystemModel(QObject *parent) : QFileSystemModel(parent) { - } // *************************************************************************** BNPFileSystemModel::~BNPFileSystemModel() 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 33157733e..4022931d1 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 @@ -67,7 +67,7 @@ BNPManagerWindow::BNPManagerWindow(QWidget *parent) // this SLOT is triggered if the user activates a bnp files in the // dirtree view - connect(m_BnpDirTreeDialog, SIGNAL(selectedForm(const QString)), + connect(m_BnpDirTreeDialog, SIGNAL(selectedFile(const QString)), this, SLOT(loadFile(const QString))); // not used From 23c6ff964ec2c929fd47f90b06aeb65a996361b7 Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 17:47:26 +0100 Subject: [PATCH 7/8] Added: Sortproxymodel class --HG-- branch : branch-bnp-manager-plugin --- .../plugins/bnp_manager/bnp_proxy_model.cpp | 56 +++++++++++++++++++ .../src/plugins/bnp_manager/bnp_proxy_model.h | 44 +++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp new file mode 100644 index 000000000..d3657d13b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp @@ -0,0 +1,56 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 + +// NeL includes +#include + +// project includes +#include "bnp_proxy_model.h" + +namespace BNPManager +{ + +bool BNPSortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if ( sourceModel()->hasChildren(left) ) + { + if ( !sourceModel()->hasChildren(right) ) + { + return true; + } + else + { + QString leftString = sourceModel()->data( left ).toString(); + QString rightString = sourceModel()->data( right ).toString(); + return QString::localeAwareCompare(leftString, rightString) < 0; + } + } + else + { + if ( sourceModel()->hasChildren(right) ) + return false; + else + { + QString leftString = sourceModel()->data( left ).toString(); + QString rightString = sourceModel()->data( right ).toString(); + return QString::localeAwareCompare(leftString, rightString) < 0; + } + } +} + +} /* namespace Plugin */ + +/* end of file */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h new file mode 100644 index 000000000..ed2da5966 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h @@ -0,0 +1,44 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// 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 + +#ifndef BNP_PROXY_MODEL_H +#define BNP_PROXY_MODEL_H + +// Qt includes +#include + +namespace BNPManager +{ + + class BNPSortProxyModel : public QSortFilterProxyModel + { + + public: + BNPSortProxyModel(QObject *parent = 0): QSortFilterProxyModel(parent) + { + } + ~BNPSortProxyModel() + { + } + + protected: + virtual bool lessThan ( const QModelIndex & left, const QModelIndex & right ) const; + + };/* class BNPSortProxyModel */ + +} // BNPManager + +#endif // BNP_PROXY_MODEL_H From 0d607ba7931b780d28e7384187e6175b8bd9544d Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 18:00:05 +0100 Subject: [PATCH 8/8] Added: Drop handling from outside into FileListDialog --HG-- branch : branch-bnp-manager-plugin --- .../bnp_manager/bnp_filelist_dialog.cpp | 27 +++++++++++++++++-- .../plugins/bnp_manager/bnp_filelist_dialog.h | 6 +++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp index b78e6c0bf..418d7fa04 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp @@ -20,6 +20,10 @@ // Qt includes #include +#include +#include +#include +#include // NeL includes #include @@ -34,6 +38,7 @@ BnpFileListDialog::BnpFileListDialog(QString bnpPath, QWidget *parent) m_DataPath(bnpPath) { m_ui.setupUi(this); + setAcceptDrops(true); } // *************************************************************************** BnpFileListDialog::~BnpFileListDialog() @@ -70,7 +75,7 @@ void BnpFileListDialog::setupTable(int nbrows) m_ui.tableWidget->setObjectName("tablewidget"); } // *************************************************************************** -bool BnpFileListDialog::loadTable(const QString fileName) +bool BnpFileListDialog::loadTable(const QString filePath) { // reference to the BNPFileHandle singletone instance BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); @@ -79,7 +84,7 @@ bool BnpFileListDialog::loadTable(const QString fileName) int row = 0; // read the header from the bnp file - if (!myBNPFileHandle.readHeader( fileName.toStdString()) ) + if (!myBNPFileHandle.readHeader( filePath.toStdString()) ) { return false; } @@ -100,6 +105,9 @@ bool BnpFileListDialog::loadTable(const QString fileName) row++; } + // Set the file path as the widgets title + setWindowTitle(filePath); + return true; } // *************************************************************************** @@ -118,5 +126,20 @@ void BnpFileListDialog::getSelections(TSelectionList& SelectionList) } } // *************************************************************************** +void BnpFileListDialog::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only one file + // In the future a tabbed FileListDialog would accept more + if ( event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) + event->acceptProposedAction(); +} +// *************************************************************************** +void BnpFileListDialog::dropEvent(QDropEvent *event) + { + // Excraft the local file url from the drop object and fill the table + const QMimeData *mimeData = event->mimeData(); + QList urlList = mimeData->urls(); + loadTable( urlList.first().toLocalFile() ); + } } // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h index f2cc021a9..5b5491d8f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h @@ -51,7 +51,7 @@ public: * \param Filename * \return true if everything went well */ - bool loadTable(const QString filename); + bool loadTable(const QString filePath); /** * Set the dimension of the table @@ -67,8 +67,10 @@ public: */ void getSelections(TSelectionList& SelectionList); +protected: + void dragEnterEvent (QDragEnterEvent *event); + void dropEvent(QDropEvent *event); private: - Ui::BnpFileListDialog m_ui; // common data path as root folder for the dirtree view