mirror of https://github.com/OpenMW/openmw
Merge branch 'vfs_normalized_path_25' into 'master'
Use normalized path for correctResourcePath and related functions (#8138) See merge request OpenMW/openmw!5031
This commit is contained in:
commit
cd4901245e
|
|
@ -9,6 +9,12 @@ namespace Misc::ResourceHelpers
|
||||||
{
|
{
|
||||||
using namespace ::testing;
|
using namespace ::testing;
|
||||||
|
|
||||||
|
constexpr VFS::Path::NormalizedView sound("sound");
|
||||||
|
constexpr VFS::Path::NormalizedView textures("textures");
|
||||||
|
constexpr VFS::Path::NormalizedView bookart("bookart");
|
||||||
|
constexpr VFS::Path::ExtensionView mp3("mp3");
|
||||||
|
constexpr VFS::Path::ExtensionView b("b");
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs)
|
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||||
|
|
@ -25,112 +31,91 @@ namespace Misc::ResourceHelpers
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfBothExistsInVfs)
|
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfBothExistsInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
|
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
|
||||||
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
|
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||||
{ wav, nullptr },
|
{ wavPath, nullptr },
|
||||||
{ mp3, nullptr },
|
{ mp3Path, nullptr },
|
||||||
});
|
});
|
||||||
EXPECT_EQ(correctSoundPath(wav, *vfs), "sound/foo.wav");
|
EXPECT_EQ(correctSoundPath(wavPath, *vfs), "sound/foo.wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfDoesNotExistInVfs)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtensionIfDoesNotExistInVfs)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
const std::unique_ptr<const VFS::Manager> 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)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtensionIfBothExistInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
|
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
|
||||||
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
|
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||||
{ wav, nullptr },
|
{ wavPath, nullptr },
|
||||||
{ mp3, nullptr },
|
{ mp3Path, nullptr },
|
||||||
});
|
});
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.mp3");
|
EXPECT_EQ(correctResourcePath({ { sound } }, wavPath, *vfs, mp3), "sound/foo.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtentionIfExistInVfs)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtensionIfExistInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
|
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||||
{ wav, nullptr },
|
{ wavPath, nullptr },
|
||||||
});
|
});
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.wav");
|
EXPECT_EQ(correctResourcePath({ { sound } }, wavPath, *vfs, mp3), "sound/foo.wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView path("foo.mp3");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
const std::unique_ptr<const VFS::Manager> 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)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView a("textures/foo.a");
|
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
|
||||||
|
constexpr VFS::Path::NormalizedView aPath("textures/foo.a");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||||
{ a, nullptr },
|
{ aPath, nullptr },
|
||||||
});
|
});
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.a");
|
||||||
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.a");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs)
|
||||||
{
|
{
|
||||||
constexpr VFS::Path::NormalizedView b("textures/foo.b");
|
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
|
||||||
|
constexpr VFS::Path::NormalizedView bPath("textures/foo.b");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||||
{ b, nullptr },
|
{ bPath, nullptr },
|
||||||
});
|
});
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.b");
|
||||||
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.b");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldLowerCase)
|
|
||||||
{
|
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveLeadingSlash)
|
|
||||||
{
|
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveDuplicateSlashes)
|
|
||||||
{
|
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\\\SOUND\\\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldConvertToForwardSlash)
|
|
||||||
{
|
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
|
||||||
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND/Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory)
|
TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView path("sound");
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
const std::unique_ptr<const VFS::Manager> 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<std::string>
|
struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<VFS::Path::NormalizedView>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected)
|
TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected)
|
||||||
{
|
{
|
||||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
const std::unique_ptr<const VFS::Manager> 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<std::string> pathsWithPrefix = {
|
const std::vector<VFS::Path::NormalizedView> pathsWithPrefix = {
|
||||||
"data/sound/foo.mp3",
|
VFS::Path::NormalizedView("data/sound/foo.mp3"),
|
||||||
"data/notsound/sound/foo.mp3",
|
VFS::Path::NormalizedView("data/notsound/sound/foo.mp3"),
|
||||||
"data/soundnot/sound/foo.mp3",
|
VFS::Path::NormalizedView("data/soundnot/sound/foo.mp3"),
|
||||||
"data/notsoundnot/sound/foo.mp3",
|
VFS::Path::NormalizedView("data/notsoundnot/sound/foo.mp3"),
|
||||||
};
|
};
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,13 @@ namespace VFS::Path
|
||||||
{
|
{
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
|
template <class T0, class T1>
|
||||||
|
struct TypePair
|
||||||
|
{
|
||||||
|
using Type0 = T0;
|
||||||
|
using Type1 = T1;
|
||||||
|
};
|
||||||
|
|
||||||
struct VFSPathIsNormalizedTest : TestWithParam<std::pair<std::string_view, bool>>
|
struct VFSPathIsNormalizedTest : TestWithParam<std::pair<std::string_view, bool>>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
@ -53,6 +60,80 @@ namespace VFS::Path
|
||||||
EXPECT_EQ(value, "foo");
|
EXPECT_EQ(value, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(VFSPathExtensionViewTest, shouldSupportDefaultConstructor)
|
||||||
|
{
|
||||||
|
constexpr ExtensionView extension;
|
||||||
|
EXPECT_TRUE(extension.empty());
|
||||||
|
EXPECT_EQ(extension.value(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VFSPathExtensionViewTest, shouldSupportConstexprConstructorFromConstCharPtr)
|
||||||
|
{
|
||||||
|
constexpr ExtensionView extension("png");
|
||||||
|
EXPECT_FALSE(extension.empty());
|
||||||
|
EXPECT_EQ(extension.value(), "png");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionOnNotNormalizedValue)
|
||||||
|
{
|
||||||
|
EXPECT_THROW([] { ExtensionView("PNG"); }(), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsExtensionSeparator)
|
||||||
|
{
|
||||||
|
EXPECT_THROW([] { ExtensionView(".png"); }(), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsSeparator)
|
||||||
|
{
|
||||||
|
EXPECT_THROW([] { ExtensionView("/png"); }(), std::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct VFSPathExtensionViewOperatorsTest : Test
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest);
|
||||||
|
|
||||||
|
TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsEqual)
|
||||||
|
{
|
||||||
|
using Type0 = typename TypeParam::Type0;
|
||||||
|
using Type1 = typename TypeParam::Type1;
|
||||||
|
const Type0 extension{ "png" };
|
||||||
|
const Type1 otherEqual{ "png" };
|
||||||
|
const Type1 otherNotEqual{ "jpg" };
|
||||||
|
EXPECT_EQ(extension, otherEqual);
|
||||||
|
EXPECT_EQ(otherEqual, extension);
|
||||||
|
EXPECT_NE(extension, otherNotEqual);
|
||||||
|
EXPECT_NE(otherNotEqual, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsLess)
|
||||||
|
{
|
||||||
|
using Type0 = typename TypeParam::Type0;
|
||||||
|
using Type1 = typename TypeParam::Type1;
|
||||||
|
const Type0 extension{ "png" };
|
||||||
|
const Type1 otherEqual{ "png" };
|
||||||
|
const Type1 otherLess{ "jpg" };
|
||||||
|
const Type1 otherGreater{ "tga" };
|
||||||
|
EXPECT_FALSE(extension < otherEqual);
|
||||||
|
EXPECT_FALSE(otherEqual < extension);
|
||||||
|
EXPECT_LT(otherLess, extension);
|
||||||
|
EXPECT_FALSE(extension < otherLess);
|
||||||
|
EXPECT_LT(extension, otherGreater);
|
||||||
|
EXPECT_FALSE(otherGreater < extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest, supportsEqual, supportsLess);
|
||||||
|
|
||||||
|
using VFSPathExtensionViewOperatorsTypePairs
|
||||||
|
= Types<TypePair<ExtensionView, ExtensionView>, TypePair<ExtensionView, const char*>,
|
||||||
|
TypePair<ExtensionView, std::string>, TypePair<ExtensionView, std::string_view>>;
|
||||||
|
|
||||||
|
INSTANTIATE_TYPED_TEST_SUITE_P(
|
||||||
|
Typed, VFSPathExtensionViewOperatorsTest, VFSPathExtensionViewOperatorsTypePairs);
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, shouldSupportDefaultConstructor)
|
TEST(VFSPathNormalizedTest, shouldSupportDefaultConstructor)
|
||||||
{
|
{
|
||||||
const Normalized value;
|
const Normalized value;
|
||||||
|
|
@ -131,41 +212,34 @@ namespace VFS::Path
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot)
|
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot)
|
||||||
{
|
{
|
||||||
Normalized value("foo/bar.a");
|
Normalized value("foo/ba.r.a");
|
||||||
ASSERT_TRUE(value.changeExtension("so"));
|
constexpr ExtensionView extension("so");
|
||||||
EXPECT_EQ(value.value(), "foo/bar.so");
|
ASSERT_TRUE(value.changeExtension(extension));
|
||||||
}
|
EXPECT_EQ(value.value(), "foo/ba.r.so");
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnNotNormalizedExtension)
|
|
||||||
{
|
|
||||||
Normalized value("foo/bar.a");
|
|
||||||
EXPECT_THROW(value.changeExtension("\\SO"), std::invalid_argument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot)
|
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot)
|
||||||
{
|
{
|
||||||
Normalized value("foo/bar");
|
Normalized value("foo/bar");
|
||||||
ASSERT_FALSE(value.changeExtension("so"));
|
constexpr ExtensionView extension("so");
|
||||||
|
ASSERT_FALSE(value.changeExtension(extension));
|
||||||
EXPECT_EQ(value.value(), "foo/bar");
|
EXPECT_EQ(value.value(), "foo/bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithDotBeforeSeparator)
|
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithDotBeforeSeparator)
|
||||||
{
|
{
|
||||||
Normalized value("foo.bar/baz");
|
Normalized value("foo.bar/baz");
|
||||||
ASSERT_FALSE(value.changeExtension("so"));
|
constexpr ExtensionView extension("so");
|
||||||
|
ASSERT_FALSE(value.changeExtension(extension));
|
||||||
EXPECT_EQ(value.value(), "foo.bar/baz");
|
EXPECT_EQ(value.value(), "foo.bar/baz");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithDot)
|
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceWithShorterExtension)
|
||||||
{
|
{
|
||||||
Normalized value("foo.a");
|
Normalized value("foo/bar.nif");
|
||||||
EXPECT_THROW(value.changeExtension(".so"), std::invalid_argument);
|
constexpr ExtensionView extension("kf");
|
||||||
}
|
ASSERT_TRUE(value.changeExtension(extension));
|
||||||
|
EXPECT_EQ(value.value(), "foo/bar.kf");
|
||||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithSeparator)
|
|
||||||
{
|
|
||||||
Normalized value("foo.a");
|
|
||||||
EXPECT_THROW(value.changeExtension("so/"), std::invalid_argument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(VFSPathNormalizedTest, filenameShouldReturnLastComponentOfThePath)
|
TEST(VFSPathNormalizedTest, filenameShouldReturnLastComponentOfThePath)
|
||||||
|
|
@ -218,20 +292,14 @@ namespace VFS::Path
|
||||||
|
|
||||||
REGISTER_TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest, supportsEqual, supportsLess);
|
REGISTER_TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest, supportsEqual, supportsLess);
|
||||||
|
|
||||||
template <class T0, class T1>
|
using VFSPathNormalizedOperatorsTypePairs
|
||||||
struct TypePair
|
= Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
|
||||||
{
|
TypePair<Normalized, std::string>, TypePair<Normalized, std::string_view>,
|
||||||
using Type0 = T0;
|
TypePair<Normalized, NormalizedView>, TypePair<NormalizedView, Normalized>,
|
||||||
using Type1 = T1;
|
TypePair<NormalizedView, const char*>, TypePair<NormalizedView, std::string>,
|
||||||
};
|
TypePair<NormalizedView, std::string_view>, TypePair<NormalizedView, NormalizedView>>;
|
||||||
|
|
||||||
using TypePairs = Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
|
INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, VFSPathNormalizedOperatorsTypePairs);
|
||||||
TypePair<Normalized, std::string>, TypePair<Normalized, std::string_view>,
|
|
||||||
TypePair<Normalized, NormalizedView>, TypePair<NormalizedView, Normalized>,
|
|
||||||
TypePair<NormalizedView, const char*>, TypePair<NormalizedView, std::string>,
|
|
||||||
TypePair<NormalizedView, std::string_view>, TypePair<NormalizedView, NormalizedView>>;
|
|
||||||
|
|
||||||
INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, TypePairs);
|
|
||||||
|
|
||||||
TEST(VFSPathNormalizedViewTest, shouldSupportConstructorFromNormalized)
|
TEST(VFSPathNormalizedViewTest, shouldSupportConstructorFromNormalized)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ namespace MWGui
|
||||||
const ESM::BirthSign* birth = store.get<ESM::BirthSign>().find(mCurrentBirthId);
|
const ESM::BirthSign* birth = store.get<ESM::BirthSign>().find(mCurrentBirthId);
|
||||||
|
|
||||||
mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath(
|
mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath(
|
||||||
birth->mTexture, MWBase::Environment::get().getResourceSystem()->getVFS()));
|
VFS::Path::toNormalized(birth->mTexture), *MWBase::Environment::get().getResourceSystem()->getVFS()));
|
||||||
|
|
||||||
std::vector<ESM::RefId> abilities, powers, spells;
|
std::vector<ESM::RefId> abilities, powers, spells;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1196,8 +1196,8 @@ namespace MWGui
|
||||||
if (const auto* id = classId.getIf<ESM::StringRefId>())
|
if (const auto* id = classId.getIf<ESM::StringRefId>())
|
||||||
{
|
{
|
||||||
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
classImage
|
classImage = Misc::ResourceHelpers::correctTexturePath(
|
||||||
= Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + id->getValue() + ".dds", vfs);
|
VFS::Path::toNormalized("textures\\levelup\\" + id->getValue() + ".dds"), *vfs);
|
||||||
if (!vfs->exists(classImage))
|
if (!vfs->exists(classImage))
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";
|
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";
|
||||||
|
|
|
||||||
|
|
@ -330,15 +330,16 @@ namespace MWGui::Formatting
|
||||||
if (auto heightIt = attr.find("height"); heightIt != attr.end())
|
if (auto heightIt = attr.find("height"); heightIt != attr.end())
|
||||||
height = MyGUI::utility::parseInt(heightIt->second);
|
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();
|
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
|
|
||||||
std::string correctedSrc;
|
VFS::Path::Normalized correctedSrc;
|
||||||
|
|
||||||
constexpr std::string_view imgPrefix = "img://";
|
constexpr std::string_view imgPrefix = "img://";
|
||||||
if (src.starts_with(imgPrefix))
|
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)
|
if (width == 0)
|
||||||
{
|
{
|
||||||
width = 50;
|
width = 50;
|
||||||
|
|
@ -351,7 +352,8 @@ namespace MWGui::Formatting
|
||||||
{
|
{
|
||||||
if (width == 0 || height == 0)
|
if (width == 0 || height == 0)
|
||||||
continue;
|
continue;
|
||||||
correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs);
|
correctedSrc = Misc::ResourceHelpers::correctBookartPath(
|
||||||
|
VFS::Path::toNormalized(src), width, height, *vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vfs->exists(correctedSrc))
|
if (!vfs->exists(correctedSrc))
|
||||||
|
|
|
||||||
|
|
@ -386,9 +386,9 @@ namespace MWGui
|
||||||
std::replace(icon.begin(), icon.end(), '/', '\\');
|
std::replace(icon.begin(), icon.end(), '/', '\\');
|
||||||
size_t slashPos = icon.rfind('\\');
|
size_t slashPos = icon.rfind('\\');
|
||||||
icon.insert(slashPos + 1, "b_");
|
icon.insert(slashPos + 1, "b_");
|
||||||
icon = Misc::ResourceHelpers::correctIconPath(
|
const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath(
|
||||||
icon, MWBase::Environment::get().getResourceSystem()->getVFS());
|
VFS::Path::toNormalized(icon), *MWBase::Environment::get().getResourceSystem()->getVFS());
|
||||||
mSpellImage->setSpellIcon(icon);
|
mSpellImage->setSpellIcon(iconPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mSpellImage->setSpellIcon({});
|
mSpellImage->setSpellIcon({});
|
||||||
|
|
|
||||||
|
|
@ -136,16 +136,17 @@ namespace MWGui
|
||||||
|
|
||||||
void ItemWidget::setIcon(const MWWorld::Ptr& ptr)
|
void ItemWidget::setIcon(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView defaultIcon("default icon.tga");
|
||||||
std::string_view icon = ptr.getClass().getInventoryIcon(ptr);
|
std::string_view icon = ptr.getClass().getInventoryIcon(ptr);
|
||||||
if (icon.empty())
|
if (icon.empty())
|
||||||
icon = "default icon.tga";
|
icon = defaultIcon.value();
|
||||||
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
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))
|
if (!vfs->exists(invIcon))
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Failed to open image: '" << invIcon
|
Log(Debug::Error) << "Failed to open image: '" << invIcon << "' not found, falling back to '"
|
||||||
<< "' not found, falling back to 'default-icon.tga'";
|
<< defaultIcon.value() << "'";
|
||||||
invIcon = Misc::ResourceHelpers::correctIconPath("default icon.tga", vfs);
|
invIcon = Misc::ResourceHelpers::correctIconPath(defaultIcon, *vfs);
|
||||||
}
|
}
|
||||||
setIcon(invIcon);
|
setIcon(invIcon);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,8 @@ namespace MWGui
|
||||||
std::string path = effect->mIcon;
|
std::string path = effect->mIcon;
|
||||||
std::replace(path.begin(), path.end(), '/', '\\');
|
std::replace(path.begin(), path.end(), '/', '\\');
|
||||||
path.insert(path.rfind('\\') + 1, "b_");
|
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;
|
float scale = 1.f;
|
||||||
MyGUI::ITexture* texture
|
MyGUI::ITexture* texture
|
||||||
|
|
@ -335,7 +336,7 @@ namespace MWGui
|
||||||
|
|
||||||
const int diameter = static_cast<int>(44 * scale);
|
const int diameter = static_cast<int>(44 * scale);
|
||||||
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(0, 0, diameter, diameter));
|
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)
|
if (mMagicSelectionDialog)
|
||||||
mMagicSelectionDialog->setVisible(false);
|
mMagicSelectionDialog->setVisible(false);
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ namespace MWGui
|
||||||
void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect)
|
void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect)
|
||||||
{
|
{
|
||||||
mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath(
|
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) + "}");
|
mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,8 +150,9 @@ namespace MWGui
|
||||||
"ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default);
|
"ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default);
|
||||||
mWidgetMap[effectId] = image;
|
mWidgetMap[effectId] = image;
|
||||||
|
|
||||||
image->setImageTexture(Misc::ResourceHelpers::correctIconPath(
|
image->setImageTexture(
|
||||||
effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS()));
|
Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon),
|
||||||
|
*MWBase::Environment::get().getResourceSystem()->getVFS()));
|
||||||
|
|
||||||
const std::string& name = ESM::MagicEffect::indexToGmstString(effectId);
|
const std::string& name = ESM::MagicEffect::indexToGmstString(effectId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -447,8 +447,8 @@ namespace MWGui
|
||||||
|
|
||||||
const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2;
|
const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2;
|
||||||
|
|
||||||
const std::string realImage
|
const VFS::Path::Normalized realImage = Misc::ResourceHelpers::correctIconPath(
|
||||||
= Misc::ResourceHelpers::correctIconPath(image, MWBase::Environment::get().getResourceSystem()->getVFS());
|
VFS::Path::toNormalized(image), *MWBase::Environment::get().getResourceSystem()->getVFS());
|
||||||
|
|
||||||
Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(
|
Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(
|
||||||
"NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
|
"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("ToolTipType", "Layout");
|
||||||
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
|
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
|
||||||
widget->setUserString(
|
widget->setUserString("ImageTexture_BirthSignImage",
|
||||||
"ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs));
|
Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(sign->mTexture), *vfs));
|
||||||
widget->setUserString("Caption_BirthSignName", sign->mName);
|
widget->setUserString("Caption_BirthSignName", sign->mName);
|
||||||
widget->setUserString("Caption_BirthSignDescription", sign->mDescription);
|
widget->setUserString("Caption_BirthSignDescription", sign->mDescription);
|
||||||
|
|
||||||
|
|
@ -962,7 +962,8 @@ namespace MWGui
|
||||||
|
|
||||||
std::string icon = effect->mIcon;
|
std::string icon = effect->mIcon;
|
||||||
icon.insert(icon.rfind('\\') + 1, "b_");
|
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("ToolTipType", "Layout");
|
||||||
widget->setUserString("ToolTipLayout", "MagicEffectToolTip");
|
widget->setUserString("ToolTipLayout", "MagicEffectToolTip");
|
||||||
|
|
@ -972,6 +973,6 @@ namespace MWGui
|
||||||
"#{sSchool}: "
|
"#{sSchool}: "
|
||||||
+ MyGUI::TextIterator::toTagsString(
|
+ MyGUI::TextIterator::toTagsString(
|
||||||
store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName));
|
store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName));
|
||||||
widget->setUserString("ImageTexture_MagicEffectImage", icon);
|
widget->setUserString("ImageTexture_MagicEffectImage", iconPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ namespace MWGui::Widgets
|
||||||
mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;
|
mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;
|
||||||
|
|
||||||
mImageWidget->setImageTexture(Misc::ResourceHelpers::correctIconPath(
|
mImageWidget->setImageTexture(Misc::ResourceHelpers::correctIconPath(
|
||||||
magicEffect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS()));
|
VFS::Path::toNormalized(magicEffect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS()));
|
||||||
}
|
}
|
||||||
|
|
||||||
MWSpellEffect::~MWSpellEffect() {}
|
MWSpellEffect::~MWSpellEffect() {}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace MWLua
|
||||||
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
|
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
|
||||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
|
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 {
|
signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table {
|
||||||
return createReadOnlyRefIdTable(lua, rec.mPowers.mList);
|
return createReadOnlyRefIdTable(lua, rec.mPowers.mList);
|
||||||
|
|
|
||||||
|
|
@ -394,7 +394,7 @@ namespace MWLua
|
||||||
});
|
});
|
||||||
magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string {
|
magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string {
|
||||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
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"]
|
magicEffectT["particle"]
|
||||||
= sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; });
|
= sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; });
|
||||||
|
|
|
||||||
|
|
@ -649,7 +649,7 @@ namespace MWLua
|
||||||
attributeT["description"]
|
attributeT["description"]
|
||||||
= sol::readonly_property([](const ESM::Attribute& rec) -> std::string_view { return rec.mDescription; });
|
= 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 {
|
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);
|
sol::table skills(lua, sol::create);
|
||||||
|
|
@ -669,7 +669,7 @@ namespace MWLua
|
||||||
return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization);
|
return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization);
|
||||||
});
|
});
|
||||||
skillT["icon"] = sol::readonly_property([vfs](const ESM::Skill& rec) -> std::string {
|
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* {
|
skillT["school"] = sol::readonly_property([](const ESM::Skill& rec) -> const ESM::MagicSchool* {
|
||||||
if (!rec.mSchool)
|
if (!rec.mSchool)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace MWLua
|
||||||
record["mwscript"]
|
record["mwscript"]
|
||||||
= sol::readonly_property([](const ESM::Apparatus& rec) -> ESM::RefId { return rec.mScript; });
|
= sol::readonly_property([](const ESM::Apparatus& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
|
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["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; });
|
record["value"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mValue; });
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ namespace MWLua
|
||||||
record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; });
|
record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; });
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
|
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["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; });
|
record["mwscript"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ namespace MWLua
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["mwscript"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mScript; });
|
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 {
|
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["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; });
|
record["enchant"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mEnchant; });
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ namespace MWLua
|
||||||
record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; });
|
record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; });
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string {
|
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["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; });
|
record["mwscript"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace MWLua
|
||||||
record["mwscript"]
|
record["mwscript"]
|
||||||
= sol::readonly_property([](const ESM::Ingredient& rec) -> ESM::RefId { return rec.mScript; });
|
= sol::readonly_property([](const ESM::Ingredient& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
|
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"]
|
record["weight"]
|
||||||
= sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; });
|
= sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; });
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ namespace MWLua
|
||||||
record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; });
|
record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; });
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
|
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"]
|
record["sound"]
|
||||||
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });
|
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace MWLua
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["mwscript"] = sol::readonly_property([](const ESM::Lockpick& rec) -> ESM::RefId { return rec.mScript; });
|
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 {
|
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"]
|
record["maxCondition"]
|
||||||
= sol::readonly_property([](const ESM::Lockpick& rec) -> int { return rec.mData.mUses; });
|
= sol::readonly_property([](const ESM::Lockpick& rec) -> int { return rec.mData.mUses; });
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ namespace MWLua
|
||||||
record["mwscript"]
|
record["mwscript"]
|
||||||
= sol::readonly_property([](const ESM::Miscellaneous& rec) -> ESM::RefId { return rec.mScript; });
|
= sol::readonly_property([](const ESM::Miscellaneous& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
|
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(
|
record["isKey"] = sol::readonly_property(
|
||||||
[](const ESM::Miscellaneous& rec) -> bool { return rec.mData.mFlags & ESM::Miscellaneous::Key; });
|
[](const ESM::Miscellaneous& rec) -> bool { return rec.mData.mFlags & ESM::Miscellaneous::Key; });
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ namespace MWLua
|
||||||
record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; });
|
record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; });
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
|
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["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; });
|
record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; });
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace MWLua
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["mwscript"] = sol::readonly_property([](const ESM::Probe& rec) -> ESM::RefId { return rec.mScript; });
|
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 {
|
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["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; });
|
record["value"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mValue; });
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace MWLua
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["mwscript"] = sol::readonly_property([](const ESM::Repair& rec) -> ESM::RefId { return rec.mScript; });
|
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 {
|
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["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; });
|
record["value"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mValue; });
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ namespace MWLua
|
||||||
record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; });
|
record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; });
|
||||||
addModelProperty(record);
|
addModelProperty(record);
|
||||||
record["icon"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string {
|
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["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; });
|
record["mwscript"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mScript; });
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,9 @@ namespace MWLua
|
||||||
weatherT["cloudSpeed"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudSpeed; },
|
weatherT["cloudSpeed"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudSpeed; },
|
||||||
[](MWWorld::Weather& w, const FiniteFloat cloudSpeed) { w.mCloudSpeed = cloudSpeed; });
|
[](MWWorld::Weather& w, const FiniteFloat cloudSpeed) { w.mCloudSpeed = cloudSpeed; });
|
||||||
weatherT["cloudTexture"] = sol::property(
|
weatherT["cloudTexture"] = sol::property(
|
||||||
[vfs](
|
[vfs](const MWWorld::Weather& w) -> std::string {
|
||||||
const MWWorld::Weather& w) { return Misc::ResourceHelpers::correctTexturePath(w.mCloudTexture, vfs); },
|
return Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(w.mCloudTexture), *vfs);
|
||||||
|
},
|
||||||
[](MWWorld::Weather& w, std::string_view cloudTexture) { w.mCloudTexture = cloudTexture; });
|
[](MWWorld::Weather& w, std::string_view cloudTexture) { w.mCloudTexture = cloudTexture; });
|
||||||
weatherT["cloudsMaximumPercent"]
|
weatherT["cloudsMaximumPercent"]
|
||||||
= sol::property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; },
|
= sol::property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; },
|
||||||
|
|
|
||||||
|
|
@ -659,21 +659,21 @@ namespace MWRender
|
||||||
|
|
||||||
path.replace(extensionStart, path.size() - extensionStart, "/");
|
path.replace(extensionStart, path.size() - extensionStart, "/");
|
||||||
|
|
||||||
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
for (const VFS::Path::Normalized& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(path))
|
for (const VFS::Path::Normalized& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(path))
|
||||||
{
|
if (name.extension() == kf)
|
||||||
if (Misc::getFileExtension(name) == "kf")
|
|
||||||
{
|
|
||||||
addSingleAnimSource(name, baseModel);
|
addSingleAnimSource(name, baseModel);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::addAnimSource(std::string_view model, const std::string& baseModel)
|
void Animation::addAnimSource(std::string_view model, const std::string& baseModel)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
|
constexpr VFS::Path::ExtensionView nif("nif");
|
||||||
|
|
||||||
VFS::Path::Normalized kfname(model);
|
VFS::Path::Normalized kfname(model);
|
||||||
|
|
||||||
if (Misc::getFileExtension(kfname) == "nif")
|
if (kfname.extension() == nif)
|
||||||
kfname.changeExtension("kf");
|
kfname.changeExtension(kf);
|
||||||
|
|
||||||
addSingleAnimSource(kfname, baseModel);
|
addSingleAnimSource(kfname, baseModel);
|
||||||
|
|
||||||
|
|
@ -757,10 +757,12 @@ namespace MWRender
|
||||||
// Get the blending rules
|
// Get the blending rules
|
||||||
if (Settings::game().mSmoothAnimTransitions)
|
if (Settings::game().mSmoothAnimTransitions)
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::ExtensionView yaml("yaml");
|
||||||
|
|
||||||
// Note, even if the actual config is .json - we should send a .yaml path to AnimBlendRulesManager, the
|
// Note, even if the actual config is .json - we should send a .yaml path to AnimBlendRulesManager, the
|
||||||
// manager will check for .json if it will not find a specified .yaml file.
|
// manager will check for .json if it will not find a specified .yaml file.
|
||||||
VFS::Path::Normalized blendConfigPath(kfname);
|
VFS::Path::Normalized blendConfigPath(kfname);
|
||||||
blendConfigPath.changeExtension("yaml");
|
blendConfigPath.changeExtension(yaml);
|
||||||
|
|
||||||
// globalBlendConfigPath is only used with actors! Objects have no default blending.
|
// globalBlendConfigPath is only used with actors! Objects have no default blending.
|
||||||
constexpr VFS::Path::NormalizedView globalBlendConfigPath("animations/animation-config.yaml");
|
constexpr VFS::Path::NormalizedView globalBlendConfigPath("animations/animation-config.yaml");
|
||||||
|
|
@ -1795,7 +1797,7 @@ namespace MWRender
|
||||||
// Notify that this animation has attached magic effects
|
// Notify that this animation has attached magic effects
|
||||||
mHasMagicEffects = true;
|
mHasMagicEffects = true;
|
||||||
|
|
||||||
overrideFirstRootTexture(texture, mResourceSystem, *node);
|
overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::removeEffect(std::string_view effectId)
|
void Animation::removeEffect(std::string_view effectId)
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ namespace MWRender
|
||||||
node->accept(assignVisitor);
|
node->accept(assignVisitor);
|
||||||
|
|
||||||
if (isMagicVFX)
|
if (isMagicVFX)
|
||||||
overrideFirstRootTexture(textureOverride, mResourceSystem, *node);
|
overrideFirstRootTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node);
|
||||||
else
|
else
|
||||||
overrideTexture(textureOverride, mResourceSystem, *node);
|
overrideTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node);
|
||||||
|
|
||||||
mParentNode->addChild(trans);
|
mParentNode->addChild(trans);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -744,10 +744,12 @@ namespace MWRender
|
||||||
if (activeGrid && type != ESM::REC_STAT && type != ESM::REC_STAT4)
|
if (activeGrid && type != ESM::REC_STAT && type != ESM::REC_STAT4)
|
||||||
{
|
{
|
||||||
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
|
model = Misc::ResourceHelpers::correctActorModelPath(model, mSceneManager->getVFS());
|
||||||
if (Misc::getFileExtension(model) == "nif")
|
constexpr VFS::Path::ExtensionView nif("nif");
|
||||||
|
if (model.extension() == nif)
|
||||||
{
|
{
|
||||||
VFS::Path::Normalized kfname = model;
|
VFS::Path::Normalized kfname = model;
|
||||||
kfname.changeExtension("kf");
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
|
kfname.changeExtension(kf);
|
||||||
if (mSceneManager->getVFS()->exists(kfname))
|
if (mSceneManager->getVFS()->exists(kfname))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -763,7 +763,7 @@ namespace MWRender
|
||||||
mClouds = weather.mCloudTexture;
|
mClouds = weather.mCloudTexture;
|
||||||
|
|
||||||
const VFS::Path::Normalized texture
|
const VFS::Path::Normalized texture
|
||||||
= Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS());
|
= Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(mClouds), *mSceneManager->getVFS());
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> cloudTex
|
osg::ref_ptr<osg::Texture2D> cloudTex
|
||||||
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));
|
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));
|
||||||
|
|
@ -785,8 +785,8 @@ namespace MWRender
|
||||||
|
|
||||||
if (!mNextClouds.empty())
|
if (!mNextClouds.empty())
|
||||||
{
|
{
|
||||||
const VFS::Path::Normalized texture
|
const VFS::Path::Normalized texture = Misc::ResourceHelpers::correctTexturePath(
|
||||||
= Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS());
|
VFS::Path::toNormalized(mNextClouds), *mSceneManager->getVFS());
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> cloudTex
|
osg::ref_ptr<osg::Texture2D> cloudTex
|
||||||
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));
|
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,9 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class TextureOverrideVisitor : public osg::NodeVisitor
|
struct TextureOverrideVisitor : osg::NodeVisitor
|
||||||
{
|
{
|
||||||
public:
|
explicit TextureOverrideVisitor(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourcesystem)
|
||||||
TextureOverrideVisitor(std::string_view texture, Resource::ResourceSystem* resourcesystem)
|
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
, mTexture(texture)
|
, mTexture(texture)
|
||||||
, mResourcesystem(resourcesystem)
|
, mResourcesystem(resourcesystem)
|
||||||
|
|
@ -35,23 +34,25 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
traverse(node);
|
traverse(node);
|
||||||
}
|
}
|
||||||
std::string_view mTexture;
|
|
||||||
|
VFS::Path::NormalizedView mTexture;
|
||||||
Resource::ResourceSystem* mResourcesystem;
|
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);
|
TextureOverrideVisitor overrideVisitor(texture, resourceSystem);
|
||||||
node.accept(overrideVisitor);
|
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())
|
if (texture.empty())
|
||||||
return;
|
return;
|
||||||
const VFS::Path::Normalized correctedTexture
|
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?
|
// Not sure if wrap settings should be pulled from the overridden texture?
|
||||||
osg::ref_ptr<osg::Texture2D> tex
|
osg::ref_ptr<osg::Texture2D> tex
|
||||||
= new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
|
= new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef OPENMW_MWRENDER_UTIL_H
|
#ifndef OPENMW_MWRENDER_UTIL_H
|
||||||
#define OPENMW_MWRENDER_UTIL_H
|
#define OPENMW_MWRENDER_UTIL_H
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include <osg/LightModel>
|
#include <osg/LightModel>
|
||||||
#include <osg/NodeCallback>
|
#include <osg/NodeCallback>
|
||||||
|
|
||||||
|
|
@ -21,9 +23,10 @@ namespace MWRender
|
||||||
// Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty
|
// 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
|
// of the .NIF file's root node, if it had a NiTexturingProperty. Used for applying "particle textures" to magic
|
||||||
// effects.
|
// 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.
|
// Node callback to entirely skip the traversal.
|
||||||
class NoTraverseCallback : public osg::NodeCallback
|
class NoTraverseCallback : public osg::NodeCallback
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ namespace MWSound
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView soundDir("sound");
|
||||||
|
constexpr VFS::Path::ExtensionView mp3("mp3");
|
||||||
|
|
||||||
struct AudioParams
|
struct AudioParams
|
||||||
{
|
{
|
||||||
float mAudioDefaultMinDistance;
|
float mAudioDefaultMinDistance;
|
||||||
|
|
@ -192,7 +195,7 @@ namespace MWSound
|
||||||
max = std::max(min, max);
|
max = std::max(min, max);
|
||||||
|
|
||||||
SoundBuffer& sfx = mSoundBuffers.emplace_back(
|
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);
|
mBufferNameMap.emplace(soundId, &sfx);
|
||||||
return &sfx;
|
return &sfx;
|
||||||
|
|
@ -200,22 +203,22 @@ namespace MWSound
|
||||||
|
|
||||||
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound)
|
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound)
|
||||||
{
|
{
|
||||||
std::string path = Misc::ResourceHelpers::correctResourcePath(
|
VFS::Path::Normalized path = Misc::ResourceHelpers::correctResourcePath({ { soundDir } },
|
||||||
{ { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
|
VFS::Path::toNormalized(sound.mSoundFile), *MWBase::Environment::get().getResourceSystem()->getVFS(), mp3);
|
||||||
float volume = 1, min = 1, max = 255; // TODO: needs research
|
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);
|
mBufferNameMap.emplace(soundId, &sfx);
|
||||||
return &sfx;
|
return &sfx;
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound)
|
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound)
|
||||||
{
|
{
|
||||||
std::string path = Misc::ResourceHelpers::correctResourcePath(
|
VFS::Path::Normalized path = Misc::ResourceHelpers::correctResourcePath({ { soundDir } },
|
||||||
{ { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
|
VFS::Path::toNormalized(sound.mSoundFile), *MWBase::Environment::get().getResourceSystem()->getVFS(), mp3);
|
||||||
float volume = 1, min = 1, max = 255; // TODO: needs research
|
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
|
// TODO: sound.mSoundId can link to another SoundReference, probably we will need to add additional lookups to
|
||||||
// ESMStore.
|
// 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);
|
mBufferNameMap.emplace(soundId, &sfx);
|
||||||
return &sfx;
|
return &sfx;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,12 @@ namespace MWWorld
|
||||||
if (!vfs.exists(mesh))
|
if (!vfs.exists(mesh))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Misc::getFileName(mesh).starts_with('x') && Misc::getFileExtension(mesh) == "nif")
|
constexpr VFS::Path::ExtensionView nif("nif");
|
||||||
|
if (Misc::getFileName(mesh).starts_with('x') && mesh.extension() == nif)
|
||||||
{
|
{
|
||||||
kfname = mesh;
|
kfname = mesh;
|
||||||
kfname.changeExtension("kf");
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
|
kfname.changeExtension(kf);
|
||||||
if (vfs.exists(kfname))
|
if (vfs.exists(kfname))
|
||||||
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
|
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ namespace MWWorld
|
||||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime);
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime);
|
||||||
state.mNode->accept(assignVisitor);
|
state.mNode->accept(assignVisitor);
|
||||||
|
|
||||||
MWRender::overrideFirstRootTexture(texture, mResourceSystem, *projectile);
|
MWRender::overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *projectile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::update(State& state, float duration)
|
void ProjectileManager::update(State& state, float duration)
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ namespace ESMTerrain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this is needed due to MWs messed up texture handling
|
// 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,
|
void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@
|
||||||
|
|
||||||
namespace
|
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 VFS::Path::ExtensionView dds("dds");
|
||||||
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
|
constexpr VFS::Path::ExtensionView nif("nif");
|
||||||
|
constexpr VFS::Path::ExtensionView mp3("mp3");
|
||||||
|
|
||||||
bool changeExtension(std::string& path, std::string_view ext)
|
bool changeExtension(std::string& path, std::string_view ext)
|
||||||
{
|
{
|
||||||
std::string::size_type pos = path.rfind('.');
|
std::string::size_type pos = path.rfind('.');
|
||||||
|
|
@ -26,14 +35,15 @@ namespace
|
||||||
return false;
|
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::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;)
|
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)
|
if (position == std::string_view::npos)
|
||||||
return std::string_view::npos;
|
return std::string_view::npos;
|
||||||
|
|
@ -50,13 +60,6 @@ namespace
|
||||||
|
|
||||||
return std::string_view::npos;
|
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)
|
bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path)
|
||||||
|
|
@ -65,51 +68,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.
|
// 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<const std::string_view> topLevelDirectories,
|
VFS::Path::Normalized Misc::ResourceHelpers::correctResourcePath(
|
||||||
std::string_view resPath, const VFS::Manager* vfs, std::string_view ext)
|
std::span<const VFS::Path::NormalizedView> topLevelDirectories, VFS::Path::NormalizedView resPath,
|
||||||
|
const VFS::Manager& vfs, VFS::Path::ExtensionView ext)
|
||||||
{
|
{
|
||||||
VFS::Path::Normalized correctedPath(resPath);
|
VFS::Path::Normalized correctedPath;
|
||||||
|
|
||||||
// Handle top level directory
|
// Handle top level directory
|
||||||
bool needsPrefix = true;
|
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)
|
topLevelPos != std::string::npos)
|
||||||
{
|
{
|
||||||
correctedPath = VFS::Path::Normalized(correctedPath.value().substr(topLevelPos));
|
correctedPath = VFS::Path::Normalized(resPath.value().substr(topLevelPos));
|
||||||
needsPrefix = false;
|
needsPrefix = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsPrefix)
|
if (needsPrefix)
|
||||||
correctedPath = withPrefix(correctedPath, topLevelDirectories.front());
|
correctedPath = topLevelDirectories.front() / resPath;
|
||||||
|
|
||||||
const VFS::Path::Normalized origExt = correctedPath;
|
const VFS::Path::Normalized origExt = correctedPath;
|
||||||
|
|
||||||
// replace extension if `ext` is specified (used for .tga -> .dds, .wav -> .mp3)
|
// replace extension if `ext` is specified (used for .tga -> .dds, .wav -> .mp3)
|
||||||
const bool isExtChanged = !ext.empty() && correctedPath.changeExtension(ext);
|
const bool isExtChanged = !ext.empty() && correctedPath.changeExtension(ext);
|
||||||
|
|
||||||
if (vfs->exists(correctedPath))
|
if (vfs.exists(correctedPath))
|
||||||
return correctedPath;
|
return correctedPath;
|
||||||
|
|
||||||
// fall back to original extension
|
// fall back to original extension
|
||||||
if (isExtChanged && vfs->exists(origExt))
|
if (isExtChanged && vfs.exists(origExt))
|
||||||
return origExt;
|
return origExt;
|
||||||
|
|
||||||
// fall back to a resource in the top level directory if it exists
|
// fall back to a resource in the top level directory if it exists
|
||||||
{
|
{
|
||||||
const VFS::Path::Normalized fallback = withPrefix(correctedPath.filename(), topLevelDirectories.front());
|
const VFS::Path::Normalized fallback = topLevelDirectories.front() / correctedPath.filename();
|
||||||
if (vfs->exists(fallback))
|
if (vfs.exists(fallback))
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExtChanged)
|
if (isExtChanged)
|
||||||
{
|
{
|
||||||
const VFS::Path::Normalized fallback = withPrefix(origExt.filename(), topLevelDirectories.front());
|
const VFS::Path::Normalized fallback = topLevelDirectories.front() / origExt.filename();
|
||||||
if (vfs->exists(fallback))
|
if (vfs.exists(fallback))
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,33 +124,36 @@ std::string Misc::ResourceHelpers::correctResourcePath(std::span<const std::stri
|
||||||
// but all texture file name references were kept as .tga. So we pass ext=".dds" to all helpers
|
// but all texture file name references were kept as .tga. So we pass ext=".dds" to all helpers
|
||||||
// looking for textures.
|
// looking for textures.
|
||||||
|
|
||||||
std::string Misc::ResourceHelpers::correctTexturePath(std::string_view resPath, const VFS::Manager* vfs)
|
VFS::Path::Normalized Misc::ResourceHelpers::correctTexturePath(
|
||||||
|
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
||||||
{
|
{
|
||||||
return correctResourcePath({ { "textures", "bookart" } }, resPath, vfs, "dds");
|
return correctResourcePath({ { textures, bookart } }, resPath, vfs, dds);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::ResourceHelpers::correctIconPath(std::string_view resPath, const VFS::Manager* vfs)
|
VFS::Path::Normalized Misc::ResourceHelpers::correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
||||||
{
|
{
|
||||||
return correctResourcePath({ { "icons" } }, resPath, vfs, "dds");
|
return correctResourcePath({ { icons } }, resPath, vfs, dds);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::ResourceHelpers::correctBookartPath(std::string_view resPath, const VFS::Manager* vfs)
|
VFS::Path::Normalized Misc::ResourceHelpers::correctBookartPath(
|
||||||
|
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
||||||
{
|
{
|
||||||
return correctResourcePath({ { "bookart", "textures" } }, resPath, vfs, "dds");
|
return correctResourcePath({ { bookart, textures } }, resPath, vfs, dds);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::ResourceHelpers::correctBookartPath(
|
VFS::Path::Normalized Misc::ResourceHelpers::correctBookartPath(
|
||||||
std::string_view resPath, int width, int height, const VFS::Manager* vfs)
|
VFS::Path::NormalizedView resPath, int width, int height, const VFS::Manager& vfs)
|
||||||
{
|
{
|
||||||
std::string image = correctBookartPath(resPath, vfs);
|
VFS::Path::Normalized image = correctBookartPath(resPath, vfs);
|
||||||
|
|
||||||
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
|
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
|
||||||
// So if the image isn't found, try appending the size.
|
// So if the image isn't found, try appending the size.
|
||||||
if (!vfs->exists(image))
|
if (!vfs.exists(image))
|
||||||
{
|
{
|
||||||
std::stringstream str;
|
std::stringstream str;
|
||||||
str << image.substr(0, image.rfind('.')) << "_" << width << "_" << height << image.substr(image.rfind('.'));
|
str << image.view().substr(0, image.view().rfind('.')) << "_" << width << "_" << height
|
||||||
image = Misc::ResourceHelpers::correctBookartPath(str.str(), vfs);
|
<< image.view().substr(image.view().rfind('.'));
|
||||||
|
image = Misc::ResourceHelpers::correctBookartPath(VFS::Path::Normalized(std::move(str).str()), vfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
|
@ -163,8 +170,8 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctActorModelPath(
|
||||||
mdlname.insert(mdlname.begin(), 'x');
|
mdlname.insert(mdlname.begin(), 'x');
|
||||||
|
|
||||||
VFS::Path::Normalized kfname(mdlname);
|
VFS::Path::Normalized kfname(mdlname);
|
||||||
if (Misc::getFileExtension(mdlname) == "nif")
|
if (kfname.extension() == nif)
|
||||||
kfname.changeExtension("kf");
|
kfname.changeExtension(kf);
|
||||||
|
|
||||||
if (!vfs->exists(kfname))
|
if (!vfs->exists(kfname))
|
||||||
return VFS::Path::Normalized(resPath);
|
return VFS::Path::Normalized(resPath);
|
||||||
|
|
@ -172,9 +179,10 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctActorModelPath(
|
||||||
return mdlname;
|
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)
|
VFS::Path::Normalized Misc::ResourceHelpers::correctMeshPath(VFS::Path::NormalizedView resPath)
|
||||||
|
|
@ -210,16 +218,16 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctSoundPath(
|
||||||
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
||||||
{
|
{
|
||||||
// Note: likely should be replaced with
|
// Note: likely should be replaced with
|
||||||
// return correctResourcePath({ { "sound" } }, resPath, vfs, "mp3");
|
// return correctResourcePath({ { "sound" } }, resPath, vfs, mp3);
|
||||||
// but there is a slight difference in behaviour:
|
// but there is a slight difference in behaviour:
|
||||||
// - `correctResourcePath(..., "mp3")` first checks `.mp3`, then tries the original extension
|
// - `correctResourcePath(..., mp3)` first checks `.mp3`, then tries the original extension
|
||||||
// - the implementation below first tries the original extension, then falls back to `.mp3`.
|
// - the implementation below first tries the original extension, then falls back to `.mp3`.
|
||||||
|
|
||||||
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
||||||
if (!vfs.exists(resPath))
|
if (!vfs.exists(resPath))
|
||||||
{
|
{
|
||||||
VFS::Path::Normalized sound(resPath);
|
VFS::Path::Normalized sound(resPath);
|
||||||
sound.changeExtension("mp3");
|
sound.changeExtension(mp3);
|
||||||
return sound;
|
return sound;
|
||||||
}
|
}
|
||||||
return VFS::Path::Normalized(resPath);
|
return VFS::Path::Normalized(resPath);
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,17 @@ namespace Misc
|
||||||
namespace ResourceHelpers
|
namespace ResourceHelpers
|
||||||
{
|
{
|
||||||
bool changeExtensionToDds(std::string& path);
|
bool changeExtensionToDds(std::string& path);
|
||||||
std::string correctResourcePath(std::span<const std::string_view> topLevelDirectories, std::string_view resPath,
|
VFS::Path::Normalized correctResourcePath(std::span<const VFS::Path::NormalizedView> topLevelDirectories,
|
||||||
const VFS::Manager* vfs, std::string_view ext = {});
|
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs, VFS::Path::ExtensionView ext = {});
|
||||||
std::string correctTexturePath(std::string_view resPath, const VFS::Manager* vfs);
|
VFS::Path::Normalized correctTexturePath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||||
std::string correctIconPath(std::string_view resPath, const VFS::Manager* vfs);
|
VFS::Path::Normalized correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||||
std::string correctBookartPath(std::string_view resPath, const VFS::Manager* vfs);
|
VFS::Path::Normalized correctBookartPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||||
std::string correctBookartPath(std::string_view resPath, int width, int height, 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
|
/// 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". :(
|
/// 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);
|
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/".
|
// Prepends "meshes/".
|
||||||
VFS::Path::Normalized correctMeshPath(VFS::Path::NormalizedView resPath);
|
VFS::Path::Normalized correctMeshPath(VFS::Path::NormalizedView resPath);
|
||||||
|
|
|
||||||
|
|
@ -568,7 +568,7 @@ namespace NifOsg
|
||||||
if (st)
|
if (st)
|
||||||
{
|
{
|
||||||
if (st->mExternal)
|
if (st->mExternal)
|
||||||
return getTextureImage(st->mFile);
|
return getTextureImage(VFS::Path::toNormalized(st->mFile));
|
||||||
|
|
||||||
if (!st->mData.empty())
|
if (!st->mData.empty())
|
||||||
return handleInternalTexture(st->mData.getPtr());
|
return handleInternalTexture(st->mData.getPtr());
|
||||||
|
|
@ -1065,13 +1065,12 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> getTextureImage(std::string_view path) const
|
osg::ref_ptr<osg::Image> getTextureImage(VFS::Path::NormalizedView path) const
|
||||||
{
|
{
|
||||||
if (!mImageManager)
|
if (!mImageManager)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return mImageManager->getImage(
|
return mImageManager->getImage(Misc::ResourceHelpers::correctTexturePath(path, *mImageManager->getVFS()));
|
||||||
VFS::Path::toNormalized(Misc::ResourceHelpers::correctTexturePath(path, mImageManager->getVFS())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static osg::ref_ptr<osg::Texture2D> attachTexture(const std::string& name, osg::ref_ptr<osg::Image> image,
|
static osg::ref_ptr<osg::Texture2D> attachTexture(const std::string& name, osg::ref_ptr<osg::Image> image,
|
||||||
|
|
@ -1095,8 +1094,9 @@ namespace NifOsg
|
||||||
return texture2d;
|
return texture2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> attachExternalTexture(const std::string& name, const std::string& path, bool wrapS,
|
osg::ref_ptr<osg::Texture2D> attachExternalTexture(const std::string& name, VFS::Path::NormalizedView path,
|
||||||
bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const
|
bool wrapS, bool wrapT, unsigned int uvSet, osg::StateSet* stateset,
|
||||||
|
std::vector<unsigned int>& boundTextures) const
|
||||||
{
|
{
|
||||||
return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures);
|
return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures);
|
||||||
}
|
}
|
||||||
|
|
@ -2223,18 +2223,19 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bgsm::MaterialFilePtr getShaderMaterial(
|
static Bgsm::MaterialFilePtr getShaderMaterial(
|
||||||
std::string_view path, Resource::BgsmFileManager* materialManager)
|
VFS::Path::NormalizedView path, Resource::BgsmFileManager* materialManager)
|
||||||
{
|
{
|
||||||
if (!materialManager)
|
if (!materialManager)
|
||||||
return nullptr;
|
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;
|
return nullptr;
|
||||||
|
|
||||||
std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, materialManager->getVFS());
|
const VFS::Path::Normalized normalizedPath
|
||||||
|
= Misc::ResourceHelpers::correctMaterialPath(path, *materialManager->getVFS());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return materialManager->get(VFS::Path::Normalized(normalizedPath));
|
return materialManager->get(normalizedPath);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
@ -2254,14 +2255,16 @@ namespace NifOsg
|
||||||
const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material);
|
const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material);
|
||||||
|
|
||||||
if (!bgsm->mDiffuseMap.empty())
|
if (!bgsm->mDiffuseMap.empty())
|
||||||
attachExternalTexture(
|
attachExternalTexture("diffuseMap", VFS::Path::toNormalized(bgsm->mDiffuseMap), wrapS, wrapT, uvSet,
|
||||||
"diffuseMap", bgsm->mDiffuseMap, wrapS, wrapT, uvSet, stateset, boundTextures);
|
stateset, boundTextures);
|
||||||
|
|
||||||
if (!bgsm->mNormalMap.empty())
|
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())
|
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)
|
if (bgsm->mTree)
|
||||||
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
|
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
|
||||||
|
|
@ -2271,7 +2274,8 @@ namespace NifOsg
|
||||||
const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material);
|
const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material);
|
||||||
|
|
||||||
if (!bgem->mBaseMap.empty())
|
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;
|
bool useFalloff = bgem->mFalloff;
|
||||||
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
|
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
|
||||||
|
|
@ -2386,16 +2390,16 @@ namespace NifOsg
|
||||||
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
|
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
|
||||||
{
|
{
|
||||||
case Nif::BSShaderTextureSet::TextureType::Base:
|
case Nif::BSShaderTextureSet::TextureType::Base:
|
||||||
attachExternalTexture(
|
attachExternalTexture("diffuseMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
|
||||||
"diffuseMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
|
wrapT, uvSet, stateset, boundTextures);
|
||||||
break;
|
break;
|
||||||
case Nif::BSShaderTextureSet::TextureType::Normal:
|
case Nif::BSShaderTextureSet::TextureType::Normal:
|
||||||
attachExternalTexture(
|
attachExternalTexture("normalMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
|
||||||
"normalMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
|
wrapT, uvSet, stateset, boundTextures);
|
||||||
break;
|
break;
|
||||||
case Nif::BSShaderTextureSet::TextureType::Glow:
|
case Nif::BSShaderTextureSet::TextureType::Glow:
|
||||||
attachExternalTexture(
|
attachExternalTexture("emissiveMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
|
||||||
"emissiveMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures);
|
wrapT, uvSet, stateset, boundTextures);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
|
@ -2578,8 +2582,8 @@ namespace NifOsg
|
||||||
if (!texprop->mFilename.empty())
|
if (!texprop->mFilename.empty())
|
||||||
{
|
{
|
||||||
const unsigned int uvSet = 0;
|
const unsigned int uvSet = 0;
|
||||||
attachExternalTexture("diffuseMap", texprop->mFilename, texprop->wrapS(), texprop->wrapT(),
|
attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mFilename),
|
||||||
uvSet, stateset, boundTextures);
|
texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures);
|
||||||
}
|
}
|
||||||
if (mBethVersion >= 27)
|
if (mBethVersion >= 27)
|
||||||
{
|
{
|
||||||
|
|
@ -2599,7 +2603,8 @@ namespace NifOsg
|
||||||
node->setUserValue("shaderRequired", shaderRequired);
|
node->setUserValue("shaderRequired", shaderRequired);
|
||||||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||||
clearBoundTextures(stateset, boundTextures);
|
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);
|
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
|
||||||
break;
|
break;
|
||||||
|
|
@ -2626,7 +2631,8 @@ namespace NifOsg
|
||||||
node->setUserValue("shaderRequired", shaderRequired);
|
node->setUserValue("shaderRequired", shaderRequired);
|
||||||
osg::StateSet* stateset = node->getOrCreateStateSet();
|
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||||
clearBoundTextures(stateset, boundTextures);
|
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);
|
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
|
||||||
break;
|
break;
|
||||||
|
|
@ -2635,8 +2641,8 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
const unsigned int uvSet = 0;
|
const unsigned int uvSet = 0;
|
||||||
unsigned int texUnit = static_cast<unsigned>(boundTextures.size());
|
unsigned int texUnit = static_cast<unsigned>(boundTextures.size());
|
||||||
attachExternalTexture("diffuseMap", texprop->mSourceTexture, texprop->wrapS(), texprop->wrapT(),
|
attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mSourceTexture),
|
||||||
uvSet, stateset, boundTextures);
|
texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures);
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat);
|
osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat);
|
||||||
// This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController)
|
// 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:
|
case Nif::RC_BSLightingShaderProperty:
|
||||||
{
|
{
|
||||||
auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(property);
|
auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(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);
|
handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha);
|
||||||
if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting)
|
if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting)
|
||||||
|
|
@ -2871,7 +2878,8 @@ namespace NifOsg
|
||||||
case Nif::RC_BSEffectShaderProperty:
|
case Nif::RC_BSEffectShaderProperty:
|
||||||
{
|
{
|
||||||
auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(property);
|
auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(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);
|
handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ namespace Resource
|
||||||
, mPath(path)
|
, mPath(path)
|
||||||
, mVFS(&vfs)
|
, mVFS(&vfs)
|
||||||
{
|
{
|
||||||
mPath.changeExtension("txt");
|
constexpr VFS::Path::ExtensionView txt("txt");
|
||||||
|
mPath.changeExtension(txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RetrieveAnimationsVisitor::belongsToLeftUpperExtremity(const std::string& name)
|
bool RetrieveAnimationsVisitor::belongsToLeftUpperExtremity(const std::string& name)
|
||||||
|
|
@ -214,7 +215,8 @@ namespace Resource
|
||||||
return osg::ref_ptr<const SceneUtil::KeyframeHolder>(static_cast<SceneUtil::KeyframeHolder*>(obj.get()));
|
return osg::ref_ptr<const SceneUtil::KeyframeHolder>(static_cast<SceneUtil::KeyframeHolder*>(obj.get()));
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::KeyframeHolder> loaded(new SceneUtil::KeyframeHolder);
|
osg::ref_ptr<SceneUtil::KeyframeHolder> loaded(new SceneUtil::KeyframeHolder);
|
||||||
if (Misc::getFileExtension(name.value()) == "kf")
|
constexpr VFS::Path::ExtensionView kf("kf");
|
||||||
|
if (name.extension() == kf)
|
||||||
{
|
{
|
||||||
auto file = std::make_shared<Nif::NIFFile>(name);
|
auto file = std::make_shared<Nif::NIFFile>(name);
|
||||||
Nif::Reader reader(*file, mEncoder);
|
Nif::Reader reader(*file, mEncoder);
|
||||||
|
|
|
||||||
|
|
@ -947,10 +947,20 @@ namespace Resource
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> SceneManager::loadErrorMarker()
|
osg::ref_ptr<osg::Node> SceneManager::loadErrorMarker()
|
||||||
{
|
{
|
||||||
|
constexpr VFS::Path::ExtensionView meshTypes[] = {
|
||||||
|
VFS::Path::ExtensionView("nif"),
|
||||||
|
VFS::Path::ExtensionView("osg"),
|
||||||
|
VFS::Path::ExtensionView("osgt"),
|
||||||
|
VFS::Path::ExtensionView("osgb"),
|
||||||
|
VFS::Path::ExtensionView("osgx"),
|
||||||
|
VFS::Path::ExtensionView("osg2"),
|
||||||
|
VFS::Path::ExtensionView("dae"),
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
VFS::Path::Normalized path("meshes/marker_error.****");
|
VFS::Path::Normalized path("meshes/marker_error.****");
|
||||||
for (const auto meshType : { "nif", "osg", "osgt", "osgb", "osgx", "osg2", "dae" })
|
for (const VFS::Path::ExtensionView meshType : meshTypes)
|
||||||
{
|
{
|
||||||
path.changeExtension(meshType);
|
path.changeExtension(meshType);
|
||||||
if (mVFS->exists(path))
|
if (mVFS->exists(path))
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,67 @@ namespace VFS::Path
|
||||||
return std::find_if(begin, end, [](char v) { return v == extensionSeparator || v == separator; });
|
return std::find_if(begin, end, [](char v) { return v == extensionSeparator || v == separator; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline constexpr bool isExtension(std::string_view value)
|
||||||
|
{
|
||||||
|
return isNormalized(value) && findSeparatorOrExtensionSeparator(value.begin(), value.end()) == value.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
class NormalizedView;
|
||||||
|
|
||||||
|
class ExtensionView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr ExtensionView() noexcept = default;
|
||||||
|
|
||||||
|
constexpr explicit ExtensionView(const char* value)
|
||||||
|
: mValue(value)
|
||||||
|
{
|
||||||
|
if (!isExtension(mValue))
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"ExtensionView value is invalid extension: \"" + std::string(mValue) + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::string_view value() const noexcept { return mValue; }
|
||||||
|
|
||||||
|
constexpr bool empty() const noexcept { return mValue.empty(); }
|
||||||
|
|
||||||
|
friend constexpr bool operator==(const ExtensionView& lhs, const ExtensionView& rhs) = default;
|
||||||
|
|
||||||
|
friend constexpr bool operator==(const ExtensionView& lhs, const auto& rhs) { return lhs.mValue == rhs; }
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1935
|
||||||
|
friend constexpr bool operator==(const auto& lhs, const ExtensionView& rhs)
|
||||||
|
{
|
||||||
|
return lhs == rhs.mValue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
friend constexpr bool operator<(const ExtensionView& lhs, const ExtensionView& rhs)
|
||||||
|
{
|
||||||
|
return lhs.mValue < rhs.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator<(const ExtensionView& lhs, const auto& rhs)
|
||||||
|
{
|
||||||
|
return lhs.mValue < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator<(const auto& lhs, const ExtensionView& rhs)
|
||||||
|
{
|
||||||
|
return lhs < rhs.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& stream, const ExtensionView& value)
|
||||||
|
{
|
||||||
|
return stream << value.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string_view mValue;
|
||||||
|
|
||||||
|
friend class NormalizedView;
|
||||||
|
};
|
||||||
|
|
||||||
class Normalized;
|
class Normalized;
|
||||||
|
|
||||||
class NormalizedView
|
class NormalizedView
|
||||||
|
|
@ -191,6 +252,15 @@ namespace VFS::Path
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr ExtensionView extension() const
|
||||||
|
{
|
||||||
|
ExtensionView result;
|
||||||
|
if (const std::size_t position = mValue.find_last_of(extensionSeparator);
|
||||||
|
position != std::string_view::npos)
|
||||||
|
result.mValue = mValue.substr(position + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string_view mValue;
|
std::string_view mValue;
|
||||||
};
|
};
|
||||||
|
|
@ -238,18 +308,13 @@ namespace VFS::Path
|
||||||
|
|
||||||
operator const std::string&() const { return mValue; }
|
operator const std::string&() const { return mValue; }
|
||||||
|
|
||||||
bool changeExtension(std::string_view extension)
|
bool changeExtension(ExtensionView extension)
|
||||||
{
|
{
|
||||||
if (!isNormalized(extension))
|
|
||||||
throw std::invalid_argument("Not normalized extension: " + std::string(extension));
|
|
||||||
if (findSeparatorOrExtensionSeparator(extension.begin(), extension.end()) != extension.end())
|
|
||||||
throw std::invalid_argument("Invalid extension: " + std::string(extension));
|
|
||||||
const auto it = findSeparatorOrExtensionSeparator(mValue.rbegin(), mValue.rend());
|
const auto it = findSeparatorOrExtensionSeparator(mValue.rbegin(), mValue.rend());
|
||||||
if (it == mValue.rend() || *it == separator)
|
if (it == mValue.rend() || *it == separator)
|
||||||
return false;
|
return false;
|
||||||
const std::string::difference_type pos = mValue.rend() - it;
|
const std::string::difference_type pos = mValue.rend() - it;
|
||||||
mValue.replace(pos, mValue.size(), extension);
|
mValue.replace(pos, mValue.size(), extension.value());
|
||||||
std::transform(mValue.begin() + pos, mValue.end(), mValue.begin() + pos, normalize);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,6 +407,11 @@ namespace VFS::Path
|
||||||
return NormalizedView(*this).filename();
|
return NormalizedView(*this).filename();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExtensionView extension() const
|
||||||
|
{
|
||||||
|
return NormalizedView(*this).extension();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mValue;
|
std::string mValue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue