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