Skip to content

Commit

Permalink
Restore Tiled TMX plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-johansson committed Oct 20, 2024
1 parent 5bfe7e5 commit 3eea5dd
Show file tree
Hide file tree
Showing 22 changed files with 1,817 additions and 76 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ jobs:
- name: Run Tiled TMJ format tests
working-directory: ./build/debug
run: ./tactile-tiled-tmj-format-test

- name: Run runtime tests
working-directory: ./build/debug
run: ./tactile-runtime-test
4 changes: 4 additions & 0 deletions .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ jobs:
- name: Run Tiled TMJ format tests
working-directory: ./build/debug
run: ./tactile-tiled-tmj-format-test

- name: Run runtime tests
working-directory: ./build/debug
run: ./tactile-runtime-test
5 changes: 5 additions & 0 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,8 @@ jobs:
working-directory: ./build/debug
shell: cmd
run: tactile-tiled-tmj-format-test.exe

- name: Run runtime tests
working-directory: ./build/debug
shell: cmd
run: tactile-runtime-test.exe
4 changes: 4 additions & 0 deletions source/base/lib/inc/tactile/base/debug/error_code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ enum class ErrorCode : int
/** A parse operation failed. */
kParseError,

/** A write operation failed. */
kWriteError,

/** A compression operation failed. */
kCouldNotCompress,

Expand All @@ -78,6 +81,7 @@ constexpr auto to_string(const ErrorCode errc) noexcept -> std::string_view
case ErrorCode::kBadFileCopy: return "file copy error";
case ErrorCode::kBadImage: return "invalid image";
case ErrorCode::kParseError: return "parse error";
case ErrorCode::kWriteError: return "write error";
case ErrorCode::kCouldNotCompress: return "could not compress";
case ErrorCode::kCouldNotDecompress: return "could not decompress";
}
Expand Down
1 change: 1 addition & 0 deletions source/plugins/tiled_tmx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
project(tactile-tiled-tmx-format CXX)

find_path(CPPCODEC_INCLUDE_DIRS "cppcodec/base32_crockford.hpp")
find_package(pugixml CONFIG REQUIRED)

add_subdirectory("lib")
Expand Down
33 changes: 22 additions & 11 deletions source/plugins/tiled_tmx/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
project(tactile-tiled-tmx-format-lib CXX)
project(tactile-tiled-tmx-lib CXX)

add_library(tactile-tiled-tmx-format SHARED)
add_library(tactile::tiled_tmx_format ALIAS tactile-tiled-tmx-format)
add_library(tactile-tiled-tmx SHARED)
add_library(tactile::tiled_tmx ALIAS tactile-tiled-tmx)

target_sources(tactile-tiled-tmx-format
target_sources(tactile-tiled-tmx
PRIVATE
"src/tiled_tmx_format_plugin.cpp"
"src/tmx_common.cpp"
"src/tmx_format_parser.cpp"
"src/tmx_format_plugin.cpp"
"src/tmx_format_save_visitor.cpp"
"src/tmx_save_format.cpp"

PUBLIC FILE_SET "HEADERS" BASE_DIRS "inc" FILES
"inc/tactile/tiled_tmx/api.hpp"
"inc/tactile/tiled_tmx/tiled_tmx_format_plugin.hpp"
"inc/tactile/tiled_tmx/tmx_common.hpp"
"inc/tactile/tiled_tmx/tmx_format_parser.hpp"
"inc/tactile/tiled_tmx/tmx_format_plugin.hpp"
"inc/tactile/tiled_tmx/tmx_format_save_visitor.hpp"
"inc/tactile/tiled_tmx/tmx_save_format.hpp"
)

tactile_prepare_target(tactile-tiled-tmx-format)
tactile_prepare_target(tactile-tiled-tmx)

target_compile_definitions(tactile-tiled-tmx-format
target_compile_definitions(tactile-tiled-tmx
PRIVATE
"TACTILE_BUILDING_TILED_TMX_FORMAT"
)

target_link_libraries(tactile-tiled-tmx-format
target_include_directories(tactile-tiled-tmx
PRIVATE
"${CPPCODEC_INCLUDE_DIRS}"
)

target_link_libraries(tactile-tiled-tmx
PUBLIC
tactile::base
tactile::runtime

PRIVATE
pugixml::pugixml
)
4 changes: 2 additions & 2 deletions source/plugins/tiled_tmx/lib/inc/tactile/tiled_tmx/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "tactile/base/prelude.hpp"

#ifdef TACTILE_BUILDING_TILED_TMX_FORMAT
#define TACTILE_TMX_FORMAT_API TACTILE_DLL_EXPORT
#define TACTILE_TILED_TMX_API TACTILE_DLL_EXPORT
#else
#define TACTILE_TMX_FORMAT_API TACTILE_DLL_IMPORT
#define TACTILE_TILED_TMX_API TACTILE_DLL_IMPORT
#endif

This file was deleted.

173 changes: 173 additions & 0 deletions source/plugins/tiled_tmx/lib/inc/tactile/tiled_tmx/tmx_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#pragma once

#include <algorithm> // contains
#include <concepts> // signed_integral, unsigned_integral, floating_point, same_as
#include <expected> // expected, unexpected
#include <filesystem> // path
#include <limits> // numeric_limits
#include <optional> // optional, nullopt
#include <span> // span
#include <string> // string
#include <string_view> // string_view
#include <utility> // move

#include <pugixml.hpp>

#include "tactile/base/debug/error_code.hpp"
#include "tactile/base/io/compress/compression_format_id.hpp"
#include "tactile/base/layer/layer_type.hpp"
#include "tactile/base/meta/attribute_type.hpp"
#include "tactile/base/numeric/conversion.hpp"
#include "tactile/tiled_tmx/api.hpp"

namespace tactile::tiled_tmx {

[[nodiscard]]
TACTILE_TILED_TMX_API auto read_xml_document(const std::filesystem::path& path)
-> std::expected<pugi::xml_document, ErrorCode>;

[[nodiscard]]
TACTILE_TILED_TMX_API auto save_xml_document(const pugi::xml_document& document,
const std::filesystem::path& path)
-> std::expected<void, ErrorCode>;

[[nodiscard]]
TACTILE_TILED_TMX_API auto read_property_type(std::string_view name)
-> std::expected<AttributeType, ErrorCode>;

[[nodiscard]]
TACTILE_TILED_TMX_API auto get_property_type_name(AttributeType type) -> const char*;

[[nodiscard]]
TACTILE_TILED_TMX_API auto read_layer_type(std::string_view name)
-> std::expected<LayerType, ErrorCode>;

[[nodiscard]]
TACTILE_TILED_TMX_API auto get_layer_type_name(LayerType type) -> const char*;

[[nodiscard]]
TACTILE_TILED_TMX_API auto read_compression_format(std::string_view name)
-> std::expected<std::optional<CompressionFormatId>, ErrorCode>;

[[nodiscard]]
TACTILE_TILED_TMX_API auto get_compression_format_name(CompressionFormatId format) //
-> const char*;

template <typename T, std::invocable<const pugi::xml_node&> Parser>
[[nodiscard]] auto read_nodes(const pugi::xml_node& parent_node,
const std::span<const std::string_view> node_names,
const Parser& value_parser)
-> std::expected<std::vector<T>, ErrorCode>
{
std::vector<T> values {};

for (const auto& node : parent_node.children()) {
if (!std::ranges::contains(node_names, node.name())) {
continue;
}

auto value = value_parser(node);

if (!value.has_value()) {
return std::unexpected {value.error()};
}

values.push_back(std::move(*value));
}

return std::move(values);
}

template <typename T, std::invocable<const pugi::xml_node&> Parser>
[[nodiscard]] auto read_nodes(const pugi::xml_node& parent_node,
const std::string_view node_name,
const Parser& value_parser)
-> std::expected<std::vector<T>, ErrorCode>
{
return read_nodes<T>(parent_node, std::span {&node_name, 1}, value_parser);
}

template <typename T>
[[nodiscard]] auto read_attr(const pugi::xml_node& node, const char* name) -> std::optional<T>;

template <>
[[nodiscard]] inline auto read_attr<std::string>(const pugi::xml_node& node, const char* name)
-> std::optional<std::string>
{
if (const auto* str = node.attribute(name).as_string(nullptr)) {
return std::string {str};
}

return std::nullopt;
}

template <>
[[nodiscard]] inline auto read_attr<bool>(const pugi::xml_node& node, const char* name)
-> std::optional<bool>
{
if (const char* str = node.attribute(name).as_string(nullptr)) {
const std::string_view str_view {str};

if (str_view == "true") {
return true;
}

if (str_view == "false") {
return false;
}
}

return std::nullopt;
}

template <std::signed_integral T>
requires(!std::same_as<T, bool>)
[[nodiscard]] auto read_attr(const pugi::xml_node& node, const char* name) -> std::optional<T>
{
constexpr auto sentinel = std::numeric_limits<long long>::max();

if (const auto value = node.attribute(name).as_llong(sentinel); value != sentinel) {
return narrow<T>(value);
}

return std::nullopt;
}

template <std::unsigned_integral T>
requires(!std::same_as<T, bool>)
[[nodiscard]] auto read_attr(const pugi::xml_node& node, const char* name) -> std::optional<T>
{
constexpr auto sentinel = std::numeric_limits<unsigned long long>::max();

if (const auto value = node.attribute(name).as_ullong(sentinel); value != sentinel) {
return narrow<T>(value);
}

return std::nullopt;
}

template <std::floating_point T>
[[nodiscard]] auto read_attr(const pugi::xml_node& node, const char* name) -> std::optional<T>
{
constexpr auto sentinel = std::numeric_limits<double>::max();

if (const auto value = node.attribute(name).as_double(sentinel); value != sentinel) {
return static_cast<T>(value);
}

return std::nullopt;
}

template <typename T>
[[nodiscard]] auto read_attr_to(const pugi::xml_node& node, const char* name, T& result)
-> std::expected<void, ErrorCode>
{
if (auto value = read_attr<T>(node, name)) {
result = std::move(*value);
return {};
}

return std::unexpected {ErrorCode::kParseError};
}

} // namespace tactile::tiled_tmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <expected> // expected
#include <filesystem> // path

#include "tactile/base/debug/error_code.hpp"
#include "tactile/base/io/save/save_format.hpp"
#include "tactile/base/runtime/runtime.hpp"
#include "tactile/tiled_tmx/api.hpp"

namespace tactile::tiled_tmx {

/**
* Attempts to parse a single Tiled TMX map.
*
* \param runtime The associated runtime.
* \param map_path The file path to the TMX map.
* \param options The configured read options.
*
* \return
* The parsed map if successful; an error code otherwise.
*
* \see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#map
*/
[[nodiscard]]
TACTILE_TILED_TMX_API auto parse_map(const IRuntime& runtime,
const std::filesystem::path& map_path,
const SaveFormatReadOptions& options)
-> std::expected<ir::Map, ErrorCode>;

} // namespace tactile::tiled_tmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2024 Albin Johansson (GNU General Public License v3.0)

#pragma once

#include "tactile/base/io/save/save_format.hpp"
#include "tactile/base/runtime/plugin.hpp"
#include "tactile/tiled_tmx/api.hpp"

namespace tactile::tiled_tmx {

class TACTILE_TILED_TMX_API TmxFormatPlugin final : public IPlugin
{
public:
void load(IRuntime* runtime) override;

void unload() override;

private:
IRuntime* m_runtime {};
std::unique_ptr<ISaveFormat> m_format {};
};

extern "C"
{
TACTILE_TILED_TMX_API auto tactile_make_plugin() -> IPlugin*;
TACTILE_TILED_TMX_API void tactile_free_plugin(IPlugin* plugin);
}

} // namespace tactile::tiled_tmx
Loading

0 comments on commit 3eea5dd

Please sign in to comment.