Skip to content

Commit cd49012

Browse files
committed
Merge branch 'vfs_normalized_path_25' into 'master'
Use normalized path for correctResourcePath and related functions (#8138) See merge request OpenMW/openmw!5031
2 parents a6097d0 + 878f9f8 commit cd49012

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+432
-260
lines changed

apps/components_tests/misc/testresourcehelpers.cpp

Lines changed: 43 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ namespace Misc::ResourceHelpers
99
{
1010
using namespace ::testing;
1111

12+
constexpr VFS::Path::NormalizedView sound("sound");
13+
constexpr VFS::Path::NormalizedView textures("textures");
14+
constexpr VFS::Path::NormalizedView bookart("bookart");
15+
constexpr VFS::Path::ExtensionView mp3("mp3");
16+
constexpr VFS::Path::ExtensionView b("b");
17+
1218
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs)
1319
{
1420
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
@@ -25,112 +31,91 @@ namespace Misc::ResourceHelpers
2531

2632
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfBothExistsInVfs)
2733
{
28-
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
29-
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
34+
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
35+
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
3036
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
31-
{ wav, nullptr },
32-
{ mp3, nullptr },
37+
{ wavPath, nullptr },
38+
{ mp3Path, nullptr },
3339
});
34-
EXPECT_EQ(correctSoundPath(wav, *vfs), "sound/foo.wav");
40+
EXPECT_EQ(correctSoundPath(wavPath, *vfs), "sound/foo.wav");
3541
}
3642

37-
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfDoesNotExistInVfs)
43+
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtensionIfDoesNotExistInVfs)
3844
{
45+
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
3946
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
40-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound/foo.wav", vfs.get(), "mp3"), "sound/foo.mp3");
47+
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, mp3), "sound/foo.mp3");
4148
}
4249

43-
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfBothExistInVfs)
50+
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtensionIfBothExistInVfs)
4451
{
45-
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
46-
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
52+
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
53+
constexpr VFS::Path::NormalizedView mp3Path("sound/foo.mp3");
4754
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
48-
{ wav, nullptr },
49-
{ mp3, nullptr },
55+
{ wavPath, nullptr },
56+
{ mp3Path, nullptr },
5057
});
51-
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.mp3");
58+
EXPECT_EQ(correctResourcePath({ { sound } }, wavPath, *vfs, mp3), "sound/foo.mp3");
5259
}
5360

54-
TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtentionIfExistInVfs)
61+
TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtensionIfExistInVfs)
5562
{
56-
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
63+
constexpr VFS::Path::NormalizedView wavPath("sound/foo.wav");
5764
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
58-
{ wav, nullptr },
65+
{ wavPath, nullptr },
5966
});
60-
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.wav");
67+
EXPECT_EQ(correctResourcePath({ { sound } }, wavPath, *vfs, mp3), "sound/foo.wav");
6168
}
6269

6370
TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory)
6471
{
72+
constexpr VFS::Path::NormalizedView path("foo.mp3");
6573
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
66-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "foo.mp3", vfs.get(), "mp3"), "sound/foo.mp3");
74+
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, mp3), "sound/foo.mp3");
6775
}
6876

6977
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs)
7078
{
71-
constexpr VFS::Path::NormalizedView a("textures/foo.a");
79+
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
80+
constexpr VFS::Path::NormalizedView aPath("textures/foo.a");
7281
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
73-
{ a, nullptr },
82+
{ aPath, nullptr },
7483
});
75-
EXPECT_EQ(
76-
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.a");
84+
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.a");
7785
}
7886

7987
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs)
8088
{
81-
constexpr VFS::Path::NormalizedView b("textures/foo.b");
89+
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
90+
constexpr VFS::Path::NormalizedView bPath("textures/foo.b");
8291
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
83-
{ b, nullptr },
92+
{ bPath, nullptr },
8493
});
85-
EXPECT_EQ(
86-
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.b");
87-
}
88-
89-
TEST(MiscResourceHelpersCorrectResourcePath, shouldLowerCase)
90-
{
91-
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
92-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
93-
}
94-
95-
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveLeadingSlash)
96-
{
97-
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
98-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
99-
}
100-
101-
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveDuplicateSlashes)
102-
{
103-
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
104-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\\\SOUND\\\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
105-
}
106-
107-
TEST(MiscResourceHelpersCorrectResourcePath, shouldConvertToForwardSlash)
108-
{
109-
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
110-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND/Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
94+
EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, b), "textures/foo.b");
11195
}
11296

11397
TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory)
11498
{
99+
constexpr VFS::Path::NormalizedView path("sound");
115100
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
116-
EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound", vfs.get(), "mp3"), "sound/sound");
101+
EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, mp3), "sound/sound");
117102
}
118103

119-
struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<std::string>
104+
struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<VFS::Path::NormalizedView>
120105
{
121106
};
122107

123108
TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected)
124109
{
125110
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
126-
EXPECT_EQ(correctResourcePath({ { "sound" } }, GetParam(), vfs.get(), "mp3"), "sound/foo.mp3");
111+
EXPECT_EQ(correctResourcePath({ { sound } }, GetParam(), *vfs, mp3), "sound/foo.mp3");
127112
}
128113

129-
const std::vector<std::string> pathsWithPrefix = {
130-
"data/sound/foo.mp3",
131-
"data/notsound/sound/foo.mp3",
132-
"data/soundnot/sound/foo.mp3",
133-
"data/notsoundnot/sound/foo.mp3",
114+
const std::vector<VFS::Path::NormalizedView> pathsWithPrefix = {
115+
VFS::Path::NormalizedView("data/sound/foo.mp3"),
116+
VFS::Path::NormalizedView("data/notsound/sound/foo.mp3"),
117+
VFS::Path::NormalizedView("data/soundnot/sound/foo.mp3"),
118+
VFS::Path::NormalizedView("data/notsoundnot/sound/foo.mp3"),
134119
};
135120

136121
INSTANTIATE_TEST_SUITE_P(

apps/components_tests/vfs/testpathutil.cpp

Lines changed: 101 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ namespace VFS::Path
1010
{
1111
using namespace testing;
1212

13+
template <class T0, class T1>
14+
struct TypePair
15+
{
16+
using Type0 = T0;
17+
using Type1 = T1;
18+
};
19+
1320
struct VFSPathIsNormalizedTest : TestWithParam<std::pair<std::string_view, bool>>
1421
{
1522
};
@@ -53,6 +60,80 @@ namespace VFS::Path
5360
EXPECT_EQ(value, "foo");
5461
}
5562

63+
TEST(VFSPathExtensionViewTest, shouldSupportDefaultConstructor)
64+
{
65+
constexpr ExtensionView extension;
66+
EXPECT_TRUE(extension.empty());
67+
EXPECT_EQ(extension.value(), "");
68+
}
69+
70+
TEST(VFSPathExtensionViewTest, shouldSupportConstexprConstructorFromConstCharPtr)
71+
{
72+
constexpr ExtensionView extension("png");
73+
EXPECT_FALSE(extension.empty());
74+
EXPECT_EQ(extension.value(), "png");
75+
}
76+
77+
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionOnNotNormalizedValue)
78+
{
79+
EXPECT_THROW([] { ExtensionView("PNG"); }(), std::invalid_argument);
80+
}
81+
82+
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsExtensionSeparator)
83+
{
84+
EXPECT_THROW([] { ExtensionView(".png"); }(), std::invalid_argument);
85+
}
86+
87+
TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsSeparator)
88+
{
89+
EXPECT_THROW([] { ExtensionView("/png"); }(), std::invalid_argument);
90+
}
91+
92+
template <class T>
93+
struct VFSPathExtensionViewOperatorsTest : Test
94+
{
95+
};
96+
97+
TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest);
98+
99+
TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsEqual)
100+
{
101+
using Type0 = typename TypeParam::Type0;
102+
using Type1 = typename TypeParam::Type1;
103+
const Type0 extension{ "png" };
104+
const Type1 otherEqual{ "png" };
105+
const Type1 otherNotEqual{ "jpg" };
106+
EXPECT_EQ(extension, otherEqual);
107+
EXPECT_EQ(otherEqual, extension);
108+
EXPECT_NE(extension, otherNotEqual);
109+
EXPECT_NE(otherNotEqual, extension);
110+
}
111+
112+
TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsLess)
113+
{
114+
using Type0 = typename TypeParam::Type0;
115+
using Type1 = typename TypeParam::Type1;
116+
const Type0 extension{ "png" };
117+
const Type1 otherEqual{ "png" };
118+
const Type1 otherLess{ "jpg" };
119+
const Type1 otherGreater{ "tga" };
120+
EXPECT_FALSE(extension < otherEqual);
121+
EXPECT_FALSE(otherEqual < extension);
122+
EXPECT_LT(otherLess, extension);
123+
EXPECT_FALSE(extension < otherLess);
124+
EXPECT_LT(extension, otherGreater);
125+
EXPECT_FALSE(otherGreater < extension);
126+
}
127+
128+
REGISTER_TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest, supportsEqual, supportsLess);
129+
130+
using VFSPathExtensionViewOperatorsTypePairs
131+
= Types<TypePair<ExtensionView, ExtensionView>, TypePair<ExtensionView, const char*>,
132+
TypePair<ExtensionView, std::string>, TypePair<ExtensionView, std::string_view>>;
133+
134+
INSTANTIATE_TYPED_TEST_SUITE_P(
135+
Typed, VFSPathExtensionViewOperatorsTest, VFSPathExtensionViewOperatorsTypePairs);
136+
56137
TEST(VFSPathNormalizedTest, shouldSupportDefaultConstructor)
57138
{
58139
const Normalized value;
@@ -131,41 +212,34 @@ namespace VFS::Path
131212

132213
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot)
133214
{
134-
Normalized value("foo/bar.a");
135-
ASSERT_TRUE(value.changeExtension("so"));
136-
EXPECT_EQ(value.value(), "foo/bar.so");
137-
}
138-
139-
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnNotNormalizedExtension)
140-
{
141-
Normalized value("foo/bar.a");
142-
EXPECT_THROW(value.changeExtension("\\SO"), std::invalid_argument);
215+
Normalized value("foo/ba.r.a");
216+
constexpr ExtensionView extension("so");
217+
ASSERT_TRUE(value.changeExtension(extension));
218+
EXPECT_EQ(value.value(), "foo/ba.r.so");
143219
}
144220

145221
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot)
146222
{
147223
Normalized value("foo/bar");
148-
ASSERT_FALSE(value.changeExtension("so"));
224+
constexpr ExtensionView extension("so");
225+
ASSERT_FALSE(value.changeExtension(extension));
149226
EXPECT_EQ(value.value(), "foo/bar");
150227
}
151228

152229
TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithDotBeforeSeparator)
153230
{
154231
Normalized value("foo.bar/baz");
155-
ASSERT_FALSE(value.changeExtension("so"));
232+
constexpr ExtensionView extension("so");
233+
ASSERT_FALSE(value.changeExtension(extension));
156234
EXPECT_EQ(value.value(), "foo.bar/baz");
157235
}
158236

159-
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithDot)
237+
TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceWithShorterExtension)
160238
{
161-
Normalized value("foo.a");
162-
EXPECT_THROW(value.changeExtension(".so"), std::invalid_argument);
163-
}
164-
165-
TEST(VFSPathNormalizedTest, changeExtensionShouldThrowExceptionOnExtensionWithSeparator)
166-
{
167-
Normalized value("foo.a");
168-
EXPECT_THROW(value.changeExtension("so/"), std::invalid_argument);
239+
Normalized value("foo/bar.nif");
240+
constexpr ExtensionView extension("kf");
241+
ASSERT_TRUE(value.changeExtension(extension));
242+
EXPECT_EQ(value.value(), "foo/bar.kf");
169243
}
170244

171245
TEST(VFSPathNormalizedTest, filenameShouldReturnLastComponentOfThePath)
@@ -218,20 +292,14 @@ namespace VFS::Path
218292

219293
REGISTER_TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest, supportsEqual, supportsLess);
220294

221-
template <class T0, class T1>
222-
struct TypePair
223-
{
224-
using Type0 = T0;
225-
using Type1 = T1;
226-
};
227-
228-
using TypePairs = Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
229-
TypePair<Normalized, std::string>, TypePair<Normalized, std::string_view>,
230-
TypePair<Normalized, NormalizedView>, TypePair<NormalizedView, Normalized>,
231-
TypePair<NormalizedView, const char*>, TypePair<NormalizedView, std::string>,
232-
TypePair<NormalizedView, std::string_view>, TypePair<NormalizedView, NormalizedView>>;
295+
using VFSPathNormalizedOperatorsTypePairs
296+
= Types<TypePair<Normalized, Normalized>, TypePair<Normalized, const char*>,
297+
TypePair<Normalized, std::string>, TypePair<Normalized, std::string_view>,
298+
TypePair<Normalized, NormalizedView>, TypePair<NormalizedView, Normalized>,
299+
TypePair<NormalizedView, const char*>, TypePair<NormalizedView, std::string>,
300+
TypePair<NormalizedView, std::string_view>, TypePair<NormalizedView, NormalizedView>>;
233301

234-
INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, TypePairs);
302+
INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, VFSPathNormalizedOperatorsTypePairs);
235303

236304
TEST(VFSPathNormalizedViewTest, shouldSupportConstructorFromNormalized)
237305
{

apps/openmw/mwgui/birth.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ namespace MWGui
212212
const ESM::BirthSign* birth = store.get<ESM::BirthSign>().find(mCurrentBirthId);
213213

214214
mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath(
215-
birth->mTexture, MWBase::Environment::get().getResourceSystem()->getVFS()));
215+
VFS::Path::toNormalized(birth->mTexture), *MWBase::Environment::get().getResourceSystem()->getVFS()));
216216

217217
std::vector<ESM::RefId> abilities, powers, spells;
218218

apps/openmw/mwgui/class.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,8 +1196,8 @@ namespace MWGui
11961196
if (const auto* id = classId.getIf<ESM::StringRefId>())
11971197
{
11981198
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
1199-
classImage
1200-
= Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + id->getValue() + ".dds", vfs);
1199+
classImage = Misc::ResourceHelpers::correctTexturePath(
1200+
VFS::Path::toNormalized("textures\\levelup\\" + id->getValue() + ".dds"), *vfs);
12011201
if (!vfs->exists(classImage))
12021202
{
12031203
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";

apps/openmw/mwgui/formatting.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,16 @@ namespace MWGui::Formatting
330330
if (auto heightIt = attr.find("height"); heightIt != attr.end())
331331
height = MyGUI::utility::parseInt(heightIt->second);
332332

333-
const std::string& src = srcIt->second;
333+
const std::string_view src = srcIt->second;
334334
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
335335

336-
std::string correctedSrc;
336+
VFS::Path::Normalized correctedSrc;
337337

338338
constexpr std::string_view imgPrefix = "img://";
339339
if (src.starts_with(imgPrefix))
340340
{
341-
correctedSrc = src.substr(imgPrefix.size(), src.size() - imgPrefix.size());
341+
correctedSrc
342+
= VFS::Path::toNormalized(src.substr(imgPrefix.size(), src.size() - imgPrefix.size()));
342343
if (width == 0)
343344
{
344345
width = 50;
@@ -351,7 +352,8 @@ namespace MWGui::Formatting
351352
{
352353
if (width == 0 || height == 0)
353354
continue;
354-
correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs);
355+
correctedSrc = Misc::ResourceHelpers::correctBookartPath(
356+
VFS::Path::toNormalized(src), width, height, *vfs);
355357
}
356358

357359
if (!vfs->exists(correctedSrc))

0 commit comments

Comments
 (0)