add Ep3 trial map format
This commit is contained in:
@@ -1630,6 +1630,50 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
return join(lines, "\n");
|
||||
}
|
||||
|
||||
MapDefinitionTrial::MapDefinitionTrial(const MapDefinition& map)
|
||||
: unknown_a1(map.unknown_a1),
|
||||
map_number(map.map_number),
|
||||
width(map.width),
|
||||
height(map.height),
|
||||
environment_number(map.environment_number),
|
||||
num_alt_maps(map.num_alt_maps),
|
||||
map_tiles(map.map_tiles),
|
||||
start_tile_definitions(map.start_tile_definitions),
|
||||
alt_maps1(map.alt_maps1),
|
||||
alt_maps_unknown_a3(map.alt_maps_unknown_a3),
|
||||
unknown_a4(map.unknown_a4),
|
||||
modification_tiles(map.modification_tiles),
|
||||
unknown_a5(map.unknown_a5),
|
||||
default_rules(map.default_rules),
|
||||
unknown_a6(map.unknown_a6),
|
||||
name(map.name),
|
||||
location_name(map.location_name),
|
||||
quest_name(map.quest_name),
|
||||
description(map.description),
|
||||
map_x(map.map_x),
|
||||
map_y(map.map_y),
|
||||
npc_decks(map.npc_decks),
|
||||
npc_chars(map.npc_chars),
|
||||
unknown_a7_a(map.unknown_a7_a),
|
||||
unknown_a7_b(map.unknown_a7_b),
|
||||
before_message(map.before_message),
|
||||
after_message(map.after_message),
|
||||
dispatch_message(map.dispatch_message),
|
||||
dialogue_sets(),
|
||||
reward_card_ids(map.reward_card_ids),
|
||||
unknown_a9_a(map.unknown_a9_a),
|
||||
unknown_a9_b(map.unknown_a9_b),
|
||||
unknown_a9_c(map.unknown_a9_c),
|
||||
unknown_a9_d(map.unknown_a9_d),
|
||||
unknown_a10(map.unknown_a10),
|
||||
cyber_block_type(map.cyber_block_type),
|
||||
unknown_a11(map.unknown_a11),
|
||||
unknown_t12(0xFF) {
|
||||
for (size_t z = 0; z < this->dialogue_sets.size(); z++) {
|
||||
this->dialogue_sets[z] = map.dialogue_sets[z].sub<8>(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Rules::check_invalid_fields() const {
|
||||
Rules t = *this;
|
||||
return t.check_and_reset_invalid_fields();
|
||||
@@ -1969,11 +2013,19 @@ MapIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
|
||||
this->map = *reinterpret_cast<const MapDefinition*>(decompressed.data());
|
||||
}
|
||||
|
||||
string MapIndex::MapEntry::compressed() const {
|
||||
if (this->compressed_data.empty()) {
|
||||
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
|
||||
const string& MapIndex::MapEntry::compressed(bool is_trial) const {
|
||||
if (is_trial) {
|
||||
if (this->compressed_trial_data.empty()) {
|
||||
MapDefinitionTrial mdt(this->map);
|
||||
this->compressed_trial_data = prs_compress(&mdt, sizeof(mdt));
|
||||
}
|
||||
return this->compressed_trial_data;
|
||||
} else {
|
||||
if (this->compressed_data.empty()) {
|
||||
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
|
||||
}
|
||||
return this->compressed_data;
|
||||
}
|
||||
return this->compressed_data;
|
||||
}
|
||||
|
||||
const string& MapIndex::get_compressed_list() const {
|
||||
|
||||
+68
-11
@@ -553,7 +553,7 @@ struct CardDefinition {
|
||||
/* 00A0 */ ptext<char, 0x14> en_name;
|
||||
/* 00B4 */ ptext<char, 0x0B> jp_short_name;
|
||||
/* 00BF */ ptext<char, 0x08> en_short_name;
|
||||
/* 00C7 */ Effect effects[3];
|
||||
/* 00C7 */ parray<Effect, 3> effects;
|
||||
/* 0127 */ uint8_t unused4;
|
||||
/* 0128 */
|
||||
|
||||
@@ -573,9 +573,9 @@ struct CardDefinitionsFooter {
|
||||
/* 08 */ be_uint32_t num_cards2;
|
||||
/* 0C */ parray<be_uint32_t, 11> unknown_a2;
|
||||
/* 38 */ be_uint32_t unknown_offset_a3;
|
||||
/* 3C */ be_uint32_t unknown_a4[3];
|
||||
/* 3C */ parray<be_uint32_t, 3> unknown_a4;
|
||||
/* 48 */ be_uint32_t footer_offset;
|
||||
/* 4C */ be_uint32_t unknown_a5[3];
|
||||
/* 4C */ parray<be_uint32_t, 3> unknown_a5;
|
||||
/* 58 */
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -701,6 +701,12 @@ struct Rules {
|
||||
/* 0D */ parray<uint8_t, 3> unused;
|
||||
/* 10 */
|
||||
|
||||
// Annoyingly, this structure is a different size in Episode 3 Trial Edition.
|
||||
// This means that many command formats, as well as the map format, are
|
||||
// different, and the existing Server implementation can't serve Trial Edition
|
||||
// clients. It'd be nice to support Trial Edition battles, but that would
|
||||
// likely be more work than it's worth.
|
||||
|
||||
Rules();
|
||||
explicit Rules(std::shared_ptr<const JSONObject> json);
|
||||
std::shared_ptr<JSONObject> json() const;
|
||||
@@ -838,9 +844,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// - If the team has 3 players, bytes [3] through [5] are used.
|
||||
/* 010C */ parray<parray<uint8_t, 6>, 2> start_tile_definitions;
|
||||
|
||||
/* 0118 */ parray<parray<uint8_t, 0x10>, 0x10> alt_maps1[2][0x0A];
|
||||
/* 1518 */ parray<be_float, 0x12> alt_maps_unknown_a3[2][0x0A];
|
||||
/* 1AB8 */ parray<be_float, 0x24> unknown_a4[3];
|
||||
/* 0118 */ parray<parray<parray<parray<uint8_t, 0x10>, 0x10>, 0x0A>, 2> alt_maps1;
|
||||
/* 1518 */ parray<parray<parray<be_float, 0x12>, 0x0A>, 2> alt_maps_unknown_a3;
|
||||
/* 1AB8 */ parray<parray<be_float, 0x24>, 3> unknown_a4;
|
||||
|
||||
// In the modification_tiles array, the values are:
|
||||
// 10 = blocked by rock (as if the corresponding map_tiles value was 00)
|
||||
@@ -871,7 +877,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
|
||||
/* 58 */
|
||||
} __attribute__((packed));
|
||||
/* 1FE8 */ NPCDeck npc_decks[3]; // Unused if name[0] == 0
|
||||
/* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0
|
||||
|
||||
struct NPCCharacter {
|
||||
/* 0000 */ parray<be_uint16_t, 2> unknown_a1;
|
||||
/* 0004 */ parray<uint8_t, 4> unknown_a2;
|
||||
@@ -879,7 +886,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 0018 */ parray<be_uint16_t, 0x7E> unknown_a3;
|
||||
/* 0114 */
|
||||
} __attribute__((packed));
|
||||
/* 20F0 */ NPCCharacter npc_chars[3]; // Unused if name[0] == 0
|
||||
/* 20F0 */ parray<NPCCharacter, 3> npc_chars; // Unused if name[0] == 0
|
||||
|
||||
/* 242C */ parray<uint8_t, 8> unknown_a7_a; // Always FF?
|
||||
/* 2434 */ parray<be_uint32_t, 3> unknown_a7_b; // Always FF?
|
||||
@@ -896,10 +903,10 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
struct DialogueSet {
|
||||
/* 0000 */ be_uint16_t unknown_a1;
|
||||
/* 0002 */ be_uint16_t unknown_a2; // Always 0x0064 if valid, 0xFFFF if unused?
|
||||
/* 0004 */ ptext<char, 0x40> strings[4];
|
||||
/* 0004 */ parray<ptext<char, 0x40>, 4> strings;
|
||||
/* 0104 */
|
||||
} __attribute__((packed));
|
||||
/* 28F0 */ DialogueSet dialogue_sets[3][0x10]; // Up to 0x10 per valid NPC
|
||||
/* 28F0 */ parray<parray<DialogueSet, 0x10>, 3> dialogue_sets; // Up to 0x10 per valid NPC
|
||||
|
||||
/* 59B0 */ parray<be_uint16_t, 0x10> reward_card_ids;
|
||||
|
||||
@@ -958,6 +965,55 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
std::string str(const CardIndex* card_index = nullptr) const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MapDefinitionTrial {
|
||||
// This is the format of Episode 3 Trial Edition maps. See the comments in
|
||||
// MapDefinition for what each field means.
|
||||
|
||||
/* 0000 */ be_uint32_t unknown_a1;
|
||||
/* 0004 */ be_uint32_t map_number;
|
||||
/* 0008 */ uint8_t width;
|
||||
/* 0009 */ uint8_t height;
|
||||
/* 000A */ uint8_t environment_number;
|
||||
/* 000B */ uint8_t num_alt_maps;
|
||||
/* 000C */ parray<parray<uint8_t, 0x10>, 0x10> map_tiles;
|
||||
/* 010C */ parray<parray<uint8_t, 6>, 2> start_tile_definitions;
|
||||
/* 0118 */ parray<parray<parray<parray<uint8_t, 0x10>, 0x10>, 0x0A>, 2> alt_maps1;
|
||||
/* 1518 */ parray<parray<parray<be_float, 0x12>, 0x0A>, 2> alt_maps_unknown_a3;
|
||||
/* 1AB8 */ parray<parray<be_float, 0x24>, 3> unknown_a4;
|
||||
/* 1C68 */ parray<parray<uint8_t, 0x10>, 0x10> modification_tiles;
|
||||
/* 1D68 */ parray<uint8_t, 0x6C> unknown_a5;
|
||||
/* 1DD4 */ Rules default_rules;
|
||||
/* 1DE4 */ parray<uint8_t, 4> unknown_a6;
|
||||
/* 1DE8 */ ptext<char, 0x14> name;
|
||||
/* 1DFC */ ptext<char, 0x14> location_name;
|
||||
/* 1E10 */ ptext<char, 0x3C> quest_name;
|
||||
/* 1E4C */ ptext<char, 0x190> description;
|
||||
/* 1FDC */ be_uint16_t map_x;
|
||||
/* 1FDE */ be_uint16_t map_y;
|
||||
/* 1FE0 */ parray<MapDefinition::NPCDeck, 3> npc_decks;
|
||||
/* 20E8 */ parray<MapDefinition::NPCCharacter, 3> npc_chars;
|
||||
/* 2424 */ parray<uint8_t, 8> unknown_a7_a;
|
||||
/* 242C */ parray<be_uint32_t, 3> unknown_a7_b;
|
||||
/* 2438 */ ptext<char, 0x190> before_message;
|
||||
/* 25C8 */ ptext<char, 0x190> after_message;
|
||||
/* 2758 */ ptext<char, 0x190> dispatch_message;
|
||||
/* 28E8 */ parray<parray<MapDefinition::DialogueSet, 8>, 3> dialogue_sets;
|
||||
/* 4148 */ parray<be_uint16_t, 0x10> reward_card_ids;
|
||||
/* 4168 */ be_uint32_t unknown_a9_a;
|
||||
/* 416C */ be_uint32_t unknown_a9_b;
|
||||
/* 4170 */ be_uint16_t unknown_a9_c;
|
||||
/* 4172 */ be_uint16_t unknown_a9_d;
|
||||
/* 4174 */ uint8_t unknown_a10;
|
||||
/* 4175 */ uint8_t cyber_block_type;
|
||||
/* 4176 */ parray<uint8_t, 2> unknown_a11;
|
||||
// TODO: This field may contain some version of unavailable_sc_cards and/or
|
||||
// entry_states from MapDefinition, but the format isn't the same
|
||||
/* 4178 */ parray<uint8_t, 0x28> unknown_t12;
|
||||
/* 41A0 */
|
||||
|
||||
MapDefinitionTrial(const MapDefinition& map);
|
||||
} __attribute__((packed));
|
||||
|
||||
struct COMDeckDefinition {
|
||||
size_t index;
|
||||
std::string player_name;
|
||||
@@ -1000,10 +1056,11 @@ public:
|
||||
MapEntry(const MapDefinition& map, bool is_quest);
|
||||
MapEntry(const std::string& compressed_data, bool is_quest);
|
||||
|
||||
std::string compressed() const;
|
||||
const std::string& compressed(bool is_trial) const;
|
||||
|
||||
private:
|
||||
mutable std::string compressed_data;
|
||||
mutable std::string compressed_trial_data;
|
||||
};
|
||||
|
||||
const std::string& get_compressed_list() const;
|
||||
|
||||
@@ -225,8 +225,9 @@ void Server::send_6xB4x46() const {
|
||||
this->send(cmd46);
|
||||
}
|
||||
|
||||
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::MapEntry> map) {
|
||||
const auto& compressed = map->compressed();
|
||||
string Server::prepare_6xB6x41_map_definition(
|
||||
shared_ptr<const MapIndex::MapEntry> map, bool is_trial) {
|
||||
const auto& compressed = map->compressed(is_trial);
|
||||
|
||||
StringWriter w;
|
||||
uint32_t subcommand_size = (compressed.size() + sizeof(G_MapData_GC_Ep3_6xB6x41) + 3) & (~3);
|
||||
@@ -235,7 +236,7 @@ string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::MapEntr
|
||||
return std::move(w.str());
|
||||
}
|
||||
|
||||
void Server::send_commands_for_joining_spectator(Channel& c) const {
|
||||
void Server::send_commands_for_joining_spectator(Channel& c, bool is_trial) const {
|
||||
bool should_send_state = true;
|
||||
if (this->setup_phase == SetupPhase::REGISTRATION) {
|
||||
// If registration is still in progress, we only need to send the map data
|
||||
@@ -248,7 +249,7 @@ void Server::send_commands_for_joining_spectator(Channel& c) const {
|
||||
|
||||
auto map = this->base()->last_chosen_map;
|
||||
if (map) {
|
||||
string data = this->prepare_6xB6x41_map_definition(map);
|
||||
string data = this->prepare_6xB6x41_map_definition(map, is_trial);
|
||||
c.send(0x6C, 0x00, data);
|
||||
}
|
||||
|
||||
@@ -2167,7 +2168,8 @@ void Server::handle_6xB3x41_map_request(const string& data) {
|
||||
}
|
||||
|
||||
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);
|
||||
auto out_cmd = this->prepare_6xB6x41_map_definition(
|
||||
base->last_chosen_map, l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
send_command(l, 0x6C, 0x00, out_cmd);
|
||||
for (auto watcher_l : l->watcher_lobbies) {
|
||||
send_command_if_not_loading(watcher_l, 0x6C, 0x00, out_cmd);
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
}
|
||||
void send(const void* data, size_t size) const;
|
||||
|
||||
void send_commands_for_joining_spectator(Channel& ch) const;
|
||||
void send_commands_for_joining_spectator(Channel& ch, bool is_trial) const;
|
||||
|
||||
__attribute__((format(printf, 2, 3))) void log_debug(const char* fmt, ...) const;
|
||||
|
||||
@@ -233,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 MapIndex::MapEntry> map);
|
||||
std::shared_ptr<const MapIndex::MapEntry> map, bool is_trial);
|
||||
G_SetTrapTileLocations_GC_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() const;
|
||||
|
||||
std::vector<std::shared_ptr<Card>> const_cast_set_cards_v(
|
||||
|
||||
@@ -916,9 +916,9 @@ static HandlerResult S_6x(shared_ptr<ServerState>,
|
||||
string map_data = prs_decompress(
|
||||
data.data() + sizeof(cmd), data.size() - sizeof(cmd));
|
||||
save_file(filename, map_data);
|
||||
if (map_data.size() != sizeof(Episode3::MapDefinition)) {
|
||||
session.log.warning("Wrote %zu bytes to %s (expected %zu bytes; the file may be invalid)",
|
||||
map_data.size(), filename.c_str(), sizeof(Episode3::MapDefinition));
|
||||
if (map_data.size() != sizeof(Episode3::MapDefinition) && map_data.size() != sizeof(Episode3::MapDefinitionTrial)) {
|
||||
session.log.warning("Wrote %zu bytes to %s (expected %zu or %zu bytes; the file may be invalid)",
|
||||
map_data.size(), filename.c_str(), sizeof(Episode3::MapDefinitionTrial), sizeof(Episode3::MapDefinition));
|
||||
} else {
|
||||
session.log.info("Wrote %zu bytes to %s", map_data.size(), filename.c_str());
|
||||
}
|
||||
|
||||
@@ -1241,7 +1241,7 @@ static void on_DC_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (tourn) {
|
||||
send_ep3_set_tournament_player_decks(s, l, c, l->tournament_match);
|
||||
string data = Episode3::Server::prepare_6xB6x41_map_definition(
|
||||
tourn->get_map());
|
||||
tourn->get_map(), l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
c->channel.send(0x6C, 0x00, data);
|
||||
}
|
||||
}
|
||||
@@ -3525,7 +3525,8 @@ static void on_6F(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||
if (l->battle_player && (l->flags & Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY)) {
|
||||
l->battle_player->start();
|
||||
} else if (watched_lobby && watched_lobby->ep3_server_base) {
|
||||
watched_lobby->ep3_server_base->server->send_commands_for_joining_spectator(c->channel);
|
||||
watched_lobby->ep3_server_base->server->send_commands_for_joining_spectator(
|
||||
c->channel, c->flags & Client::Flag::IS_EP3_TRIAL_EDITION);
|
||||
}
|
||||
|
||||
// If there are more players to bring in, try to do so
|
||||
|
||||
Reference in New Issue
Block a user