Import the AVA2 drop items the server lacks as ordinary 40250 items

tools/server/ava2-items stages the 478 items referenced by the AVA2 drop
tables that 203 does not have: server item_proto/item_names rows, a client
item_proto/item_list/itemdesc patch over the biolog tables, and the AVA2
icons, ground models (BitKnit gr2 via sdk/gr2) and their textures, packed
as ava2items ahead of biolog. Materials, quest items, chests, the guardian
weapons and shoes keep their meaning; items of AVA2-only systems (pets,
pendants, new rings, mounts, sashes, costume enchants, talisman items, new
skill books) become plain ITEM_NONE items until those systems are ported.
Names are translated in names_zh.tsv.

item_proto_patch is the full-record successor of biolog_proto_patch, with
--dump for checking conversions against the shipped table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-10-16 10:10:47 +09:00
co-authored by Claude Opus 5.5
parent d11d46c469
commit 90f213c4cc
7 changed files with 527 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
// PORT: adds or replaces whole item_proto records (all 156-byte fields); untouched records stay byte-identical.
// Row: vnum type sub size anti flags wear immune buy sell refined refine_set magic_pct
// limit0 value0 limit1 value1 apply0 value0 apply1 value1 apply2 value2 v0..v5 specular gain_socket \t name
#include "GameLib/StdAfx.h"
#include "GameLib/ItemData.h"
#include "EterBase/lzo.h"
#include <array>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <vector>
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 dump(const Item& r){
std::printf("%u %u %u %u %u %u %u %u %u %u %u %u %u",r.dwVnum,r.bType,r.bSubType,r.bSize,r.dwAntiFlags,r.dwFlags,r.dwWearFlags,r.dwImmuneFlag,
r.dwIBuyItemPrice,r.dwISellItemPrice,r.dwRefinedVnum,r.wRefineSet,r.bAlterToMagicItemPct);
for(const auto& limit:r.aLimits)std::printf(" %u %d",limit.bType,int(limit.lValue));
for(const auto& apply:r.aApplies)std::printf(" %u %d",apply.bType,int(apply.lValue));
for(auto value:r.alValues)std::printf(" %d",int(value));
std::printf(" %u %u\t%.*s\n",r.bSpecular,r.bGainSocketPct,int(strnlen(r.szLocaleName,sizeof(r.szLocaleName))),r.szLocaleName);
}
int main(int argc,char** argv) {
const bool dumping=argc==3&&!std::strcmp(argv[1],"--dump");
if(argc!=4&&!dumping){std::fprintf(stderr,"usage: item_proto_patch in_proto rows.tsv out_proto | --dump proto\n");return 2;}
if(dumping)argv[1]=argv[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;}
if(dumping){for(const auto& pair:rows)dump(pair.second);return 0;}
std::ifstream file(argv[2],std::ios::binary);std::string line;size_t added=0,replaced=0;
while(std::getline(file,line)) {
if(line.empty())continue;
auto tab=line.find('\t',0);std::string numbers=line,name;
// the name is the last tab-separated column
auto last=line.rfind('\t');if(last==std::string::npos)throw std::runtime_error("item row without name");
numbers=line.substr(0,last);name=line.substr(last+1);(void)tab;
std::istringstream stream(numbers);std::array<long long,31> f{};
for(auto& value:f)if(!(stream>>value))throw std::runtime_error("item row: "+line.substr(0,40));
long long extra;if(stream>>extra)throw std::runtime_error("item row has extra columns");
if(name.size()>CItemData::ITEM_NAME_MAX_LEN)throw std::runtime_error("item name too long");
Item r{};int i=0;
r.dwVnum=f[i++];r.bType=f[i++];r.bSubType=f[i++];r.bSize=f[i++];r.dwAntiFlags=f[i++];r.dwFlags=f[i++];
r.dwWearFlags=f[i++];r.dwImmuneFlag=f[i++];r.dwIBuyItemPrice=f[i++];r.dwISellItemPrice=f[i++];
r.dwRefinedVnum=f[i++];r.wRefineSet=f[i++];r.bAlterToMagicItemPct=f[i++];
for(auto& limit:r.aLimits){limit.bType=f[i++];limit.lValue=f[i++];}
for(auto& apply:r.aApplies){apply.bType=f[i++];apply.lValue=f[i++];}
for(auto& value:r.alValues)value=f[i++];
r.bSpecular=f[i++];r.bGainSocketPct=f[i++];
std::memcpy(r.szName,name.data(),name.size());std::memcpy(r.szLocaleName,name.data(),name.size());
(rows.count(r.dwVnum)?replaced:added)++;rows[r.dwVnum]=r;
}
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");
std::ofstream out(argv[3],std::ios::binary|std::ios::trunc);
if(!out.write(reinterpret_cast<const char*>(result.data()),result.size()))throw std::runtime_error("cannot write proto");
std::printf("items added=%zu replaced=%zu total=%zu; verified round-trip\n",added,replaced,rows.size());return 0;
}catch(const std::exception& e){std::fprintf(stderr,"%s\n",e.what());return 1;}
}