diff --git a/src/FileContentsCache.cc b/src/FileContentsCache.cc index 15c176a1..91fb7679 100644 --- a/src/FileContentsCache.cc +++ b/src/FileContentsCache.cc @@ -79,20 +79,13 @@ shared_ptr ThreadSafeFileCache::get( const string& name, std::function(const std::string&)> generate) { try { shared_lock g(this->lock); - auto ret = this->name_to_file.at(name); - if (!ret) { - throw cannot_open_file(name); - } - return ret; + return this->name_to_file.at(name); } catch (const out_of_range&) { unique_lock g(this->lock); auto it = this->name_to_file.find(name); if (it == this->name_to_file.end()) { it = this->name_to_file.emplace(name, generate(name)).first; } - if (!it->second) { - throw cannot_open_file(name); - } return it->second; } } diff --git a/src/Lobby.cc b/src/Lobby.cc index 4b20f6d0..d63d0e21 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -291,10 +291,30 @@ shared_ptr Lobby::load_maps( uint8_t difficulty, uint8_t event, uint32_t lobby_id, + shared_ptr sdt, function(Version, const string&)> get_file_data, shared_ptr rare_rates, shared_ptr random_crypt, - const parray& variations) { + const parray& variations, + const PrefixedLogger* log) { + auto enemy_filenames = sdt->map_filenames_for_variations(variations, episode, mode, true); + auto object_filenames = sdt->map_filenames_for_variations(variations, episode, mode, false); + return Lobby::load_maps(enemy_filenames, object_filenames, version, episode, mode, difficulty, event, lobby_id, get_file_data, rare_rates, random_crypt, log); +} + +shared_ptr Lobby::load_maps( + const vector& enemy_filenames, + const vector& object_filenames, + Version version, + Episode episode, + GameMode mode, + uint8_t difficulty, + uint8_t event, + uint32_t lobby_id, + function(Version, const string&)> get_file_data, + shared_ptr rare_rates, + shared_ptr random_crypt, + const PrefixedLogger* log) { auto map = make_shared(version, lobby_id, random_crypt); // Don't load free-roam maps in Challenge mode, since players can't go to @@ -303,58 +323,42 @@ shared_ptr Lobby::load_maps( return map; } - for (size_t floor = 0; floor < 0x10; floor++) { - auto enemy_filenames = map_filenames_for_variation( - version, - episode, - mode, - floor, - variations[floor * 2], - variations[floor * 2 + 1], - true); - if (!enemy_filenames.empty()) { - bool any_map_loaded = false; - for (const string& filename : enemy_filenames) { - auto map_data = get_file_data(version, filename); - if (map_data) { - map->add_enemies_from_map_data( - episode, - difficulty, - event, - floor, - map_data->data(), - map_data->size(), - rare_rates); - any_map_loaded = true; - break; + for (size_t floor = 0; floor < 0x12; floor++) { + const auto& floor_enemy_filename = enemy_filenames.at(floor); + if (!floor_enemy_filename.empty()) { + auto map_data = get_file_data(version, floor_enemy_filename); + if (map_data) { + map->add_enemies_from_map_data( + episode, + difficulty, + event, + floor, + map_data->data(), + map_data->size(), + rare_rates); + if (log) { + log->info("Loaded enemies map %s for floor %02zX", floor_enemy_filename.c_str(), floor); } + } else if (log) { + log->info("Enemies map %s for floor %02zX cannot be used; skipping", floor_enemy_filename.c_str(), floor); } - if (!any_map_loaded) { - throw runtime_error(string_printf("no enemy maps loaded for floor %zu", floor)); - } + } else if (log) { + log->info("No enemies to load for floor %02zX", floor); } - auto object_filenames = map_filenames_for_variation( - version, - episode, - mode, - floor, - variations[floor * 2], - variations[floor * 2 + 1], - false); - if (!object_filenames.empty()) { - bool any_map_loaded = false; - for (const string& filename : object_filenames) { - auto map_data = get_file_data(version, filename); - if (map_data) { - map->add_objects_from_map_data(floor, map_data->data(), map_data->size()); - any_map_loaded = true; - break; + const auto& floor_object_filename = object_filenames.at(floor); + if (!floor_object_filename.empty()) { + auto map_data = get_file_data(version, floor_object_filename); + if (map_data) { + map->add_objects_from_map_data(floor, map_data->data(), map_data->size()); + if (log) { + log->info("Loaded objects map %s for floor %02zX", floor_object_filename.c_str(), floor); } + } else if (log) { + log->info("Objects map %s for floor %02zX cannot be used; skipping", floor_object_filename.c_str(), floor); } - if (!any_map_loaded) { - throw runtime_error(string_printf("no object maps loaded for floor %zu", floor)); - } + } else if (log) { + log->info("No objects to load for floor %02zX", floor); } } @@ -392,10 +396,12 @@ void Lobby::load_maps() { this->difficulty, this->event, this->lobby_id, + s->set_data_table(this->base_version, this->episode, this->mode, this->difficulty), bind(&ServerState::load_map_file, s.get(), placeholders::_1, placeholders::_2), rare_rates, this->random_crypt, - this->variations); + this->variations, + &this->log); } else { this->map = make_shared(this->base_version, this->lobby_id, this->random_crypt); diff --git a/src/Lobby.hh b/src/Lobby.hh index 82d93b83..edb6d8ae 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -211,10 +211,25 @@ struct Lobby : public std::enable_shared_from_this { uint8_t difficulty, uint8_t event, uint32_t lobby_id, + std::shared_ptr sdt, std::function(Version, const std::string&)> get_file_data, std::shared_ptr rare_rates, std::shared_ptr random_crypt, - const parray& variations); + const parray& variations, + const PrefixedLogger* log = nullptr); + static std::shared_ptr load_maps( + const std::vector& enemy_filenames, + const std::vector& object_filenames, + Version version, + Episode episode, + GameMode mode, + uint8_t difficulty, + uint8_t event, + uint32_t lobby_id, + std::function(Version, const std::string&)> get_file_data, + std::shared_ptr rare_rates, + std::shared_ptr random_crypt, + const PrefixedLogger* log = nullptr); void load_maps(); void create_ep3_server(); diff --git a/src/Main.cc b/src/Main.cc index 888bc3d7..af777835 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1114,6 +1114,196 @@ Action a_disassemble_quest_map( string result = Map::disassemble_quest_data(data.data(), data.size()); write_output_data(args, result.data(), result.size(), "txt"); }); +Action a_disassemble_set_data_table( + "disassemble-set-data-table", "\ + disassemble-set-data-table [INPUT-FILENAME]\n\ + Show the contents of a SetDataTable.rel file. A version option is required.\n", + +[](Arguments& args) { + Version version = get_cli_version(args); + SetDataTable sdt(version, read_input_data(args)); + string str = sdt.str(); + write_output_data(args, str.data(), str.size(), "txt"); + }); +Action a_check_set_data_table( + "check-set-data-tables", nullptr, +[](Arguments&) { + ServerState s; + s.load_objects_and_upstream_dependents("set_data_tables"); + static_game_data_log.min_level = LogLevel::DISABLED; + + auto get_file_data = [&](Version version, const string& filename) -> shared_ptr { + try { + return s.load_map_file(version, filename); + } catch (const cannot_open_file&) { + return nullptr; + } + }; + + auto check_filenames = [&](Version version, const string& sdt_filename, const vector& ns_filenames) -> string { + for (size_t z = 0; z < ns_filenames.size(); z++) { + const auto& ns_filename = ns_filenames[z]; + auto data = get_file_data(version, ns_filename); + if (data) { + if (sdt_filename != ns_filename) { + string ns_filenames_str = join(ns_filenames, ", "); + return string_printf("SDT => %s, NS => [%s]", sdt_filename.c_str(), ns_filenames_str.c_str()); + } + return "OK"; + } + if (!data && (sdt_filename == ns_filename)) { + string ns_filenames_str = join(ns_filenames, ", "); + return string_printf("SDT => %s (missing)", sdt_filename.c_str()); + } + } + if (ns_filenames.empty() && sdt_filename.empty()) { + return "OK (no files)"; + } else if (ns_filenames.empty()) { + auto data = get_file_data(version, sdt_filename); + if (data) { + return string_printf("NS blank, SDT => %s", sdt_filename.c_str()); + } else { + return string_printf("NS blank, SDT => %s (missing)", sdt_filename.c_str()); + } + } else if (sdt_filename.empty()) { + string ns_filenames_str = join(ns_filenames, ", "); + return string_printf("SDT blank, NS => [%s] (all missing)", ns_filenames_str.c_str()); + } else { + string ns_filenames_str = join(ns_filenames, ", "); + return string_printf("SDT => %s (missing), NS => [%s] (all missing)", sdt_filename.c_str(), ns_filenames_str.c_str()); + } + }; + + size_t num_checks = 0; + size_t num_errors = 0; + auto check_table = [&](Version version) { + vector episodes({Episode::EP1}); + if (!is_v1_or_v2(version) || (version == Version::GC_NTE)) { + episodes.emplace_back(Episode::EP2); + if (is_v4(version)) { + episodes.emplace_back(Episode::EP4); + } + } + + vector modes({GameMode::NORMAL}); + if (!is_v1(version)) { + modes.emplace_back(GameMode::BATTLE); + modes.emplace_back(GameMode::CHALLENGE); + } + if (is_v4(version)) { + modes.emplace_back(GameMode::SOLO); + } + + uint8_t max_difficulty = is_v1(version) ? 2 : 3; + + for (Episode episode : episodes) { + for (GameMode mode : modes) { + for (uint8_t difficulty = 0; difficulty <= max_difficulty; difficulty++) { + auto sdt = s.set_data_table(version, episode, mode, difficulty); + auto ns_var_maxes = variation_maxes_deprecated(version, episode, (mode == GameMode::SOLO)); + size_t num_floors; + if (episode == Episode::EP4) { + num_floors = 0x0B; + } else if (episode == Episode::EP2) { + num_floors = 0x10; + } else { + num_floors = 0x0F; + } + for (size_t floor = 0; floor < num_floors; floor++) { + auto sdt_var_avail = sdt->num_available_variations_for_floor(episode, floor); + auto sdt_var_maxes = sdt->num_free_roam_variations_for_floor(episode, mode == GameMode::SOLO, floor); + size_t sdt_var1_max_avail = sdt_var_avail.first - 1; + size_t sdt_var2_max_avail = sdt_var_avail.second - 1; + size_t sdt_var1_max = sdt_var_maxes.first - 1; + size_t sdt_var2_max = sdt_var_maxes.second - 1; + size_t ns_var1_max = ns_var_maxes[floor * 2]; + size_t ns_var2_max = ns_var_maxes[floor * 2 + 1]; + num_checks += 4; + if (sdt_var1_max > sdt_var1_max_avail) { + fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR1:[SDT:%02zX SDTA:%02zX]\n", + name_for_enum(version), + name_for_episode(episode), + name_for_mode(mode), + name_for_difficulty(difficulty), + floor, + sdt_var1_max, + sdt_var1_max_avail); + num_errors++; + } + if (sdt_var2_max > sdt_var2_max_avail) { + fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR2:[SDT:%02zX SDTA:%02zX]\n", + name_for_enum(version), + name_for_episode(episode), + name_for_mode(mode), + name_for_difficulty(difficulty), + floor, + sdt_var2_max, + sdt_var2_max_avail); + num_errors++; + } + if (sdt_var1_max < ns_var1_max) { + fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR1:[SDT:%02zX NS:%02zX]\n", + name_for_enum(version), + name_for_episode(episode), + name_for_mode(mode), + name_for_difficulty(difficulty), + floor, + sdt_var1_max, + ns_var1_max); + num_errors++; + } + if (sdt_var2_max < ns_var2_max) { + fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR2:[SDT:%02zX NS:%02zX]\n", + name_for_enum(version), + name_for_episode(episode), + name_for_mode(mode), + name_for_difficulty(difficulty), + floor, + sdt_var2_max, + ns_var2_max); + num_errors++; + } + for (size_t var1 = 0; var1 <= ns_var1_max; var1++) { + for (size_t var2 = 0; var2 <= ns_var2_max; var2++) { + auto sdt_enemy_filename = sdt->map_filename_for_variation(floor, var1, var2, episode, mode, true); + auto sdt_object_filename = sdt->map_filename_for_variation(floor, var1, var2, episode, mode, false); + auto ns_enemy_filenames = map_filenames_for_variation_deprecated(floor, var1, var2, version, episode, mode, true); + auto ns_object_filenames = map_filenames_for_variation_deprecated(floor, var1, var2, version, episode, mode, false); + string enemies_error = check_filenames(version, sdt_enemy_filename, ns_enemy_filenames); + string objects_error = check_filenames(version, sdt_object_filename, ns_object_filenames); + num_checks += 2; + num_errors += (enemies_error != "OK") + (objects_error != "OK"); + fprintf(stdout, "%s %-8s %-10s %-10s %-10s %02zX %02zX %02zX E:[%s] O:[%s] E:%-30s O:%-30s\n", + ((enemies_error != "OK") || (objects_error != "OK")) ? "##" : " ", + name_for_enum(version), + name_for_episode(episode), + name_for_mode(mode), + name_for_difficulty(difficulty), + floor, + var1, + var2, + enemies_error.c_str(), + objects_error.c_str(), + sdt_enemy_filename.c_str(), + sdt_object_filename.c_str()); + } + } + } + } + } + } + }; + + check_table(Version::DC_NTE); + check_table(Version::DC_V1_11_2000_PROTOTYPE); + check_table(Version::DC_V1); + check_table(Version::DC_V2); + check_table(Version::PC_NTE); + check_table(Version::PC_V2); + check_table(Version::GC_NTE); + check_table(Version::GC_V3); + check_table(Version::XB_V3); + check_table(Version::BB_V4); + fprintf(stdout, "%zu/%zu errors\n", num_errors, num_checks); + }); Action a_assemble_quest_script( "assemble-quest-script", "\ @@ -1883,7 +2073,7 @@ Action a_find_rare_enemy_seeds( map = Lobby::load_maps(version, episode, difficulty, 0, 0, rare_rates, random_crypt, vq); } else { - generate_variations(variations, random_crypt, version, episode, (mode == GameMode::SOLO)); + generate_variations_deprecated(variations, random_crypt, version, episode, (mode == GameMode::SOLO)); map = Lobby::load_maps( version, episode, @@ -1891,6 +2081,7 @@ Action a_find_rare_enemy_seeds( difficulty, 0, 0, + s.set_data_table(version, episode, mode, difficulty), bind(&ServerState::load_map_file, &s, placeholders::_1, placeholders::_2), rare_rates, random_crypt, diff --git a/src/Map.cc b/src/Map.cc index 36449322..cde22a2e 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -1637,8 +1637,59 @@ string Map::disassemble_quest_data(const void* data, size_t size) { return join(ret, "\n") + "\n"; } -SetDataTable::SetDataTable(shared_ptr data, bool big_endian) { - if (big_endian) { +SetDataTableBase::SetDataTableBase(Version version) : version(version) {} + +parray SetDataTableBase::generate_variations( + Episode episode, bool is_solo, std::shared_ptr random_crypt) const { + parray ret; + for (size_t floor = 0; floor < 0x10; floor++) { + auto num_vars = this->num_free_roam_variations_for_floor(episode, is_solo, floor); + ret[floor * 2] = (num_vars.first > 1) ? (random_crypt->next() % num_vars.first) : 0; + ret[floor * 2 + 1] = (num_vars.second > 1) ? (random_crypt->next() % num_vars.second) : 0; + } + return ret; +} + +vector SetDataTableBase::map_filenames_for_variations( + const parray& variations, Episode episode, GameMode mode, bool is_enemies) const { + vector ret; + for (uint8_t floor = 0; floor < 0x10; floor++) { + ret.emplace_back(this->map_filename_for_variation( + floor, variations[floor * 2], variations[floor * 2 + 1], episode, mode, is_enemies)); + } + for (uint8_t floor = 0x10; floor < 0x12; floor++) { + ret.emplace_back(this->map_filename_for_variation(floor, 0, 0, episode, mode, is_enemies)); + } + return ret; +} + +uint8_t SetDataTableBase::default_area_for_floor(Episode episode, uint8_t floor) const { + // For some inscrutable reason, Pioneer 2's area number in Episode 4 is + // discontiguous with all the rest. Why, Sega?? + static const std::array areas_ep1 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11}; + static const std::array areas_ep2_gc_nte = { + 0x00, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0xFF, 0xFF}; + static const std::array areas_ep2 = { + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23}; + static const std::array areas_ep4 = { + 0x2D, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + switch (episode) { + case Episode::EP1: + return areas_ep1.at(floor); + case Episode::EP2: { + const auto& areas = ((this->version == Version::GC_NTE) ? areas_ep2_gc_nte : areas_ep2); + return areas.at(floor); + } + case Episode::EP4: + return areas_ep4.at(floor); + default: + throw logic_error("incorrect episode"); + } +} + +SetDataTable::SetDataTable(Version version, const string& data) : SetDataTableBase(version) { + if (is_big_endian(this->version)) { this->load_table_t(data); } else { this->load_table_t(data); @@ -1646,10 +1697,10 @@ SetDataTable::SetDataTable(shared_ptr data, bool big_endian) { } template -void SetDataTable::load_table_t(shared_ptr data) { +void SetDataTable::load_table_t(const string& data) { using U32T = typename conditional::type; - StringReader r(*data); + StringReader r(data); struct Footer { U32T table3_offset; @@ -1688,17 +1739,99 @@ void SetDataTable::load_table_t(shared_ptr data) { } } -void SetDataTable::print(FILE* stream) const { +pair SetDataTable::num_available_variations_for_floor(Episode episode, uint8_t floor) const { + uint8_t area = this->default_area_for_floor(episode, floor); + if (area == 0xFF) { + return make_pair(1, 1); + } else { + const auto& e = this->entries.at(area); + return make_pair(e.size(), e.at(0).size()); + } +} + +pair SetDataTable::num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const { + uint8_t area = this->default_area_for_floor(episode, floor); + if (area == 0xFF) { + return make_pair(1, 1); + } + static const array counts_on = { + // Episode 1 (00-11) + // P2 -F1-, -F2-, -C1-, -C2-, -C3-, -M1-, -M2-, -R1-, -R2-, -R3-, DRGN, DRL-, -VO-, -DF-, LOBBY, VS1-, VS2-, + 1, 1, 1, 5, 1, 5, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, + // Episode 2 (12-23) + // P2 VRTA, VRTB, VRSA, VRSB, CCA-, -JN-, -JS-, MNTN, SEAS, SBU-, SBL-, -GG-, -OF-, -BR-, -GD-, SSN-, TWR-, + 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 3, 1, 3, 1, 3, 2, 2, 1, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // Episode 4 (24-2E) + // CE -CW-, -CS-, -CN-, -CI-, DES1, DES2, DES3, SMIL, -P2-, TEST + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1}; + static const array counts_off = { + // Episode 1 (00-11) + // P2 -F1-, -F2-, -C1-, -C2-, -C3-, -M1-, -M2-, -R1-, -R2-, -R3-, DRGN, DRL-, -VO-, -DF-, LOBBY, VS1-, VS2-, + 1, 1, 1, 3, 1, 3, 3, 1, 3, 1, 3, 1, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, + // Episode 2 (12-23) + // P2 VRTA, VRTB, VRSA, VRSB, CCA-, -JN-, -JS-, MNTN, SEAS, SBU-, SBL-, -GG-, -OF-, -BR-, -GD-, SSN-, TWR-, + 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 3, 1, 3, 1, 3, 2, 2, 1, 3, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + // Episode 4 (24-2E) + // CE -CW-, -CS-, -CN-, -CI-, DES1, DES2, DES3, SMIL, -P2-, TEST + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1}; + const auto& data = is_solo ? counts_off : counts_on; + if ((floor * 2 + 1) < data.size()) { + auto available = this->num_available_variations_for_floor(episode, floor); + return make_pair(min(available.first, data[area * 2]), min(available.second, data[area * 2 + 1])); + } + throw runtime_error("invalid area"); +} + +string SetDataTable::map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const { + uint8_t area = this->default_area_for_floor(episode, floor); + if (area == 0xFF) { + return ""; + } + + if (area >= this->entries.size()) { + return ""; + } + + const auto& entry = this->entries.at(area).at(var1).at(var2); + string filename = is_enemies ? entry.enemy_list_basename : entry.object_list_basename; + + filename += (is_enemies ? "e" : "o"); + + switch ((floor != 0) ? GameMode::NORMAL : mode) { + case GameMode::NORMAL: + filename += ".dat"; + break; + case GameMode::SOLO: + filename += "_s.dat"; + break; + case GameMode::CHALLENGE: + filename += "_c1.dat"; + break; + case GameMode::BATTLE: + filename += "_d.dat"; + break; + default: + throw logic_error("invalid game mode"); + } + + return filename; +} + +string SetDataTable::str() const { + vector lines; + lines.emplace_back(string_printf("FL/V1/V2 => ----------------------OBJECT -----------------------ENEMY -----------------------EVENT\n")); for (size_t a = 0; a < this->entries.size(); a++) { const auto& v1_v = this->entries[a]; for (size_t v1 = 0; v1 < v1_v.size(); v1++) { const auto& v2_v = v1_v[v1]; for (size_t v2 = 0; v2 < v2_v.size(); v2++) { const auto& e = v2_v[v2]; - fprintf(stream, "[%02zX/%02zX/%02zX] %s %s %s\n", a, v1, v2, e.object_list_basename.c_str(), e.enemy_list_basename.c_str(), e.event_list_basename.c_str()); + lines.emplace_back(string_printf("%02zX/%02zX/%02zX => %28s %28s %28s\n", a, v1, v2, e.object_list_basename.c_str(), e.enemy_list_basename.c_str(), e.event_list_basename.c_str())); } } } + return join(lines, ""); } struct AreaMapFileInfo { @@ -1715,23 +1848,97 @@ struct AreaMapFileInfo { variation2_values(variation2_values) {} }; +const array>, 0x10> SetDataTableDCNTE::NAMES = {{ + /* 00 */ {{"map_city00_00"}}, + /* 01 */ {{"map_forest01_00", "map_forest01_01"}}, + /* 02 */ {{"map_forest02_00", "map_forest02_03"}}, + /* 03 */ {{"map_cave01_00_00", "map_cave01_00_01"}, {"map_cave01_01_00", "map_cave01_01_01"}}, + /* 04 */ {{"map_cave02_00_00", "map_cave02_00_01"}, {"map_cave02_01_00", "map_cave02_01_01"}}, + /* 05 */ {{"map_cave03_00_00", "map_cave03_00_01"}, {"map_cave03_01_00", "map_cave03_01_01"}}, + /* 06 */ {{"map_machine01_00_00", "map_machine01_00_01"}}, + /* 07 */ {{"map_machine02_00_00", "map_machine02_00_01"}}, + /* 08 */ {{"map_ancient01_00_00", "map_ancient01_00_01"}, {"map_ancient01_01_00", "map_ancient01_01_01"}}, + /* 09 */ {{"map_ancient02_00_00", "map_ancient02_00_01"}, {"map_ancient02_01_00", "map_ancient02_01_01"}}, + /* 0A */ {{"map_ancient03_00_00", "map_ancient03_00_01"}, {"map_ancient03_01_00", "map_ancient03_01_01"}}, + /* 0B */ {{"map_boss01"}}, + /* 0C */ {{"map_boss02"}}, + /* 0D */ {{"map_boss03"}}, + /* 0E */ {{"map_boss04"}}, + /* 0F */ {{"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}}, +}}; + +SetDataTableDCNTE::SetDataTableDCNTE() : SetDataTableBase(Version::DC_NTE) {} + +pair SetDataTableDCNTE::num_available_variations_for_floor(Episode, uint8_t floor) const { + return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size()); +} + +pair SetDataTableDCNTE::num_free_roam_variations_for_floor(Episode episode, bool, uint8_t floor) const { + return this->num_available_variations_for_floor(episode, floor); +} + +string SetDataTableDCNTE::map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, bool is_enemies) const { + if (floor >= this->NAMES.size()) { + return ""; + } + return this->NAMES.at(floor).at(var1).at(var2) + (is_enemies ? "e.dat" : "o.dat"); +} + +const array>, 0x10> SetDataTableDC112000::NAMES = {{ + /* 00 */ {{"map_city00_00"}}, + /* 01 */ {{"map_forest01_00", "map_forest01_01", "map_forest01_02", "map_forest01_03", "map_forest01_04"}}, + /* 02 */ {{"map_forest02_00", "map_forest02_01", "map_forest02_02", "map_forest02_03", "map_forest02_04"}}, + /* 03 */ {{"map_cave01_00_00", "map_cave01_00_01"}, {"map_cave01_01_00", "map_cave01_01_01"}, {"map_cave01_02_00", "map_cave01_02_01"}}, + /* 04 */ {{"map_cave02_00_00", "map_cave02_00_01"}, {"map_cave02_01_00", "map_cave02_01_01"}, {"map_cave02_02_00", "map_cave02_02_01"}}, + /* 05 */ {{"map_cave03_00_00", "map_cave03_00_01"}, {"map_cave03_01_00", "map_cave03_01_01"}, {"map_cave03_02_00", "map_cave03_02_01"}}, + /* 06 */ {{"map_machine01_00_00", "map_machine01_00_01"}, {"map_machine01_01_00", "map_machine01_01_01"}, {"map_machine01_02_00", "map_machine01_02_01"}}, + /* 07 */ {{"map_machine02_00_00", "map_machine02_00_01"}, {"map_machine02_01_00", "map_machine02_01_01"}, {"map_machine02_02_00", "map_machine02_02_01"}}, + /* 08 */ {{"map_ancient01_00_00", "map_ancient01_00_01"}, {"map_ancient01_01_00", "map_ancient01_01_01"}, {"map_ancient01_02_00", "map_ancient01_02_01"}}, + /* 09 */ {{"map_ancient02_00_00", "map_ancient02_00_01"}, {"map_ancient02_01_00", "map_ancient02_01_01"}, {"map_ancient02_02_00", "map_ancient02_02_01"}}, + /* 0A */ {{"map_ancient03_00_00", "map_ancient03_00_01"}, {"map_ancient03_01_00", "map_ancient03_01_01"}, {"map_ancient03_02_00", "map_ancient03_02_01"}}, + /* 0B */ {{"map_boss01"}}, + /* 0C */ {{"map_boss02"}}, + /* 0D */ {{"map_boss03"}}, + /* 0E */ {{"map_boss04"}}, + /* 0F */ {{"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}}, +}}; + +SetDataTableDC112000::SetDataTableDC112000() : SetDataTableBase(Version::DC_V1_11_2000_PROTOTYPE) {} + +pair SetDataTableDC112000::num_available_variations_for_floor(Episode, uint8_t floor) const { + return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size()); +} + +pair SetDataTableDC112000::num_free_roam_variations_for_floor(Episode episode, bool, uint8_t floor) const { + return this->num_available_variations_for_floor(episode, floor); +} + +string SetDataTableDC112000::map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, bool is_enemies) const { + if (floor >= this->NAMES.size()) { + return ""; + } + return this->NAMES.at(floor).at(var1).at(var2) + (is_enemies ? "e.dat" : "o.dat"); +} + static const vector map_file_info_dc_nte = { {"city00", {}, {0}}, - {"forest01", {}, {0, 1, 2, 3, 4}}, - {"forest02", {}, {0, 1, 2, 3, 4}}, - {"cave01", {0, 1, 2}, {0, 1}}, - {"cave02", {0, 1, 2}, {0, 1}}, - {"cave03", {0, 1, 2}, {0, 1}}, - {"machine01", {0, 1}, {0, 1}}, - {"machine02", {0, 1}, {0, 1}}, - {"ancient01", {0, 1}, {0, 1}}, - {"ancient02", {0, 1}, {0, 1}}, - {"ancient03", {0, 1}, {0, 1}}, + {"forest01", {}, {0, 1}}, + {"forest02", {}, {0, 3}}, + {"cave01", {0, 1}, {0, 1}}, + {"cave02", {0, 1}, {0, 1}}, + {"cave03", {0, 1}, {0, 1}}, + {"machine01", {0}, {0, 1}}, + {"machine02", {0}, {0, 1}}, + {"ancient01", {0}, {0, 1}}, + {"ancient02", {0}, {0, 1}}, + {"ancient03", {0}, {0, 1}}, {"boss01", {}, {}}, {"boss02", {}, {}}, {"boss03", {}, {}}, {"boss04", {}, {}}, - {nullptr, {}, {}}, + {"map_visuallobby", {}, {}}, }; static const vector> map_file_info_gc_nte = { @@ -1752,13 +1959,13 @@ static const vector> map_file_info_gc_nte = { {"boss02", {}, {}}, {"boss03", {}, {}}, {"boss04", {}, {}}, - {nullptr, {}, {}}, + {"lobby_01", {}, {}}, }, { // Episode 2 Non-solo {"labo00", {}, {0}}, - {"ruins01", {0, 1}, {0}}, - {"ruins02", {0, 1}, {0}}, + {"ruins01", {0}, {0}}, + {"ruins02", {0}, {0}}, {"space01", {0, 1}, {0}}, {"space02", {0, 1}, {0}}, {"jungle01", {}, {0, 1}}, @@ -1766,8 +1973,8 @@ static const vector> map_file_info_gc_nte = { {"jungle03", {}, {0, 1}}, {"jungle04", {0, 1}, {0}}, {"jungle05", {}, {0, 1}}, - {nullptr, {}, {}}, - {nullptr, {}, {}}, + {"seabed01", {0, 1}, {0}}, + {"seabed02", {0}, {0}}, {"boss05", {}, {}}, {"boss06", {}, {}}, {"boss07", {}, {}}, @@ -1796,7 +2003,7 @@ static const vector>> map_file_info = { {"boss02", {}, {}}, {"boss03", {}, {}}, {"boss04", {}, {}}, - {nullptr, {}, {}}, + {"lobby_01", {}, {}}, }, { // Solo @@ -1873,7 +2080,7 @@ static const vector>> map_file_info = { {"desert02", {0}, {0, 1, 2}}, {"desert03", {0, 1, 2}, {0}}, {"boss09", {0}, {0}}, - {nullptr, {}, {}}, + {"test01", {0}, {0}}, {nullptr, {}, {}}, {nullptr, {}, {}}, {nullptr, {}, {}}, @@ -1892,7 +2099,7 @@ static const vector>> map_file_info = { {"desert02", {0}, {0, 1, 2}}, {"desert03", {0, 1, 2}, {0}}, {"boss09", {0}, {0}}, - {nullptr, {}, {}}, + {"test01", {0}, {0}}, {nullptr, {}, {}}, {nullptr, {}, {}}, {nullptr, {}, {}}, @@ -1902,7 +2109,7 @@ static const vector>> map_file_info = { }, }; -const AreaMapFileInfo& file_info_for_variation( +const AreaMapFileInfo& file_info_for_variation_deprecated( Version version, Episode episode, uint8_t area, bool is_solo) { const vector* multi_index = nullptr; const vector* solo_index = nullptr; @@ -1919,8 +2126,7 @@ const AreaMapFileInfo& file_info_for_variation( default: throw invalid_argument("episode has no maps"); } - } - else { + } else { switch (episode) { case Episode::EP1: multi_index = &map_file_info.at(0).at(0); @@ -1952,14 +2158,14 @@ const AreaMapFileInfo& file_info_for_variation( return multi_index->at(area); } -void generate_variations( +void generate_variations_deprecated( parray& variations, shared_ptr random_crypt, Version version, Episode episode, bool is_solo) { for (size_t z = 0; z < 0x10; z++) { - const auto& a = file_info_for_variation(version, episode, z, is_solo); + const auto& a = file_info_for_variation_deprecated(version, episode, z, is_solo); if (!a.name_token) { variations[z * 2 + 0] = 0; variations[z * 2 + 1] = 0; @@ -1970,10 +2176,10 @@ void generate_variations( } } -vector> generate_all_possible_variations(Version version, Episode episode, bool is_solo) { - parray maxes; +parray variation_maxes_deprecated(Version version, Episode episode, bool is_solo) { + parray maxes; for (size_t z = 0; z < 0x10; z++) { - const auto& a = file_info_for_variation(version, episode, z, is_solo); + const auto& a = file_info_for_variation_deprecated(version, episode, z, is_solo); if (!a.name_token) { maxes[z * 2 + 0] = 0; maxes[z * 2 + 1] = 0; @@ -1982,38 +2188,32 @@ vector> generate_all_possible_variations(Version versi maxes[z * 2 + 1] = (a.variation2_values.size() <= 1) ? 0 : (a.variation2_values.size() - 1); } } - - vector> ret; - parray current; - for (;;) { - ret.emplace_back(current); - - // Increment current by 1 as if it were an 0x20-place integer, with each - // "place" having a base of maxes[x] + 1 - ssize_t x; - for (x = 0x1F; x >= 0; x--) { - if (current[x] < maxes[x]) { - current[x]++; - break; - } else { - current[x] = 0; - } - } - if (x < 0) { - break; - } - } - - return ret; + return maxes; } -vector map_filenames_for_variation( - Version version, - Episode episode, - GameMode mode, +bool next_variation_deprecated(parray& variations, Version version, Episode episode, bool is_solo) { + auto maxes = variation_maxes_deprecated(version, episode, is_solo); + + // Increment variations by 1 as if it were an 0x20-place integer, with each + // "place" having a base of maxes[x] + 1 + for (ssize_t x = 0x1F; x >= 0; x--) { + if (variations[x] < maxes[x]) { + variations[x]++; + return true; + } else { + variations[x] = 0; + } + } + return false; +} + +vector map_filenames_for_variation_deprecated( uint8_t floor, uint32_t var1, uint32_t var2, + Version version, + Episode episode, + GameMode mode, bool is_enemies) { // Map filenames are like map___(_off)?(e|o)(_s|_c1)?.dat // name_token comes from AreaMapFileInfo @@ -2024,7 +2224,7 @@ vector map_filenames_for_variation( // _c1 is used for the city map in Challenge mode (which we don't load, // since it contains only NPCs and not enemies) // e|o specifies what kind of data: e = enemies, o = objects - const auto& a = file_info_for_variation(version, episode, floor, mode == GameMode::SOLO); + const auto& a = file_info_for_variation_deprecated(version, episode, floor, mode == GameMode::SOLO); if (!a.name_token) { return vector(); } @@ -2067,5 +2267,18 @@ vector map_filenames_for_variation( return ret; } +vector> map_filenames_for_variations_deprecated( + const parray& variations, + Version version, + Episode episode, + GameMode mode, + bool is_enemies) { + vector> ret; + for (size_t z = 0; z < 0x10; z++) { + ret.emplace_back(map_filenames_for_variation_deprecated(z, variations[z * 2], variations[z * 2 + 1], version, episode, mode, is_enemies)); + } + return ret; +} + const shared_ptr Map::NO_RARE_ENEMIES = make_shared(0, 0); const shared_ptr Map::DEFAULT_RARE_ENEMIES = make_shared(0x0083126E, 0x1999999A); diff --git a/src/Map.hh b/src/Map.hh index 18db5475..a6088507 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -321,11 +321,28 @@ struct Map { std::vector rare_enemy_indexes; }; -// TODO: This class is currently unused. It would be nice if we could use this -// to generate variations and link to the corresponding map filenames, but it -// seems that SetDataTable.rel files link to map filenames that don't actually -// exist in some cases, so we can't just directly use this data structure. -class SetDataTable { +class SetDataTableBase { +public: + virtual ~SetDataTableBase() = default; + + parray generate_variations(Episode episode, bool is_solo, std::shared_ptr random_crypt) const; + virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const = 0; + virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const = 0; + + virtual std::string map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const = 0; + std::vector map_filenames_for_variations( + const parray& variations, Episode episode, GameMode mode, bool is_enemies) const; + + uint8_t default_area_for_floor(Episode episode, uint8_t floor) const; + +protected: + explicit SetDataTableBase(Version version); + + Version version; +}; + +class SetDataTable : public SetDataTableBase { public: struct SetEntry { std::string object_list_basename; @@ -333,31 +350,68 @@ public: std::string event_list_basename; }; - SetDataTable(std::shared_ptr data, bool big_endian); + SetDataTable(Version version, const std::string& data); + virtual ~SetDataTable() = default; - inline const std::vector>> get() const { - return this->entries; - } + virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; + virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; + virtual std::string map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; - void print(FILE* stream) const; + std::string str() const; private: template - void load_table_t(std::shared_ptr data); + void load_table_t(const std::string& data); // Indexes are [floor][variation1][variation2] // floor is cumulative per episode, so Ep2 starts at floor=18. std::vector>> entries; }; -void generate_variations( +class SetDataTableDCNTE : public SetDataTableBase { +public: + SetDataTableDCNTE(); + virtual ~SetDataTableDCNTE() = default; + + virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; + virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; + virtual std::string map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; + +private: + static const std::array>, 0x10> NAMES; +}; + +class SetDataTableDC112000 : public SetDataTableBase { +public: + SetDataTableDC112000(); + virtual ~SetDataTableDC112000() = default; + + virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; + virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; + virtual std::string map_filename_for_variation( + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; + +private: + static const std::array>, 0x10> NAMES; +}; + +void generate_variations_deprecated( parray& variations, std::shared_ptr random, Version version, Episode episode, bool is_solo); -std::vector> generate_all_possible_variations( - Version version, Episode episode, bool is_solo); -std::vector map_filenames_for_variation( - Version version, Episode episode, GameMode mode, uint8_t floor, uint32_t var1, uint32_t var2, bool is_enemies); +parray variation_maxes_deprecated(Version version, Episode episode, bool is_solo); +bool next_variation_deprecated(parray& variations, Version version, Episode episode, bool is_solo); + +std::vector map_filenames_for_variation_deprecated( + uint8_t floor, uint32_t var1, uint32_t var2, Version version, Episode episode, GameMode mode, bool is_enemies); +std::vector> map_filenames_for_variations_deprecated( + const parray& variations, + Version version, + Episode episode, + GameMode mode, + bool is_enemies); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 2616d24d..b2664df3 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4200,8 +4200,19 @@ shared_ptr create_game_generic( game->rare_enemy_rates = s->rare_enemy_rates_by_difficulty.at(game->difficulty); } - generate_variations(game->variations, game->random_crypt, game->base_version, game->episode, is_solo); - game->load_maps(); + if (game->episode != Episode::EP3) { + // GC NTE ignores the passed-in variations and always uses all zeroes + if (game->base_version != Version::GC_NTE) { + auto sdt = s->set_data_table(game->base_version, game->episode, game->mode, game->difficulty); + game->variations = sdt->generate_variations(game->episode, is_solo, game->random_crypt); + } else { + game->variations.clear(0); + } + game->load_maps(); + } else { + game->variations.clear(0); + game->map = make_shared(game->base_version, game->lobby_id, game->random_crypt); + } return game; } diff --git a/src/ServerState.cc b/src/ServerState.cc index 16339f34..2e000cb4 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -31,8 +31,8 @@ ServerState::QuestF960Result::QuestF960Result(const JSON& json, std::shared_ptr< } ServerState::ServerState(const string& config_filename) - : creation_time(now()), - config_filename(config_filename) { + : creation_time(now()), + config_filename(config_filename) { this->create_load_step_graph(); } @@ -371,6 +371,21 @@ void ServerState::dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx) { reinterpret_cast(ctx)->lobbies_to_destroy.clear(); } +shared_ptr ServerState::set_data_table( + Version version, Episode episode, GameMode mode, uint8_t difficulty) const { + bool use_ult_tables = ((episode == Episode::EP1) && (difficulty == 3) && !is_v1(version) && (version != Version::PC_NTE)); + if (mode == GameMode::SOLO && is_v4(version)) { + return use_ult_tables ? this->bb_solo_set_data_table_ep1_ult : this->bb_solo_set_data_table; + } + + const auto& tables = use_ult_tables ? this->set_data_tables_ep1_ult : this->set_data_tables; + auto ret = tables.at(static_cast(version)); + if (ret == nullptr) { + throw runtime_error("no set data table exists for this version"); + } + return ret; +} + shared_ptr ServerState::item_parameter_table(Version version) const { auto ret = this->item_parameter_tables.at(static_cast(version)); if (ret == nullptr) { @@ -450,11 +465,8 @@ shared_ptr ServerState::load_bb_file( // First, look in the patch tree's data directory string patch_index_path = "./data/" + patch_index_filename; try { - auto ret = this->bb_patch_file_index->get(patch_index_path)->load_data(); - static_game_data_log.info("Loaded %s from file in BB patch tree", patch_index_path.c_str()); - return ret; + return this->bb_patch_file_index->get(patch_index_path)->load_data(); } catch (const out_of_range&) { - static_game_data_log.info("%s missing from BB patch tree", patch_index_path.c_str()); } } @@ -464,11 +476,8 @@ shared_ptr ServerState::load_bb_file( try { // TODO: It's kinda not great that we copy the data here; find a way to // avoid doing this (also in the below case) - auto ret = make_shared(this->bb_data_gsl->get_copy(effective_gsl_filename)); - static_game_data_log.info("Loaded %s from data.gsl in BB patch tree", effective_gsl_filename.c_str()); - return ret; + return make_shared(this->bb_data_gsl->get_copy(effective_gsl_filename)); } catch (const out_of_range&) { - static_game_data_log.info("%s missing from data.gsl in BB patch tree", effective_gsl_filename.c_str()); } // Third, look in data.gsl without the filename extension @@ -476,11 +485,8 @@ shared_ptr ServerState::load_bb_file( if (dot_offset != string::npos) { string no_ext_gsl_filename = effective_gsl_filename.substr(0, dot_offset); try { - auto ret = make_shared(this->bb_data_gsl->get_copy(no_ext_gsl_filename)); - static_game_data_log.info("Loaded %s from data.gsl in BB patch tree", no_ext_gsl_filename.c_str()); - return ret; + return make_shared(this->bb_data_gsl->get_copy(no_ext_gsl_filename)); } catch (const out_of_range&) { - static_game_data_log.info("%s missing from data.gsl in BB patch tree", no_ext_gsl_filename.c_str()); } } } @@ -490,11 +496,8 @@ shared_ptr ServerState::load_bb_file( static FileContentsCache cache(10 * 60 * 1000 * 1000); // 10 minutes try { auto ret = cache.get_or_load("system/blueburst/" + effective_bb_directory_filename); - static_game_data_log.info("Loaded %s", effective_bb_directory_filename.c_str()); return ret.file->data; } catch (const exception& e) { - static_game_data_log.info("%s missing from system/blueburst", effective_bb_directory_filename.c_str()); - static_game_data_log.error("%s not found in any source", patch_index_filename.c_str()); throw cannot_open_file(patch_index_filename); } } @@ -509,25 +512,20 @@ shared_ptr ServerState::load_map_file_uncached(Version version, co try { return this->load_bb_file(filename); } catch (const exception& e) { - static_game_data_log.info("Failed to load %s from BB patch tree: %s", filename.c_str(), e.what()); } } else if (version == Version::PC_V2) { try { string path = "system/patch-pc/Media/PSO/" + filename; auto ret = make_shared(load_file(path)); - static_game_data_log.info("Loaded %s from PC patch tree", filename.c_str()); return ret; } catch (const exception& e) { - static_game_data_log.info("Failed to load %s from PC patch tree: %s", filename.c_str(), e.what()); } } try { string path = string_printf("system/maps/%s/%s", file_path_token_for_version(version), filename.c_str()); auto ret = make_shared(load_file(path)); - static_game_data_log.info("Loaded %s from default maps", filename.c_str()); return ret; } catch (const exception& e) { - static_game_data_log.info("Failed to load %s from default maps: %s", filename.c_str(), e.what()); } return nullptr; } @@ -1129,6 +1127,36 @@ void ServerState::clear_map_file_caches() { } } +void ServerState::load_set_data_tables() { + config_log.info("Loading set data tables"); + std::array, NUM_VERSIONS> set_data_tables; + + auto load_table = [this](Version version) -> void { + auto data = this->load_map_file(version, "SetDataTableOn.rel"); + this->set_data_tables[static_cast(version)] = make_shared(version, *data); + if (!is_v1(version) && (version != Version::PC_NTE)) { + auto data_ep1_ult = this->load_map_file(version, "SetDataTableOnUlti.rel"); + this->set_data_tables_ep1_ult[static_cast(version)] = make_shared(version, *data_ep1_ult); + } + }; + + this->set_data_tables[static_cast(Version::DC_NTE)] = make_shared(); + this->set_data_tables[static_cast(Version::DC_V1_11_2000_PROTOTYPE)] = make_shared(); + load_table(Version::DC_V1); + load_table(Version::DC_V2); + load_table(Version::PC_NTE); + load_table(Version::PC_V2); + load_table(Version::GC_NTE); + load_table(Version::GC_V3); + load_table(Version::XB_V3); + load_table(Version::BB_V4); + + auto bb_solo_data = this->load_map_file(Version::BB_V4, "SetDataTableOff.rel"); + this->bb_solo_set_data_table = make_shared(Version::BB_V4, *bb_solo_data); + auto bb_solo_data_ep1_ult = this->load_map_file(Version::BB_V4, "SetDataTableOffUlti.rel"); + this->bb_solo_set_data_table_ep1_ult = make_shared(Version::BB_V4, *bb_solo_data_ep1_ult); +} + void ServerState::load_battle_params() { config_log.info("Loading battle parameters"); this->battle_params = make_shared( @@ -1505,6 +1533,10 @@ void ServerState::create_load_step_graph() { // Out: lobbies this->load_step_graph.add_step("lobbies", {"all"}, bind(&ServerState::create_default_lobbies, this)); + // In: bb_patch_file_index + // Out: set_data_tables + this->load_step_graph.add_step("set_data_tables", {"all", "patch_indexes"}, bind(&ServerState::load_set_data_tables, this)); + // In: bb_patch_file_index // Out: battle_params this->load_step_graph.add_step("battle_params", {"all", "patch_indexes"}, bind(&ServerState::load_battle_params, this)); diff --git a/src/ServerState.hh b/src/ServerState.hh index de27ebf1..ead8d29c 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -148,6 +148,10 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr text_index; std::array, NUM_VERSIONS> item_name_indexes; std::shared_ptr word_select_table; + std::array, NUM_VERSIONS> set_data_tables; + std::array, NUM_VERSIONS> set_data_tables_ep1_ult; + std::shared_ptr bb_solo_set_data_table; + std::shared_ptr bb_solo_set_data_table_ep1_ult; std::array, 4> rare_enemy_rates_by_difficulty; std::shared_ptr rare_enemy_rates_challenge; std::array, 3> min_levels_v4; // Indexed as [episode][difficulty] @@ -272,6 +276,8 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr proxy_destinations_menu(Version version) const; const std::vector>& proxy_destinations(Version version) const; + std::shared_ptr set_data_table(Version version, Episode episode, GameMode mode, uint8_t difficulty) const; + std::shared_ptr item_parameter_table(Version version) const; std::shared_ptr item_parameter_table_for_encode(Version version) const; void set_item_parameter_table(Version version, std::shared_ptr table); @@ -314,6 +320,7 @@ struct ServerState : public std::enable_shared_from_this { void load_item_name_indexes(); void load_drop_tables(); void load_item_definitions(); + void load_set_data_tables(); void load_word_select_table(); void load_ep3_data(); void load_quest_index(); diff --git a/system/maps/bb-v4/SetDataTableOff.rel b/system/maps/bb-v4/SetDataTableOff.rel new file mode 100644 index 00000000..0705d70a Binary files /dev/null and b/system/maps/bb-v4/SetDataTableOff.rel differ diff --git a/system/maps/bb-v4/SetDataTableOffUlti.rel b/system/maps/bb-v4/SetDataTableOffUlti.rel new file mode 100755 index 00000000..cb9f9e5d Binary files /dev/null and b/system/maps/bb-v4/SetDataTableOffUlti.rel differ diff --git a/system/maps/bb-v4/SetDataTableOn.rel b/system/maps/bb-v4/SetDataTableOn.rel new file mode 100644 index 00000000..7edf6c7a Binary files /dev/null and b/system/maps/bb-v4/SetDataTableOn.rel differ diff --git a/system/maps/bb-v4/SetDataTableOnUlti.rel b/system/maps/bb-v4/SetDataTableOnUlti.rel new file mode 100755 index 00000000..7e8118a2 Binary files /dev/null and b/system/maps/bb-v4/SetDataTableOnUlti.rel differ diff --git a/system/maps/bb-v4/map_city02_00_00e_c1.dat b/system/maps/bb-v4/map_city02_00_00e_c1.dat new file mode 120000 index 00000000..5580ba73 --- /dev/null +++ b/system/maps/bb-v4/map_city02_00_00e_c1.dat @@ -0,0 +1 @@ +map_city02_00_00e.dat \ No newline at end of file diff --git a/system/maps/bb-v4/map_city02_00_00e_d.dat b/system/maps/bb-v4/map_city02_00_00e_d.dat new file mode 120000 index 00000000..5580ba73 --- /dev/null +++ b/system/maps/bb-v4/map_city02_00_00e_d.dat @@ -0,0 +1 @@ +map_city02_00_00e.dat \ No newline at end of file diff --git a/system/maps/bb-v4/map_city02_00_00o_c1.dat b/system/maps/bb-v4/map_city02_00_00o_c1.dat new file mode 120000 index 00000000..39199e78 --- /dev/null +++ b/system/maps/bb-v4/map_city02_00_00o_c1.dat @@ -0,0 +1 @@ +map_city02_00_00o.dat \ No newline at end of file diff --git a/system/maps/bb-v4/map_city02_00_00o_d.dat b/system/maps/bb-v4/map_city02_00_00o_d.dat new file mode 120000 index 00000000..39199e78 --- /dev/null +++ b/system/maps/bb-v4/map_city02_00_00o_d.dat @@ -0,0 +1 @@ +map_city02_00_00o.dat \ No newline at end of file diff --git a/system/maps/bb-v4/map_test01_00_00e.dat b/system/maps/bb-v4/map_test01_00_00e.dat new file mode 100644 index 00000000..e69de29b diff --git a/system/maps/bb-v4/map_test01_00_00o.dat b/system/maps/bb-v4/map_test01_00_00o.dat new file mode 100644 index 00000000..e69de29b diff --git a/system/maps/dc-v1/SetDataTableOff.rel b/system/maps/dc-v1/SetDataTableOff.rel new file mode 100644 index 00000000..e1b7e740 Binary files /dev/null and b/system/maps/dc-v1/SetDataTableOff.rel differ diff --git a/system/maps/dc-v1/SetDataTableOn.rel b/system/maps/dc-v1/SetDataTableOn.rel new file mode 100644 index 00000000..5b33adf9 Binary files /dev/null and b/system/maps/dc-v1/SetDataTableOn.rel differ diff --git a/system/maps/dc-v2/SetDataTableGAMEJAM.rel b/system/maps/dc-v2/SetDataTableGAMEJAM.rel new file mode 100644 index 00000000..e776d522 Binary files /dev/null and b/system/maps/dc-v2/SetDataTableGAMEJAM.rel differ diff --git a/system/maps/dc-v2/SetDataTableOff.rel b/system/maps/dc-v2/SetDataTableOff.rel new file mode 100644 index 00000000..60df6dcf Binary files /dev/null and b/system/maps/dc-v2/SetDataTableOff.rel differ diff --git a/system/maps/dc-v2/SetDataTableOffUlti.rel b/system/maps/dc-v2/SetDataTableOffUlti.rel new file mode 100644 index 00000000..6000d05e Binary files /dev/null and b/system/maps/dc-v2/SetDataTableOffUlti.rel differ diff --git a/system/maps/dc-v2/SetDataTableOn.rel b/system/maps/dc-v2/SetDataTableOn.rel new file mode 100644 index 00000000..460e3a4c Binary files /dev/null and b/system/maps/dc-v2/SetDataTableOn.rel differ diff --git a/system/maps/dc-v2/SetDataTableOnUlti.rel b/system/maps/dc-v2/SetDataTableOnUlti.rel new file mode 100644 index 00000000..71c623e5 Binary files /dev/null and b/system/maps/dc-v2/SetDataTableOnUlti.rel differ diff --git a/system/maps/gc-nte/SetDataTableGAMEJAM.rel b/system/maps/gc-nte/SetDataTableGAMEJAM.rel new file mode 100755 index 00000000..46d5baab Binary files /dev/null and b/system/maps/gc-nte/SetDataTableGAMEJAM.rel differ diff --git a/system/maps/gc-nte/SetDataTableOff.rel b/system/maps/gc-nte/SetDataTableOff.rel new file mode 100755 index 00000000..af661cec Binary files /dev/null and b/system/maps/gc-nte/SetDataTableOff.rel differ diff --git a/system/maps/gc-nte/SetDataTableOffUlti.rel b/system/maps/gc-nte/SetDataTableOffUlti.rel new file mode 100755 index 00000000..431bd30e Binary files /dev/null and b/system/maps/gc-nte/SetDataTableOffUlti.rel differ diff --git a/system/maps/gc-nte/SetDataTableOn.rel b/system/maps/gc-nte/SetDataTableOn.rel new file mode 100755 index 00000000..3e2f931e Binary files /dev/null and b/system/maps/gc-nte/SetDataTableOn.rel differ diff --git a/system/maps/gc-nte/SetDataTableOnUlti.rel b/system/maps/gc-nte/SetDataTableOnUlti.rel new file mode 100755 index 00000000..8cfd23d9 Binary files /dev/null and b/system/maps/gc-nte/SetDataTableOnUlti.rel differ diff --git a/system/maps/gc-nte/SetDataTable_TGS2001.rel b/system/maps/gc-nte/SetDataTable_TGS2001.rel new file mode 100755 index 00000000..2945b416 Binary files /dev/null and b/system/maps/gc-nte/SetDataTable_TGS2001.rel differ diff --git a/system/maps/gc-nte/map_labo00_00e_c1.dat b/system/maps/gc-nte/map_labo00_00e_c1.dat new file mode 120000 index 00000000..ad77e260 --- /dev/null +++ b/system/maps/gc-nte/map_labo00_00e_c1.dat @@ -0,0 +1 @@ +map_labo00_00e.dat \ No newline at end of file diff --git a/system/maps/gc-nte/map_labo00_00e_d.dat b/system/maps/gc-nte/map_labo00_00e_d.dat new file mode 120000 index 00000000..ad77e260 --- /dev/null +++ b/system/maps/gc-nte/map_labo00_00e_d.dat @@ -0,0 +1 @@ +map_labo00_00e.dat \ No newline at end of file diff --git a/system/maps/gc-nte/map_labo00_00o_c1.dat b/system/maps/gc-nte/map_labo00_00o_c1.dat new file mode 120000 index 00000000..95f62883 --- /dev/null +++ b/system/maps/gc-nte/map_labo00_00o_c1.dat @@ -0,0 +1 @@ +map_labo00_00o.dat \ No newline at end of file diff --git a/system/maps/gc-nte/map_labo00_00o_d.dat b/system/maps/gc-nte/map_labo00_00o_d.dat new file mode 120000 index 00000000..95f62883 --- /dev/null +++ b/system/maps/gc-nte/map_labo00_00o_d.dat @@ -0,0 +1 @@ +map_labo00_00o.dat \ No newline at end of file diff --git a/system/maps/gc-nte/map_seabed01_00_00e.dat b/system/maps/gc-nte/map_seabed01_00_00e.dat index 8339418e..24630889 100644 Binary files a/system/maps/gc-nte/map_seabed01_00_00e.dat and b/system/maps/gc-nte/map_seabed01_00_00e.dat differ diff --git a/system/maps/gc-nte/map_seabed01_01_00e.dat b/system/maps/gc-nte/map_seabed01_01_00e.dat index 8339418e..24630889 100644 Binary files a/system/maps/gc-nte/map_seabed01_01_00e.dat and b/system/maps/gc-nte/map_seabed01_01_00e.dat differ diff --git a/system/maps/gc-v3/SetDataTableOff.rel b/system/maps/gc-v3/SetDataTableOff.rel new file mode 100755 index 00000000..e06c7a1f Binary files /dev/null and b/system/maps/gc-v3/SetDataTableOff.rel differ diff --git a/system/maps/gc-v3/SetDataTableOffUlti.rel b/system/maps/gc-v3/SetDataTableOffUlti.rel new file mode 100755 index 00000000..860f47ab Binary files /dev/null and b/system/maps/gc-v3/SetDataTableOffUlti.rel differ diff --git a/system/maps/gc-v3/SetDataTableOn.rel b/system/maps/gc-v3/SetDataTableOn.rel new file mode 100755 index 00000000..79fa03ce Binary files /dev/null and b/system/maps/gc-v3/SetDataTableOn.rel differ diff --git a/system/maps/gc-v3/SetDataTableOnUlti.rel b/system/maps/gc-v3/SetDataTableOnUlti.rel new file mode 100755 index 00000000..35d12bb3 Binary files /dev/null and b/system/maps/gc-v3/SetDataTableOnUlti.rel differ diff --git a/system/maps/gc-v3/SetDataTable_TGS2001.rel b/system/maps/gc-v3/SetDataTable_TGS2001.rel new file mode 100755 index 00000000..41e1fa4a Binary files /dev/null and b/system/maps/gc-v3/SetDataTable_TGS2001.rel differ diff --git a/system/maps/gc-v3/map_lobby_01o.dat b/system/maps/gc-v3/map_lobby_01o.dat new file mode 100755 index 00000000..ad683600 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_01o.dat differ diff --git a/system/maps/gc-v3/map_lobby_02o.dat b/system/maps/gc-v3/map_lobby_02o.dat new file mode 100755 index 00000000..0b4a0402 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_02o.dat differ diff --git a/system/maps/gc-v3/map_lobby_03o.dat b/system/maps/gc-v3/map_lobby_03o.dat new file mode 100755 index 00000000..a4dd5b4c Binary files /dev/null and b/system/maps/gc-v3/map_lobby_03o.dat differ diff --git a/system/maps/gc-v3/map_lobby_04o.dat b/system/maps/gc-v3/map_lobby_04o.dat new file mode 100755 index 00000000..14a3bc46 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_04o.dat differ diff --git a/system/maps/gc-v3/map_lobby_05o.dat b/system/maps/gc-v3/map_lobby_05o.dat new file mode 100755 index 00000000..91a03929 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_05o.dat differ diff --git a/system/maps/gc-v3/map_lobby_06o.dat b/system/maps/gc-v3/map_lobby_06o.dat new file mode 100755 index 00000000..19663f36 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_06o.dat differ diff --git a/system/maps/gc-v3/map_lobby_07o.dat b/system/maps/gc-v3/map_lobby_07o.dat new file mode 100755 index 00000000..325bffc9 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_07o.dat differ diff --git a/system/maps/gc-v3/map_lobby_08o.dat b/system/maps/gc-v3/map_lobby_08o.dat new file mode 100755 index 00000000..53277e6c Binary files /dev/null and b/system/maps/gc-v3/map_lobby_08o.dat differ diff --git a/system/maps/gc-v3/map_lobby_09o.dat b/system/maps/gc-v3/map_lobby_09o.dat new file mode 100755 index 00000000..3bbd16cd Binary files /dev/null and b/system/maps/gc-v3/map_lobby_09o.dat differ diff --git a/system/maps/gc-v3/map_lobby_10o.dat b/system/maps/gc-v3/map_lobby_10o.dat new file mode 100755 index 00000000..1d21d43b Binary files /dev/null and b/system/maps/gc-v3/map_lobby_10o.dat differ diff --git a/system/maps/gc-v3/map_lobby_green_be00o.dat b/system/maps/gc-v3/map_lobby_green_be00o.dat new file mode 100755 index 00000000..1b9a66e0 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_green_be00o.dat differ diff --git a/system/maps/gc-v3/map_lobby_red_be00o.dat b/system/maps/gc-v3/map_lobby_red_be00o.dat new file mode 100755 index 00000000..1b9a66e0 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_red_be00o.dat differ diff --git a/system/maps/gc-v3/map_lobby_soccer01o.dat b/system/maps/gc-v3/map_lobby_soccer01o.dat new file mode 100755 index 00000000..9d6f55f2 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_soccer01o.dat differ diff --git a/system/maps/gc-v3/map_lobby_soccer02o.dat b/system/maps/gc-v3/map_lobby_soccer02o.dat new file mode 100755 index 00000000..9d6f55f2 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_soccer02o.dat differ diff --git a/system/maps/gc-v3/map_lobby_yellow_be00o.dat b/system/maps/gc-v3/map_lobby_yellow_be00o.dat new file mode 100755 index 00000000..1b9a66e0 Binary files /dev/null and b/system/maps/gc-v3/map_lobby_yellow_be00o.dat differ diff --git a/system/maps/pc-nte/SetDataTableOff.rel b/system/maps/pc-nte/SetDataTableOff.rel new file mode 100644 index 00000000..60df6dcf Binary files /dev/null and b/system/maps/pc-nte/SetDataTableOff.rel differ diff --git a/system/maps/pc-nte/SetDataTableOn.rel b/system/maps/pc-nte/SetDataTableOn.rel new file mode 100644 index 00000000..460e3a4c Binary files /dev/null and b/system/maps/pc-nte/SetDataTableOn.rel differ diff --git a/system/maps/pc-v2/SetDataTableGAMEJAM.rel b/system/maps/pc-v2/SetDataTableGAMEJAM.rel new file mode 100644 index 00000000..e776d522 Binary files /dev/null and b/system/maps/pc-v2/SetDataTableGAMEJAM.rel differ diff --git a/system/maps/pc-v2/SetDataTableOff.rel b/system/maps/pc-v2/SetDataTableOff.rel new file mode 100644 index 00000000..60df6dcf Binary files /dev/null and b/system/maps/pc-v2/SetDataTableOff.rel differ diff --git a/system/maps/pc-v2/SetDataTableOffUlti.rel b/system/maps/pc-v2/SetDataTableOffUlti.rel new file mode 100644 index 00000000..6000d05e Binary files /dev/null and b/system/maps/pc-v2/SetDataTableOffUlti.rel differ diff --git a/system/maps/pc-v2/SetDataTableOn.rel b/system/maps/pc-v2/SetDataTableOn.rel new file mode 100644 index 00000000..460e3a4c Binary files /dev/null and b/system/maps/pc-v2/SetDataTableOn.rel differ diff --git a/system/maps/pc-v2/SetDataTableOnUlti.rel b/system/maps/pc-v2/SetDataTableOnUlti.rel new file mode 100644 index 00000000..71c623e5 Binary files /dev/null and b/system/maps/pc-v2/SetDataTableOnUlti.rel differ diff --git a/system/maps/xb-v3/SetDataTableOn.rel b/system/maps/xb-v3/SetDataTableOn.rel new file mode 100644 index 00000000..f72a54a1 Binary files /dev/null and b/system/maps/xb-v3/SetDataTableOn.rel differ diff --git a/system/maps/xb-v3/SetDataTableOnUlti.rel b/system/maps/xb-v3/SetDataTableOnUlti.rel new file mode 100644 index 00000000..7e8118a2 Binary files /dev/null and b/system/maps/xb-v3/SetDataTableOnUlti.rel differ diff --git a/system/maps/xb-v3/map_lobby_01o.dat b/system/maps/xb-v3/map_lobby_01o.dat new file mode 100644 index 00000000..ad683600 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_01o.dat differ diff --git a/system/maps/xb-v3/map_lobby_02o.dat b/system/maps/xb-v3/map_lobby_02o.dat new file mode 100644 index 00000000..0b4a0402 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_02o.dat differ diff --git a/system/maps/xb-v3/map_lobby_03o.dat b/system/maps/xb-v3/map_lobby_03o.dat new file mode 100644 index 00000000..a4dd5b4c Binary files /dev/null and b/system/maps/xb-v3/map_lobby_03o.dat differ diff --git a/system/maps/xb-v3/map_lobby_04o.dat b/system/maps/xb-v3/map_lobby_04o.dat new file mode 100644 index 00000000..14a3bc46 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_04o.dat differ diff --git a/system/maps/xb-v3/map_lobby_05o.dat b/system/maps/xb-v3/map_lobby_05o.dat new file mode 100644 index 00000000..91a03929 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_05o.dat differ diff --git a/system/maps/xb-v3/map_lobby_06o.dat b/system/maps/xb-v3/map_lobby_06o.dat new file mode 100644 index 00000000..19663f36 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_06o.dat differ diff --git a/system/maps/xb-v3/map_lobby_07o.dat b/system/maps/xb-v3/map_lobby_07o.dat new file mode 100644 index 00000000..325bffc9 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_07o.dat differ diff --git a/system/maps/xb-v3/map_lobby_08o.dat b/system/maps/xb-v3/map_lobby_08o.dat new file mode 100644 index 00000000..53277e6c Binary files /dev/null and b/system/maps/xb-v3/map_lobby_08o.dat differ diff --git a/system/maps/xb-v3/map_lobby_09o.dat b/system/maps/xb-v3/map_lobby_09o.dat new file mode 100644 index 00000000..3bbd16cd Binary files /dev/null and b/system/maps/xb-v3/map_lobby_09o.dat differ diff --git a/system/maps/xb-v3/map_lobby_10o.dat b/system/maps/xb-v3/map_lobby_10o.dat new file mode 100644 index 00000000..1d21d43b Binary files /dev/null and b/system/maps/xb-v3/map_lobby_10o.dat differ diff --git a/system/maps/xb-v3/map_lobby_green_be00o.dat b/system/maps/xb-v3/map_lobby_green_be00o.dat new file mode 100644 index 00000000..80ca3051 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_green_be00o.dat differ diff --git a/system/maps/xb-v3/map_lobby_red_be00o.dat b/system/maps/xb-v3/map_lobby_red_be00o.dat new file mode 100644 index 00000000..80ca3051 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_red_be00o.dat differ diff --git a/system/maps/xb-v3/map_lobby_soccer01o.dat b/system/maps/xb-v3/map_lobby_soccer01o.dat new file mode 100644 index 00000000..9d6f55f2 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_soccer01o.dat differ diff --git a/system/maps/xb-v3/map_lobby_soccer02o.dat b/system/maps/xb-v3/map_lobby_soccer02o.dat new file mode 100644 index 00000000..9d6f55f2 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_soccer02o.dat differ diff --git a/system/maps/xb-v3/map_lobby_yellow_be00o.dat b/system/maps/xb-v3/map_lobby_yellow_be00o.dat new file mode 100644 index 00000000..80ca3051 Binary files /dev/null and b/system/maps/xb-v3/map_lobby_yellow_be00o.dat differ