move ep3 behavior flags into DataIndex
This commit is contained in:
+24
-13
@@ -5,6 +5,7 @@
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <phosg/Filesystem.hh>
|
||||
#include <phosg/Time.hh>
|
||||
|
||||
#include "../Loggers.hh"
|
||||
#include "../Compression.hh"
|
||||
@@ -1154,12 +1155,12 @@ bool Rules::check_and_reset_invalid_fields() {
|
||||
|
||||
|
||||
|
||||
DataIndex::DataIndex(const string& directory, bool debug)
|
||||
: debug(debug) {
|
||||
DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
|
||||
: behavior_flags(behavior_flags) {
|
||||
|
||||
unordered_map<uint32_t, vector<string>> card_tags;
|
||||
unordered_map<uint32_t, string> card_text;
|
||||
if (this->debug) {
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
try {
|
||||
string data = prs_decompress(load_file(directory + "/card-text.mnr"));
|
||||
StringReader r(data);
|
||||
@@ -1248,14 +1249,11 @@ DataIndex::DataIndex(const string& directory, bool debug)
|
||||
string decompressed_data;
|
||||
if (isfile(directory + "/card-definitions.mnrd")) {
|
||||
decompressed_data = load_file(directory + "/card-definitions.mnrd");
|
||||
this->compressed_card_definitions = prs_compress(decompressed_data);
|
||||
this->compressed_card_definitions.clear();
|
||||
} else {
|
||||
this->compressed_card_definitions = load_file(directory + "/card-definitions.mnr");
|
||||
decompressed_data = prs_decompress(this->compressed_card_definitions);
|
||||
}
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
throw runtime_error("compressed card list data is too long");
|
||||
}
|
||||
if (decompressed_data.size() > 0x36EC0) {
|
||||
throw runtime_error("decompressed card list data is too long");
|
||||
}
|
||||
@@ -1266,17 +1264,18 @@ DataIndex::DataIndex(const string& directory, bool debug)
|
||||
"decompressed card update file size %zX is not aligned with card definition size %zX (%zX extra bytes)",
|
||||
decompressed_data.size(), sizeof(CardDefinition), decompressed_data.size() % sizeof(CardDefinition)));
|
||||
}
|
||||
const auto* def = reinterpret_cast<const CardDefinition*>(decompressed_data.data());
|
||||
auto* defs = reinterpret_cast<CardDefinition*>(decompressed_data.data());
|
||||
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 (def[x].card_id & 0xFFFF0000) {
|
||||
if (defs[x].card_id & 0xFFFF0000) {
|
||||
continue;
|
||||
}
|
||||
shared_ptr<CardEntry> entry(new CardEntry({def[x], {}, {}}));
|
||||
|
||||
shared_ptr<CardEntry> entry(new CardEntry({defs[x], {}, {}}));
|
||||
if (!this->card_definitions.emplace(entry->def.card_id, entry).second) {
|
||||
throw runtime_error(string_printf(
|
||||
"duplicate card id: %08" PRIX32, entry->def.card_id.load()));
|
||||
@@ -1293,16 +1292,28 @@ DataIndex::DataIndex(const string& directory, bool debug)
|
||||
entry->def.mv.decode_code();
|
||||
entry->def.decode_range();
|
||||
|
||||
if (this->debug) {
|
||||
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
|
||||
try {
|
||||
entry->text = move(card_text.at(def[x].card_id));
|
||||
entry->text = move(card_text.at(defs[x].card_id));
|
||||
} catch (const out_of_range&) { }
|
||||
try {
|
||||
entry->debug_tags = move(card_tags.at(def[x].card_id));
|
||||
entry->debug_tags = move(card_tags.at(defs[x].card_id));
|
||||
} catch (const out_of_range&) { }
|
||||
}
|
||||
}
|
||||
|
||||
if (this->compressed_card_definitions.empty()) {
|
||||
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);
|
||||
}
|
||||
if (this->compressed_card_definitions.size() > 0x7BF8) {
|
||||
throw runtime_error("compressed card list data is too long");
|
||||
}
|
||||
|
||||
static_game_data_log.info("Indexed %zu Episode 3 card definitions", this->card_definitions.size());
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to load Episode 3 card update: %s", e.what());
|
||||
|
||||
@@ -23,6 +23,17 @@ class DataIndex;
|
||||
|
||||
|
||||
|
||||
enum BehaviorFlag {
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum class StatSwapType : uint8_t {
|
||||
NONE = 0,
|
||||
A_T_SWAP = 1,
|
||||
@@ -462,7 +473,7 @@ struct CardDefinition {
|
||||
|
||||
be_uint32_t card_id;
|
||||
parray<uint8_t, 0x40> jp_name;
|
||||
CardType type; // Type enum. If <0, then this is the end of the card list
|
||||
CardType type; // If <0 (signed), then this is the end of the card list
|
||||
uint8_t self_cost; // ATK dice points required
|
||||
uint8_t ally_cost; // ATK points from allies required; PBs use this
|
||||
uint8_t unused1;
|
||||
@@ -767,7 +778,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
|
||||
class DataIndex {
|
||||
public:
|
||||
explicit DataIndex(const std::string& directory, bool debug = false);
|
||||
DataIndex(const std::string& directory, uint32_t behavior_flags);
|
||||
|
||||
struct CardEntry {
|
||||
CardDefinition def;
|
||||
@@ -798,9 +809,9 @@ public:
|
||||
std::shared_ptr<const MapEntry> definition_for_map_number(uint32_t id) const;
|
||||
std::set<uint32_t> all_map_ids() const;
|
||||
|
||||
private:
|
||||
bool debug;
|
||||
const uint32_t behavior_flags;
|
||||
|
||||
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;
|
||||
|
||||
@@ -33,11 +33,9 @@ void ServerBase::PresenceEntry::clear() {
|
||||
ServerBase::ServerBase(
|
||||
shared_ptr<Lobby> lobby,
|
||||
shared_ptr<const DataIndex> data_index,
|
||||
uint32_t behavior_flags,
|
||||
uint32_t random_seed)
|
||||
: lobby(lobby),
|
||||
data_index(data_index),
|
||||
behavior_flags(behavior_flags),
|
||||
random_seed(random_seed) { }
|
||||
|
||||
void ServerBase::init() {
|
||||
@@ -154,7 +152,7 @@ void Server::send(const void* data, size_t size) const {
|
||||
__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()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
std::string buf = string_vprintf(fmt, va);
|
||||
@@ -1714,7 +1712,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->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
|
||||
if (b->data_index->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;
|
||||
@@ -1744,9 +1742,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()->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
|
||||
// Note: Sega's original implementation doesn't use the card counts here
|
||||
if (this->base()->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
|
||||
if (this->base()->data_index->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,
|
||||
@@ -1756,7 +1754,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()->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
|
||||
if (!(this->base()->data_index->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;
|
||||
|
||||
@@ -52,20 +52,11 @@ class Server;
|
||||
|
||||
|
||||
|
||||
enum BehaviorFlag {
|
||||
SKIP_DECK_VERIFY = 0x00000001,
|
||||
IGNORE_CARD_COUNTS = 0x00000002,
|
||||
SKIP_D1_D2_REPLACE = 0x00000004,
|
||||
DISABLE_TIME_LIMITS = 0x00000008,
|
||||
ENABLE_STATUS_MESSAGES = 0x00000010,
|
||||
};
|
||||
|
||||
class ServerBase : public std::enable_shared_from_this<ServerBase> {
|
||||
public:
|
||||
ServerBase(
|
||||
std::shared_ptr<Lobby> lobby,
|
||||
std::shared_ptr<const DataIndex> data_index,
|
||||
uint32_t behavior_flags,
|
||||
uint32_t random_seed);
|
||||
void init();
|
||||
void reset();
|
||||
@@ -81,7 +72,6 @@ public:
|
||||
|
||||
std::weak_ptr<Lobby> lobby;
|
||||
std::shared_ptr<const DataIndex> data_index;
|
||||
uint32_t behavior_flags;
|
||||
uint32_t random_seed;
|
||||
|
||||
std::shared_ptr<MapAndRulesState> map_and_rules1;
|
||||
|
||||
+3
-2
@@ -778,7 +778,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
case Behavior::SHOW_EP3_DATA: {
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
Episode3::DataIndex index("system/ep3", true);
|
||||
Episode3::DataIndex index("system/ep3", Episode3::BehaviorFlag::LOAD_CARD_TEXT);
|
||||
|
||||
if (ep3_card_id == 0xFFFF) {
|
||||
auto map_ids = index.all_map_ids();
|
||||
@@ -884,7 +884,8 @@ int main(int argc, char** argv) {
|
||||
state->load_bb_file("ItemRT.rel")));
|
||||
|
||||
config_log.info("Collecting Episode 3 data");
|
||||
state->ep3_data_index.reset(new Episode3::DataIndex("system/ep3"));
|
||||
state->ep3_data_index.reset(new Episode3::DataIndex(
|
||||
"system/ep3", state->ep3_behavior_flags));
|
||||
|
||||
config_log.info("Collecting quest metadata");
|
||||
state->quest_index.reset(new QuestIndex("system/quests"));
|
||||
|
||||
@@ -921,7 +921,7 @@ static void on_ep3_server_data_request(shared_ptr<ServerState> s, shared_ptr<Cli
|
||||
l->log.info("Recreating Episode 3 server state");
|
||||
}
|
||||
l->ep3_server_base = make_shared<Episode3::ServerBase>(
|
||||
l, s->ep3_data_index, s->ep3_behavior_flags, l->random_seed);
|
||||
l, s->ep3_data_index, l->random_seed);
|
||||
l->ep3_server_base->init();
|
||||
|
||||
if (s->ep3_behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
|
||||
|
||||
@@ -259,6 +259,9 @@
|
||||
// 0x00000008 => Disable overall and per-phase battle time limits, regardless
|
||||
// of the options chosen during battle setup
|
||||
// 0x00000010 => Enable debug messages in Episode 3 games and battles
|
||||
// 0x00000020 => Load card text as well as card definitions (has no behavioral
|
||||
// effects; this flag exists to be used internally when the
|
||||
// --show-ep3-data option is given)
|
||||
"Episode3BehaviorFlags": 0x00000002,
|
||||
|
||||
// Episode 3 card auction configuration. CardAuctionPoints specifies how many
|
||||
|
||||
Reference in New Issue
Block a user