From 714b19015cb43e5a5829020730de145b15d3c7f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 Jan 2015 20:31:08 +0100 Subject: [PATCH 01/12] Fix unknown record error message --- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 54c3726bd7..87b303b6fb 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -426,7 +426,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str default: // ignore invalid records - std::cerr << "Ignoring unknown record: " << n.name << std::endl; + std::cerr << "Ignoring unknown record: " << n.toString() << std::endl; reader.skipRecord(); } int progressPercent = static_cast(float(reader.getFileOffset())/total*100); From 1869d37cfc3b1dfb786f51a7995818d3225a4f15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 04:55:42 +0100 Subject: [PATCH 02/12] Remove unused mLastDrowningHit --- apps/openmw/mwmechanics/npcstats.cpp | 3 --- apps/openmw/mwmechanics/npcstats.hpp | 2 -- components/esm/npcstats.cpp | 8 +++----- components/esm/npcstats.hpp | 1 - 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ba01caf95e..6d9388408f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -33,7 +33,6 @@ MWMechanics::NpcStats::NpcStats() , mWerewolfKills (0) , mProfit(0) , mTimeToStartDrowning(20.0) -, mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } @@ -522,7 +521,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); state.mTimeToStartDrowning = mTimeToStartDrowning; - state.mLastDrowningHit = mLastDrowningHit; } void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) @@ -573,5 +571,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mUsedIds.insert (*iter); mTimeToStartDrowning = state.mTimeToStartDrowning; - mLastDrowningHit = state.mLastDrowningHit; } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index ee897033bf..579455a756 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -46,8 +46,6 @@ namespace MWMechanics /// Countdown to getting damage while underwater float mTimeToStartDrowning; - /// time since last hit from drowning - float mLastDrowningHit; public: diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 3e6aed99dd..4ba0ce7e3f 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -75,8 +75,9 @@ void ESM::NpcStats::load (ESMReader &esm) mTimeToStartDrowning = 0; esm.getHNOT (mTimeToStartDrowning, "DRTI"); - mLastDrowningHit = 0; - esm.getHNOT (mLastDrowningHit, "DRLH"); + // No longer used + float lastDrowningHit = 0; + esm.getHNOT (lastDrowningHit, "DRLH"); // No longer used float levelHealthBonus = 0; @@ -146,9 +147,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mTimeToStartDrowning) esm.writeHNT ("DRTI", mTimeToStartDrowning); - if (mLastDrowningHit) - esm.writeHNT ("DRLH", mLastDrowningHit); - if (mCrimeId != -1) esm.writeHNT ("CRID", mCrimeId); } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 0061fc05f4..f6e9c1e588 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -45,7 +45,6 @@ namespace ESM int mSkillIncrease[8]; std::vector mUsedIds; float mTimeToStartDrowning; - float mLastDrowningHit; int mCrimeId; void load (ESMReader &esm); From b77558726a99427b62d59256db75ace243d4bef7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 14:59:42 +0100 Subject: [PATCH 03/12] Disable collision for placeable objects (Fixes #1634) --- libs/openengine/bullet/physic.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index a3ec25e22a..1ef00a0e13 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -461,7 +461,9 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - if (placeable && !raycasting && shape->mCollisionShape && shape->mAutogenerated) + // TODO: add option somewhere to enable collision for placeable meshes + + if (placeable && !raycasting && shape->mCollisionShape) return NULL; if (!shape->mCollisionShape && !raycasting) From 14aacf9a726e2b1d94b3339d1403af1ecabab13c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 17:56:19 +0100 Subject: [PATCH 04/12] Add comment --- apps/openmw/mwmechanics/npcstats.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 579455a756..4594492e10 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -108,8 +108,10 @@ namespace MWMechanics /// Called at character creation. void flagAsUsed (const std::string& id); + ///< @note Id must be lower-case bool hasBeenUsed (const std::string& id) const; + ///< @note Id must be lower-case int getBounty() const; From fc663addfa336032dab9f02a026474f91e3c80c7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 16 Jan 2015 23:10:57 +0100 Subject: [PATCH 05/12] Fix null character issue in ESMReader::getString --- components/esm/esmreader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 6facee381c..7cf0de1a99 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -299,8 +299,7 @@ std::string ESMReader::getString(int size) char *ptr = &mBuffer[0]; getExact(ptr, size); - if (size>0 && ptr[size-1]==0) - --size; + size = strnlen(ptr, size); // Convert to UTF8 and return if (mEncoder) From 031eec45509c3b157dd4fce9e366ac4fcfffe571 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Jan 2015 00:11:36 +0100 Subject: [PATCH 06/12] Starting ESS importer for Morrowind save files --- CMakeLists.txt | 11 + apps/essimporter/CMakeLists.txt | 25 +++ apps/essimporter/converter.cpp | 40 ++++ apps/essimporter/converter.hpp | 262 +++++++++++++++++++++++ apps/essimporter/importacdt.hpp | 25 +++ apps/essimporter/importcellref.cpp | 28 +++ apps/essimporter/importcellref.hpp | 33 +++ apps/essimporter/importcrec.cpp | 13 ++ apps/essimporter/importcrec.hpp | 22 ++ apps/essimporter/importer.cpp | 306 +++++++++++++++++++++++++++ apps/essimporter/importer.hpp | 25 +++ apps/essimporter/importercontext.cpp | 0 apps/essimporter/importercontext.hpp | 65 ++++++ apps/essimporter/importnpcc.cpp | 18 ++ apps/essimporter/importnpcc.hpp | 29 +++ apps/essimporter/importplayer.cpp | 59 ++++++ apps/essimporter/importplayer.hpp | 58 +++++ apps/essimporter/main.cpp | 60 ++++++ components/esm/esmreader.hpp | 1 + components/esm/loadcell.cpp | 5 + components/esm/loadnpcc.hpp | 27 ++- components/esm/loadscpt.cpp | 26 +++ components/esm/loadtes3.cpp | 19 ++ components/esm/loadtes3.hpp | 14 ++ 24 files changed, 1160 insertions(+), 11 deletions(-) create mode 100644 apps/essimporter/CMakeLists.txt create mode 100644 apps/essimporter/converter.cpp create mode 100644 apps/essimporter/converter.hpp create mode 100644 apps/essimporter/importacdt.hpp create mode 100644 apps/essimporter/importcellref.cpp create mode 100644 apps/essimporter/importcellref.hpp create mode 100644 apps/essimporter/importcrec.cpp create mode 100644 apps/essimporter/importcrec.hpp create mode 100644 apps/essimporter/importer.cpp create mode 100644 apps/essimporter/importer.hpp create mode 100644 apps/essimporter/importercontext.cpp create mode 100644 apps/essimporter/importercontext.hpp create mode 100644 apps/essimporter/importnpcc.cpp create mode 100644 apps/essimporter/importnpcc.hpp create mode 100644 apps/essimporter/importplayer.cpp create mode 100644 apps/essimporter/importplayer.hpp create mode 100644 apps/essimporter/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bbce7ad42..6e971b8fb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ option(BUILD_BSATOOL "build BSA extractor" ON) option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) +option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON) option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WIZARD "build Installation Wizard" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) @@ -414,6 +415,9 @@ IF(NOT WIN32 AND NOT APPLE) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_ESSIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) ENDIF(BUILD_OPENCS) @@ -472,6 +476,9 @@ if(WIN32) IF(BUILD_MWINIIMPORTER) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".") ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_ESSIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".") + ENDIF(BUILD_ESSIMPORTER) IF(BUILD_OPENCS) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".") @@ -582,6 +589,10 @@ if (BUILD_MWINIIMPORTER) add_subdirectory( apps/mwiniimporter ) endif() +if (BUILD_ESSIMPORTER) + add_subdirectory (apps/essimporter ) +endif() + if (BUILD_OPENCS) add_subdirectory (apps/opencs) endif() diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt new file mode 100644 index 0000000000..231e31fd84 --- /dev/null +++ b/apps/essimporter/CMakeLists.txt @@ -0,0 +1,25 @@ +set(ESSIMPORTER_FILES + main.cpp + importer.cpp + importplayer.cpp + importnpcc.cpp + importcrec.cpp + importcellref.cpp + importacdt.hpp + importercontext.cpp + converter.cpp +) + +add_executable(openmw-essimporter + ${ESSIMPORTER_FILES} +) + +target_link_libraries(openmw-essimporter + ${Boost_LIBRARIES} + components +) + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(openmw-essimporter gcov) +endif() diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp new file mode 100644 index 0000000000..4f36f13ee3 --- /dev/null +++ b/apps/essimporter/converter.cpp @@ -0,0 +1,40 @@ +#include "converter.hpp" + +#include + +namespace +{ + + void convertImage(char* data, int size, int width, int height, Ogre::PixelFormat pf, const std::string& out) + { + Ogre::Image screenshot; + Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(data, size)); + screenshot.loadRawData(stream, width, height, 1, pf); + screenshot.save(out); + } + +} + +namespace ESSImport +{ + + + struct MAPH + { + unsigned int size; + unsigned int value; + }; + + void ConvertFMAP::read(ESM::ESMReader &esm) + { + MAPH maph; + esm.getHNT(maph, "MAPH"); + std::vector data; + esm.getSubNameIs("MAPD"); + esm.getSubHeader(); + data.resize(esm.getSubSize()); + esm.getExact(&data[0], data.size()); + convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga"); + } + +} diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp new file mode 100644 index 0000000000..2662f8c752 --- /dev/null +++ b/apps/essimporter/converter.hpp @@ -0,0 +1,262 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTER_H +#define OPENMW_ESSIMPORT_CONVERTER_H + +#include +#include + +#include +#include +#include +#include +#include "importcrec.hpp" + +#include "importercontext.hpp" +#include "importcellref.hpp" + +namespace ESSImport +{ + +class Converter +{ +public: + /// @return the order for writing this converter's records to the output file, in relation to other converters + virtual int getStage() { return 1; } + + virtual ~Converter() {} + + void setContext(Context& context) { mContext = &context; } + + virtual void read(ESM::ESMReader& esm) + { + } + + /// Called after the input file has been read in completely, which may be necessary + /// if the conversion process relies on information in other records + virtual void write(ESM::ESMWriter& esm) + { + + } + +protected: + Context* mContext; +}; + +/// Default converter: simply reads the record and writes it unmodified to the output +template +class DefaultConverter : public Converter +{ +public: + virtual int getStage() { return 0; } + + virtual void read(ESM::ESMReader& esm) + { + std::string id = esm.getHNString("NAME"); + T record; + record.load(esm); + mRecords[id] = record; + } + + virtual void write(ESM::ESMWriter& esm) + { + for (typename std::map::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) + { + esm.startRecord(T::sRecordId); + esm.writeHNString("NAME", it->first); + it->second.save(esm); + esm.endRecord(T::sRecordId); + } + } + +protected: + std::map mRecords; +}; + +class ConvertNPC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + // this is always the player + ESM::NPC npc; + std::string id = esm.getHNString("NAME"); + assert (id == "player"); + npc.load(esm); + mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; + mContext->mPlayerBase = npc; + std::map empty; + for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + } +}; + +class ConvertGlobal : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Global global; + global.load(esm); + if (Misc::StringUtils::ciEqual(id, "gamehour")) + mContext->mHour = global.mValue.getFloat(); + if (Misc::StringUtils::ciEqual(id, "day")) + mContext->mDay = global.mValue.getInteger(); + if (Misc::StringUtils::ciEqual(id, "month")) + mContext->mMonth = global.mValue.getInteger(); + if (Misc::StringUtils::ciEqual(id, "year")) + mContext->mYear = global.mValue.getInteger(); + mRecords[id] = global; + } +}; + +class ConvertClass : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Class class_; + class_.load(esm); + + if (id == "NEWCLASSID_CHARGEN") + mContext->mCustomPlayerClassName = class_.mName; + + mRecords[id] = class_; + } +}; + +class ConvertBook : public DefaultConverter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + ESM::Book book; + book.load(esm); + if (book.mData.mSkillID == -1) + mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id)); + + mRecords[id] = book; + } +}; + +class ConvertNPCC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + NPCC npcc; + npcc.load(esm); + if (id == "PlayerSaveGame") + { + mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; + } + } +}; + +class ConvertREFR : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + REFR refr; + refr.load(esm); + assert(refr.mRefID == "PlayerSaveGame"); + mContext->mPlayer.mObject.mPosition = refr.mPos; + + ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats; + for (int i=0; i<3; ++i) + { + int writeIndex = translateDynamicIndex(i); + cStats.mDynamic[writeIndex].mBase = refr.mACDT.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mMod = refr.mACDT.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mCurrent = refr.mACDT.mDynamic[i][0]; + } + for (int i=0; i<8; ++i) + { + cStats.mAttributes[i].mBase = refr.mACDT.mAttributes[i][1]; + cStats.mAttributes[i].mMod = refr.mACDT.mAttributes[i][0]; + cStats.mAttributes[i].mCurrent = refr.mACDT.mAttributes[i][0]; + } + ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; + for (int i=0; imPlayer.mBirthsign = pcdt.mBirthsign; + mContext->mPlayer.mObject.mNpcStats.mBounty = pcdt.mBounty; + for (std::vector::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) + { + ESM::NpcStats::Faction faction; + faction.mExpelled = it->mFlags & 0x2; + faction.mRank = it->mRank; + faction.mReputation = it->mReputation; + mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; + } + + } +}; + +class ConvertCREC : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + CREC crec; + crec.load(esm); + } +}; + +class ConvertFMAP : public Converter +{ +public: + virtual void read(ESM::ESMReader &esm); +}; + +class ConvertCell : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + ESM::Cell cell; + std::string id = esm.getHNString("NAME"); + cell.load(esm, false); + CellRef ref; + while (esm.hasMoreSubs()) + { + ref.load (esm); + if (esm.isNextSub("DELE")) + std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + } + } + +}; + +} + +#endif diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp new file mode 100644 index 0000000000..1d79f899e3 --- /dev/null +++ b/apps/essimporter/importacdt.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORT_ACDT_H +#define OPENMW_ESSIMPORT_ACDT_H + +namespace ESSImport +{ + + /// Actor data, shared by (at least) REFR and CellRef + struct ACDT + { + unsigned char mUnknown1[40]; + float mDynamic[3][2]; + unsigned char mUnknown2[16]; + float mAttributes[8][2]; + unsigned char mUnknown3[120]; + }; + + /// Unknown, shared by (at least) REFR and CellRef + struct ACSC + { + unsigned char unknown[112]; + }; + +} + +#endif diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp new file mode 100644 index 0000000000..f59358d8b8 --- /dev/null +++ b/apps/essimporter/importcellref.cpp @@ -0,0 +1,28 @@ +#include "importcellref.hpp" + +#include + +namespace ESSImport +{ + + void CellRef::load(ESM::ESMReader &esm) + { + esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + + mIndexedRefId = esm.getHNString("NAME"); + + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + if (esm.isNextSub("CRED")) + esm.skipHSub(); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); + esm.getHNOT(mPos, "DATA", 24); + } + +} diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp new file mode 100644 index 0000000000..f06278ba50 --- /dev/null +++ b/apps/essimporter/importcellref.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_ESSIMPORT_CELLREF_H +#define OPENMW_ESSIMPORT_CELLREF_H + +#include + +#include + +#include "importacdt.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + // Not sure if we can share any code with ESM::CellRef here + struct CellRef + { + std::string mIndexedRefId; + ESM::RefNum mRefNum; + + ACDT mACDT; + + ESM::Position mPos; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp new file mode 100644 index 0000000000..c770b2386a --- /dev/null +++ b/apps/essimporter/importcrec.cpp @@ -0,0 +1,13 @@ +#include "importcrec.hpp" + +#include + +namespace ESSImport +{ + + void CREC::load(ESM::ESMReader &esm) + { + esm.getHNT(mIndex, "INDX"); + } + +} diff --git a/apps/essimporter/importcrec.hpp b/apps/essimporter/importcrec.hpp new file mode 100644 index 0000000000..62881c582a --- /dev/null +++ b/apps/essimporter/importcrec.hpp @@ -0,0 +1,22 @@ +#ifndef OPENMW_ESSIMPORT_CREC_H +#define OPENMW_ESSIMPORT_CREC_H + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + /// Creature changes + struct CREC + { + int mIndex; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp new file mode 100644 index 0000000000..96b79e029f --- /dev/null +++ b/apps/essimporter/importer.cpp @@ -0,0 +1,306 @@ +#include "importer.hpp" + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "importercontext.hpp" + +#include "converter.hpp" + +namespace +{ + + void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out) + { + Ogre::Image screenshot; + std::vector screenshotData = fileHeader.mSCRS; // MemoryDataStream doesn't work with const data :( + Ogre::DataStreamPtr screenshotStream (new Ogre::MemoryDataStream(&screenshotData[0], screenshotData.size())); + screenshot.loadRawData(screenshotStream, 128, 128, 1, Ogre::PF_BYTE_BGRA); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + out.mScreenshot.resize(encoded->size()); + encoded->read(&out.mScreenshot[0], encoded->size()); + } + +} + +namespace ESSImport +{ + + Importer::Importer(const std::string &essfile, const std::string &outfile) + : mEssFile(essfile) + , mOutFile(outfile) + { + + } + + struct File + { + struct Subrecord + { + std::string mName; + size_t mFileOffset; + std::vector mData; + }; + + struct Record + { + std::string mName; + size_t mFileOffset; + std::vector mSubrecords; + }; + + std::vector mRecords; + }; + + void read(const std::string& filename, File& file) + { + ESM::ESMReader esm; + esm.open(filename); + + while (esm.hasMoreRecs()) + { + ESM::NAME n = esm.getRecName(); + esm.getRecHeader(); + + File::Record rec; + rec.mName = n.toString(); + rec.mFileOffset = esm.getFileOffset(); + while (esm.hasMoreSubs()) + { + File::Subrecord sub; + esm.getSubName(); + esm.getSubHeader(); + sub.mFileOffset = esm.getFileOffset(); + sub.mName = esm.retSubName().toString(); + sub.mData.resize(esm.getSubSize()); + esm.getExact(&sub.mData[0], sub.mData.size()); + rec.mSubrecords.push_back(sub); + } + file.mRecords.push_back(rec); + } + } + + void Importer::compare() + { + // data that always changes should be blacklisted + std::set > blacklist; + blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour + blacklist.insert(std::make_pair("REFR", "DATA")); // player position + + File file1; + read(mEssFile, file1); + File file2; + read(mOutFile, file2); // todo rename variable + + for (unsigned int i=0; i= file2.mRecords.size()) + { + std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset; + return; + } + + File::Record rec2 = file2.mRecords[i]; + + if (rec.mName != rec2.mName) + { + std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl; + return; // TODO: try to recover + } + + // FIXME: use max(size1, size2) + for (unsigned int j=0; j= rec2.mSubrecords.size()) + { + std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset; + return; + } + + File::Subrecord sub2 = rec2.mSubrecords[j]; + + if (sub.mName != sub2.mName) + { + std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset + << " (2) 0x" << sub2.mFileOffset << std::endl; + return; // TODO: try to recover + } + + if (sub.mData != sub2.mData) + { + if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) + continue; + + std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset + << " (2) 0x" << sub2.mFileOffset << std::endl; + + std::cout << "Data 1:" << std::endl; + for (unsigned int k=0; k globals; + + const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; + const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; + const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; + + std::map > converters; + converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); + converters[ESM::REC_BOOK] = boost::shared_ptr(new ConvertBook()); + converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); + converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); + converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); + converters[recREFR] = boost::shared_ptr(new ConvertREFR()); + converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); + converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); + converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); + converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); + converters[ESM::REC_SPEL] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_ARMO] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CLOT] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_ENCH] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + + + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + it->second->setContext(context); + } + + while (esm.hasMoreRecs()) + { + ESM::NAME n = esm.getRecName(); + esm.getRecHeader(); + + std::map >::iterator it = converters.find(n.val); + if (it != converters.end()) + { + it->second->read(esm); + } + else + { + std::cerr << "unknown record " << n.toString() << std::endl; + esm.skipRecord(); + } + } + + const ESM::Header& header = esm.getHeader(); + + ESM::ESMWriter writer; + + writer.setFormat (ESM::Header::CurrentFormat); + + std::ofstream stream(mOutFile.c_str(), std::ios::binary); + // all unused + writer.setVersion(0); + writer.setType(0); + writer.setAuthor(""); + writer.setDescription(""); + writer.setRecordCount (0); + writer.save (stream); + + ESM::SavedGame profile; + for (std::vector::const_iterator it = header.mMaster.begin(); + it != header.mMaster.end(); ++it) + { + profile.mContentFiles.push_back(it->name); + } + profile.mDescription = esm.getDesc(); + profile.mInGameTime.mDay = context.mDay; + profile.mInGameTime.mGameHour = context.mHour; + profile.mInGameTime.mMonth = context.mMonth; + profile.mInGameTime.mYear = context.mYear; + profile.mPlayerCell = header.mGameData.mCurrentCell.toString(); + if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN") + profile.mPlayerClassName = context.mCustomPlayerClassName; + else + profile.mPlayerClassId = context.mPlayerBase.mClass; + profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel; + profile.mPlayerName = header.mGameData.mPlayerName.toString(); + + writeScreenshot(header, profile); + + writer.startRecord (ESM::REC_SAVE); + profile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + // Writing order should be Dynamic Store -> Cells -> Player, + // so that references to dynamic records can be recognized when loading + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + if (it->second->getStage() != 0) + continue; + it->second->write(writer); + } + + writer.startRecord(ESM::REC_NPC_); + writer.writeHNString("NAME", "player"); + context.mPlayerBase.save(writer); + writer.endRecord(ESM::REC_NPC_); + + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + if (it->second->getStage() != 1) + continue; + it->second->write(writer); + } + + writer.startRecord(ESM::REC_PLAY); + context.mPlayer.save(writer); + writer.endRecord(ESM::REC_PLAY); + } + + +} diff --git a/apps/essimporter/importer.hpp b/apps/essimporter/importer.hpp new file mode 100644 index 0000000000..eb199b6dfb --- /dev/null +++ b/apps/essimporter/importer.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESSIMPORTER_IMPORTER_H +#define OPENMW_ESSIMPORTER_IMPORTER_H + +#include + +namespace ESSImport +{ + + class Importer + { + public: + Importer(const std::string& essfile, const std::string& outfile); + + void run(); + + void compare(); + + private: + std::string mEssFile; + std::string mOutFile; + }; + +} + +#endif diff --git a/apps/essimporter/importercontext.cpp b/apps/essimporter/importercontext.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp new file mode 100644 index 0000000000..e9af776788 --- /dev/null +++ b/apps/essimporter/importercontext.hpp @@ -0,0 +1,65 @@ +#ifndef OPENMW_ESSIMPORT_CONTEXT_H +#define OPENMW_ESSIMPORT_CONTEXT_H + +#include + +#include "importnpcc.hpp" +#include "importplayer.hpp" + +#include + +namespace ESSImport +{ + + struct Context + { + ESM::Player mPlayer; + ESM::NPC mPlayerBase; + std::string mCustomPlayerClassName; + + int mDay, mMonth, mYear; + float mHour; + + Context() + { + mPlayer.mAutoMove = 0; + ESM::CellId playerCellId; + playerCellId.mPaged = true; + playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; + mPlayer.mCellId = playerCellId; + //mPlayer.mLastKnownExteriorPosition + mPlayer.mHasMark = 0; // TODO + mPlayer.mCurrentCrimeId = 0; // TODO + mPlayer.mObject.mCount = 1; + mPlayer.mObject.mEnabled = 1; + mPlayer.mObject.mHasLocals = false; + mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame + mPlayer.mObject.mCreatureStats.mHasAiSettings = true; + mPlayer.mObject.mCreatureStats.mDead = false; + mPlayer.mObject.mCreatureStats.mDied = false; + mPlayer.mObject.mCreatureStats.mKnockdown = false; + mPlayer.mObject.mCreatureStats.mKnockdownOneFrame = false; + mPlayer.mObject.mCreatureStats.mKnockdownOverOneFrame = false; + mPlayer.mObject.mCreatureStats.mHitRecovery = false; + mPlayer.mObject.mCreatureStats.mBlock = false; + mPlayer.mObject.mCreatureStats.mMovementFlags = 0; + mPlayer.mObject.mCreatureStats.mAttackStrength = 0.f; + mPlayer.mObject.mCreatureStats.mFallHeight = 0.f; + mPlayer.mObject.mCreatureStats.mRecalcDynamicStats = false; + mPlayer.mObject.mCreatureStats.mDrawState = 0; + mPlayer.mObject.mCreatureStats.mDeathAnimation = 0; + mPlayer.mObject.mNpcStats.mIsWerewolf = false; + mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; + mPlayer.mObject.mNpcStats.mLevelProgress = 0; + mPlayer.mObject.mNpcStats.mDisposition = 0; + mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; + mPlayer.mObject.mNpcStats.mReputation = 0; + mPlayer.mObject.mNpcStats.mCrimeId = -1; + mPlayer.mObject.mNpcStats.mWerewolfKills = 0; + mPlayer.mObject.mNpcStats.mProfit = 0; + } + }; + +} + +#endif diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp new file mode 100644 index 0000000000..192fe5da41 --- /dev/null +++ b/apps/essimporter/importnpcc.cpp @@ -0,0 +1,18 @@ +#include "importnpcc.hpp" + +#include + +namespace ESSImport +{ + + void NPCC::load(ESM::ESMReader &esm) + { + esm.getHNT(mNPDT, "NPDT"); + + // container: + // XIDX + // XHLT - condition + // WIDX - equipping? + } + +} diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp new file mode 100644 index 0000000000..12135850f6 --- /dev/null +++ b/apps/essimporter/importnpcc.hpp @@ -0,0 +1,29 @@ +#ifndef OPENMW_ESSIMPORT_NPCC_H +#define OPENMW_ESSIMPORT_NPCC_H + +#include +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + struct NPCC : public ESM::NPC + { + struct NPDT + { + unsigned char unknown[2]; + unsigned char mReputation; + unsigned char unknown2[5]; + } mNPDT; + + void load(ESM::ESMReader &esm); + }; + +} + +#endif diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp new file mode 100644 index 0000000000..81f81e764e --- /dev/null +++ b/apps/essimporter/importplayer.cpp @@ -0,0 +1,59 @@ +#include "importplayer.hpp" + +#include + +namespace ESSImport +{ + + void REFR::load(ESM::ESMReader &esm) + { + esm.getHNT(mRefNum.mIndex, "FRMR"); + + mRefID = esm.getHNString("NAME"); + + if (esm.isNextSub("STPR")) + esm.skipHSub(); // ESS TODO + + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + esm.getHNExact(mSkills, 27*2*sizeof(int), "CHRD"); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); // ESS TODO (1 byte) + + esm.getHNOT(mPos, "DATA", 24); + } + + void PCDT::load(ESM::ESMReader &esm) + { + if (esm.isNextSub("PNAM")) + esm.skipHSub(); + if (esm.isNextSub("SNAM")) + esm.skipHSub(); + if (esm.isNextSub("NAM9")) + esm.skipHSub(); + + mBounty = 0; + esm.getHNOT(mBounty, "CNAM"); + + mBirthsign = esm.getHNOString("BNAM"); + + if (esm.isNextSub("ENAM")) + esm.skipHSub(); + + while (esm.isNextSub("FNAM")) + { + FNAM fnam; + esm.getHT(fnam); + mFactions.push_back(fnam); + } + + if (esm.isNextSub("KNAM")) + esm.skipHSub(); + } + +} diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp new file mode 100644 index 0000000000..8a0a087a53 --- /dev/null +++ b/apps/essimporter/importplayer.hpp @@ -0,0 +1,58 @@ +#ifndef OPENMW_ESSIMPORT_PLAYER_H +#define OPENMW_ESSIMPORT_PLAYER_H + +#include +#include + +#include +#include +#include + +#include "importacdt.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + +/// Player-agnostic player data +struct REFR +{ + ACDT mACDT; + + std::string mRefID; + ESM::Position mPos; + ESM::RefNum mRefNum; + + int mSkills[27][2]; + float mAttributes[8][2]; + + void load(ESM::ESMReader& esm); +}; + +/// Other player data +struct PCDT +{ + int mBounty; + std::string mBirthsign; + + struct FNAM + { + unsigned char mRank; + unsigned char mUnknown1[3]; + int mReputation; + unsigned char mFlags; // 0x1: unknown, 0x2: expelled + unsigned char mUnknown2[3]; + ESM::NAME32 mFactionName; + }; + std::vector mFactions; + + void load(ESM::ESMReader& esm); +}; + +} + +#endif diff --git a/apps/essimporter/main.cpp b/apps/essimporter/main.cpp new file mode 100644 index 0000000000..0e5c65e951 --- /dev/null +++ b/apps/essimporter/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "importer.hpp" + +namespace bpo = boost::program_options; +namespace bfs = boost::filesystem; + + + +int main(int argc, const char** argv) +{ + try + { + bpo::options_description desc("Syntax: openmw-essimporter infile.ess outfile.omwsave\nAllowed options"); + bpo::positional_options_description p_desc; + desc.add_options() + ("help,h", "produce help message") + ("mwsave,m", bpo::value(), "morrowind .ess save file") + ("output,o", bpo::value(), "output file (.omwsave)") + ("compare,c", "compare two .ess files") + ; + p_desc.add("mwsave", 1).add("output", 1); + + bpo::variables_map vm; + + bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) + .options(desc) + .positional(p_desc) + .run(); + + bpo::store(parsed, vm); + + if(vm.count("help") || !vm.count("mwsave") || !vm.count("output")) { + std::cout << desc; + return 0; + } + + bpo::notify(vm); + + std::string essFile = vm["mwsave"].as(); + std::string outputFile = vm["output"].as(); + + ESSImport::Importer importer(essFile, outputFile); + + if (vm.count("compare")) + importer.compare(); + else + importer.run(); + } + catch (std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 1549e15f53..642ec4168e 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -36,6 +36,7 @@ public: const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } const std::vector &getGameFiles() const { return mHeader.mMaster; } + const Header& getHeader() const { return mHeader; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index e4f847dec2..f6649d94ab 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -111,6 +111,11 @@ void Cell::loadData(ESMReader &esm) } esm.getHNT(mData, "DATA", 12); + + // ess only, unknown + char nam8[32]; + if (esm.isNextSub("NAM8")) + esm.getHExact(nam8, 32); } void Cell::postLoad(ESMReader &esm) diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index c87c2545f7..6c087fd017 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -17,26 +17,29 @@ class ESMWriter; * * Some general observations about savegames: * - * Magical items/potions/spells/etc are added normally as new ALCH, - * SPEL, etc. records, with unique numeric identifiers. - * - * Books with ability enhancements are listed in the save if they have - * been read. - * - * GLOB records set global variables. - * * SCPT records do not define new scripts, but assign values to the * variables of existing ones. * * STLN - stolen items, ONAM is the owner * - * GAME - contains a GMDT (game data) of unknown format + * GAME - weather data + * struct GMDT + { + char mCellName[64]; + int mFogColour; + float mFogDensity; + int mCurrentWeather, mNextWeather; + int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition + int masserPhase, secundaPhase; // top 3 bytes may be garbage + }; * - * VFXM, SPLM, KLST - no clue + * VFXM, SPLM - no clue + * KLST - kill counter * * PCDT - seems to contain a lot of DNAMs, strings? * - * FMAP - MAPH and MAPD, probably map data. + * FMAP - MAPH and MAPD, global map image. * * JOUR - the entire journal in html * @@ -44,6 +47,8 @@ class ESMWriter; * ones you have done or begun. * * REGN - lists all regions in the game, even unvisited ones. + * notable differences to Regions in ESM files: mMapColor may be missing, and includes an unknown WNAM subrecord. + * * * The DIAL/INFO blocks contain changes to characters' dialog status. * diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 07561c2eaf..f2f13d0864 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -92,6 +92,32 @@ void Script::load(ESMReader &esm) if (esm.isNextSub("SCVR")) { esm.skipHSub(); } + + // ESS only, TODO: move + if (esm.isNextSub("SLCS")) + { + esm.getSubHeader(); + char unknown[12]; + esm.getExact(unknown, 12); + } + if (esm.isNextSub("SLSD")) + { + float read; + esm.getSubHeader(); + // values of variables? + for (int i=0; i mSCRD; // Used in .ess savegames only, screenshot? + std::vector mSCRS; // Used in .ess savegames only, screenshot? + Data mData; int mFormat; std::vector mMaster; From cbf56dbb4750822650dfc5335e78cc577a596c31 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 17 Jan 2015 03:07:24 +0100 Subject: [PATCH 07/12] ESSImport: work on cell fog of war --- apps/essimporter/converter.cpp | 50 ++++++++++++++++++++++++++++++++++ apps/essimporter/converter.hpp | 35 ++++++++++++++++++------ apps/essimporter/importer.cpp | 6 ++-- components/esm/loadcell.cpp | 5 ---- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 4f36f13ee3..5f73666258 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -37,4 +37,54 @@ namespace ESSImport convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga"); } + void ConvertCell::read(ESM::ESMReader &esm) + { + ESM::Cell cell; + std::string id = esm.getHNString("NAME"); + cell.load(esm, false); + + Cell newcell; + newcell.mCell = cell; + + // fog of war + // seems to be a 1-bit pixel format, 16*16 pixels + // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures, + // MW handles it when rendering only) + unsigned char nam8[32]; + if (esm.isNextSub("NAM8")) + { + esm.getHExact(nam8, 32); + + newcell.mFogOfWar.reserve(16*16); + for (int x=0; x<16; ++x) + { + for (int y=0; y<16; ++y) + { + size_t pos = x*16+y; + size_t bytepos = pos/8; + assert(bytepos<32); + int bit = pos%8; + newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff); + } + } + + std::ostringstream filename; + filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; + + convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + } + + std::vector cellrefs; + while (esm.hasMoreSubs()) + { + CellRef ref; + ref.load (esm); + if (esm.isNextSub("DELE")) + std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + } + + newcell.mRefs = cellrefs; + mCells[id] = newcell; + } + } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 2662f8c752..011508f36d 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "importcrec.hpp" #include "importercontext.hpp" @@ -241,20 +242,36 @@ public: class ConvertCell : public Converter { public: - virtual void read(ESM::ESMReader& esm) + virtual void read(ESM::ESMReader& esm); + + virtual void write(ESM::ESMWriter& esm) { - ESM::Cell cell; - std::string id = esm.getHNString("NAME"); - cell.load(esm, false); - CellRef ref; - while (esm.hasMoreSubs()) + for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) { - ref.load (esm); - if (esm.isNextSub("DELE")) - std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + const ESM::Cell& cell = it->second.mCell; + esm.startRecord(ESM::REC_CSTA); + ESM::CellState csta; + csta.mHasFogOfWar = 0; + csta.mId = cell.getCellId(); + csta.mId.save(esm); + // TODO csta.mLastRespawn; + // shouldn't be needed if we respawn on global schedule like in original MW + csta.mWaterLevel = cell.mWater; + csta.save(esm); + esm.endRecord(ESM::REC_CSTA); } } +private: + struct Cell + { + ESM::Cell mCell; + std::vector mRefs; + std::vector mFogOfWar; + }; + + std::map mCells; + }; } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 96b79e029f..b511dfe5e3 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -99,16 +99,18 @@ namespace ESSImport void Importer::compare() { - // data that always changes should be blacklisted + // data that always changes (and/or is already fully decoded) should be blacklisted std::set > blacklist; blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour blacklist.insert(std::make_pair("REFR", "DATA")); // player position + blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war File file1; read(mEssFile, file1); File file2; read(mOutFile, file2); // todo rename variable + // FIXME: use max(size1, size2) for (unsigned int i=0; i globals; - const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index f6649d94ab..e4f847dec2 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -111,11 +111,6 @@ void Cell::loadData(ESMReader &esm) } esm.getHNT(mData, "DATA", 12); - - // ess only, unknown - char nam8[32]; - if (esm.isNextSub("NAM8")) - esm.getHExact(nam8, 32); } void Cell::postLoad(ESMReader &esm) From c8ed24cc84f5112d7784d957afd1f6e745225c87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 16:13:52 +0100 Subject: [PATCH 08/12] ESSImport: creature CellRefs work, need probing to find ref type --- apps/essimporter/converter.cpp | 60 ++++++++++++++++++++++++++++ apps/essimporter/converter.hpp | 22 ++-------- apps/essimporter/importcellref.cpp | 9 +++++ apps/essimporter/importcellref.hpp | 2 + apps/essimporter/importercontext.hpp | 10 ++++- 5 files changed, 84 insertions(+), 19 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 5f73666258..1ad60d7ba7 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -2,6 +2,8 @@ #include +#include + namespace { @@ -81,10 +83,68 @@ namespace ESSImport ref.load (esm); if (esm.isNextSub("DELE")) std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + cellrefs.push_back(ref); } newcell.mRefs = cellrefs; mCells[id] = newcell; } + void ConvertCell::write(ESM::ESMWriter &esm) + { + for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) + { + const ESM::Cell& cell = it->second.mCell; + esm.startRecord(ESM::REC_CSTA); + ESM::CellState csta; + csta.mHasFogOfWar = 0; + csta.mId = cell.getCellId(); + csta.mId.save(esm); + // TODO csta.mLastRespawn; + // shouldn't be needed if we respawn on global schedule like in original MW + csta.mWaterLevel = cell.mWater; + csta.save(esm); + + for (std::vector::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt) + { + const CellRef& cellref = *refIt; + ESM::CellRef out; + out.blank(); + + if (cellref.mIndexedRefId.size() < 8) + { + std::cerr << "CellRef with no index?" << std::endl; + continue; + } + std::stringstream stream; + stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; + + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + std::cerr << "Can't' find CREC for " << refIndex << " " << out.mRefID << std::endl; + continue; + } + + ESM::CreatureState objstate; + objstate.mCount = 1; + objstate.mEnabled = cellref.mEnabled; + objstate.mHasLocals = 0; + objstate.mLocalRotation[0] = objstate.mLocalRotation[1] = objstate.mLocalRotation[2] = 0; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + } + + esm.endRecord(ESM::REC_CSTA); + } + } + } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 011508f36d..71eb3ab9fd 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -153,6 +153,7 @@ public: { mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; } + //mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), crec)); } }; @@ -230,6 +231,8 @@ public: std::string id = esm.getHNString("NAME"); CREC crec; crec.load(esm); + + mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); } }; @@ -243,24 +246,7 @@ class ConvertCell : public Converter { public: virtual void read(ESM::ESMReader& esm); - - virtual void write(ESM::ESMWriter& esm) - { - for (std::map::const_iterator it = mCells.begin(); it != mCells.end(); ++it) - { - const ESM::Cell& cell = it->second.mCell; - esm.startRecord(ESM::REC_CSTA); - ESM::CellState csta; - csta.mHasFogOfWar = 0; - csta.mId = cell.getCellId(); - csta.mId.save(esm); - // TODO csta.mLastRespawn; - // shouldn't be needed if we respawn on global schedule like in original MW - csta.mWaterLevel = cell.mWater; - csta.save(esm); - esm.endRecord(ESM::REC_CSTA); - } - } + virtual void write(ESM::ESMWriter& esm); private: struct Cell diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index f59358d8b8..51e771081c 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -9,6 +9,11 @@ namespace ESSImport { esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + // this is required since openmw supports more than 255 content files + int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; + mRefNum.mContentFile = pluginIndex-1; + mRefNum.mIndex &= 0x00ffffff; + mIndexedRefId = esm.getHNString("NAME"); esm.getHNT(mACDT, "ACDT"); @@ -22,6 +27,10 @@ namespace ESSImport if (esm.isNextSub("ND3D")) esm.skipHSub(); + + mEnabled = true; + esm.getHNOT(mEnabled, "ZNAM"); + esm.getHNOT(mPos, "DATA", 24); } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index f06278ba50..72c5af2570 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -25,6 +25,8 @@ namespace ESSImport ESM::Position mPos; + bool mEnabled; + void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index e9af776788..6f26e8329d 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -1,12 +1,16 @@ #ifndef OPENMW_ESSIMPORT_CONTEXT_H #define OPENMW_ESSIMPORT_CONTEXT_H +#include + #include +#include #include "importnpcc.hpp" +#include "importcrec.hpp" #include "importplayer.hpp" -#include + namespace ESSImport { @@ -20,6 +24,10 @@ namespace ESSImport int mDay, mMonth, mYear; float mHour; + // key + std::map, CREC> mCreatureChanges; + std::map, NPCC> mNpcChanges; + Context() { mPlayer.mAutoMove = 0; From 08ad4d73bbf9873887a8c6716c37b641e2603026 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 19:59:29 +0100 Subject: [PATCH 09/12] ESSImport: player is placed in correct cell, npc cellrefs work --- apps/essimporter/CMakeLists.txt | 4 ++- apps/essimporter/convertacdt.cpp | 42 ++++++++++++++++++++++ apps/essimporter/convertacdt.hpp | 22 ++++++++++++ apps/essimporter/converter.cpp | 54 +++++++++++++++++++++------- apps/essimporter/converter.hpp | 40 +++++---------------- apps/essimporter/convertnpcc.cpp | 22 ++++++++++++ apps/essimporter/convertnpcc.hpp | 15 ++++++++ apps/essimporter/importacdt.cpp | 28 +++++++++++++++ apps/essimporter/importacdt.hpp | 24 +++++++++++++ apps/essimporter/importcellref.cpp | 19 +++++----- apps/essimporter/importcellref.hpp | 4 ++- apps/essimporter/importer.cpp | 19 ++++++++-- apps/essimporter/importercontext.hpp | 30 +++------------- apps/essimporter/importnpcc.cpp | 25 ++++++++++--- apps/essimporter/importnpcc.hpp | 12 +++++-- apps/essimporter/importplayer.cpp | 14 +++----- apps/essimporter/importplayer.hpp | 5 +-- components/esm/creaturestate.cpp | 8 ++++- components/esm/creaturestate.hpp | 3 ++ components/esm/creaturestats.cpp | 36 +++++++++++++++++-- components/esm/creaturestats.hpp | 3 ++ components/esm/npcstate.cpp | 9 ++++- components/esm/npcstate.hpp | 3 ++ components/esm/npcstats.cpp | 15 ++++++++ components/esm/npcstats.hpp | 3 ++ components/esm/objectstate.cpp | 16 ++++++++- components/esm/objectstate.hpp | 5 ++- 27 files changed, 370 insertions(+), 110 deletions(-) create mode 100644 apps/essimporter/convertacdt.cpp create mode 100644 apps/essimporter/convertacdt.hpp create mode 100644 apps/essimporter/convertnpcc.cpp create mode 100644 apps/essimporter/convertnpcc.hpp create mode 100644 apps/essimporter/importacdt.cpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 231e31fd84..257a466ee2 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -5,9 +5,11 @@ set(ESSIMPORTER_FILES importnpcc.cpp importcrec.cpp importcellref.cpp - importacdt.hpp + importacdt.cpp importercontext.cpp converter.cpp + convertacdt.cpp + convertnpcc.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp new file mode 100644 index 0000000000..81ab61084f --- /dev/null +++ b/apps/essimporter/convertacdt.cpp @@ -0,0 +1,42 @@ +#include "convertacdt.hpp" + +namespace ESSImport +{ + + int translateDynamicIndex(int mwIndex) + { + if (mwIndex == 1) + return 2; + else if (mwIndex == 2) + return 1; + return mwIndex; + } + + void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats) + { + for (int i=0; i<3; ++i) + { + int writeIndex = translateDynamicIndex(i); + cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1]; + cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0]; + } + for (int i=0; i<8; ++i) + { + cStats.mAttributes[i].mBase = acdt.mAttributes[i][1]; + cStats.mAttributes[i].mMod = acdt.mAttributes[i][0]; + cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0]; + } + } + + void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) + { + for (int i=0; i +#include +#include + +#include "importacdt.hpp" + +namespace ESSImport +{ + + // OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka + int translateDynamicIndex(int mwIndex); + + + void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats); + + void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 1ad60d7ba7..2e7a954e08 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -43,8 +43,15 @@ namespace ESSImport { ESM::Cell cell; std::string id = esm.getHNString("NAME"); + cell.mName = id; cell.load(esm, false); + // note if the player is in a nameless exterior cell, we will assign the cellId later based on player position + if (id == mContext->mPlayerCellName) + { + mContext->mPlayer.mCellId = cell.getCellId(); + } + Cell newcell; newcell.mCell = cell; @@ -55,7 +62,12 @@ namespace ESSImport unsigned char nam8[32]; if (esm.isNextSub("NAM8")) { - esm.getHExact(nam8, 32); + esm.getSubHeader(); + // FIXME: different size in interior cells for some reason + if (esm.getSubSize() == 36) + esm.skip(4); + + esm.getExact(nam8, 32); newcell.mFogOfWar.reserve(16*16); for (int x=0; x<16; ++x) @@ -87,6 +99,8 @@ namespace ESSImport } newcell.mRefs = cellrefs; + + // FIXME: map by ID for exterior cells mCells[id] = newcell; } @@ -127,20 +141,36 @@ namespace ESSImport std::make_pair(refIndex, out.mRefID)); if (crecIt != mContext->mCreatureChanges.end()) { - std::cerr << "Can't' find CREC for " << refIndex << " " << out.mRefID << std::endl; + ESM::CreatureState objstate; + objstate.blank(); + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); continue; } - ESM::CreatureState objstate; - objstate.mCount = 1; - objstate.mEnabled = cellref.mEnabled; - objstate.mHasLocals = 0; - objstate.mLocalRotation[0] = objstate.mLocalRotation[1] = objstate.mLocalRotation[2] = 0; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - esm.writeHNT ("OBJE", ESM::REC_CREA); - objstate.save(esm); + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef = out; + objstate.mRef.mRefNum = cellref.mRefNum; + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); + continue; + } + + std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; } esm.endRecord(ESM::REC_CSTA); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 71eb3ab9fd..7e91669022 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -14,6 +14,9 @@ #include "importercontext.hpp" #include "importcellref.hpp" +#include "convertacdt.hpp" +#include "convertnpcc.hpp" + namespace ESSImport { @@ -151,9 +154,10 @@ public: npcc.load(esm); if (id == "PlayerSaveGame") { - mContext->mPlayer.mObject.mNpcStats.mReputation = npcc.mNPDT.mReputation; + convertNPCC(npcc, mContext->mPlayer.mObject); } - //mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), crec)); + else + mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc)); } }; @@ -168,36 +172,10 @@ public: mContext->mPlayer.mObject.mPosition = refr.mPos; ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats; - for (int i=0; i<3; ++i) - { - int writeIndex = translateDynamicIndex(i); - cStats.mDynamic[writeIndex].mBase = refr.mACDT.mDynamic[i][1]; - cStats.mDynamic[writeIndex].mMod = refr.mACDT.mDynamic[i][1]; - cStats.mDynamic[writeIndex].mCurrent = refr.mACDT.mDynamic[i][0]; - } - for (int i=0; i<8; ++i) - { - cStats.mAttributes[i].mBase = refr.mACDT.mAttributes[i][1]; - cStats.mAttributes[i].mMod = refr.mACDT.mAttributes[i][0]; - cStats.mAttributes[i].mCurrent = refr.mACDT.mAttributes[i][0]; - } - ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats; - for (int i=0; imPlayer.mObject.mNpcStats; + convertNpcData(refr.mActorData, npcStats); } }; diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp new file mode 100644 index 0000000000..11e665bfd0 --- /dev/null +++ b/apps/essimporter/convertnpcc.cpp @@ -0,0 +1,22 @@ +#include "convertnpcc.hpp" + +namespace ESSImport +{ + + void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) + { + npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; + + for (std::vector::const_iterator it = npcc.mInventory.begin(); + it != npcc.mInventory.end(); ++it) + { + ESM::ObjectState obj; + obj.blank(); + obj.mRef.mRefID = it->mId; + obj.mRef.mCharge = it->mCondition; + + // Don't know type of object :( change save format? + // npcState.mInventory.mItems.push_back(std::make_pair(obj, std::make_pair(0,0))); + } + } +} diff --git a/apps/essimporter/convertnpcc.hpp b/apps/essimporter/convertnpcc.hpp new file mode 100644 index 0000000000..eb12d8f3bc --- /dev/null +++ b/apps/essimporter/convertnpcc.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H +#define OPENMW_ESSIMPORT_CONVERTNPCC_H + +#include "importnpcc.hpp" + +#include + +namespace ESSImport +{ + + void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState); + +} + +#endif diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp new file mode 100644 index 0000000000..b3e399f9fc --- /dev/null +++ b/apps/essimporter/importacdt.cpp @@ -0,0 +1,28 @@ +#include "importacdt.hpp" + +#include + +namespace ESSImport +{ + + void ActorData::load(ESM::ESMReader &esm) + { + esm.getHNT(mACDT, "ACDT"); + + ACSC acsc; + esm.getHNOT(acsc, "ACSC"); + esm.getHNOT(acsc, "ACSL"); + + if (esm.isNextSub("CHRD")) // npc only + esm.getHExact(mSkills, 27*2*sizeof(int)); + + if (esm.isNextSub("CRED")) // creature only + esm.getHExact(mCombatStats, 3*2*sizeof(int)); + + mScript = esm.getHNOString("SCRI"); + + if (esm.isNextSub("ND3D")) + esm.skipHSub(); + } + +} diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 1d79f899e3..6efae9dc57 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -1,9 +1,17 @@ #ifndef OPENMW_ESSIMPORT_ACDT_H #define OPENMW_ESSIMPORT_ACDT_H +#include + +namespace ESM +{ + struct ESMReader; +} + namespace ESSImport { + /// Actor data, shared by (at least) REFR and CellRef struct ACDT { @@ -14,6 +22,22 @@ namespace ESSImport unsigned char mUnknown3[120]; }; + struct ActorData + { + ACDT mACDT; + + int mSkills[27][2]; + + // creature combat stats, base and modified + // I think these can be ignored in the conversion, because it is not possible + // to change them ingame + int mCombatStats[3][2]; + + std::string mScript; + + void load(ESM::ESMReader& esm); + }; + /// Unknown, shared by (at least) REFR and CellRef struct ACSC { diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 51e771081c..76356769aa 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -16,22 +16,19 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); - esm.getHNT(mACDT, "ACDT"); + // the following two occur in ESM::CellRef too (Charge and Gold), + // but may have entirely different meanings here + int intv; + esm.getHNOT(intv, "INTV"); + int nam9; + esm.getHNOT(nam9, "NAM9"); - ACSC acsc; - esm.getHNOT(acsc, "ACSC"); - esm.getHNOT(acsc, "ACSL"); - - if (esm.isNextSub("CRED")) - esm.skipHSub(); - - if (esm.isNextSub("ND3D")) - esm.skipHSub(); + mActorData.load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - esm.getHNOT(mPos, "DATA", 24); + esm.getHNT(mPos, "DATA", 24); } } diff --git a/apps/essimporter/importcellref.hpp b/apps/essimporter/importcellref.hpp index 72c5af2570..77763d4343 100644 --- a/apps/essimporter/importcellref.hpp +++ b/apps/essimporter/importcellref.hpp @@ -21,10 +21,12 @@ namespace ESSImport std::string mIndexedRefId; ESM::RefNum mRefNum; - ACDT mACDT; + ActorData mActorData; ESM::Position mPos; + std::string mScript; + bool mEnabled; void load(ESM::ESMReader& esm); diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index b511dfe5e3..572489d9a7 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -185,6 +185,9 @@ namespace ESSImport Context context; + const ESM::Header& header = esm.getHeader(); + context.mPlayerCellName = header.mGameData.mCurrentCell.toString(); + const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; @@ -234,8 +237,6 @@ namespace ESSImport } } - const ESM::Header& header = esm.getHeader(); - ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); @@ -247,6 +248,11 @@ namespace ESSImport writer.setAuthor(""); writer.setDescription(""); writer.setRecordCount (0); + + for (std::vector::const_iterator it = header.mMaster.begin(); + it != header.mMaster.end(); ++it) + writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0 + writer.save (stream); ESM::SavedGame profile; @@ -298,6 +304,15 @@ namespace ESSImport } writer.startRecord(ESM::REC_PLAY); + if (context.mPlayer.mCellId.mPaged) + { + // exterior cell -> determine cell coordinates based on position + const int cellSize = 8192; + int cellX = std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize); + int cellY = std::floor(context.mPlayer.mObject.mPosition.pos[1]/cellSize); + context.mPlayer.mCellId.mIndex.mX = cellX; + context.mPlayer.mCellId.mIndex.mY = cellY; + } context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 6f26e8329d..9183e61796 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -17,6 +17,9 @@ namespace ESSImport struct Context { + // set from the TES3 header + std::string mPlayerCellName; + ESM::Player mPlayer; ESM::NPC mPlayerBase; std::string mCustomPlayerClassName; @@ -38,33 +41,8 @@ namespace ESSImport //mPlayer.mLastKnownExteriorPosition mPlayer.mHasMark = 0; // TODO mPlayer.mCurrentCrimeId = 0; // TODO - mPlayer.mObject.mCount = 1; - mPlayer.mObject.mEnabled = 1; - mPlayer.mObject.mHasLocals = false; + mPlayer.mObject.blank(); mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame - mPlayer.mObject.mCreatureStats.mHasAiSettings = true; - mPlayer.mObject.mCreatureStats.mDead = false; - mPlayer.mObject.mCreatureStats.mDied = false; - mPlayer.mObject.mCreatureStats.mKnockdown = false; - mPlayer.mObject.mCreatureStats.mKnockdownOneFrame = false; - mPlayer.mObject.mCreatureStats.mKnockdownOverOneFrame = false; - mPlayer.mObject.mCreatureStats.mHitRecovery = false; - mPlayer.mObject.mCreatureStats.mBlock = false; - mPlayer.mObject.mCreatureStats.mMovementFlags = 0; - mPlayer.mObject.mCreatureStats.mAttackStrength = 0.f; - mPlayer.mObject.mCreatureStats.mFallHeight = 0.f; - mPlayer.mObject.mCreatureStats.mRecalcDynamicStats = false; - mPlayer.mObject.mCreatureStats.mDrawState = 0; - mPlayer.mObject.mCreatureStats.mDeathAnimation = 0; - mPlayer.mObject.mNpcStats.mIsWerewolf = false; - mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; - mPlayer.mObject.mNpcStats.mLevelProgress = 0; - mPlayer.mObject.mNpcStats.mDisposition = 0; - mPlayer.mObject.mNpcStats.mTimeToStartDrowning = 20; - mPlayer.mObject.mNpcStats.mReputation = 0; - mPlayer.mObject.mNpcStats.mCrimeId = -1; - mPlayer.mObject.mNpcStats.mWerewolfKills = 0; - mPlayer.mObject.mNpcStats.mProfit = 0; } }; diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 192fe5da41..4cfc816c91 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -7,12 +7,29 @@ namespace ESSImport void NPCC::load(ESM::ESMReader &esm) { + mIndex = 0; + esm.getHNOT(mIndex, "INDX"); + esm.getHNT(mNPDT, "NPDT"); - // container: - // XIDX - // XHLT - condition - // WIDX - equipping? + while (esm.isNextSub("NPCO")) + { + InventoryItem item; + item.mId = esm.getHString(); + + if (esm.isNextSub("XIDX")) + esm.skipHSub(); + + item.mCondition = -1; + esm.getHNOT(item.mCondition, "XHLT"); + mInventory.push_back(item); + } + + while (esm.isNextSub("WIDX")) + { + // equipping? + esm.skipHSub(); + } } } diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index 12135850f6..d10048fe0d 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -1,7 +1,6 @@ #ifndef OPENMW_ESSIMPORT_NPCC_H #define OPENMW_ESSIMPORT_NPCC_H -#include #include namespace ESM @@ -12,7 +11,7 @@ namespace ESM namespace ESSImport { - struct NPCC : public ESM::NPC + struct NPCC { struct NPDT { @@ -21,6 +20,15 @@ namespace ESSImport unsigned char unknown2[5]; } mNPDT; + struct InventoryItem + { + std::string mId; + int mCondition; + }; + std::vector mInventory; + + int mIndex; + void load(ESM::ESMReader &esm); }; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 81f81e764e..f8c78dab01 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -14,16 +14,7 @@ namespace ESSImport if (esm.isNextSub("STPR")) esm.skipHSub(); // ESS TODO - esm.getHNT(mACDT, "ACDT"); - - ACSC acsc; - esm.getHNOT(acsc, "ACSC"); - esm.getHNOT(acsc, "ACSL"); - - esm.getHNExact(mSkills, 27*2*sizeof(int), "CHRD"); - - if (esm.isNextSub("ND3D")) - esm.skipHSub(); // ESS TODO (1 byte) + mActorData.load(esm); esm.getHNOT(mPos, "DATA", 24); } @@ -45,6 +36,9 @@ namespace ESSImport if (esm.isNextSub("ENAM")) esm.skipHSub(); + if (esm.isNextSub("LNAM")) + esm.skipHSub(); + while (esm.isNextSub("FNAM")) { FNAM fnam; diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 8a0a087a53..2493fcdad0 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -21,15 +21,12 @@ namespace ESSImport /// Player-agnostic player data struct REFR { - ACDT mACDT; + ActorData mActorData; std::string mRefID; ESM::Position mPos; ESM::RefNum mRefNum; - int mSkills[27][2]; - float mAttributes[8][2]; - void load(ESM::ESMReader& esm); }; diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index 9e9b561026..c2838f78db 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -17,4 +17,10 @@ void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const mInventory.save (esm); mCreatureStats.save (esm); -} \ No newline at end of file +} + +void ESM::CreatureState::blank() +{ + ObjectState::blank(); + mCreatureStats.blank(); +} diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index 604c2f3a70..9a3d41daae 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -14,6 +14,9 @@ namespace ESM InventoryState mInventory; CreatureStats mCreatureStats; + /// Initialize to default state + void blank(); + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 1fdce703c4..a1ef7eb0e1 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -218,6 +218,38 @@ void ESM::CreatureStats::save (ESMWriter &esm) const } esm.writeHNT("AISE", mHasAiSettings); - for (int i=0; i<4; ++i) - mAiSettings[i].save(esm); + if (mHasAiSettings) + { + for (int i=0; i<4; ++i) + mAiSettings[i].save(esm); + } +} + +void ESM::CreatureStats::blank() +{ + mTradeTime.mHour = 0; + mTradeTime.mDay = 0; + mGoldPool = 0; + mActorId = 0; + mHasAiSettings = false; + mDead = false; + mDied = false; + mMurdered = false; + mFriendlyHits = 0; + mTalkedTo = false; + mAlarmed = false; + mAttacked = false; + mAttackingOrSpell = false; + mKnockdown = false; + mKnockdownOneFrame = false; + mKnockdownOverOneFrame = false; + mHitRecovery = false; + mBlock = false; + mMovementFlags = 0; + mAttackStrength = 0.f; + mFallHeight = 0.f; + mRecalcDynamicStats = false; + mDrawState = 0; + mDeathAnimation = 0; + mLevel = 1; } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 680577f932..91ee983332 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -66,6 +66,9 @@ namespace ESM SpellState mSpells; ActiveSpells mActiveSpells; + /// Initialize to default state + void blank(); + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index e59ec3e268..6134193c24 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -21,4 +21,11 @@ void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const mNpcStats.save (esm); mCreatureStats.save (esm); -} \ No newline at end of file +} + +void ESM::NpcState::blank() +{ + ObjectState::blank(); + mNpcStats.blank(); + mCreatureStats.blank(); +} diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 39858d5533..b90cd85e6a 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -16,6 +16,9 @@ namespace ESM NpcStats mNpcStats; CreatureStats mCreatureStats; + /// Initialize to default state + void blank(); + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; }; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 4ba0ce7e3f..e305ddab09 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -150,3 +150,18 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mCrimeId != -1) esm.writeHNT ("CRID", mCrimeId); } + +void ESM::NpcStats::blank() +{ + mIsWerewolf = false; + mDisposition = 0; + mBounty = 0; + mReputation = 0; + mWerewolfKills = 0; + mProfit = 0; + mLevelProgress = 0; + for (int i=0; i<8; ++i) + mSkillIncrease[i] = 0; + mTimeToStartDrowning = 20; + mCrimeId = -1; +} diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index f6e9c1e588..a8ec4cf444 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -47,6 +47,9 @@ namespace ESM float mTimeToStartDrowning; int mCrimeId; + /// Initialize to default state + void blank(); + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index be00f3ef6e..f7755f8cbe 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -48,4 +48,18 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const } } -ESM::ObjectState::~ObjectState() {} \ No newline at end of file +void ESM::ObjectState::blank() +{ + mRef.blank(); + mHasLocals = 0; + mEnabled = false; + mCount = 1; + for (int i=0;i<3;++i) + { + mPosition.pos[i] = 0; + mPosition.rot[i] = 0; + mLocalRotation[i] = 0; + } +} + +ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 9c9ca5f2e8..5b05e0949e 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -29,8 +29,11 @@ namespace ESM virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; + /// Initialize to default state + void blank(); + virtual ~ObjectState(); }; } -#endif \ No newline at end of file +#endif From 19ed047dec4763362556141e876b12945a4894b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 Jan 2015 22:52:11 +0100 Subject: [PATCH 10/12] ESSImport: add some subrecords to CellRef and others, most files should load now, importacdt/cellref class structure need some refactoring --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.cpp | 34 +++++++++++-- apps/essimporter/converter.hpp | 16 ++++--- apps/essimporter/convertnpcc.cpp | 12 ----- apps/essimporter/importacdt.cpp | 71 +++++++++++++++++++++++++++- apps/essimporter/importcellref.cpp | 24 ++++++---- apps/essimporter/importcrec.cpp | 7 +++ apps/essimporter/importcrec.hpp | 4 ++ apps/essimporter/importinventory.cpp | 46 ++++++++++++++++++ apps/essimporter/importinventory.hpp | 31 ++++++++++++ apps/essimporter/importnpcc.cpp | 24 +++------- apps/essimporter/importnpcc.hpp | 11 ++--- apps/essimporter/importplayer.cpp | 28 +++++++++-- apps/essimporter/importplayer.hpp | 2 + components/esm/cellref.cpp | 5 ++ components/esm/cellref.hpp | 3 ++ 16 files changed, 261 insertions(+), 58 deletions(-) create mode 100644 apps/essimporter/importinventory.cpp create mode 100644 apps/essimporter/importinventory.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 257a466ee2..3d3e0860c1 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -6,6 +6,7 @@ set(ESSIMPORTER_FILES importcrec.cpp importcellref.cpp importacdt.cpp + importinventory.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 2e7a954e08..c51e91ed7c 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -60,12 +60,17 @@ namespace ESSImport // TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures, // MW handles it when rendering only) unsigned char nam8[32]; - if (esm.isNextSub("NAM8")) + // exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start + // (probably offset of that specific fog texture?) + while (esm.isNextSub("NAM8")) { esm.getSubHeader(); - // FIXME: different size in interior cells for some reason + if (esm.getSubSize() == 36) + { + // flag on interiors esm.skip(4); + } esm.getExact(nam8, 32); @@ -82,10 +87,24 @@ namespace ESSImport } } - std::ostringstream filename; - filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; + if (cell.isExterior()) + { + std::ostringstream filename; + filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga"; - convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str()); + } + } + + // moved reference, not handled yet + // NOTE: MVRF can also occur in within normal references (importcellref.cpp)? + // this does not match the ESM file implementation, + // verify if that can happen with ESM files too + while (esm.isNextSub("MVRF")) + { + esm.skipHSub(); // skip MVRF + esm.getSubName(); + esm.skipHSub(); // skip CNDT } std::vector cellrefs; @@ -94,7 +113,11 @@ namespace ESSImport CellRef ref; ref.load (esm); if (esm.isNextSub("DELE")) + { + // strangely this can be e.g. 52 instead of just 1, std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + esm.skipHSub(); + } cellrefs.push_back(ref); } @@ -148,6 +171,7 @@ namespace ESSImport objstate.mPosition = cellref.mPos; objstate.mRef = out; objstate.mRef.mRefNum = cellref.mRefNum; + // FIXME: change save format to not require object type, instead look up it up using the RefId esm.writeHNT ("OBJE", ESM::REC_CREA); objstate.save(esm); continue; diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 7e91669022..3f8b4cbbee 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -83,13 +83,17 @@ public: // this is always the player ESM::NPC npc; std::string id = esm.getHNString("NAME"); - assert (id == "player"); npc.load(esm); - mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; - mContext->mPlayerBase = npc; - std::map empty; - for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) - mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + if (id != "player") // seems to occur sometimes, with "chargen X" names + std::cerr << "non-player NPC record: " << id << std::endl; + else + { + mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; + mContext->mPlayerBase = npc; + std::map empty; + for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) + mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; + } } }; diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index 11e665bfd0..fdf96c15a3 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -6,17 +6,5 @@ namespace ESSImport void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; - - for (std::vector::const_iterator it = npcc.mInventory.begin(); - it != npcc.mInventory.end(); ++it) - { - ESM::ObjectState obj; - obj.blank(); - obj.mRef.mRefID = it->mId; - obj.mRef.mCharge = it->mCondition; - - // Don't know type of object :( change save format? - // npcState.mInventory.mItems.push_back(std::make_pair(obj, std::make_pair(0,0))); - } } } diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index b3e399f9fc..5bec5bd823 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -2,17 +2,73 @@ #include +#include + namespace ESSImport { void ActorData::load(ESM::ESMReader &esm) { - esm.getHNT(mACDT, "ACDT"); + // unsure at which point between NAME and ESM::CellRef + if (esm.isNextSub("MNAM")) + esm.skipHSub(); + + if (esm.isNextSub("ACTN")) + esm.skipHSub(); + + if (esm.isNextSub("STPR")) + esm.skipHSub(); + + ESM::CellRef bla; + bla.ESM::CellRef::loadData(esm); + + // FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef + esm.getHNOT(mACDT, "ACDT"); ACSC acsc; esm.getHNOT(acsc, "ACSC"); esm.getHNOT(acsc, "ACSL"); + if (esm.isNextSub("CSTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + if (esm.isNextSub("LSTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + // unsure at which point between LSTN and TGTN + if (esm.isNextSub("CSHN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + // unsure if before or after CSTN/LSTN + if (esm.isNextSub("LSHN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + while (esm.isNextSub("TGTN")) + esm.skipHSub(); // "PlayerSaveGame", link to some object? + + while (esm.isNextSub("FGTN")) + esm.getHString(); // fight target? + + // unsure at which point between FGTN and CHRD + if (esm.isNextSub("PWPC")) + esm.skipHSub(); + if (esm.isNextSub("PWPS")) + esm.skipHSub(); + + if (esm.isNextSub("WNAM")) + { + esm.skipHSub(); // seen values: "ancestor guardian", "bound dagger_en". Summoned creature / bound weapons? + + if (esm.isNextSub("XNAM")) + { + // "demon tanto", probably the ID of spell/item that created the bound weapon/crature? + esm.skipHSub(); + } + + if (esm.isNextSub("YNAM")) + esm.skipHSub(); // 4 byte, 0 + } + if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); @@ -21,8 +77,21 @@ namespace ESSImport mScript = esm.getHNOString("SCRI"); + // script variables? + if (!mScript.empty()) + { + if (esm.isNextSub("SLCS")) + esm.skipHSub(); + if (esm.isNextSub("SLSD")) // Short Data? + esm.skipHSub(); + if (esm.isNextSub("SLFD")) // Float Data? + esm.skipHSub(); + } + if (esm.isNextSub("ND3D")) esm.skipHSub(); + if (esm.isNextSub("ANIS")) + esm.skipHSub(); } } diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 76356769aa..454690adc0 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -7,7 +7,7 @@ namespace ESSImport void CellRef::load(ESM::ESMReader &esm) { - esm.getHNT(mRefNum.mIndex, "FRMR"); // TODO: adjust RefNum + esm.getHNT(mRefNum.mIndex, "FRMR"); // this is required since openmw supports more than 255 content files int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; @@ -16,19 +16,27 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); - // the following two occur in ESM::CellRef too (Charge and Gold), - // but may have entirely different meanings here - int intv; - esm.getHNOT(intv, "INTV"); - int nam9; - esm.getHNOT(nam9, "NAM9"); + if (esm.isNextSub("LVCR")) + esm.skipHSub(); mActorData.load(esm); mEnabled = true; esm.getHNOT(mEnabled, "ZNAM"); - esm.getHNT(mPos, "DATA", 24); + // should occur for all references but not levelled creature spawners + esm.getHNOT(mPos, "DATA", 24); + + // i've seen DATA record TWICE on a creature record - and with the exact same content too! weird + // alarmvoi0000.ess + esm.getHNOT(mPos, "DATA", 24); + + if (esm.isNextSub("MVRF")) + { + esm.skipHSub(); + esm.getSubName(); + esm.skipHSub(); + } } } diff --git a/apps/essimporter/importcrec.cpp b/apps/essimporter/importcrec.cpp index c770b2386a..9cf4555889 100644 --- a/apps/essimporter/importcrec.cpp +++ b/apps/essimporter/importcrec.cpp @@ -8,6 +8,13 @@ namespace ESSImport void CREC::load(ESM::ESMReader &esm) { esm.getHNT(mIndex, "INDX"); + + // equivalent of ESM::Creature XSCL? probably don't have to convert this, + // since the value can't be changed + float scale; + esm.getHNOT(scale, "XSCL"); + + mInventory.load(esm); } } diff --git a/apps/essimporter/importcrec.hpp b/apps/essimporter/importcrec.hpp index 62881c582a..16b7528070 100644 --- a/apps/essimporter/importcrec.hpp +++ b/apps/essimporter/importcrec.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H +#include "importinventory.hpp" + namespace ESM { class ESMReader; @@ -14,6 +16,8 @@ namespace ESSImport { int mIndex; + Inventory mInventory; + void load(ESM::ESMReader& esm); }; diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp new file mode 100644 index 0000000000..d132fd76da --- /dev/null +++ b/apps/essimporter/importinventory.cpp @@ -0,0 +1,46 @@ +#include "importinventory.hpp" + +#include + +namespace ESSImport +{ + + void Inventory::load(ESM::ESMReader &esm) + { + while (esm.isNextSub("NPCO")) + { + InventoryItem item; + item.mId = esm.getHString(); + + if (esm.isNextSub("XIDX")) + esm.skipHSub(); + + std::string script = esm.getHNOString("SCRI"); + // script variables? + // unsure if before or after ESM::CellRef + if (!script.empty()) + { + if (esm.isNextSub("SLCS")) + esm.skipHSub(); + if (esm.isNextSub("SLSD")) // Short Data? + esm.skipHSub(); + if (esm.isNextSub("SLFD")) // Float Data? + esm.skipHSub(); + } + + // for XSOL and XCHG seen so far, but probably others too + item.ESM::CellRef::loadData(esm); + + item.mCondition = -1; + esm.getHNOT(item.mCondition, "XHLT"); + mItems.push_back(item); + } + + while (esm.isNextSub("WIDX")) + { + // equipping? + esm.skipHSub(); + } + } + +} diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp new file mode 100644 index 0000000000..e31cab76ad --- /dev/null +++ b/apps/essimporter/importinventory.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H +#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H + +#include +#include + +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + + struct Inventory + { + struct InventoryItem : public ESM::CellRef + { + std::string mId; + int mCondition; + }; + std::vector mItems; + + void load(ESM::ESMReader& esm); + }; + +} + +#endif diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 4cfc816c91..8400a9e4ac 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -12,24 +12,14 @@ namespace ESSImport esm.getHNT(mNPDT, "NPDT"); - while (esm.isNextSub("NPCO")) - { - InventoryItem item; - item.mId = esm.getHString(); - - if (esm.isNextSub("XIDX")) - esm.skipHSub(); - - item.mCondition = -1; - esm.getHNOT(item.mCondition, "XHLT"); - mInventory.push_back(item); - } - - while (esm.isNextSub("WIDX")) - { - // equipping? + if (esm.isNextSub("AI_E")) esm.skipHSub(); - } + if (esm.isNextSub("AI_T")) + esm.skipHSub(); + if (esm.isNextSub("AI_F")) + esm.skipHSub(); + + mInventory.load(esm); } } diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index d10048fe0d..f3c4e24c7c 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -3,6 +3,10 @@ #include +#include + +#include "importinventory.hpp" + namespace ESM { class ESMReader; @@ -20,12 +24,7 @@ namespace ESSImport unsigned char unknown2[5]; } mNPDT; - struct InventoryItem - { - std::string mId; - int mCondition; - }; - std::vector mInventory; + Inventory mInventory; int mIndex; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index f8c78dab01..8a57ae5886 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -11,9 +11,6 @@ namespace ESSImport mRefID = esm.getHNString("NAME"); - if (esm.isNextSub("STPR")) - esm.skipHSub(); // ESS TODO - mActorData.load(esm); esm.getHNOT(mPos, "DATA", 24); @@ -21,6 +18,12 @@ namespace ESSImport void PCDT::load(ESM::ESMReader &esm) { + while (esm.isNextSub("DNAM")) + { + // TODO: deal with encoding? + mKnownDialogueTopics.push_back(esm.getHString()); + } + if (esm.isNextSub("PNAM")) esm.skipHSub(); if (esm.isNextSub("SNAM")) @@ -33,6 +36,17 @@ namespace ESSImport mBirthsign = esm.getHNOString("BNAM"); + // Holds the names of the last used Alchemy apparatus. Don't need to import this ATM, + // because our GUI auto-selects the best apparatus. + if (esm.isNextSub("NAM0")) + esm.skipHSub(); + if (esm.isNextSub("NAM1")) + esm.skipHSub(); + if (esm.isNextSub("NAM2")) + esm.skipHSub(); + if (esm.isNextSub("NAM3")) + esm.skipHSub(); + if (esm.isNextSub("ENAM")) esm.skipHSub(); @@ -48,6 +62,14 @@ namespace ESSImport if (esm.isNextSub("KNAM")) esm.skipHSub(); + + if (esm.isNextSub("WERE")) + { + // some werewolf data, 152 bytes + // maybe current skills and attributes for werewolf form + esm.getSubHeader(); + esm.skip(152); + } } } diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 2493fcdad0..af8625fa64 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -36,6 +36,8 @@ struct PCDT int mBounty; std::string mBirthsign; + std::vector mKnownDialogueTopics; + struct FNAM { unsigned char mRank; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index f93fe1535c..43b1f4d293 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -20,6 +20,11 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNString ("NAME"); + loadData(esm); +} + +void ESM::CellRef::loadData(ESMReader &esm) +{ // Again, UNAM sometimes appears after NAME and sometimes later. // Or perhaps this UNAM means something different? mReferenceBlocked = -1; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index f3986ccf39..b7b00a6548 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -91,6 +91,9 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); + /// Implicitly called by load + void loadData (ESMReader& esm); + void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const; void blank(); From f9cf31fcd5319dca02dddf89dead998cff3e9955 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 00:35:19 +0100 Subject: [PATCH 11/12] ESSImport: convert custom map markers, not working for interiors yet --- apps/essimporter/converter.cpp | 39 +++++++++++++++++++++++++- apps/essimporter/converter.hpp | 3 ++ apps/essimporter/importacdt.cpp | 7 +++++ apps/essimporter/importcellref.cpp | 3 +- apps/essimporter/importer.cpp | 5 +++- apps/openmw/mwgui/mapwindow.cpp | 39 +++++++------------------- apps/openmw/mwgui/mapwindow.hpp | 34 ++++++---------------- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +-- components/CMakeLists.txt | 2 +- components/esm/custommarkerstate.cpp | 26 +++++++++++++++++ components/esm/custommarkerstate.hpp | 30 ++++++++++++++++++++ 11 files changed, 132 insertions(+), 60 deletions(-) create mode 100644 components/esm/custommarkerstate.cpp create mode 100644 components/esm/custommarkerstate.hpp diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index c51e91ed7c..ce3cea6758 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -108,7 +108,7 @@ namespace ESSImport } std::vector cellrefs; - while (esm.hasMoreSubs()) + while (esm.hasMoreSubs() && esm.isNextSub("FRMR")) { CellRef ref; ref.load (esm); @@ -121,6 +121,36 @@ namespace ESSImport cellrefs.push_back(ref); } + while (esm.isNextSub("MPCD")) + { + float notepos[3]; + esm.getHT(notepos, 3*sizeof(float)); + + // Markers seem to be arranged in a 32*32 grid, notepos has grid-indices. + // This seems to be the reason markers can't be placed everywhere in interior cells, + // i.e. when the grid is exceeded. + // Converting the interior markers correctly could be rather tricky, but is probably similar logic + // as used for the FoW texture placement, which we need to figure out anyway + notepos[1] += 31.f; + notepos[0] += 0.5; + notepos[1] += 0.5; + notepos[0] = 8192 * notepos[0] / 32.f; + notepos[1] = 8192 * notepos[1] / 32.f; + if (cell.isExterior()) + { + notepos[0] += 8192 * cell.mData.mX; + notepos[1] += 8192 * cell.mData.mY; + } + // TODO: what encoding is this in? + std::string note = esm.getHNString("MPNT"); + ESM::CustomMarker marker; + marker.mWorldX = notepos[0]; + marker.mWorldY = notepos[1]; + marker.mNote = note; + marker.mCell = cell.getCellId(); + mMarkers.push_back(marker); + } + newcell.mRefs = cellrefs; // FIXME: map by ID for exterior cells @@ -199,6 +229,13 @@ namespace ESSImport esm.endRecord(ESM::REC_CSTA); } + + for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) + { + esm.startRecord(ESM::REC_MARK); + it->save(esm); + esm.endRecord(ESM::REC_MARK); + } } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 3f8b4cbbee..5f19617177 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -9,6 +9,8 @@ #include #include #include +#include + #include "importcrec.hpp" #include "importercontext.hpp" @@ -240,6 +242,7 @@ private: std::map mCells; + std::vector mMarkers; }; } diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index 5bec5bd823..d961720a73 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -49,6 +49,13 @@ namespace ESSImport while (esm.isNextSub("FGTN")) esm.getHString(); // fight target? + // unsure at which point between TGTN and CRED + if (esm.isNextSub("AADT")) + { + // occured when a creature was in the middle of its attack, 44 bytes + esm.skipHSub(); + } + // unsure at which point between FGTN and CHRD if (esm.isNextSub("PWPC")) esm.skipHSub(); diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 454690adc0..8c2b8f36d5 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -7,7 +7,8 @@ namespace ESSImport void CellRef::load(ESM::ESMReader &esm) { - esm.getHNT(mRefNum.mIndex, "FRMR"); + // (FRMR subrecord name is already read by the loop in ConvertCell) + esm.getHT(mRefNum.mIndex); // FRMR // this is required since openmw supports more than 255 content files int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24; diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 572489d9a7..12136cdea6 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -213,6 +213,7 @@ namespace ESSImport converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + std::set unknownRecords; for (std::map >::const_iterator it = converters.begin(); it != converters.end(); ++it) @@ -232,7 +233,9 @@ namespace ESSImport } else { - std::cerr << "unknown record " << n.toString() << std::endl; + if (unknownRecords.insert(n.val).second) + std::cerr << "unknown record " << n.toString() << std::endl; + esm.skipRecord(); } } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 4de9af1086..abd1256d7f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -86,35 +86,16 @@ namespace namespace MWGui { - void CustomMarker::save(ESM::ESMWriter &esm) const - { - esm.writeHNT("POSX", mWorldX); - esm.writeHNT("POSY", mWorldY); - mCell.save(esm); - if (!mNote.empty()) - esm.writeHNString("NOTE", mNote); - } - - void CustomMarker::load(ESM::ESMReader &esm) - { - esm.getHNT(mWorldX, "POSX"); - esm.getHNT(mWorldY, "POSY"); - mCell.load(esm); - mNote = esm.getHNOString("NOTE"); - } - - // ------------------------------------------------------ - - void CustomMarkerCollection::addMarker(const CustomMarker &marker, bool triggerEvent) + void CustomMarkerCollection::addMarker(const ESM::CustomMarker &marker, bool triggerEvent) { mMarkers.push_back(marker); if (triggerEvent) eventMarkersChanged(); } - void CustomMarkerCollection::deleteMarker(const CustomMarker &marker) + void CustomMarkerCollection::deleteMarker(const ESM::CustomMarker &marker) { - std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); if (it != mMarkers.end()) mMarkers.erase(it); else @@ -123,9 +104,9 @@ namespace MWGui eventMarkersChanged(); } - void CustomMarkerCollection::updateMarker(const CustomMarker &marker, const std::string &newNote) + void CustomMarkerCollection::updateMarker(const ESM::CustomMarker &marker, const std::string &newNote) { - std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); + std::vector::iterator it = std::find(mMarkers.begin(), mMarkers.end(), marker); if (it != mMarkers.end()) it->mNote = newNote; else @@ -140,12 +121,12 @@ namespace MWGui eventMarkersChanged(); } - std::vector::const_iterator CustomMarkerCollection::begin() const + std::vector::const_iterator CustomMarkerCollection::begin() const { return mMarkers.begin(); } - std::vector::const_iterator CustomMarkerCollection::end() const + std::vector::const_iterator CustomMarkerCollection::end() const { return mMarkers.end(); } @@ -295,9 +276,9 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(*it); mCustomMarkerWidgets.clear(); - for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { - const CustomMarker& marker = *it; + const ESM::CustomMarker& marker = *it; if (marker.mCell.mPaged != !mInterior) continue; @@ -654,7 +635,7 @@ namespace MWGui void MapWindow::onCustomMarkerDoubleClicked(MyGUI::Widget *sender) { - mEditingMarker = *sender->getUserData(); + mEditingMarker = *sender->getUserData(); mEditNoteDialog.setText(mEditingMarker.mNote); mEditNoteDialog.showDeleteButton(true); mEditNoteDialog.setVisible(true); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 118fee0f6c..6840694673 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -7,6 +7,8 @@ #include +#include + namespace MWRender { class GlobalMap; @@ -26,43 +28,25 @@ namespace Loading namespace MWGui { - struct CustomMarker - { - float mWorldX; - float mWorldY; - - ESM::CellId mCell; - - std::string mNote; - - bool operator == (const CustomMarker& other) - { - return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; - } - - void load (ESM::ESMReader& reader); - void save (ESM::ESMWriter& writer) const; - }; - class CustomMarkerCollection { public: - void addMarker(const CustomMarker& marker, bool triggerEvent=true); - void deleteMarker (const CustomMarker& marker); - void updateMarker(const CustomMarker& marker, const std::string& newNote); + void addMarker(const ESM::CustomMarker& marker, bool triggerEvent=true); + void deleteMarker (const ESM::CustomMarker& marker); + void updateMarker(const ESM::CustomMarker& marker, const std::string& newNote); void clear(); size_t size() const; - std::vector::const_iterator begin() const; - std::vector::const_iterator end() const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; EventHandle_Void eventMarkersChanged; private: - std::vector mMarkers; + std::vector mMarkers; }; class LocalMapBase @@ -233,7 +217,7 @@ namespace MWGui MWRender::GlobalMap* mGlobalMapRender; EditNoteDialog mEditNoteDialog; - CustomMarker mEditingMarker; + ESM::CustomMarker mEditingMarker; virtual void onPinToggled(); virtual void onTitleDoubleClicked(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d5a71bc775..5b2bbee906 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1612,7 +1612,7 @@ namespace MWGui writer.endRecord(ESM::REC_ASPL); } - for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) + for (std::vector::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { writer.startRecord(ESM::REC_MARK); (*it).save(writer); @@ -1633,7 +1633,7 @@ namespace MWGui } else if (type == ESM::REC_MARK) { - CustomMarker marker; + ESM::CustomMarker marker; marker.load(reader); mCustomMarkers.addMarker(marker, false); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a3e79758f5..0578a44b00 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -63,7 +63,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util + aisequence magiceffects util custommarkerstate ) add_component_dir (esmterrain diff --git a/components/esm/custommarkerstate.cpp b/components/esm/custommarkerstate.cpp new file mode 100644 index 0000000000..dc81c123d4 --- /dev/null +++ b/components/esm/custommarkerstate.cpp @@ -0,0 +1,26 @@ +#include "custommarkerstate.hpp" + +#include "esmwriter.hpp" +#include "esmreader.hpp" + +namespace ESM +{ + +void CustomMarker::save(ESM::ESMWriter &esm) const +{ + esm.writeHNT("POSX", mWorldX); + esm.writeHNT("POSY", mWorldY); + mCell.save(esm); + if (!mNote.empty()) + esm.writeHNString("NOTE", mNote); +} + +void CustomMarker::load(ESM::ESMReader &esm) +{ + esm.getHNT(mWorldX, "POSX"); + esm.getHNT(mWorldY, "POSY"); + mCell.load(esm); + mNote = esm.getHNOString("NOTE"); +} + +} diff --git a/components/esm/custommarkerstate.hpp b/components/esm/custommarkerstate.hpp new file mode 100644 index 0000000000..fc9286bfe2 --- /dev/null +++ b/components/esm/custommarkerstate.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H +#define OPENMW_ESM_CUSTOMMARKERSTATE_H + +#include "cellid.hpp" + +namespace ESM +{ + +// format 0, saved games only +struct CustomMarker +{ + float mWorldX; + float mWorldY; + + ESM::CellId mCell; + + std::string mNote; + + bool operator == (const CustomMarker& other) + { + return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY; + } + + void load (ESM::ESMReader& reader); + void save (ESM::ESMWriter& writer) const; +}; + +} + +#endif From ad398f0c65d2df8299d22bdb7182c5deb89e71ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 01:06:45 +0100 Subject: [PATCH 12/12] ESSImport: convert kill counter --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.hpp | 29 +++++++++++++++++++++++++++++ apps/essimporter/importer.cpp | 5 ++++- apps/essimporter/importklst.cpp | 22 ++++++++++++++++++++++ apps/essimporter/importklst.hpp | 28 ++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 apps/essimporter/importklst.cpp create mode 100644 apps/essimporter/importklst.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 3d3e0860c1..cf32184545 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -7,6 +7,7 @@ set(ESSIMPORTER_FILES importcellref.cpp importacdt.cpp importinventory.cpp + importklst.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 5f19617177..aee60f2e75 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -15,6 +15,7 @@ #include "importercontext.hpp" #include "importcellref.hpp" +#include "importklst.hpp" #include "convertacdt.hpp" #include "convertnpcc.hpp" @@ -93,6 +94,7 @@ public: mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map empty; + // FIXME: not working? for (std::vector::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; } @@ -245,6 +247,33 @@ private: std::vector mMarkers; }; +class ConvertKLST : public Converter +{ +public: + virtual void read(ESM::ESMReader& esm) + { + KLST klst; + klst.load(esm); + mKillCounter = klst.mKillCounter; + + mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; + } + + virtual void write(ESM::ESMWriter &esm) + { + esm.startRecord(ESM::REC_DCOU); + for (std::map::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) + { + esm.writeHNString("ID__", it->first); + esm.writeHNT ("COUN", it->second); + } + esm.endRecord(ESM::REC_DCOU); + } + +private: + std::map mKillCounter; +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 12136cdea6..4cf41a8619 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -146,7 +146,7 @@ namespace ESSImport { std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; - return; // TODO: try to recover + break; // TODO: try to recover } if (sub.mData != sub2.mData) @@ -177,6 +177,7 @@ namespace ESSImport void Importer::run() { + // construct Ogre::Root to gain access to image codecs Ogre::LogManager logman; Ogre::Root root; @@ -191,6 +192,7 @@ namespace ESSImport const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value; const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; + const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); @@ -201,6 +203,7 @@ namespace ESSImport converters[recREFR] = boost::shared_ptr(new ConvertREFR()); converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); + converters[recKLST] = boost::shared_ptr(new ConvertKLST()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); diff --git a/apps/essimporter/importklst.cpp b/apps/essimporter/importklst.cpp new file mode 100644 index 0000000000..daa1ab0774 --- /dev/null +++ b/apps/essimporter/importklst.cpp @@ -0,0 +1,22 @@ +#include "importklst.hpp" + +#include + +namespace ESSImport +{ + + void KLST::load(ESM::ESMReader &esm) + { + while (esm.isNextSub("KNAM")) + { + std::string refId = esm.getHString(); + int count; + esm.getHNT(count, "CNAM"); + mKillCounter[refId] = count; + } + + mWerewolfKills = 0; + esm.getHNOT(mWerewolfKills, "INTV"); + } + +} diff --git a/apps/essimporter/importklst.hpp b/apps/essimporter/importklst.hpp new file mode 100644 index 0000000000..e333e71509 --- /dev/null +++ b/apps/essimporter/importklst.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESSIMPORT_KLST_H +#define OPENMW_ESSIMPORT_KLST_H + +#include +#include + +namespace ESM +{ + struct ESMReader; +} + +namespace ESSImport +{ + + /// Kill Stats + struct KLST + { + void load(ESM::ESMReader& esm); + + /// RefId, kill count + std::map mKillCounter; + + int mWerewolfKills; + }; + +} + +#endif