diff --git a/openrct2.vcxproj b/openrct2.vcxproj index f0f2277fe1..fe0d037877 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -42,6 +42,7 @@ + @@ -355,6 +356,7 @@ + diff --git a/src/core/FileEnumerator.cpp b/src/core/FileEnumerator.cpp new file mode 100644 index 0000000000..a2fbc4b029 --- /dev/null +++ b/src/core/FileEnumerator.cpp @@ -0,0 +1,154 @@ +#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 "FileEnumerator.h" +#include "Memory.hpp" +#include "Path.hpp" +#include "String.hpp" + +extern "C" +{ + #include "../platform/platform.h" +} + +FileEnumerator::FileEnumerator(const utf8 * pattern, bool recurse) +{ + _rootPath = Memory::Allocate(MAX_PATH); + Path::GetDirectory(_rootPath, MAX_PATH, pattern); + _pattern = String::Duplicate(Path::GetFileName(pattern)); + _recurse = recurse; + + _fileInfo = Memory::Allocate(); + Memory::Set(_fileInfo, 0, sizeof(file_info)); + _path = Memory::Allocate(MAX_PATH); + + _fileHandle = INVALID_HANDLE; + + Reset(); +} + +FileEnumerator::~FileEnumerator() +{ + CloseHandles(); + Memory::Free(_path); + Memory::Free(_fileInfo); + Memory::Free(_pattern); + Memory::Free(_rootPath); +} + +void FileEnumerator::Reset() +{ + CloseHandles(); + + DirectoryState directoryState; + directoryState.Directory = String::Duplicate(_rootPath); + directoryState.Handle = INVALID_HANDLE; + _directoryStack.push(directoryState); +} + +bool FileEnumerator::Next() +{ + while (true) + { + while (_fileHandle == INVALID_HANDLE) + { + if (_directoryStack.size() == 0) + { + return false; + } + + DirectoryState directoryState = _directoryStack.top(); + if (directoryState.Handle == INVALID_HANDLE) + { + // Start enumerating files for this directory + utf8 pattern[MAX_PATH]; + String::Set(pattern, sizeof(pattern), directoryState.Directory); + Path::Append(pattern, sizeof(pattern), _pattern); + + _fileHandle = platform_enumerate_files_begin(pattern); + } + else + { + // Next directory + utf8 name[MAX_PATH]; + if (platform_enumerate_directories_next(directoryState.Handle, name)) + { + DirectoryState newDirectoryState; + newDirectoryState.Handle = INVALID_HANDLE; + newDirectoryState.Directory = Memory::Allocate(MAX_PATH); + + String::Set(newDirectoryState.Directory, MAX_PATH, directoryState.Directory); + Path::Append(newDirectoryState.Directory, MAX_PATH, name); + + _directoryStack.push(newDirectoryState); + } + else + { + platform_enumerate_directories_end(directoryState.Handle); + Memory::Free(directoryState.Directory); + _directoryStack.pop(); + } + } + } + + // Next file + if (platform_enumerate_files_next(_fileHandle, _fileInfo)) + { + String::Set(_path, MAX_PATH, _directoryStack.top().Directory); + Path::Append(_path, MAX_PATH, _fileInfo->path); + return true; + } + platform_enumerate_files_end(_fileHandle); + _fileHandle = INVALID_HANDLE; + + if (_recurse) + { + // Start enumerating sub-directories + DirectoryState * directoryState = &_directoryStack.top(); + directoryState->Handle = platform_enumerate_directories_begin(directoryState->Directory); + if (directoryState->Handle == INVALID_HANDLE) + { + Memory::Free(directoryState->Directory); + _directoryStack.pop(); + } + } + else + { + Memory::Free(_directoryStack.top().Directory); + _directoryStack.pop(); + return false; + } + } +} + +void FileEnumerator::CloseHandles() +{ + if (_fileHandle != INVALID_HANDLE) + { + platform_enumerate_files_end(_fileHandle); + _fileHandle = INVALID_HANDLE; + } + while (_directoryStack.size() > 0) + { + DirectoryState directoryState = _directoryStack.top(); + if (directoryState.Handle != INVALID_HANDLE) + { + platform_enumerate_directories_end(directoryState.Handle); + } + Memory::Free(directoryState.Directory); + _directoryStack.pop(); + } +} diff --git a/src/core/FileEnumerator.h b/src/core/FileEnumerator.h new file mode 100644 index 0000000000..d39b237c14 --- /dev/null +++ b/src/core/FileEnumerator.h @@ -0,0 +1,57 @@ +#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 +#include "../common.h" + +struct file_info; +class FileEnumerator +{ +private: + struct DirectoryState + { + utf8 * Directory; + int Handle; + }; + + // Enumeration options + utf8 * _rootPath; + utf8 * _pattern; + bool _recurse; + + // Enumeration state + int _fileHandle; + std::stack _directoryStack; + + // Current enumeration + file_info * _fileInfo; + utf8 * _path; + +public: + FileEnumerator(const utf8 * pattern, bool recurse); + ~FileEnumerator(); + + const file_info * GetFileInfo() const { return _fileInfo; } + const utf8 * GetPath() const { return _path; } + + void Reset(); + bool Next(); + +private: + void CloseHandles(); +}; diff --git a/src/object/ObjectRepository.cpp b/src/object/ObjectRepository.cpp index dda8b85fd9..be084925f9 100644 --- a/src/object/ObjectRepository.cpp +++ b/src/object/ObjectRepository.cpp @@ -19,6 +19,7 @@ #include "../common.h" #include "../core/Console.hpp" +#include "../core/FileEnumerator.h" #include "../core/FileStream.hpp" #include "../core/Guard.hpp" #include "../core/IStream.hpp" @@ -196,48 +197,23 @@ private: void QueryDirectory(QueryDirectoryResult * result, const utf8 * directory) { - // Enumerate through each object in the directory - utf8 * pattern = Memory::Allocate(MAX_PATH); - String::Set(pattern, MAX_PATH, directory); - Path::Append(pattern, MAX_PATH, "*.dat"); + utf8 pattern[MAX_PATH]; + String::Set(pattern, sizeof(pattern), directory); + Path::Append(pattern, sizeof(pattern), "*.dat"); - // Query files - int enumFileHandle = platform_enumerate_files_begin(pattern); - if (enumFileHandle != INVALID_HANDLE) + auto fileEnumerator = FileEnumerator(pattern, true); + while (fileEnumerator.Next()) { - 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); - result->PathChecksum += GetPathChecksum(directory, enumFileInfo.path); - } - platform_enumerate_files_end(enumFileHandle); - } + const file_info * enumFileInfo = fileEnumerator.GetFileInfo(); + const utf8 * enumPath = fileEnumerator.GetPath(); - Memory::Free(pattern); - - // Query sub-directories - int enumDirectoryHandle = platform_enumerate_directories_begin(directory); - if (enumDirectoryHandle != INVALID_HANDLE) - { - utf8 * directoryAbs = Memory::Allocate(MAX_PATH); - utf8 * directoryRel = Memory::Allocate(MAX_PATH); - - while (platform_enumerate_directories_next(enumDirectoryHandle, directoryRel)) - { - String::Set(directoryAbs, MAX_PATH, directory); - Path::Append(directoryAbs, MAX_PATH, directoryRel); - QueryDirectory(result, directoryAbs); - } - platform_enumerate_directories_end(enumDirectoryHandle); - - Memory::Free(directoryRel); - Memory::Free(directoryAbs); + result->TotalFiles++; + result->TotalFileSize += enumFileInfo->size; + result->FileDateModifiedChecksum ^= + (uint32)(enumFileInfo->last_modified >> 32) ^ + (uint32)(enumFileInfo->last_modified & 0xFFFFFFFF); + result->FileDateModifiedChecksum = ror32(result->FileDateModifiedChecksum, 5); + result->PathChecksum += GetPathChecksum(enumPath); } } @@ -263,48 +239,15 @@ private: void ScanDirectory(const utf8 * directory) { - // Enumerate through each object in the directory - utf8 * pattern = Memory::Allocate(MAX_PATH); - String::Set(pattern, MAX_PATH, directory); - Path::Append(pattern, MAX_PATH, "*.dat"); + utf8 pattern[MAX_PATH]; + String::Set(pattern, sizeof(pattern), directory); + Path::Append(pattern, sizeof(pattern), "*.dat"); - // Query files - int enumFileHandle = platform_enumerate_files_begin(pattern); - if (enumFileHandle != INVALID_HANDLE) + auto fileEnumerator = FileEnumerator(pattern, true); + while (fileEnumerator.Next()) { - utf8 * pathAbs = Memory::Allocate(MAX_PATH); - - file_info enumFileInfo; - while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) - { - String::Set(pathAbs, MAX_PATH, directory); - Path::Append(pathAbs, MAX_PATH, enumFileInfo.path); - ScanObject(pathAbs); - } - platform_enumerate_files_end(enumFileHandle); - - Memory::Free(pathAbs); - } - - Memory::Free(pattern); - - // Query sub-directories - int enumDirectoryHandle = platform_enumerate_directories_begin(directory); - if (enumDirectoryHandle != INVALID_HANDLE) - { - utf8 * directoryAbs = Memory::Allocate(MAX_PATH); - utf8 * directoryRel = Memory::Allocate(MAX_PATH); - - while (platform_enumerate_directories_next(enumDirectoryHandle, directoryRel)) - { - String::Set(directoryAbs, MAX_PATH, directory); - Path::Append(directoryAbs, MAX_PATH, directoryRel); - ScanDirectory(directoryAbs); - } - platform_enumerate_directories_end(enumDirectoryHandle); - - Memory::Free(directoryRel); - Memory::Free(directoryAbs); + const utf8 * enumPath = fileEnumerator.GetPath(); + ScanObject(enumPath); } } @@ -576,25 +519,15 @@ private: platform_get_user_directory(buffer, "object"); } - static uint32 GetPathChecksum(const utf8 * directory, const utf8 * file) + static uint32 GetPathChecksum(const utf8 * path) { - uint32 hashA = 0xD8430DED; - for (const utf8 * ch = directory; *ch != '\0'; ch++) + uint32 hash = 0xD8430DED; + for (const utf8 * ch = path; *ch != '\0'; ch++) { - hashA += (*ch); - hashA += (hashA << 10); - hashA ^= (hashA >> 6); + hash += (*ch); + hash += (hash << 10); + hash ^= (hash >> 6); } - - uint32 hashB = 0xAA7F8EA9; - for (const utf8 * ch = file; *ch != '\0'; ch++) - { - hashB += (*ch); - hashB += (hashB << 10); - hashB ^= (hashB >> 6); - } - - uint32 hash = hashA ^ hashB; hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15);