Initial Character support for RTA

This commit is contained in:
HikikoMarmy
2025-06-15 00:19:00 +01:00
parent 08c6604f68
commit f54870cc1a
4 changed files with 297 additions and 0 deletions

145
Game/RealmCharacter.cpp Normal file
View File

@@ -0,0 +1,145 @@
#include "RealmCharacter.h"
#include "../../logging.h"
RealmCharacter::RealmCharacter( std::vector< uint8_t > &data )
{
ByteBuffer stream( data );
m_metaData.Deserialize( stream );
auto characterDataSize = stream.read_u32();
if( characterDataSize > 0 && characterDataSize < 1024 )
{
m_characterData = stream.read_bytes( characterDataSize );
}
}
RealmCharacter::RealmCharacter(sptr_byte_stream stream)
{
m_metaData.Deserialize(*stream);
auto characterDataSize = stream->read_u32();
if (characterDataSize > 0 && characterDataSize < 1024)
{
m_characterData = stream->read_bytes(characterDataSize);
}
}
const std::vector< uint8_t > RealmCharacter::GetCharacterData() const
{
return m_characterData;
}
void RealmCharacter::SetCharacterData( const std::vector<uint8_t> &data )
{
m_characterData = data;
}
const std::vector< uint8_t > RealmCharacter::GetInventoryData() const
{
return m_inventoryData;
}
void RealmCharacter::SetInventoryData( const std::vector<uint8_t> &data )
{
m_inventoryData = data;
}
RealmCharacterMetaData RealmCharacter::GetMetaData() const
{
return m_metaData;
}
void RealmCharacter::SetMetaData( const std::vector<uint8_t> &data )
{
ByteBuffer stream( data );
m_metaData.Deserialize( stream );
}
const std::vector<uint8_t> RealmCharacter::Unpack()
{
std::vector<uint8_t> output;
const int expectedOutputSize = 19504;
const size_t inputSize = m_characterData.size();
const uint8_t* input = m_characterData.data();
output.reserve(expectedOutputSize);
try
{
size_t i = 0;
while (i < inputSize && output.size() < expectedOutputSize) {
uint8_t byte = input[i++];
if (byte != 0x00) {
output.push_back(byte);
}
else {
if (i >= inputSize) {
throw std::runtime_error("Invalid blob format: zero-byte with no length");
}
uint8_t next = input[i++];
if (next == 0x00) {
// Escaped literal 0x00 byte
output.push_back(0x00);
}
else {
// Insert `next` zero bytes
output.insert(output.end(), next, 0x00);
}
}
}
}
catch (const std::exception& e)
{
Log::Error("Error unpacking character data: " + std::string(e.what()));
}
// Ensure we have exactly 19504 bytes
if (output.size() != expectedOutputSize)
{
Log::Error("Unpacked character data size mismatch: expected " + std::to_string(expectedOutputSize) + ", got " + std::to_string(output.size()));
}
return output;
}
std::vector<uint8_t> RealmCharacter::Pack(const std::vector<uint8_t>& input)
{
constexpr size_t ExpectedInputSize = 19504;
if (input.size() != ExpectedInputSize)
{
throw std::runtime_error("Input to compressor must be exactly 19504 bytes");
}
std::vector<uint8_t> output;
// Optional: add 4-byte header if game expects it (match decompress behavior)
output.insert(output.end(), { 0x00, 0x00, 0x00, 0x00 }); // Placeholder header
size_t i = 0;
while (i < input.size())
{
uint8_t byte = input[i];
if (byte != 0x00)
{
// Literal non-zero byte, just write it
output.push_back(byte);
++i;
}
else
{
// Zero run: count how many consecutive 0x00s (max 255)
size_t zeroCount = 0;
while (i < input.size() && input[i] == 0x00 && zeroCount < 255)
{
++zeroCount;
++i;
}
output.push_back(0x00); // Zero marker
output.push_back(static_cast<uint8_t>(zeroCount)); // Count
}
}
return output;
}

40
Game/RealmCharacter.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include "../Common/ByteStream.h"
#include "RealmCharacterMetaKV.h"
class RealmCharacter {
public:
// Character metadata, such as name, class, etc.
// This is used for displaying character information in the character selection screen.
RealmCharacterMetaData m_metaData;
// Character data blob, such as stats, skills, etc.
std::vector< uint8_t > m_characterData;
// Inventory data blob
std::vector< uint8_t > m_inventoryData;
public:
RealmCharacter() = default;
RealmCharacter( std::vector< uint8_t > &data );
RealmCharacter(sptr_byte_stream stream);
const std::vector< uint8_t > GetCharacterData() const;
void SetCharacterData( const std::vector< uint8_t > &data );
const std::vector< uint8_t > GetInventoryData() const;
void SetInventoryData( const std::vector< uint8_t > &data );
RealmCharacterMetaData GetMetaData() const;
void SetMetaData( const std::vector< uint8_t > &data );
const std::vector< uint8_t > Unpack();
std::vector< uint8_t > Pack(const std::vector<uint8_t>& input);
};
using sptr_realm_character = std::shared_ptr< RealmCharacter >;

View File

@@ -0,0 +1,67 @@
#include "RealmCharacterMetaKV.h"
#include "../Common/ByteStream.h"
#include "../Common/Utility.h"
#include "../logging.h"
RealmCharacterMetaData::RealmCharacterMetaData( const std::vector<uint8_t> &data )
{
ByteBuffer stream( data );
Deserialize( stream );
}
void RealmCharacterMetaData::Deserialize( ByteBuffer &stream )
{
auto numberOfKeys = stream.read_u32();
if( !Util::IsInRange< int32_t >( numberOfKeys, 0, 4 ) )
{
m_metaData.clear();
return;
}
m_metaData.clear();
m_metaData.reserve( numberOfKeys );
for( auto i = 0; i < numberOfKeys; ++i )
{
std::wstring key = stream.read_utf16();
m_metaData.emplace_back( key, L"" );
}
auto numberOfValues = stream.read_u32();
for( auto &pair : m_metaData )
{
pair.second = stream.read_utf16();
}
}
void RealmCharacterMetaData::Deserialize( const std::vector<uint8_t> &data )
{
ByteBuffer stream( data );
Deserialize( stream );
}
std::vector< uint8_t > RealmCharacterMetaData::Serialize() const
{
ByteBuffer stream;
// Number of keys
stream.write_u32( static_cast< int32_t >( m_metaData.size() ) );
for( const auto &pair : m_metaData )
{
stream.write_utf16( pair.first );
}
// Number of values
stream.write_u32( static_cast< uint32_t >( m_metaData.size() ) );
for( const auto &pair : m_metaData )
{
stream.write_utf16( pair.second );
}
return stream.m_buffer;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <string>
#include <vector>
#include <utility>
#include "../Common/ByteStream.h"
using CharacterMetaKV = std::pair<std::wstring, std::wstring>;
class RealmCharacterMetaData {
private:
std::vector<CharacterMetaKV> m_metaData;
public:
RealmCharacterMetaData() = default;
explicit RealmCharacterMetaData( const std::vector<uint8_t> &data );
void Deserialize( ByteBuffer &stream );
void Deserialize( const std::vector<uint8_t> &data );
std::vector<uint8_t> Serialize() const;
const std::vector<CharacterMetaKV> &GetMetaData() const
{
return m_metaData;
}
void SetMetaData( const std::vector<CharacterMetaKV> &metaData )
{
m_metaData = metaData;
}
std::wstring GetValue( std::wstring key )
{
for( const auto &kv : m_metaData )
{
if( kv.first == key )
{
return kv.second;
}
}
return L"";
}
};