mirror of
https://github.com/HikikoMarmy/Champions-Reborn-Server.git
synced 2026-04-05 08:59:54 -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