Skip to content

Commit

Permalink
ScriptAPI: FillDirList can sort files by name or time
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-mogilko committed Oct 4, 2024
1 parent eb472e1 commit 5ac3ec2
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 43 deletions.
53 changes: 52 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 @@ -235,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
6 changes: 6 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 @@ -131,6 +132,11 @@ class AssetManager
// 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
6 changes: 6 additions & 0 deletions Common/util/directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ void GetFiles(const String &dir_path, std::vector<String> &files, const String &
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();
Expand Down
102 changes: 86 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 @@ -49,27 +65,13 @@ namespace 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 @@ -206,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
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
16 changes: 15 additions & 1 deletion Editor/AGS.Editor/Resources/agsdefns.sh
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,20 @@ enum RenderLayer
eRenderLayerRoom = 0x00000008,
eRenderLayerAll = 0xFFFFFFFF
};
enum FileSortStyle
{
eFileSort_None = 0,
eFileSort_Name = 1,
eFileSort_Time = 2
};
enum SortDirection
{
eSortNoDirection = 0,
eSortAscending = 1,
eSortDescending = 2
};
#endif
Expand Down Expand Up @@ -1925,7 +1939,7 @@ builtin managed struct ListBox extends GUIControl {
/// Removes all the items from the list.
import void Clear();
/// Fills the list box with all the filenames that match the specified file mask.
import void FillDirList(const string fileMask);
import void FillDirList(const string fileMask, FileSortStyle fileSortStyle = eFileSort_Name, SortDirection sortDirection = eSortAscending);
/// Fills the list box with the current user's saved games in the given range of slots.
import int FillSaveGameList(int min_slot = 1, int max_slot = 50);
/// Gets the item index at the specified screen co-ordinates, if they lie within the list box.
Expand Down
Loading

0 comments on commit 5ac3ec2

Please sign in to comment.