1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-16 03:23:15 +01:00

Fix all errors

This commit is contained in:
Ted John
2017-08-29 23:25:52 +01:00
parent 4f3669f279
commit e417d2f8b0
4 changed files with 267 additions and 260 deletions

View File

@@ -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"

View File

@@ -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);
}

View File

@@ -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;
}
};

View File

@@ -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);