mirror of https://github.com/OpenMW/openmw
Add extension path type
This commit is contained in:
parent
b5196b2fd1
commit
878f9f8433
|
|
@ -12,6 +12,8 @@ namespace Misc::ResourceHelpers
|
|||
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)
|
||||
{
|
||||
|
|
@ -29,74 +31,74 @@ namespace Misc::ResourceHelpers
|
|||
|
||||
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfBothExistsInVfs)
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
|
||||
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
|
||||
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
|
||||
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||
{ wav, nullptr },
|
||||
{ mp3, nullptr },
|
||||
{ wavPath, 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({});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "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 mp3("sound/foo.mp3");
|
||||
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
|
||||
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||
{ wav, nullptr },
|
||||
{ mp3, nullptr },
|
||||
{ wavPath, nullptr },
|
||||
{ mp3Path, nullptr },
|
||||
});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "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({
|
||||
{ wav, nullptr },
|
||||
{ wavPath, nullptr },
|
||||
});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "mp3"), "sound/foo.wav");
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, wavPath, *vfs, mp3), "sound/foo.wav");
|
||||
}
|
||||
|
||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory)
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView path("foo.mp3");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/foo.mp3");
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, mp3), "sound/foo.mp3");
|
||||
}
|
||||
|
||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs)
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
|
||||
constexpr VFS::Path::NormalizedView a("textures/foo.a");
|
||||
constexpr VFS::Path::NormalizedView aPath("textures/foo.a");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||
{ a, nullptr },
|
||||
{ aPath, nullptr },
|
||||
});
|
||||
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.a");
|
||||
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.a");
|
||||
}
|
||||
|
||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs)
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
|
||||
constexpr VFS::Path::NormalizedView b("textures/foo.b");
|
||||
constexpr VFS::Path::NormalizedView bPath("textures/foo.b");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
|
||||
{ b, nullptr },
|
||||
{ bPath, nullptr },
|
||||
});
|
||||
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.b");
|
||||
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.b");
|
||||
}
|
||||
|
||||
TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory)
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView path("sound");
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/sound");
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, mp3), "sound/sound");
|
||||
}
|
||||
|
||||
struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<VFS::Path::NormalizedView>
|
||||
|
|
@ -106,7 +108,7 @@ namespace Misc::ResourceHelpers
|
|||
TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected)
|
||||
{
|
||||
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, GetParam(), *vfs, "mp3"), "sound/foo.mp3");
|
||||
EXPECT_EQ(correctResourcePath({ { sound } }, GetParam(), *vfs, mp3), "sound/foo.mp3");
|
||||
}
|
||||
|
||||
const std::vector<VFS::Path::NormalizedView> pathsWithPrefix = {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ namespace VFS::Path
|
|||
{
|
||||
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>>
|
||||
{
|
||||
};
|
||||
|
|
@ -53,6 +60,80 @@ namespace VFS::Path
|
|||
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)
|
||||
{
|
||||
const Normalized value;
|
||||
|
|
@ -131,41 +212,34 @@ namespace VFS::Path
|
|||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot)
|
||||
{
|
||||
Normalized value("foo/bar.a");
|
||||
ASSERT_TRUE(value.changeExtension("so"));
|
||||
EXPECT_EQ(value.value(), "foo/bar.so");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnNotNormalizedExtension)
|
||||
{
|
||||
Normalized value("foo/bar.a");
|
||||
EXPECT_THROW(value.changeExtension("\\SO"), std::invalid_argument);
|
||||
Normalized value("foo/ba.r.a");
|
||||
constexpr ExtensionView extension("so");
|
||||
ASSERT_TRUE(value.changeExtension(extension));
|
||||
EXPECT_EQ(value.value(), "foo/ba.r.so");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot)
|
||||
{
|
||||
Normalized value("foo/bar");
|
||||
ASSERT_FALSE(value.changeExtension("so"));
|
||||
constexpr ExtensionView extension("so");
|
||||
ASSERT_FALSE(value.changeExtension(extension));
|
||||
EXPECT_EQ(value.value(), "foo/bar");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithDotBeforeSeparator)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithDot)
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceWithShorterExtension)
|
||||
{
|
||||
Normalized value("foo.a");
|
||||
EXPECT_THROW(value.changeExtension(".so"), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithSeparator)
|
||||
{
|
||||
Normalized value("foo.a");
|
||||
EXPECT_THROW(value.changeExtension("so/"), std::invalid_argument);
|
||||
Normalized value("foo/bar.nif");
|
||||
constexpr ExtensionView extension("kf");
|
||||
ASSERT_TRUE(value.changeExtension(extension));
|
||||
EXPECT_EQ(value.value(), "foo/bar.kf");
|
||||
}
|
||||
|
||||
TEST(VFSPathNormalizedTest, filenameShouldReturnLastComponentOfThePath)
|
||||
|
|
@ -218,20 +292,14 @@ namespace VFS::Path
|
|||
|
||||
REGISTER_TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest, supportsEqual, supportsLess);
|
||||
|
||||
template <class T0, class T1>
|
||||
struct TypePair
|
||||
{
|
||||
using Type0 = T0;
|
||||
using Type1 = T1;
|
||||
};
|
||||
using VFSPathNormalizedOperatorsTypePairs
|
||||
= Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
|
||||
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>>;
|
||||
|
||||
using TypePairs = Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
|
||||
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);
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, VFSPathNormalizedOperatorsTypePairs);
|
||||
|
||||
TEST(VFSPathNormalizedViewTest, shouldSupportConstructorFromNormalized)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -659,21 +659,21 @@ namespace MWRender
|
|||
|
||||
path.replace(extensionStart, path.size() - extensionStart, "/");
|
||||
|
||||
constexpr VFS::Path::ExtensionView kf("kf");
|
||||
for (const VFS::Path::Normalized& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(path))
|
||||
{
|
||||
if (Misc::getFileExtension(name) == "kf")
|
||||
{
|
||||
if (name.extension() == kf)
|
||||
addSingleAnimSource(name, 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);
|
||||
|
||||
if (Misc::getFileExtension(kfname) == "nif")
|
||||
kfname.changeExtension("kf");
|
||||
if (kfname.extension() == nif)
|
||||
kfname.changeExtension(kf);
|
||||
|
||||
addSingleAnimSource(kfname, baseModel);
|
||||
|
||||
|
|
@ -757,10 +757,12 @@ namespace MWRender
|
|||
// Get the blending rules
|
||||
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
|
||||
// manager will check for .json if it will not find a specified .yaml file.
|
||||
VFS::Path::Normalized blendConfigPath(kfname);
|
||||
blendConfigPath.changeExtension("yaml");
|
||||
blendConfigPath.changeExtension(yaml);
|
||||
|
||||
// globalBlendConfigPath is only used with actors! Objects have no default blending.
|
||||
constexpr VFS::Path::NormalizedView globalBlendConfigPath("animations/animation-config.yaml");
|
||||
|
|
|
|||
|
|
@ -744,10 +744,12 @@ namespace MWRender
|
|||
if (activeGrid && type != ESM::REC_STAT && type != ESM::REC_STAT4)
|
||||
{
|
||||
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;
|
||||
kfname.changeExtension("kf");
|
||||
constexpr VFS::Path::ExtensionView kf("kf");
|
||||
kfname.changeExtension(kf);
|
||||
if (mSceneManager->getVFS()->exists(kfname))
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace MWSound
|
|||
namespace
|
||||
{
|
||||
constexpr VFS::Path::NormalizedView soundDir("sound");
|
||||
constexpr VFS::Path::ExtensionView mp3("mp3");
|
||||
|
||||
struct AudioParams
|
||||
{
|
||||
|
|
@ -202,9 +203,8 @@ namespace MWSound
|
|||
|
||||
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound)
|
||||
{
|
||||
VFS::Path::Normalized path
|
||||
= Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile),
|
||||
*MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
|
||||
VFS::Path::Normalized path = Misc::ResourceHelpers::correctResourcePath({ { soundDir } },
|
||||
VFS::Path::toNormalized(sound.mSoundFile), *MWBase::Environment::get().getResourceSystem()->getVFS(), mp3);
|
||||
float volume = 1, min = 1, max = 255; // TODO: needs research
|
||||
SoundBuffer& sfx = mSoundBuffers.emplace_back(std::move(path), volume, min, max);
|
||||
mBufferNameMap.emplace(soundId, &sfx);
|
||||
|
|
@ -213,9 +213,8 @@ namespace MWSound
|
|||
|
||||
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound)
|
||||
{
|
||||
VFS::Path::Normalized path
|
||||
= Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile),
|
||||
*MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
|
||||
VFS::Path::Normalized path = Misc::ResourceHelpers::correctResourcePath({ { soundDir } },
|
||||
VFS::Path::toNormalized(sound.mSoundFile), *MWBase::Environment::get().getResourceSystem()->getVFS(), mp3);
|
||||
float volume = 1, min = 1, max = 255; // TODO: needs research
|
||||
// TODO: sound.mSoundId can link to another SoundReference, probably we will need to add additional lookups to
|
||||
// ESMStore.
|
||||
|
|
|
|||
|
|
@ -122,10 +122,12 @@ namespace MWWorld
|
|||
if (!vfs.exists(mesh))
|
||||
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.changeExtension("kf");
|
||||
constexpr VFS::Path::ExtensionView kf("kf");
|
||||
kfname.changeExtension(kf);
|
||||
if (vfs.exists(kfname))
|
||||
mPreloadedObjects.insert(mKeyframeManager->get(kfname));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ namespace
|
|||
constexpr VFS::Path::NormalizedView bookart("bookart");
|
||||
constexpr VFS::Path::NormalizedView icons("icons");
|
||||
constexpr VFS::Path::NormalizedView materials("materials");
|
||||
constexpr std::string_view dds("dds");
|
||||
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)
|
||||
{
|
||||
|
|
@ -67,7 +70,7 @@ 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.
|
||||
VFS::Path::Normalized Misc::ResourceHelpers::correctResourcePath(
|
||||
std::span<const VFS::Path::NormalizedView> topLevelDirectories, VFS::Path::NormalizedView resPath,
|
||||
const VFS::Manager& vfs, std::string_view ext)
|
||||
const VFS::Manager& vfs, VFS::Path::ExtensionView ext)
|
||||
{
|
||||
VFS::Path::Normalized correctedPath;
|
||||
|
||||
|
|
@ -167,8 +170,8 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctActorModelPath(
|
|||
mdlname.insert(mdlname.begin(), 'x');
|
||||
|
||||
VFS::Path::Normalized kfname(mdlname);
|
||||
if (Misc::getFileExtension(mdlname) == "nif")
|
||||
kfname.changeExtension("kf");
|
||||
if (kfname.extension() == nif)
|
||||
kfname.changeExtension(kf);
|
||||
|
||||
if (!vfs->exists(kfname))
|
||||
return VFS::Path::Normalized(resPath);
|
||||
|
|
@ -215,16 +218,16 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctSoundPath(
|
|||
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
|
||||
{
|
||||
// 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:
|
||||
// - `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`.
|
||||
|
||||
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
||||
if (!vfs.exists(resPath))
|
||||
{
|
||||
VFS::Path::Normalized sound(resPath);
|
||||
sound.changeExtension("mp3");
|
||||
sound.changeExtension(mp3);
|
||||
return sound;
|
||||
}
|
||||
return VFS::Path::Normalized(resPath);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Misc
|
|||
{
|
||||
bool changeExtensionToDds(std::string& path);
|
||||
VFS::Path::Normalized correctResourcePath(std::span<const VFS::Path::NormalizedView> topLevelDirectories,
|
||||
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs, std::string_view ext = {});
|
||||
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs, VFS::Path::ExtensionView ext = {});
|
||||
VFS::Path::Normalized correctTexturePath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||
VFS::Path::Normalized correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||
VFS::Path::Normalized correctBookartPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ namespace Resource
|
|||
, mPath(path)
|
||||
, mVFS(&vfs)
|
||||
{
|
||||
mPath.changeExtension("txt");
|
||||
constexpr VFS::Path::ExtensionView txt("txt");
|
||||
mPath.changeExtension(txt);
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
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);
|
||||
Nif::Reader reader(*file, mEncoder);
|
||||
|
|
|
|||
|
|
@ -947,10 +947,20 @@ namespace Resource
|
|||
|
||||
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
|
||||
{
|
||||
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);
|
||||
if (mVFS->exists(path))
|
||||
|
|
|
|||
|
|
@ -107,6 +107,67 @@ namespace VFS::Path
|
|||
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 NormalizedView
|
||||
|
|
@ -191,6 +252,15 @@ namespace VFS::Path
|
|||
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:
|
||||
std::string_view mValue;
|
||||
};
|
||||
|
|
@ -238,18 +308,13 @@ namespace VFS::Path
|
|||
|
||||
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());
|
||||
if (it == mValue.rend() || *it == separator)
|
||||
return false;
|
||||
const std::string::difference_type pos = mValue.rend() - it;
|
||||
mValue.replace(pos, mValue.size(), extension);
|
||||
std::transform(mValue.begin() + pos, mValue.end(), mValue.begin() + pos, normalize);
|
||||
mValue.replace(pos, mValue.size(), extension.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -342,6 +407,11 @@ namespace VFS::Path
|
|||
return NormalizedView(*this).filename();
|
||||
}
|
||||
|
||||
ExtensionView extension() const
|
||||
{
|
||||
return NormalizedView(*this).extension();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mValue;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue