You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

344 lines
6.5 KiB

#pragma once
#include <memory>
#include <utility>
#include "appfat.h"
#include "engine/cel_header.hpp"
#include "utils/pointer_value_union.hpp"
#include "utils/stdcompat/cstddef.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {
class OwnedCelSprite;
class OptionalCelSprite;
/**
* Stores a CEL or CL2 sprite and its width(s).
* Does not own the data.
*/
class CelSprite {
public:
CelSprite(const byte *data, uint16_t width)
: data_ptr_(data)
, width_(width)
{
}
CelSprite(const byte *data, const uint16_t *widths)
: data_ptr_(data)
, width_(widths)
{
}
CelSprite(const byte *data, PointerOrValue<uint16_t> widths)
: data_ptr_(data)
, width_(widths)
{
}
explicit CelSprite(const OwnedCelSprite &owned);
CelSprite(const CelSprite &) = default;
CelSprite(CelSprite &&) noexcept = default;
CelSprite &operator=(const CelSprite &) = default;
CelSprite &operator=(CelSprite &&) noexcept = default;
[[nodiscard]] const byte *Data() const
{
return data_ptr_;
}
[[nodiscard]] uint16_t Width(std::size_t frame = 0) const
{
return width_.HoldsPointer() ? width_.AsPointer()[frame] : width_.AsValue();
}
[[nodiscard]] PointerOrValue<uint16_t> widthOrWidths() const
{
return width_;
}
[[nodiscard]] bool operator==(CelSprite other) const
{
return data_ptr_ == other.data_ptr_;
}
[[nodiscard]] bool operator!=(CelSprite other) const
{
return data_ptr_ != other.data_ptr_;
}
private:
// for OptionalCelSprite
CelSprite()
: data_ptr_(nullptr)
, width_(nullptr)
{
}
const byte *data_ptr_;
PointerOrValue<uint16_t> width_;
friend class OptionalCelSprite;
};
/**
* @brief Equivalent to `std::optional<CelSprite>` but smaller.
*/
class OptionalCelSprite {
public:
OptionalCelSprite() = default;
OptionalCelSprite(CelSprite sprite)
: sprite_(sprite)
{
}
explicit OptionalCelSprite(const OwnedCelSprite &owned);
OptionalCelSprite(std::nullopt_t)
: OptionalCelSprite()
{
}
template <typename... Args>
CelSprite &emplace(Args &&...args)
{
sprite_ = CelSprite(std::forward<Args>(args)...);
return sprite_;
}
OptionalCelSprite &operator=(CelSprite sprite)
{
sprite_ = sprite;
return *this;
}
OptionalCelSprite &operator=(std::nullopt_t)
{
sprite_ = {};
return *this;
}
CelSprite operator*() const
{
assert(sprite_.data_ptr_ != nullptr);
return sprite_;
}
CelSprite *operator->()
{
assert(sprite_.data_ptr_ != nullptr);
return &sprite_;
}
const CelSprite *operator->() const
{
assert(sprite_.data_ptr_ != nullptr);
return &sprite_;
}
operator bool() const
{
return sprite_.data_ptr_ != nullptr;
}
private:
CelSprite sprite_;
};
class OptionalOwnedCelSprite;
/**
* Stores a CEL or CL2 sprite and its width(s).
* Owns the data.
*/
class OwnedCelSprite {
public:
OwnedCelSprite(std::unique_ptr<byte[]> data, uint16_t width)
: data_(std::move(data))
, width_(width)
{
}
OwnedCelSprite(std::unique_ptr<byte[]> data, const uint16_t *widths)
: data_(std::move(data))
, width_(widths)
{
}
OwnedCelSprite(std::unique_ptr<byte[]> data, PointerOrValue<uint16_t> widths)
: data_(std::move(data))
, width_(widths)
{
}
OwnedCelSprite(OwnedCelSprite &&) noexcept = default;
OwnedCelSprite &operator=(OwnedCelSprite &&) noexcept = default;
[[nodiscard]] byte *MutableData()
{
return data_.get();
}
std::unique_ptr<byte[]> data() &&
{
return std::move(data_);
}
private:
// for OptionalOwnedCelSprite.
OwnedCelSprite()
: data_(nullptr)
, width_(nullptr)
{
}
std::unique_ptr<byte[]> data_;
PointerOrValue<uint16_t> width_;
friend class CelSprite;
friend class OptionalOwnedCelSprite;
};
/**
* @brief Equivalent to `std::optional<OwnedCelSprite>` but smaller.
*/
class OptionalOwnedCelSprite {
public:
OptionalOwnedCelSprite() = default;
OptionalOwnedCelSprite(OwnedCelSprite &&sprite)
: sprite_(std::move(sprite))
{
}
OptionalOwnedCelSprite(std::nullopt_t)
: OptionalOwnedCelSprite()
{
}
template <typename... Args>
OwnedCelSprite &emplace(Args &&...args)
{
sprite_ = OwnedCelSprite(std::forward<Args>(args)...);
return sprite_;
}
OptionalOwnedCelSprite &operator=(OwnedCelSprite &&sprite)
{
sprite_ = std::move(sprite);
return *this;
}
OptionalOwnedCelSprite &operator=(std::nullopt_t)
{
sprite_ = {};
return *this;
}
OwnedCelSprite &operator*()
{
assert(sprite_.data_ != nullptr);
return sprite_;
}
const OwnedCelSprite &operator*() const
{
assert(sprite_.data_ != nullptr);
return sprite_;
}
OwnedCelSprite *operator->()
{
assert(sprite_.data_ != nullptr);
return &sprite_;
}
const OwnedCelSprite *operator->() const
{
assert(sprite_.data_ != nullptr);
return &sprite_;
}
operator bool() const
{
return sprite_.data_ != nullptr;
}
private:
OwnedCelSprite sprite_;
};
inline CelSprite::CelSprite(const OwnedCelSprite &owned)
: CelSprite(owned.data_.get(), owned.width_)
{
}
inline OptionalCelSprite::OptionalCelSprite(const OwnedCelSprite &owned)
{
sprite_ = CelSprite { owned };
}
struct CelSpriteWithFrameHeight {
CelSprite sprite;
unsigned frameHeight;
};
struct CelFrameWithHeight {
CelSprite sprite;
uint16_t frameHeight;
uint16_t frame;
[[nodiscard]] uint16_t width() const
{
return sprite.Width(frame);
}
[[nodiscard]] uint16_t height() const
{
return frameHeight;
}
};
struct OwnedCelSpriteWithFrameHeight {
OwnedCelSprite ownedSprite;
uint16_t frameHeight;
[[nodiscard]] CelFrameWithHeight sprite() const
{
return { CelSprite { ownedSprite }, frameHeight, 0 };
}
};
struct CelSpriteSheetWithFrameHeight {
CelSprite sheet;
uint16_t frameHeight;
[[nodiscard]] unsigned numFrames() const
{
return LoadLE32(sheet.Data());
}
[[nodiscard]] CelFrameWithHeight sprite(uint16_t frame) const
{
return { CelSprite { sheet }, frameHeight, frame };
}
};
struct OwnedCelSpriteSheetWithFrameHeight {
OwnedCelSprite ownedSprite;
uint16_t frameHeight;
[[nodiscard]] CelSpriteSheetWithFrameHeight sheet() const
{
return CelSpriteSheetWithFrameHeight { CelSprite { ownedSprite }, frameHeight };
}
[[nodiscard]] CelFrameWithHeight sprite(uint16_t frame) const
{
return CelFrameWithHeight { CelSprite { ownedSprite }, frameHeight, frame };
}
};
} // namespace devilution