Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script API: expand methods for displaying list of files and saves #2541

Merged
77 changes: 76 additions & 1 deletion Common/core/assetmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "core/assetmanager.h"
#include <algorithm>
#include <regex>
#include "util/directory.h"
#include "util/file.h"
#include "util/multifilelib.h"
#include "util/path.h"
Expand Down Expand Up @@ -177,6 +176,30 @@ bool AssetManager::DoesAssetExist(const String &asset_name, const String &filter
return false;
}

bool AssetManager::GetAssetTime(const String &asset_name, time_t &ft, const String &filter) const
{
for (const auto *lib : _activeLibs)
{
if (!lib->TestFilter(filter)) continue; // filter does not match

if (IsAssetLibDir(lib))
{
String filename = File::FindFileCI(lib->BaseDir, asset_name);
if (!filename.IsEmpty())
{
ft = File::GetFileTime(filename);
return true;
}
}
else
{
ft = File::GetFileTime(lib->RealLibFiles[0]);
return true;
}
}
return false;
}

void AssetManager::FindAssets(std::vector<String> &assets, const String &wildcard,
const String &filter) const
{
Expand Down Expand Up @@ -211,6 +234,58 @@ void AssetManager::FindAssets(std::vector<String> &assets, const String &wildcar
assets.erase(std::unique(assets.begin(), assets.end(), StrEqNoCase()), assets.end());
}

void AssetManager::FindAssets(std::vector<FileEntry> &assets, const String &wildcard,
const String &filter) const
{
// TODO: consider refactoring and merging this with FindAssets(std::vector<String> &assets);
// there are two separate methods now, because retrieving filename only is faster than
// full FileEntry (may require extra system calls on certain platforms).

String pattern = StrUtil::WildcardToRegex(wildcard);
const std::regex regex(pattern.GetCStr(), std::regex_constants::icase);
std::cmatch mr;

std::vector<FileEntry> lib_fileents;
for (const auto *lib : _activeLibs)
{
if (!lib->TestFilter(filter)) continue; // filter does not match

lib_fileents.clear();
if (IsAssetLibDir(lib))
{
// FIXME: do basedir/getparent(wildcard), getfilename(wildcard) instead?
// because FindFile does not support subdirs in wildcard!!
for (FindFile ff = FindFile::OpenFiles(lib->BaseDir, wildcard);
!ff.AtEnd(); ff.Next())
lib_fileents.push_back(ff.GetEntry());
}
else
{
time_t lib_time = File::GetFileTime(lib->RealLibFiles[0]);
for (const auto &a : lib->AssetInfos)
{
if (std::regex_match(a.FileName.GetCStr(), mr, regex))
lib_fileents.push_back(FileEntry(a.FileName, true, false, lib_time));
}
}

// We have to filter out matching entries and keep only ones that were found first by lib priority
if (assets.empty())
{
assets = std::move(lib_fileents);
}
else
{
for (const auto &fe : lib_fileents)
{
auto it_place = std::upper_bound(assets.begin(), assets.end(), fe, FileEntryCmpByNameCI());
if (it_place != assets.begin() && (it_place - 1)->Name.CompareNoCase(fe.Name) != 0)
assets.insert(it_place, fe);
}
}
}
}

AssetError AssetManager::RegisterAssetLib(const String &path, AssetLibEx *&out_lib)
{
// Test for a directory
Expand Down
9 changes: 9 additions & 0 deletions Common/core/assetmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <memory>
#include <unordered_map>
#include "core/asset.h"
#include "util/directory.h"
#include "util/stream.h"
#include "util/string_types.h"

Expand Down Expand Up @@ -123,11 +124,19 @@ class AssetManager
// Tells whether asset exists in any of the registered search locations
bool DoesAssetExist(const String &asset_name, const String &filter = "") const;
inline bool DoesAssetExist(const AssetPath &apath) const { return DoesAssetExist(apath.Name, apath.Filter); }
// Tries to get asset's "file time" (last modification time).
// Note that for the assets packed within a CLIB format this will return library's time instead.
bool GetAssetTime(const String &asset_name, time_t &ft, const String &filter = "") const;
// Searches in all the registered locations and collects a list of
// assets using given wildcard pattern
// TODO: variant accepting std::regex instead of wildcard, and replace uses where convenient
void FindAssets(std::vector<String> &assets, const String &wildcard,
const String &filter = "") const;
// Searches in all the registered locations and collects a list of
// FileEntry objects corresponding to assets, using given wildcard pattern.
// NOTE: lib file assets will have their time property equal to lib's time.
void FindAssets(std::vector<FileEntry> &assets, const String &wildcard,
const String &filter = "") const;
// Open asset stream in the given work mode; returns null if asset is not found or cannot be opened
// This method only searches in libraries that do not have any defined filters
std::unique_ptr<Stream> OpenAsset(const String &asset_name) const;
Expand Down
17 changes: 17 additions & 0 deletions Common/util/directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,23 @@ void GetFiles(const String &dir_path, std::vector<String> &files)
}
}

void GetFiles(const String &dir_path, std::vector<String> &files, const String &wildcard)
{
for (FindFile ff = FindFile::OpenFiles(dir_path, wildcard); !ff.AtEnd(); ff.Next())
files.push_back(ff.Current());
}

void GetFiles(const String &dir_path, std::vector<FileEntry> &files, const String &wildcard)
{
for (FindFile ff = FindFile::OpenFiles(dir_path, wildcard); !ff.AtEnd(); ff.Next())
files.push_back(ff.GetEntry());
}

bool HasAnyFiles(const String &dir_path)
{
return !FindFile::OpenFiles(dir_path).AtEnd();
}

} // namespace Directory


Expand Down
106 changes: 90 additions & 16 deletions Common/util/directory.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ namespace AGS
namespace Common
{

// FileEntry describes a single entry in the filesystem.
struct FileEntry
{
String Name;
// TODO: make flags instead?
bool IsFile = false;
bool IsDir = false;
time_t Time{};

FileEntry() = default;
FileEntry(const String &name, bool is_file, bool is_dir, const time_t &time)
: Name(name), IsFile(is_file), IsDir(is_dir), Time(time) {}

operator bool() const { return !Name.IsEmpty(); }
};

namespace Directory
{
// Creates new directory (if it does not exist)
Expand All @@ -47,25 +63,15 @@ namespace Directory
void GetDirs(const String &dir_path, std::vector<String> &dirs);
// Get list of files found in the given directory
void GetFiles(const String &dir_path, std::vector<String> &files);
// Get list of files found in the given directory using wildcard pattern
void GetFiles(const String &dir_path, std::vector<String> &files, const String &wildcard);
// Get list of file entries in the given directory using wildcard pattern
void GetFiles(const String &dir_path, std::vector<FileEntry> &files, const String &wildcard);
// Tells whether there are any files in the given directory
bool HasAnyFiles(const String &dir_path);
} // namespace Directory


// FileEntry describes a single entry in the filesystem.
struct FileEntry
{
String Name;
// TODO: make flags instead?
bool IsFile = false;
bool IsDir = false;
time_t Time{};

FileEntry() = default;
FileEntry(const String &name, bool is_file, bool is_dir, const time_t &time)
: Name(name), IsFile(is_file), IsDir(is_dir), Time(time) {}

operator bool() const { return !Name.IsEmpty(); }
};

//
// DirectoryIterator iterates entries in the directory.
// The order of iteration is undefined.
Expand Down Expand Up @@ -202,6 +208,74 @@ class FindFile
bool _doDirs = false;
};


//
// FileEntry comparators
//
struct FileEntryEqByName
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe1.Name == fe2.Name;
}
};

struct FileEntryEqByNameCI
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe1.Name.CompareNoCase(fe2.Name) == 0;
}
};

struct FileEntryCmpByName
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe1.Name.Compare(fe2.Name) < 0;
}
};

struct FileEntryCmpByNameDsc
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe2.Name.Compare(fe1.Name) < 0;
}
};

struct FileEntryCmpByNameCI
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe1.Name.CompareNoCase(fe2.Name) < 0;
}
};

struct FileEntryCmpByNameDscCI
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe2.Name.CompareNoCase(fe1.Name) < 0;
}
};

struct FileEntryCmpByTime
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe1.Time < fe2.Time;
}
};

struct FileEntryCmpByTimeDsc
{
bool operator()(const FileEntry &fe1, const FileEntry &fe2) const
{
return fe2.Time < fe1.Time;
}
};

} // namespace Common
} // namespace AGS

Expand Down
6 changes: 6 additions & 0 deletions Common/util/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ soff_t File::GetFileSize(const String &filename)
return size;
}

time_t File::GetFileTime(const String &filename)
{
return ags_file_time(filename.GetCStr());
// NOTE: ANDROID's AAsset storage seems to be unapplicable here
}

bool File::TestReadFile(const String &filename)
{
FILE *test_file = ags_fopen(filename.GetCStr(), "rb");
Expand Down
2 changes: 2 additions & 0 deletions Common/util/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ namespace File
bool IsFileOrDir(const String &filename);
// Returns size of a file, or -1 if no such file found
soff_t GetFileSize(const String &filename);
// Returns file's last writing time, or time_t() if no such file found
time_t GetFileTime(const String &filename);
// Tests if file could be opened for reading
bool TestReadFile(const String &filename);
// Opens a file for writing or creates new one if it does not exist; deletes file if it was created during test
Expand Down
41 changes: 30 additions & 11 deletions Common/util/stdio_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,17 @@ int ags_directory_exists(const char *path)

int ags_path_exists(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
return PathFileExistsW(wstr);
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return 0;
}
return S_ISREG(path_stat.st_mode) || S_ISDIR(path_stat.st_mode);
#endif
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
return PathFileExistsW(wstr);
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return 0;
}
return S_ISREG(path_stat.st_mode) || S_ISDIR(path_stat.st_mode);
#endif
}

file_off_t ags_file_size(const char *path)
Expand All @@ -158,6 +158,25 @@ file_off_t ags_file_size(const char *path)
#endif
}

time_t ags_file_time(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
WCHAR wstr[MAX_PATH_SZ];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wstr, MAX_PATH_SZ);
struct _stat64 path_stat;
if (_wstat64(wstr, &path_stat) != 0) {
return -1;
}
return path_stat.st_mtime;
#else
struct stat path_stat;
if (stat(path, &path_stat) != 0) {
return -1;
}
return path_stat.st_mtime;
#endif
}

int ags_file_remove(const char *path)
{
#if AGS_PLATFORM_OS_WINDOWS
Expand Down
2 changes: 2 additions & 0 deletions Common/util/stdio_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <stdio.h>
#include <stdint.h>
#include <time.h>

typedef int64_t file_off_t;

Expand All @@ -38,6 +39,7 @@ int ags_file_exists(const char *path);
int ags_directory_exists(const char *path);
int ags_path_exists(const char *path);
file_off_t ags_file_size(const char *path);
time_t ags_file_time(const char *path);

int ags_file_remove(const char *path);
int ags_file_rename(const char *src, const char *dst);
Expand Down
9 changes: 9 additions & 0 deletions Common/util/string_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ namespace Common
// Various comparison functors
//

// Test case-sensitive String equality
struct StrEq
{
bool operator()(const String &s1, const String &s2) const
{
return s1 == s2;
}
};

// Test case-insensitive String equality
struct StrEqNoCase
{
Expand Down
Loading