Merge remote-tracking branch 'origin/sync/upstream-20260507'
This commit is contained in:
@@ -30,8 +30,6 @@
|
||||
|
||||
## PSOBB
|
||||
|
||||
- Figure out why Pouilly Slime EXP doesn't work
|
||||
- Make server-specified rare enemies work with maps loaded by the proxy
|
||||
- Implement serialization for various table types (ItemPMT, ItemPT, etc.)
|
||||
- Record some BB tests
|
||||
- Add all necessary Guild Card number rewrites in BB commands on the proxy
|
||||
|
||||
@@ -81,12 +81,14 @@ Disable item equip restrictions ("God of equip")
|
||||
3OJ5 => 041050D4 38000005
|
||||
3OJT => 0415BF50 38000005
|
||||
3OP0 => 041052D4 38000005
|
||||
59NJ => 005C9F35 E9A7000000
|
||||
59NL => 005C9F31 E9A7000000
|
||||
|
||||
All items visible in Pioneer 2
|
||||
3OE1 => 04102D88 38600000
|
||||
|
||||
Mags visible in Pioneer 2
|
||||
59NJ => 005D8F27 EB04
|
||||
59NL => 005D8F4B EB04
|
||||
|
||||
Disable pause menu background + offset
|
||||
@@ -94,6 +96,9 @@ Disable pause menu background + offset
|
||||
0428735C 4800000C
|
||||
3OE2 => 0424CED8 48000370
|
||||
042887D8 4800000C
|
||||
59NJ => 00719C58 9090
|
||||
00733C57 9090
|
||||
00733ABE 90E9
|
||||
59NL => 00719B54 9090
|
||||
00733BA7 9090
|
||||
00733A0E 90E9
|
||||
@@ -637,6 +642,8 @@ Fast tekker (skips wind-up jingle)
|
||||
0023EF77 jmp +0x0A
|
||||
4OPU => 0023F14C mov dword [ebp + 0x14C], 1
|
||||
0023F167 jmp +0x0A
|
||||
59NJ => 006DA14B mov dword [edi + 0x14C], 1
|
||||
006DA168 jmp +0x0B
|
||||
59NL => 006DA113 mov dword [edi + 0x14C], 1
|
||||
006DA130 jmp +0x0B
|
||||
|
||||
@@ -973,6 +980,8 @@ Override Challenge mode random enemy location tables limit
|
||||
4OEU => 002E742C XXXXXXXX (count as little-endian dword)
|
||||
4OPD => 002E720C XXXXXXXX (count as little-endian dword)
|
||||
4OPU => 002E745C XXXXXXXX (count as little-endian dword)
|
||||
59NJ => 0080FA3F XXXXXXXX (count * 4 as little-endian dword)
|
||||
0080FA58 XXXXXXXX (count as little-endian dword)
|
||||
59NL => 0080ECB7 XXXXXXXX (count * 4 as little-endian dword)
|
||||
0080ECD0 XXXXXXXX (count as little-endian dword)
|
||||
|
||||
|
||||
+1
-1
@@ -486,7 +486,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParam
|
||||
if (should_encode_v2_data && (this->data1[1] > 0x26)) {
|
||||
if (this->data1[1] < 0x89) {
|
||||
this->data1[5] = this->data1[1];
|
||||
this->data1[1] = item_parameter_table->get_weapon_class(this->data1[1]);
|
||||
this->data1[1] = item_parameter_table->get_weapon_kind(this->data1[1]);
|
||||
if (this->data1[1] == 0x00) {
|
||||
this->data1[1] = 0x0F;
|
||||
}
|
||||
|
||||
+195
-11
@@ -1,5 +1,7 @@
|
||||
#include "ItemNameIndex.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "StaticGameData.hh"
|
||||
|
||||
using namespace std;
|
||||
@@ -787,9 +789,9 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
auto pmt = this->item_parameter_table;
|
||||
|
||||
phosg::fwrite_fmt(stream, "WEAPONS\n");
|
||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CR --A1-- A4 A5 TB BF CL ST* USL ---DIVISOR--- NAME\n");
|
||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CR --A1-- A4 A5 TB(TN:FL:AMOUNT, ... ) BF CL ST* USL ---DIVISOR--- NAME\n");
|
||||
for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes(); data1_1++) {
|
||||
uint8_t weapon_class = pmt->get_weapon_class(data1_1);
|
||||
uint8_t weapon_class = pmt->get_weapon_kind(data1_1);
|
||||
float sale_divisor = pmt->get_sale_divisor(0x00, data1_1);
|
||||
string divisor_str = std::format("{:g}", sale_divisor);
|
||||
divisor_str.resize(13, ' ');
|
||||
@@ -806,8 +808,24 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
item.data1[2] = data1_2;
|
||||
string name = this->describe_item(item);
|
||||
|
||||
auto& stat_boost = pmt->get_stat_boost(w.stat_boost_entry_index);
|
||||
phosg::fwrite_fmt(stream, " 00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}{:02X}{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:2}* {} {} {}\n",
|
||||
const auto& stat_boost = pmt->get_stat_boost(w.stat_boost_entry_index);
|
||||
|
||||
string tech_boost_str;
|
||||
if (w.tech_boost_entry_index < pmt->num_tech_boosts()) {
|
||||
const auto& tech_boost = pmt->get_tech_boost(w.tech_boost_entry_index);
|
||||
tech_boost_str = std::format("({:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g})",
|
||||
tech_boost.tech_num1, tech_boost.flags1, tech_boost.amount1, tech_boost.tech_num2, tech_boost.flags2,
|
||||
tech_boost.amount2, tech_boost.tech_num3, tech_boost.flags3, tech_boost.amount3);
|
||||
}
|
||||
tech_boost_str.resize(40, ' ');
|
||||
|
||||
phosg::fwrite_fmt(stream,
|
||||
// CODE => ID TYPE SKIN PTS FLAG ATP- ATP+ ATPR MSTR ATAR MST GND PH SP ATA
|
||||
" 00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} "
|
||||
// SB( S1:AMT1 , S2:AMT2 ) PJ 1X 1Y 2X 2Y CR --------A1-------- A4
|
||||
"{:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}{:02X}{:02X} {:02X} "
|
||||
// A5 TB( TN: FL: AMT, TN: FL: AMT, TN: FL: AMT) BF CL ST* US DV NAME\n"
|
||||
"{:02X} {:02X}{} {:02X} {:02X} {:2}* {} {} {}\n",
|
||||
data1_1,
|
||||
data1_2,
|
||||
w.id,
|
||||
@@ -841,7 +859,8 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
w.unknown_a1[2],
|
||||
w.unknown_a4,
|
||||
w.unknown_a5,
|
||||
w.tech_boost,
|
||||
w.tech_boost_entry_index,
|
||||
tech_boost_str,
|
||||
w.behavior_flags,
|
||||
weapon_class,
|
||||
stars,
|
||||
@@ -852,7 +871,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "ARMORS\n");
|
||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS -DFP- -EVP- BP BE FLAG LVL EFR ETH EIC EDK ELT DFR EVR SB(S1:AMT1,S2:AMT2) TB FT A4 ST* ---DIVISOR--- NAME\n");
|
||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS -DFP- -EVP- BP BE FLAG LVL EFR ETH EIC EDK ELT DFR EVR SB(S1:AMT1,S2:AMT2) TB(TN:FL:AMOUNT, ... ) FT A4 ST* ---DIVISOR--- NAME\n");
|
||||
for (size_t data1_1 = 1; data1_1 < 3; data1_1++) {
|
||||
float sale_divisor = pmt->get_sale_divisor(0x01, data1_1);
|
||||
string divisor_str = std::format("{:g}", sale_divisor);
|
||||
@@ -870,7 +889,17 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
string name = this->describe_item(item);
|
||||
|
||||
auto& stat_boost = pmt->get_stat_boost(a.stat_boost_entry_index);
|
||||
phosg::fwrite_fmt(stream, " 01{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:5} {:02X} {:02X} {:04X} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:2}* {} {}\n",
|
||||
|
||||
string tech_boost_str;
|
||||
if (a.tech_boost_entry_index < pmt->num_tech_boosts()) {
|
||||
const auto& tech_boost = pmt->get_tech_boost(a.tech_boost_entry_index);
|
||||
tech_boost_str = std::format("({:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g},{:02X}:{:02X}:{:g})",
|
||||
tech_boost.tech_num1, tech_boost.flags1, tech_boost.amount1, tech_boost.tech_num2, tech_boost.flags2,
|
||||
tech_boost.amount2, tech_boost.tech_num3, tech_boost.flags3, tech_boost.amount3);
|
||||
}
|
||||
tech_boost_str.resize(40, ' ');
|
||||
|
||||
phosg::fwrite_fmt(stream, " 01{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:5} {:02X} {:02X} {:04X} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X}{} {:02X} {:02X} {:2}* {} {}\n",
|
||||
data1_1,
|
||||
data1_2,
|
||||
a.id,
|
||||
@@ -895,7 +924,8 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
stat_boost.amount1,
|
||||
stat_boost.stat2,
|
||||
stat_boost.amount2,
|
||||
a.tech_boost,
|
||||
a.tech_boost_entry_index,
|
||||
tech_boost_str,
|
||||
a.flags_type,
|
||||
a.unknown_a4,
|
||||
stars,
|
||||
@@ -1040,7 +1070,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "SPECIAL DEFINITIONS\n");
|
||||
phosg::fwrite_fmt(stream, " SPECIAL => TYPE COUNT ST* NAME\n");
|
||||
phosg::fwrite_fmt(stream, " SP => TYPE COUNT ST* NAME\n");
|
||||
for (size_t index = 0; index < pmt->num_specials(); index++) {
|
||||
const auto& sp = pmt->get_special(index);
|
||||
uint8_t stars = pmt->get_special_stars(index);
|
||||
@@ -1051,12 +1081,12 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:04X} {:5} {:2}* {}\n", index, sp.type, sp.amount, stars, name);
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:04X} {:5} {:2}* {}\n", index, sp.type, sp.amount, stars, name);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "ITEM COMBINATIONS\n");
|
||||
phosg::fwrite_fmt(stream, " ---USE + -EQUIP => RESULT MLV GND LVL CLS\n");
|
||||
for (const auto& combo_list_it : pmt->get_all_item_combinations()) {
|
||||
for (const auto& combo_list_it : pmt->item_combinations_index()) {
|
||||
for (const auto& combo : combo_list_it.second) {
|
||||
phosg::fwrite_fmt(stream, " {:02X}{:02X}{:02X} + {:02X}{:02X}{:02X} => {:02X}{:02X}{:02X}",
|
||||
combo.used_item[0], combo.used_item[1], combo.used_item[2],
|
||||
@@ -1096,4 +1126,158 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
||||
event_item.item[0], event_item.item[1], event_item.item[2], event_item.probability);
|
||||
}
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "PHOTON COLORS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => ---A1--- (A2) (A3)\n");
|
||||
for (size_t z = 0; z < pmt->num_photon_colors(); z++) {
|
||||
const auto& pc = pmt->get_photon_color(z);
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:08X} ({:g}, {:g}, {:g}, {:g}) ({:g}, {:g}, {:g}, {:g})\n",
|
||||
z, pc.unknown_a1, pc.unknown_a2.x, pc.unknown_a2.y, pc.unknown_a2.z, pc.unknown_a2.t,
|
||||
pc.unknown_a3.x, pc.unknown_a3.y, pc.unknown_a3.z, pc.unknown_a3.t);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "WEAPON RANGES\n");
|
||||
phosg::fwrite_fmt(stream, " ## => ---A3--- ---A4--- ---A5--- (A1) (A2)\n");
|
||||
for (size_t z = 0; z < pmt->num_weapon_ranges(); z++) {
|
||||
const auto& wr = pmt->get_weapon_range(z);
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:08X} {:08X} {:08X} ({:g}) ({:g})\n",
|
||||
z, wr.unknown_a3, wr.unknown_a4, wr.unknown_a5, wr.unknown_a1, wr.unknown_a2);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "SALE DIVISORS\n");
|
||||
phosg::fwrite_fmt(stream, " ARMOR = {:g}\n", pmt->get_sale_divisor(1, 1));
|
||||
phosg::fwrite_fmt(stream, " SHIELD = {:g}\n", pmt->get_sale_divisor(1, 2));
|
||||
phosg::fwrite_fmt(stream, " UNIT = {:g}\n", pmt->get_sale_divisor(1, 3));
|
||||
phosg::fwrite_fmt(stream, " MAG = {:g}\n", pmt->get_sale_divisor(2, 0));
|
||||
|
||||
auto write_data_string = [&](const std::string& data, size_t addr = 0) -> void {
|
||||
if (data.empty()) {
|
||||
phosg::fwrite_fmt(stream, " (no data)\n");
|
||||
} else {
|
||||
auto data_str = phosg::format_data(data, addr);
|
||||
phosg::strip_trailing_whitespace(data_str);
|
||||
phosg::fwrite_fmt(stream, " {}\n", phosg::str_replace_all(data_str, "\n", "\n "));
|
||||
}
|
||||
};
|
||||
|
||||
phosg::fwrite_fmt(stream, "STAR VALUES\n");
|
||||
write_data_string(pmt->get_star_value_table(), pmt->get_star_value_index_range().first);
|
||||
|
||||
phosg::fwrite_fmt(stream, "UNKNOWN_A1\n");
|
||||
write_data_string(pmt->get_unknown_a1());
|
||||
|
||||
phosg::fwrite_fmt(stream, "WEAPON EFFECTS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => -SOUND1- -VALUE1- -SOUND2- -VALUE2- ----------------A5---------------\n");
|
||||
for (size_t z = 0; z < pmt->num_weapon_effects(); z++) {
|
||||
const auto& we = pmt->get_weapon_effect(z);
|
||||
auto a5_str = phosg::format_data_string(we.unknown_a5.data(), we.unknown_a5.size());
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:08X} {:08X} {:08X} {:08X} {}\n",
|
||||
z, we.sound_id1, we.eff_value1, we.sound_id2, we.eff_value2, a5_str);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "WEAPON STAT BOOST INDEX TABLE\n");
|
||||
write_data_string(pmt->get_weapon_stat_boost_index_table());
|
||||
|
||||
phosg::fwrite_fmt(stream, "ARMOR STAT BOOST INDEX TABLE\n");
|
||||
write_data_string(pmt->get_armor_stat_boost_index_table());
|
||||
|
||||
phosg::fwrite_fmt(stream, "SHIELD STAT BOOST INDEX TABLE\n");
|
||||
write_data_string(pmt->get_shield_stat_boost_index_table());
|
||||
|
||||
phosg::fwrite_fmt(stream, "STAT BOOSTS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => BOOSTS\n");
|
||||
for (size_t z = 0; z < pmt->num_stat_boosts(); z++) {
|
||||
const auto& sb = pmt->get_stat_boost(z);
|
||||
static constexpr std::array<const char*, 0x10> stat_names{
|
||||
"ATP+", "ATA+", "EVP+", "DFP+", "MST+", "HP+", "LCK+", "ALL+",
|
||||
"ATP-", "ATA-", "EVP-", "DFP-", "MST-", "HP-", "LCK-", "ALL-"};
|
||||
string s;
|
||||
if (sb.stat1 > 0x10) {
|
||||
s = std::format("[{:02X}:{:04X}]", sb.stat1, sb.amount1);
|
||||
} else if (sb.stat1 > 0) {
|
||||
s = std::format("{}{}", stat_names[sb.stat1 - 1], sb.amount1);
|
||||
}
|
||||
if (sb.stat2) {
|
||||
if (!s.empty()) {
|
||||
s += ", ";
|
||||
}
|
||||
if (sb.stat2 > 0x10) {
|
||||
s += std::format("[{:02X}:{:04X}]", sb.stat2, sb.amount2);
|
||||
} else if (sb.stat2 > 0) {
|
||||
s += std::format("{}{}", stat_names[sb.stat2 - 1], sb.amount2);
|
||||
}
|
||||
}
|
||||
if (s.empty()) {
|
||||
s = "(none)";
|
||||
}
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {}\n", z, s);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "SHIELD EFFECTS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => -SOUND1- ---A1---\n");
|
||||
for (size_t z = 0; z < pmt->num_shield_effects(); z++) {
|
||||
const auto& se = pmt->get_shield_effect(z);
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:08X} {:08X}\n", z, se.sound_id, se.unknown_a1);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "SOUND REMAPS\n");
|
||||
phosg::fwrite_fmt(stream, " -SOUND1- => RT:[...] CC:[...]\n");
|
||||
for (const auto& remap : pmt->get_all_sound_remaps()) {
|
||||
std::string rt_str;
|
||||
for (uint32_t rt_sound_id : remap.by_rt_index) {
|
||||
if (!rt_str.empty()) {
|
||||
rt_str += ",";
|
||||
}
|
||||
rt_str += std::format("{:08X}", rt_sound_id);
|
||||
}
|
||||
std::string cc_str;
|
||||
for (uint32_t cc_sound_id : remap.by_char_class) {
|
||||
if (!cc_str.empty()) {
|
||||
cc_str += ",";
|
||||
}
|
||||
cc_str += std::format("{:08X}", cc_sound_id);
|
||||
}
|
||||
phosg::fwrite_fmt(stream, " {:08X} => RT:[{}] CC:[{}]\n", remap.sound_id, rt_str, cc_str);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "TECH BOOSTS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => BOOSTS\n");
|
||||
for (size_t z = 0; z < pmt->num_tech_boosts(); z++) {
|
||||
const auto& tb = pmt->get_tech_boost(z);
|
||||
string s;
|
||||
if (tb.amount1) {
|
||||
s += std::format("{:02X}:{:02X}:{:g}", tb.tech_num1, tb.flags1, tb.amount1);
|
||||
}
|
||||
if (tb.amount2) {
|
||||
s += std::format("{:02X}:{:02X}:{:g}", tb.tech_num2, tb.flags2, tb.amount2);
|
||||
}
|
||||
if (tb.amount3) {
|
||||
s += std::format("{:02X}:{:02X}:{:g}", tb.tech_num3, tb.flags3, tb.amount3);
|
||||
}
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {}\n", z, s);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "UNSEALABLE ITEMS\n");
|
||||
phosg::fwrite_fmt(stream, " -ITEM- NAME\n");
|
||||
std::vector<uint32_t> unsealable_items;
|
||||
for (uint32_t item_code : pmt->all_unsealable_items()) {
|
||||
unsealable_items.emplace_back(item_code);
|
||||
}
|
||||
std::sort(unsealable_items.begin(), unsealable_items.end());
|
||||
for (uint32_t item_code : unsealable_items) {
|
||||
ItemData item;
|
||||
item.data1[0] = item_code >> 16;
|
||||
item.data1[1] = item_code >> 8;
|
||||
item.data1[2] = item_code;
|
||||
string name = this->describe_item(item);
|
||||
phosg::fwrite_fmt(stream, " {:06X} {}\n", item_code, name);
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "RANGED SPECIALS\n");
|
||||
phosg::fwrite_fmt(stream, " ## => 11 12 WR A1\n");
|
||||
for (size_t z = 0; z < pmt->num_ranged_specials(); z++) {
|
||||
const auto& rs = pmt->get_ranged_special(z);
|
||||
phosg::fwrite_fmt(stream, " {:02X} => {:02X} {:02X} {:02X} {:02X}\n",
|
||||
z, rs.data1_1, rs.data1_2, rs.weapon_range_index, rs.unknown_a1);
|
||||
}
|
||||
}
|
||||
|
||||
+2329
-279
File diff suppressed because it is too large
Load Diff
+207
-67
@@ -51,6 +51,9 @@ public:
|
||||
uint16_t type = 0; // "Model" in Soly's ItemPMT editor
|
||||
uint16_t skin = 0; // "Texture" in Soly's ItemPMT editor
|
||||
uint32_t team_points = 0;
|
||||
|
||||
void parse_base_from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
struct Weapon : ItemBase {
|
||||
uint16_t class_flags = 0;
|
||||
@@ -64,7 +67,8 @@ public:
|
||||
uint8_t photon = 0;
|
||||
uint8_t special = 0;
|
||||
uint8_t ata = 0;
|
||||
uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits)
|
||||
uint8_t stat_boost_entry_index = 0;
|
||||
parray<uint8_t, 3> v2_unknown_a9;
|
||||
uint8_t projectile = 0;
|
||||
int8_t trail1_x = 0;
|
||||
int8_t trail1_y = 0;
|
||||
@@ -74,13 +78,16 @@ public:
|
||||
parray<uint8_t, 3> unknown_a1 = 0;
|
||||
uint8_t unknown_a4 = 0;
|
||||
uint8_t unknown_a5 = 0;
|
||||
uint8_t tech_boost = 0;
|
||||
uint8_t tech_boost_entry_index = 0;
|
||||
// Bits in behavior_flags:
|
||||
// 01 = disable combos (weapon can only be used once in a row)
|
||||
// 02 = TODO (sets TItemWeapon flag 40000; used in TItemWeapon_v1E)
|
||||
// 04 = TODO (sets TItemWeapon flag 80000; used in TItemWeapon_v1E)
|
||||
// 08 = weapon cannot have attributes (they are ignored if present)
|
||||
uint8_t behavior_flags = 0;
|
||||
|
||||
static Weapon from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct ArmorOrShield : ItemBase {
|
||||
@@ -98,7 +105,7 @@ public:
|
||||
uint8_t dfp_range = 0;
|
||||
uint8_t evp_range = 0;
|
||||
uint8_t stat_boost_entry_index = 0;
|
||||
uint8_t tech_boost = 0;
|
||||
uint8_t tech_boost_entry_index = 0;
|
||||
// TODO: Figure out what this does. Only a few values appear to do anything:
|
||||
// Armors:
|
||||
// 01 sets item->flags |= 1 (used in TItemProArmor_v10)
|
||||
@@ -108,12 +115,18 @@ public:
|
||||
// 03 sets item->flags |= 8 (used in TItemProShield_v1A)
|
||||
uint8_t flags_type = 0;
|
||||
uint8_t unknown_a4 = 0;
|
||||
|
||||
static ArmorOrShield from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct Unit : ItemBase {
|
||||
uint16_t stat = 0;
|
||||
uint16_t stat_amount = 0;
|
||||
int16_t modifier_amount = 0;
|
||||
|
||||
static Unit from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct Mag : ItemBase {
|
||||
@@ -142,6 +155,9 @@ public:
|
||||
uint8_t on_death_flag = 0;
|
||||
uint8_t on_boss_flag = 0;
|
||||
uint16_t class_flags = 0x00FF;
|
||||
|
||||
static Mag from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct Tool : ItemBase {
|
||||
@@ -157,7 +173,10 @@ public:
|
||||
// 00000020 - usable in boss arenas
|
||||
// 00000040 - usable in Challenge mode
|
||||
// 00000080 - is rare (renders as red box; V3+ only)
|
||||
/* 0C */ uint32_t item_flags = 0;
|
||||
uint32_t item_flags = 0;
|
||||
|
||||
static Tool from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct MagFeedResult {
|
||||
@@ -168,11 +187,17 @@ public:
|
||||
int8_t iq = 0;
|
||||
int8_t synchro = 0;
|
||||
parray<uint8_t, 2> unused;
|
||||
|
||||
static MagFeedResult from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __packed_ws__(MagFeedResult, 8);
|
||||
|
||||
struct Special {
|
||||
uint16_t type = 0xFFFF;
|
||||
uint16_t amount = 0;
|
||||
|
||||
static Special from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct StatBoost {
|
||||
@@ -201,6 +226,9 @@ public:
|
||||
uint16_t amount1 = 0;
|
||||
uint8_t stat2 = 0;
|
||||
uint16_t amount2 = 0;
|
||||
|
||||
static StatBoost from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Indexed as [tech_num][char_class]
|
||||
@@ -215,23 +243,41 @@ public:
|
||||
uint8_t level = 0;
|
||||
uint8_t char_class = 0;
|
||||
parray<uint8_t, 3> unused;
|
||||
|
||||
static ItemCombination from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __packed_ws__(ItemCombination, 0x10);
|
||||
|
||||
struct TechniqueBoost {
|
||||
uint8_t tech_num = 0;
|
||||
// It appears that only one bit in the flags field is used: 01 = enable piercing (for Megid)
|
||||
uint8_t flags = 0;
|
||||
float amount = 0.0f;
|
||||
struct TechBoost {
|
||||
// It appears that only one bit in the flags fields is used: 01 = enable piercing (for Megid)
|
||||
uint8_t tech_num1 = 0;
|
||||
uint8_t flags1 = 0;
|
||||
float amount1 = 0.0f;
|
||||
uint8_t tech_num2 = 0;
|
||||
uint8_t flags2 = 0;
|
||||
float amount2 = 0.0f;
|
||||
uint8_t tech_num3 = 0;
|
||||
uint8_t flags3 = 0;
|
||||
float amount3 = 0.0f;
|
||||
|
||||
static TechBoost from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct EventItem {
|
||||
parray<uint8_t, 3> item;
|
||||
uint8_t probability = 0;
|
||||
|
||||
static EventItem from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __packed_ws__(EventItem, 4);
|
||||
|
||||
struct UnsealableItem {
|
||||
parray<uint8_t, 3> item;
|
||||
uint8_t unused = 0;
|
||||
|
||||
static UnsealableItem from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __packed_ws__(UnsealableItem, 4);
|
||||
|
||||
struct NonWeaponSaleDivisors {
|
||||
@@ -239,119 +285,213 @@ public:
|
||||
float shield_divisor = 0.0f;
|
||||
float unit_divisor = 0.0f;
|
||||
float mag_divisor = 0.0f;
|
||||
|
||||
static NonWeaponSaleDivisors from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct ShieldEffect {
|
||||
uint32_t sound_id;
|
||||
uint32_t unknown_a1;
|
||||
uint32_t sound_id = 0;
|
||||
uint32_t unknown_a1 = 0;
|
||||
|
||||
static ShieldEffect from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct PhotonColorEntry {
|
||||
uint32_t unknown_a1;
|
||||
uint32_t unknown_a1 = 0;
|
||||
VectorXYZTF unknown_a2;
|
||||
VectorXYZTF unknown_a3;
|
||||
|
||||
static PhotonColorEntry from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct UnknownA1 {
|
||||
uint16_t unknown_a1;
|
||||
uint16_t unknown_a2;
|
||||
};
|
||||
uint16_t unknown_a1 = 0;
|
||||
uint16_t unknown_a2 = 0;
|
||||
|
||||
struct UnknownA5 {
|
||||
uint32_t target_param; // For players, char_class; for enemies, rt_index; for objects, 0x30
|
||||
uint32_t unknown_a2;
|
||||
uint32_t unknown_a3;
|
||||
static UnknownA1 from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct WeaponEffect {
|
||||
uint32_t sound_id1;
|
||||
uint32_t eff_value1;
|
||||
uint32_t sound_id2;
|
||||
uint32_t eff_value2;
|
||||
uint32_t sound_id1 = 0;
|
||||
uint32_t eff_value1 = 0;
|
||||
uint32_t sound_id2 = 0;
|
||||
uint32_t eff_value2 = 0;
|
||||
parray<uint8_t, 0x10> unknown_a5;
|
||||
|
||||
static WeaponEffect from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct WeaponRange {
|
||||
float unknown_a1;
|
||||
float unknown_a2;
|
||||
uint32_t unknown_a3; // Angle
|
||||
uint32_t unknown_a4; // Angle
|
||||
uint32_t unknown_a5;
|
||||
float unknown_a1 = 0;
|
||||
float unknown_a2 = 0;
|
||||
uint32_t unknown_a3 = 0; // Angle
|
||||
uint32_t unknown_a4 = 0; // Angle
|
||||
uint32_t unknown_a5 = 0;
|
||||
|
||||
static WeaponRange from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
struct RangedSpecial {
|
||||
uint8_t data1_1;
|
||||
uint8_t data1_2;
|
||||
uint8_t weapon_range_index;
|
||||
uint8_t unknown_a1;
|
||||
uint8_t data1_1 = 0;
|
||||
uint8_t data1_2 = 0;
|
||||
uint8_t weapon_range_index = 0;
|
||||
uint8_t unknown_a1 = 0;
|
||||
|
||||
static RangedSpecial from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
} __packed_ws__(RangedSpecial, 4);
|
||||
|
||||
ItemParameterTable() = delete;
|
||||
struct SoundRemaps {
|
||||
uint32_t sound_id = 0;
|
||||
std::vector<uint32_t> by_rt_index;
|
||||
std::vector<uint32_t> by_char_class;
|
||||
|
||||
static SoundRemaps from_json(const phosg::JSON& json);
|
||||
phosg::JSON json() const;
|
||||
};
|
||||
|
||||
virtual ~ItemParameterTable() = default;
|
||||
|
||||
static std::shared_ptr<ItemParameterTable> create(std::shared_ptr<const std::string> data, Version version);
|
||||
static std::shared_ptr<ItemParameterTable> from_binary(std::shared_ptr<const std::string> data, Version version);
|
||||
static std::shared_ptr<ItemParameterTable> from_json(const phosg::JSON& json);
|
||||
|
||||
phosg::JSON json() const;
|
||||
std::string serialize_binary(Version version) const;
|
||||
|
||||
std::set<uint32_t> compute_all_valid_primary_identifiers() const;
|
||||
|
||||
// weapon_table accessors
|
||||
virtual size_t num_weapon_classes() const = 0;
|
||||
virtual size_t num_weapons_in_class(uint8_t data1_1) const = 0;
|
||||
virtual const Weapon& get_weapon(uint8_t data1_1, uint8_t data1_2) const = 0;
|
||||
|
||||
// armor_table accessors
|
||||
virtual size_t num_armors_or_shields_in_class(uint8_t data1_1) const = 0;
|
||||
virtual const ArmorOrShield& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const = 0;
|
||||
|
||||
// unit_table accessors
|
||||
virtual size_t num_units() const = 0;
|
||||
virtual const Unit& get_unit(uint8_t data1_2) const = 0;
|
||||
virtual size_t num_mags() const = 0;
|
||||
virtual const Mag& get_mag(uint8_t data1_1) const = 0;
|
||||
|
||||
// tool_table accessors
|
||||
virtual size_t num_tool_classes() const = 0;
|
||||
virtual size_t num_tools_in_class(uint8_t data1_1) const = 0;
|
||||
virtual const Tool& get_tool(uint8_t data1_1, uint8_t data1_2) const = 0;
|
||||
virtual std::pair<uint8_t, uint8_t> find_tool_by_id(uint32_t id) const = 0;
|
||||
|
||||
std::variant<const Weapon*, const ArmorOrShield*, const Unit*, const Mag*, const Tool*>
|
||||
definition_for_primary_identifier(uint32_t primary_identifier) const;
|
||||
// mag_table accessors
|
||||
virtual size_t num_mags() const = 0;
|
||||
virtual const Mag& get_mag(uint8_t data1_1) const = 0;
|
||||
|
||||
// weapon_kind_table accessors (data1_1 in [0, num_weapon_classes()])
|
||||
virtual size_t num_weapon_kinds() const = 0;
|
||||
virtual uint8_t get_weapon_kind(uint8_t data1_1) const = 0;
|
||||
|
||||
// photon_color_table accessors
|
||||
virtual size_t num_photon_colors() const = 0;
|
||||
virtual const PhotonColorEntry& get_photon_color(size_t index) const = 0;
|
||||
|
||||
// weapon_range_table accessors
|
||||
virtual size_t num_weapon_ranges() const = 0;
|
||||
virtual const WeaponRange& get_weapon_range(size_t index) const = 0;
|
||||
|
||||
// weapon_sale_divisor_table and non_weapon_sale_divisor_table accessors (data1_0 in [0, 1, 2]; data1_1 in [0,
|
||||
// num_weapon_classes()] for weapons or ignored otherwise)
|
||||
virtual size_t num_weapon_sale_divisors() const = 0;
|
||||
virtual float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const = 0;
|
||||
virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t which) const = 0;
|
||||
|
||||
// mag_feed_table accessors (table_index in [0, 7], item_index in [0, 10])
|
||||
virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t item_index) const = 0;
|
||||
|
||||
// star_value_table accessors
|
||||
virtual std::pair<uint32_t, uint32_t> get_star_value_index_range() const = 0;
|
||||
virtual uint32_t get_special_stars_base_index() const = 0;
|
||||
virtual uint8_t get_item_stars(uint32_t id) const = 0;
|
||||
virtual uint8_t get_special_stars(uint8_t special) const = 0;
|
||||
std::string get_star_value_table() const;
|
||||
|
||||
// unknown_a1 accessors
|
||||
virtual std::string get_unknown_a1() const = 0;
|
||||
|
||||
// special_table accessors
|
||||
virtual size_t num_specials() const = 0;
|
||||
virtual const Special& get_special(uint8_t special) const = 0;
|
||||
virtual const StatBoost& get_stat_boost(uint8_t entry_index) const = 0;
|
||||
virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const = 0;
|
||||
virtual uint8_t get_weapon_class(uint8_t data1_1) const = 0;
|
||||
|
||||
// weapon_effect_table accessors
|
||||
virtual size_t num_weapon_effects() const = 0;
|
||||
virtual const WeaponEffect& get_weapon_effect(size_t index) const = 0;
|
||||
|
||||
// weapon_stat_boost_index_table accessors
|
||||
virtual size_t num_weapon_stat_boost_indexes() const = 0;
|
||||
virtual uint8_t get_weapon_stat_boost_index(size_t index) const = 0;
|
||||
std::string get_weapon_stat_boost_index_table() const;
|
||||
|
||||
// armor_stat_boost_index_table accessors
|
||||
virtual size_t num_armor_stat_boost_indexes() const = 0;
|
||||
virtual uint8_t get_armor_stat_boost_index(size_t index) const = 0;
|
||||
std::string get_armor_stat_boost_index_table() const;
|
||||
|
||||
// shield_stat_boost_index_table accessors
|
||||
virtual size_t num_shield_stat_boost_indexes() const = 0;
|
||||
virtual uint8_t get_shield_stat_boost_index(size_t index) const = 0;
|
||||
std::string get_shield_stat_boost_index_table() const;
|
||||
|
||||
// stat_boost_table accessors
|
||||
virtual size_t num_stat_boosts() const = 0;
|
||||
virtual const StatBoost& get_stat_boost(size_t index) const = 0;
|
||||
|
||||
// shield_effect_table accessors
|
||||
virtual size_t num_shield_effects() const = 0;
|
||||
virtual const ShieldEffect& get_shield_effect(size_t index) const = 0;
|
||||
|
||||
// max_tech_level_table accessors
|
||||
virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const = 0;
|
||||
|
||||
// combination_table accessors
|
||||
virtual size_t num_item_combinations() const = 0;
|
||||
virtual const ItemCombination& get_item_combination(size_t index) const = 0;
|
||||
const std::map<uint32_t, std::vector<ItemCombination>>& item_combinations_index() const;
|
||||
const std::vector<ItemCombination>& all_combinations_for_used_item(const ItemData& used_item) const;
|
||||
const ItemCombination& get_item_combination(const ItemData& used_item, const ItemData& equipped_item) const;
|
||||
|
||||
// sound_remap_table accessors
|
||||
virtual const std::vector<SoundRemaps>& get_all_sound_remaps() const = 0;
|
||||
|
||||
// tech_boost_table accessors
|
||||
virtual size_t num_tech_boosts() const = 0;
|
||||
virtual const TechBoost& get_tech_boost(size_t index) const = 0;
|
||||
|
||||
// unwrap_table accessors
|
||||
virtual size_t num_events() const = 0;
|
||||
virtual std::pair<const EventItem*, size_t> get_event_items(uint8_t event_number) const = 0;
|
||||
|
||||
// unsealable_table accessors
|
||||
virtual const std::unordered_set<uint32_t>& all_unsealable_items() const = 0;
|
||||
bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const;
|
||||
bool is_unsealable_item(const ItemData& item) const;
|
||||
|
||||
// ranged_special_table accessors
|
||||
virtual size_t num_ranged_specials() const = 0;
|
||||
virtual const RangedSpecial& get_ranged_special(size_t index) const = 0;
|
||||
|
||||
// Composite accessors
|
||||
std::variant<const Weapon*, const ArmorOrShield*, const Unit*, const Mag*, const Tool*>
|
||||
definition_for_primary_identifier(uint32_t primary_identifier) const;
|
||||
uint32_t get_item_id(const ItemData& item) const;
|
||||
uint32_t get_item_team_points(const ItemData& item) const;
|
||||
uint8_t get_item_base_stars(const ItemData& item) const;
|
||||
uint8_t get_item_adjusted_stars(const ItemData& item, bool ignore_unidentified = false) const;
|
||||
bool is_item_rare(const ItemData& item) const;
|
||||
virtual bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const = 0;
|
||||
bool is_unsealable_item(const ItemData& item) const;
|
||||
const ItemCombination& get_item_combination(const ItemData& used_item, const ItemData& equipped_item) const;
|
||||
const std::vector<ItemCombination>& get_all_combinations_for_used_item(const ItemData& used_item) const;
|
||||
virtual const std::map<uint32_t, std::vector<ItemCombination>>& get_all_item_combinations() const = 0;
|
||||
virtual size_t num_events() const = 0;
|
||||
virtual std::pair<const EventItem*, size_t> get_event_items(uint8_t event_number) const = 0;
|
||||
|
||||
size_t price_for_item(const ItemData& item) const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<const std::string> data;
|
||||
phosg::StringReader r;
|
||||
ItemParameterTable() = default;
|
||||
|
||||
mutable std::unordered_map<uint16_t, Weapon> weapons;
|
||||
mutable std::vector<ArmorOrShield> armors;
|
||||
mutable std::vector<ArmorOrShield> shields;
|
||||
mutable std::vector<Unit> units;
|
||||
mutable std::vector<Mag> mags;
|
||||
mutable std::unordered_map<uint16_t, Tool> tools;
|
||||
|
||||
mutable std::vector<Special> specials;
|
||||
mutable std::vector<StatBoost> stat_boosts;
|
||||
|
||||
// Key is used_item. We can't index on (used_item, equipped_item) because equipped_item may contain wildcards, and
|
||||
// the matching order matters.
|
||||
mutable std::map<uint32_t, std::vector<ItemCombination>> item_combination_index;
|
||||
|
||||
explicit ItemParameterTable(std::shared_ptr<const std::string> data);
|
||||
mutable std::optional<std::map<uint32_t, std::vector<ItemCombination>>> item_combination_index;
|
||||
};
|
||||
|
||||
@@ -46,18 +46,18 @@ ItemTranslationTable::ItemTranslationTable(
|
||||
uint32_t e_id = this->entries[z].id_for_version[v_s];
|
||||
if (is_canonical(e_id)) {
|
||||
if (!entry_index.count(e_id)) {
|
||||
throw logic_error(std::format("(row {} version {}) canonical ID {:X} is missing from the index", z, phosg::name_for_enum(v), e_id));
|
||||
throw logic_error(std::format("(row {} version {}) canonical ID {:08X} is missing from the index", z, phosg::name_for_enum(v), e_id));
|
||||
}
|
||||
try {
|
||||
item_parameter_table->definition_for_primary_identifier(e_id);
|
||||
} catch (const out_of_range&) {
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:X} not defined in item parameter table", z, phosg::name_for_enum(v), e_id));
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:08X} not defined in item parameter table", z, phosg::name_for_enum(v), e_id));
|
||||
}
|
||||
if (!remaining_identifiers.erase(e_id)) {
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:X} not in item parameter table's primary identifier list", z, phosg::name_for_enum(v), e_id));
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:08X} not in item parameter table's primary identifier list", z, phosg::name_for_enum(v), e_id));
|
||||
}
|
||||
} else if (!entry_index.count(make_canonical(e_id))) {
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:X} refers to nonexistent canonical ID", z, phosg::name_for_enum(v), e_id));
|
||||
throw runtime_error(std::format("(row {} version {}) ID {:08X} refers to nonexistent canonical ID", z, phosg::name_for_enum(v), e_id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
@@ -2387,6 +2387,47 @@ Action a_compare_common_item_set(
|
||||
cs1->print_diff(stdout, *cs2);
|
||||
});
|
||||
|
||||
Action a_decode_item_parameter_table(
|
||||
"decode-item-parameter-table", "\
|
||||
decode-item-parameter-table [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\
|
||||
Converts an ItemPMT file into a JSON item parameter table. A version\n\
|
||||
option is required. Use --hex to make item codes in the output readable;\n\
|
||||
however, this option also uses nonstandard JSON syntax - newserv can parse\n\
|
||||
it, but many other JSON parsers can\'t. Expects compressed input (a .prs\n\
|
||||
file) by default; use --decompressed if the input is not compressed.\n",
|
||||
+[](phosg::Arguments& args) {
|
||||
auto input_data = read_input_data(args);
|
||||
if (!args.get<bool>("decompressed")) {
|
||||
input_data = prs_decompress(input_data);
|
||||
}
|
||||
auto data = std::make_shared<string>(std::move(input_data));
|
||||
auto pmt = ItemParameterTable::from_binary(data, get_cli_version(args, Version::BB_V4));
|
||||
auto json = pmt->json();
|
||||
uint32_t serialize_options = phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS;
|
||||
if (args.get<bool>("hex")) {
|
||||
serialize_options |= phosg::JSON::SerializeOption::HEX_INTEGERS;
|
||||
}
|
||||
string json_data = json.serialize(serialize_options);
|
||||
write_output_data(args, json_data.data(), json_data.size(), nullptr);
|
||||
});
|
||||
|
||||
Action a_encode_item_parameter_table(
|
||||
"encode-item-parameter-table", "\
|
||||
encode-item-parameter-table [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\
|
||||
Converts a JSON item parameter table into an ItemPMT file compatible with\n\
|
||||
the game client. A version option is required. By default the output will\n\
|
||||
be compressed, as the client expects; use --decompressed to get\n\
|
||||
uncompressed output.\n",
|
||||
+[](phosg::Arguments& args) {
|
||||
auto json = phosg::JSON::parse(read_input_data(args));
|
||||
auto pmt = ItemParameterTable::from_json(json);
|
||||
string data = pmt->serialize_binary(get_cli_version(args, Version::BB_V4));
|
||||
if (!args.get<bool>("decompressed")) {
|
||||
data = prs_compress_optimal(data);
|
||||
}
|
||||
write_output_data(args, data.data(), data.size(), nullptr);
|
||||
});
|
||||
|
||||
Action a_describe_item(
|
||||
"describe-item", "\
|
||||
describe-item DATA-OR-DESCRIPTION\n\
|
||||
|
||||
+48
-50
@@ -692,6 +692,7 @@ void send_guild_card_chunk_bb(shared_ptr<Client> c, size_t chunk_index) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool is_battle_param_stream_file_for_blueballz(const string& filename) {
|
||||
return (filename == "BattleParamEntry.dat") ||
|
||||
(filename == "BattleParamEntry_on.dat") ||
|
||||
@@ -701,31 +702,23 @@ static bool is_battle_param_stream_file_for_blueballz(const string& filename) {
|
||||
(filename == "BattleParamEntry_ep4_on.dat");
|
||||
}
|
||||
|
||||
static shared_ptr<const string> bb_stream_file_data_for_client(shared_ptr<Client> c, const string& filename) {
|
||||
static string bb_stream_file_data_for_client(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
|
||||
auto raw_data = s->bb_stream_files_cache->get_or_load("system/blueburst/" + filename).file->data;
|
||||
|
||||
int64_t effective_blueballz_hp_scale_tier = (c->selected_blueballz_tier >= 0)
|
||||
? c->selected_blueballz_tier
|
||||
: s->blueballz_enemy_hp_scale_tier;
|
||||
|
||||
if (!is_battle_param_stream_file_for_blueballz(filename) || (effective_blueballz_hp_scale_tier < 0)) {
|
||||
return raw_data;
|
||||
if (effective_blueballz_hp_scale_tier < 0) {
|
||||
return s->bb_stream_file->data;
|
||||
}
|
||||
|
||||
effective_blueballz_hp_scale_tier = std::min<int64_t>(
|
||||
s->blueballz_max_tier,
|
||||
effective_blueballz_hp_scale_tier);
|
||||
|
||||
string scaled_data = *raw_data;
|
||||
if (scaled_data.size() < sizeof(BattleParamsIndex::Table)) {
|
||||
c->log.warning_f("Blueballz enemy HP scaling skipped for {}; file is too small", filename);
|
||||
return raw_data;
|
||||
}
|
||||
|
||||
string scaled_data = s->bb_stream_file->data;
|
||||
double mult = 1.0 + (static_cast<double>(effective_blueballz_hp_scale_tier) * 0.25);
|
||||
auto* table = reinterpret_cast<BattleParamsIndex::Table*>(scaled_data.data());
|
||||
size_t ultimate_index = static_cast<size_t>(Difficulty::ULTIMATE);
|
||||
|
||||
auto scale_u16 = [mult](uint32_t v) -> uint16_t {
|
||||
@@ -742,28 +735,39 @@ static shared_ptr<const string> bb_stream_file_data_for_client(shared_ptr<Client
|
||||
return static_cast<uint16_t>(scaled);
|
||||
};
|
||||
|
||||
for (size_t z = 0; z < 0x60; z++) {
|
||||
auto& stats = table->stats[ultimate_index][z];
|
||||
stats.char_stats.hp = scale_u16(stats.char_stats.hp);
|
||||
for (const auto& sf_entry : s->bb_stream_file->entries) {
|
||||
if (!is_battle_param_stream_file_for_blueballz(sf_entry.filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((sf_entry.offset > scaled_data.size()) ||
|
||||
(sf_entry.size > (scaled_data.size() - sf_entry.offset))) {
|
||||
c->log.warning_f("Blueballz enemy HP scaling skipped for {}; invalid stream-file range",
|
||||
sf_entry.filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sf_entry.size < sizeof(BattleParamsIndex::Table)) {
|
||||
c->log.warning_f("Blueballz enemy HP scaling skipped for {}; file is too small",
|
||||
sf_entry.filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* table = reinterpret_cast<BattleParamsIndex::Table*>(
|
||||
scaled_data.data() + sf_entry.offset);
|
||||
|
||||
for (size_t z = 0; z < 0x60; z++) {
|
||||
auto& stats = table->stats[ultimate_index][z];
|
||||
stats.char_stats.hp = scale_u16(stats.char_stats.hp);
|
||||
}
|
||||
|
||||
c->log.info_f("Blueballz enemy HP scaling: serving {} with tier {} ({:g}x Ultimate HP)",
|
||||
sf_entry.filename, effective_blueballz_hp_scale_tier, mult);
|
||||
}
|
||||
|
||||
c->log.info_f("Blueballz enemy HP scaling: serving {} with tier {} ({:g}x Ultimate HP)",
|
||||
filename, effective_blueballz_hp_scale_tier, mult);
|
||||
|
||||
return make_shared<string>(std::move(scaled_data));
|
||||
return scaled_data;
|
||||
}
|
||||
|
||||
static const vector<string> stream_file_entries = {
|
||||
"ItemMagEdit.prs",
|
||||
"ItemPMT.prs",
|
||||
"BattleParamEntry.dat",
|
||||
"BattleParamEntry_on.dat",
|
||||
"BattleParamEntry_lab.dat",
|
||||
"BattleParamEntry_lab_on.dat",
|
||||
"BattleParamEntry_ep4.dat",
|
||||
"BattleParamEntry_ep4_on.dat",
|
||||
"PlyLevelTbl.prs",
|
||||
};
|
||||
|
||||
void send_stream_file_index_bb(shared_ptr<Client> c) {
|
||||
auto s = c->require_server_state();
|
||||
@@ -771,15 +775,19 @@ void send_stream_file_index_bb(shared_ptr<Client> c) {
|
||||
c->log.info_f("PSO Peeps BBZ stream debug: send_stream_file_index_bb called");
|
||||
|
||||
vector<S_StreamFileIndexEntry_BB_01EB> entries;
|
||||
size_t offset = 0;
|
||||
for (const string& filename : stream_file_entries) {
|
||||
auto file_data = bb_stream_file_data_for_client(c, filename);
|
||||
string contents = bb_stream_file_data_for_client(c);
|
||||
for (const auto& sf_entry : s->bb_stream_file->entries) {
|
||||
if ((sf_entry.offset > contents.size()) ||
|
||||
(sf_entry.size > (contents.size() - sf_entry.offset))) {
|
||||
throw runtime_error("invalid BB stream file entry range");
|
||||
}
|
||||
|
||||
auto& e = entries.emplace_back();
|
||||
e.size = file_data->size();
|
||||
e.checksum = crc32(file_data->data(), e.size);
|
||||
e.offset = offset;
|
||||
e.filename.encode(filename);
|
||||
offset += e.size;
|
||||
e.size = sf_entry.size;
|
||||
e.checksum = crc32(contents.data() + sf_entry.offset, e.size);
|
||||
e.offset = sf_entry.offset;
|
||||
e.filename.encode(sf_entry.filename);
|
||||
|
||||
}
|
||||
send_command_vt(c, 0x01EB, entries.size(), entries);
|
||||
}
|
||||
@@ -787,18 +795,7 @@ void send_stream_file_index_bb(shared_ptr<Client> c) {
|
||||
void send_stream_file_chunk_bb(shared_ptr<Client> c, uint32_t chunk_index) {
|
||||
auto s = c->require_server_state();
|
||||
|
||||
c->log.info_f("PSO Peeps BBZ stream debug: send_stream_file_chunk_bb called for chunk {}", chunk_index);
|
||||
|
||||
size_t total_bytes = 0;
|
||||
for (const auto& name : stream_file_entries) {
|
||||
total_bytes += bb_stream_file_data_for_client(c, name)->size();
|
||||
}
|
||||
|
||||
string contents;
|
||||
contents.reserve(total_bytes);
|
||||
for (const auto& name : stream_file_entries) {
|
||||
contents += *bb_stream_file_data_for_client(c, name);
|
||||
}
|
||||
string contents = bb_stream_file_data_for_client(c);
|
||||
|
||||
S_StreamFileChunk_BB_02EB chunk_cmd;
|
||||
chunk_cmd.chunk_index = chunk_index;
|
||||
@@ -809,6 +806,7 @@ void send_stream_file_chunk_bb(shared_ptr<Client> c, uint32_t chunk_index) {
|
||||
size_t bytes = min<size_t>(contents.size() - offset, sizeof(chunk_cmd.data));
|
||||
chunk_cmd.data.assign_range(reinterpret_cast<const uint8_t*>(contents.data() + offset), bytes, 0);
|
||||
|
||||
|
||||
size_t cmd_size = offsetof(S_StreamFileChunk_BB_02EB, data) + bytes;
|
||||
cmd_size = (cmd_size + 3) & ~3;
|
||||
send_command(c, 0x02EB, 0x00000000, &chunk_cmd, cmd_size);
|
||||
|
||||
+36
-6
@@ -79,7 +79,6 @@ ServerState::ServerState(const string& config_filename, bool is_replay)
|
||||
config_filename(config_filename),
|
||||
is_replay(is_replay),
|
||||
thread_pool(make_unique<asio::thread_pool>()),
|
||||
bb_stream_files_cache(new FileContentsCache(3600000000ULL)),
|
||||
bb_system_cache(new FileContentsCache(3600000000ULL)),
|
||||
gba_files_cache(new FileContentsCache(3600000000ULL)) {}
|
||||
|
||||
@@ -1847,8 +1846,6 @@ vector<shared_ptr<const SuperMap>> ServerState::supermaps_for_variations(
|
||||
}
|
||||
|
||||
void ServerState::clear_file_caches() {
|
||||
config_log.info_f("Clearing BB stream file cache");
|
||||
this->bb_stream_files_cache.reset(new FileContentsCache(3600000000ULL));
|
||||
config_log.info_f("Clearing BB system cache");
|
||||
this->bb_system_cache.reset(new FileContentsCache(3600000000ULL));
|
||||
config_log.info_f("Clearing GBA file cache");
|
||||
@@ -2164,10 +2161,9 @@ void ServerState::load_item_definitions() {
|
||||
config_log.info_f("Loading item definition tables");
|
||||
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
|
||||
Version v = static_cast<Version>(v_s);
|
||||
string path = std::format("system/item-tables/ItemPMT-{}.prs", file_path_token_for_version(v));
|
||||
string path = std::format("system/item-tables/item-parameter-table-{}.json", file_path_token_for_version(v));
|
||||
config_log.debug_f("Loading item definition table {}", path);
|
||||
auto data = make_shared<string>(prs_decompress(phosg::load_file(path)));
|
||||
new_item_parameter_tables[v_s] = ItemParameterTable::create(data, v);
|
||||
new_item_parameter_tables[v_s] = ItemParameterTable::from_json(phosg::JSON::parse(phosg::load_file(path)));
|
||||
}
|
||||
|
||||
auto json = phosg::JSON::parse(phosg::load_file("system/item-tables/translation-table.json"));
|
||||
@@ -2243,6 +2239,39 @@ void ServerState::load_dol_files() {
|
||||
this->dol_file_index = make_shared<DOLFileIndex>("system/dol");
|
||||
}
|
||||
|
||||
void ServerState::generate_bb_stream_file() {
|
||||
config_log.info_f("Generating BB stream file");
|
||||
auto sf = std::make_shared<BBStreamFile>();
|
||||
|
||||
auto add_file = [&](const std::string& filename, std::string&& file_data = "") -> void {
|
||||
if (file_data.empty()) {
|
||||
file_data = phosg::load_file("system/blueburst/" + filename);
|
||||
}
|
||||
auto& e = sf->entries.emplace_back();
|
||||
e.size = file_data.size();
|
||||
e.checksum = phosg::crc32(file_data.data(), file_data.size());
|
||||
e.offset = sf->data.size();
|
||||
e.filename = filename;
|
||||
sf->data += file_data;
|
||||
config_log.debug_f(
|
||||
"[BBStreamFile] Added file {} at offset {:08X} ({:08X} bytes) with checksum {:08X}; total size is now {:08X}",
|
||||
filename, e.offset, e.size, e.checksum, sf->data.size());
|
||||
};
|
||||
|
||||
add_file("BattleParamEntry.dat");
|
||||
add_file("BattleParamEntry_on.dat");
|
||||
add_file("BattleParamEntry_lab.dat");
|
||||
add_file("BattleParamEntry_lab_on.dat");
|
||||
add_file("BattleParamEntry_ep4.dat");
|
||||
add_file("BattleParamEntry_ep4_on.dat");
|
||||
add_file("PlyLevelTbl.prs");
|
||||
add_file("ItemMagEdit.prs");
|
||||
auto pmt = this->item_parameter_table(Version::BB_V4);
|
||||
add_file("ItemPMT.prs", prs_compress_optimal(pmt->serialize_binary(Version::BB_V4)));
|
||||
|
||||
this->bb_stream_file = sf;
|
||||
}
|
||||
|
||||
void ServerState::create_default_lobbies() {
|
||||
if (this->default_lobbies_created) {
|
||||
return;
|
||||
@@ -2322,6 +2351,7 @@ void ServerState::load_all(bool enable_thread_pool) {
|
||||
this->load_config_late();
|
||||
this->load_teams();
|
||||
this->load_quest_index();
|
||||
this->generate_bb_stream_file();
|
||||
}
|
||||
|
||||
void ServerState::disconnect_all_banned_clients() {
|
||||
|
||||
+13
-1
@@ -65,6 +65,17 @@ struct CheatFlags {
|
||||
explicit CheatFlags(const phosg::JSON& json);
|
||||
};
|
||||
|
||||
struct BBStreamFile {
|
||||
struct Entry {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t checksum; // crc32
|
||||
std::string filename;
|
||||
};
|
||||
std::vector<Entry> entries;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
enum class RunShellBehavior {
|
||||
DEFAULT = 0,
|
||||
@@ -188,7 +199,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::unordered_map<uint64_t, std::shared_ptr<const SuperMap>> supermap_for_source_hash_sum;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<const SuperMap>> supermap_for_free_play_key;
|
||||
std::shared_ptr<const RoomLayoutIndex> room_layout_index;
|
||||
std::shared_ptr<FileContentsCache> bb_stream_files_cache;
|
||||
std::shared_ptr<const BBStreamFile> bb_stream_file;
|
||||
std::shared_ptr<FileContentsCache> bb_system_cache;
|
||||
std::shared_ptr<FileContentsCache> gba_files_cache;
|
||||
std::shared_ptr<const DOLFileIndex> dol_file_index;
|
||||
@@ -450,6 +461,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
void load_quest_index(bool raise_on_any_failure = false);
|
||||
void compile_functions(bool raise_on_any_failure = false);
|
||||
void load_dol_files();
|
||||
void generate_bb_stream_file();
|
||||
|
||||
void load_all(bool enable_thread_pool);
|
||||
|
||||
|
||||
@@ -211,14 +211,17 @@ ShellCommand c_reload(
|
||||
args.s->load_set_data_tables();
|
||||
} else if (type == "battle-params") {
|
||||
args.s->load_battle_params();
|
||||
args.s->generate_bb_stream_file();
|
||||
} else if (type == "level-tables") {
|
||||
args.s->load_level_tables();
|
||||
args.s->generate_bb_stream_file();
|
||||
} else if (type == "text-index") {
|
||||
args.s->load_text_index();
|
||||
} else if (type == "word-select") {
|
||||
args.s->load_word_select_table();
|
||||
} else if (type == "item-definitions") {
|
||||
args.s->load_item_definitions();
|
||||
args.s->generate_bb_stream_file();
|
||||
} else if (type == "item-name-index") {
|
||||
args.s->load_item_name_indexes();
|
||||
} else if (type == "drop-tables") {
|
||||
|
||||
@@ -478,8 +478,8 @@ Language language_for_name(const string& name) {
|
||||
}
|
||||
|
||||
const vector<string> tech_id_to_name = {
|
||||
"foie", "gifoie", "rafoie", "barta", "gibarta", "rabarta", "zonde", "gizonde", "razonde", "grants", "deband",
|
||||
"jellen", "zalure", "shifta", "ryuker", "resta", "anti", "reverser", "megid"};
|
||||
"Foie", "Gifoie", "Rafoie", "Barta", "Gibarta", "Rabarta", "Zonde", "Gizonde", "Razonde", "Grants", "Deband",
|
||||
"Jellen", "Zalure", "Shifta", "Ryuker", "Resta", "Anti", "Reverser", "Megid"};
|
||||
|
||||
const unordered_map<string, uint8_t> name_to_tech_id = {
|
||||
{"foie", 0}, {"gifoie", 1}, {"rafoie", 2}, {"barta", 3}, {"gibarta", 4}, {"rabarta", 5}, {"zonde", 6},
|
||||
@@ -497,7 +497,7 @@ const string& name_for_technique(uint8_t tech) {
|
||||
|
||||
uint8_t technique_for_name(const string& name) {
|
||||
try {
|
||||
return name_to_tech_id.at(name);
|
||||
return name_to_tech_id.at(phosg::tolower(name));
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../item-tables/ItemPMT-bb-v4.prs
|
||||
+9
-7
@@ -1,6 +1,8 @@
|
||||
.meta name="Kill count fix"
|
||||
.meta description="Fixes client-side\nkill counts when\nmultiple enemies are\nkilled on the same\nframe"
|
||||
|
||||
.versions 59NJ 59NL
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
@@ -9,9 +11,9 @@ start:
|
||||
|
||||
|
||||
|
||||
.data 0x005E32C8
|
||||
.data <VERS 0x005E32A4 0x005E32C8>
|
||||
.deltaof TItemUnitUnsealable_count_kill, TItemUnitUnsealable_count_kill_end
|
||||
.address 0x005E32C8
|
||||
.address <VERS 0x005E32A4 0x005E32C8>
|
||||
TItemUnitUnsealable_count_kill: # [std] (TItemUnitUnsealable* this @ ecx) -> void
|
||||
mov eax, [ecx + 0xF8]
|
||||
movsx eax, word [eax + 0x11A] # eax = this->owner_player->num_kills_since_map_load
|
||||
@@ -29,14 +31,14 @@ TItemUnitUnsealable_count_kill_skip_update:
|
||||
setae dh
|
||||
shl edx, 1
|
||||
or dword [ecx + 0xDC], edx
|
||||
jmp 0x005E2C34
|
||||
jmp <VERS 0x005E2C10 0x005E2C34>
|
||||
TItemUnitUnsealable_count_kill_end:
|
||||
|
||||
|
||||
|
||||
.data 0x005F3EFC
|
||||
.data <VERS 0x005F3E94 0x005F3EFC>
|
||||
.deltaof TItemWeapon_LameDArgent_count_kill, TItemWeapon_LameDArgent_count_kill_end
|
||||
.address 0x005F3EFC
|
||||
.address <VERS 0x005F3E94 0x005F3EFC>
|
||||
TItemWeapon_LameDArgent_count_kill:
|
||||
mov eax, [ecx + 0xF8]
|
||||
movsx eax, word [eax + 0x11A]
|
||||
@@ -59,9 +61,9 @@ TItemWeapon_LameDArgent_count_kill_end:
|
||||
|
||||
|
||||
|
||||
.data 0x005FCA74
|
||||
.data <VERS 0x005FC95C 0x005FCA74>
|
||||
.deltaof TItemWeapon_SealedJSword_count_kill, TItemWeapon_SealedJSword_count_kill_end
|
||||
.address 0x005FCA74
|
||||
.address <VERS 0x005FC95C 0x005FCA74>
|
||||
TItemWeapon_SealedJSword_count_kill:
|
||||
mov eax, [ecx + 0xF8]
|
||||
movsx eax, word [eax + 0x11A]
|
||||
@@ -0,0 +1,624 @@
|
||||
# This patch changes the number of BB character save slots from 4 to any number
|
||||
# up to 127.
|
||||
|
||||
# This patch is for documentation purposes only; it works when used as a server
|
||||
# patch via newserv, but is decidedly inconvenient to use via this method. This
|
||||
# is because it affects logic that runs before any patches can be sent by the
|
||||
# server, so the player has to connect once to get the patch, then disconnect
|
||||
# and connect again to use the additional slots.
|
||||
|
||||
# As written, this patch changes the slot count from 4 to 12. To use a
|
||||
# different slot count, first compute the following values:
|
||||
# slot count = your desired number of player slots (must be >= 4, <= 127)
|
||||
# total file size = (slot count * 0x2EA4) + 0x14
|
||||
# bgm_test_songs_unlocked offset = total file size - 0x10
|
||||
# save_count offset = total file size - 8
|
||||
# round2_seed offset = total file size - 4
|
||||
# Then, for each of the above, search for the string to the left of the = sign
|
||||
# and change the values used in all of the matching lines.
|
||||
|
||||
.meta name="More save slots"
|
||||
.meta description=""
|
||||
.meta hide_from_patches_menu
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
# Include a few functions first
|
||||
write_call_to_code:
|
||||
.include WriteCallToCode-59NJ
|
||||
memcpy:
|
||||
.include CopyData
|
||||
ret
|
||||
|
||||
|
||||
|
||||
start:
|
||||
# Apply all necessary patches
|
||||
call apply_enable_scroll_patch
|
||||
call apply_fix_scroll_patch1
|
||||
call apply_fix_scroll_patch2
|
||||
call apply_fix_file_index
|
||||
call apply_preview_window_fix
|
||||
call apply_static_patches
|
||||
# Rewrite the existing char file regions to have the appropriate size; this
|
||||
# must be done after the patches are applied because we call the checksum
|
||||
# function, which is patched by one of the above calls
|
||||
call update_existing_char_file_list
|
||||
jmp update_existing_char_file_list_memcard
|
||||
|
||||
|
||||
|
||||
apply_enable_scroll_patch:
|
||||
# This patch enables scrolling behavior within the character list
|
||||
push -5 # Jump size (negative = jmp instead of call)
|
||||
push 0x00413B77 # Jump address
|
||||
call get_code_size_for_enable_scroll
|
||||
.deltaof enable_scroll_start, enable_scroll_end
|
||||
get_code_size_for_enable_scroll:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call enable_scroll_end
|
||||
enable_scroll_start:
|
||||
mov eax, dword ptr [edi + 0x28] # cursor = char_select_menu->cursor_obj (TAdSelectCurGC*)
|
||||
or dword [eax + 0x01F8], 3 # cursor->flags |= 3 # Enable scrolling
|
||||
mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0]
|
||||
mov ecx, [eax + 0xEC] # ecx = scroll_bar->client_id
|
||||
imul ecx, ecx, 0x24
|
||||
# Set up scroll bar graphics (in struct at scroll_bar + 0x1C)
|
||||
mov dword [eax + ecx + 0x1C], 0x439D0000
|
||||
mov dword [eax + ecx + 0x20], 0x43360000
|
||||
mov dword [eax + ecx + 0x24], 0x439D0000
|
||||
mov dword [eax + ecx + 0x28], 0x4392AB85
|
||||
mov dword [eax + ecx + 0x2C], 0x40400000
|
||||
mov dword [eax + ecx + 0x30], 0x425EA3D7
|
||||
mov dword [eax + ecx + 0x34], 0x00000008
|
||||
mov dword [eax + ecx + 0x38], 0x00000000
|
||||
mov dword [eax + ecx + 0x3C], 0x00000000
|
||||
or dword [eax + 0xF0], 1 # scroll_bar->flags |= 1
|
||||
mov ecx, [eax + 0xEC]
|
||||
shl ecx, 4
|
||||
mov dword [eax + ecx + 0xAC], 0 # scroll_bar->selection_state[client_id].scroll_offset = 0
|
||||
mov dword [eax + ecx + 0xB0], 0 # scroll_bar->selection_state[client_id].selected_index = 0
|
||||
mov dword [eax + ecx + 0xB4], 4 # scroll_bar->selection_state[client_id].num_items_in_view = 4
|
||||
mov dword [eax + ecx + 0xB8], 0x0B # scroll_bar->selection_state[client_id].last_item_index = (slot count - 1)
|
||||
pop edi
|
||||
ret
|
||||
enable_scroll_end:
|
||||
call write_call_to_code
|
||||
ret
|
||||
|
||||
|
||||
|
||||
apply_fix_scroll_patch1:
|
||||
# This patch fixes character selection cursor object so it will take the
|
||||
# scroll offset into account
|
||||
push 6 # Call size
|
||||
push 0x00413C30 # Call address
|
||||
call get_code_size_for_fix_scroll_patch1
|
||||
.deltaof fix_scroll_patch1_start, fix_scroll_patch1_end
|
||||
get_code_size_for_fix_scroll_patch1:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call fix_scroll_patch1_end
|
||||
fix_scroll_patch1_start:
|
||||
mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*)
|
||||
mov ebp, [edx + 0x44] # ebp = cursor->selected_index_within_view
|
||||
mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0]
|
||||
add ebp, [eax + 0xAC] # ebp += scroll_bar->selection_state[0].scroll_offset
|
||||
ret
|
||||
fix_scroll_patch1_end:
|
||||
call write_call_to_code
|
||||
ret
|
||||
|
||||
|
||||
|
||||
apply_fix_scroll_patch2:
|
||||
# This patch changes the TAdSinglePlyChrSelectGC::selected_index_within_view
|
||||
# to be the selected character's absolute index (including scroll_offset),
|
||||
# not the index only within the displayed four characters
|
||||
push 6 # Call size
|
||||
push 0x00413CD0 # Call address
|
||||
call get_code_size_for_fix_scroll_patch2
|
||||
.deltaof fix_scroll_patch2_start, fix_scroll_patch2_end
|
||||
get_code_size_for_fix_scroll_patch2:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call fix_scroll_patch2_end
|
||||
fix_scroll_patch2_start:
|
||||
mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0]
|
||||
mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset
|
||||
mov edx, [edi + 0x28] # cursor = this->ad_select_cur_obj (TAdSelectCurGC*)
|
||||
add eax, [edx + 0x44] # eax += cursor->selected_index_within_view
|
||||
ret
|
||||
fix_scroll_patch2_end:
|
||||
call write_call_to_code
|
||||
ret
|
||||
|
||||
|
||||
|
||||
apply_fix_file_index:
|
||||
# This patch fixes the character file indexing so it will account for the
|
||||
# scroll position
|
||||
push 5 # Call size
|
||||
push 0x00413CE8 # Call address
|
||||
call get_code_size_for_selection_index_fix2
|
||||
.deltaof selection_index_fix2_start, selection_index_fix2_end
|
||||
get_code_size_for_selection_index_fix2:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call selection_index_fix2_end
|
||||
selection_index_fix2_start:
|
||||
mov eax, [0x00A38BD0]
|
||||
mov eax, [eax + 0xAC] # eax = TAdScrollBarXb_objs[0]->selection_state[0].scroll_offset
|
||||
add ebp, eax # arg0 += eax
|
||||
mov [esp + 4], ebp
|
||||
mov eax, 0x006C1ABC
|
||||
jmp eax # set_current_char_slot
|
||||
selection_index_fix2_end:
|
||||
call write_call_to_code
|
||||
ret
|
||||
|
||||
|
||||
|
||||
apply_preview_window_fix:
|
||||
# This patch fixes the preview display so it will show the correct section
|
||||
# ID, level, etc.
|
||||
push 5 # Call size
|
||||
push 0x0040216C # Call address
|
||||
call get_code_size_for_preview_window_fix
|
||||
.deltaof preview_window_fix_start, preview_window_fix_end
|
||||
get_code_size_for_preview_window_fix:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call preview_window_fix_end
|
||||
preview_window_fix_start:
|
||||
mov eax, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0]
|
||||
mov eax, [eax + 0xAC] # eax = scroll_bar->selection_state[0].scroll_offset
|
||||
add [esp + 4], eax
|
||||
mov eax, 0x006C4514 # get_player_preview_info
|
||||
jmp eax
|
||||
preview_window_fix_end:
|
||||
# This patch applies in two places, so push the second set of args now, then
|
||||
# apply it twice
|
||||
push 5 # Call size
|
||||
push 0x00401842 # Call address
|
||||
push dword [esp + 0x10] # Code size
|
||||
push dword [esp + 0x10] # Code address
|
||||
call write_call_to_code
|
||||
call write_call_to_code
|
||||
ret
|
||||
|
||||
|
||||
|
||||
apply_static_patches:
|
||||
.include WriteCodeBlocksBB
|
||||
# These patches change various places where the character data size and slot
|
||||
# count are referenced
|
||||
.data 0x00475294
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count; TDataProtocol::handle_E5
|
||||
.data 0x0047534B
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count; import_player_preview
|
||||
.data 0x004786D1
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count; TDataProtocol::handle_E4
|
||||
.data 0x00482559
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C17FB
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C1D07
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C1D3A
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C1D58
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C1E13
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C226A
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C22A9
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C22CA
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C22DA
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C2517
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C267F
|
||||
.data 0x00000004
|
||||
.data 0x00022FBC # save_count offset
|
||||
.data 0x006C2689
|
||||
.data 0x00000004
|
||||
.data 0x00022FBC # save_count offset
|
||||
.data 0x006C272B
|
||||
.data 0x00000004
|
||||
.data 0x00022FBC # save_count offset
|
||||
.data 0x006C2741
|
||||
.data 0x00000004
|
||||
.data 0x00022FC0 # round2_seed offset
|
||||
.data 0x006C27CF
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C28A8
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C314F
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C357B
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C35BA
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C35E6
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C35F3
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C360E
|
||||
.data 0x00000004
|
||||
.data 0x00022FBC # save_count offset
|
||||
.data 0x006C3617
|
||||
.data 0x00000004
|
||||
.data 0x00022FBC # save_count offset
|
||||
.data 0x006C371C
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C3B5A
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C424D
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C4833
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C486A
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C49A6
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C49DD
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C4AC5
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C4AFE
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C4CDE
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C4D15
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C4DFD
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C4E36
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C4F9C
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C4FD7
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C51C5
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5201
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5376
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C53B0
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5545
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5581
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C56F6
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5730
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C58B6
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C58F0
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5A85
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5AC1
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5BB2
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5BEC
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5D72
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5DAC
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C5F32
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C5F6C
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C60F2
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C612C
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C6346
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C6381
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C6505
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C6541
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C6632
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C666C
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C67F2
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C682C
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C69B2
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C69EC
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C6B87
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C6BB8
|
||||
.data 0x00000004
|
||||
.data 0x0000005D # memcard block count
|
||||
.data 0x006C6C3A
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C6C74
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C6E82
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C6EBC
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C70B9
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C70F3
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C7A46
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C7D66
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x006C7D7C
|
||||
.data 0x00000001
|
||||
.binary 0C # slot count
|
||||
.data 0x006C7DC0
|
||||
.data 0x00000004
|
||||
.data 0x00022FC4 # total file size
|
||||
.data 0x0077CC72
|
||||
.data 0x00000004
|
||||
.data 0x00022FB4 # bgm_test_songs_unlocked offset
|
||||
|
||||
# Signature check on all save files (rewritten as loop)
|
||||
.data 0x006C1C69
|
||||
.deltaof sig_check_begin, sig_check_end
|
||||
sig_check_begin:
|
||||
mov edx, 0xC87ED5B1 # Expected signature value
|
||||
add eax, 0x04E8 # &char_file_list->chars[0].part2.signature
|
||||
mov ecx, 0x0C # slot count
|
||||
again:
|
||||
cmp dword [eax], 0 # signature == 0 (no char in slot)
|
||||
je sig_ok
|
||||
cmp dword [eax], edx # signature == expected value
|
||||
jne sig_bad
|
||||
sig_ok:
|
||||
add eax, 0x2EA4 # Advance to next slot
|
||||
dec ecx
|
||||
jnz again
|
||||
xor eax, eax # All signatures OK (eax = 0)
|
||||
jmp sig_check_end
|
||||
sig_bad:
|
||||
xor eax, eax # Bad signature (eax = 1)
|
||||
inc eax
|
||||
jmp sig_check_end
|
||||
.binary CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||
sig_check_end: # 006C1CB2
|
||||
|
||||
# Send slot count in E3 command
|
||||
.data 0x0046EC10 # TDataProtocol::send_E3_for_index
|
||||
.deltaof send_slot_count_in_E3_begin, send_slot_count_in_E3_end
|
||||
send_slot_count_in_E3_begin:
|
||||
# ecx = this (TDataProtocol*)
|
||||
# [esp + 4] = slot_index
|
||||
push 0
|
||||
push dword [esp + 8] # slot_index
|
||||
push 0x0C # slot count
|
||||
push 0x00E30010
|
||||
mov eax, esp
|
||||
push 0x10
|
||||
push eax
|
||||
mov eax, [ecx]
|
||||
call [eax + 0x20] # this->send_command(&cmd, 0x10) // ret 8
|
||||
add esp, 8
|
||||
mov eax, 0x006C1ABC
|
||||
call eax # set_current_char_slot(slot_index) // ret 0
|
||||
add esp, 8
|
||||
ret 4
|
||||
send_slot_count_in_E3_end:
|
||||
|
||||
# Show slot number in each menu item
|
||||
.data 0x00401D57
|
||||
.deltaof show_slot_number_begin, show_slot_number_end
|
||||
show_slot_number_begin:
|
||||
# Original call (sprintf(line_buf, "LV%d", preview_info->visual.disp.level + 1))
|
||||
lea edx, [esp + 0x02C4]
|
||||
mov ebx, [ebx + 8]
|
||||
inc ebx
|
||||
push ebx
|
||||
mov ecx, esi
|
||||
push edx
|
||||
mov eax, 0x00402604
|
||||
call eax
|
||||
# Find the end of the string
|
||||
lea eax, [esp + 0x02C4]
|
||||
show_slot_number_strend_again:
|
||||
cmp word [eax], 0
|
||||
je show_slot_number_strend_done
|
||||
add eax, 2
|
||||
jmp show_slot_number_strend_again
|
||||
show_slot_number_strend_done:
|
||||
# Format the slot number and append it to the string
|
||||
mov ecx, [0x00A38BD0] # scroll_bar = TAdScrollBarXb_objs[0]
|
||||
mov ecx, [ecx + 0xAC] # ecx = scroll_bar->selection_state[0].scroll_offset
|
||||
lea ecx, [ecx + ebp + 1]
|
||||
push ecx # Slot number (scroll_offset + z)
|
||||
call get_show_slot_number_suffix_fmt
|
||||
.binary 20002800230025006400290020000000 # L" (#%d) "
|
||||
get_show_slot_number_suffix_fmt:
|
||||
push eax # Destination buffer
|
||||
mov eax, 0x00835578 # _swprintf
|
||||
call eax
|
||||
add esp, 0x0C
|
||||
jmp show_slot_number_end
|
||||
.zero 0x96
|
||||
show_slot_number_end: # 00401E4D
|
||||
|
||||
# End static patches
|
||||
.data 0x00000000
|
||||
.data 0x00000000
|
||||
|
||||
|
||||
|
||||
update_existing_char_file_list:
|
||||
# Replace the existing character list with an appropriately-longer one. This
|
||||
# part does not need to be done if the patch is applied statically to the
|
||||
# executable; this is only necessary when used as a server patch because the
|
||||
# character list is already allocated at the time the patch is applied.
|
||||
push 0x00022FC4 # total file size
|
||||
mov eax, 0x00835915 # operator_new
|
||||
call eax
|
||||
add esp, 4
|
||||
mov edx, [0x00A939C4] # edx = old char_file_list
|
||||
mov [0x00A939C4], eax
|
||||
mov ecx, [edx + 0xBA94] # Copy bgm_test_songs_unlocked_high to new file
|
||||
mov [eax + 0x00022FB4], ecx
|
||||
mov ecx, [edx + 0xBA98] # Copy bgm_test_songs_unlocked_low to new file
|
||||
mov [eax + 0x00022FB8], ecx
|
||||
mov ecx, [edx + 0xBA9C] # Copy save_count to new file
|
||||
mov [eax + 0x00022FBC], ecx
|
||||
mov ecx, [edx + 0xBAA0] # Copy round2_seed to new file
|
||||
mov [eax + 0x00022FC0], ecx
|
||||
add eax, 4
|
||||
add edx, 4
|
||||
mov ecx, 0xBA90
|
||||
call memcpy # Copy the existing 4 characters over
|
||||
mov eax, [0x00A939C4]
|
||||
add eax, 0xBA94
|
||||
mov ecx, 4
|
||||
clear_next_char:
|
||||
cmp ecx, 0x0C # slot count
|
||||
jge clear_next_char_done
|
||||
lea edx, [eax + 0x2EA4] # edx = ptr to next char (or footer)
|
||||
clear_next_char_write_again:
|
||||
mov dword [eax], 0
|
||||
add eax, 4
|
||||
cmp eax, edx
|
||||
jl clear_next_char_write_again
|
||||
clear_next_char_done:
|
||||
|
||||
# Call eh_vector_constructor_iterator(
|
||||
# &char_file_list.chars[4],
|
||||
# sizeof(char_file_list.chars[0]),
|
||||
# countof(char_file_list.chars) - 4,
|
||||
# PSOCharacterFile::init,
|
||||
# PSOCharacterFile::destroy)
|
||||
push 0x006C197C # PSOCharacterFile::destroy
|
||||
push 0x006C182C # PSOCharacterFile::init
|
||||
push 0x08 # slot count - 4
|
||||
push 0x2EA4 # sizeof(PSOCharacterFile)
|
||||
mov eax, [0x00A939C4]
|
||||
add eax, 0xBA94
|
||||
push eax
|
||||
mov eax, 0x00835E86
|
||||
call eax
|
||||
|
||||
# Fix the file's checksum
|
||||
mov eax, [0x00A939C4]
|
||||
mov ecx, 0x006C2738
|
||||
jmp ecx # PSOBBCharacterFileList::checksum(char_file_list)
|
||||
|
||||
|
||||
|
||||
update_existing_char_file_list_memcard:
|
||||
# Allocate a new memory card file area and copy the data there too. It seems
|
||||
# Sega didn't fully strip out the local saving code from PSOBB; instead, they
|
||||
# just made it write to a heap-allocated buffer. Since the file is much
|
||||
# bigger now, we also have to make that heap-allocated buffer larger. We add
|
||||
# a few "blocks" on the end, since the original code in the game does that
|
||||
# too, but it's probably not strictly necessary.
|
||||
# Like the above, this part is not necessary if this patch is statically
|
||||
# applied to the executable.
|
||||
mov eax, 0x00022FC4 # total file size
|
||||
add eax, 0x0000FFFF
|
||||
and eax, 0xFFFFC000
|
||||
push eax
|
||||
mov eax, 0x0084F258
|
||||
call eax # malloc10(total file size)
|
||||
add esp, 4
|
||||
mov [0x00A939AC], eax
|
||||
mov edx, [0x00A939C4]
|
||||
mov ecx, 0x00022FC4 # total file size
|
||||
jmp memcpy
|
||||
@@ -0,0 +1,103 @@
|
||||
# This patch causes the client not to generate its own EXP text and instead use
|
||||
# the EXP values generated by the server when showing the purple text for enemy
|
||||
# deaths. This makes EXP gained via EXP share visible, as well as makes
|
||||
# fractional EXP multiplers (in config.json) display properly.
|
||||
|
||||
.meta name="Server EXP display"
|
||||
.meta description=""
|
||||
.meta hide_from_patches_menu
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
call install_hook
|
||||
call apply_static_patches
|
||||
ret
|
||||
|
||||
|
||||
|
||||
install_hook:
|
||||
pop ecx
|
||||
push 0 # Write address instead of a call/jmp opcode
|
||||
push 0x00A0DC54
|
||||
call get_code_size
|
||||
.deltaof handle_6xBF_start, handle_6xBF_end
|
||||
get_code_size:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call handle_6xBF_end
|
||||
handle_6xBF_start: # [std](G_6xBF* cmd @ [esp + 4]) -> void
|
||||
mov edx, [esp + 4]
|
||||
|
||||
mov ecx, [0x00A9A074] # local_client_id
|
||||
cmp [edx + 2], cx
|
||||
jne skip_text
|
||||
|
||||
cmp byte [edx + 1], 3
|
||||
jl skip_text
|
||||
movzx eax, word [edx + 8] # cmd.from_enemy_id
|
||||
cmp eax, 0x1000
|
||||
jl skip_text
|
||||
cmp eax, 0x1B50
|
||||
jge skip_text
|
||||
call get_enemy_entity
|
||||
|
||||
test eax, eax
|
||||
jnz enemy_entity_ok
|
||||
|
||||
# Use player entity if enemy entity is already gone
|
||||
mov eax, 0x0068D618
|
||||
xchg eax, ecx
|
||||
call ecx # eax = TObjPlayer::for_client_id(local_client_id); conveniently, this function preserves all regs except eax
|
||||
|
||||
enemy_entity_ok:
|
||||
push 0x0000FFFF # entity_id; ignored by TFontSmallTask if not a player
|
||||
push dword [edx + 4] # amount = cmd.amount
|
||||
push 0x00976380 # prefix = L"EXP"
|
||||
push 0x14
|
||||
push 0x14
|
||||
push 0xFFFF00FF # color (ARGB)
|
||||
add eax, 0x300
|
||||
push eax # position
|
||||
mov eax, 0x0078B8E8
|
||||
call eax # TFontSmallTask___new__(...)
|
||||
add esp, 0x1C
|
||||
|
||||
skip_text:
|
||||
mov eax, 0x0069292C # Original handle_6xBF
|
||||
jmp eax # original_handle_6xBF(cmd)
|
||||
|
||||
get_enemy_entity:
|
||||
.include GetEnemyEntity-59NJ
|
||||
ret
|
||||
|
||||
handle_6xBF_end:
|
||||
push ecx
|
||||
.include WriteCallToCode-59NJ
|
||||
|
||||
|
||||
|
||||
apply_static_patches:
|
||||
.include WriteCodeBlocksBB
|
||||
|
||||
.data 0x0078827D
|
||||
.deltaof disable_kill_enemy_callsite_start, disable_kill_enemy_callsite_end
|
||||
disable_kill_enemy_callsite_start:
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
disable_kill_enemy_callsite_end:
|
||||
|
||||
.data 0x00777381
|
||||
.deltaof disable_exp_steal_callsite_start, disable_exp_steal_callsite_end
|
||||
disable_exp_steal_callsite_start:
|
||||
add esp, 0x0C # Original function has `ret 0x0C`
|
||||
nop
|
||||
nop
|
||||
disable_exp_steal_callsite_end:
|
||||
|
||||
.data 0x00000000
|
||||
.data 0x00000000
|
||||
+5
-3
@@ -10,6 +10,8 @@
|
||||
.meta description=""
|
||||
.meta hide_from_patches_menu
|
||||
|
||||
.versions 59NJ 59NL
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
@@ -17,7 +19,7 @@ start:
|
||||
.include WriteCodeBlocksBB
|
||||
|
||||
# Patch 1: rewrite item_is_stackable
|
||||
.data 0x005C502C
|
||||
.data <VERS 0x005C5020 0x005C502C>
|
||||
.deltaof item_is_stackable_start, item_is_stackable_end
|
||||
|
||||
item_is_stackable_start:
|
||||
@@ -32,7 +34,7 @@ item_is_stackable_start:
|
||||
push eax
|
||||
mov ecx, esp
|
||||
|
||||
.binary E8EC130100 # call max_stack_size_for_tool_start
|
||||
.binary <VERS E8D8130100 E8EC130100> # call max_stack_size_for_tool_start
|
||||
pop ecx
|
||||
cmp eax, 1
|
||||
jg return_1
|
||||
@@ -48,7 +50,7 @@ return_1:
|
||||
item_is_stackable_end:
|
||||
|
||||
# Patch 2: rewrite max_stack_size_for_tool
|
||||
.data 0x005D6430
|
||||
.data <VERS 0x005D6410 0x005D6430>
|
||||
.deltaof max_stack_size_for_tool_start, max_stack_size_for_tool_end
|
||||
|
||||
max_stack_size_for_tool_start:
|
||||
@@ -0,0 +1,146 @@
|
||||
# Currently beta quality, map objects that fade like boxes, and Pioneer's
|
||||
# background billboards and elevators still have regular draw distance.
|
||||
# TODO: 90% of stuff is included, bring home the last 10%.
|
||||
|
||||
.meta name="Draw Distance"
|
||||
.meta description="Extends the draw\ndistance of many\nobjects"
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
write_call_func:
|
||||
.include WriteCallToCode-59NJ
|
||||
|
||||
start:
|
||||
mov eax, 0x41800000 # Environment clip distance mod 16.0f
|
||||
mov [0x0097D198], eax # This affects mostly static map objects
|
||||
mov [0x0097D19C], eax
|
||||
mov [0x00097D1A0], eax
|
||||
|
||||
mov ax, 0x9090
|
||||
mov [0x00689BC7], ax # Players draw distance 10000.0f always
|
||||
mov eax, 0x41000000 # Use newly acquired skipped branch room
|
||||
mov [0x00689BD1], eax # to store our float multiplier
|
||||
|
||||
call patch_func_1 # Floor items
|
||||
call patch_func_2 # Whole bunch of stuff, including NPCs
|
||||
call patch_func_3 # Duplicate function from above, reuse same hook
|
||||
call patch_func_4 # TODO: Which objects this affects?
|
||||
call patch_func_5 # TODO: This one too?
|
||||
call patch_func_6 # TODO: And this one?
|
||||
ret
|
||||
|
||||
# Floor items
|
||||
patch_func_1:
|
||||
pop ecx
|
||||
push 8
|
||||
push 0x005C525B
|
||||
call get_code_size1
|
||||
.deltaof patch_code1, patch_code_end1
|
||||
get_code_size1:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end1
|
||||
patch_code1:
|
||||
mov edx, [esp + 0x18]
|
||||
fld st0, dword [0x00689BD1]
|
||||
fld st0, dword [esp + 0x14]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
patch_code_end1:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# Whole bunch of stuff, including NPCs
|
||||
patch_func_2:
|
||||
pop ecx
|
||||
push 9
|
||||
push 0x007BB21E
|
||||
call get_code_size2
|
||||
.deltaof patch_code2, patch_code_end2
|
||||
get_code_size2:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end2
|
||||
patch_code2:
|
||||
test eax, 0x400
|
||||
fld st0, dword [0x00689BD1]
|
||||
fld st0, dword [esp + 0x2C]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
patch_code_end2:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# Duplicate function from above, reuse same hook
|
||||
patch_func_3:
|
||||
mov eax, dword [0x007BB21F]
|
||||
add eax, 0x002A1C74
|
||||
mov dword [0x00518843], eax
|
||||
mov byte [0x00518842], 0xE8
|
||||
mov dword [0x00518847], 0x90909090
|
||||
ret
|
||||
|
||||
# TOComputerMachine01
|
||||
patch_func_4:
|
||||
pop ecx
|
||||
push 7
|
||||
push 0x00616FF4
|
||||
call get_code_size4
|
||||
.deltaof patch_code4, patch_code_end4
|
||||
get_code_size4:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end4
|
||||
patch_code4:
|
||||
lea edx, [edi + 0x38]
|
||||
fld st0, dword [0x00689BD1]
|
||||
fld st0, dword [esp + 0x14]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
patch_code_end4:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# TObjCamera
|
||||
patch_func_5:
|
||||
pop ecx
|
||||
push 6
|
||||
push 0x006439A8
|
||||
call get_code_size5
|
||||
.deltaof patch_code5, patch_code_end5
|
||||
get_code_size5:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end5
|
||||
patch_code5:
|
||||
fld st0, dword [0x00689BD1]
|
||||
fld st0, dword [esp + 0x28]
|
||||
fmulp st1, st0
|
||||
fchs st0
|
||||
ret
|
||||
patch_code_end5:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# TODO: And this one?
|
||||
patch_func_6:
|
||||
pop ecx
|
||||
push 6
|
||||
push 0x0065B959
|
||||
call get_code_size6
|
||||
.deltaof patch_code6, patch_code_end6
|
||||
get_code_size6:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end6
|
||||
patch_code6:
|
||||
mov ebp, ecx
|
||||
fld st0, dword [0x00689BD1]
|
||||
fld st0, dword [esp + 0x30]
|
||||
fmulp st1, st0
|
||||
ret
|
||||
patch_code_end6:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
@@ -0,0 +1,258 @@
|
||||
.meta name="DMC"
|
||||
.meta description="Mitigates effects\nof enemy health\ndesync"
|
||||
.meta client_flag="0x0000001000000000"
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
write_call_to_code_multi:
|
||||
.include WriteCallToCodeMulti-59NJ
|
||||
write_address_of_code:
|
||||
.include WriteAddressOfCode-59NJ
|
||||
|
||||
start:
|
||||
|
||||
# Replace 6x09 with 6xE4 in subcommand handler table
|
||||
mov dword [0x00A0DC30], 0x000600E4 # subcommand=0xE4, flags=6
|
||||
push 0x00A0DC34
|
||||
call +4
|
||||
.deltaof handle_6xE4_start, handle_6xE4_end
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call handle_6xE4_end
|
||||
|
||||
handle_6xE4_start: # (G_6xE4* cmd @ [esp + 4]) -> void
|
||||
push ebx
|
||||
push esi
|
||||
push edi
|
||||
|
||||
test byte [0x00AA8DFC], 0x80
|
||||
jz handle_6xE4_return
|
||||
mov ebx, [esp + 0x10] # cmd
|
||||
movzx eax, word [ebx + 2]
|
||||
cmp eax, 0x1000
|
||||
jl handle_6xE4_return
|
||||
cmp eax, 0x1B50
|
||||
jge handle_6xE4_return
|
||||
|
||||
movzx eax, word [ebx + 2]
|
||||
.include GetEnemyEntity-59NJ # auto* ene = get_enemy_entity(cmd->header.entity_id);
|
||||
push eax
|
||||
|
||||
movzx eax, word [ebx + 2]
|
||||
and eax, 0x0FFF
|
||||
imul eax, eax, 0x0C
|
||||
add eax, [0x00AADE38] # eax = state_for_enemy(cmd->header.entity_id)
|
||||
|
||||
cmp dword [ebx + 0x0C], 0
|
||||
jl handle_6xE4_not_proportional
|
||||
mov cx, [ebx + 0x0A] # cmd->max_hp
|
||||
sub cx, [eax + 0x06] # st.total_damage
|
||||
movzx ecx, cx
|
||||
xor edx, edx
|
||||
cmp ecx, edx
|
||||
cmovl ecx, edx
|
||||
push ecx
|
||||
fild st0, dword [esp] # current_hp = static_cast<float>(max<int32_t>(cmd->max_hp - st.total_damage, 0))
|
||||
fld st0, dword [ebx + 0x0C]
|
||||
fmulp st1, st0
|
||||
fistp dword [esp], st0
|
||||
mov ecx, dword [esp] # adjusted_hit_amount = static_cast<int16_t>(current_hp * cmd->factor)
|
||||
add esp, 4
|
||||
xor edx, edx
|
||||
inc edx
|
||||
cmp ecx, edx
|
||||
cmovl ecx, edx
|
||||
mov [ebx + 0x04], cx # cmd->hit_amount = min<int32_t>(1, adjusted_hit_amount)
|
||||
handle_6xE4_not_proportional:
|
||||
|
||||
movzx edx, word [eax + 0x06] # st.total_damage
|
||||
movsx esi, word [ebx + 0x04] # cmd->hit_amount
|
||||
movzx edi, word [ebx + 0x0A] # cmd->max_hp
|
||||
add edx, esi # st.total_damage + cmd->hit_amount
|
||||
cmp edx, edi
|
||||
jl handle_6xE4_damage_less_than_max_hp
|
||||
mov [eax + 0x06], di # st.total_damage = cmd->max_hp;
|
||||
mov edx, [eax]
|
||||
test edx, 0x800
|
||||
jnz handle_6xE4_return_pop_ene
|
||||
or edx, 0x800
|
||||
mov [eax], edx
|
||||
cmp dword [esp], 0
|
||||
je handle_6xE4_return_pop_ene
|
||||
push edx # out_cmd.flags
|
||||
sub esp, 8
|
||||
mov word [esp], 0x030A # out_cmd.header.{subcommand,size}
|
||||
mov si, [ebx + 2]
|
||||
mov [esp + 2], si # out_cmd.header.entity_id
|
||||
and si, 0x0FFF
|
||||
mov [esp + 4], si # out_cmd.entity_index
|
||||
mov [esp + 6], di # out_cmd.total_damage
|
||||
mov ecx, esp
|
||||
mov edx, 0x00801150
|
||||
call edx # send_and_handle_60(&out_cmd);
|
||||
add esp, 0x10
|
||||
jmp handle_6xE4_return
|
||||
|
||||
handle_6xE4_damage_less_than_max_hp:
|
||||
xor edi, edi
|
||||
cmp edx, edx
|
||||
cmovl edx, edi
|
||||
mov [eax + 0x06], dx # st.total_damage = std::max<int16_t>(st.total_damage + cmd->hit_amount, 0);
|
||||
|
||||
mov edx, eax # edx = ene_st
|
||||
mov eax, [esp] # eax = ene
|
||||
test eax, eax
|
||||
jz handle_6xE4_return_pop_ene
|
||||
mov ecx, eax
|
||||
push edx
|
||||
mov edx, [ecx]
|
||||
call [edx + 0x148] # ene->vtable[0x52](ene, &st);
|
||||
|
||||
handle_6xE4_return_pop_ene:
|
||||
add esp, 4
|
||||
handle_6xE4_return:
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebx
|
||||
ret
|
||||
|
||||
handle_6xE4_end:
|
||||
call write_address_of_code
|
||||
|
||||
|
||||
|
||||
# Write TObjectV00b421c0::incr_hp_with_sync
|
||||
push 5
|
||||
push 0x00775224 # TObjectV00b421c0::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp!
|
||||
push 5
|
||||
push 0x00778063 # TObjectV00b421c0::subtract_hp_if_not_in_state_2
|
||||
push 5
|
||||
push 0x00777AB2 # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x00777B2B # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x00777BFC # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x00777C75 # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x00776D2D # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x007769C2 # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x0077683C # TObjectV00b421c0::v19_handle_hit_special_effects
|
||||
push 5
|
||||
push 0x00776502 # TObjectV00b421c0::v19_handle_hit_special_effects (Devil's/Demon's)
|
||||
push 5
|
||||
push 0x00775B57 # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x00775A23 # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x007757F0 # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x00775606 # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x007754BC # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x00774E3D # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x00774CD6 # TObjectV00b421c0::v18_accept_hit
|
||||
push 5
|
||||
push 0x00774713 # TObjectV00b421c0::v17
|
||||
push 18
|
||||
call +4
|
||||
.deltaof on_add_or_subtract_hp_start, on_add_or_subtract_hp_end
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call on_add_or_subtract_hp_end
|
||||
|
||||
on_add_or_subtract_hp_start: # (TObjectV00b421c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax
|
||||
test byte [0x00AA8DFC], 0x80
|
||||
jz on_add_or_subtract_hp_skip_send
|
||||
movzx eax, word [ecx + 0x1C] # ene->entity_id
|
||||
cmp eax, 0x1000
|
||||
jl on_add_or_subtract_hp_skip_send
|
||||
cmp eax, 0x1B50
|
||||
jge on_add_or_subtract_hp_skip_send
|
||||
|
||||
and eax, 0x0FFF
|
||||
imul eax, eax, 0x0C
|
||||
add eax, [0x00AADE38] # eax = state_for_enemy(cmd->header.entity_id)
|
||||
|
||||
sub esp, 0x10
|
||||
mov word [esp], 0x04E4
|
||||
mov dx, [ecx + 0x1C]
|
||||
mov [esp + 0x02], dx # cmd.entity_id
|
||||
mov dx, [esp + 0x14]
|
||||
cmp dword [esp + 0x10], 0x00775229 # Check if callsite is add_hp
|
||||
jne on_add_or_subtract_hp_skip_negate_amount
|
||||
neg dx
|
||||
on_add_or_subtract_hp_skip_negate_amount:
|
||||
mov [esp + 0x04], dx # cmd.hit_amount
|
||||
mov dx, [eax + 6]
|
||||
mov [esp + 0x06], dx # cmd.total_damage_before_hit
|
||||
mov dx, [ecx + 0x0334]
|
||||
mov [esp + 0x08], dx # cmd.current_hp
|
||||
mov dx, [ecx + 0x02BC]
|
||||
mov [esp + 0x0A], dx # cmd.max_hp
|
||||
mov dword [esp + 0x0C], 0xBF800000 # cmd.factor
|
||||
|
||||
cmp dword [esp + 0x10], 0x00776507 # Check if callsite is Devil's/Demon's
|
||||
jne on_add_or_subtract_hp_not_proportional
|
||||
# esp is 0x18 down from where it is in caller's context
|
||||
mov edx, 100
|
||||
sub edx, [esp + 0x24] # edx = (100 - special_amount)
|
||||
push edx
|
||||
fild st0, dword [esp] # current_hp_factor = static_cast<float>(100 - special_amount)
|
||||
fmul st0, dword [esp + 0x54] # *= weapon_reduction_factor
|
||||
mov dword [esp], 0x42C80000 # 100.0f
|
||||
fdiv st0, dword [esp]
|
||||
add esp, 4
|
||||
fstp dword [esp + 0x0C], st0 # cmd.factor = ((100 - special_amount) * weapon_reduction_factor) / 100
|
||||
on_add_or_subtract_hp_not_proportional:
|
||||
|
||||
mov edx, esp
|
||||
push ecx
|
||||
push 0x10
|
||||
push edx
|
||||
mov ecx, [0x00AA8E04]
|
||||
mov edx, 0x007D4CBC
|
||||
call edx # send_60(root_protocol, &cmd, sizeof(cmd));
|
||||
pop ecx
|
||||
add esp, 0x10
|
||||
|
||||
on_add_or_subtract_hp_skip_send:
|
||||
mov eax, 0x007781F0 # subtract_hp
|
||||
mov edx, 0x007781B0 # add_hp
|
||||
cmp dword [esp], 0x00775229 # Check if callsite is add_hp
|
||||
cmove eax, edx
|
||||
jmp eax
|
||||
|
||||
on_add_or_subtract_hp_end:
|
||||
call write_call_to_code_multi
|
||||
|
||||
|
||||
|
||||
push 5
|
||||
push 0x0078864B
|
||||
push 1
|
||||
call +4
|
||||
.deltaof on_6x0A_patch_start, on_6x0A_patch_end
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call on_6x0A_patch_end
|
||||
|
||||
on_6x0A_patch_start: # (TObjectV00b421c0* this @ ecx, int16_t amount @ [esp + 4]) -> bool @ eax
|
||||
test byte [0x00AA8DFC], 0x80
|
||||
jz on_6x0A_patch_skip_write
|
||||
mov [esp + 0x0A], cx
|
||||
on_6x0A_patch_skip_write:
|
||||
ret
|
||||
|
||||
on_6x0A_patch_end:
|
||||
call write_call_to_code_multi
|
||||
|
||||
|
||||
|
||||
ret
|
||||
+14
-12
@@ -1,45 +1,47 @@
|
||||
.meta name="Enemy HP bars"
|
||||
.meta description="Shows HP bars in\nenemy info windows"
|
||||
|
||||
.versions 59NJ 59NL
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include WriteCodeBlocksBB
|
||||
.data 0x007318DD
|
||||
.data <VERS 0x0073197D 0x007318DD>
|
||||
.data 6
|
||||
.binary 81E2FDFFFFFF
|
||||
.data 0x00731F2F
|
||||
.data <VERS 0x00731FCF 0x00731F2F>
|
||||
.data 1
|
||||
.binary FA
|
||||
.data 0x009F2DA4
|
||||
.data <VERS 0x009F0DA4 0x009F2DA4>
|
||||
.data 4
|
||||
.data 0x42480000
|
||||
.data 0x009F2DAC
|
||||
.data <VERS 0x009F0DAC 0x009F2DAC>
|
||||
.data 4
|
||||
.data 0x41C00000
|
||||
.data 0x009F2DD4
|
||||
.data <VERS 0x009F0DD4 0x009F2DD4>
|
||||
.data 4
|
||||
.data 0x42480000
|
||||
.data 0x009F2DDC
|
||||
.data <VERS 0x009F0DDC 0x009F2DDC>
|
||||
.data 4
|
||||
.data 0x41C00000
|
||||
.data 0x009F2E04
|
||||
.data <VERS 0x009F0E04 0x009F2E04>
|
||||
.data 4
|
||||
.data 0x42480000
|
||||
.data 0x009F2E0C
|
||||
.data <VERS 0x009F0E0C 0x009F2E0C>
|
||||
.data 4
|
||||
.data 0x41C00000
|
||||
.data 0x009F2E34
|
||||
.data <VERS 0x009F0E34 0x009F2E34>
|
||||
.data 4
|
||||
.data 0x42480000
|
||||
.data 0x009F2E3C
|
||||
.data <VERS 0x009F0E3C 0x009F2E3C>
|
||||
.data 4
|
||||
.data 0x41C00000
|
||||
.data 0x009F2E64
|
||||
.data <VERS 0x009F0E64 0x009F2E64>
|
||||
.data 4
|
||||
.data 0x42200000
|
||||
.data 0x009F2E80
|
||||
.data <VERS 0x009F0E80 0x009F2E80>
|
||||
.data 4
|
||||
.data 0xFF00FF15
|
||||
.data 0x00000000
|
||||
@@ -1,17 +0,0 @@
|
||||
# This function implements $exit in a game when no quest is loaded.
|
||||
|
||||
.meta name="Exit anywhere"
|
||||
.meta description=""
|
||||
.meta hide_from_patches_menu
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
start:
|
||||
xor eax, eax
|
||||
mov [0x00A95624], eax # is_in_quest = false
|
||||
mov [0x00A955E0], eax # dat_source_type = NONE
|
||||
inc eax
|
||||
mov [0x00AAE6D4], ax # should_leave_game = true
|
||||
ret
|
||||
@@ -0,0 +1,19 @@
|
||||
# This function implements $exit in a game when no quest is loaded.
|
||||
|
||||
.meta name="Exit anywhere"
|
||||
.meta description=""
|
||||
.meta hide_from_patches_menu
|
||||
|
||||
.versions 59NJ 59NL
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
start:
|
||||
xor eax, eax
|
||||
mov [<VERS 0x00A931A4 0x00A95624>], eax # is_in_quest = false
|
||||
mov [<VERS 0x00A93160 0x00A955E0>], eax # dat_source_type = NONE
|
||||
inc eax
|
||||
mov [<VERS 0x00AAC254 0x00AAE6D4>], ax # should_leave_game = true
|
||||
ret
|
||||
+4
-2
@@ -1,19 +1,21 @@
|
||||
.meta name="Fast tekker"
|
||||
.meta description="Skips wind-up sound\nat tekker window"
|
||||
|
||||
.versions 59NJ 59NL
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include WriteCodeBlocksBB
|
||||
|
||||
.data 0x006DA113
|
||||
.data <VERS 0x006DA14B 0x006DA113>
|
||||
.deltaof patch1_start, patch1_end
|
||||
patch1_start:
|
||||
mov dword [edi + 0x14C], 1
|
||||
patch1_end:
|
||||
|
||||
.data 0x006DA130
|
||||
.data <VERS 0x006DA168 0x006DA130>
|
||||
.deltaof patch2_start, patch2_end
|
||||
patch2_start:
|
||||
nop
|
||||
@@ -0,0 +1,34 @@
|
||||
.meta name="MAG alert"
|
||||
.meta description="Plays a sound when\nyour MAG is hungry"
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
pop ecx
|
||||
push 6
|
||||
push 0x005D91BE
|
||||
call get_code_size
|
||||
.deltaof patch_code, patch_code_end
|
||||
get_code_size:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end
|
||||
patch_code: # [eax] (TItemMag* this @ ecx) -> void
|
||||
mov dword [ecx + 0x01B8], eax
|
||||
mov eax, [ecx + 0x00F8]
|
||||
movzx eax, word [eax + 0x001C] # eax = this->owner_player->entity_id
|
||||
cmp [0x00A9A074], eax
|
||||
jne patch_code_skip_sound
|
||||
push 0
|
||||
push 0
|
||||
push 0
|
||||
push 0xAC
|
||||
mov eax, 0x00815020
|
||||
call eax
|
||||
add esp, 0x10
|
||||
patch_code_skip_sound:
|
||||
ret
|
||||
patch_code_end:
|
||||
push ecx
|
||||
.include WriteCallToCode-59NJ
|
||||
@@ -0,0 +1,43 @@
|
||||
# Original patch by Soly, in Blue Burst Patch Project
|
||||
# https://github.com/Solybum/Blue-Burst-Patch-Project
|
||||
|
||||
.meta name="No rare selling"
|
||||
.meta description="Stops you from accidentally\nselling rares to vendors"
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
# This works by setting the item price to zero if it's rare, which causes
|
||||
# the game to prevent you from selling the item. For armors and weapons, this
|
||||
# is easy because there are easily-patchable opcodes within branches that
|
||||
# return a constant price for rare items.
|
||||
xor eax, eax
|
||||
mov [0x005D258F], eax # Rare armors
|
||||
mov [0x005D26D1], eax # Unidentified weapons
|
||||
mov [0x005D26E6], eax # Rare weapons
|
||||
|
||||
# For tools, it's harder to implement this, because the price comes from the
|
||||
# ItemPMT tools table and there is no branch for rares. Still, we can add a
|
||||
# branch to a stub to handle tools.
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x005D2508
|
||||
call get_code_size
|
||||
.deltaof patch_code, patch_code_end
|
||||
get_code_size:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end
|
||||
patch_code:
|
||||
# TODO: It'd be nice to have something like WriteJumpToAndFromCode, since
|
||||
# this hook is supposed to return to a different place than where it was
|
||||
# called, hence this mov [esp].
|
||||
mov dword [esp], 0x005D2556
|
||||
xor edi, edi
|
||||
test byte [eax + 0x14], 0x80 # flags & 0x80 = is rare
|
||||
cmovz edi, [eax + 0x10] # Use price from table if not rare
|
||||
ret
|
||||
patch_code_end:
|
||||
push ecx
|
||||
.include WriteCallToCode-59NJ
|
||||
@@ -0,0 +1,230 @@
|
||||
# Original patch by Soly, in Blue Burst Patch Project
|
||||
# https://github.com/Solybum/Blue-Burst-Patch-Project
|
||||
|
||||
.meta name="Palette"
|
||||
.meta description="Enables the alternate action\npalette for number keys"
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
|
||||
write_call_func:
|
||||
.include WriteCallToCode-59NJ
|
||||
|
||||
start:
|
||||
mov al, 0xEB
|
||||
mov [0x0068A7A5], al # SecondaryPaletteAttack1
|
||||
xor al, al
|
||||
mov [0x006A11B7], al # SecondaryPaletteAttack2
|
||||
mov [0x006A0CB7], al # SecondaryPaletteAttack3
|
||||
|
||||
call patch_func_1 # GetCurrentPalette
|
||||
call patch_func_2 # CheckHotkey1_1
|
||||
call patch_func_3 # CheckHotkey1_2
|
||||
call patch_func_4 # CheckHotkey2_1
|
||||
call patch_func_5 # CheckHotkey2_2
|
||||
call patch_func_6 # CheckHotkey3_1
|
||||
call patch_func_7 # CheckHotkey3_2
|
||||
jmp write_code_blocks # UnsetHotkey1, UnsetHotkey2, SetHotkey
|
||||
|
||||
# GetCurrentPalette
|
||||
patch_func_1:
|
||||
pop ecx
|
||||
push 8
|
||||
push 0x00748990
|
||||
call get_code_size1
|
||||
.deltaof patch_code1, patch_code_end1
|
||||
get_code_size1:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end1
|
||||
patch_code1:
|
||||
mov edx, [ebp - 0x14]
|
||||
mov edx, [edx + 0x2C]
|
||||
movzx edx, byte [edx + 0x62]
|
||||
test edx, edx
|
||||
setnz byte [0x00748B1B]
|
||||
mov edx, edi
|
||||
and edx, 0xFF
|
||||
ret
|
||||
patch_code_end1:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey1_1
|
||||
patch_func_2:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x007489DE
|
||||
call get_code_size2
|
||||
.deltaof patch_code2, patch_code_end2
|
||||
get_code_size2:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end2
|
||||
patch_code2:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx edx, byte [eax + esi * 4 + 0x04] # main palette
|
||||
ret
|
||||
movzx edx, byte [eax + esi * 4 + 0x3C] # alt palette
|
||||
ret
|
||||
patch_code_end2:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey1_2
|
||||
patch_func_3:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x007489ED
|
||||
call get_code_size3
|
||||
.deltaof patch_code3, patch_code_end3
|
||||
get_code_size3:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end3
|
||||
patch_code3:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx ecx, byte [eax + ecx * 2 + 0x05] # main palette
|
||||
ret
|
||||
movzx ecx, byte [eax + ecx * 2 + 0x3D] # alt palette
|
||||
ret
|
||||
patch_code_end3:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey2_1
|
||||
patch_func_4:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x00748A88
|
||||
call get_code_size4
|
||||
.deltaof patch_code4, patch_code_end4
|
||||
get_code_size4:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end4
|
||||
patch_code4:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx edx, byte [edx + ebx * 4 + 0x04] # main palette
|
||||
ret
|
||||
movzx edx, byte [edx + ebx * 4 + 0x3C] # alt palette
|
||||
ret
|
||||
patch_code_end4:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey2_2
|
||||
patch_func_5:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x00748A97
|
||||
call get_code_size5
|
||||
.deltaof patch_code5, patch_code_end5
|
||||
get_code_size5:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end5
|
||||
patch_code5:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx ecx, byte [edx + eax * 2 + 0x05] # main palette
|
||||
ret
|
||||
movzx ecx, byte [edx + eax * 2 + 0x3D] # alt palette
|
||||
ret
|
||||
patch_code_end5:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey3_1
|
||||
patch_func_6:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x007103D3
|
||||
call get_code_size6
|
||||
.deltaof patch_code6, patch_code_end6
|
||||
get_code_size6:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end6
|
||||
patch_code6:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx ecx, byte [eax + edx * 4 + 0x04] # main palette
|
||||
ret
|
||||
movzx ecx, byte [eax + edx * 4 + 0x3C] # alt palette
|
||||
ret
|
||||
patch_code_end6:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
# CheckHotkey3_2
|
||||
patch_func_7:
|
||||
pop ecx
|
||||
push 5
|
||||
push 0x007103DC
|
||||
call get_code_size7
|
||||
.deltaof patch_code7, patch_code_end7
|
||||
get_code_size7:
|
||||
pop eax
|
||||
push dword [eax]
|
||||
call patch_code_end7
|
||||
patch_code7:
|
||||
cmp byte [0x00748B1B], 0
|
||||
jnz +0x06
|
||||
movzx ecx, byte [eax + edx * 4 + 0x05] # main palette
|
||||
ret
|
||||
movzx ecx, byte [eax + edx * 4 + 0x3D] # alt palette
|
||||
ret
|
||||
patch_code_end7:
|
||||
push ecx
|
||||
jmp write_call_func
|
||||
|
||||
write_code_blocks:
|
||||
.include WriteCodeBlocksBB
|
||||
|
||||
.data 0x00748A05
|
||||
.deltaof code_block1_start, code_block1_end
|
||||
|
||||
# UnsetHotkey1
|
||||
code_block1_start:
|
||||
push dword [0x00748B1B]
|
||||
push eax
|
||||
mov eax, 0x0068CE4C # SetPaletteHotkey
|
||||
call eax
|
||||
.binary 909090909090909090
|
||||
code_block1_end:
|
||||
.data 0x00748AAB
|
||||
.deltaof code_block2_start, code_block2_end
|
||||
|
||||
# UnsetHotkey2
|
||||
code_block2_start:
|
||||
push dword [0x00748B1B]
|
||||
push eax
|
||||
mov eax, 0x0068CE4C # SetPaletteHotkey
|
||||
call eax
|
||||
.binary 909090909090909090
|
||||
code_block2_end:
|
||||
.data 0x00748B0A
|
||||
.deltaof code_block3_start, code_block3_end
|
||||
|
||||
# SetHotkey
|
||||
code_block3_start:
|
||||
mov eax, [ebp - 0x24]
|
||||
mov ecx, [ebp - 0x28]
|
||||
movzx ebx, word [eax]
|
||||
movzx edx, word [eax + 0x02]
|
||||
push edx
|
||||
push ebx
|
||||
push esi
|
||||
.binary 6800000000 # tmpCurrentPalette = 0x00748B1B
|
||||
push 0
|
||||
mov eax, 0x0068CE4C # SetPaletteHotkey
|
||||
call eax
|
||||
.binary 90909090909090909090909090909090
|
||||
code_block3_end:
|
||||
.data 0x00000000
|
||||
.data 0x00000000
|
||||
@@ -0,0 +1,44 @@
|
||||
# (uint16_t entity_id @ eax) -> TObjectV00b421c0* @ eax
|
||||
# Preserves all registers except eax
|
||||
get_enemy_entity:
|
||||
push esi
|
||||
push edi
|
||||
push edx
|
||||
push ecx
|
||||
xor edx, edx
|
||||
xchg edx, eax
|
||||
cmp edx, 0x1000
|
||||
jl done
|
||||
cmp edx, 0x4000
|
||||
jge done
|
||||
|
||||
mov esi, [0x00AABCE8] # bs_low = next_player_entity_index
|
||||
mov edi, [0x00AABCE4]
|
||||
lea edi, [edi + esi - 1] # bs_high = next_player_entity_index + next_enemy_entity_index - 1
|
||||
bs_again:
|
||||
cmp esi, edi
|
||||
jge bs_done
|
||||
lea ecx, [esi + edi]
|
||||
shr ecx, 1
|
||||
mov eax, [ecx * 4 + 0x00AAB2A0] # all_entities[ecx]
|
||||
cmp [eax + 0x1C], dx
|
||||
jge bs_not_less
|
||||
lea esi, [ecx + 1]
|
||||
jmp bs_again
|
||||
bs_not_less:
|
||||
mov edi, ecx
|
||||
jmp bs_again
|
||||
bs_done:
|
||||
|
||||
mov eax, [esi * 4 + 0x00AAB2A0] # all_entities[bs_low]
|
||||
test eax, eax
|
||||
je done
|
||||
xor ecx, ecx
|
||||
cmp [eax + 0x1C], dx
|
||||
cmovne eax, ecx
|
||||
|
||||
done:
|
||||
pop ecx
|
||||
pop edx
|
||||
pop edi
|
||||
pop esi
|
||||
@@ -0,0 +1,42 @@
|
||||
# This file defines the following function:
|
||||
# write_address_of_code(
|
||||
# const void* patch_code,
|
||||
# size_t patch_code_size,
|
||||
# void** ptr_addr);
|
||||
# This function allocates memory for patch_code, copies patch_code to that
|
||||
# memory, then writes the address of the allocated code at the specified
|
||||
# pointer. The allocated memory is never freed.
|
||||
# This function pops its arguments off the stack before returning.
|
||||
|
||||
write_call_to_code:
|
||||
# [esp + 0x04] = code ptr
|
||||
# [esp + 0x08] = code size
|
||||
# [esp + 0x0C] = ptr addr
|
||||
|
||||
# Allocate memory for the copied code
|
||||
mov ecx, [0x00AA8F84]
|
||||
push dword [esp + 0x08]
|
||||
mov eax, 0x007A984C
|
||||
call eax # malloc7
|
||||
test eax, eax
|
||||
je done
|
||||
|
||||
# Copy the code to the newly-allocated memory
|
||||
# eax = dest pointer (from malloc7 call above)
|
||||
mov edx, [esp + 0x04] # edx = source pointer
|
||||
mov ecx, [esp + 0x08] # ecx = source size
|
||||
push ebx
|
||||
memcpy_again:
|
||||
dec ecx
|
||||
mov bl, [edx + ecx] # Copy one byte from source to dest
|
||||
mov [eax + ecx], bl
|
||||
test ecx, ecx
|
||||
jne memcpy_again
|
||||
pop ebx
|
||||
|
||||
# Write the address
|
||||
mov ecx, [esp + 0x0C]
|
||||
mov [ecx], eax
|
||||
|
||||
done:
|
||||
ret 0x0C
|
||||
@@ -0,0 +1,76 @@
|
||||
# This file defines the following function:
|
||||
# write_call_to_code(
|
||||
# const void* patch_code,
|
||||
# size_t patch_code_size,
|
||||
# void* call_opcode_address,
|
||||
# ssize_t call_opcode_bytes);
|
||||
# This function allocates memory for patch_code, copies patch_code to that
|
||||
# memory, then writes a call or jmp opcode to call_opcode_address that calls
|
||||
# the code in the allocated memory region. The allocated memory is never freed.
|
||||
# call_opcode_bytes specifies how many bytes at the callsite should be
|
||||
# overwritten. This value must be at least 5; the first 5 bytes are overwritten
|
||||
# with the call/jmp opcode itself; the rest are overwritten with nop opcodes.
|
||||
# If call_opcode_bytes is positive, a call opcode is written; if it's negative,
|
||||
# a jmp opcode is written.
|
||||
# This function pops its arguments off the stack before returning.
|
||||
|
||||
write_call_to_code:
|
||||
# [esp + 0x04] = code ptr
|
||||
# [esp + 0x08] = code size
|
||||
# [esp + 0x0C] = jump callsite
|
||||
# [esp + 0x10] = callsite size (if zero, write the address instead of a call)
|
||||
|
||||
# Allocate memory for the copied code
|
||||
mov ecx, [0x00AA8F84]
|
||||
push dword [esp + 0x08]
|
||||
mov eax, 0x007A984C
|
||||
call eax # malloc7
|
||||
test eax, eax
|
||||
je done
|
||||
|
||||
# Copy the code to the newly-allocated memory
|
||||
# eax = dest pointer (from malloc7 call above)
|
||||
mov edx, [esp + 0x04] # edx = source pointer
|
||||
mov ecx, [esp + 0x08] # ecx = source size
|
||||
push ebx
|
||||
memcpy_again:
|
||||
dec ecx
|
||||
mov bl, [edx + ecx] # Copy one byte from source to dest
|
||||
mov [eax + ecx], bl
|
||||
test ecx, ecx
|
||||
jne memcpy_again
|
||||
pop ebx
|
||||
|
||||
mov edx, [esp + 0x0C] # edx = jump callsite
|
||||
|
||||
# If the callsite size is zero, just write the address directly
|
||||
cmp dword [esp + 0x10], 0
|
||||
jne write_call_or_jmp
|
||||
mov [edx], eax
|
||||
jmp done
|
||||
|
||||
# Write the call or jmp opcode
|
||||
write_call_or_jmp:
|
||||
lea ecx, [eax - 5]
|
||||
sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5
|
||||
cmp dword [esp + 0x10], 0
|
||||
setl al
|
||||
or al, 0xE8
|
||||
mov [edx], al # Write E8 (call), or E9 (jmp) if size was negative
|
||||
mov [edx + 1], ecx # Write delta
|
||||
|
||||
# Write as many nops after the call opcode as necessary
|
||||
mov ecx, 5
|
||||
mov eax, [esp + 0x10]
|
||||
cmp eax, 0
|
||||
jge write_nop_again
|
||||
neg eax
|
||||
write_nop_again:
|
||||
cmp ecx, eax
|
||||
jge done
|
||||
mov byte [edx + ecx], 0x90
|
||||
inc ecx
|
||||
jmp write_nop_again
|
||||
|
||||
done:
|
||||
ret 0x10
|
||||
@@ -0,0 +1,83 @@
|
||||
# This file defines the following function:
|
||||
# void [/std] write_call_to_code(
|
||||
# const void* patch_code @ [esp + 0x04],
|
||||
# size_t patch_code_size @ [esp + 0x08],
|
||||
# size_t call_count @ [esp + 0x0C],
|
||||
# void* call_opcode_address @ [esp + 0x10],
|
||||
# ssize_t call_opcode_bytes @ [esp + 0x14],
|
||||
# ...);
|
||||
# This function allocates memory for patch_code, copies patch_code to that
|
||||
# memory, then writes a call or jmp opcode to call_opcode_address that calls
|
||||
# the code in the allocated memory region. The allocated memory is never freed.
|
||||
# call_opcode_bytes specifies how many bytes at the callsite should be
|
||||
# overwritten. This value must be at least 5; the first 5 bytes are overwritten
|
||||
# with the call/jmp opcode itself; the rest are overwritten with nop opcodes.
|
||||
# This function pops its arguments off the stack before returning (including
|
||||
# all the varargs).
|
||||
|
||||
write_call_to_code:
|
||||
# [esp + 0x04] = code ptr
|
||||
# [esp + 0x08] = code size
|
||||
# [esp + 0x0C] = callsite count
|
||||
# [esp + 0x10] = callsite address
|
||||
# [esp + 0x14] = callsite size
|
||||
# ... (further callsite address/size pairs)
|
||||
|
||||
# Allocate memory for the copied code
|
||||
mov ecx, [0x00AA8F84]
|
||||
push dword [esp + 0x08]
|
||||
mov eax, 0x007A984C
|
||||
call eax # malloc7
|
||||
test eax, eax
|
||||
je done
|
||||
|
||||
# Copy the code to the newly-allocated memory
|
||||
# eax = dest pointer (from malloc7 call above)
|
||||
mov edx, [esp + 0x04] # edx = source pointer
|
||||
mov ecx, [esp + 0x08] # ecx = source size
|
||||
push ebx
|
||||
memcpy_again:
|
||||
dec ecx
|
||||
mov bl, [edx + ecx] # Copy one byte from source to dest
|
||||
mov [eax + ecx], bl
|
||||
test ecx, ecx
|
||||
jne memcpy_again
|
||||
pop ebx
|
||||
|
||||
# Write the call opcodes
|
||||
xchg ebx, [esp + 0x0C] # Save ebx; get callsite count
|
||||
mov [esp - 0x08], esi
|
||||
mov [esp - 0x0C], eax
|
||||
mov esi, 0x10 # Stack offset of first callsite pair
|
||||
|
||||
next_callsite:
|
||||
mov edx, [esp + esi] # edx = jump callsite
|
||||
lea ecx, [eax - 5]
|
||||
sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5
|
||||
mov byte [edx], 0xE8
|
||||
mov [edx + 1], ecx # Write E8 (call) followed by delta
|
||||
|
||||
# Write as many nops after the call opcode as necessary
|
||||
mov ecx, 5
|
||||
mov eax, [esp + esi + 4]
|
||||
write_nop_again:
|
||||
cmp ecx, eax
|
||||
jge this_callsite_done
|
||||
mov byte [edx + ecx], 0x90
|
||||
inc ecx
|
||||
jmp write_nop_again
|
||||
|
||||
this_callsite_done:
|
||||
mov eax, [esp - 0x0C]
|
||||
add esi, 8
|
||||
dec ebx
|
||||
jnz next_callsite
|
||||
|
||||
mov ecx, esi
|
||||
mov ebx, [esp + 0x0C]
|
||||
mov esi, [esp - 0x08]
|
||||
|
||||
done:
|
||||
mov eax, [esp]
|
||||
add esp, ecx
|
||||
jmp eax
|
||||
@@ -576,14 +576,14 @@
|
||||
],
|
||||
|
||||
// BB bank size. If you change either of these values, you must also add "BankSize" to BBRequiredPatches, and change
|
||||
// the patch contents in system/client-functions/BlueBurstExclusive/BankSize.59NL.patch.s to reflect the counts here.
|
||||
// the patch contents in system/client-functions/BlueBurstExclusive/BankSize.5___.patch.s to reflect the counts here.
|
||||
"BBMaxBankItems": 200,
|
||||
"BBMaxBankMeseta": 999999,
|
||||
|
||||
// Item stack limits. Note that changing these does not affect the client's behavior automatically - this only exists
|
||||
// to allow the server to understand the behavior of clients that are already patched with different stack limits.
|
||||
// If you want to use an unpatched BB client but still have custom stack limits, you can use the StackLimits runtime
|
||||
// patch by editing system/client-functions/BlueBurstExclusive/StackLimits.59NL.patch.s to match the BB stack limits
|
||||
// patch by editing system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s to match the BB stack limits
|
||||
// and adding "StackLimits" to the BBRequiredPatches list.
|
||||
// It's important that all players in the same game have the same stack limits, both on the client and server! So, if
|
||||
// you change the BB stack limits, you must either prevent BB from playing with other versions (see
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
ItemPMT-pc-v2.prs
|
||||
@@ -1 +0,0 @@
|
||||
ItemPMT-gc-v3.prs
|
||||
@@ -1 +0,0 @@
|
||||
ItemPMT-gc-v3.prs
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
ItemPMT-pc-v2.prs
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
item-parameter-table-pc-v2.json
|
||||
@@ -0,0 +1 @@
|
||||
item-parameter-table-gc-v3.json
|
||||
@@ -0,0 +1 @@
|
||||
item-parameter-table-gc-v3.json
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
item-parameter-table-pc-v2.json
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,15 @@
|
||||
[
|
||||
// This table maps in-game primary identifiers across client versions. The
|
||||
// high bit of the entries in this table specifies the entry type; if this
|
||||
// bit is set, the entry is not "canonical"; that is, it's a one-way mapping
|
||||
// and the client does not actually have that item defined. Each primary
|
||||
// identifier must have at most one "canonical" listing for each version
|
||||
// (but may have none). To convert an item from client A to client B:
|
||||
// 1. Scan the column for A's version in the table to find the cell that
|
||||
// contains the primary identifier sent by A. (We optimize this with an
|
||||
// index.)
|
||||
// This table maps in-game primary identifiers across client versions. The high bit of the entries in this table
|
||||
// specifies the entry type; if this bit is set, the entry is not "canonical"; that is, it's a one-way mapping and
|
||||
// the client does not actually have that item defined. Each primary identifier must have at most one "canonical"
|
||||
// listing for each version (but may have none). To convert an item from client A to client B:
|
||||
// 1. Scan the column for A's version in the table to find the cell that contains the primary identifier sent by A.
|
||||
// (We optimize this with an index.)
|
||||
// 2. Move left or right to the column for B's version.
|
||||
// 3. Take the value from that cell, clear the high bit if needed, and use
|
||||
// that as the primary identifier to show to B.
|
||||
// 3. Take the value from that cell, clear the high bit if needed, and use that as the primary identifier for B.
|
||||
// This logic is implemented in ItemTranslationTable.cc.
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, "UNUSED"],
|
||||
[0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000, "Saber"],
|
||||
[0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, 0x00010100, "Brand"],
|
||||
@@ -134,7 +130,6 @@
|
||||
[0x000F0100, 0x000F0100, 0x000F0100, 0x000F0100, 0x000F0100, 0x000F0100, 0x800F0000, 0x000F0100, 0x000F0100, 0x000F0100, 0x000F0100, 0x000F0100, "ANGRY FIST"],
|
||||
[0x000F0200, 0x000F0200, 0x000F0200, 0x000F0200, 0x000F0200, 0x000F0200, 0x800F0000, 0x000F0200, 0x000F0200, 0x000F0200, 0x000F0200, 0x000F0200, "GOD HAND"],
|
||||
[0x000F0300, 0x000F0300, 0x000F0300, 0x000F0300, 0x000F0300, 0x000F0300, 0x800F0000, 0x000F0300, 0x000F0300, 0x000F0300, 0x000F0300, 0x000F0300, "SONIC KNUCKLE"],
|
||||
[0x000F0400, 0x000F0400, 0x000F0400, 0x000F0400, 0x000F0400, 0x000F0400, 0x80020500, 0x80100000, 0x80100000, 0x80100000, 0x80100000, 0x80100000, "OROTIAGITO (v0-v2)"],
|
||||
[0x800F0300, 0x800F0300, 0x800F0300, 0x800F0300, 0x800F0300, 0x800F0300, 0x800F0000, 0x800F0300, 0x800F0300, 0x800F0300, 0x800F0300, 0x000F0400, "LOGiN"],
|
||||
[0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, 0x00100000, "OROTIAGITO"],
|
||||
[0x00100100, 0x00100100, 0x00100100, 0x00100100, 0x00100100, 0x00100100, 0x80100000, 0x00100100, 0x00100100, 0x00100100, 0x00100100, 0x00100100, "AGITO (1975)"],
|
||||
@@ -439,7 +434,9 @@
|
||||
[0x80080500, 0x80080500, 0x80080500, 0x80770000, 0x80770000, 0x80770000, 0x80080500, 0x80770000, 0x80770000, 0x80770000, 0x80770000, 0x00EA0000, "TypeME/MECHGUN"],
|
||||
[0x80090500, 0x80090500, 0x80090500, 0x80780000, 0x80780000, 0x80780000, 0x80090500, 0x80780000, 0x80780000, 0x80780000, 0x80780000, 0x00EB0000, "TypeSH/SHOT"],
|
||||
[0x800C0400, 0x800C0400, 0x800C0400, 0x807B0000, 0x807B0000, 0x807B0000, 0x800C0400, 0x807B0000, 0x807B0000, 0x807B0000, 0x807B0000, 0x00EC0000, "TypeWA/WAND"],
|
||||
[0x80010000, 0x80010000, 0x80010000, 0x00890000, 0x00890000, 0x00890000, 0x008D0000, 0x00AA0000, 0x00AA0000, 0x00AA0000, 0x00AA0000, 0x00ED0000, "???? (WEAPON)"],
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, 0x01010000, "Frame"],
|
||||
[0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, 0x01010100, "Armor"],
|
||||
[0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, 0x01010200, "Psy Armor"],
|
||||
@@ -528,8 +525,9 @@
|
||||
[0x81012800, 0x81012800, 0x81012800, 0x81013400, 0x81013400, 0x81013400, 0x81010C00, 0x81013400, 0x81013400, 0x81013400, 0x81013400, 0x01015500, "UNION FIELD"],
|
||||
[0x81012800, 0x81012800, 0x81012800, 0x81013400, 0x81013400, 0x81013400, 0x81010C00, 0x81013400, 0x81013400, 0x81013400, 0x81013400, 0x01015600, "SAMURAI ARMOR"],
|
||||
[0x81012800, 0x81012800, 0x81012800, 0x81013400, 0x81013400, 0x81013400, 0x81010C00, 0x81013400, 0x81013400, 0x81013400, 0x81013400, 0x01015700, "STEALTH SUIT"],
|
||||
[0x81012800, 0x81012800, 0x81012800, 0x81013400, 0x81013400, 0x81013400, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01015800, "???? (ARMOR)"],
|
||||
[0x81012800, 0x81012800, 0x81012800, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01013500, 0x01015800, "???? (ARMOR)"],
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, 0x01020000, "Barrier"],
|
||||
[0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, 0x01020100, "Shield"],
|
||||
[0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, 0x01020200, "Core Shield"],
|
||||
@@ -695,8 +693,9 @@
|
||||
[0x81022600, 0x81022600, 0x81022600, 0x81023500, 0x81023500, 0x81023500, 0x81020C00, 0x81028400, 0x81028400, 0x81028400, 0x81028400, 0x0102A200, "GENPEI (unused)"],
|
||||
[0x81022600, 0x81022600, 0x81022600, 0x81023500, 0x81023500, 0x81023500, 0x81020C00, 0x81028400, 0x81028400, 0x81028400, 0x81028400, 0x0102A300, "GENPEI (unused)"],
|
||||
[0x81022600, 0x81022600, 0x81022600, 0x81023500, 0x81023500, 0x81023500, 0x81020C00, 0x81028400, 0x81028400, 0x81028400, 0x81028400, 0x0102A400, "GENPEI (unused)"],
|
||||
[0x81022600, 0x81022600, 0x81022600, 0x81023500, 0x81023500, 0x81023500, 0x81020C00, 0x01028500, 0x01028500, 0x01028500, 0x01028500, 0x0102A500, "???? (SHIELD)"],
|
||||
[0x81022600, 0x81022600, 0x81022600, 0x01023A00, 0x01023A00, 0x01023A00, 0x81020C00, 0x01028500, 0x01028500, 0x01028500, 0x01028500, 0x0102A500, "???? (SHIELD)"],
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, 0x01030000, "Knight/Power"],
|
||||
[0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, 0x01030100, "General/Power"],
|
||||
[0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, 0x01030200, "Ogre/Power"],
|
||||
@@ -799,8 +798,9 @@
|
||||
[0x81033500, 0x81033500, 0x81033500, 0x81033500, 0x81033500, 0x81033500, 0x81033400, 0x81033500, 0x81033500, 0x81033500, 0x81033500, 0x01036100, "HP/Resurrection"],
|
||||
[0x81033800, 0x81033800, 0x81033800, 0x81033800, 0x81033800, 0x81033800, 0x81033700, 0x81033800, 0x81033800, 0x81033800, 0x81033800, 0x01036200, "TP/Resurrection"],
|
||||
[0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033A00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x01036300, "PB/Increase"],
|
||||
[0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x81033B00, 0x01034700, 0x01034800, 0x01034800, 0x01034800, 0x01034800, 0x01036400, "???? (UNIT)"],
|
||||
[0x81033B00, 0x81033B00, 0x81033B00, 0x01034400, 0x01034400, 0x01034400, 0x01034700, 0x01034800, 0x01034800, 0x01034800, 0x01034800, 0x01036400, "???? (UNIT)"],
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, 0x02000000, "Mag"],
|
||||
[0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, 0x02010000, "Varuna"],
|
||||
[0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, "Mitra"],
|
||||
@@ -841,10 +841,10 @@
|
||||
[0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, 0x02250000, "Naraka"],
|
||||
[0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, 0x02260000, "Madhu"],
|
||||
[0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, 0x02270000, "Churel"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x02280000, 0x02280000, 0x02280000, 0x82000000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, "ROBOCHAO"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x02290000, 0x02290000, 0x02290000, 0x82000000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, "OPA-OPA"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x022A0000, 0x022A0000, 0x022A0000, 0x82000000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, "PIAN"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x022B0000, 0x022B0000, 0x022B0000, 0x82000000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, "CHAO"],
|
||||
[0x02280000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, 0x82000000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, 0x02280000, "ROBOCHAO"],
|
||||
[0x02290000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, 0x82000000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, 0x02290000, "OPA-OPA"],
|
||||
[0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, 0x82000000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, 0x022A0000, "PIAN"],
|
||||
[0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, 0x82000000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, 0x022B0000, "CHAO"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x022C0000, 0x022C0000, 0x022C0000, 0x82000000, 0x022C0000, 0x022C0000, 0x022C0000, 0x022C0000, 0x022C0000, "CHU CHU"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x022D0000, 0x022D0000, 0x022D0000, 0x82000000, 0x022D0000, 0x022D0000, 0x022D0000, 0x022D0000, 0x022D0000, "KAPU KAPU"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x022E0000, 0x022E0000, 0x022E0000, 0x82000000, 0x022E0000, 0x022E0000, 0x022E0000, 0x022E0000, 0x022E0000, "ANGEL'S WING"],
|
||||
@@ -883,8 +883,9 @@
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x024F0000, "Cell of MAG 0505 (mag)"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x02500000, "Cell of MAG 0506 (mag)"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x02510000, "Cell of MAG 0507 (mag)"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x82000000, 0x02280000, 0x02420000, 0x02420000, 0x02420000, 0x02420000, 0x02520000, "???? (MAG)"],
|
||||
[0x82000000, 0x82000000, 0x82000000, 0x02390000, 0x02390000, 0x02390000, 0x02280000, 0x02420000, 0x02420000, 0x02420000, 0x02420000, 0x02520000, "???? (MAG)"],
|
||||
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
[0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, 0x03000000, "Monomate"],
|
||||
[0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, 0x03000100, "Dimate"],
|
||||
[0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, 0x03000200, "Trimate"],
|
||||
@@ -1027,7 +1028,7 @@
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x03100200, 0x03100200, 0x03100200, 0x03100200, 0x03100200, "Photon Crystal"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x83100000, 0x83100000, 0x83100000, 0x83100000, 0x03100300, "Secret Ticket"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x83100000, 0x83100000, 0x83100000, 0x83100000, 0x03100400, "Photon Ticket"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030E0700, 0x030E0700, 0x030E0700, 0x830E0000, 0x03120000, 0x03120000, 0x03120000, 0x03120000, 0x03120000, "Weapons Bronze Badge"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030E0700, 0x030E0700, 0x030E0700, 0x03120000, 0x03120000, 0x03120000, 0x03120000, 0x03120000, 0x03120000, "Weapons Bronze Badge"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030E0800, 0x030E0800, 0x030E0800, 0x830E0000, 0x03120100, 0x03120100, 0x03120100, 0x03120100, 0x03120100, "Weapons Silver Badge"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030E0900, 0x030E0900, 0x030E0900, 0x830E0000, 0x03120200, 0x03120200, 0x03120200, 0x03120200, 0x03120200, "Weapons Gold Badge"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030E0A00, 0x030E0A00, 0x030E0A00, 0x830E0000, 0x03120300, 0x03120300, 0x03120300, 0x03120300, 0x03120300, "Weapons Crystal Badge"],
|
||||
@@ -1090,5 +1091,6 @@
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x03190100, "Team Points 1000"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x03190200, "Team Points 5000"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x830E0000, 0x830D0500, 0x830D0500, 0x830D0500, 0x830D0500, 0x03190300, "Team Points 10000"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030F0000, 0x030F0000, 0x030F0000, 0x03120000, 0x83170400, 0x83170400, 0x83170400, 0x83170400, 0x031A0000, "???? (TOOL)"],
|
||||
[0x830C0100, 0x830D0500, 0x830D0500, 0x030F0000, 0x030F0000, 0x030F0000, 0x03130000, 0x03180000, 0x03180000, 0x03180000, 0x03180000, 0x031A0000, "???? (TOOL)"],
|
||||
// DC_NTE-- 11/2000--- DC_V1----- DC_V2----- PC_NTE---- PC_V2----- GC_NTE---- GC_V3----- GC_EP3_NTE GC_EP3---- XB_V3----- BB_V4----- NAME
|
||||
]
|
||||
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
EXECUTABLE="$1"
|
||||
if [ -z "$EXECUTABLE" ]; then
|
||||
EXECUTABLE="./newserv"
|
||||
fi
|
||||
|
||||
DIR=tests/item-parameter-tables
|
||||
|
||||
echo "... DC NTE"
|
||||
$EXECUTABLE decode-item-parameter-table --dc-nte $DIR/dc-nte.expected.bin --decompressed $DIR/dc-nte.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --dc-nte $DIR/dc-nte.json $DIR/dc-nte.encoded.bin --decompressed
|
||||
bindiff $DIR/dc-nte.expected.bin $DIR/dc-nte.encoded.bin
|
||||
|
||||
echo "... DC 11/2000"
|
||||
$EXECUTABLE decode-item-parameter-table --dc-11-2000 $DIR/dc-11-2000.expected.bin --decompressed $DIR/dc-11-2000.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --dc-11-2000 $DIR/dc-11-2000.json $DIR/dc-11-2000.encoded.bin --decompressed
|
||||
bindiff $DIR/dc-11-2000.expected.bin $DIR/dc-11-2000.encoded.bin
|
||||
|
||||
echo "... DC V1"
|
||||
$EXECUTABLE decode-item-parameter-table --dc-v1 $DIR/dc-v1.expected.bin --decompressed $DIR/dc-v1.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --dc-v1 $DIR/dc-v1.json $DIR/dc-v1.encoded.bin --decompressed
|
||||
bindiff $DIR/dc-v1.expected.bin $DIR/dc-v1.encoded.bin
|
||||
|
||||
echo "... DC V2"
|
||||
$EXECUTABLE decode-item-parameter-table --dc-v2 $DIR/dc-v2.expected.bin --decompressed $DIR/dc-v2.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --dc-v2 $DIR/dc-v2.json $DIR/dc-v2.encoded.bin --decompressed
|
||||
bindiff $DIR/dc-v2.expected.bin $DIR/dc-v2.encoded.bin
|
||||
|
||||
echo "... PC NTE"
|
||||
$EXECUTABLE decode-item-parameter-table --pc-nte $DIR/pc-nte.expected.bin --decompressed $DIR/pc-nte.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --pc-nte $DIR/pc-nte.json $DIR/pc-nte.encoded.bin --decompressed
|
||||
bindiff $DIR/pc-nte.expected.bin $DIR/pc-nte.encoded.bin
|
||||
|
||||
echo "... PC V2"
|
||||
$EXECUTABLE decode-item-parameter-table --pc-v2 $DIR/pc-v2.expected.bin --decompressed $DIR/pc-v2.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --pc-v2 $DIR/pc-v2.json $DIR/pc-v2.encoded.bin --decompressed
|
||||
bindiff $DIR/pc-v2.expected.bin $DIR/pc-v2.encoded.bin
|
||||
|
||||
echo "... GC NTE"
|
||||
$EXECUTABLE decode-item-parameter-table --gc-nte $DIR/gc-nte.expected.bin --decompressed $DIR/gc-nte.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --gc-nte $DIR/gc-nte.json $DIR/gc-nte.encoded.bin --decompressed
|
||||
bindiff $DIR/gc-nte.expected.bin $DIR/gc-nte.encoded.bin
|
||||
|
||||
echo "... GC V3"
|
||||
$EXECUTABLE decode-item-parameter-table --gc-v3 $DIR/gc-v3.expected.bin --decompressed $DIR/gc-v3.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --gc-v3 $DIR/gc-v3.json $DIR/gc-v3.encoded.bin --decompressed
|
||||
bindiff $DIR/gc-v3.expected.bin $DIR/gc-v3.encoded.bin
|
||||
|
||||
echo "... GC Ep3 NTE"
|
||||
$EXECUTABLE decode-item-parameter-table --gc-ep3-nte $DIR/gc-ep3-nte.expected.bin --decompressed $DIR/gc-ep3-nte.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --gc-ep3-nte $DIR/gc-ep3-nte.json $DIR/gc-ep3-nte.encoded.bin --decompressed
|
||||
bindiff $DIR/gc-ep3-nte.expected.bin $DIR/gc-ep3-nte.encoded.bin
|
||||
|
||||
echo "... GC Ep3"
|
||||
$EXECUTABLE decode-item-parameter-table --gc-ep3 $DIR/gc-ep3.expected.bin --decompressed $DIR/gc-ep3.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --gc-ep3 $DIR/gc-ep3.json $DIR/gc-ep3.encoded.bin --decompressed
|
||||
bindiff $DIR/gc-ep3.expected.bin $DIR/gc-ep3.encoded.bin
|
||||
|
||||
echo "... XB"
|
||||
$EXECUTABLE decode-item-parameter-table --xb-v3 $DIR/xb-v3.expected.bin --decompressed $DIR/xb-v3.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --xb-v3 $DIR/xb-v3.json $DIR/xb-v3.encoded.bin --decompressed
|
||||
bindiff $DIR/xb-v3.expected.bin $DIR/xb-v3.encoded.bin
|
||||
|
||||
echo "... BB"
|
||||
$EXECUTABLE decode-item-parameter-table --bb-v4 $DIR/bb-v4.expected.bin --decompressed $DIR/bb-v4.json --hex
|
||||
$EXECUTABLE encode-item-parameter-table --bb-v4 $DIR/bb-v4.json $DIR/bb-v4.encoded.bin --decompressed
|
||||
bindiff $DIR/bb-v4.expected.bin $DIR/bb-v4.encoded.bin
|
||||
|
||||
echo "... clean up"
|
||||
rm -f tests/item-parameter-tables/*.encoded.bin tests/item-parameter-tables/*.json
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user