use SetDataTable files as map indexes

This commit is contained in:
Martin Michelsen
2024-01-20 15:30:24 -08:00
parent fe1d5a874a
commit e13b5950ca
82 changed files with 689 additions and 159 deletions
+1 -8
View File
@@ -79,20 +79,13 @@ shared_ptr<const string> ThreadSafeFileCache::get(
const string& name, std::function<shared_ptr<const string>(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;
}
}
+54 -48
View File
@@ -291,10 +291,30 @@ shared_ptr<Map> Lobby::load_maps(
uint8_t difficulty,
uint8_t event,
uint32_t lobby_id,
shared_ptr<const SetDataTableBase> sdt,
function<shared_ptr<const string>(Version, const string&)> get_file_data,
shared_ptr<const Map::RareEnemyRates> rare_rates,
shared_ptr<PSOLFGEncryption> random_crypt,
const parray<le_uint32_t, 0x20>& variations) {
const parray<le_uint32_t, 0x20>& 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<Map> Lobby::load_maps(
const vector<string>& enemy_filenames,
const vector<string>& object_filenames,
Version version,
Episode episode,
GameMode mode,
uint8_t difficulty,
uint8_t event,
uint32_t lobby_id,
function<shared_ptr<const string>(Version, const string&)> get_file_data,
shared_ptr<const Map::RareEnemyRates> rare_rates,
shared_ptr<PSOLFGEncryption> random_crypt,
const PrefixedLogger* log) {
auto map = make_shared<Map>(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<Map> 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<Map>(this->base_version, this->lobby_id, this->random_crypt);
+16 -1
View File
@@ -211,10 +211,25 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
uint8_t difficulty,
uint8_t event,
uint32_t lobby_id,
std::shared_ptr<const SetDataTableBase> sdt,
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
std::shared_ptr<PSOLFGEncryption> random_crypt,
const parray<le_uint32_t, 0x20>& variations);
const parray<le_uint32_t, 0x20>& variations,
const PrefixedLogger* log = nullptr);
static std::shared_ptr<Map> load_maps(
const std::vector<std::string>& enemy_filenames,
const std::vector<std::string>& object_filenames,
Version version,
Episode episode,
GameMode mode,
uint8_t difficulty,
uint8_t event,
uint32_t lobby_id,
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_file_data,
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
std::shared_ptr<PSOLFGEncryption> random_crypt,
const PrefixedLogger* log = nullptr);
void load_maps();
void create_ep3_server();
+192 -1
View File
@@ -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<const string> {
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<string>& 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<Episode> 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<GameMode> 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,
+274 -61
View File
@@ -1637,8 +1637,59 @@ string Map::disassemble_quest_data(const void* data, size_t size) {
return join(ret, "\n") + "\n";
}
SetDataTable::SetDataTable(shared_ptr<const string> data, bool big_endian) {
if (big_endian) {
SetDataTableBase::SetDataTableBase(Version version) : version(version) {}
parray<le_uint32_t, 0x20> SetDataTableBase::generate_variations(
Episode episode, bool is_solo, std::shared_ptr<PSOLFGEncryption> random_crypt) const {
parray<le_uint32_t, 0x20> 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<string> SetDataTableBase::map_filenames_for_variations(
const parray<le_uint32_t, 0x20>& variations, Episode episode, GameMode mode, bool is_enemies) const {
vector<string> 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<uint8_t, 0x12> areas_ep1 = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11};
static const std::array<uint8_t, 0x12> 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<uint8_t, 0x12> areas_ep2 = {
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23};
static const std::array<uint8_t, 0x12> 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<true>(data);
} else {
this->load_table_t<false>(data);
@@ -1646,10 +1697,10 @@ SetDataTable::SetDataTable(shared_ptr<const string> data, bool big_endian) {
}
template <bool IsBigEndian>
void SetDataTable::load_table_t(shared_ptr<const string> data) {
void SetDataTable::load_table_t(const string& data) {
using U32T = typename conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
StringReader r(*data);
StringReader r(data);
struct Footer {
U32T table3_offset;
@@ -1688,17 +1739,99 @@ void SetDataTable::load_table_t(shared_ptr<const string> data) {
}
}
void SetDataTable::print(FILE* stream) const {
pair<uint32_t, uint32_t> 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<uint32_t, uint32_t> 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<uint32_t, 0x2F * 2> 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<uint32_t, 0x2F * 2> 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<uint32_t>(available.first, data[area * 2]), min<uint32_t>(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<string> 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<vector<vector<string>>, 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<uint32_t, uint32_t> SetDataTableDCNTE::num_available_variations_for_floor(Episode, uint8_t floor) const {
return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size());
}
pair<uint32_t, uint32_t> 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<vector<vector<string>>, 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<uint32_t, uint32_t> SetDataTableDC112000::num_available_variations_for_floor(Episode, uint8_t floor) const {
return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size());
}
pair<uint32_t, uint32_t> 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<AreaMapFileInfo> 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<vector<AreaMapFileInfo>> map_file_info_gc_nte = {
@@ -1752,13 +1959,13 @@ static const vector<vector<AreaMapFileInfo>> 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<vector<AreaMapFileInfo>> 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<vector<vector<AreaMapFileInfo>>> map_file_info = {
{"boss02", {}, {}},
{"boss03", {}, {}},
{"boss04", {}, {}},
{nullptr, {}, {}},
{"lobby_01", {}, {}},
},
{
// Solo
@@ -1873,7 +2080,7 @@ static const vector<vector<vector<AreaMapFileInfo>>> 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<vector<vector<AreaMapFileInfo>>> 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<vector<vector<AreaMapFileInfo>>> 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<AreaMapFileInfo>* multi_index = nullptr;
const vector<AreaMapFileInfo>* 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<le_uint32_t, 0x20>& variations,
shared_ptr<PSOLFGEncryption> 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<parray<le_uint32_t, 0x20>> generate_all_possible_variations(Version version, Episode episode, bool is_solo) {
parray<uint32_t, 0x20> maxes;
parray<le_uint32_t, 0x20> variation_maxes_deprecated(Version version, Episode episode, bool is_solo) {
parray<le_uint32_t, 0x20> 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<parray<le_uint32_t, 0x20>> generate_all_possible_variations(Version versi
maxes[z * 2 + 1] = (a.variation2_values.size() <= 1) ? 0 : (a.variation2_values.size() - 1);
}
}
vector<parray<le_uint32_t, 0x20>> ret;
parray<le_uint32_t, 0x20> 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<string> map_filenames_for_variation(
Version version,
Episode episode,
GameMode mode,
bool next_variation_deprecated(parray<le_uint32_t, 0x20>& 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<string> 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_<name_token>_<VV>_<VV>(_off)?(e|o)(_s|_c1)?.dat
// name_token comes from AreaMapFileInfo
@@ -2024,7 +2224,7 @@ vector<string> 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<string>();
}
@@ -2067,5 +2267,18 @@ vector<string> map_filenames_for_variation(
return ret;
}
vector<vector<string>> map_filenames_for_variations_deprecated(
const parray<le_uint32_t, 0x20>& variations,
Version version,
Episode episode,
GameMode mode,
bool is_enemies) {
vector<vector<string>> 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<const Map::RareEnemyRates> Map::NO_RARE_ENEMIES = make_shared<Map::RareEnemyRates>(0, 0);
const shared_ptr<const Map::RareEnemyRates> Map::DEFAULT_RARE_ENEMIES = make_shared<Map::RareEnemyRates>(0x0083126E, 0x1999999A);
+70 -16
View File
@@ -321,11 +321,28 @@ struct Map {
std::vector<size_t> 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<le_uint32_t, 0x20> generate_variations(Episode episode, bool is_solo, std::shared_ptr<PSOLFGEncryption> random_crypt) const;
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const = 0;
virtual std::pair<uint32_t, uint32_t> 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<std::string> map_filenames_for_variations(
const parray<le_uint32_t, 0x20>& 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<const std::string> data, bool big_endian);
SetDataTable(Version version, const std::string& data);
virtual ~SetDataTable() = default;
inline const std::vector<std::vector<std::vector<SetEntry>>> get() const {
return this->entries;
}
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
virtual std::pair<uint32_t, uint32_t> 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 <bool IsBigEndian>
void load_table_t(std::shared_ptr<const std::string> 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<std::vector<std::vector<SetEntry>>> entries;
};
void generate_variations(
class SetDataTableDCNTE : public SetDataTableBase {
public:
SetDataTableDCNTE();
virtual ~SetDataTableDCNTE() = default;
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
virtual std::pair<uint32_t, uint32_t> 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<std::vector<std::vector<std::string>>, 0x10> NAMES;
};
class SetDataTableDC112000 : public SetDataTableBase {
public:
SetDataTableDC112000();
virtual ~SetDataTableDC112000() = default;
virtual std::pair<uint32_t, uint32_t> num_available_variations_for_floor(Episode episode, uint8_t floor) const;
virtual std::pair<uint32_t, uint32_t> 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<std::vector<std::vector<std::string>>, 0x10> NAMES;
};
void generate_variations_deprecated(
parray<le_uint32_t, 0x20>& variations,
std::shared_ptr<PSOLFGEncryption> random,
Version version,
Episode episode,
bool is_solo);
std::vector<parray<le_uint32_t, 0x20>> generate_all_possible_variations(
Version version, Episode episode, bool is_solo);
std::vector<std::string> map_filenames_for_variation(
Version version, Episode episode, GameMode mode, uint8_t floor, uint32_t var1, uint32_t var2, bool is_enemies);
parray<le_uint32_t, 0x20> variation_maxes_deprecated(Version version, Episode episode, bool is_solo);
bool next_variation_deprecated(parray<le_uint32_t, 0x20>& variations, Version version, Episode episode, bool is_solo);
std::vector<std::string> 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<std::vector<std::string>> map_filenames_for_variations_deprecated(
const parray<le_uint32_t, 0x20>& variations,
Version version,
Episode episode,
GameMode mode,
bool is_enemies);
+13 -2
View File
@@ -4200,8 +4200,19 @@ shared_ptr<Lobby> 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<Map>(game->base_version, game->lobby_id, game->random_crypt);
}
return game;
}
+54 -22
View File
@@ -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<ServerState*>(ctx)->lobbies_to_destroy.clear();
}
shared_ptr<const SetDataTableBase> 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<size_t>(version));
if (ret == nullptr) {
throw runtime_error("no set data table exists for this version");
}
return ret;
}
shared_ptr<const ItemParameterTable> ServerState::item_parameter_table(Version version) const {
auto ret = this->item_parameter_tables.at(static_cast<size_t>(version));
if (ret == nullptr) {
@@ -450,11 +465,8 @@ shared_ptr<const string> 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<const string> 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<string>(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<string>(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<const string> 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<string>(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<string>(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<const string> 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<const string> 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<string>(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<string>(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<std::shared_ptr<const SetDataTableBase>, 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<size_t>(version)] = make_shared<SetDataTable>(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<size_t>(version)] = make_shared<SetDataTable>(version, *data_ep1_ult);
}
};
this->set_data_tables[static_cast<size_t>(Version::DC_NTE)] = make_shared<SetDataTableDCNTE>();
this->set_data_tables[static_cast<size_t>(Version::DC_V1_11_2000_PROTOTYPE)] = make_shared<SetDataTableDC112000>();
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<SetDataTable>(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<SetDataTable>(Version::BB_V4, *bb_solo_data_ep1_ult);
}
void ServerState::load_battle_params() {
config_log.info("Loading battle parameters");
this->battle_params = make_shared<BattleParamsIndex>(
@@ -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));
+7
View File
@@ -148,6 +148,10 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const TextIndex> text_index;
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
std::shared_ptr<const WordSelectTable> word_select_table;
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables;
std::array<std::shared_ptr<const SetDataTableBase>, NUM_VERSIONS> set_data_tables_ep1_ult;
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table;
std::shared_ptr<const SetDataTableBase> bb_solo_set_data_table_ep1_ult;
std::array<std::shared_ptr<const Map::RareEnemyRates>, 4> rare_enemy_rates_by_difficulty;
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge;
std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty]
@@ -272,6 +276,8 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const Menu> proxy_destinations_menu(Version version) const;
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations(Version version) const;
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, uint8_t difficulty) const;
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(Version version) const;
void set_item_parameter_table(Version version, std::shared_ptr<const ItemParameterTable> table);
@@ -314,6 +320,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
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();