[Scummvm-git-logs] scummvm master -> 5a84a5fdc147dc1f1f6affeb6fc85c95d3a1fbfa

lephilousophe noreply at scummvm.org
Sun Jul 9 19:42:02 UTC 2023


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
7880a660cf COMMON: Allow to know in which archive file was found in SearchSet
f34bef6840 COMMON: Add normalization to Path class
5a84a5fdc1 BACKENDS: OPENGL: Rework shader assets search


Commit: 7880a660cfea0514f1c073d1bf8ff7390887bfe4
    https://github.com/scummvm/scummvm/commit/7880a660cfea0514f1c073d1bf8ff7390887bfe4
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2023-07-09T21:41:58+02:00

Commit Message:
COMMON: Allow to know in which archive file was found in SearchSet

Changed paths:
    common/archive.cpp
    common/archive.h


diff --git a/common/archive.cpp b/common/archive.cpp
index c09a2aecccf..25e642e04a6 100644
--- a/common/archive.cpp
+++ b/common/archive.cpp
@@ -339,19 +339,27 @@ int SearchSet::listMembers(ArchiveMemberList &list) const {
 	return matches;
 }
 
-const ArchiveMemberPtr SearchSet::getMember(const Path &path) const {
+const ArchiveMemberPtr SearchSet::getMember(const Path &path, Archive **container) const {
 	if (path.empty())
 		return ArchiveMemberPtr();
 
 	ArchiveNodeList::const_iterator it = _list.begin();
 	for (; it != _list.end(); ++it) {
-		if (it->_arc->hasFile(path))
+		if (it->_arc->hasFile(path)) {
+			if (container) {
+				*container = it->_arc;
+			}
 			return it->_arc->getMember(path);
+		}
 	}
 
 	return ArchiveMemberPtr();
 }
 
+const ArchiveMemberPtr SearchSet::getMember(const Path &path) const {
+	return getMember(path, nullptr);
+}
+
 SeekableReadStream *SearchSet::createReadStreamForMember(const Path &path) const {
 	if (path.empty())
 		return nullptr;
diff --git a/common/archive.h b/common/archive.h
index 8a31928958e..e3e45bd7b49 100644
--- a/common/archive.h
+++ b/common/archive.h
@@ -355,6 +355,8 @@ public:
 
 	const ArchiveMemberPtr getMember(const Path &path) const override;
 
+	const ArchiveMemberPtr getMember(const Path &path, Archive **container) const;
+
 	/**
 	 * Implement createReadStreamForMember from the Archive base class. The current policy is
 	 * opening the first file encountered that matches the name.


Commit: f34bef6840c42e5e7fb73bc9d5cd9c4f11337308
    https://github.com/scummvm/scummvm/commit/f34bef6840c42e5e7fb73bc9d5cd9c4f11337308
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2023-07-09T21:41:58+02:00

Commit Message:
COMMON: Add normalization to Path class

Changed paths:
    common/path.cpp
    common/path.h
    test/common/path.h


diff --git a/common/path.cpp b/common/path.cpp
index 7319896a6aa..5df923fb679 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -20,8 +20,10 @@
  */
 
 #include "common/path.h"
-#include "common/punycode.h"
+
 #include "common/hash-str.h"
+#include "common/list.h"
+#include "common/punycode.h"
 
 const char ESCAPER = '/';
 const char ESCAPE_SLASH = '+';
@@ -244,6 +246,64 @@ Path Path::join(const char *str, char separator) const {
 	return temp;
 }
 
+Path Path::normalize() const {
+	if (empty()) {
+		return Path();
+	}
+
+	Common::String::const_iterator cur = _str.begin();
+	const Common::String::const_iterator end = _str.end();
+
+	Common::Path result;
+
+	// If there is a leading slash, preserve that:
+	if (cur != end &&
+	    *cur == ESCAPER &&
+	    *(cur + 1) == ESCAPE_SEPARATOR) {
+		result._str += DIR_SEPARATOR;
+		cur += 2;
+		// Skip over multiple leading slashes, so "//" equals "/"
+		while (cur != end && *cur == ESCAPER && *(cur + 1) == ESCAPE_SEPARATOR)
+			cur += 2;
+	}
+
+	// Scan for path components till the end of the String
+	List<String> comps;
+	while (cur != end) {
+		Common::String::const_iterator start = cur;
+
+		// Scan till the next path separator resp. the end of the string
+		while (!(*cur == ESCAPER && *(cur + 1) == ESCAPE_SEPARATOR) && cur != end)
+			cur++;
+
+		const String component(start, cur);
+
+		if (component.empty() || component == ".") {
+			// Skip empty components and dot components
+		} else if (!comps.empty() && component == ".." && comps.back() != "..") {
+			// If stack is non-empty and top is not "..", remove top
+			comps.pop_back();
+		} else {
+			// Add the component to the stack
+			comps.push_back(component);
+		}
+
+		// Skip over separator chars
+		while (cur != end && *cur == ESCAPER && *(cur + 1) == ESCAPE_SEPARATOR)
+			cur += 2;
+	}
+
+	// Finally, assemble all components back into a path
+	while (!comps.empty()) {
+		result._str += comps.front();
+		comps.pop_front();
+		if (!comps.empty())
+			result._str += DIR_SEPARATOR;
+	}
+
+	return result;
+}
+
 StringArray Path::splitComponents() const {
 	StringArray res;
 	String cur;
diff --git a/common/path.h b/common/path.h
index 7cf5530f2ee..a9d89f9d039 100644
--- a/common/path.h
+++ b/common/path.h
@@ -207,6 +207,17 @@ public:
 	 */
 	bool matchPattern(const Path& pattern) const;
 
+	/**
+	 * Normalize path to a canonical form. In particular:
+	 * - trailing separators are removed:  /foo/bar/ -> /foo/bar
+	 * - double separators (= empty components) are removed:   /foo//bar -> /foo/bar
+	 * - dot components are removed:  /foo/./bar -> /foo/bar
+	 * - double dot components are removed:  /foo/baz/../bar -> /foo/bar
+	 *
+	 * @return      the normalized path
+	 */
+	Path normalize() const;
+
 	/**
 	 * Splits into path components. After every component except
 	 * last there is an implied separator. First component is empty
diff --git a/test/common/path.h b/test/common/path.h
index d7c48f3b869..043f19dcea1 100644
--- a/test/common/path.h
+++ b/test/common/path.h
@@ -66,4 +66,33 @@ class PathTestSuite : public CxxTest::TestSuite
 		TS_ASSERT_EQUALS(p2.getParent().toString('#'), "par#nt/dir/fil#");
 		TS_ASSERT_EQUALS(p2.getParent().getParent().toString('#'), "par#");
 	}
+
+	void test_normalize() {
+		TS_ASSERT_EQUALS(Common::Path("/", '/').normalize().toString(), "/");
+		TS_ASSERT_EQUALS(Common::Path("/foo/bar", '/').normalize().toString(), "/foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("/foo//bar/", '/').normalize().toString(), "/foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("/foo/./bar", '/').normalize().toString(), "/foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("/foo//./bar//", '/').normalize().toString(), "/foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("/foo//.bar//", '/').normalize().toString(), "/foo/.bar");
+
+		TS_ASSERT_EQUALS(Common::Path("", '/').normalize().toString(), "");
+		TS_ASSERT_EQUALS(Common::Path("foo/bar", '/').normalize().toString(), "foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//bar/", '/').normalize().toString(), "foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("foo/./bar", '/').normalize().toString(), "foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//./bar//", '/').normalize().toString(), "foo/bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//.bar//", '/').normalize().toString(), "foo/.bar");
+
+		TS_ASSERT_EQUALS(Common::Path("..", '/').normalize().toString(), "..");
+		TS_ASSERT_EQUALS(Common::Path("../", '/').normalize().toString(), "..");
+		TS_ASSERT_EQUALS(Common::Path("/..", '/').normalize().toString(), "/..");
+		TS_ASSERT_EQUALS(Common::Path("../bar", '/').normalize().toString(), "../bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//../", '/').normalize().toString(), "");
+		TS_ASSERT_EQUALS(Common::Path("foo/../bar", '/').normalize().toString(), "bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//../bar//", '/').normalize().toString(), "bar");
+		TS_ASSERT_EQUALS(Common::Path("foo//..bar//", '/').normalize().toString(), "foo/..bar");
+
+		TS_ASSERT_EQUALS(Common::Path("foo/../../bar//", '/').normalize().toString(), "../bar");
+		TS_ASSERT_EQUALS(Common::Path("../foo/../bar", '/').normalize().toString(), "../bar");
+		TS_ASSERT_EQUALS(Common::Path("../../foo/bar/", '/').normalize().toString(), "../../foo/bar");
+	}
 };


Commit: 5a84a5fdc147dc1f1f6affeb6fc85c95d3a1fbfa
    https://github.com/scummvm/scummvm/commit/5a84a5fdc147dc1f1f6affeb6fc85c95d3a1fbfa
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2023-07-09T21:41:58+02:00

Commit Message:
BACKENDS: OPENGL: Rework shader assets search

Determine in which archive the shader is located and load assets from
the same archive.
When using a FSNode, use the filesystem code to build the base path.
Use a Path object to store the base path to allow proper join of
components.

Changed paths:
    backends/graphics/opengl/pipelines/libretro.cpp
    backends/graphics/opengl/pipelines/libretro.h
    backends/graphics/opengl/pipelines/libretro/parser.cpp
    backends/graphics/opengl/pipelines/libretro/parser.h
    backends/graphics/opengl/pipelines/libretro/types.h


diff --git a/backends/graphics/opengl/pipelines/libretro.cpp b/backends/graphics/opengl/pipelines/libretro.cpp
index 552f3d0bb8e..e8b43906542 100644
--- a/backends/graphics/opengl/pipelines/libretro.cpp
+++ b/backends/graphics/opengl/pipelines/libretro.cpp
@@ -44,17 +44,26 @@ namespace OpenGL {
 using LibRetro::UniformsMap;
 
 template<typename DecoderType>
-static Graphics::Surface *loadViaImageDecoder(const Common::String &fileName, Common::SearchSet &archSet) {
-	Common::SeekableReadStream *stream;
+static Graphics::Surface *loadViaImageDecoder(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet) {
+	Common::SeekableReadStream *stream = nullptr;
 
-	// First try SearchMan, then fallback to filesystem
-	if (archSet.hasFile(fileName)) {
-		stream = archSet.createReadStreamForMember(fileName);
+	if (container) {
+		// Look first in our current container and fallback on archive set
+		if (container->hasFile(fileName)) {
+			stream = container->createReadStreamForMember(fileName);
+		}
+		if (!stream) {
+			stream = archSet.createReadStreamForMemberNext(fileName, container);
+		}
+		if (!stream) {
+			warning("LibRetroPipeline::loadViaImageDecoder: Invalid file path '%s'", fileName.toString().c_str());
+			return nullptr;
+		}
 	} else {
 		Common::FSNode fsnode(fileName);
 		if (!fsnode.exists() || !fsnode.isReadable() || fsnode.isDirectory()
 				|| !(stream = fsnode.createReadStream())) {
-			warning("LibRetroPipeline::loadViaImageDecoder: Invalid file path '%s'", fileName.c_str());
+			warning("LibRetroPipeline::loadViaImageDecoder: Invalid file path '%s'", fileName.toString().c_str());
 			return nullptr;
 		}
 	}
@@ -80,7 +89,7 @@ static Graphics::Surface *loadViaImageDecoder(const Common::String &fileName, Co
 
 struct ImageLoader {
 	const char *extension;
-	Graphics::Surface *(*load)(const Common::String &fileName, Common::SearchSet &archSet);
+	Graphics::Surface *(*load)(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet);
 };
 
 static const ImageLoader s_imageLoaders[] = {
@@ -388,7 +397,7 @@ bool LibRetroPipeline::loadTextures(Common::SearchSet &archSet) {
 	for (LibRetro::ShaderPreset::TextureArray::const_iterator
 		 i = _shaderPreset->textures.begin(), end = _shaderPreset->textures.end();
 		 i != end; ++i) {
-		Texture texture = loadTexture(Common::normalizePath(_shaderPreset->basePath + Common::String("/") + i->fileName, '/'), archSet);
+		Texture texture = loadTexture(_shaderPreset->basePath.join(i->fileName).normalize(), _shaderPreset->container, archSet);
 		texture.id = i->id;
 
 		if (!texture.textureData || !texture.glTexture) {
@@ -451,17 +460,26 @@ bool LibRetroPipeline::loadPasses(Common::SearchSet &archSet) {
 	for (LibRetro::ShaderPreset::PassArray::const_iterator
 		 i = _shaderPreset->passes.begin(), end = _shaderPreset->passes.end();
 		 i != end; ++i) {
-		Common::String fileName(Common::normalizePath(_shaderPreset->basePath + Common::String("/") + i->fileName, '/'));
-		Common::SeekableReadStream *stream;
+		Common::Path fileName(_shaderPreset->basePath.join(i->fileName).normalize());
+		Common::SeekableReadStream *stream = nullptr;
 
-		// First try SearchMan, then fallback to filesystem
-		if (archSet.hasFile(fileName)) {
-			stream = archSet.createReadStreamForMember(fileName);
+		if (_shaderPreset->container) {
+			// Look first in our current container and fallback on archive set
+			if (_shaderPreset->container->hasFile(fileName)) {
+				stream = _shaderPreset->container->createReadStreamForMember(fileName);
+			}
+			if (!stream) {
+				stream = archSet.createReadStreamForMemberNext(fileName, _shaderPreset->container);
+			}
+			if (!stream) {
+				warning("LibRetroPipeline::loadPasses: Invalid file path '%s'", fileName.toString().c_str());
+				return false;
+			}
 		} else {
 			Common::FSNode fsnode(fileName);
 			if (!fsnode.exists() || !fsnode.isReadable() || fsnode.isDirectory()
 					|| !(stream = fsnode.createReadStream())) {
-				warning("LibRetroPipeline::loadPasses: Invalid file path '%s'", fileName.c_str());
+				warning("LibRetroPipeline::loadPasses: Invalid file path '%s'", fileName.toString().c_str());
 				return false;
 			}
 		}
@@ -474,7 +492,7 @@ bool LibRetroPipeline::loadPasses(Common::SearchSet &archSet) {
 		delete stream;
 
 		if (!readSuccess) {
-			warning("LibRetroPipeline::loadPasses: Could not read file '%s'", fileName.c_str());
+			warning("LibRetroPipeline::loadPasses: Could not read file '%s'", fileName.toString().c_str());
 			return false;
 		}
 
@@ -530,7 +548,7 @@ bool LibRetroPipeline::loadPasses(Common::SearchSet &archSet) {
 			shaderFileStart,
 		};
 
-		if (!shader->loadFromStringsArray(fileName,
+		if (!shader->loadFromStringsArray(fileName.toString(),
 				 ARRAYSIZE(vertexSources), vertexSources,
 				 ARRAYSIZE(fragmentSources), fragmentSources,
 				 g_libretroShaderAttributes)) {
@@ -722,25 +740,26 @@ void LibRetroPipeline::setShaderTexUniforms(const Common::String &prefix, Shader
 	shader->setUniform(prefix + "TextureSize", Math::Vector2d(texture.getWidth(), texture.getHeight()));
 }
 
-LibRetroPipeline::Texture LibRetroPipeline::loadTexture(const Common::String &fileName, Common::SearchSet &archSet) {
+LibRetroPipeline::Texture LibRetroPipeline::loadTexture(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet) {
+	Common::String baseName(fileName.getLastComponent().toString());
 	const char *extension = nullptr;
-	for (int dotPos = fileName.size() - 1; dotPos >= 0; --dotPos) {
-		if (fileName[dotPos] == '.') {
-			extension = fileName.c_str() + dotPos + 1;
+	for (int dotPos = baseName.size() - 1; dotPos >= 0; --dotPos) {
+		if (baseName[dotPos] == '.') {
+			extension = baseName.c_str() + dotPos + 1;
 			break;
 		}
 	}
 
 	if (!extension) {
-		warning("LibRetroPipeline::loadTexture: File name '%s' misses extension", fileName.c_str());
+		warning("LibRetroPipeline::loadTexture: File name '%s' misses extension", fileName.toString().c_str());
 		return Texture();
 	}
 
 	for (const ImageLoader *loader = s_imageLoaders; loader->extension; ++loader) {
 		if (!scumm_stricmp(loader->extension, extension)) {
-			Graphics::Surface *textureData = loader->load(fileName, archSet);
+			Graphics::Surface *textureData = loader->load(fileName, container, archSet);
 			if (!textureData) {
-				warning("LibRetroPipeline::loadTexture: Loader for '%s' could not load file '%s'", loader->extension, fileName.c_str());
+				warning("LibRetroPipeline::loadTexture: Loader for '%s' could not load file '%s'", loader->extension, fileName.toString().c_str());
 				return Texture();
 			}
 
@@ -751,7 +770,7 @@ LibRetroPipeline::Texture LibRetroPipeline::loadTexture(const Common::String &fi
 		}
 	}
 
-	warning("LibRetroPipeline::loadTexture: No loader for file '%s' present", fileName.c_str());
+	warning("LibRetroPipeline::loadTexture: No loader for file '%s' present", fileName.toString().c_str());
 	return Texture();
 }
 
diff --git a/backends/graphics/opengl/pipelines/libretro.h b/backends/graphics/opengl/pipelines/libretro.h
index 01dd0bba798..5db0c260096 100644
--- a/backends/graphics/opengl/pipelines/libretro.h
+++ b/backends/graphics/opengl/pipelines/libretro.h
@@ -117,7 +117,7 @@ private:
 		Graphics::Surface *textureData;
 		GLTexture *glTexture;
 	};
-	Texture loadTexture(const Common::String &fileName, Common::SearchSet &archSet);
+	Texture loadTexture(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet);
 
 	typedef Common::Array<Texture> TextureArray;
 	TextureArray _textures;
diff --git a/backends/graphics/opengl/pipelines/libretro/parser.cpp b/backends/graphics/opengl/pipelines/libretro/parser.cpp
index 718ffb1cb64..f66b3479e56 100644
--- a/backends/graphics/opengl/pipelines/libretro/parser.cpp
+++ b/backends/graphics/opengl/pipelines/libretro/parser.cpp
@@ -533,33 +533,42 @@ bool PresetParser::parseParameters() {
 	return true;
 }
 
-ShaderPreset *parsePreset(const Common::String &shaderPreset, Common::SearchSet &archSet) {
+ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet) {
 	Common::SeekableReadStream *stream;
+	Common::Archive *container = nullptr;
+	Common::Path basePath;
 
 	// First try SearchMan, then fallback to filesystem
 	if (archSet.hasFile(shaderPreset)) {
-		stream = archSet.createReadStreamForMember(shaderPreset);
+		Common::ArchiveMemberPtr member = archSet.getMember(shaderPreset, &container);
+		stream = member->createReadStream();
+		basePath = shaderPreset.getParent();
 	} else {
 		Common::FSNode fsnode(shaderPreset);
 		if (!fsnode.exists() || !fsnode.isReadable() || fsnode.isDirectory()
 				|| !(stream = fsnode.createReadStream())) {
-			warning("LibRetro Preset Parsing: Invalid file path '%s'", shaderPreset.c_str());
+			warning("LibRetro Preset Parsing: Invalid file path '%s'", shaderPreset.toString().c_str());
 			return nullptr;
 		}
+#if defined(WIN32)
+		static const char delimiter = '\\';
+#else
+		static const char delimiter = '/';
+#endif
+		basePath = Common::Path(fsnode.getParent().getPath(), delimiter);
 	}
 
-	Common::String basePath(Common::firstPathComponents(shaderPreset, '/'));
-
 	PresetParser parser;
 	ShaderPreset *shader = parser.parseStream(*stream);
 
 	delete stream;
 
 	if (!shader) {
-		warning("LibRetro Preset Parsing: Error while parsing file '%s': %s", shaderPreset.c_str(), parser.getErrorDesc().c_str());
+		warning("LibRetro Preset Parsing: Error while parsing file '%s': %s", shaderPreset.toString().c_str(), parser.getErrorDesc().c_str());
 		return nullptr;
 	}
 
+	shader->container = container;
 	shader->basePath = basePath;
 	return shader;
 }
diff --git a/backends/graphics/opengl/pipelines/libretro/parser.h b/backends/graphics/opengl/pipelines/libretro/parser.h
index 8f5b076cfc6..c76792155fd 100644
--- a/backends/graphics/opengl/pipelines/libretro/parser.h
+++ b/backends/graphics/opengl/pipelines/libretro/parser.h
@@ -30,7 +30,7 @@
 namespace OpenGL {
 namespace LibRetro {
 
-ShaderPreset *parsePreset(const Common::String &shaderPreset, Common::SearchSet &archSet);
+ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet);
 
 } // End of namespace LibRetro
 } // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/libretro/types.h b/backends/graphics/opengl/pipelines/libretro/types.h
index 0fcb5f41a00..92054566600 100644
--- a/backends/graphics/opengl/pipelines/libretro/types.h
+++ b/backends/graphics/opengl/pipelines/libretro/types.h
@@ -113,7 +113,8 @@ struct ShaderPass {
 };
 
 struct ShaderPreset {
-	Common::String basePath;
+	Common::Archive *container;
+	Common::Path basePath;
 
 	typedef Common::Array<ShaderTexture> TextureArray;
 	TextureArray textures;




More information about the Scummvm-git-logs mailing list