// Synthetic item_proto / mob_proto records. Locks the 40250 CItemData::TItemTable (156 bytes) and // CPythonNonPlayer::TMobTable (255 bytes) offsets without the client assets. #include "../src/proto/proto.h" #include #include #include #include static int g_fail = 0; #define CHECK(c, msg) \ do { \ if (!(c)) { \ std::fprintf(stderr, "FAIL: %s\n", msg); \ ++g_fail; \ } \ } while (0) template static void put_u32(std::array &record, size_t offset, uint32_t value) { record[offset + 0] = static_cast(value); record[offset + 1] = static_cast(value >> 8); record[offset + 2] = static_cast(value >> 16); record[offset + 3] = static_cast(value >> 24); } template static void put_u16(std::array &record, size_t offset, uint16_t value) { record[offset + 0] = static_cast(value); record[offset + 1] = static_cast(value >> 8); } static void item_layout() { std::array record{}; put_u32(record, 0, 12345); put_u32(record, 4, 7); std::memcpy(record.data() + 8, "TestItem", 9); std::memcpy(record.data() + 33, "LocaleItem", 11); record[58] = 3; record[59] = 4; record[60] = 2; record[61] = 1; put_u32(record, 62, 1u << 8); put_u32(record, 66, 1u << 3); put_u32(record, 70, 0x12345678); put_u32(record, 74, 0x55); put_u32(record, 78, 9000); put_u32(record, 82, 6000); record[86] = 1; put_u32(record, 87, 42); record[96] = 2; put_u32(record, 97, static_cast(-7)); put_u32(record, 111 + 3 * 4, 3); put_u32(record, 135, 1001); put_u32(record, 139, static_cast(-1)); put_u32(record, 147, 12346); put_u16(record, 151, 0x1234); record[153] = 11; record[154] = 75; record[155] = 88; const mtproto::ItemRecord item = mtproto::parse_item(record.data(), record.size()); CHECK(item.vnum == 12345, "vnum offset"); CHECK(item.vnum_range == 7, "vnum range offset"); CHECK(item.name == "TestItem", "name offset"); CHECK(item.locale_name == "LocaleItem", "locale name offset"); CHECK(item.type == 3 && item.sub_type == 4 && item.weight == 2 && item.size == 1, "type offsets"); CHECK(item.anti_flags == (1u << 8), "anti_flags offset"); CHECK(item.flags == (1u << 3), "flags offset"); CHECK(item.wear_flags == 0x12345678, "wear_flags offset"); CHECK(item.immune_flag == 0x55, "immune_flag offset"); CHECK(item.buy_price == 9000 && item.sell_price == 6000, "price offsets"); CHECK(item.limits[0].type == 1 && item.limits[0].value == 42, "limit offsets"); CHECK(item.applies[0].type == 2 && item.applies[0].value == -7, "apply offsets"); CHECK(item.values[3] == 3, "values offset"); CHECK(item.sockets[0] == 1001 && item.sockets[1] == -1, "socket offsets"); CHECK(item.refined_vnum == 12346 && item.refine_set == 0x1234, "refine offsets"); CHECK(item.alter_to_magic_pct == 11 && item.specular == 75 && item.gain_socket_pct == 88, "tail offsets"); CHECK(mtproto::parse_item(record.data(), 236).vnum == 0, "m2dev 236-byte stride rejected"); } static void mob_layout() { std::array record{}; put_u32(record, 0, 101); std::memcpy(record.data() + 4, "Wild Dog", 9); std::memcpy(record.data() + 29, "LocaleDog", 10); record[54] = 0; // bType record[55] = 1; // bRank record[56] = 2; // bBattleType record[57] = 45; // bLevel record[58] = 1; // bSize // 59 gold/exp/hp (16), 75 regen (2), 77 wDef, 79 AI/race/immune (12), 91 str..int (4), 95 damage (8) put_u16(record, 103, static_cast(-5)); // sAttackSpeed put_u16(record, 105, 120); // sMovingSpeed // 107 aggr pct, 108 sight, 110 range, 112 enchants[6], 118 resists[11], 129 resurrection, 133 drop record[137] = 3; // bMountCapacity record[138] = 1; // bOnClickType record[139] = 2; // bEmpire std::memcpy(record.data() + 140, "monster/dog", 12); // 205 fDamMultiply, 209 summon, 213 drain sp put_u32(record, 217, 0xAABBCCDD); // dwMonsterColor // 221 polymorph, 225 skills 5x5, 250 berserk..revive record[254] = 9; const mtproto::MobRecord m = mtproto::parse_mob(record.data(), record.size()); CHECK(m.vnum == 101, "mob vnum offset"); CHECK(m.name == "Wild Dog" && m.locale_name == "LocaleDog", "mob name offsets"); CHECK(m.rank == 1 && m.battle_type == 2 && m.level == 45 && m.size == 1, "mob byte offsets"); CHECK(m.attack_speed == -5 && m.moving_speed == 120, "mob speed offsets"); CHECK(m.on_click_type == 1 && m.empire == 2, "mob click/empire offsets"); CHECK(m.folder == "monster/dog", "mob folder offset"); CHECK(m.monster_color == 0xAABBCCDD, "mob color offset"); CHECK(mtproto::parse_mob(record.data(), 335).vnum == 0, "m2dev 335-byte stride rejected"); } int main() { item_layout(); mob_layout(); if (g_fail) { std::fprintf(stderr, "%d check(s) failed\n", g_fail); return 1; } std::printf("all item/mob layout checks passed\n"); return 0; }