diff --git a/apps/components_tests/misc/testresourcehelpers.cpp b/apps/components_tests/misc/testresourcehelpers.cpp index 286fb57f7a..53d65c4300 100644 --- a/apps/components_tests/misc/testresourcehelpers.cpp +++ b/apps/components_tests/misc/testresourcehelpers.cpp @@ -9,6 +9,10 @@ namespace Misc::ResourceHelpers { using namespace ::testing; + constexpr VFS::Path::NormalizedView sound("sound"); + constexpr VFS::Path::NormalizedView textures("textures"); + constexpr VFS::Path::NormalizedView bookart("bookart"); + TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs) { constexpr VFS::Path::NormalizedView path("sound/foo.wav"); @@ -36,8 +40,9 @@ namespace Misc::ResourceHelpers TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfDoesNotExistInVfs) { + constexpr VFS::Path::NormalizedView path("sound/foo.wav"); const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound/foo.wav", vfs.get(), "mp3"), "sound/foo.mp3"); + EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/foo.mp3"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfBothExistInVfs) @@ -48,7 +53,7 @@ namespace Misc::ResourceHelpers { wav, nullptr }, { mp3, nullptr }, }); - EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.mp3"); + EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "mp3"), "sound/foo.mp3"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtentionIfExistInVfs) @@ -57,80 +62,58 @@ namespace Misc::ResourceHelpers const std::unique_ptr vfs = TestingOpenMW::createTestVFS({ { wav, nullptr }, }); - EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.wav"); + EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "mp3"), "sound/foo.wav"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory) { + constexpr VFS::Path::NormalizedView path("foo.mp3"); const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "foo.mp3", vfs.get(), "mp3"), "sound/foo.mp3"); + EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/foo.mp3"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs) { + constexpr VFS::Path::NormalizedView path("bookart/foo.a"); constexpr VFS::Path::NormalizedView a("textures/foo.a"); const std::unique_ptr vfs = TestingOpenMW::createTestVFS({ { a, nullptr }, }); - EXPECT_EQ( - correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.a"); + EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.a"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs) { + constexpr VFS::Path::NormalizedView path("bookart/foo.a"); constexpr VFS::Path::NormalizedView b("textures/foo.b"); const std::unique_ptr vfs = TestingOpenMW::createTestVFS({ { b, nullptr }, }); - EXPECT_EQ( - correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.b"); - } - - TEST(MiscResourceHelpersCorrectResourcePath, shouldLowerCase) - { - const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3"); - } - - TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveLeadingSlash) - { - const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3"); - } - - TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveDuplicateSlashes) - { - const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\\\SOUND\\\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3"); - } - - TEST(MiscResourceHelpersCorrectResourcePath, shouldConvertToForwardSlash) - { - const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND/Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3"); + EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.b"); } TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory) { + constexpr VFS::Path::NormalizedView path("sound"); const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound", vfs.get(), "mp3"), "sound/sound"); + EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/sound"); } - struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam + struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam { }; TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected) { const std::unique_ptr vfs = TestingOpenMW::createTestVFS({}); - EXPECT_EQ(correctResourcePath({ { "sound" } }, GetParam(), vfs.get(), "mp3"), "sound/foo.mp3"); + EXPECT_EQ(correctResourcePath({ { sound } }, GetParam(), *vfs, "mp3"), "sound/foo.mp3"); } - const std::vector pathsWithPrefix = { - "data/sound/foo.mp3", - "data/notsound/sound/foo.mp3", - "data/soundnot/sound/foo.mp3", - "data/notsoundnot/sound/foo.mp3", + const std::vector pathsWithPrefix = { + VFS::Path::NormalizedView("data/sound/foo.mp3"), + VFS::Path::NormalizedView("data/notsound/sound/foo.mp3"), + VFS::Path::NormalizedView("data/soundnot/sound/foo.mp3"), + VFS::Path::NormalizedView("data/notsoundnot/sound/foo.mp3"), }; INSTANTIATE_TEST_SUITE_P( diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index c0ecf0575a..eb5761b0af 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -212,7 +212,7 @@ namespace MWGui const ESM::BirthSign* birth = store.get().find(mCurrentBirthId); mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath( - birth->mTexture, MWBase::Environment::get().getResourceSystem()->getVFS())); + VFS::Path::toNormalized(birth->mTexture), *MWBase::Environment::get().getResourceSystem()->getVFS())); std::vector abilities, powers, spells; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 0c8c9548df..2dbe890420 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1196,8 +1196,8 @@ namespace MWGui if (const auto* id = classId.getIf()) { const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - classImage - = Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + id->getValue() + ".dds", vfs); + classImage = Misc::ResourceHelpers::correctTexturePath( + VFS::Path::toNormalized("textures\\levelup\\" + id->getValue() + ".dds"), *vfs); if (!vfs->exists(classImage)) { Log(Debug::Warning) << "No class image for " << classId << ", falling back to default"; diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index b2d9415897..b4261e3eb4 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -330,15 +330,16 @@ namespace MWGui::Formatting if (auto heightIt = attr.find("height"); heightIt != attr.end()) height = MyGUI::utility::parseInt(heightIt->second); - const std::string& src = srcIt->second; + const std::string_view src = srcIt->second; auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - std::string correctedSrc; + VFS::Path::Normalized correctedSrc; constexpr std::string_view imgPrefix = "img://"; if (src.starts_with(imgPrefix)) { - correctedSrc = src.substr(imgPrefix.size(), src.size() - imgPrefix.size()); + correctedSrc + = VFS::Path::toNormalized(src.substr(imgPrefix.size(), src.size() - imgPrefix.size())); if (width == 0) { width = 50; @@ -351,7 +352,8 @@ namespace MWGui::Formatting { if (width == 0 || height == 0) continue; - correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs); + correctedSrc = Misc::ResourceHelpers::correctBookartPath( + VFS::Path::toNormalized(src), width, height, *vfs); } if (!vfs->exists(correctedSrc)) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1927f8899c..fbd4dff33b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -386,9 +386,9 @@ namespace MWGui std::replace(icon.begin(), icon.end(), '/', '\\'); size_t slashPos = icon.rfind('\\'); icon.insert(slashPos + 1, "b_"); - icon = Misc::ResourceHelpers::correctIconPath( - icon, MWBase::Environment::get().getResourceSystem()->getVFS()); - mSpellImage->setSpellIcon(icon); + const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath( + VFS::Path::toNormalized(icon), *MWBase::Environment::get().getResourceSystem()->getVFS()); + mSpellImage->setSpellIcon(iconPath); } else mSpellImage->setSpellIcon({}); diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 801ec2d0c2..ea877e5228 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -136,16 +136,17 @@ namespace MWGui void ItemWidget::setIcon(const MWWorld::Ptr& ptr) { + constexpr VFS::Path::NormalizedView defaultIcon("default icon.tga"); std::string_view icon = ptr.getClass().getInventoryIcon(ptr); if (icon.empty()) - icon = "default icon.tga"; + icon = defaultIcon.value(); const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - std::string invIcon = Misc::ResourceHelpers::correctIconPath(icon, vfs); + std::string invIcon = Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(icon), *vfs); if (!vfs->exists(invIcon)) { - Log(Debug::Error) << "Failed to open image: '" << invIcon - << "' not found, falling back to 'default-icon.tga'"; - invIcon = Misc::ResourceHelpers::correctIconPath("default icon.tga", vfs); + Log(Debug::Error) << "Failed to open image: '" << invIcon << "' not found, falling back to '" + << defaultIcon.value() << "'"; + invIcon = Misc::ResourceHelpers::correctIconPath(defaultIcon, *vfs); } setIcon(invIcon); } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index a42ea5e566..9ccf67d622 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -325,7 +325,8 @@ namespace MWGui std::string path = effect->mIcon; std::replace(path.begin(), path.end(), '/', '\\'); path.insert(path.rfind('\\') + 1, "b_"); - path = Misc::ResourceHelpers::correctIconPath(path, MWBase::Environment::get().getResourceSystem()->getVFS()); + const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath( + VFS::Path::toNormalized(path), *MWBase::Environment::get().getResourceSystem()->getVFS()); float scale = 1.f; MyGUI::ITexture* texture @@ -335,7 +336,7 @@ namespace MWGui const int diameter = static_cast(44 * scale); mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(0, 0, diameter, diameter)); - mSelected->button->setIcon(path); + mSelected->button->setIcon(iconPath); if (mMagicSelectionDialog) mMagicSelectionDialog->setVisible(false); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index dc3cbce4f4..c5ece8ebe0 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -220,7 +220,7 @@ namespace MWGui void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) { mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( - effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); + VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index f296617eb6..fae68389fd 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -150,8 +150,9 @@ namespace MWGui "ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default); mWidgetMap[effectId] = image; - image->setImageTexture(Misc::ResourceHelpers::correctIconPath( - effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); + image->setImageTexture( + Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon), + *MWBase::Environment::get().getResourceSystem()->getVFS())); const std::string& name = ESM::MagicEffect::indexToGmstString(effectId); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index ec56644aa4..88eedae99c 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -447,8 +447,8 @@ namespace MWGui const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2; - const std::string realImage - = Misc::ResourceHelpers::correctIconPath(image, MWBase::Environment::get().getResourceSystem()->getVFS()); + const VFS::Path::Normalized realImage = Misc::ResourceHelpers::correctIconPath( + VFS::Path::toNormalized(image), *MWBase::Environment::get().getResourceSystem()->getVFS()); Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget( "NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); @@ -887,8 +887,8 @@ namespace MWGui widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "BirthSignToolTip"); - widget->setUserString( - "ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs)); + widget->setUserString("ImageTexture_BirthSignImage", + Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(sign->mTexture), *vfs)); widget->setUserString("Caption_BirthSignName", sign->mName); widget->setUserString("Caption_BirthSignDescription", sign->mDescription); @@ -962,7 +962,8 @@ namespace MWGui std::string icon = effect->mIcon; icon.insert(icon.rfind('\\') + 1, "b_"); - icon = Misc::ResourceHelpers::correctIconPath(icon, MWBase::Environment::get().getResourceSystem()->getVFS()); + const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath( + VFS::Path::toNormalized(icon), *MWBase::Environment::get().getResourceSystem()->getVFS()); widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); @@ -972,6 +973,6 @@ namespace MWGui "#{sSchool}: " + MyGUI::TextIterator::toTagsString( store->get().find(effect->mData.mSchool)->mSchool->mName)); - widget->setUserString("ImageTexture_MagicEffectImage", icon); + widget->setUserString("ImageTexture_MagicEffectImage", iconPath); } } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 1d14aa4af9..a4ca75d21d 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -477,7 +477,7 @@ namespace MWGui::Widgets mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset; mImageWidget->setImageTexture(Misc::ResourceHelpers::correctIconPath( - magicEffect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); + VFS::Path::toNormalized(magicEffect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS())); } MWSpellEffect::~MWSpellEffect() {} diff --git a/apps/openmw/mwlua/birthsignbindings.cpp b/apps/openmw/mwlua/birthsignbindings.cpp index 130144ab9b..d2ba10f988 100644 --- a/apps/openmw/mwlua/birthsignbindings.cpp +++ b/apps/openmw/mwlua/birthsignbindings.cpp @@ -36,7 +36,7 @@ namespace MWLua = sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; }); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string { - return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs); + return Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(rec.mTexture), *vfs); }); signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table { return createReadOnlyRefIdTable(lua, rec.mPowers.mList); diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index 3cfc84bc3c..d36b31a931 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -394,7 +394,7 @@ namespace MWLua }); magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); magicEffectT["particle"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; }); diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 52e4986e40..e4ee31d411 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -649,7 +649,7 @@ namespace MWLua attributeT["description"] = sol::readonly_property([](const ESM::Attribute& rec) -> std::string_view { return rec.mDescription; }); attributeT["icon"] = sol::readonly_property([vfs](const ESM::Attribute& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); sol::table skills(lua, sol::create); @@ -669,7 +669,7 @@ namespace MWLua return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization); }); skillT["icon"] = sol::readonly_property([vfs](const ESM::Skill& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); skillT["school"] = sol::readonly_property([](const ESM::Skill& rec) -> const ESM::MagicSchool* { if (!rec.mSchool) diff --git a/apps/openmw/mwlua/types/apparatus.cpp b/apps/openmw/mwlua/types/apparatus.cpp index 72dcbf4b30..a8bcf8a527 100644 --- a/apps/openmw/mwlua/types/apparatus.cpp +++ b/apps/openmw/mwlua/types/apparatus.cpp @@ -45,7 +45,7 @@ namespace MWLua record["mwscript"] = sol::readonly_property([](const ESM::Apparatus& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["type"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mType; }); record["value"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mValue; }); diff --git a/apps/openmw/mwlua/types/armor.cpp b/apps/openmw/mwlua/types/armor.cpp index 8ef0496d7e..3d6062f613 100644 --- a/apps/openmw/mwlua/types/armor.cpp +++ b/apps/openmw/mwlua/types/armor.cpp @@ -100,7 +100,7 @@ namespace MWLua record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; }); addModelProperty(record); record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["enchant"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mEnchant; }); record["mwscript"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mScript; }); diff --git a/apps/openmw/mwlua/types/book.cpp b/apps/openmw/mwlua/types/book.cpp index 5bb03de71a..03e4db1f45 100644 --- a/apps/openmw/mwlua/types/book.cpp +++ b/apps/openmw/mwlua/types/book.cpp @@ -108,7 +108,7 @@ namespace MWLua addModelProperty(record); record["mwscript"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; }); record["enchant"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mEnchant; }); diff --git a/apps/openmw/mwlua/types/clothing.cpp b/apps/openmw/mwlua/types/clothing.cpp index 31ba1c2904..e0fd96c707 100644 --- a/apps/openmw/mwlua/types/clothing.cpp +++ b/apps/openmw/mwlua/types/clothing.cpp @@ -95,7 +95,7 @@ namespace MWLua record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; }); addModelProperty(record); record["icon"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["enchant"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mEnchant; }); record["mwscript"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mScript; }); diff --git a/apps/openmw/mwlua/types/ingredient.cpp b/apps/openmw/mwlua/types/ingredient.cpp index 9deb49e492..f1cb73bc5f 100644 --- a/apps/openmw/mwlua/types/ingredient.cpp +++ b/apps/openmw/mwlua/types/ingredient.cpp @@ -37,7 +37,7 @@ namespace MWLua record["mwscript"] = sol::readonly_property([](const ESM::Ingredient& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["weight"] = sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; }); diff --git a/apps/openmw/mwlua/types/light.cpp b/apps/openmw/mwlua/types/light.cpp index 0a9befd9eb..e883218296 100644 --- a/apps/openmw/mwlua/types/light.cpp +++ b/apps/openmw/mwlua/types/light.cpp @@ -101,7 +101,7 @@ namespace MWLua record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; }); addModelProperty(record); record["icon"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["sound"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); }); diff --git a/apps/openmw/mwlua/types/lockpick.cpp b/apps/openmw/mwlua/types/lockpick.cpp index b8aab6c982..1c4a96f605 100644 --- a/apps/openmw/mwlua/types/lockpick.cpp +++ b/apps/openmw/mwlua/types/lockpick.cpp @@ -35,7 +35,7 @@ namespace MWLua addModelProperty(record); record["mwscript"] = sol::readonly_property([](const ESM::Lockpick& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["maxCondition"] = sol::readonly_property([](const ESM::Lockpick& rec) -> int { return rec.mData.mUses; }); diff --git a/apps/openmw/mwlua/types/misc.cpp b/apps/openmw/mwlua/types/misc.cpp index c486b9c8ab..89ca1d72e7 100644 --- a/apps/openmw/mwlua/types/misc.cpp +++ b/apps/openmw/mwlua/types/misc.cpp @@ -85,7 +85,7 @@ namespace MWLua record["mwscript"] = sol::readonly_property([](const ESM::Miscellaneous& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["isKey"] = sol::readonly_property( [](const ESM::Miscellaneous& rec) -> bool { return rec.mData.mFlags & ESM::Miscellaneous::Key; }); diff --git a/apps/openmw/mwlua/types/potion.cpp b/apps/openmw/mwlua/types/potion.cpp index 1fd8ef9c28..27139c2e01 100644 --- a/apps/openmw/mwlua/types/potion.cpp +++ b/apps/openmw/mwlua/types/potion.cpp @@ -83,7 +83,7 @@ namespace MWLua record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; }); addModelProperty(record); record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["mwscript"] = sol::readonly_property([](const ESM::Potion& rec) -> ESM::RefId { return rec.mScript; }); record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; }); diff --git a/apps/openmw/mwlua/types/probe.cpp b/apps/openmw/mwlua/types/probe.cpp index d8f499acdc..82e299edcf 100644 --- a/apps/openmw/mwlua/types/probe.cpp +++ b/apps/openmw/mwlua/types/probe.cpp @@ -35,7 +35,7 @@ namespace MWLua addModelProperty(record); record["mwscript"] = sol::readonly_property([](const ESM::Probe& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["maxCondition"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mUses; }); record["value"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mValue; }); diff --git a/apps/openmw/mwlua/types/repair.cpp b/apps/openmw/mwlua/types/repair.cpp index 9ab6219663..f6f977bd87 100644 --- a/apps/openmw/mwlua/types/repair.cpp +++ b/apps/openmw/mwlua/types/repair.cpp @@ -35,7 +35,7 @@ namespace MWLua addModelProperty(record); record["mwscript"] = sol::readonly_property([](const ESM::Repair& rec) -> ESM::RefId { return rec.mScript; }); record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["maxCondition"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mUses; }); record["value"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mValue; }); diff --git a/apps/openmw/mwlua/types/weapon.cpp b/apps/openmw/mwlua/types/weapon.cpp index 32b983a6f7..ea564755b1 100644 --- a/apps/openmw/mwlua/types/weapon.cpp +++ b/apps/openmw/mwlua/types/weapon.cpp @@ -133,7 +133,7 @@ namespace MWLua record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; }); addModelProperty(record); record["icon"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string { - return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); + return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs); }); record["enchant"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mEnchant; }); record["mwscript"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mScript; }); diff --git a/apps/openmw/mwlua/weatherbindings.cpp b/apps/openmw/mwlua/weatherbindings.cpp index e405d456c8..8b7c7de66f 100644 --- a/apps/openmw/mwlua/weatherbindings.cpp +++ b/apps/openmw/mwlua/weatherbindings.cpp @@ -155,8 +155,9 @@ namespace MWLua weatherT["cloudSpeed"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudSpeed; }, [](MWWorld::Weather& w, const FiniteFloat cloudSpeed) { w.mCloudSpeed = cloudSpeed; }); weatherT["cloudTexture"] = sol::property( - [vfs]( - const MWWorld::Weather& w) { return Misc::ResourceHelpers::correctTexturePath(w.mCloudTexture, vfs); }, + [vfs](const MWWorld::Weather& w) -> std::string { + return Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(w.mCloudTexture), *vfs); + }, [](MWWorld::Weather& w, std::string_view cloudTexture) { w.mCloudTexture = cloudTexture; }); weatherT["cloudsMaximumPercent"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; }, diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0aee682598..d13a081045 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1795,7 +1795,7 @@ namespace MWRender // Notify that this animation has attached magic effects mHasMagicEffects = true; - overrideFirstRootTexture(texture, mResourceSystem, *node); + overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *node); } void Animation::removeEffect(std::string_view effectId) diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index d14e136073..321fa2357f 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -55,9 +55,9 @@ namespace MWRender node->accept(assignVisitor); if (isMagicVFX) - overrideFirstRootTexture(textureOverride, mResourceSystem, *node); + overrideFirstRootTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node); else - overrideTexture(textureOverride, mResourceSystem, *node); + overrideTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node); mParentNode->addChild(trans); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 7e485862a9..11a79e1301 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -763,7 +763,7 @@ namespace MWRender mClouds = weather.mCloudTexture; const VFS::Path::Normalized texture - = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); + = Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(mClouds), *mSceneManager->getVFS()); osg::ref_ptr cloudTex = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); @@ -785,8 +785,8 @@ namespace MWRender if (!mNextClouds.empty()) { - const VFS::Path::Normalized texture - = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); + const VFS::Path::Normalized texture = Misc::ResourceHelpers::correctTexturePath( + VFS::Path::toNormalized(mNextClouds), *mSceneManager->getVFS()); osg::ref_ptr cloudTex = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index 2564744b3b..ac9e36a42b 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -15,10 +15,9 @@ namespace MWRender { namespace { - class TextureOverrideVisitor : public osg::NodeVisitor + struct TextureOverrideVisitor : osg::NodeVisitor { - public: - TextureOverrideVisitor(std::string_view texture, Resource::ResourceSystem* resourcesystem) + explicit TextureOverrideVisitor(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourcesystem) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mTexture(texture) , mResourcesystem(resourcesystem) @@ -35,23 +34,25 @@ namespace MWRender } traverse(node); } - std::string_view mTexture; + + VFS::Path::NormalizedView mTexture; Resource::ResourceSystem* mResourcesystem; }; } - void overrideFirstRootTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) + void overrideFirstRootTexture( + VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) { TextureOverrideVisitor overrideVisitor(texture, resourceSystem); node.accept(overrideVisitor); } - void overrideTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) + void overrideTexture(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) { if (texture.empty()) return; const VFS::Path::Normalized correctedTexture - = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS()); + = Misc::ResourceHelpers::correctTexturePath(texture, *resourceSystem->getVFS()); // Not sure if wrap settings should be pulled from the overridden texture? osg::ref_ptr tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture)); diff --git a/apps/openmw/mwrender/util.hpp b/apps/openmw/mwrender/util.hpp index bed22d2a5e..705de32fd2 100644 --- a/apps/openmw/mwrender/util.hpp +++ b/apps/openmw/mwrender/util.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_MWRENDER_UTIL_H #define OPENMW_MWRENDER_UTIL_H +#include + #include #include @@ -21,9 +23,10 @@ namespace MWRender // Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty // of the .NIF file's root node, if it had a NiTexturingProperty. Used for applying "particle textures" to magic // effects. - void overrideFirstRootTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); + void overrideFirstRootTexture( + VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); - void overrideTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); + void overrideTexture(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); // Node callback to entirely skip the traversal. class NoTraverseCallback : public osg::NodeCallback diff --git a/apps/openmw/mwsound/soundbuffer.cpp b/apps/openmw/mwsound/soundbuffer.cpp index 7a1aaba66b..e558764bfa 100644 --- a/apps/openmw/mwsound/soundbuffer.cpp +++ b/apps/openmw/mwsound/soundbuffer.cpp @@ -19,6 +19,8 @@ namespace MWSound { namespace { + constexpr VFS::Path::NormalizedView soundDir("sound"); + struct AudioParams { float mAudioDefaultMinDistance; @@ -192,7 +194,7 @@ namespace MWSound max = std::max(min, max); SoundBuffer& sfx = mSoundBuffers.emplace_back( - Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(sound.mSound)), volume, min, max); + Misc::ResourceHelpers::correctSoundPath(VFS::Path::toNormalized(sound.mSound)), volume, min, max); mBufferNameMap.emplace(soundId, &sfx); return &sfx; @@ -200,22 +202,24 @@ namespace MWSound SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound) { - std::string path = Misc::ResourceHelpers::correctResourcePath( - { { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); + VFS::Path::Normalized path + = Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile), + *MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); float volume = 1, min = 1, max = 255; // TODO: needs research - SoundBuffer& sfx = mSoundBuffers.emplace_back(VFS::Path::Normalized(std::move(path)), volume, min, max); + SoundBuffer& sfx = mSoundBuffers.emplace_back(std::move(path), volume, min, max); mBufferNameMap.emplace(soundId, &sfx); return &sfx; } SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound) { - std::string path = Misc::ResourceHelpers::correctResourcePath( - { { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); + VFS::Path::Normalized path + = Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile), + *MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); float volume = 1, min = 1, max = 255; // TODO: needs research // TODO: sound.mSoundId can link to another SoundReference, probably we will need to add additional lookups to // ESMStore. - SoundBuffer& sfx = mSoundBuffers.emplace_back(VFS::Path::Normalized(std::move(path)), volume, min, max); + SoundBuffer& sfx = mSoundBuffers.emplace_back(std::move(path), volume, min, max); mBufferNameMap.emplace(soundId, &sfx); return &sfx; } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index f4d7569fe9..203444fa61 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -255,7 +255,7 @@ namespace MWWorld SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime); state.mNode->accept(assignVisitor); - MWRender::overrideFirstRootTexture(texture, mResourceSystem, *projectile); + MWRender::overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *projectile); } void ProjectileManager::update(State& state, float duration) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 4ff61dc00a..7b87ecdf58 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -386,7 +386,7 @@ namespace ESMTerrain } } // this is needed due to MWs messed up texture handling - return Misc::ResourceHelpers::correctTexturePath(texture, mVFS); + return Misc::ResourceHelpers::correctTexturePath(VFS::Path::Normalized(texture), *mVFS); } void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, diff --git a/components/misc/resourcehelpers.cpp b/components/misc/resourcehelpers.cpp index 90081e6d81..2808d7dfec 100644 --- a/components/misc/resourcehelpers.cpp +++ b/components/misc/resourcehelpers.cpp @@ -15,6 +15,12 @@ namespace { + constexpr VFS::Path::NormalizedView textures("textures"); + constexpr VFS::Path::NormalizedView bookart("bookart"); + constexpr VFS::Path::NormalizedView icons("icons"); + constexpr VFS::Path::NormalizedView materials("materials"); + constexpr std::string_view dds("dds"); + bool changeExtension(std::string& path, std::string_view ext) { std::string::size_type pos = path.rfind('.'); @@ -26,14 +32,15 @@ namespace return false; } - std::size_t findDirectory(VFS::Path::NormalizedView path, std::string_view directory) + std::size_t findDirectory(VFS::Path::NormalizedView path, VFS::Path::NormalizedView directory) { const std::string_view pathValue = path.value(); - const std::size_t directorySize = directory.size(); + const std::string_view directoryValue = directory.value(); + const std::size_t directorySize = directoryValue.size(); for (std::size_t offset = 0, pathSize = pathValue.size(); offset < pathSize;) { - const std::size_t position = pathValue.find(directory, offset); + const std::size_t position = pathValue.find(directoryValue, offset); if (position == std::string_view::npos) return std::string_view::npos; @@ -50,13 +57,6 @@ namespace return std::string_view::npos; } - - VFS::Path::Normalized withPrefix(VFS::Path::NormalizedView path, std::string_view prefix) - { - VFS::Path::Normalized prefixed(prefix); - prefixed /= path; - return prefixed; - } } bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path) @@ -65,51 +65,52 @@ bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path) } // If `ext` is not empty we first search file with extension `ext`, then if not found fallback to original extension. -std::string Misc::ResourceHelpers::correctResourcePath(std::span topLevelDirectories, - std::string_view resPath, const VFS::Manager* vfs, std::string_view ext) +VFS::Path::Normalized Misc::ResourceHelpers::correctResourcePath( + std::span topLevelDirectories, VFS::Path::NormalizedView resPath, + const VFS::Manager& vfs, std::string_view ext) { - VFS::Path::Normalized correctedPath(resPath); + VFS::Path::Normalized correctedPath; // Handle top level directory bool needsPrefix = true; - for (const std::string_view potentialTopLevelDirectory : topLevelDirectories) + for (const VFS::Path::NormalizedView potentialTopLevelDirectory : topLevelDirectories) { - if (const std::size_t topLevelPos = findDirectory(correctedPath, potentialTopLevelDirectory); + if (const std::size_t topLevelPos = findDirectory(resPath, potentialTopLevelDirectory); topLevelPos != std::string::npos) { - correctedPath = VFS::Path::Normalized(correctedPath.value().substr(topLevelPos)); + correctedPath = VFS::Path::Normalized(resPath.value().substr(topLevelPos)); needsPrefix = false; break; } } if (needsPrefix) - correctedPath = withPrefix(correctedPath, topLevelDirectories.front()); + correctedPath = topLevelDirectories.front() / resPath; const VFS::Path::Normalized origExt = correctedPath; // replace extension if `ext` is specified (used for .tga -> .dds, .wav -> .mp3) const bool isExtChanged = !ext.empty() && correctedPath.changeExtension(ext); - if (vfs->exists(correctedPath)) + if (vfs.exists(correctedPath)) return correctedPath; // fall back to original extension - if (isExtChanged && vfs->exists(origExt)) + if (isExtChanged && vfs.exists(origExt)) return origExt; // fall back to a resource in the top level directory if it exists { - const VFS::Path::Normalized fallback = withPrefix(correctedPath.filename(), topLevelDirectories.front()); - if (vfs->exists(fallback)) + const VFS::Path::Normalized fallback = topLevelDirectories.front() / correctedPath.filename(); + if (vfs.exists(fallback)) return fallback; } if (isExtChanged) { - const VFS::Path::Normalized fallback = withPrefix(origExt.filename(), topLevelDirectories.front()); - if (vfs->exists(fallback)) + const VFS::Path::Normalized fallback = topLevelDirectories.front() / origExt.filename(); + if (vfs.exists(fallback)) return fallback; } @@ -120,33 +121,36 @@ std::string Misc::ResourceHelpers::correctResourcePath(std::spanexists(image)) + if (!vfs.exists(image)) { std::stringstream str; - str << image.substr(0, image.rfind('.')) << "_" << width << "_" << height << image.substr(image.rfind('.')); - image = Misc::ResourceHelpers::correctBookartPath(str.str(), vfs); + str << image.view().substr(0, image.view().rfind('.')) << "_" << width << "_" << height + << image.view().substr(image.view().rfind('.')); + image = Misc::ResourceHelpers::correctBookartPath(VFS::Path::Normalized(std::move(str).str()), vfs); } return image; @@ -172,9 +176,10 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctActorModelPath( return mdlname; } -std::string Misc::ResourceHelpers::correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs) +VFS::Path::Normalized Misc::ResourceHelpers::correctMaterialPath( + VFS::Path::NormalizedView resPath, const VFS::Manager& vfs) { - return correctResourcePath({ { "materials" } }, resPath, vfs); + return correctResourcePath({ { materials } }, resPath, vfs); } VFS::Path::Normalized Misc::ResourceHelpers::correctMeshPath(VFS::Path::NormalizedView resPath) diff --git a/components/misc/resourcehelpers.hpp b/components/misc/resourcehelpers.hpp index fb355d6b94..cbf5c8fea4 100644 --- a/components/misc/resourcehelpers.hpp +++ b/components/misc/resourcehelpers.hpp @@ -25,16 +25,17 @@ namespace Misc namespace ResourceHelpers { bool changeExtensionToDds(std::string& path); - std::string correctResourcePath(std::span topLevelDirectories, std::string_view resPath, - const VFS::Manager* vfs, std::string_view ext = {}); - std::string correctTexturePath(std::string_view resPath, const VFS::Manager* vfs); - std::string correctIconPath(std::string_view resPath, const VFS::Manager* vfs); - std::string correctBookartPath(std::string_view resPath, const VFS::Manager* vfs); - std::string correctBookartPath(std::string_view resPath, int width, int height, const VFS::Manager* vfs); + VFS::Path::Normalized correctResourcePath(std::span topLevelDirectories, + VFS::Path::NormalizedView resPath, const VFS::Manager& vfs, std::string_view ext = {}); + VFS::Path::Normalized correctTexturePath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs); + VFS::Path::Normalized correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs); + VFS::Path::Normalized correctBookartPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs); + VFS::Path::Normalized correctBookartPath( + VFS::Path::NormalizedView resPath, int width, int height, const VFS::Manager& vfs); /// Use "xfoo.nif" instead of "foo.nif" if "xfoo.kf" is available /// Note that if "xfoo.nif" is actually unavailable, we can't fall back to "foo.nif". :( VFS::Path::Normalized correctActorModelPath(VFS::Path::NormalizedView resPath, const VFS::Manager* vfs); - std::string correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs); + VFS::Path::Normalized correctMaterialPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs); // Prepends "meshes/". VFS::Path::Normalized correctMeshPath(VFS::Path::NormalizedView resPath); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 12aae46a49..42f57d729f 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -568,7 +568,7 @@ namespace NifOsg if (st) { if (st->mExternal) - return getTextureImage(st->mFile); + return getTextureImage(VFS::Path::toNormalized(st->mFile)); if (!st->mData.empty()) return handleInternalTexture(st->mData.getPtr()); @@ -1065,13 +1065,12 @@ namespace NifOsg } } - osg::ref_ptr getTextureImage(std::string_view path) const + osg::ref_ptr getTextureImage(VFS::Path::NormalizedView path) const { if (!mImageManager) return nullptr; - return mImageManager->getImage( - VFS::Path::toNormalized(Misc::ResourceHelpers::correctTexturePath(path, mImageManager->getVFS()))); + return mImageManager->getImage(Misc::ResourceHelpers::correctTexturePath(path, *mImageManager->getVFS())); } static osg::ref_ptr attachTexture(const std::string& name, osg::ref_ptr image, @@ -1095,8 +1094,9 @@ namespace NifOsg return texture2d; } - osg::ref_ptr attachExternalTexture(const std::string& name, const std::string& path, bool wrapS, - bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector& boundTextures) const + osg::ref_ptr attachExternalTexture(const std::string& name, VFS::Path::NormalizedView path, + bool wrapS, bool wrapT, unsigned int uvSet, osg::StateSet* stateset, + std::vector& boundTextures) const { return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures); } @@ -2223,18 +2223,19 @@ namespace NifOsg } static Bgsm::MaterialFilePtr getShaderMaterial( - std::string_view path, Resource::BgsmFileManager* materialManager) + VFS::Path::NormalizedView path, Resource::BgsmFileManager* materialManager) { if (!materialManager) return nullptr; - if (!Misc::StringUtils::ciEndsWith(path, ".bgem") && !Misc::StringUtils::ciEndsWith(path, ".bgsm")) + if (!path.value().ends_with(".bgem") && !path.value().ends_with(".bgsm")) return nullptr; - std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, materialManager->getVFS()); + const VFS::Path::Normalized normalizedPath + = Misc::ResourceHelpers::correctMaterialPath(path, *materialManager->getVFS()); try { - return materialManager->get(VFS::Path::Normalized(normalizedPath)); + return materialManager->get(normalizedPath); } catch (std::exception& e) { @@ -2254,14 +2255,16 @@ namespace NifOsg const Bgsm::BGSMFile* bgsm = static_cast(material); if (!bgsm->mDiffuseMap.empty()) - attachExternalTexture( - "diffuseMap", bgsm->mDiffuseMap, wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("diffuseMap", VFS::Path::toNormalized(bgsm->mDiffuseMap), wrapS, wrapT, uvSet, + stateset, boundTextures); if (!bgsm->mNormalMap.empty()) - attachExternalTexture("normalMap", bgsm->mNormalMap, wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("normalMap", VFS::Path::toNormalized(bgsm->mNormalMap), wrapS, wrapT, uvSet, + stateset, boundTextures); if (bgsm->mGlowMapEnabled && !bgsm->mGlowMap.empty()) - attachExternalTexture("emissiveMap", bgsm->mGlowMap, wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("emissiveMap", VFS::Path::toNormalized(bgsm->mGlowMap), wrapS, wrapT, uvSet, + stateset, boundTextures); if (bgsm->mTree) stateset->addUniform(new osg::Uniform("useTreeAnim", true)); @@ -2271,7 +2274,8 @@ namespace NifOsg const Bgsm::BGEMFile* bgem = static_cast(material); if (!bgem->mBaseMap.empty()) - attachExternalTexture("diffuseMap", bgem->mBaseMap, wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("diffuseMap", VFS::Path::toNormalized(bgem->mBaseMap), wrapS, wrapT, uvSet, + stateset, boundTextures); bool useFalloff = bgem->mFalloff; stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); @@ -2386,16 +2390,16 @@ namespace NifOsg switch (static_cast(i)) { case Nif::BSShaderTextureSet::TextureType::Base: - attachExternalTexture( - "diffuseMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("diffuseMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS, + wrapT, uvSet, stateset, boundTextures); break; case Nif::BSShaderTextureSet::TextureType::Normal: - attachExternalTexture( - "normalMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("normalMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS, + wrapT, uvSet, stateset, boundTextures); break; case Nif::BSShaderTextureSet::TextureType::Glow: - attachExternalTexture( - "emissiveMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); + attachExternalTexture("emissiveMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS, + wrapT, uvSet, stateset, boundTextures); break; default: { @@ -2578,8 +2582,8 @@ namespace NifOsg if (!texprop->mFilename.empty()) { const unsigned int uvSet = 0; - attachExternalTexture("diffuseMap", texprop->mFilename, texprop->wrapS(), texprop->wrapT(), - uvSet, stateset, boundTextures); + attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mFilename), + texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures); } if (mBethVersion >= 27) { @@ -2599,7 +2603,8 @@ namespace NifOsg node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); clearBoundTextures(stateset, boundTextures); - if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) + if (Bgsm::MaterialFilePtr material + = getShaderMaterial(VFS::Path::toNormalized(texprop->mName), mMaterialManager)) { handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures); break; @@ -2626,7 +2631,8 @@ namespace NifOsg node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); clearBoundTextures(stateset, boundTextures); - if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) + if (Bgsm::MaterialFilePtr material + = getShaderMaterial(VFS::Path::toNormalized(texprop->mName), mMaterialManager)) { handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures); break; @@ -2635,8 +2641,8 @@ namespace NifOsg { const unsigned int uvSet = 0; unsigned int texUnit = static_cast(boundTextures.size()); - attachExternalTexture("diffuseMap", texprop->mSourceTexture, texprop->wrapS(), texprop->wrapT(), - uvSet, stateset, boundTextures); + attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mSourceTexture), + texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures); { osg::ref_ptr texMat(new osg::TexMat); // This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController) @@ -2845,7 +2851,8 @@ namespace NifOsg case Nif::RC_BSLightingShaderProperty: { auto shaderprop = static_cast(property); - if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) + if (Bgsm::MaterialFilePtr shaderMat + = getShaderMaterial(VFS::Path::toNormalized(shaderprop->mName), mMaterialManager)) { handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha); if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) @@ -2871,7 +2878,8 @@ namespace NifOsg case Nif::RC_BSEffectShaderProperty: { auto shaderprop = static_cast(property); - if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) + if (Bgsm::MaterialFilePtr shaderMat + = getShaderMaterial(VFS::Path::toNormalized(shaderprop->mName), mMaterialManager)) { handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha); break;