Files
metin2-client/tests/port/biolog_proto_patch.cpp
T

61 lines
3.8 KiB
C++

// PORT: isolated item-table extension. All non-biology records stay byte-identical.
#include "GameLib/StdAfx.h"
#include "GameLib/ItemData.h"
#include "EterBase/lzo.h"
#include <array>
#include <cstring>
#include <fstream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <cstdio>
using Item=CItemData::TItemTable;
static constexpr std::array<DWORD,4> item_key{{173217,72619434,408587239,27973291}};
static std::vector<BYTE> read_file(const char* path){
std::ifstream stream(path,std::ios::binary);
if(!stream)throw std::runtime_error("cannot read proto");
return {std::istreambuf_iterator<char>(stream),std::istreambuf_iterator<char>()};
}
static DWORD word(const std::vector<BYTE>& data,size_t offset){
if(offset+4>data.size())throw std::runtime_error("truncated proto");
DWORD value;std::memcpy(&value,data.data()+offset,4);return value;
}
static void put_word(std::vector<BYTE>& data,size_t offset,DWORD value){std::memcpy(data.data()+offset,&value,4);}
static void write_file(const char* path,const std::vector<BYTE>& data){
std::ofstream stream(path,std::ios::binary|std::ios::trunc);
if(!stream.write(reinterpret_cast<const char*>(data.data()),data.size()))throw std::runtime_error("cannot write proto");
}
int main(int argc,char** argv) {
if(argc!=4)return 2;
try {
static CLZO lzo; auto key=item_key;
auto input=read_file(argv[1]);
if(input.size()<20 || std::memcmp(input.data(),"MIPX",4) || word(input,4)!=1 || word(input,8)!=sizeof(Item))throw std::runtime_error("item proto stride");
CLZObject decoded;
if(!CLZO::Instance().Decompress(decoded,input.data()+20,key.data()))throw std::runtime_error("item proto decode");
if(decoded.GetSize()!=size_t(word(input,12))*sizeof(Item))throw std::runtime_error("proto count/size mismatch");
std::map<DWORD,Item> rows;
for(DWORD i=0;i<word(input,12);++i){Item r;std::memcpy(&r,decoded.GetBuffer()+i*sizeof(Item),sizeof(Item));rows[r.dwVnum]=r;}
std::ifstream file(argv[2],std::ios::binary);std::string line;size_t changed=0;
while(std::getline(file,line)) {
std::istringstream stream(line); unsigned v,type,sub,size,anti,flags,buy,sell;
if(!(stream>>v>>type>>sub>>size>>anti>>flags>>buy>>sell))throw std::runtime_error("biology item row");
std::array<int,6> values{};for(auto& value:values)if(!(stream>>value))throw std::runtime_error("biology item values");
std::string name;std::getline(stream,name);if(!name.empty()&&name[0]=='\t')name.erase(0,1);
if(name.size()>24)throw std::runtime_error("biology item name too long");
Item r{};r.dwVnum=v;r.bType=type;r.bSubType=sub;r.bSize=size;r.dwAntiFlags=anti;r.dwFlags=flags;
r.dwIBuyItemPrice=buy;r.dwISellItemPrice=sell;
for(int i=0;i<6;++i)r.alValues[i]=values[i];
std::memcpy(r.szName,name.data(),name.size());std::memcpy(r.szLocaleName,name.data(),name.size());
rows[v]=r;++changed;
}
std::vector<BYTE> plain;for(const auto& pair:rows){const BYTE* bytes=reinterpret_cast<const BYTE*>(&pair.second);plain.insert(plain.end(),bytes,bytes+sizeof(Item));}
CLZObject encoded;if(!CLZO::Instance().CompressEncryptedMemory(encoded,plain.data(),plain.size(),key.data()))throw std::runtime_error("encode");
std::vector<BYTE> result(input.begin(),input.begin()+20);put_word(result,12,rows.size());put_word(result,16,encoded.GetSize());
result.insert(result.end(),encoded.GetBuffer(),encoded.GetBuffer()+encoded.GetSize());
CLZObject verified;if(!CLZO::Instance().Decompress(verified,result.data()+20,key.data()) || verified.GetSize()!=plain.size() || std::memcmp(verified.GetBuffer(),plain.data(),plain.size()))throw std::runtime_error("round-trip");
write_file(argv[3],result);std::printf("biology items=%zu total=%zu; verified round-trip\n",changed,rows.size());return 0;
}catch(const std::exception& e){std::fprintf(stderr,"%s\n",e.what());return 1;}
}