1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-17 12:03:07 +01:00

Refactor TrackDesignRepository to use FileIndex

This commit is contained in:
Ted John
2017-08-30 21:01:07 +01:00
parent 92b17b149c
commit 8808444562
5 changed files with 106 additions and 161 deletions

View File

@@ -68,7 +68,9 @@ private:
uint8 const _version;
std::string const _indexPath;
std::string const _pattern;
std::vector<std::string> const _paths;
public:
std::vector<std::string> const SearchPaths;
public:
/**
@@ -91,7 +93,7 @@ public:
_version(version),
_indexPath(indexPath),
_pattern(pattern),
_paths(paths)
SearchPaths(paths)
{
}
@@ -155,7 +157,7 @@ private:
{
DirectoryStats stats;
std::vector<std::string> files;
for (const auto directory : _paths)
for (const auto directory : SearchPaths)
{
log_verbose("FileIndex:Scanning for %s in '%s'", _pattern.c_str(), directory.c_str());

View File

@@ -142,6 +142,11 @@ namespace String
}
}
bool StartsWith(const std::string &str, const std::string &match, bool ignoreCase)
{
return StartsWith(str.c_str(), match.c_str(), ignoreCase);
}
size_t IndexOf(const utf8 * str, utf8 match, size_t startIndex)
{
const utf8 * ch = str + startIndex;

View File

@@ -36,6 +36,7 @@ namespace String
bool Equals(const std::string &a, const std::string &b, bool ignoreCase = false);
bool Equals(const utf8 * a, const utf8 * b, bool ignoreCase = false);
bool StartsWith(const utf8 * str, const utf8 * match, bool ignoreCase = false);
bool StartsWith(const std::string &str, const std::string &match, bool ignoreCase = false);
size_t IndexOf(const utf8 * str, utf8 match, size_t startIndex = 0);
size_t LastIndexOf(const utf8 * str, utf8 match);

View File

@@ -21,14 +21,14 @@
#include "../core/Collections.hpp"
#include "../core/Console.hpp"
#include "../core/File.h"
#include "../core/FileScanner.h"
#include "../core/FileIndex.hpp"
#include "../core/FileStream.hpp"
#include "../core/Path.hpp"
#include "RideGroupManager.h"
#include "../core/String.hpp"
#include "../object/ObjectRepository.h"
#include "../object/RideObject.h"
#include "../PlatformEnvironment.h"
#include "RideGroupManager.h"
#include "TrackDesignRepository.h"
extern "C"
@@ -38,52 +38,107 @@ extern "C"
using namespace OpenRCT2;
#pragma pack(push, 1)
struct TrackRepositoryHeader
{
uint32 MagicNumber;
uint16 Version;
uint32 TotalFiles;
uint64 TotalFileSize;
uint32 FileDateModifiedChecksum;
uint32 PathChecksum;
uint32 NumItems;
};
#pragma pack(pop)
struct TrackRepositoryItem
{
std::string Name;
std::string Path;
uint8 RideType = 0;
std::string ObjectEntry;
uint32 Flags;
uint32 Flags = 0;
};
constexpr uint32 TRACK_REPOSITORY_MAGIC_NUMBER = 0x58444954;
constexpr uint16 TRACK_REPOSITORY_VERSION = 1;
enum TRACK_REPO_ITEM_FLAGS
{
TRIF_READ_ONLY = (1 << 0),
};
static std::string GetNameFromTrackPath(const std::string &path)
{
std::string name = Path::GetFileNameWithoutExtension(path);
//The track name should be the file name until the first instance of a dot
name = name.substr(0, name.find_first_of("."));
return name;
}
class TrackDesignFileIndex final : public FileIndex<TrackRepositoryItem>
{
private:
static constexpr uint32 MAGIC_NUMBER = 0x58444954; // TIDX
static constexpr uint16 VERSION = 1;
static constexpr auto PATTERN = "*.td4;*.td6";
public:
TrackDesignFileIndex(IPlatformEnvironment * env) :
FileIndex("track design index",
MAGIC_NUMBER,
VERSION,
env->GetFilePath(PATHID::CACHE_TRACKS),
std::string(PATTERN),
std::vector<std::string>({
env->GetDirectoryPath(DIRBASE::RCT1, DIRID::TRACK),
env->GetDirectoryPath(DIRBASE::RCT2, DIRID::TRACK),
env->GetDirectoryPath(DIRBASE::USER, DIRID::TRACK) }))
{
}
public:
std::tuple<bool, TrackRepositoryItem> Create(const std::string &path) const override
{
auto td6 = track_design_open(path.c_str());
if (td6 != nullptr)
{
TrackRepositoryItem item;
item.Name = GetNameFromTrackPath(path);
item.Path = path;
item.RideType = td6->type;
item.ObjectEntry = std::string(td6->vehicle_object.name, 8);
item.Flags = 0;
if (IsTrackReadOnly(path))
{
item.Flags |= TRIF_READ_ONLY;
}
track_design_dispose(td6);
return std::make_tuple(true, item);
}
else
{
return std::make_tuple(true, TrackRepositoryItem());
}
}
protected:
void Serialise(IStream * stream, const TrackRepositoryItem &item) const override
{
stream->WriteValue(item);
}
TrackRepositoryItem Deserialise(IStream * stream) const override
{
return stream->ReadValue<TrackRepositoryItem>();
}
private:
bool IsTrackReadOnly(const std::string &path) const
{
return
String::StartsWith(path, SearchPaths[0]) ||
String::StartsWith(path, SearchPaths[1]);
}
};
class TrackDesignRepository final : public ITrackDesignRepository
{
private:
static constexpr const utf8 * TD_FILE_PATTERN = "*.td4;*.td6";
IPlatformEnvironment * _env;
IPlatformEnvironment * const _env;
TrackDesignFileIndex const _fileIndex;
std::vector<TrackRepositoryItem> _items;
QueryDirectoryResult _directoryQueryResult = { 0 };
public:
TrackDesignRepository(IPlatformEnvironment * env)
: _env(env),
_fileIndex(env)
{
Guard::ArgumentNotNull(env);
_env = env;
}
virtual ~TrackDesignRepository() final
@@ -227,21 +282,14 @@ public:
void Scan() override
{
std::string rct2Directory = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::TRACK);
std::string userDirectory = _env->GetDirectoryPath(DIRBASE::USER, DIRID::TRACK);
_items.clear();
_directoryQueryResult = { 0 };
Query(rct2Directory);
Query(userDirectory);
if (!Load())
auto trackDesigns = _fileIndex.LoadOrBuild();
for (auto td : trackDesigns)
{
Scan(rct2Directory, TRIF_READ_ONLY);
Scan(userDirectory);
SortItems();
Save();
_items.push_back(td);
}
SortItems();
}
bool Delete(const std::string &path) override
@@ -295,48 +343,18 @@ public:
std::string newPath = Path::Combine(installDir, fileName);
if (File::Copy(path, newPath, false))
{
AddTrack(path);
SortItems();
result = path;
auto td = _fileIndex.Create(path);
if (std::get<0>(td))
{
_items.push_back(std::get<1>(td));
SortItems();
result = path;
}
}
return result;
}
private:
void Query(const std::string &directory)
{
std::string pattern = Path::Combine(directory, TD_FILE_PATTERN);
Path::QueryDirectory(&_directoryQueryResult, pattern);
}
void Scan(const std::string &directory, uint32 flags = 0)
{
std::string pattern = Path::Combine(directory, TD_FILE_PATTERN);
IFileScanner * scanner = Path::ScanDirectory(pattern, true);
while (scanner->Next())
{
const utf8 * path = scanner->GetPath();
AddTrack(path, flags);
}
delete scanner;
}
void AddTrack(const std::string path, uint32 flags = 0)
{
rct_track_td6 * td6 = track_design_open(path.c_str());
if (td6 != nullptr)
{
TrackRepositoryItem item;
item.Name = GetNameFromTrackPath(path);
item.Path = path;
item.RideType = td6->type;
item.ObjectEntry = std::string(td6->vehicle_object.name, 8);
item.Flags = flags;
_items.push_back(item);
track_design_dispose(td6);
}
}
void SortItems()
{
std::sort(_items.begin(), _items.end(), [](const TrackRepositoryItem &a,
@@ -350,78 +368,6 @@ private:
});
}
bool Load()
{
std::string path = _env->GetFilePath(PATHID::CACHE_TRACKS);
bool result = false;
try
{
auto fs = FileStream(path, FILE_MODE_OPEN);
// Read header, check if we need to re-scan
auto header = fs.ReadValue<TrackRepositoryHeader>();
if (header.MagicNumber == TRACK_REPOSITORY_MAGIC_NUMBER &&
header.Version == TRACK_REPOSITORY_VERSION &&
header.TotalFiles == _directoryQueryResult.TotalFiles &&
header.TotalFileSize == _directoryQueryResult.TotalFileSize &&
header.FileDateModifiedChecksum == _directoryQueryResult.FileDateModifiedChecksum &&
header.PathChecksum == _directoryQueryResult.PathChecksum)
{
// Directory is the same, just read the saved items
for (uint32 i = 0; i < header.NumItems; i++)
{
TrackRepositoryItem item;
item.Name = fs.ReadStdString();
item.Path = fs.ReadStdString();
item.RideType = fs.ReadValue<uint8>();
item.ObjectEntry = fs.ReadStdString();
item.Flags = fs.ReadValue<uint32>();
_items.push_back(item);
}
result = true;
}
}
catch (const Exception &)
{
Console::Error::WriteLine("Unable to write object repository index.");
}
return result;
}
void Save() const
{
std::string path = _env->GetFilePath(PATHID::CACHE_TRACKS);
try
{
auto fs = FileStream(path, FILE_MODE_WRITE);
// Write header
TrackRepositoryHeader header = { 0 };
header.MagicNumber = TRACK_REPOSITORY_MAGIC_NUMBER;
header.Version = TRACK_REPOSITORY_VERSION;
header.TotalFiles = _directoryQueryResult.TotalFiles;
header.TotalFileSize = _directoryQueryResult.TotalFileSize;
header.FileDateModifiedChecksum = _directoryQueryResult.FileDateModifiedChecksum;
header.PathChecksum = _directoryQueryResult.PathChecksum;
header.NumItems = (uint32)_items.size();
fs.WriteValue(header);
// Write items
for (const auto item : _items)
{
fs.WriteString(item.Name);
fs.WriteString(item.Path);
fs.WriteValue(item.RideType);
fs.WriteString(item.ObjectEntry);
fs.WriteValue(item.Flags);
}
}
catch (const Exception &)
{
Console::Error::WriteLine("Unable to write object repository index.");
}
}
size_t GetTrackIndex(const std::string &path) const
{
for (size_t i = 0; i < _items.size(); i++)
@@ -444,15 +390,6 @@ private:
}
return result;
}
public:
static std::string GetNameFromTrackPath(const std::string &path)
{
std::string name = Path::GetFileNameWithoutExtension(path);
//The track name should be the file name until the first instance of a dot
name = name.substr(0, name.find_first_of("."));
return name;
}
};
static TrackDesignRepository * _trackDesignRepository = nullptr;
@@ -522,6 +459,6 @@ extern "C"
utf8 * track_repository_get_name_from_path(const utf8 * path)
{
return String::Duplicate(TrackDesignRepository::GetNameFromTrackPath(path));
return String::Duplicate(GetNameFromTrackPath(path));
}
}

View File

@@ -125,7 +125,7 @@ static void scenario_highscore_free(scenario_highscore_entry * highscore)
class ScenarioFileIndex final : public FileIndex<scenario_index_entry>
{
private:
static constexpr uint32 MAGIC_NUMBER = 0x58444953;
static constexpr uint32 MAGIC_NUMBER = 0x58444953; // SIDX
static constexpr uint16 VERSION = 1;
static constexpr auto PATTERN = "*.sc4;*.sc6";