mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-10 19:09:48 -03:00
Initial Character support for RTA
This commit is contained in:
145
Game/RealmCharacter.cpp
Normal file
145
Game/RealmCharacter.cpp
Normal 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
40
Game/RealmCharacter.h
Normal 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 >;
|
||||||
67
Game/RealmCharacterMetaKV.cpp
Normal file
67
Game/RealmCharacterMetaKV.cpp
Normal 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;
|
||||||
|
}
|
||||||
45
Game/RealmCharacterMetaKV.h
Normal file
45
Game/RealmCharacterMetaKV.h
Normal 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"";
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user