diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 632f25f5de..06b6562dee 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -50,7 +50,9 @@ void readVFS(std::unique_ptr&& anArchive, const std::filesystem::p if (isNIF(name)) { // std::cout << "Decoding: " << name << std::endl; - Nif::NIFFile temp_nif(myManager.get(name), archivePath / name); + Nif::NIFFile file(archivePath / name); + Nif::Reader reader(file); + reader.parse(myManager.get(name)); } else if (isBSA(name)) { @@ -120,7 +122,7 @@ int main(int argc, char** argv) if (!parseOptions(argc, argv, files)) return 1; - Nif::NIFFile::setLoadUnsupportedFiles(true); + Nif::Reader::setLoadUnsupportedFiles(true); // std::cout << "Reading Files" << std::endl; for (const auto& path : files) { @@ -129,7 +131,9 @@ int main(int argc, char** argv) if (isNIF(path)) { // std::cout << "Decoding: " << name << std::endl; - Nif::NIFFile temp_nif(Files::openConstrainedFileStream(path), path); + Nif::NIFFile file(path); + Nif::Reader reader(file); + reader.parse(Files::openConstrainedFileStream(path)); } else if (isBSA(path)) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5543133ff4..c8763df743 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -616,7 +616,7 @@ namespace MWRender MWBase::Environment::get().getWindowManager()->setCullMask(mask); NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); - Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); + Nif::Reader::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); mStateUpdater->setFogEnd(mViewDistance); diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 471ae14814..1a961374ef 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -85,6 +85,8 @@ file(GLOB UNITTEST_SRC_FILES fx/technique.cpp esm3/readerscache.cpp + + nifosg/testnifloader.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/nif/node.hpp b/apps/openmw_test_suite/nif/node.hpp new file mode 100644 index 0000000000..7e413d03cd --- /dev/null +++ b/apps/openmw_test_suite/nif/node.hpp @@ -0,0 +1,72 @@ +#ifndef OPENMW_TEST_SUITE_NIF_NODE_H +#define OPENMW_TEST_SUITE_NIF_NODE_H + +#include +#include + +namespace Nif::Testing +{ + inline void init(Transformation& value) + { + value = Transformation::getIdentity(); + } + + inline void init(Extra& value) + { + value.next = ExtraPtr(nullptr); + } + + inline void init(Named& value) + { + value.extra = ExtraPtr(nullptr); + value.extralist = ExtraList(); + value.controller = ControllerPtr(nullptr); + } + + inline void init(Node& value) + { + init(static_cast(value)); + value.flags = 0; + init(value.trafo); + value.hasBounds = false; + value.isBone = false; + } + + inline void init(NiGeometry& value) + { + init(static_cast(value)); + value.data = NiGeometryDataPtr(nullptr); + value.skin = NiSkinInstancePtr(nullptr); + } + + inline void init(NiTriShape& value) + { + init(static_cast(value)); + value.recType = RC_NiTriShape; + } + + inline void init(NiTriStrips& value) + { + init(static_cast(value)); + value.recType = RC_NiTriStrips; + } + + inline void init(NiSkinInstance& value) + { + value.data = NiSkinDataPtr(nullptr); + value.root = NodePtr(nullptr); + } + + inline void init(Controller& value) + { + value.next = ControllerPtr(nullptr); + value.flags = 0; + value.frequency = 0; + value.phase = 0; + value.timeStart = 0; + value.timeStop = 0; + value.target = NamedPtr(nullptr); + } +} + +#endif diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index 1ad51573ea..066da7b607 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -1,3 +1,5 @@ +#include "../nif/node.hpp" + #include #include #include @@ -260,71 +262,9 @@ static bool operator==(const btCollisionShape& lhs, const btCollisionShape& rhs) namespace { using namespace testing; + using namespace Nif::Testing; using NifBullet::BulletNifLoader; - void init(Nif::Transformation& value) - { - value = Nif::Transformation::getIdentity(); - } - - void init(Nif::Extra& value) - { - value.next = Nif::ExtraPtr(nullptr); - } - - void init(Nif::Named& value) - { - value.extra = Nif::ExtraPtr(nullptr); - value.extralist = Nif::ExtraList(); - value.controller = Nif::ControllerPtr(nullptr); - } - - void init(Nif::Node& value) - { - init(static_cast(value)); - value.flags = 0; - init(value.trafo); - value.hasBounds = false; - value.parents.push_back(nullptr); - value.isBone = false; - } - - void init(Nif::NiGeometry& value) - { - init(static_cast(value)); - value.data = Nif::NiGeometryDataPtr(nullptr); - value.skin = Nif::NiSkinInstancePtr(nullptr); - } - - void init(Nif::NiTriShape& value) - { - init(static_cast(value)); - value.recType = Nif::RC_NiTriShape; - } - - void init(Nif::NiTriStrips& value) - { - init(static_cast(value)); - value.recType = Nif::RC_NiTriStrips; - } - - void init(Nif::NiSkinInstance& value) - { - value.data = Nif::NiSkinDataPtr(nullptr); - value.root = Nif::NodePtr(nullptr); - } - - void init(Nif::Controller& value) - { - value.next = Nif::ControllerPtr(nullptr); - value.flags = 0; - value.frequency = 0; - value.phase = 0; - value.timeStart = 0; - value.timeStop = 0; - value.target = Nif::NamedPtr(nullptr); - } - void copy(const btTransform& src, Nif::Transformation& dst) { dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z()); @@ -333,31 +273,9 @@ namespace dst.rotation.mValues[column][row] = src.getBasis().getRow(row)[column]; } - struct NifFileMock : Nif::File - { - MOCK_METHOD(Nif::Record*, getRecord, (std::size_t), (const, override)); - MOCK_METHOD(std::size_t, numRecords, (), (const, override)); - MOCK_METHOD(Nif::Record*, getRoot, (std::size_t), (const, override)); - MOCK_METHOD(std::size_t, numRoots, (), (const, override)); - MOCK_METHOD(std::string, getString, (uint32_t), (const, override)); - MOCK_METHOD(void, setUseSkinning, (bool), (override)); - MOCK_METHOD(bool, getUseSkinning, (), (const, override)); - MOCK_METHOD(std::filesystem::path, getFilename, (), (const, override)); - MOCK_METHOD(std::string, getHash, (), (const, override)); - MOCK_METHOD(unsigned int, getVersion, (), (const, override)); - MOCK_METHOD(unsigned int, getUserVersion, (), (const, override)); - MOCK_METHOD(unsigned int, getBethVersion, (), (const, override)); - }; - - struct RecordMock : Nif::Record - { - MOCK_METHOD(void, read, (Nif::NIFStream * nif), (override)); - }; - struct TestBulletNifLoader : Test { BulletNifLoader mLoader; - const StrictMock mNifFile; Nif::Node mNode; Nif::Node mNode2; Nif::NiNode mNiNode; @@ -412,16 +330,15 @@ namespace = { osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0), osg::Vec3f(0, 1, 0) }; mNiTriStripsData.strips = { { 0, 1, 2, 3 } }; mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriStripsData); - - EXPECT_CALL(mNifFile, getHash()).WillOnce(Return(mHash)); } }; TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -432,10 +349,11 @@ namespace TEST_F(TestBulletNifLoader, should_ignore_nullptr_root) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(nullptr)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(nullptr); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -444,10 +362,11 @@ namespace TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -458,10 +377,11 @@ namespace { mNode.recType = Nif::RC_RootCollisionNode; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -470,10 +390,11 @@ namespace TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -489,10 +410,11 @@ namespace mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -515,10 +437,11 @@ namespace mNode.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNode) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -547,10 +470,11 @@ namespace mNiNode.bounds.box.center = osg::Vec3f(-4, -5, -6); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNode) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -585,10 +509,11 @@ namespace mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -623,10 +548,11 @@ namespace mNiNode.bounds.box.center = osg::Vec3f(-7, -8, -9); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(4, 5, 6); @@ -647,10 +573,11 @@ namespace mNode.bounds.box.extents = osg::Vec3f(1, 2, 3); mNode.bounds.box.center = osg::Vec3f(-1, -2, -3); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -661,10 +588,11 @@ namespace TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriShape); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -682,10 +610,11 @@ namespace mNiTriShape.bounds.box.extents = osg::Vec3f(1, 2, 3); mNiTriShape.bounds.box.center = osg::Vec3f(-1, -2, -3); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriShape); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; expected.mCollisionBox.mExtents = osg::Vec3f(1, 2, 3); @@ -699,10 +628,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -719,10 +649,11 @@ namespace mNiNode2.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); mNiTriShape.parents.push_back(&mNiNode2); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -739,10 +670,11 @@ namespace mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape), Nif::NodePtr(&mNiTriShape2) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); @@ -760,10 +692,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -779,10 +712,11 @@ namespace copy(mTransform, mNiTriShape.trafo); mNiTriShape.trafo.scale = 3; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiTriShape); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -806,10 +740,11 @@ namespace mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); mNiNode.trafo.scale = 4; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -840,10 +775,11 @@ namespace Nif::NodePtr(&mNiTriShape2), })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -876,10 +812,11 @@ namespace mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); mNiNode.trafo.scale = 4; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -912,10 +849,11 @@ namespace })); mNiNode.trafo.scale = 4; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle( @@ -943,11 +881,12 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiTriShape2)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mRoots.push_back(&mNiTriShape2); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -974,10 +913,11 @@ namespace mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); mNiNode.recType = Nif::RC_AvoidNode; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -993,10 +933,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -1011,10 +952,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -1030,10 +972,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1054,10 +997,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1077,10 +1021,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1101,10 +1046,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1131,10 +1077,11 @@ namespace mNiNode.children = Nif::NodeList( std::vector({ Nif::NodePtr(&niTriShape), Nif::NodePtr(&emptyCollisionNode) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1154,10 +1101,11 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); Resource::BulletShape expected; @@ -1177,10 +1125,11 @@ namespace mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiNode2) })); mNiNode.recType = Nif::RC_NiNode; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1194,10 +1143,11 @@ namespace { mNiTriShape.data = Nif::NiGeometryDataPtr(&mNiTriStripsData); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriShape); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1206,10 +1156,11 @@ namespace TEST_F(TestBulletNifLoader, for_tri_strips_root_node_should_return_shape_with_triangle_mesh_shape) { - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1224,10 +1175,11 @@ namespace { mNiTriStrips.data = Nif::NiGeometryDataPtr(&mNiTriShapeData); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1238,10 +1190,11 @@ namespace { mNiTriStripsData.strips.clear(); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1252,10 +1205,11 @@ namespace { mNiTriStripsData.strips.front() = { 0, 1 }; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1269,10 +1223,11 @@ namespace mNiNode.recType = Nif::RC_AvoidNode; mNiTriStripsData.strips.front() = { 0, 1 }; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1285,10 +1240,11 @@ namespace mNiTriStrips.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriStrips) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mHash = mHash; + + const auto result = mLoader.load(file); const Resource::BulletShape expected; @@ -1301,11 +1257,12 @@ namespace mNiTriShape.parents.push_back(&mNiNode); mNiNode.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiTriStrips)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mRoots.push_back(&mNiTriStrips); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles(new btTriangleMesh(false)); triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); @@ -1330,11 +1287,12 @@ namespace mNiNode2.children = Nif::NodeList(std::vector({ Nif::NodePtr(&mNiTriShape) })); mNiNode2.trafo.scale = 3; - EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(2)); - EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); - EXPECT_CALL(mNifFile, getRoot(1)).WillOnce(Return(&mNiNode2)); - EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); - const auto result = mLoader.load(mNifFile); + Nif::NIFFile file("xtest.nif"); + file.mRoots.push_back(&mNiNode); + file.mRoots.push_back(&mNiNode2); + file.mHash = mHash; + + const auto result = mLoader.load(file); std::unique_ptr triangles1(new btTriangleMesh(false)); triangles1->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); diff --git a/apps/openmw_test_suite/nifosg/testnifloader.cpp b/apps/openmw_test_suite/nifosg/testnifloader.cpp new file mode 100644 index 0000000000..5671227f80 --- /dev/null +++ b/apps/openmw_test_suite/nifosg/testnifloader.cpp @@ -0,0 +1,231 @@ +#include "../nif/node.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + using namespace testing; + using namespace NifOsg; + using namespace Nif::Testing; + + struct BaseNifOsgLoaderTest + { + VFS::Manager mVfs{ false }; + Resource::ImageManager mImageManager{ &mVfs }; + const osgDB::ReaderWriter* mReaderWriter = osgDB::Registry::instance()->getReaderWriterForExtension("osgt"); + osg::ref_ptr mOptions = new osgDB::Options; + + BaseNifOsgLoaderTest() + { + SceneUtil::registerSerializers(); + + if (mReaderWriter == nullptr) + throw std::runtime_error("osgt reader writer is not found"); + + mOptions->setPluginStringData("fileType", "Ascii"); + mOptions->setPluginStringData("WriteImageHint", "UseExternal"); + } + + std::string serialize(const osg::Node& node) const + { + std::stringstream stream; + mReaderWriter->writeNode(node, stream, mOptions); + std::string result; + for (std::string line; std::getline(stream, line);) + { + if (line.starts_with('#')) + continue; + boost::trim_right(line); + result += line; + result += '\n'; + } + return result; + } + }; + + struct NifOsgLoaderTest : Test, BaseNifOsgLoaderTest + { + }; + + TEST_F(NifOsgLoaderTest, shouldLoadFileWithDefaultNode) + { + Nif::Node node; + init(node); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&node); + auto result = Loader::load(file, &mImageManager); + EXPECT_EQ(serialize(*result), R"( +osg::Group { + UniqueID 1 + DataVariance STATIC + UserDataContainer TRUE { + osg::DefaultUserDataContainer { + UniqueID 2 + UDC_UserObjects 1 { + osg::StringValueObject { + UniqueID 3 + Name "fileHash" + } + } + } + } + Children 1 { + osg::Group { + UniqueID 4 + DataVariance STATIC + UserDataContainer TRUE { + osg::DefaultUserDataContainer { + UniqueID 5 + UDC_UserObjects 1 { + osg::UIntValueObject { + UniqueID 6 + Name "recIndex" + Value 4294967295 + } + } + } + } + } + } +} +)"); + } + + std::string formatOsgNodeForShaderProperty(std::string_view shaderPrefix) + { + static constexpr const char format[] = R"( +osg::Group { + UniqueID 1 + DataVariance STATIC + UserDataContainer TRUE { + osg::DefaultUserDataContainer { + UniqueID 2 + UDC_UserObjects 1 { + osg::StringValueObject { + UniqueID 3 + Name "fileHash" + } + } + } + } + Children 1 { + osg::Group { + UniqueID 4 + DataVariance STATIC + UserDataContainer TRUE { + osg::DefaultUserDataContainer { + UniqueID 5 + UDC_UserObjects 3 { + osg::UIntValueObject { + UniqueID 6 + Name "recIndex" + Value 4294967295 + } + osg::StringValueObject { + UniqueID 7 + Name "shaderPrefix" + Value "%s" + } + osg::BoolValueObject { + UniqueID 8 + Name "shaderRequired" + Value TRUE + } + } + } + } + StateSet TRUE { + osg::StateSet { + UniqueID 9 + } + } + } + } +} +)"; + return (boost::format(format) % shaderPrefix).str(); + } + + struct ShaderPrefixParams + { + unsigned int mShaderType; + std::string_view mExpectedShaderPrefix; + }; + + struct NifOsgLoaderBSShaderPrefixTest : TestWithParam, BaseNifOsgLoaderTest + { + static constexpr std::array sParams = { + ShaderPrefixParams{ static_cast(Nif::BSShaderType::ShaderType_Default), "nv_default" }, + ShaderPrefixParams{ static_cast(Nif::BSShaderType::ShaderType_NoLighting), "nv_nolighting" }, + ShaderPrefixParams{ static_cast(Nif::BSShaderType::ShaderType_Tile), "nv_default" }, + ShaderPrefixParams{ std::numeric_limits::max(), "nv_default" }, + }; + }; + + TEST_P(NifOsgLoaderBSShaderPrefixTest, shouldAddShaderPrefix) + { + Nif::Node node; + init(node); + Nif::BSShaderPPLightingProperty property; + property.recType = Nif::RC_BSShaderPPLightingProperty; + property.textureSet = nullptr; + property.controller = nullptr; + property.type = GetParam().mShaderType; + node.props.push_back(Nif::RecordPtrT(&property)); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&node); + auto result = Loader::load(file, &mImageManager); + EXPECT_EQ(serialize(*result), formatOsgNodeForShaderProperty(GetParam().mExpectedShaderPrefix)); + } + + INSTANTIATE_TEST_SUITE_P(Params, NifOsgLoaderBSShaderPrefixTest, ValuesIn(NifOsgLoaderBSShaderPrefixTest::sParams)); + + struct NifOsgLoaderBSLightingShaderPrefixTest : TestWithParam, BaseNifOsgLoaderTest + { + static constexpr std::array sParams = { + ShaderPrefixParams{ + static_cast(Nif::BSLightingShaderType::ShaderType_Default), "nv_default" }, + ShaderPrefixParams{ static_cast(Nif::BSLightingShaderType::ShaderType_Cloud), "nv_default" }, + ShaderPrefixParams{ std::numeric_limits::max(), "nv_default" }, + }; + }; + + TEST_P(NifOsgLoaderBSLightingShaderPrefixTest, shouldAddShaderPrefix) + { + Nif::Node node; + init(node); + Nif::BSLightingShaderProperty property; + property.recType = Nif::RC_BSLightingShaderProperty; + property.mTextureSet = nullptr; + property.controller = nullptr; + property.type = GetParam().mShaderType; + node.props.push_back(Nif::RecordPtrT(&property)); + Nif::NIFFile file("test.nif"); + file.mRoots.push_back(&node); + auto result = Loader::load(file, &mImageManager); + EXPECT_EQ(serialize(*result), formatOsgNodeForShaderProperty(GetParam().mExpectedShaderPrefix)); + } + + INSTANTIATE_TEST_SUITE_P( + Params, NifOsgLoaderBSLightingShaderPrefixTest, ValuesIn(NifOsgLoaderBSLightingShaderPrefixTest::sParams)); +} diff --git a/components/nif/base.cpp b/components/nif/base.cpp index 1b221ea6bc..ed440cd96d 100644 --- a/components/nif/base.cpp +++ b/components/nif/base.cpp @@ -19,14 +19,14 @@ namespace Nif if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0)) extra.read(nif); else - extralist.read(nif); + readRecordList(nif, extralist); controller.read(nif); } - void Named::post(NIFFile* nif) + void Named::post(Reader& nif) { extra.post(nif); - extralist.post(nif); + postRecordList(nif, extralist); controller.post(nif); } } diff --git a/components/nif/base.hpp b/components/nif/base.hpp index 30c1d5bc56..2cdbdec77f 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -18,7 +18,7 @@ namespace Nif unsigned int recordSize{ 0u }; void read(NIFStream* nif) override; - void post(NIFFile* nif) override { next.post(nif); } + void post(Reader& nif) override { next.post(nif); } }; struct Controller : public Record @@ -43,7 +43,7 @@ namespace Nif NamedPtr target; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; bool isActive() const { return flags & Flag_Active; } ExtrapolationMode extrapolationMode() const { return static_cast(flags & Mask); } @@ -58,7 +58,7 @@ namespace Nif ControllerPtr controller; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; using NiSequenceStreamHelper = Named; diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index 1430cede78..bacba07d5f 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -42,7 +42,7 @@ namespace Nif /* bool mPersistRenderData = */ nif->getBoolean(); } - void NiSourceTexture::post(NIFFile* nif) + void NiSourceTexture::post(Reader& nif) { Named::post(nif); data.post(nif); @@ -59,7 +59,7 @@ namespace Nif controller.read(nif); } - void NiParticleModifier::post(NIFFile* nif) + void NiParticleModifier::post(Reader& nif) { next.post(nif); controller.post(nif); @@ -78,7 +78,7 @@ namespace Nif data.read(nif); } - void NiParticleColorModifier::post(NIFFile* nif) + void NiParticleColorModifier::post(Reader& nif) { NiParticleModifier::post(nif); data.post(nif); diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 62a240918f..09bab3cbfd 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -62,7 +62,7 @@ namespace Nif unsigned int alpha; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct BSShaderTextureSet : public Record @@ -89,7 +89,7 @@ namespace Nif ControllerPtr controller; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiParticleGrowFade : public NiParticleModifier @@ -105,7 +105,7 @@ namespace Nif NiColorDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiGravity : public NiParticleModifier diff --git a/components/nif/controller.cpp b/components/nif/controller.cpp index aa6d6ab76b..f05c6bcd7e 100644 --- a/components/nif/controller.cpp +++ b/components/nif/controller.cpp @@ -22,7 +22,7 @@ namespace Nif target.read(nif); } - void Controller::post(NIFFile* nif) + void Controller::post(Reader& nif) { Record::post(nif); next.post(nif); @@ -44,7 +44,7 @@ namespace Nif mInterpolator.read(nif); } - void NiSingleInterpController::post(NIFFile* nif) + void NiSingleInterpController::post(Reader& nif) { NiInterpController::post(nif); mInterpolator.post(nif); @@ -104,7 +104,7 @@ namespace Nif nif->getChar(); } - void NiParticleSystemController::post(NIFFile* nif) + void NiParticleSystemController::post(Reader& nif) { Controller::post(nif); emitter.post(nif); @@ -128,7 +128,7 @@ namespace Nif mData.read(nif); } - void NiMaterialColorController::post(NIFFile* nif) + void NiMaterialColorController::post(Reader& nif) { NiPoint3InterpController::post(nif); mData.post(nif); @@ -142,7 +142,7 @@ namespace Nif target.read(nif); } - void NiLookAtController::post(NIFFile* nif) + void NiLookAtController::post(Reader& nif) { Controller::post(nif); target.post(nif); @@ -160,7 +160,7 @@ namespace Nif floatData.read(nif); } - void NiPathController::post(NIFFile* nif) + void NiPathController::post(Reader& nif) { Controller::post(nif); @@ -176,7 +176,7 @@ namespace Nif data.read(nif); } - void NiUVController::post(NIFFile* nif) + void NiUVController::post(Reader& nif) { Controller::post(nif); data.post(nif); @@ -189,7 +189,7 @@ namespace Nif mData.read(nif); } - void NiKeyframeController::post(NIFFile* nif) + void NiKeyframeController::post(Reader& nif) { NiSingleInterpController::post(nif); mData.post(nif); @@ -206,10 +206,10 @@ namespace Nif mExtraTargets = targets; } - void NiMultiTargetTransformController::post(NIFFile* nif) + void NiMultiTargetTransformController::post(Reader& nif) { NiInterpController::post(nif); - mExtraTargets.post(nif); + postRecordList(nif, mExtraTargets); } void NiAlphaController::read(NIFStream* nif) @@ -219,7 +219,7 @@ namespace Nif mData.read(nif); } - void NiAlphaController::post(NIFFile* nif) + void NiAlphaController::post(Reader& nif) { NiFloatInterpController::post(nif); mData.post(nif); @@ -232,7 +232,7 @@ namespace Nif mData.read(nif); } - void NiRollController::post(NIFFile* nif) + void NiRollController::post(Reader& nif) { NiSingleInterpController::post(nif); mData.post(nif); @@ -251,7 +251,7 @@ namespace Nif { if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) { - mInterpolators.read(nif); + readRecordList(nif, mInterpolators); if (nif->getVersion() >= NIFStream::generateVersion(10, 2, 0, 0) && nif->getBethVersion() > 9) { unsigned int numUnknown = nif->getUInt(); @@ -275,11 +275,11 @@ namespace Nif } } - void NiGeomMorpherController::post(NIFFile* nif) + void NiGeomMorpherController::post(Reader& nif) { NiInterpController::post(nif); mData.post(nif); - mInterpolators.post(nif); + postRecordList(nif, mInterpolators); } void NiVisController::read(NIFStream* nif) @@ -289,7 +289,7 @@ namespace Nif mData.read(nif); } - void NiVisController::post(NIFFile* nif) + void NiVisController::post(Reader& nif) { NiBoolInterpController::post(nif); mData.post(nif); @@ -304,13 +304,13 @@ namespace Nif timeStart = nif->getFloat(); mDelta = nif->getFloat(); } - mSources.read(nif); + readRecordList(nif, mSources); } - void NiFlipController::post(NIFFile* nif) + void NiFlipController::post(Reader& nif) { NiFloatInterpController::post(nif); - mSources.post(nif); + postRecordList(nif, mSources); } void bhkBlendController::read(NIFStream* nif) @@ -334,7 +334,7 @@ namespace Nif data.read(nif); } - void NiPoint3Interpolator::post(NIFFile* nif) + void NiPoint3Interpolator::post(Reader& nif) { data.post(nif); } @@ -345,7 +345,7 @@ namespace Nif data.read(nif); } - void NiBoolInterpolator::post(NIFFile* nif) + void NiBoolInterpolator::post(Reader& nif) { data.post(nif); } @@ -356,7 +356,7 @@ namespace Nif data.read(nif); } - void NiFloatInterpolator::post(NIFFile* nif) + void NiFloatInterpolator::post(Reader& nif) { data.post(nif); } @@ -378,7 +378,7 @@ namespace Nif data.read(nif); } - void NiTransformInterpolator::post(NIFFile* nif) + void NiTransformInterpolator::post(Reader& nif) { data.post(nif); } @@ -389,7 +389,7 @@ namespace Nif data.read(nif); } - void NiColorInterpolator::post(NIFFile* nif) + void NiColorInterpolator::post(Reader& nif) { data.post(nif); } diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 4f743c99ba..762033da24 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -44,7 +44,7 @@ namespace Nif NiInterpolatorPtr mInterpolator; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; // Base class for controllers that use a NiFloatInterpolator to animate their target. @@ -116,7 +116,7 @@ namespace Nif NiParticleModifierPtr colliders; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; bool noAutoAdjust() const { return emitFlags & EmitFlag_NoAutoAdjust; } bool emitAtVertex() const { return flags & BSPArrayController_AtVertex; } @@ -129,7 +129,7 @@ namespace Nif unsigned int mTargetColor; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiPathController : public Controller @@ -152,7 +152,7 @@ namespace Nif short followAxis; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiLookAtController : public Controller @@ -161,7 +161,7 @@ namespace Nif unsigned short lookAtFlags{ 0 }; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiUVController : public Controller @@ -170,7 +170,7 @@ namespace Nif unsigned int uvSet; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiKeyframeController : public NiSingleInterpController @@ -178,7 +178,7 @@ namespace Nif NiKeyframeDataPtr mData; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiMultiTargetTransformController : public NiInterpController @@ -186,7 +186,7 @@ namespace Nif NodeList mExtraTargets; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiAlphaController : public NiFloatInterpController @@ -194,7 +194,7 @@ namespace Nif NiFloatDataPtr mData; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiRollController : public NiSingleInterpController @@ -202,7 +202,7 @@ namespace Nif NiFloatDataPtr mData; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiGeomMorpherController : public NiInterpController @@ -214,7 +214,7 @@ namespace Nif std::vector mWeights; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiVisController : public NiBoolInterpController @@ -222,7 +222,7 @@ namespace Nif NiVisDataPtr mData; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiFlipController : public NiFloatInterpController @@ -232,7 +232,7 @@ namespace Nif NiSourceTextureList mSources; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct bhkBlendController : public Controller @@ -255,7 +255,7 @@ namespace Nif osg::Vec3f defaultVal; NiPosDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiBoolInterpolator : public NiInterpolator @@ -263,7 +263,7 @@ namespace Nif char defaultVal; NiBoolDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiFloatInterpolator : public NiInterpolator @@ -271,7 +271,7 @@ namespace Nif float defaultVal; NiFloatDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiTransformInterpolator : public NiInterpolator @@ -282,7 +282,7 @@ namespace Nif NiKeyframeDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiColorInterpolator : public NiInterpolator @@ -290,7 +290,7 @@ namespace Nif osg::Vec4f defaultVal; NiColorDataPtr data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; } // Namespace diff --git a/components/nif/data.cpp b/components/nif/data.cpp index f2d533cbce..7a6ea7b540 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -1,7 +1,10 @@ #include "data.hpp" +#include "exception.hpp" #include "nifkey.hpp" #include "node.hpp" +#include + namespace Nif { void NiSkinInstance::read(NIFStream* nif) @@ -10,28 +13,27 @@ namespace Nif if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 101)) partitions.read(nif); root.read(nif); - bones.read(nif); + readRecordList(nif, bones); } - void NiSkinInstance::post(NIFFile* nif) + void NiSkinInstance::post(Reader& nif) { data.post(nif); partitions.post(nif); root.post(nif); - bones.post(nif); + postRecordList(nif, bones); if (data.empty() || root.empty()) - nif->fail("NiSkinInstance missing root or data"); + throw Nif::Exception("NiSkinInstance missing root or data", nif.getFilename()); - size_t bnum = bones.length(); - if (bnum != data->bones.size()) - nif->fail("Mismatch in NiSkinData bone count"); + if (bones.size() != data->bones.size()) + throw Nif::Exception("Mismatch in NiSkinData bone count", nif.getFilename()); - for (size_t i = 0; i < bnum; i++) + for (auto& bone : bones) { - if (bones[i].empty()) - nif->fail("Oops: Missing bone! Don't know how to handle this."); - bones[i]->setBone(); + if (bone.empty()) + throw Nif::Exception("Oops: Missing bone! Don't know how to handle this.", nif.getFilename()); + bone->setBone(); } } @@ -301,7 +303,7 @@ namespace Nif nif->getUChars(data, numPixels * numFaces); } - void NiPixelData::post(NIFFile* nif) + void NiPixelData::post(Reader& nif) { palette.post(nif); } @@ -357,7 +359,7 @@ namespace Nif } } - void NiSkinData::post(NIFFile* nif) + void NiSkinData::post(Reader& nif) { partitions.post(nif); } @@ -470,7 +472,8 @@ namespace Nif { palette = nif->getString(); if (nif->getUInt() != palette.size()) - nif->file->warn("Failed size check in NiStringPalette"); + Log(Debug::Warning) << "NIFFile Warning: Failed size check in NiStringPalette. File: " + << nif->getFile().getFilename(); } void NiBoolData::read(NIFStream* nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 806bb4b305..2d55e6cbef 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -139,7 +139,7 @@ namespace Nif std::vector data; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiColorData : public Record @@ -169,7 +169,7 @@ namespace Nif NodeList bones; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct BSDismemberSkinInstance : public NiSkinInstance @@ -198,7 +198,7 @@ namespace Nif NiSkinPartitionPtr partitions; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiSkinPartition : public Record diff --git a/components/nif/effect.cpp b/components/nif/effect.cpp index 2d74b38c44..fdd96deeb7 100644 --- a/components/nif/effect.cpp +++ b/components/nif/effect.cpp @@ -60,7 +60,7 @@ namespace Nif nif->skip(2); // Unknown short } - void NiTextureEffect::post(NIFFile* nif) + void NiTextureEffect::post(Reader& nif) { NiDynamicEffect::post(nif); texture.post(nif); diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index 7efaff62c9..ea9e21f003 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -86,7 +86,7 @@ namespace Nif CoordGenType coordGenType; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; bool wrapT() const { return clamp & 1; } bool wrapS() const { return (clamp >> 1) & 1; } diff --git a/components/nif/exception.hpp b/components/nif/exception.hpp new file mode 100644 index 0000000000..15f0e76d70 --- /dev/null +++ b/components/nif/exception.hpp @@ -0,0 +1,21 @@ +#ifndef OPENMW_COMPONENTS_NIF_EXCEPTION_HPP +#define OPENMW_COMPONENTS_NIF_EXCEPTION_HPP + +#include +#include +#include + +#include + +namespace Nif +{ + struct Exception : std::runtime_error + { + explicit Exception(const std::string& message, const std::filesystem::path& path) + : std::runtime_error("NIFFile Error: " + message + " when reading " + Files::pathToUnicodeString(path)) + { + } + }; +} + +#endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 8aa1000a6d..15dc99701e 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -15,6 +15,7 @@ #include "controller.hpp" #include "data.hpp" #include "effect.hpp" +#include "exception.hpp" #include "extra.hpp" #include "physics.hpp" #include "property.hpp" @@ -22,11 +23,16 @@ namespace Nif { - /// Open a NIF stream. The name is used for error messages. - NIFFile::NIFFile(Files::IStreamPtr&& stream, const std::filesystem::path& name) - : filename(name) + Reader::Reader(NIFFile& file) + : ver(file.mVersion) + , userVer(file.mUserVersion) + , bethVer(file.mBethVersion) + , filename(file.mPath) + , hash(file.mHash) + , records(file.mRecords) + , roots(file.mRoots) + , mUseSkinning(file.mUseSkinning) { - parse(std::move(stream)); } template @@ -172,7 +178,7 @@ namespace Nif /// Make the factory map used for parsing the file static const std::map factories = makeFactory(); - std::string NIFFile::printVersion(unsigned int version) + std::string Reader::printVersion(unsigned int version) { int major = (version >> 24) & 0xFF; int minor = (version >> 16) & 0xFF; @@ -184,12 +190,12 @@ namespace Nif return stream.str(); } - void NIFFile::parse(Files::IStreamPtr&& stream) + void Reader::parse(Files::IStreamPtr&& stream) { const std::array fileHash = Files::getHash(filename, *stream); hash.append(reinterpret_cast(fileHash.data()), fileHash.size() * sizeof(std::uint64_t)); - NIFStream nif(this, std::move(stream)); + NIFStream nif(*this, std::move(stream)); // Check the header string std::string head = nif.getVersionString(); @@ -200,7 +206,7 @@ namespace Nif const bool supportedHeader = std::any_of(verStrings.begin(), verStrings.end(), [&](const std::string& verString) { return head.compare(0, verString.size(), verString) == 0; }); if (!supportedHeader) - fail("Invalid NIF header: " + head); + throw Nif::Exception("Invalid NIF header: " + head, filename); // Get BCD version ver = nif.getUInt(); @@ -208,15 +214,16 @@ namespace Nif // It's not used by Morrowind assets but Morrowind supports it. static const std::array supportedVers = { NIFStream::generateVersion(4, 0, 0, 0), - VER_MW, + NIFFile::VER_MW, }; const bool supportedVersion = std::find(supportedVers.begin(), supportedVers.end(), ver) != supportedVers.end(); if (!supportedVersion) { if (sLoadUnsupportedFiles) - warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!"); + Log(Debug::Warning) << " NIFFile Warning: Unsupported NIF version: " << printVersion(ver) + << ". Proceed with caution! File: " << filename; else - fail("Unsupported NIF version: " + printVersion(ver)); + throw Nif::Exception("Unsupported NIF version: " + printVersion(ver), filename); } // NIF data endianness @@ -224,7 +231,7 @@ namespace Nif { unsigned char endianness = nif.getChar(); if (endianness == 0) - fail("Big endian NIF files are unsupported"); + throw Nif::Exception("Big endian NIF files are unsupported", filename); } // User version @@ -237,19 +244,19 @@ namespace Nif // Bethesda stream header // It contains Bethesda format version and (useless) export information - if (ver == VER_OB_OLD + if (ver == NIFFile::VER_OB_OLD || (userVer >= 3 - && ((ver == VER_OB || ver == VER_BGS) + && ((ver == NIFFile::VER_OB || ver == NIFFile::VER_BGS) || (ver >= NIFStream::generateVersion(10, 1, 0, 0) && ver <= NIFStream::generateVersion(20, 0, 0, 4) && userVer <= 11)))) { bethVer = nif.getUInt(); nif.getExportString(); // Author - if (bethVer > BETHVER_FO4) + if (bethVer > NIFFile::BETHVER_FO4) nif.getUInt(); // Unknown nif.getExportString(); // Process script nif.getExportString(); // Export script - if (bethVer == BETHVER_FO4) + if (bethVer == NIFFile::BETHVER_FO4) nif.getExportString(); // Max file path } @@ -296,24 +303,19 @@ namespace Nif { std::stringstream error; error << "Record number " << i << " out of " << recNum << " is blank."; - fail(error.str()); + throw Nif::Exception(error.str(), filename); } // Record separator. Some Havok records in Oblivion do not have it. if (hasRecordSeparators && rec.compare(0, 3, "bhk")) - { if (nif.getInt()) - { - std::stringstream warning; - warning << "Record number " << i << " out of " << recNum << " is preceded by a non-zero separator."; - warn(warning.str()); - } - } + Log(Debug::Warning) << "NIFFile Warning: Record number " << i << " out of " << recNum + << " is preceded by a non-zero separator. File: " << filename; const auto entry = factories.find(rec); if (entry == factories.end()) - fail("Unknown record type " + rec); + throw Nif::Exception("Unknown record type " + rec, filename); r = entry->second(); @@ -343,45 +345,31 @@ namespace Nif else { roots[i] = nullptr; - warn("Root " + std::to_string(i + 1) + " does not point to a record: index " + std::to_string(idx)); + Log(Debug::Warning) << "NIFFile Warning: Root " << i + 1 << " does not point to a record: index " << idx + << ". File: " << filename; } } // Once parsing is done, do post-processing. for (const auto& record : records) - record->post(this); + record->post(*this); } - void NIFFile::setUseSkinning(bool skinning) + void Reader::setUseSkinning(bool skinning) { mUseSkinning = skinning; } - bool NIFFile::getUseSkinning() const - { - return mUseSkinning; - } + std::atomic_bool Reader::sLoadUnsupportedFiles = false; - std::atomic_bool NIFFile::sLoadUnsupportedFiles = false; - - void NIFFile::setLoadUnsupportedFiles(bool load) + void Reader::setLoadUnsupportedFiles(bool load) { sLoadUnsupportedFiles = load; } - void NIFFile::warn(const std::string& msg) const + std::string Reader::getString(std::uint32_t index) const { - Log(Debug::Warning) << " NIFFile Warning: " << msg << "\nFile: " << filename; - } - - [[noreturn]] void NIFFile::fail(const std::string& msg) const - { - throw std::runtime_error(" NIFFile Error: " + msg + "\nFile: " + Files::pathToUnicodeString(filename)); - } - - std::string NIFFile::getString(uint32_t index) const - { - if (index == std::numeric_limits::max()) + if (index == std::numeric_limits::max()) return std::string(); return strings.at(index); } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 21b0fa6892..5f5f112eaf 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -14,72 +14,8 @@ namespace Nif { - struct File + struct NIFFile { - virtual ~File() = default; - - virtual Record* getRecord(size_t index) const = 0; - - virtual size_t numRecords() const = 0; - - virtual Record* getRoot(size_t index = 0) const = 0; - - virtual size_t numRoots() const = 0; - - virtual std::string getString(uint32_t index) const = 0; - - virtual void setUseSkinning(bool skinning) = 0; - - virtual bool getUseSkinning() const = 0; - - virtual std::filesystem::path getFilename() const = 0; - - virtual std::string getHash() const = 0; - - virtual unsigned int getVersion() const = 0; - - virtual unsigned int getUserVersion() const = 0; - - virtual unsigned int getBethVersion() const = 0; - }; - - class NIFFile final : public File - { - /// File version, user version, Bethesda version - unsigned int ver = 0; - unsigned int userVer = 0; - unsigned int bethVer = 0; - - /// File name, used for error messages and opening the file - std::filesystem::path filename; - std::string hash; - - /// Record list - std::vector> records; - - /// Root list. This is a select portion of the pointers from records - std::vector roots; - - /// String table - std::vector strings; - - bool mUseSkinning = false; - - static std::atomic_bool sLoadUnsupportedFiles; - - /// Parse the file - void parse(Files::IStreamPtr&& stream); - - /// Get the file's version in a human readable form - ///\returns A string containing a human readable NIF version number - std::string printVersion(unsigned int version); - - /// Private Copy Constructor - NIFFile(NIFFile const&); - ///\overload - void operator=(NIFFile const&); - - public: // For generic versions NIFStream::generateVersion() is used instead enum NIFVersion { @@ -94,51 +30,119 @@ namespace Nif BETHVER_FO4 = 130 // Fallout 4 }; - /// Used if file parsing fails - [[noreturn]] void fail(const std::string& msg) const; + /// File version, user version, Bethesda version + unsigned int mVersion = 0; + unsigned int mUserVersion = 0; + unsigned int mBethVersion = 0; - /// Used when something goes wrong, but not catastrophically so - void warn(const std::string& msg) const; + /// File name, used for error messages and opening the file + std::filesystem::path mPath; + std::string mHash; - /// Open a NIF stream. The name is used for error messages. - NIFFile(Files::IStreamPtr&& stream, const std::filesystem::path& name); + /// Record list + std::vector> mRecords; - /// Get a given record - Record* getRecord(size_t index) const override { return records.at(index).get(); } - /// Number of records - size_t numRecords() const override { return records.size(); } + /// Root list. This is a select portion of the pointers from records + std::vector mRoots; + + bool mUseSkinning = false; + + explicit NIFFile(const std::filesystem::path& path) + : mPath(path) + { + } + }; + + class FileView + { + public: + FileView(const NIFFile& file) + : mFile(&file) + { + } /// Get a given root - Record* getRoot(size_t index = 0) const override - { - Record* res = roots.at(index); - return res; - } + const Record* getRoot(std::size_t index) const { return mFile->mRoots.at(index); } + /// Number of roots - size_t numRoots() const override { return roots.size(); } + std::size_t numRoots() const { return mFile->mRoots.size(); } + + /// Get the name of the file + const std::filesystem::path& getFilename() const { return mFile->mPath; } + + const std::string& getHash() const { return mFile->mHash; } + + /// Get the version of the NIF format used + unsigned int getVersion() const { return mFile->mVersion; } + + /// Get the user version of the NIF format used + unsigned int getUserVersion() const { return mFile->mUserVersion; } + + /// Get the Bethesda version of the NIF format used + unsigned int getBethVersion() const { return mFile->mBethVersion; } + + bool getUseSkinning() const { return mFile->mUseSkinning; } + + private: + const NIFFile* mFile; + }; + + class Reader + { + /// File version, user version, Bethesda version + unsigned int& ver; + unsigned int& userVer; + unsigned int& bethVer; + + /// File name, used for error messages and opening the file + std::filesystem::path& filename; + std::string& hash; + + /// Record list + std::vector>& records; + + /// Root list. This is a select portion of the pointers from records + std::vector& roots; + + /// String table + std::vector strings; + + bool& mUseSkinning; + + static std::atomic_bool sLoadUnsupportedFiles; + + /// Get the file's version in a human readable form + ///\returns A string containing a human readable NIF version number + std::string printVersion(unsigned int version); + + public: + /// Open a NIF stream. The name is used for error messages. + explicit Reader(NIFFile& file); + + /// Parse the file + void parse(Files::IStreamPtr&& stream); + + /// Get a given record + Record* getRecord(size_t index) const { return records.at(index).get(); } /// Get a given string from the file's string table - std::string getString(uint32_t index) const override; + std::string getString(uint32_t index) const; /// Set whether there is skinning contained in this NIF file. /// @note This is just a hint for users of the NIF file and has no effect on the loading procedure. - void setUseSkinning(bool skinning) override; - - bool getUseSkinning() const override; + void setUseSkinning(bool skinning); /// Get the name of the file - std::filesystem::path getFilename() const override { return filename; } - - std::string getHash() const override { return hash; } + std::filesystem::path getFilename() const { return filename; } /// Get the version of the NIF format used - unsigned int getVersion() const override { return ver; } + unsigned int getVersion() const { return ver; } /// Get the user version of the NIF format used - unsigned int getUserVersion() const override { return userVer; } + unsigned int getUserVersion() const { return userVer; } /// Get the Bethesda version of the NIF format used - unsigned int getBethVersion() const override { return bethVer; } + unsigned int getBethVersion() const { return bethVer; } static void setLoadUnsupportedFiles(bool load); }; diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index eda98eed76..8f0e54de0d 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -5,6 +5,7 @@ #include +#include "exception.hpp" #include "niffile.hpp" #include "nifstream.hpp" @@ -112,7 +113,8 @@ namespace Nif } else if (count != 0) { - nif->file->fail("Unhandled interpolation type: " + std::to_string(mInterpolationType)); + throw Nif::Exception("Unhandled interpolation type: " + std::to_string(mInterpolationType), + nif->getFile().getFilename()); } } diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 7ded5e8edf..2bd57d1633 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -34,20 +34,20 @@ namespace Nif /// Read in a string, either from the string table using the index or from the stream using the specified length std::string NIFStream::getString() { - return getVersion() < generateVersion(20, 1, 0, 1) ? getSizedString() : file->getString(getUInt()); + return getVersion() < generateVersion(20, 1, 0, 1) ? getSizedString() : file.getString(getUInt()); } // Convenience utility functions: get the versions of the currently read file unsigned int NIFStream::getVersion() const { - return file->getVersion(); + return file.getVersion(); } unsigned int NIFStream::getUserVersion() const { - return file->getBethVersion(); + return file.getBethVersion(); } unsigned int NIFStream::getBethVersion() const { - return file->getBethVersion(); + return file.getBethVersion(); } } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index a58588fad9..39f8ad0a4e 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -22,7 +22,7 @@ namespace Nif { - class NIFFile; + class Reader; template inline void readLittleEndianBufferOfType(Files::IStreamPtr& pIStream, T* dest) @@ -59,18 +59,20 @@ namespace Nif class NIFStream { + const Reader& file; + /// Input stream Files::IStreamPtr inp; public: - NIFFile* const file; - - NIFStream(NIFFile* file, Files::IStreamPtr&& inp) - : inp(std::move(inp)) - , file(file) + explicit NIFStream(const Reader& file, Files::IStreamPtr&& inp) + : file(file) + , inp(std::move(inp)) { } + const Reader& getFile() const { return file; } + void skip(size_t size) { inp->ignore(size); } char getChar() { return readLittleEndianType(inp); } diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 8c604a59fa..66065aec32 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -3,6 +3,7 @@ #include #include "data.hpp" +#include "exception.hpp" #include "physics.hpp" #include "property.hpp" @@ -68,7 +69,8 @@ namespace Nif } default: { - nif->file->fail("Unhandled NiBoundingVolume type: " + std::to_string(type)); + throw Nif::Exception( + "Unhandled NiBoundingVolume type: " + std::to_string(type), nif->getFile().getFilename()); } } } @@ -82,7 +84,7 @@ namespace Nif if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) velocity = nif->getVector3(); if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3) - props.read(nif); + readRecordList(nif, props); if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0)) hasBounds = nif->getBoolean(); @@ -97,10 +99,10 @@ namespace Nif isBone = false; } - void Node::post(NIFFile* nif) + void Node::post(Reader& nif) { Named::post(nif); - props.post(nif); + postRecordList(nif, props); collision.post(nif); } @@ -112,9 +114,9 @@ namespace Nif void NiNode::read(NIFStream* nif) { Node::read(nif); - children.read(nif); + readRecordList(nif, children); if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4) - effects.read(nif); + readRecordList(nif, effects); // Discard transformations for the root node, otherwise some meshes // occasionally get wrong orientation. Only for NiNode-s for now, but @@ -126,17 +128,17 @@ namespace Nif } } - void NiNode::post(NIFFile* nif) + void NiNode::post(Reader& nif) { Node::post(nif); - children.post(nif); - effects.post(nif); + postRecordList(nif, children); + postRecordList(nif, effects); - for (size_t i = 0; i < children.length(); i++) + for (auto& child : children) { // Why would a unique list of children contain empty refs? - if (!children[i].empty()) - children[i]->parents.push_back(this); + if (!child.empty()) + child->parents.push_back(this); } } @@ -174,7 +176,7 @@ namespace Nif } } - void NiGeometry::post(NIFFile* nif) + void NiGeometry::post(Reader& nif) { Node::post(nif); data.post(nif); @@ -182,7 +184,7 @@ namespace Nif shaderprop.post(nif); alphaprop.post(nif); if (recType != RC_NiParticles && !skin.empty()) - nif->setUseSkinning(true); + nif.setUseSkinning(true); } void BSLODTriShape::read(NIFStream* nif) @@ -269,7 +271,7 @@ namespace Nif mSubSorter.read(nif); } - void NiSortAdjustNode::post(NIFFile* nif) + void NiSortAdjustNode::post(Reader& nif) { NiNode::post(nif); mSubSorter.post(nif); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 658f6b75e0..f41913cbe3 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -94,7 +94,7 @@ namespace Nif NiCollisionObjectPtr collision; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; // Parent node, or nullptr for the root node. As far as I'm aware, only // NiNodes (or types derived from NiNodes) can be parents. @@ -126,7 +126,7 @@ namespace Nif }; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiGeometry : Node @@ -154,7 +154,7 @@ namespace Nif NiAlphaPropertyPtr alphaprop; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiTriShape : NiGeometry @@ -263,7 +263,7 @@ namespace Nif NiAccumulatorPtr mSubSorter; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiBillboardNode : NiNode diff --git a/components/nif/physics.cpp b/components/nif/physics.cpp index 04b3f48c00..9b58585c36 100644 --- a/components/nif/physics.cpp +++ b/components/nif/physics.cpp @@ -165,7 +165,7 @@ namespace Nif mWorldObjectInfo.read(nif); } - void bhkWorldObject::post(NIFFile* nif) + void bhkWorldObject::post(Reader& nif) { mShape.post(nif); } @@ -181,7 +181,7 @@ namespace Nif mShape.read(nif); } - void bhkBvTreeShape::post(NIFFile* nif) + void bhkBvTreeShape::post(Reader& nif) { mShape.post(nif); } @@ -202,14 +202,14 @@ namespace Nif mGrowBy = nif->getUInt(); if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) mScale = nif->getVector4(); - mData.read(nif); + readRecordList(nif, mData); unsigned int numFilters = nif->getUInt(); nif->getUInts(mFilters, numFilters); } - void bhkNiTriStripsShape::post(NIFFile* nif) + void bhkNiTriStripsShape::post(Reader& nif) { - mData.post(nif); + postRecordList(nif, mData); } void bhkPackedNiTriStripsShape::read(NIFStream* nif) @@ -229,7 +229,7 @@ namespace Nif mData.read(nif); } - void bhkPackedNiTriStripsShape::post(NIFFile* nif) + void bhkPackedNiTriStripsShape::post(Reader& nif) { mData.post(nif); } @@ -301,7 +301,7 @@ namespace Nif void bhkListShape::read(NIFStream* nif) { - mSubshapes.read(nif); + readRecordList(nif, mSubshapes); mHavokMaterial.read(nif); mChildShapeProperty.read(nif); mChildFilterProperty.read(nif); @@ -315,7 +315,7 @@ namespace Nif { bhkEntity::read(nif); mInfo.read(nif); - mConstraints.read(nif); + readRecordList(nif, mConstraints); if (nif->getBethVersion() < 76) mBodyFlags = nif->getUInt(); else diff --git a/components/nif/physics.hpp b/components/nif/physics.hpp index ef4df5899a..fac896b5ca 100644 --- a/components/nif/physics.hpp +++ b/components/nif/physics.hpp @@ -17,7 +17,7 @@ namespace Nif { class NIFStream; - class NIFFile; + struct NIFFile; /// Non-record data types @@ -204,7 +204,7 @@ namespace Nif NodePtr mTarget; void read(NIFStream* nif) override { mTarget.read(nif); } - void post(NIFFile* nif) override { mTarget.post(nif); } + void post(Reader& nif) override { mTarget.post(nif); } }; // Bethesda Havok-specific collision object @@ -214,7 +214,7 @@ namespace Nif bhkWorldObjectPtr mBody; void read(NIFStream* nif) override; - void post(NIFFile* nif) override + void post(Reader& nif) override { NiCollisionObject::post(nif); mBody.post(nif); @@ -228,7 +228,7 @@ namespace Nif HavokFilter mHavokFilter; bhkWorldObjectCInfo mWorldObjectInfo; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; // Abstract @@ -244,7 +244,7 @@ namespace Nif { bhkShapePtr mShape; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; // bhkBvTreeShape with Havok MOPP code @@ -265,7 +265,7 @@ namespace Nif NiTriStripsDataList mData; std::vector mFilters; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; // Bethesda packed triangle strip-based Havok shape collection @@ -278,7 +278,7 @@ namespace Nif hkPackedNiTriStripsDataPtr mData; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; // bhkPackedNiTriStripsShape data block diff --git a/components/nif/property.cpp b/components/nif/property.cpp index be1c59297f..c0fc21fffa 100644 --- a/components/nif/property.cpp +++ b/components/nif/property.cpp @@ -47,7 +47,7 @@ namespace Nif } } - void NiTexturingProperty::Texture::post(NIFFile* nif) + void NiTexturingProperty::Texture::post(Reader& nif) { texture.post(nif); } @@ -92,7 +92,7 @@ namespace Nif } } - void NiTexturingProperty::post(NIFFile* nif) + void NiTexturingProperty::post(Reader& nif) { Property::post(nif); for (size_t i = 0; i < textures.size(); i++) @@ -134,7 +134,7 @@ namespace Nif parallax.scale = nif->getFloat(); } - void BSShaderPPLightingProperty::post(NIFFile* nif) + void BSShaderPPLightingProperty::post(Reader& nif) { BSShaderLightingProperty::post(nif); textureSet.post(nif); @@ -198,7 +198,7 @@ namespace Nif } } - void BSLightingShaderProperty::post(NIFFile* nif) + void BSLightingShaderProperty::post(Reader& nif) { BSShaderProperty::post(nif); mTextureSet.post(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 5c1eab37e0..74b2711709 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -53,7 +53,7 @@ namespace Nif unsigned int clamp, uvSet; void read(NIFStream* nif); - void post(NIFFile* nif); + void post(Reader& nif); bool wrapT() const { return clamp & 1; } bool wrapS() const { return (clamp >> 1) & 1; } @@ -97,7 +97,7 @@ namespace Nif osg::Vec4f bumpMapMatrix; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiFogProperty : public Property @@ -166,7 +166,7 @@ namespace Nif ParallaxSettings parallax; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct BSShaderNoLightingProperty : public BSShaderLightingProperty @@ -212,7 +212,7 @@ namespace Nif float mEmissiveMult, mSpecStrength; void read(NIFStream* nif) override; - void post(NIFFile* nif) override; + void post(Reader& nif) override; }; struct NiDitherProperty : public Property diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 397faa03cb..8584ab8419 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -29,7 +29,7 @@ namespace Nif { - class NIFFile; + class Reader; class NIFStream; enum RecordType @@ -164,7 +164,7 @@ namespace Nif virtual void read(NIFStream* nif) = 0; /// Does post-processing, after the entire tree is loaded - virtual void post(NIFFile* nif) {} + virtual void post(Reader& nif) {} virtual ~Record() {} }; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 6982aa3b3f..a23ada3eba 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -44,13 +44,13 @@ namespace Nif } /// Resolve index to pointer - void post(NIFFile* nif) + void post(Reader& nif) { if (index < 0) ptr = nullptr; else { - Record* r = nif->getRecord(index); + Record* r = nif.getRecord(index); // And cast it ptr = dynamic_cast(r); assert(ptr != nullptr); @@ -85,39 +85,28 @@ namespace Nif implementation. */ template - class RecordListT + using RecordListT = std::vector>; + + template + void readRecordList(NIFStream* nif, RecordListT& list) { - typedef RecordPtrT Ptr; - std::vector list; + const int length = nif->getInt(); - public: - RecordListT() = default; + if (length < 0) + throw std::runtime_error("Negative NIF record list length: " + std::to_string(length)); - RecordListT(std::vector list) - : list(std::move(list)) - { - } + list.resize(static_cast(length)); - void read(NIFStream* nif) - { - int len = nif->getInt(); - list.resize(len); + for (auto& value : list) + value.read(nif); + } - for (size_t i = 0; i < list.size(); i++) - list[i].read(nif); - } - - void post(NIFFile* nif) - { - for (size_t i = 0; i < list.size(); i++) - list[i].post(nif); - } - - const Ptr& operator[](size_t index) const { return list.at(index); } - Ptr& operator[](size_t index) { return list.at(index); } - - size_t length() const { return list.size(); } - }; + template + void postRecordList(Reader& nif, RecordListT& list) + { + for (auto& value : list) + value.post(nif); + } struct Node; struct Extra; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 325903ad0e..fa3a8def6a 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -160,7 +160,7 @@ namespace namespace NifBullet { - osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) + osg::ref_ptr BulletNifLoader::load(Nif::FileView nif) { mShape = new Resource::BulletShape; @@ -284,14 +284,9 @@ namespace NifBullet if (const Nif::NiNode* ninode = dynamic_cast(&node)) { const Nif::NodeList& list = ninode->children; - for (size_t i = 0; i < list.length(); i++) - { - if (!list[i].empty()) - { - if (findBoundingBox(list[i].get(), filename)) - return true; - } - } + for (const auto& child : list) + if (!child.empty() && findBoundingBox(child.get(), filename)) + return true; } return false; } @@ -300,12 +295,11 @@ namespace NifBullet { if (const Nif::NiNode* ninode = dynamic_cast(&rootNode)) { - const Nif::NodeList& list = ninode->children; - for (size_t i = 0; i < list.length(); i++) + for (const auto& child : ninode->children) { - if (list[i].empty()) + if (child.empty()) continue; - if (list[i].getPtr()->recType == Nif::RC_RootCollisionNode) + if (child.getPtr()->recType == Nif::RC_RootCollisionNode) return true; } } @@ -316,17 +310,16 @@ namespace NifBullet { if (const Nif::NiNode* ninode = dynamic_cast(&rootNode)) { - const Nif::NodeList& list = ninode->children; - for (size_t i = 0; i < list.length(); i++) + for (const auto& child : ninode->children) { - if (list[i].empty()) + if (child.empty()) continue; - const Nif::Node* childNode = list[i].getPtr(); + const Nif::Node* childNode = child.getPtr(); if (childNode->recType != Nif::RC_RootCollisionNode) continue; const Nif::NiNode* niChildnode = static_cast(childNode); // RootCollisionNode is always a NiNode - if (childNode->hasBounds || niChildnode->children.length() > 0) + if (childNode->hasBounds || niChildnode->children.size() > 0) return false; } } @@ -412,13 +405,13 @@ namespace NifBullet { const Nif::NodeList& list = ninode->children; const Nif::Parent currentParent{ *ninode, parent }; - for (size_t i = 0; i < list.length(); i++) + for (const auto& child : list) { - if (list[i].empty()) + if (child.empty()) continue; - assert(std::find(list[i]->parents.begin(), list[i]->parents.end(), ninode) != list[i]->parents.end()); - handleNode(fileName, list[i].get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated, + assert(std::find(child->parents.begin(), child->parents.end(), ninode) != child->parents.end()); + handleNode(fileName, child.get(), ¤tParent, flags, isCollisionNode, isAnimated, autogenerated, avoid, visualCollisionType); } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index a28a27376a..947f93eadf 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -48,7 +48,7 @@ namespace NifBullet abort(); } - osg::ref_ptr load(const Nif::File& file); + osg::ref_ptr load(Nif::FileView file); private: bool findBoundingBox(const Nif::Node& node, const std::string& filename); diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index c2102a5345..ce5485ac53 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -199,7 +199,7 @@ namespace NifOsg GeomMorpherController::GeomMorpherController(const Nif::NiGeomMorpherController* ctrl) { - if (ctrl->mInterpolators.length() == 0) + if (ctrl->mInterpolators.size() == 0) { if (!ctrl->mData.empty()) { @@ -209,10 +209,10 @@ namespace NifOsg return; } - mKeyFrames.resize(ctrl->mInterpolators.length()); + mKeyFrames.resize(ctrl->mInterpolators.size()); mWeights = ctrl->mWeights; - for (size_t i = 0; i < ctrl->mInterpolators.length(); ++i) + for (std::size_t i = 0, n = ctrl->mInterpolators.size(); i < n; ++i) { if (!ctrl->mInterpolators[i].empty() && ctrl->mInterpolators[i]->recType == Nif::RC_NiFloatInterpolator) { diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 55be6a1dc3..2ad7a7ef09 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -75,9 +76,9 @@ namespace if (const Nif::NiNode* ninode = dynamic_cast(node)) { outIndices.push_back(ninode->recIndex); - for (unsigned int i = 0; i < ninode->children.length(); ++i) - if (!ninode->children[i].empty()) - getAllNiNodes(ninode->children[i].getPtr(), outIndices); + for (const auto& child : ninode->children) + if (!child.empty()) + getAllNiNodes(child.getPtr(), outIndices); } } @@ -101,18 +102,17 @@ namespace { if (parent != nullptr) collectDrawableProperties(&parent->mNiNode, parent->mParent, out); - const Nif::PropertyList& props = nifNode->props; - for (size_t i = 0; i < props.length(); ++i) + for (const auto& property : nifNode->props) { - if (!props[i].empty()) + if (!property.empty()) { - switch (props[i]->recType) + switch (property->recType) { case Nif::RC_NiMaterialProperty: case Nif::RC_NiVertexColorProperty: case Nif::RC_NiSpecularProperty: case Nif::RC_NiAlphaProperty: - out.push_back(props[i].getPtr()); + out.push_back(property.getPtr()); break; default: break; @@ -249,13 +249,13 @@ namespace NifOsg // This is used to queue emitters that weren't attached to their node yet. std::vector>> mEmitterQueue; - void loadKf(Nif::NIFFilePtr nif, SceneUtil::KeyframeHolder& target) const + void loadKf(Nif::FileView nif, SceneUtil::KeyframeHolder& target) const { const Nif::NiSequenceStreamHelper* seq = nullptr; - const size_t numRoots = nif->numRoots(); + const size_t numRoots = nif.numRoots(); for (size_t i = 0; i < numRoots; ++i) { - const Nif::Record* r = nif->getRoot(i); + const Nif::Record* r = nif.getRoot(i); if (r && r->recType == Nif::RC_NiSequenceStreamHelper) { seq = static_cast(r); @@ -265,15 +265,17 @@ namespace NifOsg if (!seq) { - nif->warn("Found no NiSequenceStreamHelper root record"); + Log(Debug::Warning) << "NIFFile Warning: Found no NiSequenceStreamHelper root record. File: " + << nif.getFilename(); return; } Nif::ExtraPtr extra = seq->extra; if (extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) { - nif->warn("First extra data was not a NiTextKeyExtraData, but a " - + (extra.empty() ? std::string("nil") : extra->recName) + "."); + Log(Debug::Warning) << "NIFFile Warning: First extra data was not a NiTextKeyExtraData, but a " + << (extra.empty() ? std::string_view("nil") : std::string_view(extra->recName)) + << ". File: " << nif.getFilename(); return; } @@ -285,7 +287,8 @@ namespace NifOsg { if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) { - nif->warn("Unexpected extra data " + extra->recName + " with controller " + ctrl->recName); + Log(Debug::Warning) << "NIFFile Warning: Unexpected extra data " << extra->recName + << " with controller " << ctrl->recName << ". File: " << nif.getFilename(); continue; } @@ -310,17 +313,17 @@ namespace NifOsg if (!target.mKeyframeControllers.emplace(strdata->string, callback).second) Log(Debug::Verbose) << "Controller " << strdata->string << " present more than once in " - << nif->getFilename() << ", ignoring later version"; + << nif.getFilename() << ", ignoring later version"; } } - osg::ref_ptr load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager) + osg::ref_ptr load(Nif::FileView nif, Resource::ImageManager* imageManager) { - const size_t numRoots = nif->numRoots(); + const size_t numRoots = nif.numRoots(); std::vector roots; for (size_t i = 0; i < numRoots; ++i) { - const Nif::Record* r = nif->getRoot(i); + const Nif::Record* r = nif.getRoot(i); if (!r) continue; const Nif::Node* nifNode = dynamic_cast(r); @@ -328,7 +331,7 @@ namespace NifOsg roots.emplace_back(nifNode); } if (roots.empty()) - nif->fail("Found no root nodes"); + throw Nif::Exception("Found no root nodes", nif.getFilename()); osg::ref_ptr textkeys(new SceneUtil::TextKeyMapHolder); @@ -348,7 +351,7 @@ namespace NifOsg // Attach particle emitters to their nodes which should all be loaded by now. handleQueuedParticleEmitters(created, nif); - if (nif->getUseSkinning()) + if (nif.getUseSkinning()) { osg::ref_ptr skel = new SceneUtil::Skeleton; skel->setStateSet(created->getStateSet()); @@ -362,7 +365,7 @@ namespace NifOsg if (!textkeys->mTextKeys.empty()) created->getOrCreateUserDataContainer()->addUserObject(textkeys); - created->setUserValue(Misc::OsgUserValues::sFileHash, nif->getHash()); + created->setUserValue(Misc::OsgUserValues::sFileHash, nif.getHash()); return created; } @@ -371,19 +374,17 @@ namespace NifOsg SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) { - const Nif::PropertyList& props = nifNode->props; - bool hasStencilProperty = false; - for (size_t i = 0; i < props.length(); ++i) + for (const auto& property : nifNode->props) { - if (props[i].empty()) + if (property.empty()) continue; - if (props[i].getPtr()->recType == Nif::RC_NiStencilProperty) + if (property.getPtr()->recType == Nif::RC_NiStencilProperty) { const Nif::NiStencilProperty* stencilprop - = static_cast(props[i].getPtr()); + = static_cast(property.getPtr()); if (stencilprop->data.enabled != 0) { hasStencilProperty = true; @@ -392,24 +393,24 @@ namespace NifOsg } } - for (size_t i = 0; i < props.length(); ++i) + for (const auto& property : nifNode->props) { - if (!props[i].empty()) + if (!property.empty()) { // Get the lowest numbered recIndex of the NiTexturingProperty root node. // This is what is overridden when a spell effect "particle texture" is used. if (nifNode->parents.empty() && !mFoundFirstRootTexturingProperty - && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) + && property.getPtr()->recType == Nif::RC_NiTexturingProperty) { - mFirstRootTextureIndex = props[i].getPtr()->recIndex; + mFirstRootTextureIndex = property.getPtr()->recIndex; mFoundFirstRootTexturingProperty = true; } - else if (props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) + else if (property.getPtr()->recType == Nif::RC_NiTexturingProperty) { - if (props[i].getPtr()->recIndex == mFirstRootTextureIndex) + if (property.getPtr()->recIndex == mFirstRootTextureIndex) applyTo->setUserValue("overrideFx", 1); } - handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags, + handleProperty(property.getPtr(), applyTo, composite, imageManager, boundTextures, animflags, hasStencilProperty); } } @@ -459,13 +460,13 @@ namespace NifOsg const Nif::NiFltAnimationNode* niFltAnimationNode = static_cast(nifNode); osg::ref_ptr sequenceNode(new osg::Sequence); sequenceNode->setName(niFltAnimationNode->name); - if (niFltAnimationNode->children.length() != 0) + if (!niFltAnimationNode->children.empty()) { if (niFltAnimationNode->swing()) sequenceNode->setDefaultTime( - niFltAnimationNode->mDuration / (niFltAnimationNode->children.length() * 2)); + niFltAnimationNode->mDuration / (niFltAnimationNode->children.size() * 2)); else - sequenceNode->setDefaultTime(niFltAnimationNode->mDuration / niFltAnimationNode->children.length()); + sequenceNode->setDefaultTime(niFltAnimationNode->mDuration / niFltAnimationNode->children.size()); } return sequenceNode; } @@ -626,12 +627,9 @@ namespace NifOsg for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) extraCollection.emplace_back(e); - for (size_t i = 0; i < nifNode->extralist.length(); ++i) - { - Nif::ExtraPtr e = nifNode->extralist[i]; - if (!e.empty()) - extraCollection.emplace_back(e); - } + for (const auto& extraNode : nifNode->extralist) + if (!extraNode.empty()) + extraCollection.emplace_back(extraNode); for (const auto& e : extraCollection) { @@ -801,20 +799,16 @@ namespace NifOsg if (ninode) { const Nif::NodeList& effects = ninode->effects; - for (size_t i = 0; i < effects.length(); ++i) - { - if (!effects[i].empty()) - handleEffect(effects[i].getPtr(), currentNode, imageManager); - } + for (const auto& effect : effects) + if (!effect.empty()) + handleEffect(effect.getPtr(), currentNode, imageManager); const Nif::NodeList& children = ninode->children; const Nif::Parent currentParent{ *ninode, parent }; - for (size_t i = 0; i < children.length(); ++i) - { - if (!children[i].empty()) - handleNode(children[i].getPtr(), ¤tParent, currentNode, imageManager, boundTextures, - animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode); - } + for (const auto& child : children) + if (!child.empty()) + handleNode(child.getPtr(), ¤tParent, currentNode, imageManager, boundTextures, animflags, + skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode); } if (nifNode->recType == Nif::RC_NiFltAnimationNode) @@ -959,7 +953,7 @@ namespace NifOsg if (matctrl->mData.empty() && matctrl->mInterpolator.empty()) continue; auto targetColor = static_cast(matctrl->mTargetColor); - if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW + if (mVersion <= Nif::NIFFile::VER_MW && targetColor == MaterialColorController::TargetColor::Specular) continue; if (!matctrl->mInterpolator.empty() @@ -1008,13 +1002,12 @@ namespace NifOsg wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); } - for (unsigned int i = 0; i < flipctrl->mSources.length(); ++i) + for (const auto& source : flipctrl->mSources) { - Nif::NiSourceTexturePtr st = flipctrl->mSources[i]; - if (st.empty()) + if (source.empty()) continue; - osg::ref_ptr image(handleSourceTexture(st.getPtr(), imageManager)); + osg::ref_ptr image(handleSourceTexture(source.getPtr(), imageManager)); osg::ref_ptr texture(new osg::Texture2D(image)); if (image) texture->setTextureSize(image->s(), image->t()); @@ -1188,7 +1181,7 @@ namespace NifOsg return emitter; } - void handleQueuedParticleEmitters(osg::Group* rootNode, Nif::NIFFilePtr nif) + void handleQueuedParticleEmitters(osg::Group* rootNode, Nif::FileView nif) { for (const auto& emitterPair : mEmitterQueue) { @@ -1198,8 +1191,9 @@ namespace NifOsg osg::Group* emitterNode = findEmitterNode.mFound; if (!emitterNode) { - nif->warn("Failed to find particle emitter emitter node (node record index " - + std::to_string(recIndex) + ")"); + Log(Debug::Warning) + << "NIFFile Warning: Failed to find particle emitter emitter node (node record index " + << recIndex << "). File: " << nif.getFilename(); continue; } @@ -1475,7 +1469,7 @@ namespace NifOsg const Nif::NiSkinInstance* skin = static_cast(nifNode)->skin.getPtr(); const Nif::NiSkinData* data = skin->data.getPtr(); const Nif::NodeList& bones = skin->bones; - for (size_t i = 0; i < bones.length(); i++) + for (std::size_t i = 0, n = bones.size(); i < n; ++i) { std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name); @@ -2393,7 +2387,7 @@ namespace NifOsg } // While NetImmerse and Gamebryo support specular lighting, Morrowind has its support disabled. - if (mVersion <= Nif::NIFFile::NIFVersion::VER_MW || !specEnabled) + if (mVersion <= Nif::NIFFile::VER_MW || !specEnabled) mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); if (lightmode == 0) @@ -2495,15 +2489,15 @@ namespace NifOsg } }; - osg::ref_ptr Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager) + osg::ref_ptr Loader::load(Nif::FileView file, Resource::ImageManager* imageManager) { - LoaderImpl impl(file->getFilename(), file->getVersion(), file->getUserVersion(), file->getBethVersion()); + LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion()); return impl.load(file, imageManager); } - void Loader::loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target) + void Loader::loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target) { - LoaderImpl impl(kf->getFilename(), kf->getVersion(), kf->getUserVersion(), kf->getBethVersion()); + LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion()); impl.loadKf(kf, target); } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 99251e3da8..737c365c5e 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -28,10 +28,10 @@ namespace NifOsg public: /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton /// if so. - static osg::ref_ptr load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager); + static osg::ref_ptr load(Nif::FileView file, Resource::ImageManager* imageManager); /// Load keyframe controllers from the given kf file. - static void loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target); + static void loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target); /// Set whether or not nodes marked as "MRK" should be shown. /// These should be hidden ingame, but visible in the editor. diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 99139fbf38..e87520caa9 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -156,8 +156,10 @@ namespace Resource osg::ref_ptr loaded(new SceneUtil::KeyframeHolder); if (Misc::getFileExtension(normalized) == "kf") { - NifOsg::Loader::loadKf( - Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get()); + auto file = std::make_shared(normalized); + Nif::Reader reader(*file); + reader.parse(mVFS->getNormalized(normalized)); + NifOsg::Loader::loadKf(*file, *loaded.get()); } else { diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp index 29ed9a6b9c..ea95edf2ae 100644 --- a/components/resource/niffilemanager.cpp +++ b/components/resource/niffilemanager.cpp @@ -45,7 +45,9 @@ namespace Resource return static_cast(obj.get())->mNifFile; else { - Nif::NIFFilePtr file(new Nif::NIFFile(mVFS->get(name), name)); + auto file = std::make_shared(name); + Nif::Reader reader(*file); + reader.parse(mVFS->get(name)); obj = new NifFileHolder(file); mCache->addEntryToObjectCache(name, obj); return file; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index e686de79fa..037da7be4c 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -714,7 +714,7 @@ namespace Resource { auto ext = Misc::getFileExtension(normalizedFilename); if (ext == "nif") - return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager); + return NifOsg::Loader::load(*nifFileManager->get(normalizedFilename), imageManager); else return loadNonNif(normalizedFilename, *vfs->get(normalizedFilename), imageManager); }