mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-02-01 11:15:13 +01:00
Convert track repository to C++
This commit is contained in:
@@ -240,11 +240,11 @@
|
||||
<ClCompile Include="src\ride\thrill\top_spin.c" />
|
||||
<ClCompile Include="src\ride\thrill\twist.c" />
|
||||
<ClCompile Include="src\ride\track.c" />
|
||||
<ClCompile Include="src\ride\TrackDesignRepository.cpp" />
|
||||
<ClCompile Include="src\ride\track_data.c" />
|
||||
<ClCompile Include="src\ride\track_data_old.c" />
|
||||
<ClCompile Include="src\ride\track_design.c" />
|
||||
<ClCompile Include="src\ride\track_design_save.c" />
|
||||
<ClCompile Include="src\ride\track_design_index.c" />
|
||||
<ClCompile Include="src\ride\track_paint.c" />
|
||||
<ClCompile Include="src\ride\transport\chairlift.c" />
|
||||
<ClCompile Include="src\ride\transport\lift.c" />
|
||||
@@ -492,6 +492,7 @@
|
||||
<ClInclude Include="src\ride\ride_ratings.h" />
|
||||
<ClInclude Include="src\ride\station.h" />
|
||||
<ClInclude Include="src\ride\track.h" />
|
||||
<ClInclude Include="src\ride\TrackDesignRepository.h" />
|
||||
<ClInclude Include="src\ride\track_data.h" />
|
||||
<ClInclude Include="src\ride\track_design.h" />
|
||||
<ClInclude Include="src\ride\track_paint.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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
367
src/ride/TrackDesignRepository.cpp
Normal file
367
src/ride/TrackDesignRepository.cpp
Normal file
@@ -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 <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#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<td_index_item> _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<track_design_file_ref> 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<utf8>(MAX_PATH);
|
||||
Path::GetFileNameWithoutExtension(name, MAX_PATH, path);
|
||||
name = Memory::ReallocateArray(name, String::SizeOf(name) + 1);
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
static std::unique_ptr<TrackDesignRepository> _trackRepository;
|
||||
|
||||
ITrackDesignRepository * GetTrackRepository()
|
||||
{
|
||||
if (_trackRepository == nullptr)
|
||||
{
|
||||
_trackRepository = std::unique_ptr<TrackDesignRepository>(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);
|
||||
}
|
||||
}
|
||||
60
src/ride/TrackDesignRepository.h
Normal file
60
src/ride/TrackDesignRepository.h
Normal file
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user