diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 3c9476d7a2..20c01af259 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -23,8 +23,7 @@ struct ESMData std::string author; std::string description; int version; - int type; - ESM::ESMReader::MasterList masters; + std::vector masters; std::deque mRecords; std::map > mCellRefs; @@ -284,16 +283,13 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); info.data.masters = esm.getMasters(); - info.data.version = esm.getVer(); - info.data.type = esm.getType(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl - << "File format version: " << esm.getFVer() << std::endl - << "Special flag: " << esm.getSpecial() << std::endl; - ESM::ESMReader::MasterList m = esm.getMasters(); + << "File format version: " << esm.getFVer() << std::endl; + std::vector m = esm.getMasters(); if (!m.empty()) { std::cout << "Masters:" << std::endl; @@ -430,9 +426,9 @@ int clone(Arguments& info) esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); - esm.setType(info.data.type); + esm.setRecordCount (recordCount); - for (ESM::ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) + for (std::vector::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) esm.addMaster(it->name, it->size); std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 0071dd1fc7..488e3c65f5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,4 +1,3 @@ - set (OPENCS_SRC main.cpp) opencs_units (. editor) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5966d089e3..8dc5366a98 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -113,5 +113,7 @@ int CS::Editor::run() { mStartup.show(); + QApplication::setQuitOnLastWindowClosed (true); + return QApplication::exec(); } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 11d877d0b1..7a88335c89 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,7 +2,7 @@ #include "document.hpp" #include - +#include void CSMDoc::Document::load (const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool lastAsModified) { @@ -237,6 +237,9 @@ CSMDoc::Document::Document (const std::vector& files, b connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } +CSMDoc::Document::~Document() +{} + QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; @@ -290,11 +293,13 @@ void CSMDoc::Document::abortOperation (int type) } } + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } + void CSMDoc::Document::operationDone (int type) { emit stateChanged (getState(), this); @@ -308,9 +313,12 @@ void CSMDoc::Document::saving() if (mSaveCount>15) { + //clear the stack before resetting the save state + //to avoid emitting incorrect states + mUndoStack.setClean(); + mSaveCount = 0; mSaveTimer.stop(); - mUndoStack.setClean(); emit stateChanged (getState(), this); } } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index a7b198689e..94d5fe85cc 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -63,6 +63,7 @@ namespace CSMDoc public: Document (const std::vector& files, bool new_); + ~Document(); QUndoStack& getUndoStack(); @@ -105,4 +106,4 @@ namespace CSMDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index ce001afaa9..7ee4b87260 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -56,7 +56,7 @@ void CSVDoc::Operations::quitOperation (int type) mLayout->removeItem ((*iter)->getLayout()); - delete *iter; + (*iter)->deleteLater(); mOperations.erase (iter); if (oldCount > 1) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 286d7a6ed6..995d3ca2e2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../../model/doc/document.hpp" @@ -39,6 +40,16 @@ void CSVDoc::View::setupFileMenu() mSave = new QAction (tr ("&Save"), this); connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); + + QAction *close = new QAction (tr ("&Close"), this); + connect (close, SIGNAL (triggered()), this, SLOT (close())); + file->addAction(close); + + QAction *exit = new QAction (tr ("&Exit"), this); + connect (exit, SIGNAL (triggered()), this, SLOT (exit())); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); + + file->addAction(exit); } void CSVDoc::View::setupEditMenu() @@ -117,15 +128,15 @@ void CSVDoc::View::updateActions() mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); } -CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent) - : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), QMainWindow (viewParent) +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) + : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), + mViewTotal (totalViews) { - setDockOptions (QMainWindow::AllowNestedDocks); - resize (300, 300); /// \todo get default size from settings and set reasonable minimal size - mSubViewWindow = new QMainWindow(); - setCentralWidget (mSubViewWindow); + mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); + + setCentralWidget (&mSubViewWindow); mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -200,7 +211,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); - mSubViewWindow->addDockWidget (Qt::TopDockWidgetArea, view); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, SLOT (addSubView (const CSMWorld::UniversalId&))); @@ -239,7 +250,12 @@ void CSVDoc::View::abortOperation (int type) updateActions(); } -QDockWidget *CSVDoc::View::getOperations() const +CSVDoc::Operations *CSVDoc::View::getOperations() const { return mOperations; } + +void CSVDoc::View::exit() +{ + emit exitApplicationRequest (this); +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 28ab24b744..e91a4d4a80 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -41,7 +41,8 @@ namespace CSVDoc std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; - QMainWindow* mSubViewWindow; + QMainWindow mSubViewWindow; + // not implemented View (const View&); @@ -65,9 +66,12 @@ namespace CSVDoc void updateActions(); + void exitApplication(); + public: - View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent); + View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); + ///< The ownership of \a document is not transferred to *this. virtual ~View(); @@ -82,7 +86,7 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); - QDockWidget *getOperations() const; + Operations *getOperations() const; signals: @@ -90,23 +94,28 @@ namespace CSVDoc void loadDocumentRequest(); + void exitApplicationRequest (CSVDoc::View *view); + public slots: void addSubView (const CSMWorld::UniversalId& id); + void abortOperation (int type); + private slots: void newView(); void save(); + void exit(); + void verify(); void addGlobalsSubView(); void addGmstsSubView(); - void abortOperation (int type); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 718b807281..33300f67a7 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -12,6 +12,11 @@ #include "view.hpp" +#include +#include +#include +#include + void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -31,7 +36,7 @@ void CSVDoc::ViewManager::updateIndices() } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) -: mDocumentManager (documentManager) + : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; @@ -40,7 +45,6 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); - } CSVDoc::ViewManager::~ViewManager() @@ -63,9 +67,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } - QMainWindow *mainWindow = new QMainWindow; + View *view = new View (*this, document, countViews (document)+1); - View *view = new View (*this, document, countViews (document)+1, mainWindow); mViews.push_back (view); @@ -94,23 +97,143 @@ bool CSVDoc::ViewManager::closeRequest (View *view) { std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); + bool continueWithClose = true; + if (iter!=mViews.end()) { bool last = countViews (view->getDocument())<=1; - /// \todo check if save is in progress -> warn user about possible data loss - /// \todo check if document has not been saved -> return false and start close dialogue - - mViews.erase (iter); - view->deleteLater(); - if (last) - mDocumentManager.removeDocument (view->getDocument()); + continueWithClose = notifySaveOnClose (view); else + { + (*iter)->deleteLater(); + mViews.erase (iter); + updateIndices(); + } } - return true; + return continueWithClose; +} + +bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view) +{ + bool result = true; + CSMDoc::Document *document = view->getDocument(); + + //notify user of saving in progress + if ( (document->getState() & CSMDoc::State_Saving) ) + result = showSaveInProgressMessageBox (view); + + //notify user of unsaved changes and process response + else if ( document->getState() & CSMDoc::State_Modified) + result = showModifiedDocumentMessageBox (view); + + return result; +} + +bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document has been modified."); + messageBox.setInformativeText ("Do you want to save your changes?"); + messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + messageBox.setDefaultButton (QMessageBox::Save); + + bool retVal = true; + + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + mUserWarned = true; + int response = messageBox.exec(); + mUserWarned = false; + + switch (response) + { + case QMessageBox::Save: + + document->save(); + mExitOnSaveStateChange = true; + retVal = false; + break; + + case QMessageBox::Discard: + + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + break; + + case QMessageBox::Cancel: + + //disconnect to prevent unintended view closures + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + break; + + default: + break; + + } + + return retVal; +} + +bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document is currently being saved."); + messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?"); + + QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole); + QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole); + QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole); + + messageBox.setDefaultButton (waitButton); + + bool retVal = true; + + //Connections shut down message box if operation ends before user makes a decision. + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + //set / clear the user warned flag to indicate whether or not the message box is currently active. + mUserWarned = true; + messageBox.exec(); + mUserWarned = false; + + //if closed by the warning handler, defaults to the RejectRole button (closeButton) + if (messageBox.clickedButton() == waitButton) + { + //save the View iterator for shutdown after the save operation ends + mExitOnSaveStateChange = true; + retVal = false; + } + + else if (messageBox.clickedButton() == closeButton) + { + //disconnect to avoid segmentation fault + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + view->abortOperation(CSMDoc::State_Saving); + mExitOnSaveStateChange = true; + } + + else if (messageBox.clickedButton() == cancelButton) + { + //abort shutdown, allow save to complete + //disconnection to prevent unintended view closures + mExitOnSaveStateChange = false; + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + } + + return retVal; } void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) @@ -126,3 +249,25 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); } + +void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document) +{ + if ( !(state & CSMDoc::State_Saving) ) + { + //if the user is being warned (message box is active), shut down the message box, + //as there is no save operation currently running + if ( mUserWarned ) + emit closeMessageBox(); + + //otherwise, the user has closed the message box before the save operation ended. + //exit the application + else if (mExitOnSaveStateChange) + QApplication::instance()->exit(); + } +} + +void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) +{ + if (notifySaveOnClose (view)) + QApplication::instance()->exit(); +} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 72e7a3e1a1..90f23eaa11 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -27,12 +27,17 @@ namespace CSVDoc CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories; + bool mExitOnSaveStateChange; + bool mUserWarned; // not implemented ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); void updateIndices(); + bool notifySaveOnClose (View *view = 0); + bool showModifiedDocumentMessageBox (View *view); + bool showSaveInProgressMessageBox (View *view); public: @@ -54,13 +59,21 @@ namespace CSVDoc void loadDocumentRequest(); + void closeMessageBox(); + + public slots: + + void exitApplication (CSVDoc::View *view); + private slots: void documentStateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); + + void onExitWarningHandler(int state, CSMDoc::Document* document); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 257676076c..09a39ff8b1 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -27,14 +27,15 @@ void ESMStore::load(ESM::ESMReader &esm) ESM::Dialogue *dialogue = 0; + /// \todo Move this to somewhere else. ESMReader? // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { - ESM::MasterData &mast = const_cast(masters[j]); + ESM::Header::MasterData &mast = const_cast(masters[j]); std::string fname = mast.name; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 38625fb529..f7b97056cb 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -39,7 +39,7 @@ add_component_dir (esm loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlocks loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat - loadweap records aipackage effectlist spelllist variant variantimp + loadweap records aipackage effectlist spelllist variant variantimp loadtes3 ) add_component_dir (misc diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 335d123378..6f51c767ec 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_COMMON_H #include +#include #include #include @@ -14,24 +15,6 @@ enum Version VER_13 = 0x3fa66666 }; -enum FileType - { - FT_ESP = 0, // Plugin - FT_ESM = 1, // Master - FT_ESS = 32 // Savegame - }; - -// Used to mark special files. The original ESM files are given -// special treatment in a few places, most noticably in loading and -// filtering out "dirtly" GMST entries correctly. -enum SpecialFile - { - SF_Other, - SF_Morrowind, - SF_Tribunal, - SF_Bloodmoon - }; - /* A structure used for holding fixed-length strings. In the case of LEN=4, it can be more efficient to match the string as a 32 bit number, therefore the struct is implemented as a union with an int. @@ -41,7 +24,7 @@ union NAME_T { char name[LEN]; int32_t val; - + bool operator==(const char *str) const { for(int i=0; i NAME; @@ -70,28 +55,6 @@ typedef NAME_T<256> NAME256; #pragma pack(push) #pragma pack(1) -/// File header data for all ES files -struct HEDRstruct -{ - /* File format version. This is actually a float, the supported - versions are 1.2 and 1.3. These correspond to: - 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 - */ - int version; - int type; // 0=esp, 1=esm, 32=ess - NAME32 author; // Author's name - NAME256 desc; // File description - int records; // Number of records? Not used. -}; - -// Defines another files (esm or esp) that this file depends upon. -struct MasterData -{ - std::string name; - uint64_t size; - int index; // Position of the parent file in the global list of loaded files -}; - // Data that is only present in save game files struct SaveData { @@ -113,7 +76,6 @@ struct ESM_Context uint32_t leftRec, leftSub; size_t leftFile; NAME recName, subName; - HEDRstruct header; // When working with multiple esX files, we will generate lists of all files that // actually contribute to a specific cell. Therefore, we need to store the index // of the file belonging to this contest. See CellStore::(list/load)refs for details. diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index b4f581d7e8..580e576d05 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -15,11 +15,16 @@ ESM_Context ESMReader::getContext() return mCtx; } -ESMReader::ESMReader(void): +ESMReader::ESMReader(): mBuffer(50*1024) { } +int ESMReader::getFormat() const +{ + return mHeader.mFormat; +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -51,18 +56,6 @@ void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name) mEsm = _esm; mCtx.filename = name; mCtx.leftFile = mEsm->size(); - - // Flag certain files for special treatment, based on the file - // name. - const char *cstr = mCtx.filename.c_str(); - if (iends(cstr, "Morrowind.esm")) - mSpf = SF_Morrowind; - else if (iends(cstr, "Tribunal.esm")) - mSpf = SF_Tribunal; - else if (iends(cstr, "Bloodmoon.esm")) - mSpf = SF_Bloodmoon; - else - mSpf = SF_Other; } void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) @@ -74,42 +67,7 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) getRecHeader(); - // Get the header - getHNT(mCtx.header, "HEDR", 300); - - // Some mods abuse the header.version field for the version of the mod instead of the version of the file format, so we can only ignore it. - - while (isNextSub("MAST")) - { - MasterData m; - m.name = getHString(); - m.size = getHNLong("DATA"); - mMasters.push_back(m); - } - - if (mCtx.header.type == FT_ESS) - { - // Savegame-related data - - // Player position etc - getHNT(mSaveData, "GMDT", 124); - - /* Image properties, five ints. Is always: - Red-mask: 0xff0000 - Blue-mask: 0x00ff00 - Green-mask: 0x0000ff - Alpha-mask: 0x000000 - Bpp: 32 - */ - getSubNameIs("SCRD"); - skipHSubSize(20); - - /* Savegame screenshot: - 128x128 pixels * 4 bytes per pixel - */ - getSubNameIs("SCRS"); - skipHSubSize(65536); - } + mHeader.load (*this); } void ESMReader::open(const std::string &file) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index ae876edf8b..f805998e43 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -12,7 +12,9 @@ #include #include + #include "esmcommon.hpp" +#include "loadtes3.hpp" namespace ESM { @@ -20,15 +22,7 @@ class ESMReader { public: - ESMReader(void); - - /************************************************************************* - * - * Public type definitions - * - *************************************************************************/ - - typedef std::vector MasterList; + ESMReader(); /************************************************************************* * @@ -36,14 +30,12 @@ public: * *************************************************************************/ - int getVer() const { return mCtx.header.version; } - float getFVer() const { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } - int getSpecial() const { return mSpf; } - int getType() const { return mCtx.header.type; } - const std::string getAuthor() const { return mCtx.header.author.toString(); } - const std::string getDesc() const { return mCtx.header.desc.toString(); } - const SaveData &getSaveData() const { return mSaveData; } - const MasterList &getMasters() const { return mMasters; } + int getVer() const { return mHeader.mData.version; } + float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } + const std::string getAuthor() const { return mHeader.mData.author.toString(); } + const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::vector &getMasters() const { return mHeader.mMaster; } + int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } @@ -263,13 +255,12 @@ private: ESM_Context mCtx; // Special file signifier (see SpecialFile enum above) - int mSpf; // Buffer for ESM strings std::vector mBuffer; - SaveData mSaveData; - MasterList mMasters; + Header mHeader; + std::vector *mGlobalReaderList; ToUTF8::Utf8Encoder* mEncoder; }; diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b9dd5b57b6..3ea6bd350a 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,6 +1,8 @@ #include "esmwriter.hpp" + +#include #include -#include +#include bool count = true; @@ -9,40 +11,40 @@ namespace ESM int ESMWriter::getVersion() { - return m_header.version; + return mHeader.mData.version; } void ESMWriter::setVersion(int ver) { - m_header.version = ver; -} - -int ESMWriter::getType() -{ - return m_header.type; -} - -void ESMWriter::setType(int type) -{ - m_header.type = type; + mHeader.mData.version = ver; } void ESMWriter::setAuthor(const std::string& auth) { - strncpy((char*)&m_header.author, auth.c_str(), 32); + mHeader.mData.author.assign (auth); } void ESMWriter::setDescription(const std::string& desc) { - strncpy((char*)&m_header.desc, desc.c_str(), 256); + mHeader.mData.desc.assign (desc); +} + +void ESMWriter::setRecordCount (int count) +{ + mHeader.mData.records = count; +} + +void ESMWriter::setFormat (int format) +{ + mHeader.mFormat = format; } void ESMWriter::addMaster(const std::string& name, uint64_t size) { - MasterData d; + Header::MasterData d; d.name = name; d.size = size; - m_masters.push_back(d); + mHeader.mMaster.push_back(d); } void ESMWriter::save(const std::string& file) @@ -58,25 +60,13 @@ void ESMWriter::save(std::ostream& file) startRecord("TES3", 0); - m_header.records = 0; - writeHNT("HEDR", m_header, 300); - m_headerPos = m_stream->tellp() - (std::streampos)4; - - for (std::list::iterator it = m_masters.begin(); it != m_masters.end(); ++it) - { - writeHNCString("MAST", it->name); - writeHNT("DATA", it->size); - } + mHeader.save (*this); endRecord("TES3"); } void ESMWriter::close() { - std::cout << "Writing amount of saved records (" << m_recordCount - 1 << ")" << std::endl; - m_stream->seekp(m_headerPos); - writeT(m_recordCount-1); - m_stream->seekp(0, std::ios::end); m_stream->flush(); if (!m_records.empty()) @@ -86,7 +76,7 @@ void ESMWriter::close() void ESMWriter::startRecord(const std::string& name, uint32_t flags) { m_recordCount++; - + writeName(name); RecordData rec; rec.name = name; @@ -109,7 +99,7 @@ void ESMWriter::startSubRecord(const std::string& name) rec.size = 0; writeT(0); // Size goes here m_records.push_back(rec); - + assert(m_records.back().size == 0); } @@ -118,7 +108,7 @@ void ESMWriter::endRecord(const std::string& name) RecordData rec = m_records.back(); assert(rec.name == name); m_records.pop_back(); - + m_stream->seekp(rec.position); count = false; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94e173b4ce..be3ae33abe 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,12 +1,13 @@ #ifndef OPENMW_ESM_WRITER_H #define OPENMW_ESM_WRITER_H -#include +#include #include -#include + +#include #include "esmcommon.hpp" -#include +#include "loadtes3.hpp" namespace ESM { @@ -22,11 +23,11 @@ class ESMWriter public: int getVersion(); void setVersion(int ver); - int getType(); - void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); void addMaster(const std::string& name, uint64_t size); @@ -80,7 +81,7 @@ public: { write((char*)&data, size); } - + void startRecord(const std::string& name, uint32_t flags); void startSubRecord(const std::string& name); void endRecord(const std::string& name); @@ -90,14 +91,13 @@ public: void write(const char* data, size_t size); private: - std::list m_masters; std::list m_records; std::ostream* m_stream; std::streampos m_headerPos; ToUTF8::Utf8Encoder* m_encoder; int m_recordCount; - HEDRstruct m_header; + Header mHeader; }; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 76a48e5ec4..da60f76af8 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -245,7 +245,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -348,7 +348,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const ESM::ESMReader::MasterList &masters = esm.getMasters(); + const std::vector &masters = esm.getMasters(); global = masters[local-1].index + 1; mref.mRefnum |= global << 24; // insert global plugin ID diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp new file mode 100644 index 0000000000..74d578ba7d --- /dev/null +++ b/components/esm/loadtes3.cpp @@ -0,0 +1,53 @@ + +#include "loadtes3.hpp" + +#include "esmcommon.hpp" +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Header::blank() +{ + mData.version = ESM::VER_13; + mData.type = 0; + mData.author.assign (""); + mData.desc.assign (""); + mData.records = 0; + mFormat = CurrentFormat; +} + +void ESM::Header::load (ESMReader &esm) +{ + esm.getHNT (mData, "HEDR", 300); + + if (esm.isNextSub ("FORM")) + { + esm.getHT (mFormat); + if (mFormat<0) + esm.fail ("invalid format code"); + } + else + mFormat = 0; + + while (esm.isNextSub ("MAST")) + { + MasterData m; + m.name = esm.getHString(); + m.size = esm.getHNLong ("DATA"); + mMaster.push_back (m); + } +} + +void ESM::Header::save (ESMWriter &esm) +{ + esm.writeHNT ("HEDR", mData, 300); + + if (mFormat>0) + esm.writeHNT ("FORM", mFormat); + + for (std::vector::iterator iter = mMaster.begin(); + iter != mMaster.end(); ++iter) + { + esm.writeHNCString ("MAST", iter->name); + esm.writeHNT ("DATA", iter->size); + } +} \ No newline at end of file diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp new file mode 100644 index 0000000000..b73a4c31e4 --- /dev/null +++ b/components/esm/loadtes3.hpp @@ -0,0 +1,55 @@ +#ifndef COMPONENT_ESM_TES3_H +#define COMPONENT_ESM_TES3_H + +#include + +#include "esmcommon.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + +#pragma pack(push) +#pragma pack(1) + + /// \brief File header record + struct Header + { + static const int CurrentFormat = 0; // most recent known format + + struct Data + { + /* File format version. This is actually a float, the supported + versions are 1.2 and 1.3. These correspond to: + 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 + */ + int version; + int type; // 0=esp, 1=esm, 32=ess (unused) + NAME32 author; // Author's name + NAME256 desc; // File description + int records; // Number of records? Not used. + }; + + // Defines another files (esm or esp) that this file depends upon. + struct MasterData + { + std::string name; + uint64_t size; + int index; // Position of the parent file in the global list of loaded files + }; + + Data mData; + int mFormat; + std::vector mMaster; + + void blank(); + + void load (ESMReader &esm); + void save (ESMWriter &esm); + }; +#pragma pack(pop) + +} + +#endif \ No newline at end of file diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index b33e2e12ab..824fbd565e 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include @@ -157,7 +157,7 @@ Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const if (!file) return Qt::NoItemFlags; - if (canBeChecked(file)) { + if (canBeChecked(file)) { if (index.column() == 0) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } else { @@ -226,7 +226,7 @@ bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2) return true; if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm")) return false; - + return e1->fileName().toLower() < e2->fileName().toLower(); } @@ -281,7 +281,7 @@ void DataFilesModel::addFiles(const QString &path) fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); - ESM::ESMReader::MasterList mlist = fileReader.getMasters(); + std::vector mlist = fileReader.getMasters(); QStringList masters; for (unsigned int i = 0; i < mlist.size(); ++i) { @@ -369,10 +369,10 @@ QStringList DataFilesModel::checkedItems() QStringList DataFilesModel::checkedItemsPaths() { QStringList list; - + QList::ConstIterator it; QList::ConstIterator itEnd = mFiles.constEnd(); - + int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { EsmFile *file = item(i); @@ -381,7 +381,7 @@ QStringList DataFilesModel::checkedItemsPaths() if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) list << file->path(); } - + return list; }