Skip to content

Commit

Permalink
Add meta.xml loading files pattern (#3203)
Browse files Browse the repository at this point in the history
* Add glob vendor

* Add loading files pattern

* Update due to code revision.

* Update due to code revision

* Add to ignore empty folder with wildcard directory

---------

Co-authored-by: lopsi <40902730+Lpsd@users.noreply.github.com>
  • Loading branch information
W3lac3 and Lpsd committed May 23, 2024
1 parent 56a77fe commit 90e2737
Show file tree
Hide file tree
Showing 8 changed files with 615 additions and 39 deletions.
155 changes: 120 additions & 35 deletions Server/mods/deathmatch/logic/CResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "lua/CLuaFunctionParseHelpers.h"
#include <net/SimHeaders.h>
#include <zip.h>
#include <glob/glob.h>

#ifdef WIN32
#include <zip/iowin32.h>
Expand Down Expand Up @@ -388,6 +389,7 @@ void CResource::TidyUp()
delete pResourceFile;

m_ResourceFiles.clear();
m_ResourceFilesCountPerDir.clear();

// Go through each included resource item and delete it
for (CIncludedResources* pIncludedResources : m_IncludedResources)
Expand Down Expand Up @@ -595,8 +597,12 @@ bool CResource::GenerateChecksums()
bool CResource::HasResourceChanged()
{
std::string strPath;
std::string_view strDirPath = m_strResourceDirectoryPath;

if (IsResourceZip())
{
strDirPath = m_strResourceCachePath;

// Zip file might have changed
CChecksum checksum = CChecksum::GenerateChecksumFromFileUnsafe(m_strResourceZip);
if (checksum != m_zipHash)
Expand Down Expand Up @@ -633,6 +639,14 @@ bool CResource::HasResourceChanged()
}
}

for (const auto& [strGlob, uiFileCount] : m_ResourceFilesCountPerDir)
{
std::vector<std::filesystem::path> files = glob::rglob(strDirPath.data() + strGlob);

if (files.size() != uiFileCount)
return true;
}

if (GetFilePath("meta.xml", strPath))
{
CChecksum checksum = CChecksum::GenerateChecksumFromFileUnsafe(strPath);
Expand Down Expand Up @@ -1341,6 +1355,23 @@ bool CResource::GetFilePath(const char* szFilename, string& strPath)
return FileExists(strPath);
}

std::vector<std::string> CResource::GetFilePaths(const char* szFilename)
{
std::vector<std::string> vecFiles;
const std::string& strDirectory = IsResourceZip() ? m_strResourceCachePath : m_strResourceDirectoryPath;
const std::string strFilePath = strDirectory + szFilename;

for (const std::filesystem::path& path : glob::rglob(strFilePath))
{
std::string strPath = std::filesystem::relative(path, strDirectory).string();
ReplaceSlashes(strPath);

vecFiles.push_back(std::move(strPath));
}

return vecFiles;
}

// Return true if file name is used by this resource
bool CResource::IsFilenameUsed(const SString& strFilename, bool bClient)
{
Expand Down Expand Up @@ -1586,37 +1617,59 @@ bool CResource::ReadIncludedFiles(CXMLNode* pRoot)

if (!strFilename.empty())
{
std::string strFullFilename;
ReplaceSlashes(strFilename);

if (IsFilenameUsed(strFilename, true))
if (!IsValidFilePath(strFilename.c_str()))
{
m_strFailureReason = SString("Couldn't find file(s) %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
CLogger::ErrorPrintf(m_strFailureReason);
return false;
}

std::vector<std::string> vecFiles = GetFilePaths(strFilename.c_str());

if (vecFiles.empty())
{
CLogger::LogPrintf("WARNING: Ignoring duplicate client file in resource '%s': '%s'\n", m_strResourceName.c_str(), strFilename.c_str());
continue;
if (glob::has_magic(strFilename))
{
m_ResourceFilesCountPerDir[strFilename] = vecFiles.size();
continue;
}

m_strFailureReason = SString("Couldn't find file(s) %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
CLogger::ErrorPrintf(m_strFailureReason);
return false;
}

bool bDownload = true;
CXMLAttribute* pDownload = Attributes.Find("download");

if (pDownload)
{
const char* szDownload = pDownload->GetValue().c_str();
const std::string strDownload = pDownload->GetValue();

if (!stricmp(szDownload, "no") || !stricmp(szDownload, "false"))
if (strDownload == "no" || strDownload == "false")
bDownload = false;
}

// Create a new resourcefile item
if (IsValidFilePath(strFilename.c_str()) && GetFilePath(strFilename.c_str(), strFullFilename))
for (const std::string& strFilePath : vecFiles)
{
m_ResourceFiles.push_back(new CResourceClientFileItem(this, strFilename.c_str(), strFullFilename.c_str(), &Attributes, bDownload));
}
else
{
m_strFailureReason = SString("Couldn't find file %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
CLogger::ErrorPrintf(m_strFailureReason);
return false;
std::string strFullFilename;

if (IsFilenameUsed(strFilePath, true))
{
CLogger::LogPrintf("WARNING: Ignoring duplicate client file in resource '%s': '%s'\n", m_strResourceName.c_str(), strFilePath.c_str());
continue;
}

if (GetFilePath(strFilePath.c_str(), strFullFilename))
{
m_ResourceFiles.push_back(new CResourceClientFileItem(this, strFilePath.c_str(), strFullFilename.c_str(), &Attributes, bDownload));
}
}

if (glob::has_magic(strFilename))
m_ResourceFilesCountPerDir[strFilename] = vecFiles.size();
}
else
{
Expand Down Expand Up @@ -1766,35 +1819,67 @@ bool CResource::ReadIncludedScripts(CXMLNode* pRoot)

if (!strFilename.empty())
{
std::string strFullFilename;
ReplaceSlashes(strFilename);

if (bClient && IsFilenameUsed(strFilename, true))
{
CLogger::LogPrintf("WARNING: Ignoring duplicate client script file in resource '%s': '%s'\n", m_strResourceName.c_str(),
strFilename.c_str());
bClient = false;
}
if (bServer && IsFilenameUsed(strFilename, false))
if (!IsValidFilePath(strFilename.c_str()))
{
CLogger::LogPrintf("WARNING: Duplicate script file in resource '%s': '%s'\n", m_strResourceName.c_str(), strFilename.c_str());
m_strFailureReason = SString("Couldn't find script(s) %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
CLogger::ErrorPrintf(m_strFailureReason);
return false;
}

// Extract / get the filepath of the file
if (IsValidFilePath(strFilename.c_str()) && GetFilePath(strFilename.c_str(), strFullFilename))
{
// Create it depending on the type (client or server or shared) and add it to the list of resource files
if (bServer)
m_ResourceFiles.push_back(new CResourceScriptItem(this, strFilename.c_str(), strFullFilename.c_str(), &Attributes));
if (bClient)
m_ResourceFiles.push_back(new CResourceClientScriptItem(this, strFilename.c_str(), strFullFilename.c_str(), &Attributes));
}
else
std::vector<std::string> vecFiles = GetFilePaths(strFilename.c_str());

if (vecFiles.empty())
{
m_strFailureReason = SString("Couldn't find script %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
if (glob::has_magic(strFilename))
{
m_ResourceFilesCountPerDir[strFilename] = vecFiles.size();
continue;
}

m_strFailureReason = SString("Couldn't find script(s) %s for resource %s\n", strFilename.c_str(), m_strResourceName.c_str());
CLogger::ErrorPrintf(m_strFailureReason);
return false;
}

for (const std::string& strFilePath : vecFiles)
{
std::string strFullFilename;
bool bLoadClient = bClient;
bool bLoadServer = bServer;

if (GetFilePath(strFilePath.c_str(), strFullFilename))
{
// Check if the filename is already used by this resource

if (bClient && IsFilenameUsed(strFilePath, true))
{
CLogger::LogPrintf("WARNING: Ignoring duplicate client script file in resource '%s': '%s'\n", m_strResourceName.c_str(),
strFilePath.c_str());
bLoadClient = false;
}

if (bServer && IsFilenameUsed(strFilePath, false))
{
CLogger::LogPrintf("WARNING: Ignoring duplicate script file in resource '%s': '%s'\n", m_strResourceName.c_str(),
strFilePath.c_str());

bLoadServer = false;
}

// Create it depending on the type (client or server or shared) and add it to the list of resource files

if (bLoadServer)
m_ResourceFiles.push_back(new CResourceScriptItem(this, strFilePath.c_str(), strFullFilename.c_str(), &Attributes));

if (bLoadClient)
m_ResourceFiles.push_back(new CResourceClientScriptItem(this, strFilePath.c_str(), strFullFilename.c_str(), &Attributes));
}
}

if (glob::has_magic(strFilename))
m_ResourceFilesCountPerDir[strFilename] = vecFiles.size();
}
else
{
Expand Down
9 changes: 6 additions & 3 deletions Server/mods/deathmatch/logic/CResource.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,11 @@ class CResource : public EHS
bool CheckIfStartable();
void DisplayInfo();

bool GetFilePath(const char* szFilename, std::string& strPath);
const std::string& GetResourceDirectoryPath() const { return m_strResourceDirectoryPath; }
const std::string& GetResourceCacheDirectoryPath() const { return m_strResourceCachePath; }
bool GetFilePath(const char* szFilename, std::string& strPath);
std::vector<std::string> GetFilePaths(const char* szFilename);

const std::string& GetResourceDirectoryPath() const { return m_strResourceDirectoryPath; }
const std::string& GetResourceCacheDirectoryPath() const { return m_strResourceCachePath; }

std::list<CResourceFile*>& GetFiles() { return m_ResourceFiles; }
size_t GetFileCount() const noexcept { return m_ResourceFiles.size(); }
Expand Down Expand Up @@ -395,6 +397,7 @@ class CResource : public EHS
KeyValueMap m_Info;
std::list<CIncludedResources*> m_IncludedResources; // we store them here temporarily, then read them once all the resources are loaded
std::list<CResourceFile*> m_ResourceFiles;
std::map<std::string, int> m_ResourceFilesCountPerDir;
std::list<CResource*> m_Dependents; // resources that have "included" or loaded this one
std::list<CExportedFunction> m_ExportedFunctions;
std::list<CResource*> m_TemporaryIncludes; // started by startResource script command
Expand Down
3 changes: 2 additions & 1 deletion Server/mods/deathmatch/premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ project "Deathmatch"
"../../../vendor/bochs",
"../../../vendor/pme",
"../../../vendor/zip",
"../../../vendor/glob/include",
"../../../vendor/zlib",
"../../../vendor/pcre",
"../../../vendor/json-c",
Expand All @@ -33,7 +34,7 @@ project "Deathmatch"

defines { "SDK_WITH_BCRYPT" }
links {
"Lua_Server", "sqlite", "ehs", "cryptopp", "pme", "pcre", "json-c", "zip", "zlib", "blowfish_bcrypt",
"Lua_Server", "sqlite", "ehs", "cryptopp", "pme", "pcre", "json-c", "zip", "glob", "zlib", "blowfish_bcrypt",
}

vpaths {
Expand Down
1 change: 1 addition & 0 deletions premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,4 @@ workspace "MTASA"
include "vendor/unrar"
include "vendor/zip"
include "vendor/zlib"
include "vendor/glob"
21 changes: 21 additions & 0 deletions vendor/glob/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Pranav

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
54 changes: 54 additions & 0 deletions vendor/glob/include/glob/glob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

#pragma once
#include <string>
#include <vector>

#ifdef GLOB_USE_GHC_FILESYSTEM
#include <ghc/filesystem.hpp>
#else
#include <filesystem>
#endif

namespace glob {

#ifdef GLOB_USE_GHC_FILESYSTEM
namespace fs = ghc::filesystem;
#else
namespace fs = std::filesystem;
#endif

/// \param pathname string containing a path specification
/// \return vector of paths that match the pathname
///
/// Pathnames can be absolute (/usr/src/Foo/Makefile) or relative (../../Tools/*/*.gif)
/// Pathnames can contain shell-style wildcards
/// Broken symlinks are included in the results (as in the shell)
std::vector<fs::path> glob(const std::string &pathname);

/// \param pathnames string containing a path specification
/// \return vector of paths that match the pathname
///
/// Globs recursively.
/// The pattern “**” will match any files and zero or more directories, subdirectories and
/// symbolic links to directories.
std::vector<fs::path> rglob(const std::string &pathname);

/// Runs `glob` against each pathname in `pathnames` and accumulates the results
std::vector<fs::path> glob(const std::vector<std::string> &pathnames);

/// Runs `rglob` against each pathname in `pathnames` and accumulates the results
std::vector<fs::path> rglob(const std::vector<std::string> &pathnames);

/// Initializer list overload for convenience
std::vector<fs::path> glob(const std::initializer_list<std::string> &pathnames);

/// Initializer list overload for convenience
std::vector<fs::path> rglob(const std::initializer_list<std::string> &pathnames);

/// Returns true if the input path matche the glob pattern
bool fnmatch(const fs::path &name, const std::string &pattern);

/// Returns true if the input path contains any magic characters
bool has_magic(const fs::path& pathname);

} // namespace glob
22 changes: 22 additions & 0 deletions vendor/glob/premake5.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
project "glob"
language "C++"
cppdialect "C++17"
kind "StaticLib"
targetname "glob"

vpaths {
["Headers/*"] = "include/**.h",
["Sources"] = "source/**.cpp",
["*"] = "premake5.lua"
}

files {
"premake5.lua",
"**.cpp",
"**.h",
}

includedirs {
"source",
"include"
}
Loading

0 comments on commit 90e2737

Please sign in to comment.