diff --git a/include/ki/pclass/HashCalculator.h b/include/ki/pclass/HashCalculator.h new file mode 100644 index 0000000..ac3b157 --- /dev/null +++ b/include/ki/pclass/HashCalculator.h @@ -0,0 +1,46 @@ +#pragma once +#include + +namespace ki +{ + namespace pclass + { + /** + * The type used to store a type/property hash. + */ + typedef uint32_t hash_t; + + /** + * A base class for type/property hash calculators. + */ + class HashCalculator + { + public: + virtual ~HashCalculator() {}; + + /** + * Calculate a type hash from the type's name. + * @param name The name of the type. + */ + virtual hash_t calculate_type_hash(const std::string &name) const = 0; + + /** + * Calculate a property hash from the property's name and type. + * @param name The name of the property. + * @param type_hash The hash of the property's type. + */ + virtual hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const = 0; + }; + + /** + * A hash calculator that uses the algorithms found and used in + * Wizard101. + */ + class WizardHashCalculator : public HashCalculator + { + public: + hash_t calculate_type_hash(const std::string& name) const override; + hash_t calculate_property_hash(const std::string& name, const hash_t type_hash) const override; + }; + } +} diff --git a/include/ki/pclass/PropertyClass.h b/include/ki/pclass/PropertyClass.h new file mode 100644 index 0000000..32cfd1b --- /dev/null +++ b/include/ki/pclass/PropertyClass.h @@ -0,0 +1,24 @@ +#pragma once + +namespace ki +{ +namespace pclass +{ + class Type; + + /** + * TODO: Documentation + */ + class PropertyClass + { + explicit PropertyClass(const Type &type) : m_type(type) {} + virtual ~PropertyClass() {}; + + const Type &get_type(); + + virtual void on_created(); + private: + const Type &m_type; + }; +} +} diff --git a/include/ki/pclass/Type.h b/include/ki/pclass/Type.h new file mode 100644 index 0000000..bc58549 --- /dev/null +++ b/include/ki/pclass/Type.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include +#include "ki/pclass/HashCalculator.h" +#include "ki/pclass/Value.h" +#include "ki/pclass/PropertyClass.h" + +namespace ki +{ + class BitStream; + +namespace pclass +{ + /** + * TODO: Documentation + */ + class Type + { + friend class TypeSystem; + + public: + /** + * TODO: Documentation + */ + enum class kind + { + NONE, + PRIMITIVE, + CLASS, + ENUM + }; + + Type(std::string name, hash_t hash); + virtual ~Type() {}; + + std::string get_name() const; + hash_t get_hash() const; + kind get_kind() const; + + virtual PropertyClass *instantiate() const; + virtual void write_to(BitStream &stream, const Value &value) const = 0; + virtual void read_from(BitStream &stream, Value &value) const = 0; + + protected: + kind m_kind; + + private: + std::string m_name; + hash_t m_hash; + + void set_hash(hash_t hash); + }; + + typedef std::vector TypeList; + typedef std::map TypeNameMap; + typedef std::map TypeHashMap; +} +} diff --git a/include/ki/pclass/TypeSystem.h b/include/ki/pclass/TypeSystem.h new file mode 100644 index 0000000..4135ab2 --- /dev/null +++ b/include/ki/pclass/TypeSystem.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include "Type.h" +#include "PrimitiveType.h" + +namespace ki +{ +namespace pclass +{ + /** + * TODO: Documentation + */ + class TypeSystem + { + public: + /** + * @return A singleton instance of TypeSystem with all C++ primitives defined. + */ + static TypeSystem &get_singleton(); + + explicit TypeSystem(HashCalculator *hash_calculator); + ~TypeSystem(); + + void set_hash_calculator(HashCalculator *hash_calculator); + + Type &get_type(const std::string &name) const; + Type &get_type(hash_t hash) const; + + template + Type &define_primitive(const std::string &name) + { + // TODO: Create primitive types + } + + template + Type &define_class(const std::string &name) + { + // Ensure that ClassT inherits PropertyClass + static_assert(std::is_base_of::value, "ClassT must inherit PropertyClass!"); + + // TODO: Create class types + } + + template + Type &define_enum(const std::string &name) + { + // Ensure that EnumT is an enum + static_assert(std::is_enum::value, "EnumT must be an enum!"); + + // TODO: Create enum types + } + + private: + static TypeSystem *s_instance; + + TypeList m_types; + TypeNameMap m_type_name_lookup; + TypeHashMap m_type_hash_lookup; + HashCalculator *m_hash_calculator; + }; +} +} diff --git a/include/ki/pclass/Value.h b/include/ki/pclass/Value.h new file mode 100644 index 0000000..1c67fa8 --- /dev/null +++ b/include/ki/pclass/Value.h @@ -0,0 +1,67 @@ +#pragma once +#include +#include +#include + +namespace ki +{ +namespace pclass +{ + /** + * A wrapper around a void pointer that ensures type safety. + */ + class Value + { + public: + template + Value(T &value) + { + m_value_ptr = value; + m_type_hash = typeid(value).hash_code(); + } + + Value(Value &&o) noexcept + : m_value_ptr(std::move(m_value_ptr)) + , m_type_hash(std::move(o.m_type_hash)) + {} + + /** + * @return Whether or not the value being held is of type T. + */ + template + bool is() const + { + // Do the type hashes match? + return m_type_hash == typeid(T).hash_code(); + } + + /** + * @return A reference to the value being held as the specified type. + */ + template + const T &get() const + { + // Make sure that this is allowed + if (!is()) + throw std::runtime_error("Type mismatch in Value::get() call."); + return *(T *)m_value_ptr; + } + + /** + * @return A reference to the value being held as the specified type. + */ + template + T &get() + { + // Make sure that this is allowed + if (!is()) + throw std::runtime_error("Type mismatch in Value::get() call."); + return *(T *)m_value_ptr; + } + + private: + void *m_value_ptr; + std::size_t m_type_hash; + }; +} +} \ No newline at end of file diff --git a/src/pclass/CMakeLists.txt b/src/pclass/CMakeLists.txt new file mode 100644 index 0000000..2e21a7b --- /dev/null +++ b/src/pclass/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${PROJECT_NAME} + PRIVATE + ${PROJECT_SOURCE_DIR}/src/pclass/HashCalculator.cpp + ${PROJECT_SOURCE_DIR}/src/pclass/Type.cpp + ${PROJECT_SOURCE_DIR}/src/pclass/TypeSystem.cpp +) \ No newline at end of file diff --git a/src/pclass/HashCalculator.cpp b/src/pclass/HashCalculator.cpp new file mode 100644 index 0000000..6fb7775 --- /dev/null +++ b/src/pclass/HashCalculator.cpp @@ -0,0 +1,50 @@ +#include "ki/pclass/HashCalculator.h" + +namespace ki +{ +namespace pclass +{ + hash_t WizardHashCalculator::calculate_type_hash(const std::string& name) const + { + // Declare variables + auto result = 0; + auto a = 0; + auto b = 32; + + // Iterate through the characters in the string + for (auto it = name.begin(); it != name.end(); ++it) + { + result ^= (*it - 32) << a; + if (a > 24) + { + result ^= (*it - 32) >> b; + if (a >= 27) + { + a -= 32; + b += 32; + } + } + + a += 5; + b -= 5; + } + + // Make the result an absolute value + if (result < 0) + result = -result; + return result; + } + + hash_t WizardHashCalculator::calculate_property_hash(const std::string& name, const hash_t type_hash) const + { + // Find the hash of the property name + hash_t result = 0x1505; + for (auto it = name.begin(); it != name.end(); ++it) + result = (0x21 * result) + *it; + result &= 0x7FFFFFFF; + + // Add the type hash onto it + return result + type_hash; + } +} +} \ No newline at end of file diff --git a/src/pclass/Type.cpp b/src/pclass/Type.cpp new file mode 100644 index 0000000..e90c8ce --- /dev/null +++ b/src/pclass/Type.cpp @@ -0,0 +1,43 @@ +#include "ki/pclass/Type.h" +#include +#include + +namespace ki +{ +namespace pclass +{ + Type::Type(const std::string name, const hash_t hash) + { + m_name = name; + m_hash = hash; + m_kind = kind::NONE; + } + + std::string Type::get_name() const + { + return m_name; + } + + void Type::set_hash(const hash_t hash) + { + m_hash = hash; + } + + hash_t Type::get_hash() const + { + return m_hash; + } + + Type::kind Type::get_kind() const + { + return m_kind; + } + + PropertyClass* Type::instantiate() const + { + std::ostringstream oss; + oss << "Type '" << m_name << "' cannot be instantiated."; + throw std::runtime_error(oss.str()); + } +} +} \ No newline at end of file diff --git a/src/pclass/TypeSystem.cpp b/src/pclass/TypeSystem.cpp new file mode 100644 index 0000000..be657aa --- /dev/null +++ b/src/pclass/TypeSystem.cpp @@ -0,0 +1,131 @@ +#include "ki/pclass/TypeSystem.h" +#include +#include + +#define DEFINE_INTEGER_PRIMTIIVE(st, ut, n) \ + s_instance->define_primitive(n); \ + s_instance->define_primitive("signed " n); \ + s_instance->define_primitive("unsigned " n) + +namespace ki +{ +namespace pclass +{ + TypeSystem& TypeSystem::get_singleton() + { + if (s_instance == nullptr) + { + // Create the static instance with the default hash calculator + s_instance = new TypeSystem(new WizardHashCalculator()); + + // Define integer types + s_instance->define_primitive("bool"); + DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "char"); + DEFINE_INTEGER_PRIMTIIVE(int8_t, uint8_t, "__int8"); + s_instance->define_primitive("int8_t"); + s_instance->define_primitive("uint8_t"); + + DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "short"); + DEFINE_INTEGER_PRIMTIIVE(int16_t, uint16_t, "__int16"); + s_instance->define_primitive("int16_t"); + s_instance->define_primitive("uint16_t"); + + DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "int"); + DEFINE_INTEGER_PRIMTIIVE(int32_t, uint32_t, "__int32"); + s_instance->define_primitive("int32_t"); + s_instance->define_primitive("uint32_t"); + + DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "long"); + DEFINE_INTEGER_PRIMTIIVE(int64_t, uint64_t, "__int64"); + s_instance->define_primitive("int64_t"); + s_instance->define_primitive("uint64_t"); + s_instance->define_primitive("gid"); + + // TODO: Define bit integer types + + // Define floating point types + s_instance->define_primitive("float"); + s_instance->define_primitive("double"); + + // TODO: Define bit floating point types + + // Define string types + s_instance->define_primitive("std::string"); + s_instance->define_primitive("std::wstring"); + } + + return *s_instance; + } + + TypeSystem::TypeSystem(HashCalculator* hash_calculator) + { + m_hash_calculator = hash_calculator; + } + + + TypeSystem::~TypeSystem() + { + // Delete all type declarations + for (auto it = m_types.begin(); it != m_types.end(); ++it) + delete *it; + + // Clear lookups + m_type_name_lookup.clear(); + m_type_hash_lookup.clear(); + + // Delete the hash calculator + delete m_hash_calculator; + } + + void TypeSystem::set_hash_calculator(HashCalculator* hash_calculator) + { + // Update the hash calculator + m_hash_calculator = hash_calculator; + + // Iterate through all types and recalculate their hash + m_type_hash_lookup.clear(); + for (auto it = m_types.begin(); it != m_types.end(); ++it) + { + // Calculate the new hash and update the type + auto *type = *it; + const auto new_hash = m_hash_calculator->calculate_type_hash(type->get_name()); + type->set_hash(new_hash); + + // Add the new hash to lookup + m_type_hash_lookup[new_hash] = type; + + // Is this type a class? + if (type->get_kind() == Type::kind::CLASS) + { + // TODO: Recalculate property hashes + } + } + } + + Type& TypeSystem::get_type(const std::string &name) const + { + const auto it = m_type_name_lookup.find(name); + if (it == m_type_name_lookup.end()) + { + std::ostringstream oss; + oss << "Could not find type with name '" << name << "'."; + throw std::runtime_error(oss.str()); + } + return *(it->second); + } + + Type& TypeSystem::get_type(const hash_t hash) const + { + const auto it = m_type_hash_lookup.find(hash); + if (it == m_type_hash_lookup.end()) + { + std::ostringstream oss; + oss << "Could not find type with hash: " << + std::hex << std::setw(8) << std::setfill('0') << + std::uppercase << hash << "."; + throw std::runtime_error(oss.str()); + } + return *(it->second); + } +} +} diff --git a/src/util/BitStream.cpp b/src/util/BitStream.cpp index 5f4284e..7040eaf 100644 --- a/src/util/BitStream.cpp +++ b/src/util/BitStream.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace ki { @@ -168,7 +168,7 @@ namespace ki void BitStream::expand_buffer() { // Work out a new buffer size - auto new_size = (2 << (uint64_t)log2(m_position.get_byte()) + 1) + 2; + auto new_size = (2 << (uint64_t)std::log2(m_position.get_byte()) + 1) + 2; if (new_size < m_buffer_size) new_size = std::numeric_limits::max();