diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 87f7d53f9c..aa51bb272c 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -117,7 +117,7 @@ - + @@ -418,7 +418,7 @@ - + diff --git a/src/object/ObjectCache.cpp b/src/object/ObjectCache.cpp deleted file mode 100644 index 158c4cd5d7..0000000000 --- a/src/object/ObjectCache.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers -/***************************************************************************** - * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. - * - * OpenRCT2 is the work of many authors, a full list can be found in contributors.md - * For more information, visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * A full copy of the GNU General Public License can be found in licence.txt - *****************************************************************************/ -#pragma endregion - -#include "../common.h" -#include "../core/Guard.hpp" -#include "../core/IStream.hpp" -#include "../core/Memory.hpp" -#include "../core/String.hpp" -#include "ObjectCache.h" - -extern "C" -{ - #include "../object.h" -} - -struct ObjectCacheEntry -{ - rct_object_entry_extended ObjectEntry; - utf8 * Path; - uint32 NumImages; - utf8 * Name; - size_t ChunkSize; - uint16 NumRequiredObjects; - rct_object_entry_extended * RequiredObjects; - union - { - struct - { - uint16 NumThemeObjects; - rct_object_entry_extended * ThemeObjects; - }; - struct - { - uint8 RideFlags; - uint8 RideCategory[2]; - uint8 RideType[3]; - }; - }; - - void Dispose() - { - Memory::Free(RequiredObjects); - Memory::Free(ThemeObjects); - RequiredObjects = nullptr; - ThemeObjects = nullptr; - } - - void Read(IStream * stream) - { - Guard::Assert(RequiredObjects == nullptr); - Guard::Assert(ThemeObjects == nullptr); - - ObjectEntry = stream->ReadValue(); - Path = stream->ReadString(); - NumImages = stream->ReadValue(); - Name = stream->ReadString(); - ChunkSize = stream->ReadValue(); - NumRequiredObjects = stream->ReadValue(); - - RequiredObjects = Memory::AllocateArray(NumRequiredObjects); - for (uint16 i = 0; i < NumRequiredObjects; i++) - { - RequiredObjects[i] = stream->ReadValue(); - } - - switch (ObjectEntry.flags & 0x0F) { - case OBJECT_TYPE_RIDE: - RideFlags = stream->ReadValue(); - for (int i = 0; i < 2; i++) - { - RideCategory[i] = stream->ReadValue(); - } - for (int i = 0; i < 3; i++) - { - RideType[i] = stream->ReadValue(); - } - break; - case OBJECT_TYPE_SCENERY_SETS: - NumThemeObjects = stream->ReadValue(); - for (uint16 i = 0; i < NumThemeObjects; i++) - { - ThemeObjects[i] = stream->ReadValue(); - } - break; - } - } - - void Write(IStream * stream) - { - stream->WriteValue(ObjectEntry); - stream->WriteString(Path); - stream->WriteValue(NumImages); - stream->WriteString(Name); - stream->WriteValue(ChunkSize); - - stream->WriteValue(NumRequiredObjects); - for (uint16 i = 0; i < NumRequiredObjects; i++) - { - stream->WriteValue(RequiredObjects[i]); - } - - switch (ObjectEntry.flags & 0x0F) { - case OBJECT_TYPE_RIDE: - stream->WriteValue(RideFlags); - for (int i = 0; i < 2; i++) - { - stream->WriteValue(RideCategory[i]); - } - for (int i = 0; i < 3; i++) - { - stream->WriteValue(RideType[i]); - } - break; - case OBJECT_TYPE_SCENERY_SETS: - stream->WriteValue(NumThemeObjects); - for (uint16 i = 0; i < NumThemeObjects; i++) - { - stream->WriteValue(ThemeObjects[i]); - } - break; - } - } -}; - -class ObjectCache : IObjectCache -{ - void Load() override - { - // TODO - } - - void Save() override - { - // TODO - } -}; diff --git a/src/object/ObjectCache.h b/src/object/ObjectCache.h deleted file mode 100644 index cb7b4ba5b3..0000000000 --- a/src/object/ObjectCache.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers -/***************************************************************************** - * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. - * - * OpenRCT2 is the work of many authors, a full list can be found in contributors.md - * For more information, visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * A full copy of the GNU General Public License can be found in licence.txt - *****************************************************************************/ -#pragma endregion - -#pragma once - -#include "../common.h" - -interface IObjectCache -{ - virtual ~IObjectCache(); - - virtual void Load() abstract; - virtual void Save() abstract; -}; diff --git a/src/object/ObjectRepository.cpp b/src/object/ObjectRepository.cpp new file mode 100644 index 0000000000..70c1382534 --- /dev/null +++ b/src/object/ObjectRepository.cpp @@ -0,0 +1,275 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include + +#include "../common.h" +#include "../core/FileStream.hpp" +#include "../core/Guard.hpp" +#include "../core/IStream.hpp" +#include "../core/Memory.hpp" +#include "../core/String.hpp" +#include "ObjectRepository.h" + +extern "C" +{ + #include "../object.h" + #include "../platform/platform.h" +} + +constexpr uint16 OBJECT_REPOSITORY_VERSION = 6; + +struct ObjectRepositoryHeader +{ + uint16 Version; + uint32 TotalFiles; + uint64 TotalFileSize; + uint32 FileDateModifiedChecksum; + uint32 NumItems; +}; + +struct QueryDirectoryResult +{ + uint32 TotalFiles; + uint64 TotalFileSize; + uint32 FileDateModifiedChecksum; +}; + +class ObjectRepository : public IObjectRepository +{ + std::vector _items; + QueryDirectoryResult _queryDirectoryResult; + +public: + void LoadOrConstruct() + { + _queryDirectoryResult = QueryDirectory(); + if (!Load()) + { + Construct(); + Save(); + } + } + + const ObjectRepositoryItem * FindObject(const rct_object_entry * objectEntry) override + { + for (uint32 i = 0; i < _items.size(); i++) + { + ObjectRepositoryItem * item = &_items[i]; + rct_object_entry * itemEntry = (rct_object_entry*)&item->ObjectEntry; + if (object_entry_compare(itemEntry, objectEntry)) + { + return item; + } + } + return nullptr; + } + +private: + void Construct() + { + // TODO + } + + bool Load() + { + utf8 path[MAX_PATH]; + GetRepositoryPath(path, sizeof(path)); + + try + { + auto fs = FileStream(path, FILE_MODE_OPEN); + auto header = fs.ReadValue(); + + if (header.Version == OBJECT_REPOSITORY_VERSION && + header.TotalFiles == _queryDirectoryResult.TotalFiles && + header.TotalFileSize == _queryDirectoryResult.TotalFileSize && + header.FileDateModifiedChecksum == _queryDirectoryResult.FileDateModifiedChecksum) + { + // Header matches, so the index is not out of date + for (uint32 i = 0; i < header.NumItems; i++) + { + ObjectRepositoryItem item = ReadItem(&fs); + _items.push_back(item); + } + return true; + } + log_info("Object repository is out of date."); + return false; + } + catch (IOException ex) + { + return false; + } + } + + void Save() const + { + utf8 path[MAX_PATH]; + GetRepositoryPath(path, sizeof(path)); + + try + { + auto fs = FileStream(path, FILE_MODE_WRITE); + + // Write header + ObjectRepositoryHeader header; + header.Version = OBJECT_REPOSITORY_VERSION; + header.TotalFiles = _queryDirectoryResult.TotalFiles; + header.TotalFileSize = _queryDirectoryResult.TotalFileSize; + header.FileDateModifiedChecksum = _queryDirectoryResult.FileDateModifiedChecksum; + header.NumItems = _items.size(); + fs.WriteValue(header); + + // Write items + for (uint32 i = 0; i < header.NumItems; i++) + { + fs.WriteValue(_items[i]); + } + } + catch (IOException ex) + { + log_error("Unable to write object repository index."); + } + } + + static ObjectRepositoryItem ReadItem(IStream * stream) + { + ObjectRepositoryItem item = { 0 }; + + item.ObjectEntry = stream->ReadValue(); + item.Path = stream->ReadString(); + item.NumImages = stream->ReadValue(); + item.Name = stream->ReadString(); + item.ChunkSize = stream->ReadValue(); + item.NumRequiredObjects = stream->ReadValue(); + + item.RequiredObjects = Memory::AllocateArray(item.NumRequiredObjects); + for (uint16 i = 0; i < item.NumRequiredObjects; i++) + { + item.RequiredObjects[i] = stream->ReadValue(); + } + + switch (item.ObjectEntry.flags & 0x0F) { + case OBJECT_TYPE_RIDE: + item.RideFlags = stream->ReadValue(); + for (int i = 0; i < 2; i++) + { + item.RideCategory[i] = stream->ReadValue(); + } + for (int i = 0; i < 3; i++) + { + item.RideType[i] = stream->ReadValue(); + } + break; + case OBJECT_TYPE_SCENERY_SETS: + item.NumThemeObjects = stream->ReadValue(); + item.ThemeObjects = Memory::AllocateArray(item.NumThemeObjects); + for (uint16 i = 0; i < item.NumThemeObjects; i++) + { + item.ThemeObjects[i] = stream->ReadValue(); + } + break; + } + return item; + } + + static void WriteItem(IStream * stream, const ObjectRepositoryItem &item) + { + stream->WriteValue(item.ObjectEntry); + stream->WriteString(item.Path); + stream->WriteValue(item.NumImages); + stream->WriteString(item.Name); + stream->WriteValue(item.ChunkSize); + + stream->WriteValue(item.NumRequiredObjects); + for (uint16 i = 0; i < item.NumRequiredObjects; i++) + { + stream->WriteValue(item.RequiredObjects[i]); + } + + switch (item.ObjectEntry.flags & 0x0F) { + case OBJECT_TYPE_RIDE: + stream->WriteValue(item.RideFlags); + for (int i = 0; i < 2; i++) + { + stream->WriteValue(item.RideCategory[i]); + } + for (int i = 0; i < 3; i++) + { + stream->WriteValue(item.RideType[i]); + } + break; + case OBJECT_TYPE_SCENERY_SETS: + stream->WriteValue(item.NumThemeObjects); + for (uint16 i = 0; i < item.NumThemeObjects; i++) + { + stream->WriteValue(item.ThemeObjects[i]); + } + break; + } + } + + static void FreeItem(ObjectRepositoryItem * item) + { + Memory::Free(item->RequiredObjects); + Memory::Free(item->ThemeObjects); + item->RequiredObjects = nullptr; + item->ThemeObjects = nullptr; + } + + static void GetRepositoryPath(utf8 * buffer, size_t bufferSize) + { + platform_get_user_directory(buffer, nullptr); + strcat(buffer, "objects.idx"); + } + + static QueryDirectoryResult QueryDirectory() + { + QueryDirectoryResult result = { 0 }; + + // Enumerate through each object in the directory + int enumFileHandle = platform_enumerate_files_begin(gRCT2AddressObjectDataPath); + if (enumFileHandle != INVALID_HANDLE) + { + file_info enumFileInfo; + while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) + { + result.TotalFiles++; + result.TotalFileSize += enumFileInfo.size; + result.FileDateModifiedChecksum ^= + (uint32)(enumFileInfo.last_modified >> 32) ^ + (uint32)(enumFileInfo.last_modified & 0xFFFFFFFF); + result.FileDateModifiedChecksum = ror32(result.FileDateModifiedChecksum, 5); + } + platform_enumerate_files_end(enumFileHandle); + } + + return result; + } +}; + +static ObjectRepository * _objectRepository = nullptr; + +IObjectRepository * GetObjectRepository() +{ + if (_objectRepository == nullptr) + { + _objectRepository = new ObjectRepository(); + _objectRepository->LoadOrConstruct(); + } + return _objectRepository; +} diff --git a/src/object/ObjectRepository.h b/src/object/ObjectRepository.h new file mode 100644 index 0000000000..c686a49de7 --- /dev/null +++ b/src/object/ObjectRepository.h @@ -0,0 +1,58 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "../common.h" + +extern "C" +{ + #include "../object.h" +} + +struct ObjectRepositoryItem +{ + rct_object_entry_extended ObjectEntry; + utf8 * Path; + uint32 NumImages; + utf8 * Name; + size_t ChunkSize; + uint16 NumRequiredObjects; + rct_object_entry_extended * RequiredObjects; + union + { + struct + { + uint16 NumThemeObjects; + rct_object_entry_extended * ThemeObjects; + }; + struct + { + uint8 RideFlags; + uint8 RideCategory[2]; + uint8 RideType[3]; + }; + }; +}; + +interface IObjectRepository +{ + virtual ~IObjectRepository() { } + + virtual const ObjectRepositoryItem * FindObject(const rct_object_entry * objectEntry) abstract; +}; + +IObjectRepository * GetObjectRepository();