|
|
|
|
#pragma once
|
|
|
|
|
/**
|
|
|
|
|
* @file clx_sprite.hpp
|
|
|
|
|
*
|
|
|
|
|
* @brief CLX format sprites.
|
|
|
|
|
*
|
|
|
|
|
* CLX is a format used for DevilutionX graphics at runtime.
|
|
|
|
|
*
|
|
|
|
|
* It is identical to CL2, except we use the frame header to store the frame's width and height.
|
|
|
|
|
*
|
|
|
|
|
* CLX frame header (10 bytes, same as CL2):
|
|
|
|
|
*
|
|
|
|
|
* Bytes | Type | Value
|
|
|
|
|
* :-----:|:--------:|------------------------------------
|
|
|
|
|
* 0..2 | uint16_t | offset to data start (same as CL2)
|
|
|
|
|
* 2..4 | uint16_t | width
|
|
|
|
|
* 4..6 | uint16_t | height
|
|
|
|
|
* 6..10 | - | unused
|
|
|
|
|
*
|
|
|
|
|
* The CLX format is otherwise identical to CL2.
|
|
|
|
|
*
|
|
|
|
|
* Since the header is identical to CL2, CL2 can be converted to CLX without reallocation.
|
|
|
|
|
*
|
|
|
|
|
* CL2 reference: https://github.com/savagesteel/d1-file-formats/blob/master/PC-Mac/CL2.md#2-file-structure
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
#include <iterator>
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
#include "appfat.h"
|
|
|
|
|
#include "utils/endian.hpp"
|
|
|
|
|
#include "utils/intrusive_optional.hpp"
|
|
|
|
|
|
|
|
|
|
namespace devilution {
|
|
|
|
|
|
|
|
|
|
class OptionalClxSprite;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A single CLX sprite.
|
|
|
|
|
*/
|
|
|
|
|
class ClxSprite {
|
|
|
|
|
static constexpr uint32_t HeaderSize = 10;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit constexpr ClxSprite(const uint8_t *data, uint32_t dataSize)
|
|
|
|
|
: data_(data)
|
|
|
|
|
, pixel_data_size_(dataSize - HeaderSize)
|
|
|
|
|
{
|
|
|
|
|
assert(data != nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint16_t width() const
|
|
|
|
|
{
|
|
|
|
|
return LoadLE16(&data_[2]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint16_t height() const
|
|
|
|
|
{
|
|
|
|
|
return LoadLE16(&data_[4]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief The raw pixel data (CL2 frame data).
|
|
|
|
|
*
|
|
|
|
|
* Format: https://github.com/savagesteel/d1-file-formats/blob/master/PC-Mac/CL2.md#42-cl2-frame-data
|
|
|
|
|
*/
|
|
|
|
|
[[nodiscard]] constexpr const uint8_t *pixelData() const
|
|
|
|
|
{
|
|
|
|
|
return &data_[HeaderSize];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint32_t pixelDataSize() const
|
|
|
|
|
{
|
|
|
|
|
return pixel_data_size_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator==(const ClxSprite &other) const
|
|
|
|
|
{
|
|
|
|
|
return data_ == other.data_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator!=(const ClxSprite &other) const
|
|
|
|
|
{
|
|
|
|
|
return !(*this == other);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// For OptionalClxSprite.
|
|
|
|
|
constexpr ClxSprite()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
, pixel_data_size_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint8_t *data_;
|
|
|
|
|
uint32_t pixel_data_size_;
|
|
|
|
|
|
|
|
|
|
friend class OptionalClxSprite;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class OwnedClxSpriteList;
|
|
|
|
|
class OptionalClxSpriteList;
|
|
|
|
|
class ClxSpriteListIterator;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A list of `ClxSprite`s.
|
|
|
|
|
*/
|
|
|
|
|
class ClxSpriteList {
|
|
|
|
|
public:
|
|
|
|
|
explicit constexpr ClxSpriteList(const uint8_t *data)
|
|
|
|
|
: data_(data)
|
|
|
|
|
{
|
|
|
|
|
assert(data != nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClxSpriteList(const OwnedClxSpriteList &owned);
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] OwnedClxSpriteList clone() const;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint32_t numSprites() const
|
|
|
|
|
{
|
|
|
|
|
return LoadLE32(data_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSprite operator[](size_t spriteIndex) const
|
|
|
|
|
{
|
|
|
|
|
assert(spriteIndex < numSprites());
|
|
|
|
|
const uint32_t begin = spriteOffset(spriteIndex);
|
|
|
|
|
const uint32_t end = spriteOffset(spriteIndex + 1);
|
|
|
|
|
return ClxSprite { &data_[begin], end - begin };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint32_t spriteOffset(size_t spriteIndex) const
|
|
|
|
|
{
|
|
|
|
|
return LoadLE32(&data_[4 + spriteIndex * 4]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @brief The offset to the next sprite sheet, or file size if this is the last sprite sheet. */
|
|
|
|
|
[[nodiscard]] constexpr uint32_t nextSpriteSheetOffsetOrFileSize() const
|
|
|
|
|
{
|
|
|
|
|
return LoadLE32(&data_[4 + numSprites() * 4]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr const uint8_t *data() const
|
|
|
|
|
{
|
|
|
|
|
return data_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteListIterator begin() const;
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteListIterator end() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// For OptionalClxSpriteList.
|
|
|
|
|
constexpr ClxSpriteList()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint8_t *data_;
|
|
|
|
|
|
|
|
|
|
friend class OptionalClxSpriteList;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ClxSpriteListIterator {
|
|
|
|
|
public:
|
|
|
|
|
using iterator_category = std::forward_iterator_tag;
|
|
|
|
|
using difference_type = int;
|
|
|
|
|
using value_type = ClxSprite;
|
|
|
|
|
using pointer = void;
|
|
|
|
|
using reference = value_type &;
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteListIterator(ClxSpriteList list, size_t index)
|
|
|
|
|
: list_(list)
|
|
|
|
|
, index_(index)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSprite operator*()
|
|
|
|
|
{
|
|
|
|
|
return list_[index_];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteListIterator &operator++()
|
|
|
|
|
{
|
|
|
|
|
++index_;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteListIterator operator++(int)
|
|
|
|
|
{
|
|
|
|
|
auto copy = *this;
|
|
|
|
|
++(*this);
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator==(const ClxSpriteListIterator &other) const
|
|
|
|
|
{
|
|
|
|
|
return index_ == other.index_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator!=(const ClxSpriteListIterator &other) const
|
|
|
|
|
{
|
|
|
|
|
return !(*this == other);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ClxSpriteList list_;
|
|
|
|
|
size_t index_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline constexpr ClxSpriteListIterator ClxSpriteList::begin() const
|
|
|
|
|
{
|
|
|
|
|
return { *this, 0 };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline constexpr ClxSpriteListIterator ClxSpriteList::end() const
|
|
|
|
|
{
|
|
|
|
|
return { *this, numSprites() };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OwnedClxSpriteSheet;
|
|
|
|
|
class OptionalClxSpriteSheet;
|
|
|
|
|
class ClxSpriteSheetIterator;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A sprite sheet is a list of `ClxSpriteList`s.
|
|
|
|
|
*/
|
|
|
|
|
class ClxSpriteSheet {
|
|
|
|
|
public:
|
|
|
|
|
explicit constexpr ClxSpriteSheet(const uint8_t *data, uint16_t numLists)
|
|
|
|
|
: data_(data)
|
|
|
|
|
, num_lists_(numLists)
|
|
|
|
|
{
|
|
|
|
|
assert(data != nullptr);
|
|
|
|
|
assert(num_lists_ > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClxSpriteSheet(const OwnedClxSpriteSheet &owned);
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint16_t numLists() const
|
|
|
|
|
{
|
|
|
|
|
return num_lists_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteList operator[](size_t sheetIndex) const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteList { &data_[sheetOffset(sheetIndex)] };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr uint32_t sheetOffset(size_t sheetIndex) const
|
|
|
|
|
{
|
|
|
|
|
assert(sheetIndex < num_lists_);
|
|
|
|
|
return LoadLE32(&data_[4 * sheetIndex]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr const uint8_t *data() const
|
|
|
|
|
{
|
|
|
|
|
return data_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteSheetIterator begin() const;
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteSheetIterator end() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// For OptionalClxSpriteSheet.
|
|
|
|
|
constexpr ClxSpriteSheet()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
, num_lists_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint8_t *data_;
|
|
|
|
|
uint16_t num_lists_;
|
|
|
|
|
|
|
|
|
|
friend class OptionalClxSpriteSheet;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ClxSpriteSheetIterator {
|
|
|
|
|
public:
|
|
|
|
|
using iterator_category = std::forward_iterator_tag;
|
|
|
|
|
using difference_type = int;
|
|
|
|
|
using value_type = ClxSpriteList;
|
|
|
|
|
using pointer = void;
|
|
|
|
|
using reference = value_type &;
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteSheetIterator(ClxSpriteSheet sheet, size_t index)
|
|
|
|
|
: sheet_(sheet)
|
|
|
|
|
, index_(index)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteList operator*()
|
|
|
|
|
{
|
|
|
|
|
return sheet_[index_];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteSheetIterator &operator++()
|
|
|
|
|
{
|
|
|
|
|
++index_;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr ClxSpriteSheetIterator operator++(int)
|
|
|
|
|
{
|
|
|
|
|
auto copy = *this;
|
|
|
|
|
++(*this);
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator==(const ClxSpriteSheetIterator &other) const
|
|
|
|
|
{
|
|
|
|
|
return index_ == other.index_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr bool operator!=(const ClxSpriteSheetIterator &other) const
|
|
|
|
|
{
|
|
|
|
|
return !(*this == other);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ClxSpriteSheet sheet_;
|
|
|
|
|
size_t index_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline constexpr ClxSpriteSheetIterator ClxSpriteSheet::begin() const
|
|
|
|
|
{
|
|
|
|
|
return { *this, 0 };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline constexpr ClxSpriteSheetIterator ClxSpriteSheet::end() const
|
|
|
|
|
{
|
|
|
|
|
return { *this, num_lists_ };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OptionalOwnedClxSpriteList;
|
|
|
|
|
class OwnedClxSpriteListOrSheet;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Implicitly convertible to `ClxSpriteList` and owns its data.
|
|
|
|
|
*/
|
|
|
|
|
class OwnedClxSpriteList {
|
|
|
|
|
public:
|
|
|
|
|
explicit OwnedClxSpriteList(std::unique_ptr<uint8_t[]> &&data)
|
|
|
|
|
: data_(std::move(data))
|
|
|
|
|
{
|
|
|
|
|
assert(data_ != nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OwnedClxSpriteList(OwnedClxSpriteList &&) noexcept = default;
|
|
|
|
|
OwnedClxSpriteList &operator=(OwnedClxSpriteList &&) noexcept = default;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] OwnedClxSpriteList clone() const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteList { *this }.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSprite operator[](size_t spriteIndex) const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteList { *this }[spriteIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// For OptionalOwnedClxSpriteList.
|
|
|
|
|
OwnedClxSpriteList() = default;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<uint8_t[]> data_;
|
|
|
|
|
|
|
|
|
|
friend class ClxSpriteList; // for implicit conversion
|
|
|
|
|
friend class OptionalOwnedClxSpriteList;
|
|
|
|
|
friend class OwnedClxSpriteListOrSheet;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline ClxSpriteList::ClxSpriteList(const OwnedClxSpriteList &owned)
|
|
|
|
|
: data_(owned.data_.get())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline OwnedClxSpriteList ClxSpriteList::clone() const
|
|
|
|
|
{
|
|
|
|
|
const size_t dataSize = nextSpriteSheetOffsetOrFileSize();
|
|
|
|
|
std::unique_ptr<uint8_t[]> data { new uint8_t[dataSize] };
|
|
|
|
|
memcpy(data.get(), data_, dataSize);
|
|
|
|
|
return OwnedClxSpriteList { std::move(data) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Implicitly convertible to `ClxSpriteSheet` and owns its data.
|
|
|
|
|
*/
|
|
|
|
|
class OwnedClxSpriteSheet {
|
|
|
|
|
public:
|
|
|
|
|
OwnedClxSpriteSheet(std::unique_ptr<uint8_t[]> &&data, uint16_t numLists)
|
|
|
|
|
: data_(std::move(data))
|
|
|
|
|
, num_lists_(numLists)
|
|
|
|
|
{
|
|
|
|
|
assert(data_ != nullptr);
|
|
|
|
|
assert(numLists > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OwnedClxSpriteSheet(OwnedClxSpriteSheet &&) noexcept = default;
|
|
|
|
|
OwnedClxSpriteSheet &operator=(OwnedClxSpriteSheet &&) noexcept = default;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSpriteList operator[](size_t sheetIndex) const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteSheet { *this }[sheetIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSpriteSheetIterator begin() const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteSheet { *this }.begin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSpriteSheetIterator end() const
|
|
|
|
|
{
|
|
|
|
|
return ClxSpriteSheet { *this }.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// For OptionalOwnedClxSpriteList.
|
|
|
|
|
OwnedClxSpriteSheet()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
, num_lists_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<uint8_t[]> data_;
|
|
|
|
|
uint16_t num_lists_;
|
|
|
|
|
|
|
|
|
|
friend class ClxSpriteSheet; // for implicit conversion.
|
|
|
|
|
friend class OptionalOwnedClxSpriteSheet;
|
|
|
|
|
friend class OwnedClxSpriteListOrSheet;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline ClxSpriteSheet::ClxSpriteSheet(const OwnedClxSpriteSheet &owned)
|
|
|
|
|
: data_(owned.data_.get())
|
|
|
|
|
, num_lists_(owned.num_lists_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class OwnedClxSpriteListOrSheet;
|
|
|
|
|
class OptionalClxSpriteListOrSheet;
|
|
|
|
|
|
|
|
|
|
inline uint16_t GetNumListsFromClxListOrSheetBuffer(const uint8_t *data, size_t size)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t maybeNumFrames = LoadLE32(data);
|
|
|
|
|
|
|
|
|
|
// If it is a number of frames, then the last frame offset will be equal to the size of the file.
|
|
|
|
|
if (LoadLE32(&data[maybeNumFrames * 4 + 4]) != size)
|
|
|
|
|
return maybeNumFrames / 4;
|
|
|
|
|
|
|
|
|
|
// Not a sprite sheet.
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A CLX sprite list or a sprite sheet (list of lists).
|
|
|
|
|
*/
|
|
|
|
|
class ClxSpriteListOrSheet {
|
|
|
|
|
public:
|
|
|
|
|
constexpr ClxSpriteListOrSheet(const uint8_t *data, uint16_t numLists)
|
|
|
|
|
: data_(data)
|
|
|
|
|
, num_lists_(numLists)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClxSpriteListOrSheet(const OwnedClxSpriteListOrSheet &listOrSheet);
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteList list() const
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ == 0);
|
|
|
|
|
return ClxSpriteList { data_ };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr ClxSpriteSheet sheet() const
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ != 0);
|
|
|
|
|
return ClxSpriteSheet { data_, num_lists_ };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] constexpr bool isSheet() const
|
|
|
|
|
{
|
|
|
|
|
return num_lists_ != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const uint8_t *data_;
|
|
|
|
|
uint16_t num_lists_;
|
|
|
|
|
|
|
|
|
|
// For OptionalClxSpriteListOrSheet.
|
|
|
|
|
constexpr ClxSpriteListOrSheet()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
, num_lists_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend class OptionalClxSpriteListOrSheet;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class OptionalOwnedClxSpriteListOrSheet;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief A CLX sprite list or a sprite sheet (list of lists).
|
|
|
|
|
*/
|
|
|
|
|
class OwnedClxSpriteListOrSheet {
|
|
|
|
|
public:
|
|
|
|
|
static OwnedClxSpriteListOrSheet FromBuffer(std::unique_ptr<uint8_t[]> &&data, size_t size)
|
|
|
|
|
{
|
|
|
|
|
const uint16_t numLists = GetNumListsFromClxListOrSheetBuffer(data.get(), size);
|
|
|
|
|
return OwnedClxSpriteListOrSheet { std::move(data), numLists };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit OwnedClxSpriteListOrSheet(std::unique_ptr<uint8_t[]> &&data, uint16_t numLists)
|
|
|
|
|
: data_(std::move(data))
|
|
|
|
|
, num_lists_(numLists)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit OwnedClxSpriteListOrSheet(OwnedClxSpriteSheet &&sheet)
|
|
|
|
|
: data_(std::move(sheet.data_))
|
|
|
|
|
, num_lists_(sheet.num_lists_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
explicit OwnedClxSpriteListOrSheet(OwnedClxSpriteList &&list)
|
|
|
|
|
: data_(std::move(list.data_))
|
|
|
|
|
, num_lists_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSpriteList list() const &
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ == 0);
|
|
|
|
|
return ClxSpriteList { data_.get() };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] OwnedClxSpriteList list() &&
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ == 0);
|
|
|
|
|
return OwnedClxSpriteList { std::move(data_) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] ClxSpriteSheet sheet() const &
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ != 0);
|
|
|
|
|
return ClxSpriteSheet { data_.get(), num_lists_ };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] OwnedClxSpriteSheet sheet() &&
|
|
|
|
|
{
|
|
|
|
|
assert(num_lists_ != 0);
|
|
|
|
|
return OwnedClxSpriteSheet { std::move(data_), num_lists_ };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool isSheet() const
|
|
|
|
|
{
|
|
|
|
|
return num_lists_ != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::unique_ptr<uint8_t[]> data_;
|
|
|
|
|
uint16_t num_lists_;
|
|
|
|
|
|
|
|
|
|
// For OptionalOwnedClxSpriteListOrSheet.
|
|
|
|
|
OwnedClxSpriteListOrSheet()
|
|
|
|
|
: data_(nullptr)
|
|
|
|
|
, num_lists_(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend class ClxSpriteListOrSheet;
|
|
|
|
|
friend class OptionalOwnedClxSpriteListOrSheet;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline ClxSpriteListOrSheet::ClxSpriteListOrSheet(const OwnedClxSpriteListOrSheet &listOrSheet)
|
|
|
|
|
: data_(listOrSheet.data_.get())
|
|
|
|
|
, num_lists_(listOrSheet.num_lists_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<ClxSprite>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalClxSprite {
|
|
|
|
|
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSprite, ClxSprite, data_, nullptr)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<ClxSpriteList>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalClxSpriteList {
|
|
|
|
|
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSpriteList, ClxSpriteList, data_, nullptr)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<ClxSpriteSheet>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalClxSpriteSheet {
|
|
|
|
|
DEFINE_CONSTEXPR_INTRUSIVE_OPTIONAL(OptionalClxSpriteSheet, ClxSpriteSheet, data_, nullptr)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<ClxSpriteListOrSheet>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalClxSpriteListOrSheet {
|
|
|
|
|
public:
|
|
|
|
|
DEFINE_INTRUSIVE_OPTIONAL(OptionalClxSpriteListOrSheet, ClxSpriteListOrSheet, data_, nullptr);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<OwnedClxSpriteList>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalOwnedClxSpriteList {
|
|
|
|
|
public:
|
|
|
|
|
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteList, OwnedClxSpriteList, data_, nullptr)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Equivalent to `std::optional<OwnedClxSpriteSheet>` but smaller.
|
|
|
|
|
*/
|
|
|
|
|
class OptionalOwnedClxSpriteSheet {
|
|
|
|
|
public:
|
|
|
|
|
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteSheet, OwnedClxSpriteSheet, data_, nullptr)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class OptionalOwnedClxSpriteListOrSheet {
|
|
|
|
|
public:
|
|
|
|
|
DEFINE_INTRUSIVE_OPTIONAL(OptionalOwnedClxSpriteListOrSheet, OwnedClxSpriteListOrSheet, data_, nullptr);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace devilution
|