mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-16 03:23:15 +01:00
Fix all errors
This commit is contained in:
@@ -14,6 +14,13 @@
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include "Console.hpp"
|
||||
#include "File.h"
|
||||
#include "FileStream.hpp"
|
||||
@@ -103,6 +110,32 @@ namespace File
|
||||
Memory::Free(data);
|
||||
return lines;
|
||||
}
|
||||
|
||||
uint64 GetLastModified(const std::string &path)
|
||||
{
|
||||
uint64 lastModified = 0;
|
||||
#ifdef _WIN32
|
||||
auto pathW = utf8_to_widechar(path.c_str());
|
||||
auto hFile = CreateFileW(pathW, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FILETIME ftCreate, ftAccess, ftWrite;
|
||||
if (GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
|
||||
{
|
||||
lastModified = ((uint64)ftWrite.dwHighDateTime << 32ULL) | (uint64)ftWrite.dwLowDateTime;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
free(pathW);
|
||||
#else
|
||||
struct stat statInfo;
|
||||
if (stat(path.c_str(), &statInfo) == 0)
|
||||
{
|
||||
lastModified = statInfo.st_mtime;
|
||||
}
|
||||
#endif
|
||||
return lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
|
||||
@@ -29,4 +29,5 @@ namespace File
|
||||
void * ReadAllBytes(const std::string &path, size_t * length);
|
||||
void WriteAllBytes(const std::string &path, const void * buffer, size_t length);
|
||||
std::vector<std::string> ReadAllLines(const std::string &path);
|
||||
uint64 GetLastModified(const std::string &path);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "../common.h"
|
||||
#include "File.h"
|
||||
#include "FileScanner.h"
|
||||
#include "FileStream.hpp"
|
||||
#include "Path.hpp"
|
||||
|
||||
template<typename TItem>
|
||||
class FileIndex
|
||||
@@ -29,7 +35,7 @@ private:
|
||||
uint64 TotalFileSize = 0;
|
||||
uint32 FileDateModifiedChecksum = 0;
|
||||
uint32 PathChecksum = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ScanResult
|
||||
{
|
||||
@@ -49,17 +55,17 @@ private:
|
||||
uint8 VersionA = 0;
|
||||
uint8 VersionB = 0;
|
||||
uint16 LanguageId = 0;
|
||||
DirectoryStats DirectoryStats;
|
||||
DirectoryStats Stats;
|
||||
uint32 NumItems = 0;
|
||||
};
|
||||
|
||||
constexpr uint8 FILE_INDEX_VERSION = 4;
|
||||
static constexpr uint8 FILE_INDEX_VERSION = 4;
|
||||
|
||||
uint32 _magicNumber;
|
||||
uint8 _version;
|
||||
std::string _indexPath;
|
||||
std::string _pattern;
|
||||
std::vector<std::string> _paths;
|
||||
uint32 const _magicNumber;
|
||||
uint8 const _version;
|
||||
std::string const _indexPath;
|
||||
std::string const _pattern;
|
||||
std::vector<std::string> const _paths;
|
||||
|
||||
public:
|
||||
FileIndex(uint32 magicNumber,
|
||||
@@ -75,11 +81,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FileIndex() = default;
|
||||
|
||||
/**
|
||||
* Queries and directories and loads the index header. If the index is up to date,
|
||||
* the items are loaded from the index and returned, otherwise the index is rebuilt.
|
||||
*/
|
||||
std::vector<TItem> LoadOrBuild()
|
||||
std::vector<TItem> LoadOrBuild() const
|
||||
{
|
||||
std::vector<TItem> items;
|
||||
auto scanResult = Scan();
|
||||
@@ -97,30 +105,32 @@ public:
|
||||
auto item = Create(filePath);
|
||||
items.push_back(item);
|
||||
}
|
||||
WriteIndexFile(items);
|
||||
WriteIndexFile(scanResult.Stats, items);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Loads the given file and creates the item representing the data to store in the index.
|
||||
*/
|
||||
virtual TItem Create(const std::string &path) abstract;
|
||||
virtual TItem Create(const std::string &path) const abstract;
|
||||
|
||||
/**
|
||||
* Serialises an index item to the given stream.
|
||||
*/
|
||||
virtual void Serialise(IStream * stream, const TItem item);
|
||||
virtual void Serialise(IStream * stream, const TItem &item) const abstract;
|
||||
|
||||
/**
|
||||
* Deserialises an index item from the given stream.
|
||||
*/
|
||||
virtual TItem Deserialise(IStream * stream) abstract;
|
||||
virtual TItem Deserialise(IStream * stream) const abstract;
|
||||
|
||||
private:
|
||||
ScanResult Scan()
|
||||
ScanResult Scan() const
|
||||
{
|
||||
ScanResult scanResult;
|
||||
DirectoryStats stats;
|
||||
std::vector<std::string> files;
|
||||
for (const auto directory : _paths)
|
||||
{
|
||||
auto pattern = Path::Combine(directory, _pattern);
|
||||
@@ -130,42 +140,43 @@ private:
|
||||
auto fileInfo = scanner->GetFileInfo();
|
||||
auto path = std::string(scanner->GetPath());
|
||||
|
||||
scanResult.Files.push(path);
|
||||
files.push_back(path);
|
||||
|
||||
scanResult.TotalFiles++;
|
||||
scanResult.TotalFileSize += fileInfo->Size;
|
||||
scanResult.FileDateModifiedChecksum ^=
|
||||
stats.TotalFiles++;
|
||||
stats.TotalFileSize += fileInfo->Size;
|
||||
stats.FileDateModifiedChecksum ^=
|
||||
(uint32)(fileInfo->LastModified >> 32) ^
|
||||
(uint32)(fileInfo->LastModified & 0xFFFFFFFF);
|
||||
scanResult.FileDateModifiedChecksum = ror32(result->FileDateModifiedChecksum, 5);
|
||||
scanResult.PathChecksum += GetPathChecksum(path);
|
||||
stats.FileDateModifiedChecksum = ror32(stats.FileDateModifiedChecksum, 5);
|
||||
stats.PathChecksum += GetPathChecksum(path);
|
||||
}
|
||||
delete scanner;
|
||||
}
|
||||
return ScanResult(stats, files);
|
||||
}
|
||||
|
||||
std::tuple<bool, std::vector<TItem>> ReadIndexFile(const DirectoryStats &stats)
|
||||
std::tuple<bool, std::vector<TItem>> ReadIndexFile(const DirectoryStats &stats) const
|
||||
{
|
||||
bool loadedItems = false;
|
||||
std::vector<TItem> items;
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
auto fs = FileStream(_indexPath, FILE_MODE_OPEN);
|
||||
|
||||
// Read header, check if we need to re-scan
|
||||
auto header = fs.ReadValue<FileIndexHeader>();
|
||||
if (header.MagicNumber == _magicNumber &&
|
||||
header.VersionA == FILE_INDEX_VERSION &&
|
||||
header.VersionB == _version &&
|
||||
header.TotalFiles == scanResult.TotalFiles &&
|
||||
header.TotalFileSize == scanResult.TotalFileSize &&
|
||||
header.FileDateModifiedChecksum == scanResult.FileDateModifiedChecksum &&
|
||||
header.PathChecksum == scanResult.PathChecksum)
|
||||
header.Stats.TotalFiles == stats.TotalFiles &&
|
||||
header.Stats.TotalFileSize == stats.TotalFileSize &&
|
||||
header.Stats.FileDateModifiedChecksum == stats.FileDateModifiedChecksum &&
|
||||
header.Stats.PathChecksum == stats.PathChecksum)
|
||||
{
|
||||
// Directory is the same, just read the saved items
|
||||
for (uint32 i = 0; i < header.NumItems; i++)
|
||||
{
|
||||
auto item = Deserialise(fs);
|
||||
auto item = Deserialise(&fs);
|
||||
items.push_back(item);
|
||||
}
|
||||
loadedItems = true;
|
||||
@@ -178,7 +189,7 @@ private:
|
||||
return std::make_tuple(loadedItems, items);
|
||||
}
|
||||
|
||||
void WriteIndexFile(const DirectoryStats &stats, const std::vector<TItem> &items)
|
||||
void WriteIndexFile(const DirectoryStats &stats, const std::vector<TItem> &items) const
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -187,23 +198,37 @@ private:
|
||||
// Write header
|
||||
FileIndexHeader header = { 0 };
|
||||
header.MagicNumber = _magicNumber;
|
||||
header.Version = SCENARIO_REPOSITORY_VERSION;
|
||||
header.VersionA = FILE_INDEX_VERSION;
|
||||
header.VersionB = _version;
|
||||
header.LanguageId = gCurrentLanguage;
|
||||
header.DirectoryStats = stats;
|
||||
header.NumItems = items.size();
|
||||
header.Stats = stats;
|
||||
header.NumItems = (uint32)items.size();
|
||||
fs.WriteValue(header);
|
||||
|
||||
// Write items
|
||||
for (const auto item : items)
|
||||
{
|
||||
Serialise(fs, item);
|
||||
Serialise(&fs, item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to save index.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32 GetPathChecksum(const std::string &path)
|
||||
{
|
||||
uint32 hash = 0xD8430DED;
|
||||
for (const utf8 * ch = path.c_str(); *ch != '\0'; ch++)
|
||||
{
|
||||
hash += (*ch);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <vector>
|
||||
#include "../core/Console.hpp"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileIndex.hpp"
|
||||
#include "../core/FileScanner.h"
|
||||
#include "../core/FileStream.hpp"
|
||||
#include "../core/Math.hpp"
|
||||
@@ -136,6 +137,165 @@ static void scenario_highscore_free(scenario_highscore_entry * highscore)
|
||||
SafeDelete(highscore);
|
||||
}
|
||||
|
||||
class ScenarioFileIndex final : public FileIndex<scenario_index_entry>
|
||||
{
|
||||
private:
|
||||
static constexpr uint32 MAGIC_NUMBER = 0x58444953;
|
||||
static constexpr uint16 VERSION = 1;
|
||||
static constexpr auto PATTERN = "*.sc4;*.sc6";
|
||||
|
||||
public:
|
||||
ScenarioFileIndex(IPlatformEnvironment * env) :
|
||||
FileIndex(MAGIC_NUMBER,
|
||||
VERSION,
|
||||
env->GetFilePath(PATHID::CACHE_SCENARIOS),
|
||||
std::string(PATTERN),
|
||||
std::vector<std::string>({
|
||||
env->GetDirectoryPath(DIRBASE::RCT1, DIRID::SCENARIO),
|
||||
env->GetDirectoryPath(DIRBASE::RCT2, DIRID::SCENARIO),
|
||||
env->GetDirectoryPath(DIRBASE::USER, DIRID::SCENARIO) }))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
scenario_index_entry Create(const std::string &path) const override
|
||||
{
|
||||
scenario_index_entry entry;
|
||||
auto timestamp = File::GetLastModified(path);
|
||||
if (!GetScenarioInfo(path, timestamp, &entry))
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void Serialise(IStream * stream, const scenario_index_entry &item) const override
|
||||
{
|
||||
// HACK: Zero highscore pointer
|
||||
auto copy = item;
|
||||
copy.highscore = nullptr;
|
||||
stream->WriteValue(copy);
|
||||
}
|
||||
|
||||
scenario_index_entry Deserialise(IStream * stream) const override
|
||||
{
|
||||
auto result = stream->ReadValue<scenario_index_entry>();
|
||||
// HACK: Zero highscore pointer
|
||||
result.highscore = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Reads basic information from a scenario file.
|
||||
*/
|
||||
static bool GetScenarioInfo(const std::string &path, uint64 timestamp, scenario_index_entry * entry)
|
||||
{
|
||||
log_verbose("GetScenarioInfo(%s, %d, ...)", path.c_str(), timestamp);
|
||||
try
|
||||
{
|
||||
std::string extension = Path::GetExtension(path);
|
||||
if (String::Equals(extension, ".sc4", true))
|
||||
{
|
||||
// RCT1 scenario
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
auto s4Importer = std::unique_ptr<IParkImporter>(ParkImporter::CreateS4());
|
||||
s4Importer->LoadScenario(path.c_str(), true);
|
||||
if (s4Importer->GetDetails(entry))
|
||||
{
|
||||
String::Set(entry->path, sizeof(entry->path), path.c_str());
|
||||
entry->timestamp = timestamp;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RCT2 scenario
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
auto chunkReader = SawyerChunkReader(&fs);
|
||||
|
||||
rct_s6_header header = chunkReader.ReadChunkAs<rct_s6_header>();
|
||||
if (header.type == S6_TYPE_SCENARIO)
|
||||
{
|
||||
rct_s6_info info = chunkReader.ReadChunkAs<rct_s6_info>();
|
||||
*entry = CreateNewScenarioEntry(path, timestamp, &info);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("%s is not a scenario", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to read scenario: '%s'", path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static scenario_index_entry CreateNewScenarioEntry(const std::string &path, uint64 timestamp, rct_s6_info * s6Info)
|
||||
{
|
||||
scenario_index_entry entry = { 0 };
|
||||
|
||||
// Set new entry
|
||||
String::Set(entry.path, sizeof(entry.path), path.c_str());
|
||||
entry.timestamp = timestamp;
|
||||
entry.category = s6Info->category;
|
||||
entry.objective_type = s6Info->objective_type;
|
||||
entry.objective_arg_1 = s6Info->objective_arg_1;
|
||||
entry.objective_arg_2 = s6Info->objective_arg_2;
|
||||
entry.objective_arg_3 = s6Info->objective_arg_3;
|
||||
entry.highscore = nullptr;
|
||||
if (String::IsNullOrEmpty(s6Info->name))
|
||||
{
|
||||
// If the scenario doesn't have a name, set it to the filename
|
||||
String::Set(entry.name, sizeof(entry.name), Path::GetFileNameWithoutExtension(entry.path));
|
||||
}
|
||||
else
|
||||
{
|
||||
String::Set(entry.name, sizeof(entry.name), s6Info->name);
|
||||
// Normalise the name to make the scenario as recognisable as possible.
|
||||
ScenarioSources::NormaliseName(entry.name, sizeof(entry.name), entry.name);
|
||||
}
|
||||
|
||||
String::Set(entry.details, sizeof(entry.details), s6Info->details);
|
||||
|
||||
// Look up and store information regarding the origins of this scenario.
|
||||
source_desc desc;
|
||||
if (ScenarioSources::TryGetByName(entry.name, &desc))
|
||||
{
|
||||
entry.sc_id = desc.id;
|
||||
entry.source_index = desc.index;
|
||||
entry.source_game = desc.source;
|
||||
entry.category = desc.category;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.sc_id = SC_UNIDENTIFIED;
|
||||
entry.source_index = -1;
|
||||
if (entry.category == SCENARIO_CATEGORY_REAL)
|
||||
{
|
||||
entry.source_game = SCENARIO_SOURCE_REAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.source_game = SCENARIO_SOURCE_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
scenario_translate(&entry, &s6Info->entry);
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
|
||||
class ScenarioRepository final : public IScenarioRepository
|
||||
{
|
||||
private:
|
||||
@@ -143,12 +303,13 @@ private:
|
||||
static constexpr uint32 HighscoreFileVersion = 1;
|
||||
|
||||
IPlatformEnvironment * _env;
|
||||
ScenarioFileIndex const _fileIndex;
|
||||
std::vector<scenario_index_entry> _scenarios;
|
||||
std::vector<scenario_highscore_entry*> _highscores;
|
||||
QueryDirectoryResult _directoryQueryResult = { 0 };
|
||||
|
||||
public:
|
||||
ScenarioRepository(IPlatformEnvironment * env)
|
||||
: _fileIndex(env)
|
||||
{
|
||||
_env = env;
|
||||
}
|
||||
@@ -162,29 +323,16 @@ public:
|
||||
{
|
||||
_scenarios.clear();
|
||||
|
||||
// Scan RCT2 directory
|
||||
std::string rct1dir = _env->GetDirectoryPath(DIRBASE::RCT1, DIRID::SCENARIO);
|
||||
std::string rct2dir = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::SCENARIO);
|
||||
std::string openrct2dir = _env->GetDirectoryPath(DIRBASE::USER, DIRID::SCENARIO);
|
||||
std::string mpdatdir = _env->GetFilePath(PATHID::MP_DAT);
|
||||
|
||||
_directoryQueryResult = { 0 };
|
||||
Query(rct1dir);
|
||||
Query(rct2dir);
|
||||
Query(openrct2dir);
|
||||
|
||||
if (!Load())
|
||||
auto scenarios = _fileIndex.LoadOrBuild();
|
||||
for (auto scenario : scenarios)
|
||||
{
|
||||
Scan(rct1dir);
|
||||
Scan(rct2dir);
|
||||
Scan(openrct2dir);
|
||||
Save();
|
||||
AddScenario(scenario);
|
||||
}
|
||||
|
||||
ConvertMegaPark(mpdatdir, openrct2dir);
|
||||
|
||||
// std::string mpdatdir = _env->GetFilePath(PATHID::MP_DAT);
|
||||
// ConvertMegaPark(mpdatdir, openrct2dir);
|
||||
|
||||
Sort();
|
||||
|
||||
LoadScores();
|
||||
LoadLegacyScores();
|
||||
AttachHighscores();
|
||||
@@ -286,28 +434,6 @@ private:
|
||||
return (scenario_index_entry *)repo->GetByPath(path);
|
||||
}
|
||||
|
||||
void Query(const std::string &directory)
|
||||
{
|
||||
std::string pattern = Path::Combine(directory, SC_FILE_PATTERN);
|
||||
Path::QueryDirectory(&_directoryQueryResult, pattern);
|
||||
}
|
||||
|
||||
void Scan(const std::string &directory)
|
||||
{
|
||||
utf8 pattern[MAX_PATH];
|
||||
String::Set(pattern, sizeof(pattern), directory.c_str());
|
||||
Path::Append(pattern, sizeof(pattern), SC_FILE_PATTERN);
|
||||
|
||||
IFileScanner * scanner = Path::ScanDirectory(pattern, true);
|
||||
while (scanner->Next())
|
||||
{
|
||||
auto path = scanner->GetPath();
|
||||
auto fileInfo = scanner->GetFileInfo();
|
||||
AddScenario(path, fileInfo->LastModified);
|
||||
}
|
||||
delete scanner;
|
||||
}
|
||||
|
||||
void ConvertMegaPark(std::string &mpdatDir, std::string &scenarioDir)
|
||||
{
|
||||
//Convert mp.dat from RCT1 Data directory into SC21.SC4 (Mega Park)
|
||||
@@ -340,20 +466,14 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void AddScenario(const std::string &path, uint64 timestamp)
|
||||
void AddScenario(const scenario_index_entry &entry)
|
||||
{
|
||||
scenario_index_entry entry;
|
||||
if (!GetScenarioInfo(path, timestamp, &entry))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string filename = Path::GetFileName(path);
|
||||
scenario_index_entry * existingEntry = GetByFilename(filename.c_str());
|
||||
auto filename = Path::GetFileName(entry.path);
|
||||
auto existingEntry = GetByFilename(filename);
|
||||
if (existingEntry != nullptr)
|
||||
{
|
||||
std::string conflictPath;
|
||||
if (existingEntry->timestamp > timestamp)
|
||||
if (existingEntry->timestamp > entry.timestamp)
|
||||
{
|
||||
// Existing entry is more recent
|
||||
conflictPath = String::ToStd(existingEntry->path);
|
||||
@@ -364,7 +484,7 @@ private:
|
||||
else
|
||||
{
|
||||
// This entry is more recent
|
||||
conflictPath = path;
|
||||
conflictPath = entry.path;
|
||||
}
|
||||
Console::WriteLine("Scenario conflict: '%s' ignored because it is newer.", conflictPath.c_str());
|
||||
}
|
||||
@@ -374,115 +494,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads basic information from a scenario file.
|
||||
*/
|
||||
bool GetScenarioInfo(const std::string &path, uint64 timestamp, scenario_index_entry * entry)
|
||||
{
|
||||
log_verbose("GetScenarioInfo(%s, %d, ...)", path.c_str(), timestamp);
|
||||
try
|
||||
{
|
||||
std::string extension = Path::GetExtension(path);
|
||||
if (String::Equals(extension, ".sc4", true))
|
||||
{
|
||||
// RCT1 scenario
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
auto s4Importer = std::unique_ptr<IParkImporter>(ParkImporter::CreateS4());
|
||||
s4Importer->LoadScenario(path.c_str(), true);
|
||||
if (s4Importer->GetDetails(entry))
|
||||
{
|
||||
String::Set(entry->path, sizeof(entry->path), path.c_str());
|
||||
entry->timestamp = timestamp;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RCT2 scenario
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
auto chunkReader = SawyerChunkReader(&fs);
|
||||
|
||||
rct_s6_header header = chunkReader.ReadChunkAs<rct_s6_header>();
|
||||
if (header.type == S6_TYPE_SCENARIO)
|
||||
{
|
||||
rct_s6_info info = chunkReader.ReadChunkAs<rct_s6_info>();
|
||||
*entry = CreateNewScenarioEntry(path, timestamp, &info);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("%s is not a scenario", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to read scenario: '%s'", path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
scenario_index_entry CreateNewScenarioEntry(const std::string &path, uint64 timestamp, rct_s6_info * s6Info)
|
||||
{
|
||||
scenario_index_entry entry = { 0 };
|
||||
|
||||
// Set new entry
|
||||
String::Set(entry.path, sizeof(entry.path), path.c_str());
|
||||
entry.timestamp = timestamp;
|
||||
entry.category = s6Info->category;
|
||||
entry.objective_type = s6Info->objective_type;
|
||||
entry.objective_arg_1 = s6Info->objective_arg_1;
|
||||
entry.objective_arg_2 = s6Info->objective_arg_2;
|
||||
entry.objective_arg_3 = s6Info->objective_arg_3;
|
||||
entry.highscore = nullptr;
|
||||
if (String::IsNullOrEmpty(s6Info->name))
|
||||
{
|
||||
// If the scenario doesn't have a name, set it to the filename
|
||||
String::Set(entry.name, sizeof(entry.name), Path::GetFileNameWithoutExtension(entry.path));
|
||||
}
|
||||
else
|
||||
{
|
||||
String::Set(entry.name, sizeof(entry.name), s6Info->name);
|
||||
// Normalise the name to make the scenario as recognisable as possible.
|
||||
ScenarioSources::NormaliseName(entry.name, sizeof(entry.name), entry.name);
|
||||
}
|
||||
|
||||
String::Set(entry.details, sizeof(entry.details), s6Info->details);
|
||||
|
||||
// Look up and store information regarding the origins of this scenario.
|
||||
source_desc desc;
|
||||
if (ScenarioSources::TryGetByName(entry.name, &desc))
|
||||
{
|
||||
entry.sc_id = desc.id;
|
||||
entry.source_index = desc.index;
|
||||
entry.source_game = desc.source;
|
||||
entry.category = desc.category;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.sc_id = SC_UNIDENTIFIED;
|
||||
entry.source_index = -1;
|
||||
if (entry.category == SCENARIO_CATEGORY_REAL)
|
||||
{
|
||||
entry.source_game = SCENARIO_SOURCE_REAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.source_game = SCENARIO_SOURCE_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
scenario_translate(&entry, &s6Info->entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void Sort()
|
||||
{
|
||||
if (gConfigGeneral.scenario_select_mode == SCENARIO_SELECT_MODE_ORIGIN)
|
||||
@@ -503,69 +514,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool Load()
|
||||
{
|
||||
std::string path = _env->GetFilePath(PATHID::CACHE_SCENARIOS);
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
|
||||
// Read header, check if we need to re-scan
|
||||
auto header = fs.ReadValue<ScenarioRepositoryHeader>();
|
||||
if (header.MagicNumber == SCENARIO_REPOSITORY_MAGIC_NUMBER &&
|
||||
header.Version == SCENARIO_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++)
|
||||
{
|
||||
auto scenario = fs.ReadValue<scenario_index_entry>();
|
||||
_scenarios.push_back(scenario);
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to load scenario repository index.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Save() const
|
||||
{
|
||||
std::string path = _env->GetFilePath(PATHID::CACHE_SCENARIOS);
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_WRITE);
|
||||
|
||||
// Write header
|
||||
ScenarioRepositoryHeader header = { 0 };
|
||||
header.MagicNumber = SCENARIO_REPOSITORY_MAGIC_NUMBER;
|
||||
header.Version = SCENARIO_REPOSITORY_VERSION;
|
||||
header.TotalFiles = _directoryQueryResult.TotalFiles;
|
||||
header.TotalFileSize = _directoryQueryResult.TotalFileSize;
|
||||
header.FileDateModifiedChecksum = _directoryQueryResult.FileDateModifiedChecksum;
|
||||
header.PathChecksum = _directoryQueryResult.PathChecksum;
|
||||
header.NumItems = (uint32)_scenarios.size();
|
||||
fs.WriteValue(header);
|
||||
|
||||
// Write items
|
||||
for (const auto scenario : _scenarios)
|
||||
{
|
||||
fs.WriteValue(scenario);
|
||||
}
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Unable to write scenario repository index.");
|
||||
}
|
||||
}
|
||||
|
||||
void LoadScores()
|
||||
{
|
||||
std::string path = _env->GetFilePath(PATHID::SCORES);
|
||||
|
||||
Reference in New Issue
Block a user