split Episode3::DataIndex into multiple structures
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "Episode3/DataIndex.hh"
|
||||
#include "Episode3/DataIndexes.hh"
|
||||
#include "Episode3/DeckState.hh"
|
||||
#include "Episode3/MapState.hh"
|
||||
#include "Episode3/PlayerStateSubordinates.hh"
|
||||
@@ -5178,8 +5178,8 @@ struct G_MapList_GC_Ep3_6xB6x40 {
|
||||
le_uint16_t compressed_data_size;
|
||||
le_uint16_t unused;
|
||||
// PRS-compressed map list data follows here. newserv generates this from the
|
||||
// map index at startup time; see the MapList struct in Episode3/DataIndex.hh
|
||||
// and Episode3::DataIndex::get_compressed_map_list for details on the format.
|
||||
// map index at startup; see the MapList struct in Episode3/DataIndexes.hh
|
||||
// and Episode3::MapIndex::get_compressed_map_list for details on the format.
|
||||
} __packed__;
|
||||
|
||||
struct G_MapData_GC_Ep3_6xB6x41 {
|
||||
|
||||
@@ -133,7 +133,7 @@ uint16_t AssistServer::card_id_for_card_ref(uint16_t card_ref) const {
|
||||
return this->server()->card_id_for_card_ref(card_ref);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> AssistServer::definition_for_card_id(
|
||||
shared_ptr<const CardIndex::CardEntry> AssistServer::definition_for_card_id(
|
||||
uint16_t card_id) const {
|
||||
return this->server()->definition_for_card_id(card_id);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerState.hh"
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
std::shared_ptr<const Server> server() const;
|
||||
|
||||
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
|
||||
uint32_t compute_num_assist_effects_for_client(uint16_t client_id);
|
||||
uint32_t compute_num_assist_effects_for_team(uint32_t team_id);
|
||||
@@ -40,11 +40,11 @@ private:
|
||||
|
||||
public:
|
||||
parray<AssistEffect, 4> assist_effects;
|
||||
std::shared_ptr<const DataIndex::CardEntry> assist_card_defs[4];
|
||||
std::shared_ptr<const CardIndex::CardEntry> assist_card_defs[4];
|
||||
uint32_t num_assist_cards_set;
|
||||
parray<uint8_t, 4> client_ids_with_assists;
|
||||
parray<AssistEffect, 4> active_assist_effects;
|
||||
std::shared_ptr<const DataIndex::CardEntry> active_assist_card_defs[4];
|
||||
std::shared_ptr<const CardIndex::CardEntry> active_assist_card_defs[4];
|
||||
uint32_t num_active_assists;
|
||||
std::shared_ptr<HandAndEquipState> hand_and_equip_states[4];
|
||||
std::shared_ptr<parray<CardShortStatus, 0x10>> card_short_statuses[4];
|
||||
|
||||
@@ -507,7 +507,7 @@ bool Card::get_attack_condition_value(
|
||||
cond_type, card_ref, def_effect_index, value, out_value);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Card::get_definition() const {
|
||||
shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
|
||||
return this->def_entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "../CommandFormats.hh"
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
uint8_t def_effect_index,
|
||||
uint16_t value,
|
||||
uint16_t* out_value) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> get_definition() const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> get_definition() const;
|
||||
uint16_t get_card_ref() const;
|
||||
uint8_t get_client_id() const;
|
||||
uint8_t get_current_hp() const;
|
||||
@@ -99,12 +99,12 @@ private:
|
||||
public:
|
||||
int16_t max_hp;
|
||||
int16_t current_hp;
|
||||
std::shared_ptr<const DataIndex::CardEntry> def_entry;
|
||||
std::shared_ptr<const CardIndex::CardEntry> def_entry;
|
||||
uint8_t client_id;
|
||||
uint16_t card_id;
|
||||
uint16_t card_ref;
|
||||
uint16_t sc_card_ref;
|
||||
std::shared_ptr<const DataIndex::CardEntry> sc_def_entry;
|
||||
std::shared_ptr<const CardIndex::CardEntry> sc_def_entry;
|
||||
CardType sc_card_type;
|
||||
uint8_t team_id;
|
||||
uint32_t card_flags;
|
||||
|
||||
+14
-14
@@ -816,7 +816,7 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
|
||||
// the Gifoie card's ID (00D9) for compute_effective_range.
|
||||
// TODO: We should fix this so it doesn't rely on a fixed card definition.
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
|
||||
auto card_refs_in_parry_range = target_ps->get_all_cards_within_range(
|
||||
range, target_card_loc, 0xFF);
|
||||
|
||||
@@ -2526,7 +2526,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
if (ce && ps) {
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
add_card_refs(ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM));
|
||||
}
|
||||
}
|
||||
@@ -2566,7 +2566,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
if (ce && ps) {
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
add_card_refs(ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id()));
|
||||
}
|
||||
}
|
||||
@@ -2631,7 +2631,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// should fix this eventually.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2655,7 +2655,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Again with the Gifoie hardcoding...
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2721,7 +2721,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Again with the Gifoie hardcoding...
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2775,7 +2775,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Yet another Gifoie hardcode location :(
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2802,7 +2802,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Sigh. Gifoie again.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2898,7 +2898,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// Slay instead of Gifoie
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x009C, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2927,7 +2927,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: Sigh. Gifoie. Sigh.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -2965,7 +2965,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
// TODO: One more Gifoie here.
|
||||
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
|
||||
for (uint16_t result_card_ref : result_card_refs) {
|
||||
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
|
||||
@@ -3383,7 +3383,7 @@ void CardSpecial::check_for_defense_interference(
|
||||
shared_ptr<Card> target_card,
|
||||
int16_t* inout_unknown_p4) {
|
||||
// Note: This check is not part of the original implementation.
|
||||
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4126,7 +4126,7 @@ vector<shared_ptr<const Card>> CardSpecial::filter_cards_by_range(
|
||||
// TODO: Remove hardcoded card ID here (Earthquake)
|
||||
uint16_t card_id = this->get_card_id_with_effective_range(card1, 0x00ED, card2);
|
||||
parray<uint8_t, 9 * 9> range;
|
||||
compute_effective_range(range, this->server()->base()->data_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
compute_effective_range(range, this->server()->base()->card_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
|
||||
auto card_refs_in_range = ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM);
|
||||
|
||||
for (auto card : cards) {
|
||||
@@ -4342,7 +4342,7 @@ void CardSpecial::unknown_8024A9D8(const ActionState& pa, uint16_t action_card_r
|
||||
|
||||
void CardSpecial::check_for_attack_interference(shared_ptr<Card> unknown_p2) {
|
||||
// Note: This check is not part of the original implementation.
|
||||
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -1056,6 +1056,33 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerConfig::decrypt() {
|
||||
if (!this->is_encrypted) {
|
||||
return;
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
this->basis);
|
||||
this->is_encrypted = 0;
|
||||
this->basis = 0;
|
||||
}
|
||||
|
||||
void PlayerConfig::encrypt(uint8_t basis) {
|
||||
if (this->is_encrypted) {
|
||||
if (this->basis == basis) {
|
||||
return;
|
||||
}
|
||||
this->decrypt();
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
basis);
|
||||
this->is_encrypted = 1;
|
||||
this->basis = basis;
|
||||
}
|
||||
|
||||
HPType hp_type_for_name(const char* name) {
|
||||
if (!strcmp(name, "DEFEAT_PLAYER")) {
|
||||
return HPType::DEFEAT_PLAYER;
|
||||
@@ -1351,7 +1378,7 @@ void StateFlags::clear_FF() {
|
||||
this->client_sc_card_types.clear(CardType::INVALID_FF);
|
||||
}
|
||||
|
||||
string MapDefinition::str(const DataIndex* data_index) const {
|
||||
string MapDefinition::str(const CardIndex* card_index) const {
|
||||
deque<string> lines;
|
||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||
for (size_t y = 0; y < 0x10; y++) {
|
||||
@@ -1456,10 +1483,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
|
||||
lines.emplace_back(" name: " + string(this->npc_decks[z].name));
|
||||
for (size_t w = 0; w < 0x20; w++) {
|
||||
uint16_t card_id = this->npc_decks[z].card_ids[w];
|
||||
shared_ptr<const DataIndex::CardEntry> entry;
|
||||
if (data_index) {
|
||||
shared_ptr<const CardIndex::CardEntry> entry;
|
||||
if (card_index) {
|
||||
try {
|
||||
entry = data_index->definition_for_card_id(card_id);
|
||||
entry = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
@@ -1496,10 +1523,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
|
||||
}
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
uint16_t card_id = this->reward_card_ids[z];
|
||||
shared_ptr<const DataIndex::CardEntry> entry;
|
||||
if (data_index) {
|
||||
shared_ptr<const CardIndex::CardEntry> entry;
|
||||
if (card_index) {
|
||||
try {
|
||||
entry = data_index->definition_for_card_id(card_id);
|
||||
entry = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
@@ -1671,14 +1698,12 @@ bool Rules::check_and_reset_invalid_fields() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
: behavior_flags(behavior_flags) {
|
||||
|
||||
CardIndex::CardIndex(const string& filename, const string& decompressed_filename, const string& text_filename) {
|
||||
unordered_map<uint32_t, vector<string>> card_tags;
|
||||
unordered_map<uint32_t, string> card_text;
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
if (!text_filename.empty()) {
|
||||
try {
|
||||
string data = prs_decompress(load_file(directory + "/card-text.mnr"));
|
||||
string data = prs_decompress(load_file(text_filename));
|
||||
StringReader r(data);
|
||||
|
||||
while (!r.eof()) {
|
||||
@@ -1769,20 +1794,20 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
|
||||
try {
|
||||
string decompressed_data;
|
||||
if (isfile(directory + "/card-definitions.mnrd")) {
|
||||
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnrd").st_mtime;
|
||||
decompressed_data = load_file(directory + "/card-definitions.mnrd");
|
||||
this->mtime_for_card_definitions = stat(filename).st_mtime;
|
||||
try {
|
||||
decompressed_data = load_file(decompressed_filename);
|
||||
this->compressed_card_definitions.clear();
|
||||
} else {
|
||||
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnr").st_mtime;
|
||||
this->compressed_card_definitions = load_file(directory + "/card-definitions.mnr");
|
||||
} catch (const cannot_open_file&) {
|
||||
this->compressed_card_definitions = load_file(filename);
|
||||
decompressed_data = prs_decompress(this->compressed_card_definitions);
|
||||
}
|
||||
if (decompressed_data.size() > 0x36EC0) {
|
||||
throw runtime_error("decompressed card list data is too long");
|
||||
}
|
||||
|
||||
// There's a footer after the card definitions, but we ignore it
|
||||
// There's a footer after the card definitions (it's a standard-format REL
|
||||
// file), but we ignore it
|
||||
if (decompressed_data.size() % sizeof(CardDefinition) != sizeof(CardDefinitionsFooter)) {
|
||||
throw runtime_error(string_printf(
|
||||
"decompressed card update file size %zX is not aligned with card definition size %zX (%zX extra bytes)",
|
||||
@@ -1792,10 +1817,9 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
size_t max_cards = decompressed_data.size() / sizeof(CardDefinition);
|
||||
for (size_t x = 0; x < max_cards; x++) {
|
||||
// The last card entry has the build date and some other metadata (and
|
||||
// isn't a real card, obviously), so skip it. Seems like the card ID is
|
||||
// always a large number that won't fit in a uint16_t, so we use that to
|
||||
// determine if the entry is a real card or not.
|
||||
if (defs[x].card_id & 0xFFFF0000) {
|
||||
// isn't a real card, obviously), so skip it. The game detects this by
|
||||
// checking for a negative value in type, which we also do here.
|
||||
if (static_cast<int8_t>(defs[x].type) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1816,7 +1840,7 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
entry->def.mv.decode_code();
|
||||
entry->def.decode_range();
|
||||
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
if (!text_filename.empty()) {
|
||||
try {
|
||||
entry->text = std::move(card_text.at(defs[x].card_id));
|
||||
} catch (const out_of_range&) {
|
||||
@@ -1836,6 +1860,25 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
|
||||
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
|
||||
}
|
||||
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
// Try to reduce the compressed size by clearing out text
|
||||
static_game_data_log.info("Compressed card list data is too long; removing text");
|
||||
for (size_t x = 0; x < max_cards; x++) {
|
||||
if (static_cast<int8_t>(defs[x].type) < 0) {
|
||||
continue;
|
||||
}
|
||||
defs[x].jp_name.clear();
|
||||
}
|
||||
uint64_t start = now();
|
||||
this->compressed_card_definitions = prs_compress(decompressed_data);
|
||||
uint64_t diff = now() - start;
|
||||
static_game_data_log.info(
|
||||
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
|
||||
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
|
||||
print_data(stderr, this->compressed_card_definitions);
|
||||
}
|
||||
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
throw runtime_error("compressed card list data is too long");
|
||||
}
|
||||
@@ -1844,7 +1887,36 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 card update: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
const string& CardIndex::get_compressed_definitions() const {
|
||||
if (this->compressed_card_definitions.empty()) {
|
||||
throw runtime_error("card definitions are not available");
|
||||
}
|
||||
return this->compressed_card_definitions;
|
||||
}
|
||||
|
||||
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_id(uint32_t id) const {
|
||||
return this->card_definitions.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_name(const string& name) const {
|
||||
return this->card_definitions_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> CardIndex::all_ids() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->card_definitions) {
|
||||
ret.emplace(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t CardIndex::definitions_mtime() const {
|
||||
return this->mtime_for_card_definitions;
|
||||
}
|
||||
|
||||
MapIndex::MapIndex(const string& directory) {
|
||||
auto add_maps_from_dir = [&](const string& dir, bool is_quest) -> void {
|
||||
for (const auto& filename : list_directory(dir)) {
|
||||
try {
|
||||
@@ -1879,32 +1951,13 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
};
|
||||
add_maps_from_dir(directory + "/maps-free", false);
|
||||
add_maps_from_dir(directory + "/maps-quest", true);
|
||||
|
||||
try {
|
||||
auto json = JSONObject::parse(load_file(directory + "/com-decks.json"));
|
||||
for (const auto& def_json : json->as_list()) {
|
||||
auto& def = this->com_decks.emplace_back(new COMDeckDefinition());
|
||||
def->index = this->com_decks.size() - 1;
|
||||
def->player_name = def_json->at(0)->as_string();
|
||||
def->deck_name = def_json->at(1)->as_string();
|
||||
auto card_ids_json = def_json->at(2)->as_list();
|
||||
for (size_t z = 0; z < 0x1F; z++) {
|
||||
def->card_ids[z] = card_ids_json.at(z)->as_int();
|
||||
}
|
||||
if (!this->com_decks_by_name.emplace(def->deck_name, def).second) {
|
||||
throw runtime_error("duplicate COM deck name: " + def->deck_name);
|
||||
}
|
||||
}
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
DataIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
|
||||
MapIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
|
||||
: map(map),
|
||||
is_quest(is_quest) {}
|
||||
|
||||
DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
MapIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
: is_quest(is_quest),
|
||||
compressed_data(compressed) {
|
||||
string decompressed = prs_decompress(this->compressed_data);
|
||||
@@ -1916,43 +1969,14 @@ DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
this->map = *reinterpret_cast<const MapDefinition*>(decompressed.data());
|
||||
}
|
||||
|
||||
string DataIndex::MapEntry::compressed() const {
|
||||
string MapIndex::MapEntry::compressed() const {
|
||||
if (this->compressed_data.empty()) {
|
||||
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
|
||||
}
|
||||
return this->compressed_data;
|
||||
}
|
||||
|
||||
const string& DataIndex::get_compressed_card_definitions() const {
|
||||
if (this->compressed_card_definitions.empty()) {
|
||||
throw runtime_error("card definitions are not available");
|
||||
}
|
||||
return this->compressed_card_definitions;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_id(
|
||||
uint32_t id) const {
|
||||
return this->card_definitions.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_name(
|
||||
const string& name) const {
|
||||
return this->card_definitions_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> DataIndex::all_card_ids() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->card_definitions) {
|
||||
ret.emplace(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t DataIndex::card_definitions_mtime() const {
|
||||
return this->mtime_for_card_definitions;
|
||||
}
|
||||
|
||||
const string& DataIndex::get_compressed_map_list() const {
|
||||
const string& MapIndex::get_compressed_list() const {
|
||||
if (this->compressed_map_list.empty()) {
|
||||
StringWriter entries_w;
|
||||
StringWriter strings_w;
|
||||
@@ -2012,16 +2036,15 @@ const string& DataIndex::get_compressed_map_list() const {
|
||||
return this->compressed_map_list;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_number(uint32_t id) const {
|
||||
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_number(uint32_t id) const {
|
||||
return this->maps.at(id);
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_name(
|
||||
const string& name) const {
|
||||
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_name(const string& name) const {
|
||||
return this->maps_by_name.at(name);
|
||||
}
|
||||
|
||||
set<uint32_t> DataIndex::all_map_ids() const {
|
||||
set<uint32_t> MapIndex::all_numbers() const {
|
||||
set<uint32_t> ret;
|
||||
for (const auto& it : this->maps) {
|
||||
ret.emplace(it.first);
|
||||
@@ -2029,47 +2052,41 @@ set<uint32_t> DataIndex::all_map_ids() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t DataIndex::num_com_decks() const {
|
||||
return this->com_decks.size();
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(size_t which) const {
|
||||
return this->com_decks.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(const string& which) const {
|
||||
return this->com_decks_by_name.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> DataIndex::random_com_deck() const {
|
||||
return this->com_decks[random_object<size_t>() % this->com_decks.size()];
|
||||
}
|
||||
|
||||
void PlayerConfig::decrypt() {
|
||||
if (!this->is_encrypted) {
|
||||
return;
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
this->basis);
|
||||
this->is_encrypted = 0;
|
||||
this->basis = 0;
|
||||
}
|
||||
|
||||
void PlayerConfig::encrypt(uint8_t basis) {
|
||||
if (this->is_encrypted) {
|
||||
if (this->basis == basis) {
|
||||
return;
|
||||
COMDeckIndex::COMDeckIndex(const string& filename) {
|
||||
try {
|
||||
auto json = JSONObject::parse(load_file(filename));
|
||||
for (const auto& def_json : json->as_list()) {
|
||||
auto& def = this->decks.emplace_back(new COMDeckDefinition());
|
||||
def->index = this->decks.size() - 1;
|
||||
def->player_name = def_json->at(0)->as_string();
|
||||
def->deck_name = def_json->at(1)->as_string();
|
||||
auto card_ids_json = def_json->at(2)->as_list();
|
||||
for (size_t z = 0; z < 0x1F; z++) {
|
||||
def->card_ids[z] = card_ids_json.at(z)->as_int();
|
||||
}
|
||||
if (!this->decks_by_name.emplace(def->deck_name, def).second) {
|
||||
throw runtime_error("duplicate COM deck name: " + def->deck_name);
|
||||
}
|
||||
}
|
||||
this->decrypt();
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
|
||||
}
|
||||
decrypt_trivial_gci_data(
|
||||
&this->card_counts,
|
||||
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
|
||||
basis);
|
||||
this->is_encrypted = 1;
|
||||
this->basis = basis;
|
||||
}
|
||||
|
||||
size_t COMDeckIndex::num_decks() const {
|
||||
return this->decks.size();
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_index(size_t which) const {
|
||||
return this->decks.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_name(const string& which) const {
|
||||
return this->decks_by_name.at(which);
|
||||
}
|
||||
|
||||
shared_ptr<const COMDeckDefinition> COMDeckIndex::random_deck() const {
|
||||
return this->decks[random_object<size_t>() % this->decks.size()];
|
||||
}
|
||||
|
||||
} // namespace Episode3
|
||||
@@ -15,21 +15,22 @@
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
// The comment in Server.hh does not apply to this file (and DataIndex.cc).
|
||||
// The comment in Server.hh does not apply to this file (and DataIndexes.cc).
|
||||
// Except for the Location structure, these structures and functions are not
|
||||
// based on Sega's original implementation.
|
||||
|
||||
class DataIndex;
|
||||
class CardIndex;
|
||||
class MapIndex;
|
||||
class COMDeckIndex;
|
||||
|
||||
const char* name_for_link_color(uint8_t color);
|
||||
|
||||
enum BehaviorFlag {
|
||||
enum BehaviorFlag : uint32_t {
|
||||
SKIP_DECK_VERIFY = 0x00000001,
|
||||
IGNORE_CARD_COUNTS = 0x00000002,
|
||||
SKIP_D1_D2_REPLACE = 0x00000004,
|
||||
DISABLE_TIME_LIMITS = 0x00000008,
|
||||
ENABLE_STATUS_MESSAGES = 0x00000010,
|
||||
LOAD_CARD_TEXT = 0x00000020,
|
||||
ENABLE_RECORDING = 0x00000040,
|
||||
DISABLE_MASKING = 0x00000080,
|
||||
DISABLE_INTERFERENCE = 0x00000100,
|
||||
@@ -952,10 +953,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
uint8_t deck_type;
|
||||
} __attribute__((packed));
|
||||
/* 5A10 */ parray<EntryState, 4> entry_states;
|
||||
|
||||
/* 5A18 */
|
||||
|
||||
std::string str(const DataIndex* data_index = nullptr) const;
|
||||
std::string str(const CardIndex* card_index = nullptr) const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct COMDeckDefinition {
|
||||
@@ -965,9 +965,9 @@ struct COMDeckDefinition {
|
||||
parray<le_uint16_t, 0x1F> card_ids;
|
||||
};
|
||||
|
||||
class DataIndex {
|
||||
class CardIndex {
|
||||
public:
|
||||
DataIndex(const std::string& directory, uint32_t behavior_flags);
|
||||
CardIndex(const std::string& filename, const std::string& decompressed_filename, const std::string& text_filename = "");
|
||||
|
||||
struct CardEntry {
|
||||
CardDefinition def;
|
||||
@@ -975,6 +975,23 @@ public:
|
||||
std::vector<std::string> debug_tags; // Empty unless debug == true
|
||||
};
|
||||
|
||||
const std::string& get_compressed_definitions() const;
|
||||
std::shared_ptr<const CardEntry> definition_for_id(uint32_t id) const;
|
||||
std::shared_ptr<const CardEntry> definition_for_name(const std::string& name) const;
|
||||
std::set<uint32_t> all_ids() const;
|
||||
uint64_t definitions_mtime() const;
|
||||
|
||||
private:
|
||||
std::string compressed_card_definitions;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
|
||||
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
|
||||
uint64_t mtime_for_card_definitions;
|
||||
};
|
||||
|
||||
class MapIndex {
|
||||
public:
|
||||
MapIndex(const std::string& directory);
|
||||
|
||||
class MapEntry {
|
||||
public:
|
||||
MapDefinition map;
|
||||
@@ -989,42 +1006,33 @@ public:
|
||||
mutable std::string compressed_data;
|
||||
};
|
||||
|
||||
const std::string& get_compressed_card_definitions() const;
|
||||
std::shared_ptr<const CardEntry> definition_for_card_id(uint32_t id) const;
|
||||
std::shared_ptr<const CardEntry> definition_for_card_name(
|
||||
const std::string& name) const;
|
||||
std::set<uint32_t> all_card_ids() const;
|
||||
uint64_t card_definitions_mtime() const;
|
||||
|
||||
const std::string& get_compressed_map_list() const;
|
||||
std::shared_ptr<const MapEntry> definition_for_map_number(uint32_t id) const;
|
||||
std::shared_ptr<const MapEntry> definition_for_map_name(
|
||||
const std::string& name) const;
|
||||
std::set<uint32_t> all_map_ids() const;
|
||||
|
||||
size_t num_com_decks() const;
|
||||
std::shared_ptr<const COMDeckDefinition> com_deck(size_t which) const;
|
||||
std::shared_ptr<const COMDeckDefinition> com_deck(const std::string& name) const;
|
||||
std::shared_ptr<const COMDeckDefinition> random_com_deck() const;
|
||||
|
||||
const uint32_t behavior_flags;
|
||||
const std::string& get_compressed_list() const;
|
||||
std::shared_ptr<const MapEntry> definition_for_number(uint32_t id) const;
|
||||
std::shared_ptr<const MapEntry> definition_for_name(const std::string& name) const;
|
||||
std::set<uint32_t> all_numbers() const;
|
||||
|
||||
private:
|
||||
std::string compressed_card_definitions;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
|
||||
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
|
||||
uint64_t mtime_for_card_definitions;
|
||||
|
||||
// The compressed map list is generated on demand from the maps map below.
|
||||
// It's marked mutable because the logical consistency of the DataIndex object
|
||||
// It's marked mutable because the logical consistency of the MapIndex object
|
||||
// is not violated from the caller's perspective even if we don't generate the
|
||||
// compressed map list at load time.
|
||||
mutable std::string compressed_map_list;
|
||||
std::map<uint32_t, std::shared_ptr<MapEntry>> maps;
|
||||
std::unordered_map<std::string, std::shared_ptr<MapEntry>> maps_by_name;
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<COMDeckDefinition>> com_decks;
|
||||
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> com_decks_by_name;
|
||||
class COMDeckIndex {
|
||||
public:
|
||||
COMDeckIndex(const std::string& filename);
|
||||
|
||||
size_t num_decks() const;
|
||||
std::shared_ptr<const COMDeckDefinition> deck_for_index(size_t which) const;
|
||||
std::shared_ptr<const COMDeckDefinition> deck_for_name(const std::string& name) const;
|
||||
std::shared_ptr<const COMDeckDefinition> random_deck() const;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<COMDeckDefinition>> decks;
|
||||
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> decks_by_name;
|
||||
};
|
||||
|
||||
} // namespace Episode3
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "Card.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerStateSubordinates.hh"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Text.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
|
||||
namespace Episode3 {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Episode3 {
|
||||
|
||||
void compute_effective_range(
|
||||
parray<uint8_t, 9 * 9>& ret,
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const CardIndex> card_index,
|
||||
uint16_t card_id,
|
||||
const Location& loc,
|
||||
shared_ptr<const MapAndRulesState> map_and_rules) {
|
||||
@@ -19,9 +19,9 @@ void compute_effective_range(
|
||||
// Heavy Fog: one tile directly in front
|
||||
range_def[3] = 0x00000100;
|
||||
} else {
|
||||
shared_ptr<const DataIndex::CardEntry> ce;
|
||||
shared_ptr<const CardIndex::CardEntry> ce;
|
||||
try {
|
||||
ce = data_index->definition_for_card_id(card_id);
|
||||
ce = card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
return;
|
||||
}
|
||||
@@ -126,9 +126,9 @@ void compute_effective_range(
|
||||
}
|
||||
|
||||
bool card_linkage_is_valid(
|
||||
shared_ptr<const DataIndex::CardEntry> right_ce,
|
||||
shared_ptr<const DataIndex::CardEntry> left_ce,
|
||||
shared_ptr<const DataIndex::CardEntry> sc_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> right_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> left_ce,
|
||||
shared_ptr<const CardIndex::CardEntry> sc_ce,
|
||||
bool has_permission_effect) {
|
||||
if (!right_ce) {
|
||||
return false;
|
||||
@@ -1613,7 +1613,7 @@ bool RulerServer::defense_card_matches_any_attack_card_top_color(
|
||||
return false;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
|
||||
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
|
||||
uint16_t card_id = this->card_id_for_card_ref(card_ref);
|
||||
if (card_id == 0xFFFF) {
|
||||
return nullptr;
|
||||
@@ -1972,8 +1972,7 @@ uint16_t RulerServer::get_ally_sc_card_ref(uint16_t card_ref) const {
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_id(
|
||||
uint32_t card_id) const {
|
||||
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint32_t card_id) const {
|
||||
return this->server()->definition_for_card_id(card_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "AssistServer.hh"
|
||||
#include "DataIndex.hh"
|
||||
#include "DataIndexes.hh"
|
||||
#include "DeckState.hh"
|
||||
#include "PlayerState.hh"
|
||||
|
||||
@@ -15,15 +15,15 @@ class Server;
|
||||
|
||||
void compute_effective_range(
|
||||
parray<uint8_t, 9 * 9>& ret,
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const CardIndex> card_index,
|
||||
uint16_t card_id,
|
||||
const Location& loc,
|
||||
std::shared_ptr<const MapAndRulesState> map_and_rules);
|
||||
|
||||
bool card_linkage_is_valid(
|
||||
std::shared_ptr<const DataIndex::CardEntry> right_def,
|
||||
std::shared_ptr<const DataIndex::CardEntry> left_def,
|
||||
std::shared_ptr<const DataIndex::CardEntry> sc_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> right_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> left_def,
|
||||
std::shared_ptr<const CardIndex::CardEntry> sc_def,
|
||||
bool has_permission_effect);
|
||||
|
||||
class RulerServer {
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
uint16_t attacker_sc_card_ref) const;
|
||||
bool defense_card_matches_any_attack_card_top_color(
|
||||
const ActionState& pa) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
int32_t error_code_for_client_setting_card(
|
||||
uint8_t client_id,
|
||||
uint16_t card_ref,
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
size_t num_occupied_tiles,
|
||||
size_t num_vacant_tiles) const;
|
||||
uint16_t get_ally_sc_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(
|
||||
uint32_t card_id) const;
|
||||
uint32_t get_card_id_with_effective_range(
|
||||
uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const;
|
||||
|
||||
+22
-20
@@ -26,11 +26,15 @@ void ServerBase::PresenceEntry::clear() {
|
||||
|
||||
ServerBase::ServerBase(
|
||||
shared_ptr<Lobby> lobby,
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const CardIndex> card_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
uint32_t behavior_flags,
|
||||
shared_ptr<PSOLFGEncryption> random_crypt,
|
||||
shared_ptr<const DataIndex::MapEntry> map_if_tournament)
|
||||
shared_ptr<const MapIndex::MapEntry> map_if_tournament)
|
||||
: lobby(lobby),
|
||||
data_index(data_index),
|
||||
card_index(card_index),
|
||||
map_index(map_index),
|
||||
behavior_flags(behavior_flags),
|
||||
log(lobby->log.prefix + "[Ep3::Server] "),
|
||||
random_crypt(random_crypt),
|
||||
is_tournament(!!map_if_tournament),
|
||||
@@ -182,7 +186,7 @@ void Server::send(const void* data, size_t size) const {
|
||||
}
|
||||
|
||||
string masked_data;
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (size >= 8) {
|
||||
masked_data.assign(reinterpret_cast<const char*>(data), size);
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
@@ -216,13 +220,12 @@ void Server::send_6xB4x46() const {
|
||||
|
||||
G_ServerVersionStrings_GC_Ep3_6xB4x46 cmd46;
|
||||
cmd46.version_signature = VERSION_SIGNATURE;
|
||||
cmd46.date_str1 = format_time(this->base()->data_index->card_definitions_mtime() * 1000000);
|
||||
cmd46.date_str1 = format_time(this->base()->card_index->definitions_mtime() * 1000000);
|
||||
cmd46.date_str2 = string_printf("Lobby/%08" PRIX32, l->lobby_id);
|
||||
this->send(cmd46);
|
||||
}
|
||||
|
||||
string Server::prepare_6xB6x41_map_definition(
|
||||
shared_ptr<const DataIndex::MapEntry> map) {
|
||||
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::MapEntry> map) {
|
||||
const auto& compressed = map->compressed();
|
||||
|
||||
StringWriter w;
|
||||
@@ -260,7 +263,7 @@ void Server::send_commands_for_joining_spectator(Channel& c) const {
|
||||
|
||||
__attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ...) const {
|
||||
auto l = this->base()->lobby.lock();
|
||||
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
this->base()->log.info_v(fmt, va);
|
||||
@@ -270,7 +273,7 @@ __attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ..
|
||||
|
||||
__attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(const char* fmt, ...) const {
|
||||
auto l = this->base()->lobby.lock();
|
||||
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
std::string buf = string_vprintf(fmt, va);
|
||||
@@ -367,10 +370,9 @@ void Server::draw_phase_before() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
|
||||
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
|
||||
try {
|
||||
return this->base()->data_index->definition_for_card_id(
|
||||
this->card_id_for_card_ref(card_ref));
|
||||
return this->base()->card_index->definition_for_id(this->card_id_for_card_ref(card_ref));
|
||||
} catch (const out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -581,9 +583,9 @@ void Server::copy_player_states_to_prev_states() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
|
||||
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
|
||||
try {
|
||||
return this->base()->data_index->definition_for_card_id(card_id);
|
||||
return this->base()->card_index->definition_for_id(card_id);
|
||||
} catch (const out_of_range&) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1839,7 +1841,7 @@ void Server::handle_6xB3x13_update_map_during_setup(const string& data) {
|
||||
*b->map_and_rules1 = in_cmd.map_and_rules_state;
|
||||
*b->map_and_rules2 = in_cmd.map_and_rules_state;
|
||||
b->overlay_state = in_cmd.overlay_state;
|
||||
if (b->data_index->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
|
||||
if (b->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
|
||||
b->map_and_rules1->rules.overall_time_limit = 0;
|
||||
b->map_and_rules1->rules.phase_time_limit = 0;
|
||||
b->map_and_rules2->rules.overall_time_limit = 0;
|
||||
@@ -1869,9 +1871,9 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
|
||||
}
|
||||
DeckEntry entry = in_cmd.entry;
|
||||
int32_t verify_error = 0;
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
// Note: Sega's original implementation doesn't use the card counts here
|
||||
if (this->base()->data_index->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
|
||||
if (this->base()->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
|
||||
verify_error = this->ruler_server->verify_deck(entry.card_ids);
|
||||
} else {
|
||||
verify_error = this->ruler_server->verify_deck(entry.card_ids,
|
||||
@@ -1881,7 +1883,7 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
|
||||
if (verify_error) {
|
||||
throw runtime_error(string_printf("invalid deck: -0x%" PRIX32, verify_error));
|
||||
}
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
|
||||
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
|
||||
this->ruler_server->replace_D1_D2_rarity_cards_with_Attack(entry.card_ids);
|
||||
}
|
||||
*this->base()->deck_entries[in_cmd.client_id] = in_cmd.entry;
|
||||
@@ -2135,7 +2137,7 @@ void Server::handle_6xB3x40_map_list_request(const string& data) {
|
||||
throw runtime_error("lobby is deleted");
|
||||
}
|
||||
|
||||
const auto& list_data = this->base()->data_index->get_compressed_map_list();
|
||||
const auto& list_data = this->base()->map_index->get_compressed_list();
|
||||
|
||||
StringWriter w;
|
||||
uint32_t subcommand_size = (list_data.size() + sizeof(G_MapList_GC_Ep3_6xB6x40) + 3) & (~3);
|
||||
@@ -2164,7 +2166,7 @@ void Server::handle_6xB3x41_map_request(const string& data) {
|
||||
throw runtime_error("lobby is deleted");
|
||||
}
|
||||
|
||||
base->last_chosen_map = base->data_index->definition_for_map_number(cmd.map_number);
|
||||
base->last_chosen_map = base->map_index->definition_for_number(cmd.map_number);
|
||||
auto out_cmd = this->prepare_6xB6x41_map_definition(base->last_chosen_map);
|
||||
send_command(l, 0x6C, 0x00, out_cmd);
|
||||
for (auto watcher_l : l->watcher_lobbies) {
|
||||
|
||||
+13
-9
@@ -19,7 +19,7 @@ namespace Episode3 {
|
||||
|
||||
/**
|
||||
* This implementation of Episode 3 battles (contained in all files in the
|
||||
* src/Episode3 directory, except for DataIndex.hh/cc) is derived from Sega's
|
||||
* src/Episode3 directory, except for DataIndexes.hh/cc) is derived from Sega's
|
||||
* original server implementation, reverse-engineered from the Episode 3 client
|
||||
* executable. The control flow, function breakdown, and structure definitions
|
||||
* in these files map very closely to how their server implementation was
|
||||
@@ -49,7 +49,7 @@ namespace Episode3 {
|
||||
// - - - - - - - DeckState
|
||||
// - - - - - - - HandAndEquipState
|
||||
// - - - - - - - MapAndRulesState / OverlayState
|
||||
// - - - - - - - - Everything within DataIndex
|
||||
// - - - - - - - - Everything within DataIndexes
|
||||
|
||||
class Server;
|
||||
|
||||
@@ -57,9 +57,11 @@ class ServerBase : public std::enable_shared_from_this<ServerBase> {
|
||||
public:
|
||||
ServerBase(
|
||||
std::shared_ptr<Lobby> lobby,
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const CardIndex> card_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
uint32_t behavior_flags,
|
||||
std::shared_ptr<PSOLFGEncryption> random_crypt,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map_if_tournament);
|
||||
std::shared_ptr<const MapIndex::MapEntry> map_if_tournament);
|
||||
void init();
|
||||
void reset();
|
||||
void recreate_server();
|
||||
@@ -73,11 +75,13 @@ public:
|
||||
} __attribute__((packed));
|
||||
|
||||
std::weak_ptr<Lobby> lobby;
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const CardIndex> card_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
uint32_t behavior_flags;
|
||||
PrefixedLogger log;
|
||||
std::shared_ptr<PSOLFGEncryption> random_crypt;
|
||||
bool is_tournament;
|
||||
std::shared_ptr<const DataIndex::MapEntry> last_chosen_map;
|
||||
std::shared_ptr<const MapIndex::MapEntry> last_chosen_map;
|
||||
|
||||
std::shared_ptr<MapAndRulesState> map_and_rules1;
|
||||
std::shared_ptr<MapAndRulesState> map_and_rules2;
|
||||
@@ -135,7 +139,7 @@ public:
|
||||
bool advance_battle_phase();
|
||||
void action_phase_after();
|
||||
void draw_phase_before();
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
|
||||
std::shared_ptr<Card> card_for_set_card_ref(uint16_t card_ref);
|
||||
std::shared_ptr<const Card> card_for_set_card_ref(uint16_t card_ref) const;
|
||||
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
|
||||
@@ -147,7 +151,7 @@ public:
|
||||
void compute_all_map_occupied_bits();
|
||||
void compute_team_dice_boost(uint8_t team_id);
|
||||
void copy_player_states_to_prev_states();
|
||||
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
|
||||
void destroy_cards_with_zero_hp();
|
||||
void determine_first_team_turn();
|
||||
void dice_phase_after();
|
||||
@@ -229,7 +233,7 @@ public:
|
||||
G_UpdateDecks_GC_Ep3_6xB4x07 prepare_6xB4x07_decks_update() const;
|
||||
G_SetPlayerNames_GC_Ep3_6xB4x1C prepare_6xB4x1C_names_update() const;
|
||||
static std::string prepare_6xB6x41_map_definition(
|
||||
std::shared_ptr<const DataIndex::MapEntry> map);
|
||||
std::shared_ptr<const MapIndex::MapEntry> map);
|
||||
G_SetTrapTileLocations_GC_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() const;
|
||||
|
||||
std::vector<std::shared_ptr<Card>> const_cast_set_cards_v(
|
||||
|
||||
+21
-20
@@ -280,15 +280,17 @@ shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(
|
||||
}
|
||||
|
||||
Tournament::Tournament(
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
const string& name,
|
||||
shared_ptr<const DataIndex::MapEntry> map,
|
||||
shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2)
|
||||
: log(string_printf("[Tournament/%02hhX] ", number)),
|
||||
data_index(data_index),
|
||||
map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
number(number),
|
||||
name(name),
|
||||
map(map),
|
||||
@@ -308,11 +310,13 @@ Tournament::Tournament(
|
||||
}
|
||||
|
||||
Tournament::Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> json)
|
||||
: log(string_printf("[Tournament/%02hhX] ", number)),
|
||||
data_index(data_index),
|
||||
map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
source_json(json),
|
||||
number(number),
|
||||
current_state(State::REGISTRATION) {}
|
||||
@@ -324,7 +328,7 @@ void Tournament::init() {
|
||||
if (this->source_json) {
|
||||
auto& dict = this->source_json->as_dict();
|
||||
this->name = dict.at("name")->as_string();
|
||||
this->map = this->data_index->definition_for_map_number(dict.at("map_number")->as_int());
|
||||
this->map = this->map_index->definition_for_number(dict.at("map_number")->as_int());
|
||||
this->rules = Rules(dict.at("rules"));
|
||||
this->is_2v2 = dict.at("is_2v2")->as_bool();
|
||||
is_registration_complete = dict.at("is_registration_complete")->as_bool();
|
||||
@@ -344,8 +348,7 @@ void Tournament::init() {
|
||||
team->players.emplace_back(serial_number);
|
||||
this->all_player_serial_numbers.emplace(serial_number);
|
||||
} else {
|
||||
team->players.emplace_back(this->data_index->com_deck(
|
||||
player_json->as_string()));
|
||||
team->players.emplace_back(this->com_deck_index->deck_for_name(player_json->as_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -486,10 +489,6 @@ std::shared_ptr<JSONObject> Tournament::json() const {
|
||||
return shared_ptr<JSONObject>(new JSONObject(std::move(dict)));
|
||||
}
|
||||
|
||||
std::shared_ptr<const DataIndex> Tournament::get_data_index() const {
|
||||
return this->data_index;
|
||||
}
|
||||
|
||||
uint8_t Tournament::get_number() const {
|
||||
return this->number;
|
||||
}
|
||||
@@ -498,7 +497,7 @@ const string& Tournament::get_name() const {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
shared_ptr<const DataIndex::MapEntry> Tournament::get_map() const {
|
||||
shared_ptr<const MapIndex::MapEntry> Tournament::get_map() const {
|
||||
return this->map;
|
||||
}
|
||||
|
||||
@@ -593,13 +592,13 @@ void Tournament::start() {
|
||||
throw logic_error("non-human player on team before tournament start");
|
||||
}
|
||||
}
|
||||
if (this->data_index->num_com_decks() < t->max_players - t->players.size()) {
|
||||
if (this->com_deck_index->num_decks() < t->max_players - t->players.size()) {
|
||||
throw runtime_error("not enough COM decks to complete team");
|
||||
}
|
||||
// TODO: Don't allow duplicate COM decks, nor duplicate COM SCs on the same
|
||||
// team
|
||||
while (t->players.size() < t->max_players) {
|
||||
t->players.emplace_back(this->data_index->random_com_deck());
|
||||
t->players.emplace_back(this->com_deck_index->random_deck());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,10 +653,12 @@ void Tournament::print_bracket(FILE* stream) const {
|
||||
}
|
||||
|
||||
TournamentIndex::TournamentIndex(
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
const string& state_filename,
|
||||
bool skip_load_state)
|
||||
: data_index(data_index),
|
||||
: map_index(map_index),
|
||||
com_deck_index(com_deck_index),
|
||||
state_filename(state_filename) {
|
||||
if (this->state_filename.empty() || skip_load_state) {
|
||||
return;
|
||||
@@ -671,7 +672,7 @@ TournamentIndex::TournamentIndex(
|
||||
}
|
||||
for (size_t z = 0; z < 0x20; z++) {
|
||||
if (!list.at(z)->is_null()) {
|
||||
this->tournaments[z].reset(new Tournament(this->data_index, z, list[z]));
|
||||
this->tournaments[z].reset(new Tournament(this->map_index, this->com_deck_index, z, list[z]));
|
||||
this->tournaments[z]->init();
|
||||
}
|
||||
}
|
||||
@@ -706,7 +707,7 @@ vector<shared_ptr<Tournament>> TournamentIndex::all_tournaments() const {
|
||||
|
||||
shared_ptr<Tournament> TournamentIndex::create_tournament(
|
||||
const string& name,
|
||||
shared_ptr<const DataIndex::MapEntry> map,
|
||||
shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2) {
|
||||
@@ -722,7 +723,7 @@ shared_ptr<Tournament> TournamentIndex::create_tournament(
|
||||
}
|
||||
|
||||
auto t = make_shared<Tournament>(
|
||||
this->data_index, number, name, map, rules, num_teams, is_2v2);
|
||||
this->map_index, this->com_deck_index, number, name, map, rules, num_teams, is_2v2);
|
||||
t->init();
|
||||
this->tournaments[number] = t;
|
||||
return t;
|
||||
|
||||
+14
-10
@@ -92,15 +92,17 @@ public:
|
||||
};
|
||||
|
||||
Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
const std::string& name,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map,
|
||||
std::shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2);
|
||||
Tournament(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> json);
|
||||
~Tournament() = default;
|
||||
@@ -108,10 +110,9 @@ public:
|
||||
|
||||
std::shared_ptr<JSONObject> json() const;
|
||||
|
||||
std::shared_ptr<const DataIndex> get_data_index() const;
|
||||
uint8_t get_number() const;
|
||||
const std::string& get_name() const;
|
||||
std::shared_ptr<const DataIndex::MapEntry> get_map() const;
|
||||
std::shared_ptr<const MapIndex::MapEntry> get_map() const;
|
||||
const Rules& get_rules() const;
|
||||
bool get_is_2v2() const;
|
||||
State get_state() const;
|
||||
@@ -131,11 +132,12 @@ public:
|
||||
private:
|
||||
PrefixedLogger log;
|
||||
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index;
|
||||
std::shared_ptr<const JSONObject> source_json;
|
||||
uint8_t number;
|
||||
std::string name;
|
||||
std::shared_ptr<const DataIndex::MapEntry> map;
|
||||
std::shared_ptr<const MapIndex::MapEntry> map;
|
||||
Rules rules;
|
||||
size_t num_teams;
|
||||
bool is_2v2;
|
||||
@@ -160,7 +162,8 @@ private:
|
||||
class TournamentIndex {
|
||||
public:
|
||||
explicit TournamentIndex(
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
const std::string& state_filename,
|
||||
bool skip_load_state = false);
|
||||
~TournamentIndex() = default;
|
||||
@@ -171,7 +174,7 @@ public:
|
||||
|
||||
std::shared_ptr<Tournament> create_tournament(
|
||||
const std::string& name,
|
||||
std::shared_ptr<const DataIndex::MapEntry> map,
|
||||
std::shared_ptr<const MapIndex::MapEntry> map,
|
||||
const Rules& rules,
|
||||
size_t num_teams,
|
||||
bool is_2v2);
|
||||
@@ -183,7 +186,8 @@ public:
|
||||
uint32_t serial_number) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index;
|
||||
std::string state_filename;
|
||||
std::shared_ptr<Tournament> tournaments[0x20];
|
||||
};
|
||||
|
||||
+13
-1
@@ -195,7 +195,7 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
|
||||
isdigit(filename[filename.size() - 12]) &&
|
||||
(filename[filename.size() - 11] == 'O' || filename[filename.size() - 11] == 'S') &&
|
||||
(filename[filename.size() - 10] == 'E' || filename[filename.size() - 10] == 'J' || filename[filename.size() - 10] == 'P') &&
|
||||
isdigit(filename[filename.size() - 9])) {
|
||||
(isdigit(filename[filename.size() - 9]) || filename[filename.size() - 9] == 'T')) {
|
||||
specific_version = 0x33000000 | (filename[filename.size() - 11] << 16) | (filename[filename.size() - 10] << 8) | filename[filename.size() - 9];
|
||||
patch_name = filename.substr(0, filename.size() - 13);
|
||||
}
|
||||
@@ -375,6 +375,18 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Generate entries for Trial Editions
|
||||
data.region_code = 'J';
|
||||
data.system_code = 'D';
|
||||
data.version_code = 0;
|
||||
uint32_t checksum = crc32(&data, sizeof(data));
|
||||
uint32_t specific_version = 0x33004A54 | (*game_code2 << 16);
|
||||
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
|
||||
throw logic_error("multiple specific_versions have same header checksum");
|
||||
}
|
||||
data.system_code = 'G';
|
||||
}
|
||||
}
|
||||
}
|
||||
return checksum_to_specific_version.at(header_checksum);
|
||||
|
||||
@@ -37,6 +37,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
SPECTATORS_FORBIDDEN = 0x00004000,
|
||||
START_BATTLE_PLAYER_IMMEDIATELY = 0x00008000,
|
||||
DROPS_ENABLED = 0x00010000, // Does not affect BB
|
||||
IS_EP3_TRIAL = 0x00020000,
|
||||
|
||||
// Flags used only for lobbies
|
||||
PUBLIC = 0x01000000,
|
||||
|
||||
+23
-22
@@ -1478,32 +1478,33 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::SHOW_EP3_MAPS:
|
||||
case Behavior::SHOW_EP3_CARDS: {
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
Episode3::DataIndex index("system/ep3", Episode3::BehaviorFlag::LOAD_CARD_TEXT);
|
||||
Episode3::CardIndex card_index("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr");
|
||||
|
||||
if (behavior == Behavior::SHOW_EP3_MAPS) {
|
||||
auto map_ids = index.all_map_ids();
|
||||
log_info("%zu maps", map_ids.size());
|
||||
for (uint32_t map_id : map_ids) {
|
||||
auto map = index.definition_for_map_number(map_id);
|
||||
string s = map->map.str(&index);
|
||||
fprintf(stdout, "%s\n", s.c_str());
|
||||
}
|
||||
|
||||
} else {
|
||||
auto card_ids = index.all_card_ids();
|
||||
log_info("%zu card definitions", card_ids.size());
|
||||
for (uint32_t card_id : card_ids) {
|
||||
auto entry = index.definition_for_card_id(card_id);
|
||||
string s = entry->def.str(false);
|
||||
string tags = entry->debug_tags.empty() ? "(none)" : join(entry->debug_tags, ", ");
|
||||
string text = entry->text.empty() ? "(No text available)" : str_replace_all(entry->text, "\n", "\n ");
|
||||
fprintf(stdout, "%s\n Tags: %s\n Text:\n %s\n\n", s.c_str(), tags.c_str(), text.c_str());
|
||||
}
|
||||
auto card_ids = card_index.all_ids();
|
||||
log_info("%zu card definitions", card_ids.size());
|
||||
for (uint32_t card_id : card_ids) {
|
||||
auto entry = card_index.definition_for_id(card_id);
|
||||
string s = entry->def.str(false);
|
||||
string tags = entry->debug_tags.empty() ? "(none)" : join(entry->debug_tags, ", ");
|
||||
string text = entry->text.empty() ? "(No text available)" : str_replace_all(entry->text, "\n", "\n ");
|
||||
fprintf(stdout, "%s\n Tags: %s\n Text:\n %s\n\n", s.c_str(), tags.c_str(), text.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::SHOW_EP3_MAPS: {
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
Episode3::MapIndex map_index("system/ep3");
|
||||
Episode3::CardIndex card_index("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr");
|
||||
|
||||
auto map_ids = map_index.all_numbers();
|
||||
log_info("%zu maps", map_ids.size());
|
||||
for (uint32_t map_id : map_ids) {
|
||||
auto map = map_index.definition_for_number(map_id);
|
||||
string s = map->map.str(&card_index);
|
||||
fprintf(stdout, "%s\n", s.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Episode3/DataIndex.hh"
|
||||
#include "Episode3/DataIndexes.hh"
|
||||
#include "ItemData.hh"
|
||||
#include "LevelTable.hh"
|
||||
#include "Text.hh"
|
||||
|
||||
+24
-10
@@ -303,7 +303,7 @@ static void set_console_client_flags(
|
||||
c->log.info("Game version changed to DC");
|
||||
} else if (c->version() == GameVersion::GC) {
|
||||
c->flags |= Client::Flag::IS_GC_TRIAL_EDITION;
|
||||
c->log.info("Trial edition flag set");
|
||||
c->log.info("GC Trial Edition flag set");
|
||||
}
|
||||
}
|
||||
c->flags |= flags_for_version(c->version(), sub_version);
|
||||
@@ -930,7 +930,7 @@ static bool add_next_game_client(
|
||||
state_cmd.state.first_team_turn = 0xFF;
|
||||
state_cmd.state.tournament_flag = 0x01;
|
||||
state_cmd.state.client_sc_card_types.clear(Episode3::CardType::INVALID_FF);
|
||||
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
set_mask_for_ep3_game_command(&state_cmd, sizeof(state_cmd), mask_key);
|
||||
}
|
||||
@@ -958,7 +958,7 @@ static bool add_next_game_client(
|
||||
ex_cmd.lose_entries[z].threshold = lose_entries[z].first;
|
||||
ex_cmd.lose_entries[z].value = lose_entries[z].second;
|
||||
}
|
||||
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
set_mask_for_ep3_game_command(&ex_cmd, sizeof(ex_cmd), mask_key);
|
||||
}
|
||||
@@ -1239,7 +1239,7 @@ static void on_DC_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (l->tournament_match) {
|
||||
auto tourn = l->tournament_match->tournament.lock();
|
||||
if (tourn) {
|
||||
send_ep3_set_tournament_player_decks(l, c, l->tournament_match);
|
||||
send_ep3_set_tournament_player_decks(s, l, c, l->tournament_match);
|
||||
string data = Episode3::Server::prepare_6xB6x41_map_definition(
|
||||
tourn->get_map());
|
||||
c->channel.send(0x6C, 0x00, data);
|
||||
@@ -1312,8 +1312,14 @@ static void on_CA_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
l->log.info("Recreating Episode 3 server state");
|
||||
}
|
||||
auto tourn = l->tournament_match ? l->tournament_match->tournament.lock() : nullptr;
|
||||
bool is_trial = (l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
l->ep3_server_base = make_shared<Episode3::ServerBase>(
|
||||
l, s->ep3_data_index, l->random_crypt, tourn ? tourn->get_map() : nullptr);
|
||||
l,
|
||||
is_trial ? s->ep3_card_index_trial : s->ep3_card_index,
|
||||
s->ep3_map_index,
|
||||
s->ep3_behavior_flags,
|
||||
l->random_crypt,
|
||||
tourn ? tourn->get_map() : nullptr);
|
||||
l->ep3_server_base->init();
|
||||
|
||||
if (s->ep3_behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
|
||||
@@ -1366,7 +1372,7 @@ static void on_CA_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
} else {
|
||||
throw logic_error("invalid winner team id");
|
||||
}
|
||||
send_ep3_tournament_match_result(l, l->tournament_match);
|
||||
send_ep3_tournament_match_result(s, l, l->tournament_match);
|
||||
|
||||
on_tournament_bracket_updated(s, tourn);
|
||||
l->ep3_server_base->server->tournament_match_result_sent = true;
|
||||
@@ -1866,6 +1872,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
}
|
||||
if ((game->version != c->version()) ||
|
||||
(!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) ||
|
||||
(!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) ||
|
||||
((game->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1))) {
|
||||
send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO.");
|
||||
break;
|
||||
@@ -2141,6 +2148,11 @@ static void on_84(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_lobby->is_game()) {
|
||||
send_lobby_message_box(c, u"$C6Can't change lobby\n\n$C7The specified lobby\nis a game.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_lobby->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
|
||||
send_lobby_message_box(c, u"$C6Can't change lobby\n\n$C7The lobby is for\nEpisode 3 only.");
|
||||
return;
|
||||
@@ -3203,10 +3215,11 @@ shared_ptr<Lobby> create_game_generic(
|
||||
(c->version() == GameVersion::BB) ||
|
||||
(s->item_tracking_enabled && (mode == GameMode::NORMAL || mode == GameMode::SOLO));
|
||||
|
||||
// only disable drops if the config flag is set and are playing regualr multi-mode.
|
||||
// drops are still enabled for battle and challenge modes.
|
||||
bool drops_enabled =
|
||||
(s->drops_enabled || (mode != GameMode::NORMAL));
|
||||
// Only disable drops if the config flag is set and are playing regular
|
||||
// multi-mode. Drops are still enabled for battle and challenge modes.
|
||||
bool drops_enabled = (s->drops_enabled || (mode != GameMode::NORMAL));
|
||||
|
||||
bool is_ep3_trial = (c->version() == GameVersion::GC) && (c->flags & Client::Flag::IS_EPISODE_3) && (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION);
|
||||
|
||||
shared_ptr<Lobby> game = s->create_lobby();
|
||||
game->name = name;
|
||||
@@ -3214,6 +3227,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
Lobby::Flag::GAME |
|
||||
(item_tracking_enabled ? Lobby::Flag::ITEM_TRACKING_ENABLED : 0) |
|
||||
(drops_enabled ? Lobby::Flag::DROPS_ENABLED : 0) |
|
||||
(is_ep3_trial ? Lobby::Flag::IS_EP3_TRIAL : 0) |
|
||||
((s->cheat_mode_behavior == ServerState::CheatModeBehavior::ON_BY_DEFAULT) ? Lobby::Flag::CHEATS_ENABLED : 0);
|
||||
game->password = password;
|
||||
game->version = c->version();
|
||||
|
||||
@@ -380,7 +380,7 @@ static void on_ep3_battle_subs(shared_ptr<ServerState> s,
|
||||
}
|
||||
}
|
||||
|
||||
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = 0;
|
||||
while (!mask_key) {
|
||||
mask_key = random_object<uint8_t>();
|
||||
|
||||
+13
-6
@@ -2180,7 +2180,9 @@ void send_rare_enemy_index_list(shared_ptr<Client> c, const vector<size_t>& inde
|
||||
|
||||
void send_ep3_card_list_update(shared_ptr<ServerState> s, shared_ptr<Client> c) {
|
||||
if (!(c->flags & Client::Flag::HAS_EP3_CARD_DEFS)) {
|
||||
const auto& data = s->ep3_data_index->get_compressed_card_definitions();
|
||||
const auto& data = (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)
|
||||
? s->ep3_card_index_trial->get_compressed_definitions()
|
||||
: s->ep3_card_index->get_compressed_definitions();
|
||||
|
||||
StringWriter w;
|
||||
w.put_u32l(data.size());
|
||||
@@ -2486,6 +2488,7 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
}
|
||||
|
||||
void send_ep3_set_tournament_player_decks(
|
||||
shared_ptr<ServerState> s,
|
||||
shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> c,
|
||||
shared_ptr<const Episode3::Tournament::Match> match) {
|
||||
@@ -2532,7 +2535,7 @@ void send_ep3_set_tournament_player_decks(
|
||||
add_entries_for_team(match->preceding_a->winner_team, 0);
|
||||
add_entries_for_team(match->preceding_b->winner_team, 2);
|
||||
|
||||
if (!(tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
set_mask_for_ep3_game_command(&cmd, sizeof(cmd), mask_key);
|
||||
}
|
||||
@@ -2543,7 +2546,7 @@ void send_ep3_set_tournament_player_decks(
|
||||
}
|
||||
|
||||
void send_ep3_tournament_match_result(
|
||||
shared_ptr<Lobby> l, shared_ptr<const Episode3::Tournament::Match> match) {
|
||||
shared_ptr<ServerState> s, shared_ptr<Lobby> l, shared_ptr<const Episode3::Tournament::Match> match) {
|
||||
auto tourn = match->tournament.lock();
|
||||
if (!tourn) {
|
||||
return;
|
||||
@@ -2585,13 +2588,13 @@ void send_ep3_tournament_match_result(
|
||||
// the player 1000000 and never charge for anything.
|
||||
cmd.meseta_amount = 100;
|
||||
cmd.meseta_reward_text = "You got %s meseta!";
|
||||
if (!(tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
|
||||
set_mask_for_ep3_game_command(&cmd, sizeof(cmd), mask_key);
|
||||
}
|
||||
send_command_t(l, 0xC9, 0x00, cmd);
|
||||
|
||||
if (tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
|
||||
if (s->ep3_behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
|
||||
send_text_message_printf(l, "$C5TOURN/%02hhX/%zu WIN %c",
|
||||
tourn->get_number(), match->round_num,
|
||||
match->winner_team == match->preceding_a->winner_team ? 'A' : 'B');
|
||||
@@ -2806,6 +2809,10 @@ void send_card_auction_if_all_clients_ready(
|
||||
distribution_size += it.second.first;
|
||||
}
|
||||
|
||||
auto card_index = (l->flags & Lobby::Flag::IS_EP3_TRIAL)
|
||||
? s->ep3_card_index_trial
|
||||
: s->ep3_card_index;
|
||||
|
||||
S_StartCardAuction_GC_Ep3_EF cmd;
|
||||
cmd.points_available = s->ep3_card_auction_points;
|
||||
for (size_t z = 0; z < num_cards; z++) {
|
||||
@@ -2814,7 +2821,7 @@ void send_card_auction_if_all_clients_ready(
|
||||
if (v >= it.second.first) {
|
||||
v -= it.second.first;
|
||||
} else {
|
||||
cmd.entries[z].card_id = s->ep3_data_index->definition_for_card_name(it.first)->def.card_id.load();
|
||||
cmd.entries[z].card_id = card_index->definition_for_name(it.first)->def.card_id.load();
|
||||
cmd.entries[z].min_price = it.second.second;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -352,10 +352,12 @@ void send_ep3_tournament_info(
|
||||
std::shared_ptr<Client> c,
|
||||
std::shared_ptr<const Episode3::Tournament> t);
|
||||
void send_ep3_set_tournament_player_decks(
|
||||
std::shared_ptr<ServerState> s,
|
||||
std::shared_ptr<Lobby> l,
|
||||
std::shared_ptr<Client> c,
|
||||
std::shared_ptr<const Episode3::Tournament::Match> match);
|
||||
void send_ep3_tournament_match_result(
|
||||
std::shared_ptr<ServerState> s,
|
||||
std::shared_ptr<Lobby> l,
|
||||
std::shared_ptr<const Episode3::Tournament::Match> match);
|
||||
|
||||
|
||||
+1
-1
@@ -451,7 +451,7 @@ Proxy session commands:\n\
|
||||
} else if (command_name == "create-tournament") {
|
||||
string name = get_quoted_string(command_args);
|
||||
string map_name = get_quoted_string(command_args);
|
||||
auto map = this->state->ep3_data_index->definition_for_map_name(map_name);
|
||||
auto map = this->state->ep3_map_index->definition_for_name(map_name);
|
||||
uint32_t num_teams = stoul(get_quoted_string(command_args), nullptr, 0);
|
||||
Episode3::Rules rules;
|
||||
rules.set_defaults();
|
||||
|
||||
+10
-5
@@ -888,19 +888,24 @@ void ServerState::load_item_tables() {
|
||||
}
|
||||
|
||||
void ServerState::load_ep3_data() {
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
this->ep3_data_index.reset(new Episode3::DataIndex(
|
||||
"system/ep3", this->ep3_behavior_flags));
|
||||
config_log.info("Collecting Episode 3 maps");
|
||||
this->ep3_map_index.reset(new Episode3::MapIndex("system/ep3"));
|
||||
config_log.info("Loading Episode 3 card definitions");
|
||||
this->ep3_card_index.reset(new Episode3::CardIndex("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr"));
|
||||
config_log.info("Loading Episode 3 trial card definitions");
|
||||
this->ep3_card_index_trial.reset(new Episode3::CardIndex("system/ep3/card-definitions-trial.mnr", "system/ep3/card-definitions-trial.mnrd", "system/ep3/card-text-trial.mnr"));
|
||||
config_log.info("Loading Episode 3 COM decks");
|
||||
this->ep3_com_deck_index.reset(new Episode3::COMDeckIndex("system/ep3/com-decks.json"));
|
||||
|
||||
const string& tournament_state_filename = "system/ep3/tournament-state.json";
|
||||
try {
|
||||
this->ep3_tournament_index.reset(new Episode3::TournamentIndex(
|
||||
this->ep3_data_index, tournament_state_filename));
|
||||
this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename));
|
||||
config_log.info("Loaded Episode 3 tournament state");
|
||||
} catch (const exception& e) {
|
||||
config_log.warning("Cannot load Episode 3 tournament state: %s", e.what());
|
||||
this->ep3_tournament_index.reset(new Episode3::TournamentIndex(
|
||||
this->ep3_data_index, tournament_state_filename, true));
|
||||
this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "Client.hh"
|
||||
#include "CommonItemSet.hh"
|
||||
#include "Episode3/DataIndex.hh"
|
||||
#include "Episode3/DataIndexes.hh"
|
||||
#include "Episode3/Tournament.hh"
|
||||
#include "FunctionCompiler.hh"
|
||||
#include "GSLArchive.hh"
|
||||
@@ -70,7 +70,10 @@ struct ServerState {
|
||||
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
||||
std::shared_ptr<const PatchFileIndex> bb_patch_file_index;
|
||||
std::shared_ptr<const DOLFileIndex> dol_file_index;
|
||||
std::shared_ptr<const Episode3::DataIndex> ep3_data_index;
|
||||
std::shared_ptr<const Episode3::CardIndex> ep3_card_index;
|
||||
std::shared_ptr<const Episode3::CardIndex> ep3_card_index_trial;
|
||||
std::shared_ptr<const Episode3::MapIndex> ep3_map_index;
|
||||
std::shared_ptr<const Episode3::COMDeckIndex> ep3_com_deck_index;
|
||||
std::shared_ptr<const QuestCategoryIndex> quest_category_index;
|
||||
std::shared_ptr<const QuestIndex> quest_index;
|
||||
std::shared_ptr<const LevelTable> level_table;
|
||||
|
||||
@@ -87,7 +87,6 @@ uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
Client::Flag::IS_EPISODE_3 |
|
||||
Client::Flag::IS_EP3_TRIAL_EDITION |
|
||||
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
|
||||
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
|
||||
case 0x42: // GC Ep3 JP
|
||||
return Client::Flag::NO_D6_AFTER_LOBBY |
|
||||
|
||||
Reference in New Issue
Block a user