2024-11-22 00:37:50 +00:00
|
|
|
#ifndef AMF3_H
|
|
|
|
#define AMF3_H
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
#include "dCommonVars.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2023-05-13 22:22:00 +00:00
|
|
|
#include "Game.h"
|
2024-11-21 08:05:29 +00:00
|
|
|
#include "GeneralUtils.h"
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
enum class eAmf : uint8_t {
|
|
|
|
Undefined = 0x00, // An undefined AMF Value
|
|
|
|
Null = 0x01, // A null AMF value
|
|
|
|
False = 0x02, // A false AMF value
|
|
|
|
True = 0x03, // A true AMF value
|
|
|
|
Integer = 0x04, // An integer AMF value
|
|
|
|
Double = 0x05, // A double AMF value
|
|
|
|
String = 0x06, // A string AMF value
|
|
|
|
XMLDoc = 0x07, // Unused in the live client and cannot be serialized without modification. An XML Doc AMF value
|
|
|
|
Date = 0x08, // Unused in the live client and cannot be serialized without modification. A date AMF value
|
|
|
|
Array = 0x09, // An array AMF value
|
|
|
|
Object = 0x0A, // Unused in the live client and cannot be serialized without modification. An object AMF value
|
|
|
|
XML = 0x0B, // Unused in the live client and cannot be serialized without modification. An XML AMF value
|
|
|
|
ByteArray = 0x0C, // Unused in the live client and cannot be serialized without modification. A byte array AMF value
|
|
|
|
VectorInt = 0x0D, // Unused in the live client and cannot be serialized without modification. An integer vector AMF value
|
|
|
|
VectorUInt = 0x0E, // Unused in the live client and cannot be serialized without modification. An unsigned integer AMF value
|
|
|
|
VectorDouble = 0x0F, // Unused in the live client and cannot be serialized without modification. A double vector AMF value
|
|
|
|
VectorObject = 0x10, // Unused in the live client and cannot be serialized without modification. An object vector AMF value
|
|
|
|
Dictionary = 0x11 // Unused in the live client and cannot be serialized without modification. A dictionary AMF value
|
|
|
|
};
|
|
|
|
|
|
|
|
class AMFBaseValue {
|
|
|
|
public:
|
2024-02-10 19:44:40 +00:00
|
|
|
[[nodiscard]] constexpr virtual eAmf GetValueType() const noexcept { return eAmf::Undefined; }
|
|
|
|
constexpr AMFBaseValue() noexcept = default;
|
|
|
|
constexpr virtual ~AMFBaseValue() noexcept = default;
|
2023-05-13 22:22:00 +00:00
|
|
|
};
|
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
// AMFValue template class instantiations
|
|
|
|
template <typename ValueType>
|
2023-05-13 22:22:00 +00:00
|
|
|
class AMFValue : public AMFBaseValue {
|
|
|
|
public:
|
2024-02-10 19:44:40 +00:00
|
|
|
AMFValue() = default;
|
2024-02-25 05:03:59 +00:00
|
|
|
AMFValue(const ValueType value) : m_Data{ value } {}
|
2024-02-27 07:29:51 +00:00
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
virtual ~AMFValue() override = default;
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
|
|
|
|
|
|
|
|
[[nodiscard]] const ValueType& GetValue() const { return m_Data; }
|
2024-02-25 05:03:59 +00:00
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
void SetValue(const ValueType value) { m_Data = value; }
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
protected:
|
2024-02-10 19:44:40 +00:00
|
|
|
ValueType m_Data;
|
2023-05-13 22:22:00 +00:00
|
|
|
};
|
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
// Explicit template class instantiations
|
2024-02-25 05:03:59 +00:00
|
|
|
template class AMFValue<std::nullptr_t>;
|
2024-02-10 19:44:40 +00:00
|
|
|
template class AMFValue<bool>;
|
|
|
|
template class AMFValue<int32_t>;
|
|
|
|
template class AMFValue<uint32_t>;
|
|
|
|
template class AMFValue<std::string>;
|
|
|
|
template class AMFValue<double>;
|
|
|
|
|
|
|
|
// AMFValue template class member function instantiations
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<std::nullptr_t>::GetValueType() const noexcept { return eAmf::Null; }
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<bool>::GetValueType() const noexcept { return m_Data ? eAmf::True : eAmf::False; }
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<int32_t>::GetValueType() const noexcept { return eAmf::Integer; }
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<uint32_t>::GetValueType() const noexcept { return eAmf::Integer; }
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<std::string>::GetValueType() const noexcept { return eAmf::String; }
|
|
|
|
template <> [[nodiscard]] constexpr eAmf AMFValue<double>::GetValueType() const noexcept { return eAmf::Double; }
|
|
|
|
|
|
|
|
template <typename ValueType>
|
|
|
|
[[nodiscard]] constexpr eAmf AMFValue<ValueType>::GetValueType() const noexcept { return eAmf::Undefined; }
|
|
|
|
|
|
|
|
using AMFNullValue = AMFValue<std::nullptr_t>;
|
|
|
|
using AMFBoolValue = AMFValue<bool>;
|
|
|
|
using AMFIntValue = AMFValue<int32_t>;
|
|
|
|
using AMFStringValue = AMFValue<std::string>;
|
|
|
|
using AMFDoubleValue = AMFValue<double>;
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-11-22 00:37:50 +00:00
|
|
|
// Template deduction guide to ensure string literals deduce
|
2024-11-23 00:33:04 +00:00
|
|
|
AMFValue(const char*) -> AMFValue<std::string>; // AMFStringValue
|
2024-11-22 00:37:50 +00:00
|
|
|
|
2023-05-13 22:22:00 +00:00
|
|
|
/**
|
|
|
|
* The AMFArrayValue object holds 2 types of lists:
|
|
|
|
* An associative list where a key maps to a value
|
|
|
|
* A Dense list where elements are stored back to back
|
|
|
|
*
|
|
|
|
* Objects that are Registered are owned by this object
|
|
|
|
* and are not to be deleted by a caller.
|
|
|
|
*/
|
|
|
|
class AMFArrayValue : public AMFBaseValue {
|
2024-11-19 02:45:24 +00:00
|
|
|
using AMFAssociative =
|
2024-11-23 02:14:00 +00:00
|
|
|
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<void>>;
|
2024-11-19 02:45:24 +00:00
|
|
|
|
|
|
|
using AMFDense = std::vector<std::unique_ptr<AMFBaseValue>>;
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
public:
|
2024-02-10 19:44:40 +00:00
|
|
|
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the Associative portion of the object
|
|
|
|
*/
|
2024-02-25 05:03:59 +00:00
|
|
|
[[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return m_Associative; }
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the dense portion of the object
|
|
|
|
*/
|
2024-02-25 05:03:59 +00:00
|
|
|
[[nodiscard]] inline const AMFDense& GetDense() const noexcept { return m_Dense; }
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts an AMFValue into the associative portion with the given key.
|
|
|
|
* If a duplicate is attempted to be inserted, it is ignored and the
|
|
|
|
* first value with that key is kept in the map.
|
|
|
|
*
|
|
|
|
* These objects are not to be deleted by the caller as they are owned by
|
|
|
|
* the AMFArray object which manages its own memory.
|
|
|
|
*
|
|
|
|
* @param key The key to associate with the value
|
|
|
|
* @param value The value to insert
|
|
|
|
*
|
|
|
|
* @return The inserted element if the type matched,
|
|
|
|
* or nullptr if a key existed and was not the same type
|
|
|
|
*/
|
2024-11-23 00:33:04 +00:00
|
|
|
template <typename T>
|
|
|
|
[[maybe_unused]] auto Insert(const std::string_view key, const T value) -> std::pair<decltype(AMFValue(value))*, bool> {
|
|
|
|
// This ensures the deduced type matches the AMFValue constructor
|
|
|
|
using AMFValueType = decltype(AMFValue(value));
|
|
|
|
|
2024-02-25 05:03:59 +00:00
|
|
|
const auto element = m_Associative.find(key);
|
2024-11-23 00:33:04 +00:00
|
|
|
AMFValueType* val = nullptr;
|
2023-05-13 22:22:00 +00:00
|
|
|
bool found = true;
|
2024-02-25 05:03:59 +00:00
|
|
|
if (element == m_Associative.cend()) {
|
2024-11-23 00:33:04 +00:00
|
|
|
auto newVal = std::make_unique<AMFValueType>(value);
|
2024-11-19 02:45:24 +00:00
|
|
|
val = newVal.get();
|
|
|
|
m_Associative.emplace(key, std::move(newVal));
|
2023-05-13 22:22:00 +00:00
|
|
|
} else {
|
2024-11-23 00:33:04 +00:00
|
|
|
val = dynamic_cast<AMFValueType*>(element->second.get());
|
2023-05-13 22:22:00 +00:00
|
|
|
found = false;
|
|
|
|
}
|
|
|
|
return std::make_pair(val, found);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
// Associates an array with a string key
|
2024-11-19 02:45:24 +00:00
|
|
|
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string_view key) {
|
2024-02-25 05:03:59 +00:00
|
|
|
const auto element = m_Associative.find(key);
|
2023-05-13 22:22:00 +00:00
|
|
|
AMFArrayValue* val = nullptr;
|
|
|
|
bool found = true;
|
2024-02-25 05:03:59 +00:00
|
|
|
if (element == m_Associative.cend()) {
|
2024-11-19 02:45:24 +00:00
|
|
|
auto newVal = std::make_unique<AMFArrayValue>();
|
|
|
|
val = newVal.get();
|
|
|
|
m_Associative.emplace(key, std::move(newVal));
|
2023-05-13 22:22:00 +00:00
|
|
|
} else {
|
2024-11-19 02:45:24 +00:00
|
|
|
val = dynamic_cast<AMFArrayValue*>(element->second.get());
|
2023-05-13 22:22:00 +00:00
|
|
|
found = false;
|
|
|
|
}
|
|
|
|
return std::make_pair(val, found);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
// Associates an array with an integer key
|
2024-02-10 19:44:40 +00:00
|
|
|
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
|
2023-05-13 22:22:00 +00:00
|
|
|
bool inserted = false;
|
2024-02-25 05:03:59 +00:00
|
|
|
if (index >= m_Dense.size()) {
|
|
|
|
m_Dense.resize(index + 1);
|
2024-11-19 02:45:24 +00:00
|
|
|
m_Dense.at(index) = std::make_unique<AMFArrayValue>();
|
2023-05-13 22:22:00 +00:00
|
|
|
inserted = true;
|
|
|
|
}
|
2024-11-19 02:45:24 +00:00
|
|
|
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()), inserted);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Inserts an AMFValue into the AMFArray key'd by index.
|
|
|
|
* Attempting to insert the same key to the same value twice overwrites
|
|
|
|
* the previous value with the new one.
|
|
|
|
*
|
|
|
|
* @param index The index to associate with the value
|
|
|
|
* @param value The value to insert
|
|
|
|
* @return The inserted element, or nullptr if the type did not match
|
|
|
|
* what was at the index.
|
|
|
|
*/
|
2024-11-23 00:33:04 +00:00
|
|
|
template <typename T>
|
|
|
|
[[maybe_unused]] auto Insert(const size_t index, const T value) -> std::pair<decltype(AMFValue(value))*, bool> {
|
|
|
|
// This ensures the deduced type matches the AMFValue constructor
|
|
|
|
using AMFValueType = decltype(AMFValue(value));
|
|
|
|
|
2023-05-13 22:22:00 +00:00
|
|
|
bool inserted = false;
|
2024-02-25 05:03:59 +00:00
|
|
|
if (index >= m_Dense.size()) {
|
|
|
|
m_Dense.resize(index + 1);
|
2024-11-23 00:33:04 +00:00
|
|
|
m_Dense.at(index) = std::make_unique<AMFValueType>(value);
|
2023-05-13 22:22:00 +00:00
|
|
|
inserted = true;
|
|
|
|
}
|
2024-11-23 00:33:04 +00:00
|
|
|
return std::make_pair(dynamic_cast<AMFValueType*>(m_Dense.at(index).get()), inserted);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts an AMFValue into the associative portion with the given key.
|
|
|
|
* If a duplicate is attempted to be inserted, it replaces the original
|
|
|
|
*
|
|
|
|
* The inserted element is now owned by this object and is not to be deleted
|
|
|
|
*
|
|
|
|
* @param key The key to associate with the value
|
|
|
|
* @param value The value to insert
|
|
|
|
*/
|
2024-11-19 02:45:24 +00:00
|
|
|
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
|
2024-02-25 05:03:59 +00:00
|
|
|
const auto element = m_Associative.find(key);
|
|
|
|
if (element != m_Associative.cend() && element->second) {
|
2024-11-19 02:45:24 +00:00
|
|
|
element->second = std::move(value);
|
2023-05-13 22:22:00 +00:00
|
|
|
} else {
|
2024-11-19 02:45:24 +00:00
|
|
|
m_Associative.emplace(key, std::move(value));
|
2023-05-13 22:22:00 +00:00
|
|
|
}
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts an AMFValue into the associative portion with the given index.
|
|
|
|
* If a duplicate is attempted to be inserted, it replaces the original
|
|
|
|
*
|
|
|
|
* The inserted element is now owned by this object and is not to be deleted
|
|
|
|
*
|
|
|
|
* @param key The key to associate with the value
|
|
|
|
* @param value The value to insert
|
|
|
|
*/
|
2024-11-19 02:45:24 +00:00
|
|
|
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
|
|
|
|
if (index >= m_Dense.size()) {
|
2024-02-25 05:03:59 +00:00
|
|
|
m_Dense.resize(index + 1);
|
2023-05-13 22:22:00 +00:00
|
|
|
}
|
2024-11-19 02:45:24 +00:00
|
|
|
m_Dense.at(index) = std::move(value);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Pushes an AMFValue into the back of the dense portion.
|
|
|
|
*
|
|
|
|
* These objects are not to be deleted by the caller as they are owned by
|
|
|
|
* the AMFArray object which manages its own memory.
|
|
|
|
*
|
|
|
|
* @param value The value to insert
|
|
|
|
*
|
|
|
|
* @return The inserted pointer, or nullptr should the key already be in use.
|
|
|
|
*/
|
2024-11-23 00:33:04 +00:00
|
|
|
template <typename T>
|
|
|
|
[[maybe_unused]] inline auto Push(const T value) -> decltype(AMFValue(value))* {
|
2024-02-25 05:03:59 +00:00
|
|
|
return Insert(m_Dense.size(), value).first;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the key from the associative portion
|
|
|
|
*
|
|
|
|
* The pointer removed is now no longer managed by this container
|
|
|
|
*
|
|
|
|
* @param key The key to remove from the associative portion
|
|
|
|
*/
|
2024-02-10 19:44:40 +00:00
|
|
|
void Remove(const std::string& key, const bool deleteValue = true) {
|
2024-02-25 05:03:59 +00:00
|
|
|
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
|
|
if (it != m_Associative.cend()) {
|
2024-11-19 02:45:24 +00:00
|
|
|
if (deleteValue) m_Associative.erase(it);
|
2023-05-13 22:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pops the last element in the dense portion, deleting it in the process.
|
|
|
|
*/
|
2024-02-10 19:44:40 +00:00
|
|
|
void Remove(const size_t index) {
|
2024-02-25 05:03:59 +00:00
|
|
|
if (!m_Dense.empty() && index < m_Dense.size()) {
|
|
|
|
const auto itr = m_Dense.cbegin() + index;
|
|
|
|
m_Dense.erase(itr);
|
2023-05-13 22:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pop() {
|
2024-02-25 05:03:59 +00:00
|
|
|
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
|
2023-05-13 22:22:00 +00:00
|
|
|
}
|
|
|
|
|
2024-11-19 02:45:24 +00:00
|
|
|
[[nodiscard]] AMFArrayValue* GetArray(const std::string_view key) const {
|
2024-02-25 05:03:59 +00:00
|
|
|
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
2024-11-19 02:45:24 +00:00
|
|
|
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second.get()) : nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-02-18 06:38:26 +00:00
|
|
|
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
|
2024-11-19 02:45:24 +00:00
|
|
|
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()) : nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-11-19 02:45:24 +00:00
|
|
|
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string_view key) {
|
2023-05-13 22:22:00 +00:00
|
|
|
return static_cast<AMFArrayValue*>(Insert(key).first);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
[[maybe_unused]] inline AMFArrayValue* InsertArray(const size_t index) {
|
2023-05-13 22:22:00 +00:00
|
|
|
return static_cast<AMFArrayValue*>(Insert(index).first);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
2024-02-10 19:44:40 +00:00
|
|
|
[[maybe_unused]] inline AMFArrayValue* PushArray() {
|
2024-02-25 05:03:59 +00:00
|
|
|
return static_cast<AMFArrayValue*>(Insert(m_Dense.size()).first);
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets an AMFValue by the key from the associative portion and converts it
|
|
|
|
* to the AmfValue template type. If the key did not exist, it is inserted.
|
|
|
|
*
|
|
|
|
* @tparam The target object type
|
|
|
|
* @param key The key to lookup
|
|
|
|
*
|
|
|
|
* @return The AMFValue
|
|
|
|
*/
|
|
|
|
template <typename AmfType>
|
2024-11-19 02:45:24 +00:00
|
|
|
[[nodiscard]] AMFValue<AmfType>* Get(const std::string_view key) const {
|
2024-02-25 05:03:59 +00:00
|
|
|
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
|
|
|
return it != m_Associative.cend() ?
|
2024-11-19 02:45:24 +00:00
|
|
|
dynamic_cast<AMFValue<AmfType>*>(it->second.get()) :
|
2023-05-13 22:22:00 +00:00
|
|
|
nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
// Get from the array but dont cast it
|
2024-11-19 02:45:24 +00:00
|
|
|
[[nodiscard]] AMFBaseValue* Get(const std::string_view key) const {
|
2024-02-25 05:03:59 +00:00
|
|
|
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
2024-11-19 02:45:24 +00:00
|
|
|
return it != m_Associative.cend() ? it->second.get() : nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get an AMFValue object at a position in the dense portion.
|
|
|
|
* Gets an AMFValue by the index from the dense portion and converts it
|
|
|
|
* to the AmfValue template type. If the index did not exist, it is inserted.
|
|
|
|
*
|
|
|
|
* @tparam The target object type
|
|
|
|
* @param index The index to get
|
|
|
|
* @return The casted object, or nullptr.
|
|
|
|
*/
|
|
|
|
template <typename AmfType>
|
2024-02-10 19:44:40 +00:00
|
|
|
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
|
2024-02-25 05:03:59 +00:00
|
|
|
return index < m_Dense.size() ?
|
2024-11-19 02:45:24 +00:00
|
|
|
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index).get()) :
|
2023-05-13 22:22:00 +00:00
|
|
|
nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
// Get from the dense but dont cast it
|
2024-02-10 19:44:40 +00:00
|
|
|
[[nodiscard]] AMFBaseValue* Get(const size_t index) const {
|
2024-11-19 02:45:24 +00:00
|
|
|
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
|
2024-02-10 19:44:40 +00:00
|
|
|
}
|
|
|
|
|
2023-05-13 22:22:00 +00:00
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* The associative portion. These values are key'd with strings to an AMFValue.
|
|
|
|
*/
|
2024-02-25 05:03:59 +00:00
|
|
|
AMFAssociative m_Associative;
|
2023-05-13 22:22:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The dense portion. These AMFValue's are stored one after
|
|
|
|
* another with the most recent addition being at the back.
|
|
|
|
*/
|
2024-02-25 05:03:59 +00:00
|
|
|
AMFDense m_Dense;
|
2023-05-13 22:22:00 +00:00
|
|
|
};
|
|
|
|
|
2024-11-22 00:37:50 +00:00
|
|
|
#endif //!AMF3_H
|