diff --git a/src/BattleParamsIndex.cc b/src/BattleParamsIndex.cc new file mode 100644 index 00000000..05020b5c --- /dev/null +++ b/src/BattleParamsIndex.cc @@ -0,0 +1,115 @@ +#include "BattleParamsIndex.hh" + +#include +#include + +#include "Loggers.hh" +#include "PSOEncryption.hh" +#include "StaticGameData.hh" + +using namespace std; + +string BattleParamsIndex::Entry::str() const { + string a1str = format_data_string(this->unknown_a1.data(), this->unknown_a1.bytes()); + return string_printf( + "BattleParamsEntry[ATP=%hu PSV=%hu EVP=%hu HP=%hu DFP=%hu ATA=%hu LCK=%hu ESP=%hu a1=%s EXP=%" PRIu32 " diff=%" PRIu32 "]", + this->atp.load(), + this->psv.load(), + this->evp.load(), + this->hp.load(), + this->dfp.load(), + this->ata.load(), + this->lck.load(), + this->esp.load(), + a1str.c_str(), + this->experience.load(), + this->difficulty.load()); +} + +void BattleParamsIndex::Table::print(FILE* stream) const { + auto print_entry = +[](FILE* stream, const Entry& e) { + string a1str = format_data_string(e.unknown_a1.data(), e.unknown_a1.bytes()); + fprintf(stream, + "%5hu %5hu %5hu %5hu %5hu %5hu %5hu %5hu %s %5" PRIu32 " %5" PRIu32, + e.atp.load(), + e.psv.load(), + e.evp.load(), + e.hp.load(), + e.dfp.load(), + e.ata.load(), + e.lck.load(), + e.esp.load(), + a1str.c_str(), + e.experience.load(), + e.difficulty.load()); + }; + + for (size_t diff = 0; diff < 4; diff++) { + fprintf(stream, "%c ZZ ATP PSV EVP HP DFP ATA LCK ESP A1 EXP DIFF\n", + abbreviation_for_difficulty(diff)); + for (size_t z = 0; z < 0x60; z++) { + fprintf(stream, " %02zX ", z); + print_entry(stream, this->difficulty[diff][z]); + fputc('\n', stream); + } + } +} + +BattleParamsIndex::BattleParamsIndex( + shared_ptr data_on_ep1, + shared_ptr data_on_ep2, + shared_ptr data_on_ep4, + shared_ptr data_off_ep1, + shared_ptr data_off_ep2, + shared_ptr data_off_ep4) { + this->files[0][0].data = data_on_ep1; + this->files[0][1].data = data_on_ep2; + this->files[0][2].data = data_on_ep4; + this->files[1][0].data = data_off_ep1; + this->files[1][1].data = data_off_ep2; + this->files[1][2].data = data_off_ep4; + + for (uint8_t is_solo = 0; is_solo < 2; is_solo++) { + for (uint8_t episode = 0; episode < 3; episode++) { + auto& file = this->files[is_solo][episode]; + if (file.data->size() < sizeof(Table)) { + throw runtime_error(string_printf( + "battle params table size is incorrect (expected %zX bytes, have %zX bytes; is_solo=%hhu, episode=%hhu)", + sizeof(Table), file.data->size(), is_solo, episode)); + } + file.table = reinterpret_cast(file.data->data()); + } + } +} + +const BattleParamsIndex::Entry& BattleParamsIndex::get( + bool solo, Episode episode, uint8_t difficulty, EnemyType type) const { + return this->get(solo, episode, difficulty, battle_param_index_for_enemy_type(episode, type)); +} + +const BattleParamsIndex::Entry& BattleParamsIndex::get( + bool solo, Episode episode, uint8_t difficulty, size_t index) const { + if (difficulty > 4) { + throw invalid_argument("incorrect difficulty"); + } + if (index >= 0x60) { + throw invalid_argument("incorrect monster type"); + } + + uint8_t ep_index; + switch (episode) { + case Episode::EP1: + ep_index = 0; + break; + case Episode::EP2: + ep_index = 1; + break; + case Episode::EP4: + ep_index = 2; + break; + default: + throw invalid_argument("invalid episode"); + } + + return this->files[!!solo][ep_index].table->difficulty[difficulty][index]; +} diff --git a/src/BattleParamsIndex.hh b/src/BattleParamsIndex.hh new file mode 100644 index 00000000..94d25b50 --- /dev/null +++ b/src/BattleParamsIndex.hh @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include "EnemyType.hh" +#include "StaticGameData.hh" +#include "Text.hh" + +class BattleParamsIndex { +public: + struct Entry { + le_uint16_t atp; // attack power + le_uint16_t psv; // perseverance (intelligence?) + le_uint16_t evp; // evasion + le_uint16_t hp; // hit points + le_uint16_t dfp; // defense + le_uint16_t ata; // accuracy + le_uint16_t lck; // luck + le_uint16_t esp; // ??? + parray unknown_a1; + le_uint32_t experience; + le_uint32_t difficulty; + + std::string str() const; + } __attribute__((packed)); + + struct Table { + parray, 4> difficulty; + + void print(FILE* stream) const; + } __attribute__((packed)); + + BattleParamsIndex( + std::shared_ptr data_on_ep1, // BattleParamEntry_on.dat + std::shared_ptr data_on_ep2, // BattleParamEntry_lab_on.dat + std::shared_ptr data_on_ep4, // BattleParamEntry_ep4_on.dat + std::shared_ptr data_off_ep1, // BattleParamEntry.dat + std::shared_ptr data_off_ep2, // BattleParamEntry_lab.dat + std::shared_ptr data_off_ep4); // BattleParamEntry_ep4.dat + + const Entry& get( + bool solo, Episode episode, uint8_t difficulty, EnemyType type) const; + const Entry& get( + bool solo, Episode episode, uint8_t difficulty, size_t entry_index) const; + +private: + struct LoadedFile { + std::shared_ptr data; + const Table* table; + }; + + // online/offline, episode + LoadedFile files[2][3]; +};