diff --git a/openrct2.vcxproj b/openrct2.vcxproj
index 5d52961151..68818ff390 100644
--- a/openrct2.vcxproj
+++ b/openrct2.vcxproj
@@ -240,11 +240,11 @@
+
-
@@ -492,6 +492,7 @@
+
diff --git a/src/interface/window.h b/src/interface/window.h
index bd90a61630..71912d85be 100644
--- a/src/interface/window.h
+++ b/src/interface/window.h
@@ -31,6 +31,8 @@
struct rct_window;
union rct_window_event;
+struct track_design_file_ref;
+
extern uint16 TextInputDescriptionArgs[4];
extern char gTextBoxInput[512];
extern int gMaxTextBoxInputLength;
@@ -677,7 +679,7 @@ void ride_construction_tooldown_construct(int screenX, int screenY);
void custom_currency_window_open();
void window_maze_construction_update_pressed_widgets();
-void window_track_place_open(const track_design_file_ref *tdFileRef);
+void window_track_place_open(const struct track_design_file_ref *tdFileRef);
rct_window *window_new_ride_open();
rct_window *window_new_ride_open_research();
void window_install_track_open(const char* path);
@@ -701,7 +703,7 @@ void window_research_funding_page_paint(rct_window *w, rct_drawpixelinfo *dpi, i
void window_scenery_open();
void window_music_credits_open();
void window_publisher_credits_open();
-void window_track_manage_open(track_design_file_ref *tdFileRef);
+void window_track_manage_open(struct track_design_file_ref *tdFileRef);
void window_viewport_open();
void window_themes_open();
void window_title_editor_open(int tab);
diff --git a/src/rct2.c b/src/rct2.c
index 9378cb8fd7..3cb20c8d69 100644
--- a/src/rct2.c
+++ b/src/rct2.c
@@ -42,6 +42,7 @@
#include "ride/ride.h"
#include "ride/track.h"
#include "ride/track_design.h"
+#include "ride/TrackDesignRepository.h"
#include "ScenarioRepository.h"
#include "title.h"
#include "util/util.h"
@@ -166,7 +167,7 @@ bool rct2_init()
object_list_load();
scenario_repository_scan();
- track_design_index_create();
+ track_repository_scan();
font_sprite_initialise_characters();
if (!gOpenRCT2Headless) {
diff --git a/src/ride/TrackDesignRepository.cpp b/src/ride/TrackDesignRepository.cpp
new file mode 100644
index 0000000000..f545a7be8b
--- /dev/null
+++ b/src/ride/TrackDesignRepository.cpp
@@ -0,0 +1,367 @@
+#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
+#include
+#include "../core/Console.hpp"
+#include "../core/FileEnumerator.h"
+#include "../core/FileStream.hpp"
+#include "../core/Path.hpp"
+#include "../core/String.hpp"
+#include "TrackDesignRepository.h"
+
+extern "C"
+{
+ #include "../platform/platform.h"
+ #include "track_design.h"
+}
+
+#define MAX_PATH 260
+
+#pragma pack(push, 1)
+struct TrackRepositoryHeader
+{
+ uint32 MagicNumber;
+ uint16 Version;
+ uint32 NumItems;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+typedef struct td_index_item {
+ uint8 ride_type;
+ char ride_entry[9];
+ utf8 path[MAX_PATH];
+} td_index_item;
+// NOTE: this is our own struct and should not get packed, but it is stored in a file
+// so removing packing from it would require refactoring file access
+assert_struct_size(td_index_item, 1 + 9 + 260);
+#pragma pack(pop)
+
+constexpr uint32 TRACK_REPOISTORY_MAGIC_NUMBER = 0x58444954;
+constexpr uint16 TRACK_REPOISTORY_VERSION = 0;
+
+class TrackDesignRepository : public ITrackDesignRepository
+{
+private:
+ std::vector _items;
+
+public:
+ virtual ~TrackDesignRepository()
+ {
+
+ }
+
+ size_t GetCount() const override
+ {
+ return _items.size();
+ }
+
+ size_t GetCountForObjectEntry(uint8 rideType, const utf8 * entry) const override
+ {
+ size_t count = 0;
+ for (const auto item : _items)
+ {
+ if (item.ride_type == rideType &&
+ (entry == nullptr || String::Equals(item.ride_entry, entry, true)))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ size_t GetItemsForObjectEntry(track_design_file_ref * * outRefs, uint8 rideType, const utf8 * entry) const override
+ {
+ std::vector refs;
+ for (const auto item : _items)
+ {
+ if (item.ride_type == rideType &&
+ (entry == nullptr || String::Equals(item.ride_entry, entry, true)))
+ {
+ track_design_file_ref ref;
+ ref.name = GetNameFromTrackPath(item.path);
+ ref.path = String::Duplicate(item.path);
+ refs.push_back(ref);
+ }
+ }
+
+ *outRefs = nullptr;
+ if (refs.size() != 0)
+ {
+ *outRefs = Memory::DuplicateArray(refs.data(), refs.size());
+ }
+ return refs.size();
+ }
+
+ void Scan() override
+ {
+ utf8 directory[MAX_PATH];
+
+ GetRCT2Directory(directory, sizeof(directory));
+ Scan(directory);
+
+ GetUserDirectory(directory, sizeof(directory));
+ Scan(directory);
+
+ SortItems();
+ Save();
+ }
+
+ bool Delete(const utf8 * path) override
+ {
+ bool result = false;
+ if (platform_file_delete(path))
+ {
+ size_t index = GetTrackIndex(path);
+ if (index != SIZE_MAX)
+ {
+ _items.erase(_items.begin() + index);
+ }
+ }
+ return result;
+ }
+
+ const utf8 * Rename(const utf8 * path, const utf8 * newName) override
+ {
+ utf8 * result = nullptr;
+ size_t index = GetTrackIndex(path);
+ if (index != SIZE_MAX)
+ {
+ td_index_item * item = &_items[index];
+
+ utf8 newPath[MAX_PATH];
+ Path::GetDirectory(newPath, sizeof(newPath), path);
+ Path::Append(newPath, sizeof(newPath), newName);
+ Path::Append(newPath, sizeof(newPath), ".td6");
+
+ if (platform_file_move(path, newPath))
+ {
+ String::Set(item->path, sizeof(item->path), newPath);
+
+ SortItems();
+
+ item = GetTrackItem(path);
+ if (item != nullptr)
+ {
+ result = item->path;
+ }
+ }
+ }
+ return result;
+ }
+
+ const utf8 * Install(const utf8 * path) override
+ {
+ const utf8 * result = nullptr;
+ const utf8 * fileName = Path::GetFileName(path);
+
+ utf8 newPath[MAX_PATH];
+ GetUserDirectory(newPath, sizeof(newPath));
+ Path::Append(newPath, sizeof(newPath), fileName);
+
+ if (platform_file_copy(path, newPath, false))
+ {
+ AddTrack(path);
+ SortItems();
+
+ const td_index_item * item = GetTrackItem(path);
+ if (item != nullptr)
+ {
+ result = item->path;
+ }
+ }
+ return result;
+ }
+
+private:
+ void Scan(const utf8 * directory)
+ {
+ utf8 pattern[MAX_PATH];
+ String::Set(pattern, sizeof(pattern), directory);
+ Path::Append(pattern, sizeof(pattern), "*.td6");
+
+ auto fileEnumerator = FileEnumerator(pattern, true);
+ while (fileEnumerator.Next())
+ {
+ const utf8 * path = fileEnumerator.GetPath();
+ AddTrack(path);
+ }
+ }
+
+ void AddTrack(const utf8 * path)
+ {
+ rct_track_td6 * td6 = track_design_open(path);
+ if (td6 != nullptr)
+ {
+ td_index_item tdIndexItem = { 0 };
+ String::Set(tdIndexItem.path, sizeof(tdIndexItem.path), path);
+ memcpy(tdIndexItem.ride_entry, td6->vehicle_object.name, 8);
+ tdIndexItem.ride_type = td6->type;
+ _items.push_back(tdIndexItem);
+ track_design_dispose(td6);
+ }
+ }
+
+ void SortItems()
+ {
+ std::sort(_items.begin(), _items.end(), [](const td_index_item &a,
+ const td_index_item &b) -> bool
+ {
+ if (a.ride_type != b.ride_type)
+ {
+ return a.ride_type < b.ride_type;
+ }
+
+ const utf8 * nameA = Path::GetFileName(a.path);
+ const utf8 * nameB = Path::GetFileName(b.path);
+ return _stricmp(nameA, nameB) < 0;
+ });
+ }
+
+ void Save() const
+ {
+ utf8 path[MAX_PATH];
+ GetRepositoryPath(path, sizeof(path));
+
+ try
+ {
+ auto fs = FileStream(path, FILE_MODE_WRITE);
+
+ // Write header
+ TrackRepositoryHeader header = { 0 };
+ header.MagicNumber = TRACK_REPOISTORY_MAGIC_NUMBER;
+ header.Version = TRACK_REPOISTORY_VERSION;
+ header.NumItems = (uint32)_items.size();
+ fs.WriteValue(header);
+
+ // Write items
+ fs.WriteArray(_items.data(), _items.size());
+ }
+ catch (Exception ex)
+ {
+ Console::Error::WriteLine("Unable to write object repository index.");
+ }
+ }
+
+ size_t GetTrackIndex(const utf8 * path) const
+ {
+ for (size_t i = 0; i < _items.size(); i++)
+ {
+ if (Path::Equals(_items[i].path, path))
+ {
+ return i;
+ }
+ }
+ return SIZE_MAX;
+ }
+
+ td_index_item * GetTrackItem(const utf8 * path)
+ {
+ td_index_item * result = nullptr;
+ size_t index = GetTrackIndex(path);
+ if (index != SIZE_MAX)
+ {
+ result = &_items[index];
+ }
+ return result;
+ }
+
+ utf8 * GetRCT2Directory(utf8 * buffer, size_t bufferSize) const
+ {
+ String::Set(buffer, bufferSize, gRCT2AddressAppPath);
+ Path::Append(buffer, bufferSize, "Tracks");
+ return buffer;
+ }
+
+ utf8 * GetUserDirectory(utf8 * buffer, size_t bufferSize) const
+ {
+ platform_get_user_directory(buffer, "track", bufferSize);
+ return buffer;
+ }
+
+ utf8 * GetRepositoryPath(utf8 * buffer, size_t bufferSize) const
+ {
+ platform_get_user_directory(buffer, nullptr, bufferSize);
+ Path::Append(buffer, bufferSize, "tracks.idx");
+ return buffer;
+ }
+
+public:
+ static utf8 * GetNameFromTrackPath(const utf8 * path)
+ {
+ utf8 * name = Memory::Allocate(MAX_PATH);
+ Path::GetFileNameWithoutExtension(name, MAX_PATH, path);
+ name = Memory::ReallocateArray(name, String::SizeOf(name) + 1);
+ return name;
+ }
+};
+
+static std::unique_ptr _trackRepository;
+
+ITrackDesignRepository * GetTrackRepository()
+{
+ if (_trackRepository == nullptr)
+ {
+ _trackRepository = std::unique_ptr(new TrackDesignRepository());
+ }
+ return _trackRepository.get();
+}
+
+extern "C"
+{
+ void track_repository_scan()
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ repo->Scan();
+ }
+
+ size_t track_repository_get_count_for_ride(uint8 rideType, const utf8 * entry)
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ return repo->GetCountForObjectEntry(rideType, entry);
+ }
+
+ size_t track_repository_get_items_for_ride(track_design_file_ref * * outRefs, uint8 rideType, const utf8 * entry)
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ return repo->GetItemsForObjectEntry(outRefs, rideType, entry);
+ }
+
+ bool track_repository_delete(const utf8 * path)
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ return repo->Delete(path);
+ }
+
+ const utf8 * track_repository_rename(const utf8 * path, const utf8 * newName)
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ return repo->Rename(path, newName);
+ }
+
+ const utf8 * track_repository_install(const utf8 * srcPath)
+ {
+ ITrackDesignRepository * repo = GetTrackRepository();
+ return repo->Install(srcPath);
+ }
+
+ utf8 * track_repository_get_name_from_path(const utf8 * path)
+ {
+ return TrackDesignRepository::GetNameFromTrackPath(path);
+ }
+}
diff --git a/src/ride/TrackDesignRepository.h b/src/ride/TrackDesignRepository.h
new file mode 100644
index 0000000000..3611ff3234
--- /dev/null
+++ b/src/ride/TrackDesignRepository.h
@@ -0,0 +1,60 @@
+#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"
+
+typedef struct track_design_file_ref
+{
+ utf8 * name;
+ utf8 * path;
+} track_design_file_ref;
+
+#ifdef __cplusplus
+
+interface ITrackDesignRepository
+{
+ virtual ~ITrackDesignRepository() = default;
+
+ virtual size_t GetCount() const abstract;
+ virtual size_t GetCountForObjectEntry(uint8 rideType, const utf8 * entry) const abstract;
+ virtual size_t GetItemsForObjectEntry(track_design_file_ref * * outRefs,
+ uint8 rideType,
+ const utf8 * entry) const abstract;
+
+ virtual void Scan() abstract;
+ virtual bool Delete(const utf8 * path) abstract;
+ virtual const utf8 * Rename(const utf8 * path, const utf8 * newName) abstract;
+ virtual const utf8 * Install(const utf8 * path) abstract;
+};
+
+ITrackDesignRepository * GetTrackRepository();
+
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+ void track_repository_scan();
+ size_t track_repository_get_count_for_ride(uint8 rideType, const utf8 * entry);
+ size_t track_repository_get_items_for_ride(track_design_file_ref * * outRefs, uint8 rideType, const utf8 * entry);
+ utf8 * track_repository_get_name_from_path(const utf8 *path);
+ bool track_repository_delete(const utf8 *path);
+ const utf8 * track_repository_rename(const utf8 *path, const utf8 *newName);
+ const utf8 * track_repository_install(const utf8 *srcPath);
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/ride/track_design.c b/src/ride/track_design.c
index 8f024d94d3..191b201289 100644
--- a/src/ride/track_design.c
+++ b/src/ride/track_design.c
@@ -31,9 +31,10 @@
#include "../world/scenery.h"
#include "ride.h"
#include "ride_data.h"
+#include "track.h"
#include "track_data.h"
#include "track_design.h"
-#include "track.h"
+#include "TrackDesignRepository.h"
typedef struct map_backup {
rct_map_element map_elements[MAX_MAP_ELEMENTS];
@@ -107,7 +108,7 @@ rct_track_td6 *track_design_open(const utf8 *path)
free(decoded);
if (td6 != NULL) {
- td6->name = track_design_get_name_from_path(path);
+ td6->name = track_repository_get_name_from_path(path);
return td6;
}
}
diff --git a/src/ride/track_design.h b/src/ride/track_design.h
index e60c948d63..bb81486c83 100644
--- a/src/ride/track_design.h
+++ b/src/ride/track_design.h
@@ -152,11 +152,6 @@ assert_struct_size(rct_track_td6, 0xbf);
#endif
#pragma pack(pop)
-typedef struct track_design_file_ref {
- utf8 *name;
- utf8 *path;
-} track_design_file_ref;
-
enum {
TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER = (1 << 1),
TRACK_FLAGS2_SIX_FLAGS_RIDE_DEPRECATED = (1u << 31) // Not used anymore.
@@ -205,14 +200,6 @@ void track_design_mirror(rct_track_td6 *td6);
int sub_6D01B3(rct_track_td6 *td6, uint8 bl, uint8 rideIndex, int x, int y, int z);
-void track_design_index_create();
-size_t track_design_index_get_count_for_ride(uint8 rideType, const char *entry);
-size_t track_design_index_get_for_ride(track_design_file_ref **tdRefs, uint8 rideType, const char *entry);
-utf8 *track_design_get_name_from_path(const utf8 *path);
-bool track_design_index_rename(const utf8 *path, const utf8 *newName);
-bool track_design_index_delete(const utf8 *path);
-bool track_design_index_install(const utf8 *srcPath, const utf8 *destPath);
-
void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void game_command_place_maze_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
diff --git a/src/ride/track_design_index.c b/src/ride/track_design_index.c
deleted file mode 100644
index 15653345f1..0000000000
--- a/src/ride/track_design_index.c
+++ /dev/null
@@ -1,365 +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 "../config.h"
-#include "../game.h"
-#include "../interface/window.h"
-#include "../localisation/string_ids.h"
-#include "../util/util.h"
-#include "track.h"
-#include "track_design.h"
-
-#pragma pack(push, 1)
-typedef struct td_index_item {
- uint8 ride_type;
- char ride_entry[9];
- utf8 path[MAX_PATH];
-} td_index_item;
-// NOTE: this is our own struct and should not get packed, but it is stored in a file
-// so removing packing from it would require refactoring file access
-assert_struct_size(td_index_item, 1 + 9 + 260);
-#pragma pack(pop)
-
-static bool track_design_index_read_header(SDL_RWops *file, uint32 *tdidxCount);
-static void track_design_index_scan();
-static int track_design_index_item_compare(const void *a, const void *b);
-static void track_design_index_include(const utf8 *directory);
-static void track_design_add_file(const utf8 *path);
-static void track_design_add(const td_index_item *item);
-static void track_design_index_dispose();
-static void track_design_index_get_path(utf8 * buffer, size_t bufferLength);
-
-static const uint32 TrackIndexMagicNumber = 0x58444954;
-static const uint16 TrackIndexVersion = 0;
-
-static td_index_item *_tdIndex = NULL;
-static size_t _tdIndexSize = 0;
-static size_t _tdIndexCapacity = 0;
-
-void track_design_index_create()
-{
- track_design_index_dispose();
-
- log_verbose("saving track design index (tracks.idx)");
-
- utf8 path[MAX_PATH];
- track_design_index_get_path(path, sizeof(path));
-
- SDL_RWops *file = SDL_RWFromFile(path, "wb");
- if (file != NULL) {
- track_design_index_scan();
-
- SDL_RWwrite(file, &TrackIndexMagicNumber, sizeof(TrackIndexMagicNumber), 1);
- SDL_RWwrite(file, &TrackIndexVersion, sizeof(TrackIndexVersion), 1);
- SDL_RWwrite(file, &_tdIndexSize, sizeof(uint32), 1);
- SDL_RWwrite(file, _tdIndex, sizeof(td_index_item), _tdIndexSize);
- SDL_RWclose(file);
- track_design_index_dispose();
- }
-}
-
-size_t track_design_index_get_count_for_ride(uint8 rideType, const char *entry)
-{
- log_verbose("reading track design index (tracks.idx)");
-
- utf8 path[MAX_PATH];
- track_design_index_get_path(path, sizeof(path));
-
- // Return list
- size_t refsCount = 0;
- SDL_RWops *file = SDL_RWFromFile(path, "rb");
- if (file != NULL) {
- uint32 tdidxCount;
- if (!track_design_index_read_header(file, &tdidxCount)) {
- SDL_RWclose(file);
- return 0;
- }
-
- for (uint32 i = 0; i < tdidxCount; i++) {
- td_index_item tdItem;
- SDL_RWread(file, &tdItem, sizeof(td_index_item), 1);
-
- if (tdItem.ride_type != rideType) continue;
- if (entry != NULL && _strcmpi(entry, tdItem.ride_entry) != 0) continue;
- refsCount++;
- }
- SDL_RWclose(file);
- }
-
- return refsCount;
-}
-
-size_t track_design_index_get_for_ride(track_design_file_ref **tdRefs, uint8 rideType, const char *entry)
-{
- log_verbose("reading track design index (tracks.idx)");
-
- utf8 path[MAX_PATH];
- track_design_index_get_path(path, sizeof(path));
-
- // Return list
- size_t refsCount = 0;
- size_t refsCapacity = 0;
- track_design_file_ref *refs = NULL;
-
- SDL_RWops *file = SDL_RWFromFile(path, "rb");
- if (file != NULL) {
- uint32 tdidxCount;
- if (!track_design_index_read_header(file, &tdidxCount)) {
- SDL_RWclose(file);
- return 0;
- }
-
- for (uint32 i = 0; i < tdidxCount; i++) {
- td_index_item tdItem;
- SDL_RWread(file, &tdItem, sizeof(td_index_item), 1);
-
- if (tdItem.ride_type != rideType) continue;
- if (entry != NULL && _strcmpi(entry, tdItem.ride_entry) != 0) continue;
-
- size_t nextIndex = refsCount;
- if (nextIndex >= refsCapacity) {
- refsCapacity = max(8, refsCapacity * 2);
- refs = realloc(refs, refsCapacity * sizeof(track_design_file_ref));
- if (refs == NULL) {
- log_fatal("Unable to allocate more memory.");
- exit(-1);
- }
- }
- refs[nextIndex].name = track_design_get_name_from_path(tdItem.path);
- refs[nextIndex].path = _strdup(tdItem.path);
- refsCount++;
- }
- SDL_RWclose(file);
- }
-
- *tdRefs = realloc(refs, refsCount * sizeof(track_design_file_ref));
- return refsCount;
-}
-
-utf8 *track_design_get_name_from_path(const utf8 *path)
-{
- const char *filename = path_get_filename(path);
- const char *lastDot = strrchr(filename, '.');
- size_t nameLength;
- if (lastDot == NULL) {
- nameLength = strlen(filename);
- } else {
- nameLength = (size_t)(lastDot - filename);
- }
- return strndup(filename, nameLength);
-}
-
-/**
- *
- * rct2: 0x006D3664
- */
-bool track_design_index_rename(const utf8 *path, const utf8 *newName)
-{
- if (str_is_null_or_empty(newName)) {
- gGameCommandErrorText = STR_CANT_RENAME_TRACK_DESIGN;
- return false;
- }
-
- if (!filename_valid_characters(newName)) {
- gGameCommandErrorText = STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS;
- return false;
- }
-
- utf8 newPath[MAX_PATH];
- const char *lastPathSep = strrchr(path, *PATH_SEPARATOR);
- if (lastPathSep == NULL) {
- gGameCommandErrorText = STR_CANT_RENAME_TRACK_DESIGN;
- return false;
- }
- size_t directoryLength = (size_t)(lastPathSep - path + 1);
- memcpy(newPath, path, directoryLength);
- safe_strcpy(newPath + directoryLength, newName, sizeof(newPath) - directoryLength);
- path_append_extension(newPath, ".td6", sizeof(newPath));
-
- if (!platform_file_move(path, newPath)) {
- gGameCommandErrorText = STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED;
- return false;
- }
-
- track_design_index_create();
-
- rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
- if (trackListWindow != NULL) {
- trackListWindow->track_list.reload_track_designs = true;
- }
- return true;
-}
-
-/**
- *
- * rct2: 0x006D3761
- */
-bool track_design_index_delete(const utf8 *path)
-{
- if (!platform_file_delete(path)) {
- gGameCommandErrorText = STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED;
- return false;
- }
-
- track_design_index_create();
-
- rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
- if (trackListWindow != NULL) {
- trackListWindow->track_list.reload_track_designs = true;
- }
- return true;
-}
-
-/**
- *
- * rct2: 0x006D40B2
- */
-bool track_design_index_install(const utf8 *srcPath, const utf8 *destPath)
-{
- if (!platform_file_copy(srcPath, destPath, false)) {
- return false;
- }
-
- track_design_index_create();
-
- rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
- if (trackListWindow != NULL) {
- trackListWindow->track_list.reload_track_designs = true;
- }
- return true;
-}
-
-static bool track_design_index_read_header(SDL_RWops *file, uint32 *tdidxCount)
-{
- uint32 tdidxMagicNumber;
- uint16 tdidxVersion;
- SDL_RWread(file, &tdidxMagicNumber, sizeof(tdidxMagicNumber), 1);
- SDL_RWread(file, &tdidxVersion, sizeof(tdidxVersion), 1);
- SDL_RWread(file, tdidxCount, sizeof(uint32), 1);
- if (tdidxMagicNumber != TrackIndexMagicNumber) {
- log_error("invalid track index file");
- return false;
- }
- if (tdidxVersion != TrackIndexVersion) {
- log_error("unsupported track index file version");
- return false;
- }
- return true;
-}
-
-static void track_design_index_scan()
-{
- utf8 directory[MAX_PATH];
-
- // Get track directory from RCT2
- safe_strcpy(directory, gRCT2AddressAppPath, sizeof(directory));
- safe_strcat_path(directory, "Tracks", sizeof(directory));
- track_design_index_include(directory);
-
- // Get track directory from user directory
- platform_get_user_directory(directory, "track", sizeof(directory));
- track_design_index_include(directory);
-
- // Sort items by ride type then by filename
- qsort(_tdIndex, _tdIndexSize, sizeof(td_index_item), track_design_index_item_compare);
-}
-
-static int track_design_index_item_compare(const void *a, const void *b)
-{
- const td_index_item *tdA = (const td_index_item*)a;
- const td_index_item *tdB = (const td_index_item*)b;
-
- if (tdA->ride_type != tdB->ride_type) {
- return tdA->ride_type - tdB->ride_type;
- }
-
- const utf8 *tdAName = path_get_filename(tdA->path);
- const utf8 *tdBName = path_get_filename(tdB->path);
- return _stricmp(tdAName, tdBName);
-}
-
-static void track_design_index_include(const utf8 *directory)
-{
- int handle;
- file_info fileInfo;
-
- // Scenarios in this directory
- utf8 pattern[MAX_PATH];
- safe_strcpy(pattern, directory, sizeof(pattern));
- safe_strcat_path(pattern, "*.td6", sizeof(pattern));
-
- handle = platform_enumerate_files_begin(pattern);
- while (platform_enumerate_files_next(handle, &fileInfo)) {
- utf8 path[MAX_PATH];
- safe_strcpy(path, directory, sizeof(pattern));
- safe_strcat_path(path, fileInfo.path, sizeof(pattern));
- track_design_add_file(path);
- }
- platform_enumerate_files_end(handle);
-
- // Include sub-directories
- utf8 subDirectory[MAX_PATH];
- handle = platform_enumerate_directories_begin(directory);
- while (platform_enumerate_directories_next(handle, subDirectory)) {
- utf8 path[MAX_PATH];
- safe_strcpy(path, directory, sizeof(pattern));
- safe_strcat_path(path, subDirectory, sizeof(pattern));
- track_design_index_include(path);
- }
- platform_enumerate_directories_end(handle);
-}
-
-static void track_design_add_file(const utf8 *path)
-{
- rct_track_td6 *td6 = track_design_open(path);
- if (td6 != NULL) {
- td_index_item tdIndexItem = { 0 };
- safe_strcpy(tdIndexItem.path, path, sizeof(tdIndexItem.path));
- memcpy(tdIndexItem.ride_entry, td6->vehicle_object.name, 8);
- tdIndexItem.ride_type = td6->type;
- track_design_add(&tdIndexItem);
- track_design_dispose(td6);
- }
-}
-
-static void track_design_add(const td_index_item *item)
-{
- size_t nextIndex = _tdIndexSize;
- if (nextIndex >= _tdIndexCapacity) {
- _tdIndexCapacity = max(128, _tdIndexCapacity * 2);
- _tdIndex = realloc(_tdIndex, _tdIndexCapacity * sizeof(td_index_item));
- if (_tdIndex == NULL) {
- log_fatal("Unable to allocate more memory.");
- exit(-1);
- }
- }
- _tdIndex[nextIndex] = *item;
- _tdIndexSize++;
-}
-
-static void track_design_index_dispose()
-{
- SafeFree(_tdIndex);
- _tdIndexSize = 0;
- _tdIndexCapacity = 0;
-}
-
-static void track_design_index_get_path(utf8 * buffer, size_t bufferLength)
-{
- platform_get_user_directory(buffer, NULL, bufferLength);
- safe_strcat_path(buffer, "tracks.idx", bufferLength);
-}
diff --git a/src/ride/track_design_save.c b/src/ride/track_design_save.c
index 2860919db3..65f9ee59cc 100644
--- a/src/ride/track_design_save.c
+++ b/src/ride/track_design_save.c
@@ -28,6 +28,7 @@
#include "track.h"
#include "track_data.h"
#include "track_design.h"
+#include "TrackDesignRepository.h"
#define TRACK_MAX_SAVED_MAP_ELEMENTS 1500
@@ -128,7 +129,7 @@ static void track_design_save_callback(int result) {
free(_trackDesign);
if (result == MODAL_RESULT_OK) {
- track_design_index_create();
+ track_repository_scan();
}
gfx_invalidate_screen();
}
diff --git a/src/windows/install_track.c b/src/windows/install_track.c
index 6a4c86c216..af82f7563e 100644
--- a/src/windows/install_track.c
+++ b/src/windows/install_track.c
@@ -23,6 +23,7 @@
#include "../ride/ride.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
+#include "../ride/TrackDesignRepository.h"
#include "../object/ObjectManager.h"
#include "../sprites.h"
#include "../util/util.h"
@@ -135,7 +136,7 @@ void window_install_track_open(const utf8 *path)
window_push_others_right(w);
_trackPath = _strdup(path);
- _trackName = track_design_get_name_from_path(path);
+ _trackName = track_repository_get_name_from_path(path);
_trackDesignPreviewPixels = calloc(4, TRACK_PREVIEW_IMAGE_SIZE);
window_install_track_update_preview();
@@ -400,7 +401,7 @@ static void window_install_track_design(rct_window *w)
255
);
} else {
- if (track_design_index_install(_trackPath, destPath)) {
+ if (track_repository_install(_trackPath)) {
window_close(w);
} else {
window_error_open(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE);
diff --git a/src/windows/new_ride.c b/src/windows/new_ride.c
index c8ffa93322..cfd33cca34 100644
--- a/src/windows/new_ride.c
+++ b/src/windows/new_ride.c
@@ -27,13 +27,14 @@
#include "../object.h"
#include "../rct1.h"
#include "../ride/ride.h"
-#include "../ride/track.h"
-#include "../ride/track_design.h"
-#include "../world/scenery.h"
#include "../ride/ride_data.h"
-#include "../sprites.h"
+#include "../ride/track.h"
#include "../ride/track_data.h"
+#include "../ride/track_design.h"
+#include "../ride/TrackDesignRepository.h"
+#include "../sprites.h"
#include "../util/util.h"
+#include "../world/scenery.h"
static uint8 _windowNewRideCurrentTab;
static ride_list_item _windowNewRideHighlightedItem[6];
@@ -857,7 +858,7 @@ static int get_num_track_designs(ride_list_item item)
}
}
- return (int)track_design_index_get_count_for_ride(item.type, entryPtr);
+ return (int)track_repository_get_count_for_ride(item.type, entryPtr);
}
/**
diff --git a/src/windows/track_list.c b/src/windows/track_list.c
index 1547bed634..37229e6c2e 100644
--- a/src/windows/track_list.c
+++ b/src/windows/track_list.c
@@ -25,6 +25,7 @@
#include "../ride/ride.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
+#include "../ride/TrackDesignRepository.h"
#include "../sprites.h"
#include "error.h"
@@ -139,7 +140,10 @@ void window_track_list_open(ride_list_item item)
w->track_list.var_480 = 0xFFFF;
w->track_list.var_484 = 0;
w->track_list.reload_track_designs = false;
- w->selected_list_item = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 1;
+ w->selected_list_item = 0;
+ if (_trackDesignsCount != 0 && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
+ w->selected_list_item = 1;
+ }
gTrackDesignSceneryToggle = false;
window_push_others_right(w);
_currentTrackPieceDirection = 2;
@@ -613,7 +617,7 @@ static void track_list_load_designs(ride_list_item item)
entryPtr = entry;
}
}
- _trackDesignsCount = track_design_index_get_for_ride(&_trackDesigns, item.type, entryPtr);
+ _trackDesignsCount = track_repository_get_items_for_ride(&_trackDesigns, item.type, entryPtr);
}
static bool track_list_load_design_for_preview(utf8 *path)
diff --git a/src/windows/track_manage.c b/src/windows/track_manage.c
index 5e8c8176b7..1aa6319b4d 100644
--- a/src/windows/track_manage.c
+++ b/src/windows/track_manage.c
@@ -21,6 +21,7 @@
#include "../localisation/localisation.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
+#include "../ride/TrackDesignRepository.h"
#include "../util/util.h"
#include "error.h"
@@ -217,11 +218,21 @@ static void window_track_manage_textinput(rct_window *w, int widgetIndex, char *
return;
}
- if (track_design_index_rename(_trackDesignFileReference->path, text)) {
+ if (str_is_null_or_empty(text)) {
+ window_error_open(STR_CANT_RENAME_TRACK_DESIGN, STR_NONE);
+ return;
+ }
+
+ if (!filename_valid_characters(text)) {
+ window_error_open(STR_CANT_RENAME_TRACK_DESIGN, STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS);
+ return;
+ }
+
+ if (track_repository_rename(_trackDesignFileReference->path, text)) {
window_close_by_class(WC_TRACK_DELETE_PROMPT);
window_close(w);
} else {
- window_error_open(STR_CANT_RENAME_TRACK_DESIGN, gGameCommandErrorText);
+ window_error_open(STR_CANT_RENAME_TRACK_DESIGN, STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED);
}
}
@@ -281,10 +292,10 @@ static void window_track_delete_prompt_mouseup(rct_window *w, int widgetIndex)
break;
case WIDX_PROMPT_DELETE:
window_close(w);
- if (track_design_index_delete(_trackDesignFileReference->path)) {
+ if (track_repository_delete(_trackDesignFileReference->path)) {
window_close_by_class(WC_MANAGE_TRACK_DESIGN);
} else {
- window_error_open(STR_CANT_DELETE_TRACK_DESIGN, gGameCommandErrorText);
+ window_error_open(STR_CANT_DELETE_TRACK_DESIGN, STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED);
}
break;
}
diff --git a/src/windows/track_place.c b/src/windows/track_place.c
index 6377166edd..e8d89ad204 100644
--- a/src/windows/track_place.c
+++ b/src/windows/track_place.c
@@ -26,6 +26,7 @@
#include "../ride/track.h"
#include "../ride/track_data.h"
#include "../ride/track_design.h"
+#include "../ride/TrackDesignRepository.h"
#include "../sprites.h"
#include "../util/util.h"