It was tested for a long time.
The anti-cheat system was improved.
https://www.mediafire.com/file/ajr2mig7 ... hd.7z/file
https://www.mediafire.com/file/n1xk4eoy ... r.rar/file
https://www.mediafire.com/file/6flylv79 ... 26.7z/file
fix
Code:
MarkImage.cpp
#include "MarkImage.h"
#include <base/Crc32.hpp>
#include <lz4.h>
// #define STB_IMAGE_IMPLEMENTATION
#include "stb-master/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb-master/stb_image_write.h"
#include <vector>
CGuildMarkImage::CGuildMarkImage()
: m_aakBlock{} // m_aakBlock hala mevcut, kalabilir.
{
m_pPixels = nullptr; // Yeni eklediğimiz pointer'ı boş (null) olarak başlatıyoruz.
}
CGuildMarkImage::~CGuildMarkImage()
{
Destroy();
}
void CGuildMarkImage::Destroy()
{
if (m_pPixels)
{
free(m_pPixels);
m_pPixels = nullptr;
}
}
void CGuildMarkImage::Create()
{
if (m_pPixels)
return;
// Başlangıçta boş bir siyah resim oluştur
m_pPixels = (Pixel*)malloc(sizeof(Pixel) * WIDTH * HEIGHT);
memset(m_pPixels, 0, sizeof(Pixel) * WIDTH * HEIGHT);
}
bool CGuildMarkImage::Save(const char *c_szFileName)
{
if (!m_pPixels) return false;
// Kaydetmeden önce kanalları ters çevir (BGRA -> RGBA)
uint8_t* pRawByte = (uint8_t*)m_pPixels;
uint32_t totalPixels = WIDTH * HEIGHT;
for (uint32_t i = 0; i < totalPixels; ++i)
{
uint8_t tempB = pRawByte[i * 4 + 0];
pRawByte[i * 4 + 0] = pRawByte[i * 4 + 2];
pRawByte[i * 4 + 2] = tempB;
}
// STB ile kaydet
bool result = stbi_write_tga(c_szFileName, WIDTH, HEIGHT, 4, m_pPixels) != 0;
// Hafızadaki orijinal veriyi bozmamak için tekrar eski haline getir (RGBA -> BGRA)
for (uint32_t i = 0; i < totalPixels; ++i)
{
uint8_t tempR = pRawByte[i * 4 + 0];
pRawByte[i * 4 + 0] = pRawByte[i * 4 + 2];
pRawByte[i * 4 + 2] = tempR;
}
return result;
}
bool CGuildMarkImage::Build(const char *c_szFileName)
{
Destroy();
Create();
return Save(c_szFileName);
}
bool CGuildMarkImage::Load(const char *c_szFileName)
{
Destroy();
int width, height, channels;
// TGA veya başka formatları yükle. zorla 4 kanal (RGBA) al.
Pixel* loadedPixels = (Pixel*)stbi_load(c_szFileName, &width, &height, &channels, 4);
if (!loadedPixels)
{
// Dosya yoksa oluştur
if (!Build(c_szFileName)) return false;
return Load(c_szFileName); // Yeni oluşturulanı yükle
}
if (width != WIDTH || height != HEIGHT)
{
SPDLOG_ERROR("CGuildMarkImage: {0} size mismatch!", c_szFileName);
stbi_image_free(loadedPixels);
return false;
}
// --- RENK KANALI DÜZELTME (RGBA -> BGRA) ---
// STB veriyi RGBA yükler, ancak Metin2 BGRA bekler.
// R (Kırmızı) ve B (Mavi) kanallarının yerini değiştiriyoruz.
uint8_t* pRawByte = (uint8_t*)loadedPixels;
uint32_t totalPixels = width * height;
for (uint32_t i = 0; i < totalPixels; ++i)
{
uint8_t tempR = pRawByte[i * 4 + 0]; // İlk byte: R
pRawByte[i * 4 + 0] = pRawByte[i * 4 + 2]; // R yerine B yaz (Son byte)
pRawByte[i * 4 + 2] = tempR; // B yerine R yaz
}
// -------------------------------------------
m_pPixels = loadedPixels;
BuildAllBlocks();
return true;
}
// Resmin belirli bir bölgesine veri yazma
void CGuildMarkImage::PutData(uint32_t x, uint32_t y, uint32_t width, uint32_t height, void *data)
{
if (!m_pPixels || !data) return;
Pixel* pSrc = (Pixel*)data;
for (uint32_t i = 0; i < height; ++i)
{
// Y ekseni Metin2/DevIL mantığında bazen ters olabilir, duruma göre (y+i) veya (y+height-1-i) ayarlanır.
Pixel* pDestRow = m_pPixels + ((y + i) * WIDTH) + x;
memcpy(pDestRow, pSrc + (i * width), width * sizeof(Pixel));
}
}
void CGuildMarkImage::GetData(uint32_t x, uint32_t y, uint32_t width, uint32_t height, void *data)
{
if (!m_pPixels || !data) return;
Pixel* pDest = (Pixel*)data;
for (uint32_t i = 0; i < height; ++i)
{
Pixel* pSrcRow = m_pPixels + ((y + i) * WIDTH) + x;
memcpy(pDest + (i * width), pSrcRow, width * sizeof(Pixel));
}
}
// 이미지 = 512x512
// 블럭 = 마크 4 x 4
// 마크 = 16 x 12
// 한 이미지의 블럭 = 8 x 10
bool CGuildMarkImage::SaveBlockFromCompressedData(uint32_t posBlock, const uint8_t *pbComp, uint32_t dwCompSize)
{
if (posBlock >= BLOCK_TOTAL_COUNT)
return false;
Pixel apxBuf[SGuildMarkBlock::SIZE];
int32_t sizeBuf = LZ4_decompress_safe((const char *)pbComp, (char *)apxBuf, dwCompSize, sizeof(apxBuf));
if (sizeBuf < 0)
{
SPDLOG_ERROR("cannot decompress, compressed size = {0}", dwCompSize);
return false;
}
if (sizeBuf != sizeof(apxBuf))
{
SPDLOG_ERROR("image corrupted, decompressed size = {0}", sizeBuf);
return false;
}
uint32_t rowBlock = posBlock / BLOCK_COL_COUNT;
uint32_t colBlock = posBlock % BLOCK_COL_COUNT;
PutData(colBlock * SGuildMarkBlock::WIDTH,
rowBlock * SGuildMarkBlock::HEIGHT,
SGuildMarkBlock::WIDTH,
SGuildMarkBlock::HEIGHT,
apxBuf);
m_aakBlock[rowBlock][colBlock].CopyFrom(pbComp, dwCompSize,
ComputeCrc32(0, (const char *)apxBuf,
sizeof(Pixel) * SGuildMarkBlock::SIZE));
return true;
}
void CGuildMarkImage::BuildAllBlocks() // 이미지 전체를 블럭화
{
Pixel apxBuf[SGuildMarkBlock::SIZE];
SPDLOG_INFO("CGuildMarkImage::BuildAllBlocks");
for (uint32_t row = 0; row < BLOCK_ROW_COUNT; ++row)
{
for (uint32_t col = 0; col < BLOCK_COL_COUNT; ++col)
{
GetData(col * SGuildMarkBlock::WIDTH,
row * SGuildMarkBlock::HEIGHT,
SGuildMarkBlock::WIDTH,
SGuildMarkBlock::HEIGHT,
apxBuf);
m_aakBlock[row][col].Compress(apxBuf);
}
}
}
uint32_t CGuildMarkImage::GetEmptyPosition()
{
SGuildMark kMark{};
for (uint32_t row = 0; row < MARK_ROW_COUNT; ++row)
{
for (uint32_t col = 0; col < MARK_COL_COUNT; ++col)
{
GetData(col * SGuildMark::WIDTH,
row * SGuildMark::HEIGHT,
SGuildMark::WIDTH,
SGuildMark::HEIGHT,
kMark.m_apxBuf);
if (kMark.IsEmpty())
return (row * MARK_COL_COUNT + col);
}
}
return INVALID_MARK_POSITION;
}
void CGuildMarkImage::GetBlockCRCList(uint32_t *crcList)
{
for (uint32_t row = 0; row < BLOCK_ROW_COUNT; ++row)
for (uint32_t col = 0; col < BLOCK_COL_COUNT; ++col)
*(crcList++) = m_aakBlock[row][col].GetCRC();
}
////////////////////////////////////////////////////////////////////////////////
void SGuildMark::Clear()
{
for (uint32_t iPixel = 0; iPixel < SIZE; ++iPixel)
m_apxBuf[iPixel] = 0xff000000ul;
}
bool SGuildMark::IsEmpty()
{
for (uint32_t iPixel = 0; iPixel < SIZE; ++iPixel)
if (m_apxBuf[iPixel] != 0x00000000ul)
return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////
uint32_t SGuildMarkBlock::GetCRC() const
{
return m_crc;
}
void SGuildMarkBlock::CopyFrom(const uint8_t *pbCompBuf, uint32_t dwCompSize, uint32_t crc)
{
if (dwCompSize > LZ4_compressBound(sizeof(Pixel) * SGuildMarkBlock::SIZE))
return;
m_sizeCompBuf = dwCompSize;
memcpy(m_abCompBuf.data(), pbCompBuf, dwCompSize);
m_crc = crc;
}
void SGuildMarkBlock::Compress(const Pixel *pxBuf)
{
m_sizeCompBuf = LZ4_compressBound(sizeof(Pixel) * SGuildMarkBlock::SIZE);
m_abCompBuf.resize(m_sizeCompBuf);
m_sizeCompBuf = LZ4_compress_default(reinterpret_cast<const char *>(pxBuf),
reinterpret_cast<char *>(m_abCompBuf.data()),
sizeof(Pixel) * SGuildMarkBlock::SIZE, m_sizeCompBuf);
if (m_sizeCompBuf <= 0)
{
SPDLOG_ERROR("Compress: Error! {0} > {1}",
sizeof(Pixel) * SGuildMarkBlock::SIZE,
m_sizeCompBuf);
return;
}
m_crc = ComputeCrc32(0, (const char *)pxBuf,
sizeof(Pixel) * SGuildMarkBlock::SIZE);
}


