Merge upstream master

# Conflicts:
#	README.md
This commit is contained in:
2026-06-11 18:31:23 -04:00
73 changed files with 12419 additions and 1305 deletions
+3 -1
View File
@@ -99,7 +99,7 @@ set(SOURCES
src/LevelTable.cc
src/Lobby.cc
src/Loggers.cc
src/MagEvolutionTable.cc
src/MagMetadataTable.cc
src/Main.cc
src/Map.cc
src/Menu.cc
@@ -126,9 +126,11 @@ set(SOURCES
src/ServerShell.cc
src/ServerState.cc
src/ShellCommands.cc
src/ShopRandomSets.cc
src/SignalWatcher.cc
src/StaticGameData.cc
src/TeamIndex.cc
src/TekkerAdjustmentSet.cc
src/Text.cc
src/TextIndex.cc
src/Version.cc
+4
View File
@@ -1130,3 +1130,7 @@ Rappy size modifier
Disable HP reduction warning sound in Challenge mode
3OE1 => 04076A28 4E800020
Mag invincibility effect sparkliness modifier
(Default 003C; smaller values are more sparkly)
3OE1 => 801131C4 3860XXXX
+192
View File
@@ -5,6 +5,7 @@
#include <future>
#include <phosg/Filesystem.hh>
#include <phosg/Strings.hh>
#include <resource_file/Emulators/PPC32Emulator.hh>
#include <resource_file/Emulators/X86Emulator.hh>
#include <resource_file/ExecutableFormats/DOLFile.hh>
#include <resource_file/ExecutableFormats/PEFile.hh>
@@ -973,6 +974,197 @@ std::vector<DiffEntry> diff_dol_files(const std::string& a_filename, const std::
return ret;
}
void diff_dol_files_semantic(
FILE* stream,
const std::string& a_filename,
const std::string& b_filename,
const std::unordered_set<uint32_t>& a_ignore_functions,
const std::unordered_set<uint32_t>& b_ignore_functions) {
ResourceDASM::DOLFile a(a_filename.c_str());
ResourceDASM::DOLFile b(b_filename.c_str());
// There must be the same number of sections
if (a.sections.size() != b.sections.size()) {
throw std::runtime_error("DOL files do not have the same section count");
}
for (size_t section_index = 0; section_index < a.sections.size(); section_index++) {
const auto& a_sec = a.sections[section_index];
const auto& b_sec = b.sections[section_index];
if (!a_sec.is_text || !b_sec.is_text) {
phosg::fwrite_fmt(stderr, "SECTION {} DATA\n", section_index);
// TODO: Diff the contents as binary data
} else {
phosg::fwrite_fmt(stderr, "SECTION {} TEXT\n", section_index);
struct FileAnalysis {
struct Function {
const ResourceDASM::PPC32Emulator::DisassembleResult::Label* label;
size_t size;
std::vector<std::pair<uint32_t, uint32_t>> code; // [(opcode, mask)]
};
std::vector<Function> functions;
ResourceDASM::PPC32Emulator::DisassembleResult dasm;
};
auto disassemble_section = [&](const ResourceDASM::DOLFile& file, const ResourceDASM::DOLFile::Section& sec) -> FileAnalysis {
std::multimap<uint32_t, std::string> labels;
if ((file.entrypoint >= sec.address) && (file.entrypoint < (sec.address + sec.data.size()))) {
labels.emplace(file.entrypoint, "entry");
}
FileAnalysis ret;
ret.dasm = ResourceDASM::PPC32Emulator::disassemble_structured(
sec.data.data(), sec.data.size(), sec.address, &labels);
FileAnalysis::Function* prev_fn = nullptr;
for (const auto& [addr, label] : ret.dasm.labels) {
if (label.refs.call_addrs.empty() ||
(label.address < sec.address) ||
(label.address >= (sec.address + sec.data.size()))) {
continue;
}
if (prev_fn) {
prev_fn->size = addr - prev_fn->label->address;
}
auto& fn = ret.functions.emplace_back();
fn.label = &label;
prev_fn = &fn;
}
if (prev_fn) {
prev_fn->size = sec.data.size() - (prev_fn->label->address - sec.address);
}
for (auto& fn : ret.functions) {
const be_uint32_t* code = reinterpret_cast<const be_uint32_t*>(
sec.data.data() + (fn.label->address - sec.address));
for (size_t z = 0; z < fn.size >> 2; z++) {
uint32_t opcode = code[z];
if ((opcode & 0xFC000000) == 0x34000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // subic.
} else if ((opcode & 0xFC000000) == 0x38000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // addi
} else if ((opcode & 0xFC1F0000) == 0x3C000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lis
} else if ((opcode & 0xFC000000) == 0x48000000) {
fn.code.emplace_back(opcode, 0xFC000000); // b[la]
} else if ((opcode & 0xF8000000) == 0x60000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // ori
} else if ((opcode & 0xF8000000) == 0x80000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lwz, lwzu
} else if ((opcode & 0xFC000000) == 0x88000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lbz
} else if ((opcode & 0xF8000000) == 0x90000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // stw, stwu
} else if ((opcode & 0xF8000000) == 0x98000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // stb, stbu
} else if ((opcode & 0xF8000000) == 0xA0000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lhz, lhzu
} else if ((opcode & 0xFC000000) == 0xAC000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lhau
} else if ((opcode & 0xF8000000) == 0xB0000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // sth, sthu
} else if ((opcode & 0xF8000000) == 0xC0000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lfs, lfsu
} else if ((opcode & 0xFC000000) == 0xC8000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // lfd
} else if ((opcode & 0xF8000000) == 0xD0000000) {
fn.code.emplace_back(opcode, 0xFFFF0000); // stfs, stfsu
} else {
fn.code.emplace_back(opcode, 0xFFFFFFFF);
}
}
}
return ret;
};
auto a_ana = disassemble_section(a, a_sec);
auto b_ana = disassemble_section(b, b_sec);
bool use_color = isatty(fileno(stream));
auto a_fn_it = a_ana.functions.cbegin();
auto b_fn_it = b_ana.functions.cbegin();
while ((a_fn_it != a_ana.functions.end()) || (b_fn_it != b_ana.functions.end())) {
if ((a_fn_it != a_ana.functions.end()) && (b_fn_it != b_ana.functions.end())) {
phosg::fwrite_fmt(stream, "FUNCTION: A:{:08X} B:{:08X}\n", a_fn_it->label->address, b_fn_it->label->address);
bool functions_identical = true;
for (size_t z = 0; z < std::max<size_t>(a_fn_it->code.size(), b_fn_it->code.size()); z++) {
uint32_t a_op = (z < a_fn_it->code.size()) ? a_fn_it->code[z].first : 0xFFFFFFFF;
uint32_t b_op = (z < b_fn_it->code.size()) ? b_fn_it->code[z].first : 0xFFFFFFFF;
uint32_t a_mask = (z < a_fn_it->code.size()) ? a_fn_it->code[z].second : 0xFFFFFFFF;
uint32_t b_mask = (z < b_fn_it->code.size()) ? b_fn_it->code[z].second : 0xFFFFFFFF;
uint32_t mask = a_mask | b_mask;
if ((a_op & mask) != (b_op & mask)) {
functions_identical = false;
}
}
if (!functions_identical) {
for (size_t z = 0; z < std::max<size_t>(a_fn_it->code.size(), b_fn_it->code.size()); z++) {
uint32_t a_op = (z < a_fn_it->code.size()) ? a_fn_it->code[z].first : 0xFFFFFFFF;
uint32_t b_op = (z < b_fn_it->code.size()) ? b_fn_it->code[z].first : 0xFFFFFFFF;
uint32_t a_mask = (z < a_fn_it->code.size()) ? a_fn_it->code[z].second : 0xFFFFFFFF;
uint32_t b_mask = (z < b_fn_it->code.size()) ? b_fn_it->code[z].second : 0xFFFFFFFF;
uint32_t mask = a_mask | b_mask;
if ((a_op & mask) == (b_op & mask)) {
phosg::fwrite_fmt(stream, " {:08X}->{:08X} {:08X} {}\n",
a_fn_it->label->address + z * 4, b_fn_it->label->address + z * 4,
a_op, ResourceDASM::PPC32Emulator::disassemble_one(a_fn_it->label->address + z * 4, a_op));
} else {
if (use_color) {
phosg::print_color_escape(stream, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::FG_RED, phosg::TerminalFormat::END);
}
phosg::fwrite_fmt(stream, "- {:08X}->{:08X} {:08X} {}\n",
a_fn_it->label->address + z * 4, b_fn_it->label->address + z * 4,
a_op, ResourceDASM::PPC32Emulator::disassemble_one(a_fn_it->label->address + z * 4, a_op));
if (use_color) {
phosg::print_color_escape(stream, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::FG_GREEN, phosg::TerminalFormat::END);
}
phosg::fwrite_fmt(stream, "+ {:08X}->{:08X} {:08X} {}\n",
a_fn_it->label->address + z * 4, b_fn_it->label->address + z * 4,
b_op, ResourceDASM::PPC32Emulator::disassemble_one(b_fn_it->label->address + z * 4, b_op));
if (use_color) {
phosg::print_color_escape(stream, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
}
}
}
}
do {
a_fn_it++;
} while (a_fn_it != a_ana.functions.end() && a_ignore_functions.count(a_fn_it->label->address));
do {
b_fn_it++;
} while (b_fn_it != b_ana.functions.end() && b_ignore_functions.count(b_fn_it->label->address));
} else if (a_fn_it != a_ana.functions.end()) {
phosg::fwrite_fmt(stream, "FUNCTION: A:{:08X} B:(missing)\n", a_fn_it->label->address);
for (size_t z = 0; z < a_fn_it->code.size(); z++) {
phosg::fwrite_fmt(stream, " {:08X} {:08X} {}\n",
a_fn_it->label->address + z * 4, a_fn_it->code[z].first,
ResourceDASM::PPC32Emulator::disassemble_one(a_fn_it->label->address + z * 4, a_fn_it->code[z].first));
}
do {
a_fn_it++;
} while (a_fn_it != a_ana.functions.end() && a_ignore_functions.count(a_fn_it->label->address));
} else {
phosg::fwrite_fmt(stream, "FUNCTION: A:(missing) B:{:08X}\n", b_fn_it->label->address);
for (size_t z = 0; z < b_fn_it->code.size(); z++) {
phosg::fwrite_fmt(stream, " {:08X} {:08X} {}\n",
b_fn_it->label->address + z * 4, b_fn_it->code[z].first,
ResourceDASM::PPC32Emulator::disassemble_one(b_fn_it->label->address + z * 4, b_fn_it->code[z].first));
}
do {
b_fn_it++;
} while (b_fn_it != b_ana.functions.end() && b_ignore_functions.count(b_fn_it->label->address));
}
}
}
}
}
std::vector<DiffEntry> diff_xbe_files(const std::string& a_filename, const std::string& b_filename) {
ResourceDASM::XBEFile a(a_filename.c_str());
ResourceDASM::XBEFile b(b_filename.c_str());
+9
View File
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -13,5 +14,13 @@ struct DiffEntry {
};
void run_address_translator(const std::string& directory, const std::string& use_filename, const std::string& command);
std::vector<DiffEntry> diff_dol_files(const std::string& a_filename, const std::string& b_filename);
std::vector<DiffEntry> diff_xbe_files(const std::string& a_filename, const std::string& b_filename);
void diff_dol_files_semantic(
FILE* stream,
const std::string& a_filename,
const std::string& b_filename,
const std::unordered_set<uint32_t>& a_ignore_functions,
const std::unordered_set<uint32_t>& b_ignore_functions);
-168
View File
@@ -950,171 +950,3 @@ JSONCommonItemSet::JSONCommonItemSet(const phosg::JSON& json) {
}
}
}
RELFileSet::RELFileSet(std::shared_ptr<const std::string> data) : data(data), r(*this->data) {}
ArmorRandomSet::ArmorRandomSet(std::shared_ptr<const std::string> data) : RELFileSet(data) {
// For some reason the footer tables are doubly indirect in this file
uint32_t specs_offset_offset = this->r.pget_u32b(data->size() - 0x10);
uint32_t specs_offset = this->r.pget_u32b(specs_offset_offset);
this->tables = &this->r.pget<parray<TableSpec, 3>>(specs_offset);
}
std::pair<const ArmorRandomSet::WeightTableEntry8*, size_t>
ArmorRandomSet::get_armor_table(size_t index) const {
return this->get_table<WeightTableEntry8>(this->tables->at(0), index);
}
std::pair<const ArmorRandomSet::WeightTableEntry8*, size_t>
ArmorRandomSet::get_shield_table(size_t index) const {
return this->get_table<WeightTableEntry8>(this->tables->at(1), index);
}
std::pair<const ArmorRandomSet::WeightTableEntry8*, size_t>
ArmorRandomSet::get_unit_table(size_t index) const {
return this->get_table<WeightTableEntry8>(this->tables->at(2), index);
}
ToolRandomSet::ToolRandomSet(std::shared_ptr<const std::string> data) : RELFileSet(data) {
uint32_t specs_offset = r.pget_u32b(data->size() - 0x10);
this->common_recovery_table_spec = &r.pget<TableSpec>(r.pget_u32b(specs_offset));
this->rare_recovery_table_spec = &r.pget<TableSpec>(r.pget_u32b(specs_offset + sizeof(uint32_t)), 2 * sizeof(TableSpec));
this->tech_disk_table_spec = this->rare_recovery_table_spec + 1;
this->tech_disk_level_table_spec = &r.pget<TableSpec>(r.pget_u32b(specs_offset + 2 * sizeof(uint32_t)));
}
std::pair<const uint8_t*, size_t> ToolRandomSet::get_common_recovery_table(size_t index) const {
return this->get_table<uint8_t>(*this->common_recovery_table_spec, index);
}
std::pair<const ToolRandomSet::WeightTableEntry8*, size_t>
ToolRandomSet::get_rare_recovery_table(size_t index) const {
return this->get_table<WeightTableEntry8>(*this->rare_recovery_table_spec, index);
}
std::pair<const ToolRandomSet::WeightTableEntry8*, size_t>
ToolRandomSet::get_tech_disk_table(size_t index) const {
return this->get_table<WeightTableEntry8>(*this->tech_disk_table_spec, index);
}
std::pair<const ToolRandomSet::TechDiskLevelEntry*, size_t>
ToolRandomSet::get_tech_disk_level_table(size_t index) const {
return this->get_table<TechDiskLevelEntry>(*this->tech_disk_level_table_spec, index);
}
WeaponRandomSet::WeaponRandomSet(std::shared_ptr<const std::string> data) : RELFileSet(data) {
uint32_t offsets_offset = this->r.pget_u32b(data->size() - 0x10);
this->offsets = &this->r.pget<Offsets>(offsets_offset);
}
std::pair<const WeaponRandomSet::WeightTableEntry8*, size_t>
WeaponRandomSet::get_weapon_type_table(size_t index) const {
const auto& spec = this->r.pget<TableSpec>(this->offsets->weapon_type_table + index * sizeof(TableSpec));
const auto* data = &this->r.pget<WeightTableEntry8>(spec.offset, spec.entries_per_table * sizeof(WeightTableEntry8));
return std::make_pair(data, spec.entries_per_table);
}
const parray<WeaponRandomSet::WeightTableEntry32, 6>*
WeaponRandomSet::get_bonus_type_table(size_t which, size_t index) const {
uint32_t base_offset = which ? this->offsets->bonus_type_table2 : this->offsets->bonus_type_table1;
return &this->r.pget<parray<WeightTableEntry32, 6>>(base_offset + sizeof(parray<WeightTableEntry32, 6>) * index);
}
const WeaponRandomSet::RangeTableEntry*
WeaponRandomSet::get_bonus_range(size_t which, size_t index) const {
uint32_t base_offset = which ? this->offsets->bonus_range_table2 : this->offsets->bonus_range_table1;
return &this->r.pget<RangeTableEntry>(base_offset + sizeof(RangeTableEntry) * index);
}
const parray<WeaponRandomSet::WeightTableEntry32, 3>*
WeaponRandomSet::get_special_mode_table(size_t index) const {
return &this->r.pget<parray<WeightTableEntry32, 3>>(
this->offsets->special_mode_table + sizeof(parray<WeightTableEntry32, 3>) * index);
}
const WeaponRandomSet::RangeTableEntry*
WeaponRandomSet::get_standard_grind_range(size_t index) const {
return &this->r.pget<RangeTableEntry>(this->offsets->standard_grind_range_table + sizeof(RangeTableEntry) * index);
}
const WeaponRandomSet::RangeTableEntry*
WeaponRandomSet::get_favored_grind_range(size_t index) const {
return &this->r.pget<RangeTableEntry>(this->offsets->favored_grind_range_table + sizeof(RangeTableEntry) * index);
}
TekkerAdjustmentSet::TekkerAdjustmentSet(std::shared_ptr<const std::string> data) : data(data), r(*data) {
this->offsets = &this->r.pget<Offsets>(this->r.pget_u32b(this->r.size() - 0x10));
}
const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_table(
std::array<ProbabilityTable<uint8_t, 100>, 10>& tables_default,
std::array<ProbabilityTable<uint8_t, 100>, 10>& tables_favored,
uint32_t offset_and_count_offset,
bool favored,
uint8_t section_id) const {
if (section_id >= 10) {
throw std::runtime_error("invalid section ID");
}
ProbabilityTable<uint8_t, 100>& table = favored ? tables_favored[section_id] : tables_default[section_id];
if (table.count == 0) {
uint32_t offset = r.pget_u32b(offset_and_count_offset);
uint32_t count_per_section_id = r.pget_u32b(offset_and_count_offset + 4);
auto* entries = &r.pget<DeltaProbabilityEntry>(offset, sizeof(DeltaProbabilityEntry) * count_per_section_id * 10);
for (size_t z = count_per_section_id * section_id; z < count_per_section_id * (section_id + 1); z++) {
size_t count = favored ? entries[z].count_favored : entries[z].count_default;
for (size_t w = 0; w < count; w++) {
table.push(entries[z].delta_index);
}
}
}
return table;
}
const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_special_upgrade_prob_table(uint8_t section_id, bool favored) const {
return this->get_table(
this->special_upgrade_prob_tables_default,
this->special_upgrade_prob_tables_favored,
this->offsets->special_upgrade_prob_table_offset,
favored, section_id);
}
const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_grind_delta_prob_table(uint8_t section_id, bool favored) const {
return this->get_table(
this->grind_delta_prob_tables_default,
this->grind_delta_prob_tables_favored,
this->offsets->grind_delta_prob_table_offset,
favored, section_id);
}
const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_bonus_delta_prob_table(uint8_t section_id, bool favored) const {
return this->get_table(
this->bonus_delta_prob_tables_default,
this->bonus_delta_prob_tables_favored,
this->offsets->bonus_delta_prob_table_offset,
favored, section_id);
}
int8_t TekkerAdjustmentSet::get_luck(uint32_t start_offset, uint8_t delta_index) const {
phosg::StringReader sub_r = r.sub(start_offset);
while (!sub_r.eof()) {
const auto& entry = sub_r.get<LuckTableEntry>();
if (entry.delta_index == 0xFF) {
return 0;
} else if (entry.delta_index == delta_index) {
return entry.luck;
}
}
return 0;
}
int8_t TekkerAdjustmentSet::get_luck_for_special_upgrade(uint8_t delta_index) const {
return this->get_luck(this->offsets->special_upgrade_luck_table_offset, delta_index);
}
int8_t TekkerAdjustmentSet::get_luck_for_grind_delta(uint8_t delta_index) const {
return this->get_luck(this->offsets->grind_delta_luck_table_offset, delta_index);
}
int8_t TekkerAdjustmentSet::get_luck_for_bonus_delta(uint8_t delta_index) const {
return this->get_luck(this->offsets->bonus_delta_luck_offset, delta_index);
}
-292
View File
@@ -284,295 +284,3 @@ class JSONCommonItemSet : public CommonItemSet {
public:
explicit JSONCommonItemSet(const phosg::JSON& json);
};
// Note: There are clearly better ways of doing this, but this implementation closely follows what the original code in
// the client does.
template <typename ItemT, size_t MaxCount>
struct ProbabilityTable {
ItemT items[MaxCount];
size_t count;
ProbabilityTable() : count(0) {}
void push(ItemT item) {
if (this->count == MaxCount) {
throw std::runtime_error("push to full probability table");
}
this->items[this->count++] = item;
}
ItemT pop() {
if (this->count == 0) {
throw std::runtime_error("pop from empty probability table");
}
return this->items[--this->count];
}
void shuffle(std::shared_ptr<RandomGenerator> rand_crypt) {
for (size_t z = 1; z < this->count; z++) {
size_t other_z = rand_crypt->next() % (z + 1);
ItemT t = this->items[z];
this->items[z] = this->items[other_z];
this->items[other_z] = t;
}
}
ItemT sample(std::shared_ptr<RandomGenerator> rand_crypt) const {
if (this->count == 0) {
throw std::runtime_error("sample from empty probability table");
} else if (this->count == 1) {
return this->items[0];
} else {
return this->items[rand_crypt->next() % this->count];
}
}
};
class RELFileSet {
public:
template <typename ValueT, typename WeightT = ValueT>
struct WeightTableEntry {
ValueT value;
WeightT weight;
} __attribute__((packed));
using WeightTableEntry8 = WeightTableEntry<uint8_t>;
using WeightTableEntry32 = WeightTableEntry<be_uint32_t>;
check_struct_size(WeightTableEntry8, 2);
check_struct_size(WeightTableEntry32, 8);
protected:
std::shared_ptr<const std::string> data;
phosg::StringReader r;
struct TableSpec {
be_uint32_t offset;
uint8_t entries_per_table;
parray<uint8_t, 3> unused;
} __packed_ws__(TableSpec, 8);
RELFileSet(std::shared_ptr<const std::string> data);
template <typename T>
std::pair<const T*, size_t> get_table(const TableSpec& spec, size_t index) const {
const T* entries = &r.pget<T>(
spec.offset + index * spec.entries_per_table * sizeof(T), spec.entries_per_table * sizeof(T));
return std::make_pair(entries, spec.entries_per_table);
}
};
class ArmorRandomSet : public RELFileSet {
public:
// This class parses and accesses data from ArmorRandom.rel
ArmorRandomSet(std::shared_ptr<const std::string> data);
std::pair<const WeightTableEntry8*, size_t> get_armor_table(size_t index) const;
std::pair<const WeightTableEntry8*, size_t> get_shield_table(size_t index) const;
std::pair<const WeightTableEntry8*, size_t> get_unit_table(size_t index) const;
private:
const parray<TableSpec, 3>* tables;
};
class ToolRandomSet : public RELFileSet {
public:
// This class parses and accesses data from ToolRandom.rel
ToolRandomSet(std::shared_ptr<const std::string> data);
struct TechDiskLevelEntry {
enum class Mode : uint8_t {
LEVEL_1 = 0,
PLAYER_LEVEL_DIVISOR = 1,
RANDOM_IN_RANGE = 2,
};
Mode mode;
uint8_t player_level_divisor_or_min_level;
uint8_t max_level;
} __packed_ws__(TechDiskLevelEntry, 3);
std::pair<const uint8_t*, size_t> get_common_recovery_table(size_t index) const;
std::pair<const WeightTableEntry8*, size_t> get_rare_recovery_table(size_t index) const;
std::pair<const WeightTableEntry8*, size_t> get_tech_disk_table(size_t index) const;
std::pair<const TechDiskLevelEntry*, size_t> get_tech_disk_level_table(size_t index) const;
private:
const TableSpec* common_recovery_table_spec;
const TableSpec* rare_recovery_table_spec;
const TableSpec* tech_disk_table_spec;
const TableSpec* tech_disk_level_table_spec;
};
class WeaponRandomSet : public RELFileSet {
public:
// This class parses and accesses data from WeaponRandom*.rel
WeaponRandomSet(std::shared_ptr<const std::string> data);
struct RangeTableEntry {
be_uint32_t min;
be_uint32_t max;
} __packed_ws__(RangeTableEntry, 8);
std::pair<const WeightTableEntry8*, size_t> get_weapon_type_table(size_t index) const;
const parray<WeightTableEntry32, 6>* get_bonus_type_table(size_t which, size_t index) const;
const RangeTableEntry* get_bonus_range(size_t which, size_t index) const;
const parray<WeightTableEntry32, 3>* get_special_mode_table(size_t index) const;
const RangeTableEntry* get_standard_grind_range(size_t index) const;
const RangeTableEntry* get_favored_grind_range(size_t index) const;
private:
struct Offsets {
be_uint32_t weapon_type_table; // [{c, o -> (table)}](10)
be_uint32_t bonus_type_table1; // [[{u32 value, u32 weight}](6)](9)
be_uint32_t bonus_type_table2; // [[{u32 value, u32 weight}](6)](9)
be_uint32_t bonus_range_table1; // [{u32 min_index, u32 max_index}](9)
be_uint32_t bonus_range_table2; // [{u32 min_index, u32 max_index}](9)
be_uint32_t special_mode_table; // [[{u32 value, u32 weight}](3)](8)
be_uint32_t standard_grind_range_table; // [{u32 min, u32 max}](6)
be_uint32_t favored_grind_range_table; // [{u32 min, u32 max}](6)
} __packed_ws__(Offsets, 0x20);
const Offsets* offsets;
};
class TekkerAdjustmentSet {
public:
// This class parses and accesses data from JudgeItem.rel
TekkerAdjustmentSet(std::shared_ptr<const std::string> data);
const ProbabilityTable<uint8_t, 100>& get_special_upgrade_prob_table(uint8_t section_id, bool favored) const;
const ProbabilityTable<uint8_t, 100>& get_grind_delta_prob_table(uint8_t section_id, bool favored) const;
const ProbabilityTable<uint8_t, 100>& get_bonus_delta_prob_table(uint8_t section_id, bool favored) const;
int8_t get_luck_for_special_upgrade(uint8_t delta_index) const;
int8_t get_luck_for_grind_delta(uint8_t delta_index) const;
int8_t get_luck_for_bonus_delta(uint8_t delta_index) const;
private:
const ProbabilityTable<uint8_t, 100>& get_table(
std::array<ProbabilityTable<uint8_t, 100>, 10>& tables_default,
std::array<ProbabilityTable<uint8_t, 100>, 10>& tables_favored,
uint32_t offset_and_count_offset,
bool favored,
uint8_t section_id) const;
int8_t get_luck(uint32_t start_offset, uint8_t delta_index) const;
std::shared_ptr<const std::string> data;
phosg::StringReader r;
struct DeltaProbabilityEntry {
uint8_t delta_index;
uint8_t count_default;
uint8_t count_favored;
} __packed_ws__(DeltaProbabilityEntry, 3);
struct LuckTableEntry {
uint8_t delta_index;
int8_t luck;
} __packed_ws__(LuckTableEntry, 2);
struct Offsets {
// Each section ID's favored weapon class has different probabilities than those used for all other weapons. The
// tables are labeled with (D) for the default values and (F) for the favored-class values.
// Note that the favored bonuses for Redria are all zero; these values are unused because Redria does not have a
// favored weapon type. Curiously, Yellowboze also does not have a favored weapon type, but the values for
// Yellowboze are not all zero.
// This table specifies how likely a special is to be upgraded or downgraded by one level.
// In PSO V3, the special upgrade table is:
// Viridia => (D) +1=10%, 0=60%, -1=30%
// Viridia => (F) +1=25%, 0=50%, -1=25%
// Greennill => (D) +1=25%, 0=65%, -1=10%
// Greennill => (F) +1=40%, 0=55%, -1=5%
// Skyly => (D) +1=15%, 0=70%, -1=15%
// Skyly => (F) +1=30%, 0=60%, -1=10%
// Bluefull => (D) +1=10%, 0=60%, -1=30%
// Bluefull => (F) +1=25%, 0=50%, -1=25%
// Purplenum => (D) +1=25%, 0=65%, -1=10%
// Purplenum => (F) +1=40%, 0=55%, -1=5%
// Pinkal => (D) +1=15%, 0=70%, -1=15%
// Pinkal => (F) +1=30%, 0=60%, -1=10%
// Redria => (D) +1=20%, 0=60%, -1=20%
// Redria => (F) +1=0%, 0=0%, -1=0%
// Oran => (D) +1=15%, 0=70%, -1=15%
// Oran => (F) +1=30%, 0=60%, -1=10%
// Yellowboze => (D) +1=25%, 0=65%, -1=10%
// Yellowboze => (F) +1=40%, 0=55%, -1=5%
// Whitill => (D) +1=10%, 0=60%, -1=30%
// Whitill => (F) +1=25%, 0=50%, -1=25%
be_uint32_t special_upgrade_prob_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// This table specifies how likely a weapon's grind is to be upgraded or downgraded, and by how much. The final
// grind value is clamped to the range between 0 and the weapon's maximum grind from ItemPMT, inclusive.
// In PSO V3, the grind delta table is:
// Viridia => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Viridia => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
// Greennill => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Greennill => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Skyly => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Skyly => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Bluefull => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Bluefull => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
// Purplenum => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Purplenum => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Pinkal => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Pinkal => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Redria => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Redria => (F) +3=0%, +2=0%, +1=0%, 0=0%, -1=0%, -2=0%, -3=0%
// Oran => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Oran => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Yellowboze => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Yellowboze => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Whitill => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Whitill => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
be_uint32_t grind_delta_prob_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// This table specifies how likely a weapon's bonuses are to be upgraded or downgraded, and by how much. The final
// bonuses are capped above at 100, but there is no lower limit (so negative results are possible).
// In PSO V3, the bonus delta table is:
// Viridia => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Viridia => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
// Greennill => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Greennill => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Skyly => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Skyly => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Bluefull => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Bluefull => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
// Purplenum => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Purplenum => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Pinkal => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Pinkal => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Redria => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Redria => (F) +10=0%, +5=0%, 0=0%, -5=0%, -10=0%
// Oran => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Oran => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Yellowboze => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Yellowboze => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Whitill => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Whitill => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
be_uint32_t bonus_delta_prob_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// There is a secondary computation done during weapon adjustment that appears to determine how "good" the
// resulting weapon is compared to its original state. If the result of this computation is positive, the game
// plays a jingle when the tekker result is accepted. These tables describe how much each delta affects this value,
// which we call luck.
// In PSO V3, the special upgrade luck table is:
// +1 => +20, 0 => 0, -1 => -20
be_uint32_t special_upgrade_luck_table_offset; // LuckTableEntry[...]; ending with FF FF
// In PSO V3, the grind delta luck table is:
// +3 => +10, +2 => +5, +1 => +3, 0 => 0, -1 => -3, -2 => -5, -3 => -10
be_uint32_t grind_delta_luck_table_offset; // LuckTableEntry[...]; ending with FF FF
// In PSO V3, the bonus delta luck table is:
// +10 => +15, +5 => +8, 0 => 0, -5 => -8, -10 => -15
be_uint32_t bonus_delta_luck_offset; // LuckTableEntry[...]; ending with FF FF
} __packed_ws__(Offsets, 0x18);
const Offsets* offsets;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> special_upgrade_prob_tables_default;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> special_upgrade_prob_tables_favored;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> grind_delta_prob_tables_default;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> grind_delta_prob_tables_favored;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> bonus_delta_prob_tables_default;
mutable std::array<ProbabilityTable<uint8_t, 100>, 10> bonus_delta_prob_tables_favored;
};
+127 -51
View File
@@ -28,29 +28,29 @@ const char* phosg::name_for_enum<CompressPhase>(CompressPhase v) {
}
template <size_t WindowLength, size_t MaxMatchLength>
struct WindowIndex {
struct MapWindowIndex {
const uint8_t* data;
size_t size;
size_t offset;
size_t current_offset;
std::set<size_t, std::function<bool(size_t, size_t)>> index;
WindowIndex(const void* data, size_t size)
MapWindowIndex(const void* data, size_t size)
: data(reinterpret_cast<const uint8_t*>(data)),
size(size),
offset(0),
index(std::bind(&WindowIndex::set_comparator, this, std::placeholders::_1, std::placeholders::_2)) {}
current_offset(0),
index(std::bind(&MapWindowIndex::set_comparator, this, std::placeholders::_1, std::placeholders::_2)) {}
void advance() {
if (this->offset >= WindowLength) {
this->index.erase(this->offset - WindowLength);
if (this->current_offset >= WindowLength) {
this->index.erase(this->current_offset - WindowLength);
}
this->index.emplace(this->offset);
this->offset++;
this->index.emplace(this->current_offset);
this->current_offset++;
}
size_t get_match_length(size_t match_offset) const {
size_t match_iter = match_offset;
size_t offset_iter = this->offset;
size_t offset_iter = this->current_offset;
while ((match_iter < match_offset + MaxMatchLength) &&
(match_iter < this->size) &&
(offset_iter < this->size) &&
@@ -81,14 +81,14 @@ struct WindowIndex {
return a < b; // Maximum-length match; order them by offset
};
std::pair<size_t, size_t> get_best_match() const {
std::pair<size_t, size_t> match() const {
// Find the best match from the index. It's unlikely that we'll get an exact match, so check the entry before the
// upper_bound result too. Note: We use upper_bound rather than lower_bound because in PRS, a backreference can be
// encoded with fewer bits if it's close to the decompression offset, and this makes us pick the latest match by
// default.
size_t match_offset = 0;
size_t match_size = 0;
auto it = this->index.upper_bound(this->offset);
auto it = this->index.upper_bound(this->current_offset);
if (it != this->index.end()) {
size_t new_match_offset = *it;
size_t new_match_size = this->get_match_length(new_match_offset);
@@ -110,6 +110,82 @@ struct WindowIndex {
}
};
template <size_t WindowLength, size_t MaxMatchLength>
struct TreeWindowIndex {
// This class is the result of an experiment to see if a prefix tree is faster for optimal compression. It turns out
// it's not, even if the std::map in Node is replaced with a std::unordered_map or std::array. This structure is
// easier to understand than MapWindowIndex, but is about 6-7x slower on average.
const uint8_t* data;
size_t size;
size_t current_offset;
struct Node {
size_t start_offset = 0;
std::map<uint8_t, Node> children;
};
Node root;
TreeWindowIndex(const void* data, size_t size)
: data(reinterpret_cast<const uint8_t*>(data)), size(size), current_offset(0) {}
void advance() {
// Delete the oldest string, if the current offset has passed the initial window
if (this->current_offset >= WindowLength) {
size_t start_offset = this->current_offset - WindowLength;
size_t end_offset = std::min<size_t>(this->size, start_offset + MaxMatchLength);
Node* current = &this->root;
for (size_t offset = start_offset; offset < end_offset; offset++) {
auto child_it = current->children.find(this->data[offset]);
// The child should always be in the set - we should have added all of its nodes in a previous advance() call
if (child_it == current->children.end()) {
throw std::logic_error(std::format("Attempted to delete string at offset {:X} which was not in the set",
start_offset));
}
if (child_it->second.start_offset == start_offset) {
// If the child's start offset matches start_offset, then the rest of the nodes below it must also match
// start_offset (they were never again visited by the second part of this function after the first time they
// were added) so the entire subtree can be deleted. This means there are no other strings in the window that
// share the first (start_offset + 1) bytes with this string.
current->children.erase(child_it);
break;
} else {
// If the start offset does not match start_offset, then this node belongs to a later string; we need to
// check its subtree to see if anything should be deleted
current = &child_it->second;
}
}
}
// Create all nodes for the current string, or update their start_offsets if they already exist
size_t end_offset = std::min<size_t>(size, this->current_offset + MaxMatchLength);
Node* current = &this->root;
for (size_t offset = this->current_offset; offset < end_offset; offset++) {
current = &current->children[this->data[offset]];
current->start_offset = this->current_offset;
}
this->current_offset++;
}
std::pair<size_t, size_t> match() const {
size_t end_offset = std::min<size_t>(size, this->current_offset + MaxMatchLength);
const Node* current = &this->root;
size_t offset;
for (offset = this->current_offset; offset < end_offset; offset++) {
auto it = current->children.find(this->data[offset]);
if (it == current->children.end()) {
break;
} else {
current = &it->second;
}
}
return std::make_pair(current->start_offset, offset - this->current_offset);
}
};
template <size_t WindowSize, size_t MaxMatchLength>
using WindowIndex = MapWindowIndex<WindowSize, MaxMatchLength>;
struct LZSSInterleavedWriter {
phosg::StringWriter w;
size_t buf_offset;
@@ -219,15 +295,15 @@ std::string prs_compress_optimal(const void* in_data_v, size_t in_size, Progress
// Populate all possible short copies
std::thread short_window_thread([&]() -> void {
WindowIndex<0x100, 5> window(in_data_v, in_size);
while (window.offset < in_size) {
if (window.offset && (window.offset & 0xFFF) == 0 && progress_fn) {
while (window.current_offset < in_size) {
if (window.current_offset && (window.current_offset & 0xFFF) == 0 && progress_fn) {
size_t progress = copy_progress.fetch_add(0x1000) + 0x1000;
progress_fn(CompressPhase::INDEX, progress, copy_progress_max, 0);
}
auto& node = nodes[window.offset];
auto match = window.get_best_match();
auto& node = nodes[window.current_offset];
auto match = window.match();
if (match.second >= 2) {
node.short_copy_offset = match.first - window.offset;
node.short_copy_offset = match.first - window.current_offset;
node.max_short_copy_size = match.second;
}
window.advance();
@@ -237,15 +313,15 @@ std::string prs_compress_optimal(const void* in_data_v, size_t in_size, Progress
// Populate all possible long copies
std::thread long_window_thread([&]() -> void {
WindowIndex<0x1FFF, 9> window(in_data_v, in_size);
while (window.offset < in_size) {
if (window.offset && (window.offset & 0xFFF) == 0 && progress_fn) {
while (window.current_offset < in_size) {
if (window.current_offset && (window.current_offset & 0xFFF) == 0 && progress_fn) {
size_t progress = copy_progress.fetch_add(0x1000) + 0x1000;
progress_fn(CompressPhase::INDEX, progress, copy_progress_max, 0);
}
auto& node = nodes[window.offset];
auto match = window.get_best_match();
auto& node = nodes[window.current_offset];
auto match = window.match();
if (match.second >= 3) {
node.long_copy_offset = match.first - window.offset;
node.long_copy_offset = match.first - window.current_offset;
node.max_long_copy_size = match.second;
}
window.advance();
@@ -255,15 +331,15 @@ std::string prs_compress_optimal(const void* in_data_v, size_t in_size, Progress
// Populate all possible extended copies
std::thread extended_window_thread([&]() -> void {
WindowIndex<0x1FFF, 0x100> window(in_data_v, in_size);
while (window.offset < in_size) {
if (window.offset && (window.offset & 0xFFF) == 0 && progress_fn) {
while (window.current_offset < in_size) {
if (window.current_offset && (window.current_offset & 0xFFF) == 0 && progress_fn) {
size_t progress = copy_progress.fetch_add(0x1000) + 0x1000;
progress_fn(CompressPhase::INDEX, progress, copy_progress_max, 0);
}
auto& node = nodes[window.offset];
auto match = window.get_best_match();
auto& node = nodes[window.current_offset];
auto match = window.match();
if (match.second >= 1) {
node.extended_copy_offset = match.first - window.offset;
node.extended_copy_offset = match.first - window.current_offset;
node.max_extended_copy_size = match.second;
}
window.advance();
@@ -434,10 +510,10 @@ std::string prs_compress_pessimal(const void* vdata, size_t size) {
WindowIndex<0x1FFF, 1> window(in_data, size);
LZSSInterleavedWriter w;
for (size_t z = 0; z < size; z++) {
auto match = window.get_best_match();
auto match = window.match();
if (match.second >= 1) {
// Write extended copy
int16_t offset = match.first - window.offset;
int16_t offset = match.first - window.current_offset;
w.write_control(false);
w.flush_if_ready();
w.write_control(true);
@@ -681,15 +757,15 @@ std::string prs_compress_indexed(const void* in_data_v, size_t in_size, Progress
WindowIndex<0x1FFF, 0x100> w_extended(in_data_v, in_size);
size_t last_progress_fn_call_offset = 0;
while (w_short.offset < in_size) {
if (progress_fn && ((last_progress_fn_call_offset & ~0xFFF) != (w_short.offset & ~0xFFF))) {
last_progress_fn_call_offset = w_short.offset;
progress_fn(CompressPhase::GENERATE_RESULT, w_short.offset, in_size, w.size());
while (w_short.current_offset < in_size) {
if (progress_fn && ((last_progress_fn_call_offset & ~0xFFF) != (w_short.current_offset & ~0xFFF))) {
last_progress_fn_call_offset = w_short.current_offset;
progress_fn(CompressPhase::GENERATE_RESULT, w_short.current_offset, in_size, w.size());
}
auto m_short = w_short.get_best_match();
auto m_long = w_long.get_best_match();
auto m_extended = w_extended.get_best_match();
auto m_short = w_short.match();
auto m_long = w_long.match();
auto m_extended = w_extended.match();
// Write the match that achieves the best ratio of output bytes to compressed bits used. To do this without
// floating-point math, we multiply the output byte count for each type of command by 468 / (command_bits), since
@@ -735,11 +811,11 @@ std::string prs_compress_indexed(const void* in_data_v, size_t in_size, Progress
switch (command_type) {
case PRSPathNode::CommandType::LITERAL:
w.write_control(true);
w.write_data(in_data[w_short.offset]);
w.write_data(in_data[w_short.current_offset]);
bytes_consumed = 1;
break;
case PRSPathNode::CommandType::SHORT_COPY: {
ssize_t backreference_offset = m_short.first - w_short.offset;
ssize_t backreference_offset = m_short.first - w_short.current_offset;
uint8_t encoded_size = m_short.second - 2;
w.write_control(false);
w.flush_if_ready();
@@ -753,7 +829,7 @@ std::string prs_compress_indexed(const void* in_data_v, size_t in_size, Progress
break;
}
case PRSPathNode::CommandType::LONG_COPY: {
ssize_t backreference_offset = m_long.first - w_long.offset;
ssize_t backreference_offset = m_long.first - w_long.current_offset;
w.write_control(false);
w.flush_if_ready();
w.write_control(true);
@@ -764,7 +840,7 @@ std::string prs_compress_indexed(const void* in_data_v, size_t in_size, Progress
break;
}
case PRSPathNode::CommandType::EXTENDED_COPY: {
ssize_t backreference_offset = m_extended.first - w_extended.offset;
ssize_t backreference_offset = m_extended.first - w_extended.current_offset;
w.write_control(false);
w.flush_if_ready();
w.write_control(true);
@@ -1051,12 +1127,12 @@ std::string bc0_compress_optimal(const void* in_data_v, size_t in_size, Progress
// Populate all possible backreferences
{
WindowIndex<0x1000, 0x12> window(in_data_v, in_size);
while (window.offset < in_size) {
if ((window.offset & 0xFFF) == 0 && progress_fn) {
progress_fn(CompressPhase::INDEX, window.offset, in_size, 0);
while (window.current_offset < in_size) {
if ((window.current_offset & 0xFFF) == 0 && progress_fn) {
progress_fn(CompressPhase::INDEX, window.current_offset, in_size, 0);
}
auto& node = nodes[window.offset];
auto match = window.get_best_match();
auto& node = nodes[window.current_offset];
auto match = window.match();
if (match.second >= 3) {
node.memo_offset = (match.first - 0x12) & 0xFFF;
node.max_copy_size = match.second;
@@ -1148,13 +1224,13 @@ std::string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback
WindowIndex<0x1000, 0x12> window(in_data_v, in_size);
size_t last_progress_fn_call_offset = 0;
while (window.offset < in_size) {
if (progress_fn && ((last_progress_fn_call_offset & ~0xFFF) != (window.offset & ~0xFFF))) {
last_progress_fn_call_offset = window.offset;
progress_fn(CompressPhase::GENERATE_RESULT, window.offset, in_size, w.size());
while (window.current_offset < in_size) {
if (progress_fn && ((last_progress_fn_call_offset & ~0xFFF) != (window.current_offset & ~0xFFF))) {
last_progress_fn_call_offset = window.current_offset;
progress_fn(CompressPhase::GENERATE_RESULT, window.current_offset, in_size, w.size());
}
auto match = window.get_best_match();
auto match = window.match();
// Write a backreference if a match was found; otherwise, write a literal
if (match.second >= 3) {
@@ -1164,7 +1240,7 @@ std::string bc0_compress(const void* in_data_v, size_t in_size, ProgressCallback
w.write_data(((memo_offset >> 4) & 0xF0) | (match.second - 3));
} else {
w.write_control(true);
w.write_data(in_data[window.offset]);
w.write_data(in_data[window.current_offset]);
match.second = 1;
}
w.flush_if_ready();
+142 -251
View File
@@ -6,26 +6,72 @@
#include "EnemyType.hh"
#include "Loggers.hh"
// The favored weapon type table is hardcoded in the game client. The table is:
// Viridia shots
// Greennill rifles
// Skyly swords
// Bluefull partisans
// Purplenum mechguns
// Pinkal canes
// Redria (none)
// Oran daggers
// Yellowboze (none)
// Whitill slicers
static const std::array<uint8_t, 10> favored_weapon_by_section_id = {
0x09, 0x07, 0x02, 0x04, 0x08, 0x0A, 0xFF, 0x03, 0xFF, 0x05};
// Note: There are clearly better ways of doing this, but this implementation closely follows what the original code in
// the client does.
template <typename ItemT, size_t MaxCount>
struct ProbabilityTable {
ItemT items[MaxCount];
size_t count;
ProbabilityTable() : count(0) {}
ProbabilityTable(const std::vector<ShopRandomSetBase::IntPairT<ItemT>>& table) : ProbabilityTable() {
for (const auto& entry : table) {
for (size_t y = 0; y < entry.weight; y++) {
this->push(entry.value);
}
}
}
template <size_t Count>
ProbabilityTable(const std::array<ShopRandomSetBase::IntPairT<ItemT>, Count>& table) : ProbabilityTable() {
for (const auto& entry : table) {
for (size_t y = 0; y < entry.weight; y++) {
this->push(entry.value);
}
}
}
void push(ItemT item) {
if (this->count == MaxCount) {
throw std::runtime_error("push to full probability table");
}
this->items[this->count++] = item;
}
ItemT pop() {
if (this->count == 0) {
throw std::runtime_error("pop from empty probability table");
}
return this->items[--this->count];
}
void shuffle(std::shared_ptr<RandomGenerator> rand_crypt) {
for (size_t z = 1; z < this->count; z++) {
size_t other_z = rand_crypt->next() % (z + 1);
ItemT t = this->items[z];
this->items[z] = this->items[other_z];
this->items[other_z] = t;
}
}
ItemT sample(std::shared_ptr<RandomGenerator> rand_crypt) const {
if (this->count == 0) {
throw std::runtime_error("sample from empty probability table");
} else if (this->count == 1) {
return this->items[0];
} else {
return this->items[rand_crypt->next() % this->count];
}
}
};
ItemCreator::ItemCreator(
std::shared_ptr<const CommonItemSet> common_item_set,
std::shared_ptr<const RareItemSet> rare_item_set,
std::shared_ptr<const ArmorRandomSet> armor_random_set,
std::shared_ptr<const ToolRandomSet> tool_random_set,
std::shared_ptr<const WeaponRandomSet> weapon_random_set,
std::shared_ptr<const ArmorShopRandomSet> armor_random_set,
std::shared_ptr<const ToolShopRandomSet> tool_random_set,
std::shared_ptr<const WeaponShopRandomSet> weapon_random_set,
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const ItemData::StackLimits> stack_limits,
@@ -1059,13 +1105,7 @@ void ItemCreator::generate_armor_shop_armors(std::vector<ItemData>& shop, Episod
}
size_t table_index = this->get_table_index_for_armor_shop(player_level);
ProbabilityTable<uint8_t, 100> pt;
auto src_table = this->armor_random_set->get_armor_table(table_index);
for (size_t z = 0; z < src_table.second; z++) {
for (size_t y = 0; y < src_table.first[z].weight; y++) {
pt.push(src_table.first[z].value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->armor_random_set->armor_table.at(table_index)};
pt.shuffle(this->rand_crypt);
for (size_t items_generated = 0; items_generated < num_items;) {
@@ -1103,13 +1143,7 @@ void ItemCreator::generate_armor_shop_shields(std::vector<ItemData>& shop, size_
}
size_t table_index = this->get_table_index_for_armor_shop(player_level);
ProbabilityTable<uint8_t, 100> pt;
auto src_table = this->armor_random_set->get_shield_table(table_index);
for (size_t z = 0; z < src_table.second; z++) {
for (size_t y = 0; y < src_table.first[z].weight; y++) {
pt.push(src_table.first[z].value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->armor_random_set->shield_table.at(table_index)};
pt.shuffle(this->rand_crypt);
for (size_t items_generated = 0; items_generated < num_items;) {
@@ -1146,13 +1180,7 @@ void ItemCreator::generate_armor_shop_units(std::vector<ItemData>& shop, size_t
}
size_t table_index = this->get_table_index_for_armor_shop(player_level);
ProbabilityTable<uint8_t, 100> pt;
auto src_table = this->armor_random_set->get_unit_table(table_index);
for (size_t z = 0; z < src_table.second; z++) {
for (size_t y = 0; y < src_table.first[z].weight; y++) {
pt.push(src_table.first[z].value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->armor_random_set->unit_table.at(table_index)};
pt.shuffle(this->rand_crypt);
for (size_t items_generated = 0; items_generated < num_items;) {
@@ -1190,10 +1218,6 @@ size_t ItemCreator::get_table_index_for_tool_shop(size_t player_level) {
}
}
static const std::vector<std::pair<uint8_t, uint8_t>> tool_item_defs{
{0x00, 0x00}, {0x00, 0x01}, {0x00, 0x02}, {0x01, 0x00}, {0x01, 0x01}, {0x01, 0x02}, {0x06, 0x00}, {0x06, 0x01},
{0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00}, {0x07, 0x00}, {0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0xFF, 0xFF}};
void ItemCreator::generate_common_tool_shop_recovery_items(std::vector<ItemData>& shop, size_t player_level) {
size_t table_index;
if (player_level < 11) {
@@ -1210,17 +1234,15 @@ void ItemCreator::generate_common_tool_shop_recovery_items(std::vector<ItemData>
table_index = 5;
}
auto table = this->tool_random_set->get_common_recovery_table(table_index);
for (size_t z = 0; z < table.second; z++) {
uint8_t type = table.first[z];
if (type == 0x0F) {
for (const auto& entry : this->tool_random_set->common_recovery_table.at(table_index)) {
if (entry == 0x0F) {
continue;
}
auto& item = shop.emplace_back();
item.data1[0] = 3;
item.data1[1] = tool_item_defs[type].first;
item.data1[2] = tool_item_defs[type].second;
item.data1[1] = ToolShopRandomSet::item_defs[entry].first;
item.data1[2] = ToolShopRandomSet::item_defs[entry].second;
}
}
@@ -1230,15 +1252,8 @@ void ItemCreator::generate_rare_tool_shop_recovery_items(std::vector<ItemData>&
}
static constexpr size_t num_items = 2;
ProbabilityTable<uint8_t, 100> pt;
size_t table_index = this->get_table_index_for_tool_shop(player_level);
auto table = this->tool_random_set->get_rare_recovery_table(table_index);
for (size_t z = 0; z < table.second; z++) {
const auto& e = table.first[z];
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->tool_random_set->rare_recovery_table.at(table_index)};
pt.shuffle(this->rand_crypt);
size_t effective_num_items = num_items;
@@ -1252,8 +1267,8 @@ void ItemCreator::generate_rare_tool_shop_recovery_items(std::vector<ItemData>&
} else {
ItemData item;
item.data1[0] = 3;
item.data1[1] = tool_item_defs[type].first;
item.data1[2] = tool_item_defs[type].second;
item.data1[1] = ToolShopRandomSet::item_defs[type].first;
item.data1[2] = ToolShopRandomSet::item_defs[type].second;
if (this->shop_does_not_contain_duplicate_item_by_data1_0_1_2(shop, item)) {
shop.emplace_back(std::move(item));
items_generated++;
@@ -1273,28 +1288,16 @@ void ItemCreator::generate_tool_shop_tech_disks(std::vector<ItemData>& shop, siz
}
size_t table_index = this->get_table_index_for_tool_shop(player_level);
auto table = this->tool_random_set->get_tech_disk_table(table_index);
ProbabilityTable<uint8_t, 100> pt;
for (size_t z = 0; z < table.second; z++) {
const auto& e = table.first[z];
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->tool_random_set->tech_disk_table.at(table_index)};
pt.shuffle(this->rand_crypt);
static const std::array<uint8_t, 0x13> tech_num_map = {
0x00, 0x03, 0x06, 0x0F, 0x10, 0x0D, 0x0A, 0x0B, 0x0C, 0x01, 0x04, 0x07,
0x0E, 0x11, 0x02, 0x05, 0x08, 0x09, 0x12};
size_t items_generated = 0;
while (items_generated < num_items) {
uint8_t tech_num_index = pt.pop();
ItemData item;
item.data1[0] = 3;
item.data1[1] = 2;
item.data1[4] = tech_num_map.at(tech_num_index);
item.data1[4] = ToolShopRandomSet::tech_num_map.at(tech_num_index);
this->choose_tech_disk_level_for_tool_shop(item, player_level, tech_num_index);
if (this->shop_does_not_contain_duplicate_tech_disk(shop, item)) {
shop.emplace_back(std::move(item));
@@ -1305,22 +1308,22 @@ void ItemCreator::generate_tool_shop_tech_disks(std::vector<ItemData>& shop, siz
void ItemCreator::choose_tech_disk_level_for_tool_shop(ItemData& item, size_t player_level, uint8_t tech_num_index) {
size_t table_index = this->get_table_index_for_tool_shop(player_level);
auto table = this->tool_random_set->get_tech_disk_level_table(table_index);
if (tech_num_index >= table.second) {
auto table = this->tool_random_set->tech_disk_level_table.at(table_index);
if (tech_num_index >= table.size()) {
throw std::runtime_error("technique number out of range");
}
const auto& e = table.first[tech_num_index];
const auto& e = table[tech_num_index];
switch (e.mode) {
case ToolRandomSet::TechDiskLevelEntry::Mode::LEVEL_1:
case ToolShopRandomSet::TechDiskLevelEntry::Mode::LEVEL_1:
item.data1[2] = 0;
break;
case ToolRandomSet::TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR:
case ToolShopRandomSet::TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR:
item.data1[2] = std::clamp<ssize_t>(
(std::min<size_t>(player_level, 99) / e.player_level_divisor_or_min_level) - 1, 0, 14);
break;
case ToolRandomSet::TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: {
// Note: This logic does not give a uniform distribution - if the minimumlevel is not zero (level 1), then the
case ToolShopRandomSet::TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: {
// Note: This logic does not give a uniform distribution - if the minimum level is not zero (level 1), then the
// minimum level is more likely than all the other levels. This behavior matches the client's logic, though it's
// unclear if this nonuniformity was intentional.
int16_t min_level = std::max<int16_t>(e.player_level_divisor_or_min_level - 1, 0);
@@ -1373,119 +1376,25 @@ std::vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_l
}
}
ProbabilityTable<uint8_t, 100> pt;
auto table = this->weapon_random_set->get_weapon_type_table(table_index);
for (size_t z = 0; z < table.second; z++) {
const auto& e = table.first[z];
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint8_t, 100> pt{this->weapon_random_set->weapon_type_weight_tables.at(table_index).at(section_id)};
pt.shuffle(this->rand_crypt);
std::vector<ItemData> shop;
while (shop.size() < num_items) {
ItemData item;
const std::pair<uint8_t, uint8_t>* def;
uint8_t which = pt.pop();
if (which == 0x39) {
static const std::vector<std::pair<uint8_t, uint8_t>> defs{
{0x28, 0x00}, {0x2A, 0x00}, {0x2B, 0x00}, {0x35, 0x00}, {0x52, 0x00}, {0x48, 0x00}, {0x64, 0x00},
{0x59, 0x00}, {0x8A, 0x00}, {0x99, 0x00}};
const auto& def = defs.at(this->section_id);
item.data1[0] = 0;
item.data1[1] = def.first;
item.data1[2] = def.second;
def = &WeaponShopRandomSet::type_defs_39.at(this->section_id);
} else if (which == 0x3A) {
static const std::vector<std::pair<uint8_t, uint8_t>> defs{
{0x99, 0x00}, {0x64, 0x00}, {0x8A, 0x00}, {0x28, 0x00}, {0x59, 0x00}, {0x2B, 0x00}, {0x52, 0x00},
{0x2A, 0x00}, {0x48, 0x00}, {0x35, 0x00}};
const auto& def = defs.at(this->section_id);
item.data1[0] = 0;
item.data1[1] = def.first;
item.data1[2] = def.second;
def = &WeaponShopRandomSet::type_defs_3A.at(this->section_id);
} else {
static const std::vector<std::pair<uint8_t, uint8_t>> defs({
/* 00 */ {0x01, 0x00},
/* 01 */ {0x01, 0x01},
/* 02 */ {0x01, 0x02},
/* 03 */ {0x01, 0x03},
/* 04 */ {0x01, 0x04},
/* 05 */ {0x03, 0x00},
/* 06 */ {0x03, 0x01},
/* 07 */ {0x03, 0x02},
/* 08 */ {0x03, 0x03},
/* 09 */ {0x03, 0x04},
/* 0A */ {0x02, 0x00},
/* 0B */ {0x02, 0x01},
/* 0C */ {0x02, 0x02},
/* 0D */ {0x02, 0x03},
/* 0E */ {0x02, 0x04},
/* 0F */ {0x05, 0x00},
/* 10 */ {0x05, 0x01},
/* 11 */ {0x05, 0x02},
/* 12 */ {0x05, 0x03},
/* 13 */ {0x05, 0x04},
/* 14 */ {0x04, 0x00},
/* 15 */ {0x04, 0x01},
/* 16 */ {0x04, 0x02},
/* 17 */ {0x04, 0x03},
/* 18 */ {0x04, 0x04},
/* 19 */ {0x06, 0x00},
/* 1A */ {0x06, 0x01},
/* 1B */ {0x06, 0x02},
/* 1C */ {0x06, 0x03},
/* 1D */ {0x06, 0x04},
/* 1E */ {0x07, 0x00},
/* 1F */ {0x07, 0x01},
/* 20 */ {0x07, 0x02},
/* 21 */ {0x07, 0x03},
/* 22 */ {0x07, 0x04},
/* 23 */ {0x08, 0x00},
/* 24 */ {0x08, 0x01},
/* 25 */ {0x08, 0x02},
/* 26 */ {0x08, 0x03},
/* 27 */ {0x08, 0x04},
/* 28 */ {0x09, 0x00},
/* 29 */ {0x09, 0x01},
/* 2A */ {0x09, 0x02},
/* 2B */ {0x09, 0x03},
/* 2C */ {0x09, 0x04},
/* 2D */ {0x0A, 0x00},
/* 2E */ {0x0A, 0x01},
/* 2F */ {0x0A, 0x02},
/* 30 */ {0x0A, 0x03},
/* 31 */ {0x0B, 0x00},
/* 32 */ {0x0B, 0x01},
/* 33 */ {0x0B, 0x02},
/* 34 */ {0x0B, 0x03},
/* 35 */ {0x0C, 0x00},
/* 36 */ {0x0C, 0x01},
/* 37 */ {0x0C, 0x02},
/* 38 */ {0x0C, 0x03},
/* 39 */ {0xFF, 0xFF}, // Special-cased above
/* 3A */ {0xFF, 0xFF}, // Special-cased above
/* 3B */ {0x01, 0x05},
/* 3C */ {0x02, 0x05},
/* 3D */ {0x06, 0x05},
/* 3E */ {0x08, 0x05},
/* 3F */ {0x0A, 0x04},
/* 40 */ {0x0C, 0x04},
/* 41 */ {0x0B, 0x04},
/* 42 */ {0x01, 0x06},
/* 43 */ {0x03, 0x05},
/* 44 */ {0x07, 0x05},
/* 45 */ {0x0A, 0x05},
/* 46 */ {0x0C, 0x05},
/* 47 */ {0x0B, 0x05},
});
const auto& def = defs.at(which);
item.data1[0] = 0;
item.data1[1] = def.first;
item.data1[2] = def.second;
def = &WeaponShopRandomSet::type_defs.at(which);
}
item.data1[0] = 0;
item.data1[1] = def->first;
item.data1[2] = def->second;
this->generate_weapon_shop_item_grind(item, player_level);
this->generate_weapon_shop_item_special(item, player_level);
@@ -1519,18 +1428,17 @@ void ItemCreator::generate_weapon_shop_item_grind(ItemData& item, size_t player_
table_index = 5;
}
uint8_t favored_weapon = favored_weapon_by_section_id.at(this->section_id);
uint8_t favored_weapon = TekkerAdjustmentSet::favored_weapon_type_for_section_id(this->section_id);
bool is_favored = (favored_weapon != 0xFF) && (item.data1[1] == favored_weapon);
const auto* range = is_favored
? this->weapon_random_set->get_favored_grind_range(table_index)
: this->weapon_random_set->get_standard_grind_range(table_index);
const auto& range = is_favored
? this->weapon_random_set->favored_grind_range_table.at(table_index)
: this->weapon_random_set->default_grind_range_table.at(table_index);
const auto& weapon_def = this->item_parameter_table->get_weapon(item.data1[1], item.data1[2]);
item.data1[3] = std::clamp<uint8_t>(this->rand_int(range->max + 1), range->min, weapon_def.max_grind);
item.data1[3] = std::clamp<uint8_t>(this->rand_int(range.max + 1), range.min, weapon_def.max_grind);
}
void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t player_level) {
ProbabilityTable<uint8_t, 100> pt;
size_t table_index;
if (player_level < 11) {
@@ -1551,13 +1459,8 @@ void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t playe
table_index = 7;
}
const auto* table = this->weapon_random_set->get_special_mode_table(table_index);
for (size_t z = 0; z < table->size(); z++) {
const auto& e = table->at(z);
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint32_t, 100> pt{this->weapon_random_set->special_mode_table.at(table_index)};
pt.shuffle(this->rand_crypt);
// Note: The original code shuffles pt and then pops a single value from it. For simplicity, we just sample a single
// value instead.
@@ -1576,9 +1479,6 @@ void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t playe
}
}
static const std::array<int8_t, 20> bonus_values = {
-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player_level) {
size_t table_index;
if (player_level < 4) {
@@ -1601,14 +1501,8 @@ void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player
table_index = 8;
}
const auto* type_table = this->weapon_random_set->get_bonus_type_table(0, table_index);
ProbabilityTable<uint8_t, 100> pt;
for (size_t z = 0; z < type_table->size(); z++) {
const auto& e = type_table->at(z);
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint32_t, 100> pt{this->weapon_random_set->bonus_type_table1.at(table_index)};
pt.shuffle(this->rand_crypt);
// Note: The original code shuffles pt and then pops a single value from it. For simplicity, we just sample a single
// value instead.
@@ -1616,8 +1510,8 @@ void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player
if (item.data1[6] == 0) {
item.data1[7] = 0;
} else {
const auto* range = this->weapon_random_set->get_bonus_range(0, table_index);
item.data1[7] = bonus_values.at(std::max<size_t>(this->rand_int(range->max + 1), range->min));
const auto& range = this->weapon_random_set->bonus_range_table1.at(table_index);
item.data1[7] = WeaponShopRandomSet::bonus_values.at(std::max<size_t>(this->rand_int(range.max + 1), range.min));
}
}
@@ -1643,14 +1537,7 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player
table_index = 8;
}
const auto* type_table = this->weapon_random_set->get_bonus_type_table(1, table_index);
ProbabilityTable<uint8_t, 100> pt;
for (size_t z = 0; z < type_table->size(); z++) {
const auto& e = type_table->at(z);
for (size_t y = 0; y < e.weight; y++) {
pt.push(e.value);
}
}
ProbabilityTable<uint32_t, 100> pt{this->weapon_random_set->bonus_type_table2.at(table_index)};
pt.shuffle(this->rand_crypt);
do {
@@ -1660,8 +1547,8 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player
if (item.data1[8] == 0) {
item.data1[9] = 0;
} else {
const auto* range = this->weapon_random_set->get_bonus_range(1, table_index);
item.data1[9] = bonus_values.at(std::max<size_t>(this->rand_int(range->max + 1), range->min));
const auto& range = this->weapon_random_set->bonus_range_table2.at(table_index);
item.data1[9] = WeaponShopRandomSet::bonus_values.at(std::max<size_t>(this->rand_int(range.max + 1), range.min));
}
}
@@ -1724,56 +1611,61 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
throw std::runtime_error("tekker deltas can only be applied to weapons");
}
static const std::array<int8_t, 11> delta_table = {-10, -5, -3, -2, -1, 0, 1, 2, 3, 5, 10};
bool favored = item.data1[1] == favored_weapon_by_section_id[section_id];
bool favored = (item.data1[1] == TekkerAdjustmentSet::favored_weapon_type_for_section_id(section_id));
ssize_t luck = 0;
this->log.info_f("Applying tekker deltas for {} weapon", favored ? "favored" : "non-favored");
auto sample_prob_table = [this](const TekkerAdjustmentSet::Table& table) -> int8_t {
size_t sample = this->rand_crypt->next() % table.total;
for (const auto& [k, v] : table.probs) {
if (sample < v) {
return k;
}
sample -= v;
}
throw std::logic_error("Table total is incorrect");
};
// Adjust the weapon's special
{
const auto& prob_table = this->tekker_adjustment_set->get_special_upgrade_prob_table(section_id, favored);
uint8_t delta_index = prob_table.sample(this->rand_crypt);
int8_t delta = delta_table.at(delta_index);
this->log.info_f("(Special) Delta index {}, delta {}", delta_index, delta);
// Note: The original code checks specifically for -1 and +1 here, but the data files only include delta_indexes 4,
// 5, and 6 (which correspond to -1, 0, and 1) anyway, so we just check for positive and negative numbers instead.
// When using the original JudgeItem.rel file, the behavior should be the same, but this feels more correct.
try {
uint8_t new_special;
if (delta < 0) {
new_special = item.data1[4] - 1;
} else if (delta > 0) {
new_special = item.data1[4] + 1;
} else {
new_special = item.data1[4];
}
if (new_special != item.data1[4]) {
int8_t delta = sample_prob_table(favored
? this->tekker_adjustment_set->favored_special_delta_table[section_id]
: this->tekker_adjustment_set->default_special_delta_table[section_id]);
this->log.info_f("(Special) Delta {} chosen", delta);
for (; delta != 0; delta += (delta < 0) - (0 < delta)) {
try {
// Note: The original code checks specifically for -1 and +1 here and only increments or decrements the special
// by 1, and the data files only include delta_indexes 4, 5, and 6 (which correspond to -1, 0, and 1). But we
// want to support other levels of delta indexes, so we simply add delta instead. When using the original
// JudgeItem.rel file, the behavior should be the same, but this logic feels more correct.
uint8_t new_special = item.data1[4] + delta;
if (this->item_parameter_table->get_special(item.data1[4]).type ==
this->item_parameter_table->get_special(new_special).type) {
item.data1[4] = new_special;
this->log.info_f("(Special) Delta {} applied", delta);
break;
} else {
this->log.info_f("(Special) Delta canceled because it would change special category");
this->log.info_f("(Special) Delta {} canceled because it would change special category", delta);
}
} catch (const std::out_of_range&) {
// Invalid special number passed to get_special; treat it as if delta == 0
}
} catch (const std::out_of_range&) {
// Invalid special number passed to get_special; just ignore it
}
luck += this->tekker_adjustment_set->get_luck_for_special_upgrade(delta_index);
luck += this->tekker_adjustment_set->special_luck_table.at(delta);
this->log.info_f("(Special) Luck is now {}", luck);
}
// Adjust the weapon's grind if it's not rare
if (!this->item_parameter_table->is_item_rare(item)) {
const auto& weapon_def = this->item_parameter_table->get_weapon(item.data1[1], item.data1[2]);
const auto& prob_table = this->tekker_adjustment_set->get_grind_delta_prob_table(section_id, favored);
uint8_t delta_index = prob_table.sample(this->rand_crypt);
int8_t delta = delta_table.at(delta_index);
this->log.info_f("(Grind) Delta index {}, delta {}", delta_index, delta);
int8_t delta = sample_prob_table(favored
? this->tekker_adjustment_set->favored_grind_delta_table[section_id]
: this->tekker_adjustment_set->default_grind_delta_table[section_id]);
this->log.info_f("(Grind) Delta {} chosen", delta);
int16_t new_grind = static_cast<int16_t>(item.data1[3]) + static_cast<int16_t>(delta);
item.data1[3] = std::clamp<int16_t>(new_grind, 0, weapon_def.max_grind);
luck += this->tekker_adjustment_set->get_luck_for_grind_delta(delta_index);
luck += this->tekker_adjustment_set->grind_luck_table.at(delta);
this->log.info_f("(Grind) Luck is now {}", luck);
} else {
this->log.info_f("(Grind) Item is rare; skipping grind adjustment");
@@ -1781,11 +1673,10 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
// Adjust the weapon's bonuses
{
const auto& prob_table = this->tekker_adjustment_set->get_bonus_delta_prob_table(section_id, favored);
// Note: The original code really does use the same delta for all three bonuses.
uint8_t delta_index = prob_table.sample(this->rand_crypt);
int8_t delta = delta_table.at(delta_index);
this->log.info_f("(Bonuses) Delta index {}, delta {}", delta_index, delta);
int8_t delta = sample_prob_table(favored
? this->tekker_adjustment_set->favored_bonus_delta_table[section_id]
: this->tekker_adjustment_set->default_bonus_delta_table[section_id]);
this->log.info_f("(Bonuses) Delta {} chosen", delta);
// Note: The original code doesn't check if there's actually a bonus in each slot before incrementing the values.
// Presumably there's a check later that will clear any invalid bonuses, but we don't have such a check, so we need
// to check here if each bonus is actually present.
@@ -1794,7 +1685,7 @@ ssize_t ItemCreator::apply_tekker_deltas(ItemData& item, uint8_t section_id) {
item.data1[z + 1] = std::min<int8_t>(item.data1[z + 1] + delta, 100);
}
}
luck += this->tekker_adjustment_set->get_luck_for_bonus_delta(delta_index);
luck += this->tekker_adjustment_set->bonus_luck_table.at(delta);
this->log.info_f("(Bonuses) Luck is now {}", luck);
}
+8 -6
View File
@@ -7,7 +7,9 @@
#include "PSOEncryption.hh"
#include "PlayerSubordinates.hh"
#include "RareItemSet.hh"
#include "ShopRandomSets.hh"
#include "StaticGameData.hh"
#include "TekkerAdjustmentSet.hh"
// This file and ItemCreator.cc are essentially a direct reverse-engineering of the item creation algorithm in PSO GC.
// Only minor changes have been made to support BB (as described in the comments in the implementation) and to support
@@ -19,9 +21,9 @@ public:
ItemCreator(
std::shared_ptr<const CommonItemSet> common_item_set,
std::shared_ptr<const RareItemSet> rare_item_set,
std::shared_ptr<const ArmorRandomSet> armor_random_set,
std::shared_ptr<const ToolRandomSet> tool_random_set,
std::shared_ptr<const WeaponRandomSet> weapon_random_set,
std::shared_ptr<const ArmorShopRandomSet> armor_random_set,
std::shared_ptr<const ToolShopRandomSet> tool_random_set,
std::shared_ptr<const WeaponShopRandomSet> weapon_random_set,
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const ItemData::StackLimits> stack_limits,
@@ -84,9 +86,9 @@ private:
Difficulty difficulty;
uint8_t section_id;
std::shared_ptr<const RareItemSet> rare_item_set;
std::shared_ptr<const ArmorRandomSet> armor_random_set;
std::shared_ptr<const ToolRandomSet> tool_random_set;
std::shared_ptr<const WeaponRandomSet> weapon_random_set;
std::shared_ptr<const ArmorShopRandomSet> armor_random_set;
std::shared_ptr<const ToolShopRandomSet> tool_random_set;
std::shared_ptr<const WeaponShopRandomSet> weapon_random_set;
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
std::shared_ptr<const ItemParameterTable> item_parameter_table;
std::shared_ptr<const CommonItemSet> common_item_set;
+20
View File
@@ -142,6 +142,26 @@ public:
uint16_t feed_table = 0;
uint8_t photon_blast = 0;
uint8_t activation = 0;
// Internally, when a mag effect is about to activate, the game computes a byte with the following flags:
// 01 = HP low (causes effect in mag's on_low_hp if eligible, priority 2)
// 02 = died (causes effect in mag's on_death if eligible, priority 3)
// 04 = synchro < 30 (causes effect 5, priority 7)
// 08 = synchro > 100 (causes effect 4, priority 8)
// 10 = TODO (3OE1:80112B24) (causes effect 3, priority 6)
// 20 = player leveled up (causes effect 2, priority 5)
// 40 = PB meter filled (causes effect in mag's on_pb_full if eligible, priority 1)
// 80 = entered boss arena (causes effect in mag's on_boss if eligible, priority 4)
// Values for on_* trigger fields:
// 0 = no effect
// 1 = TODO (used internally; not synced via 6x61; possibly just cancel previous effect?)
// 2 = TODO (used internally; seems to be effect 1, but delayed by 45 frames?)
// 3 = TODO (used internally; seems to be effect 1, but delayed by 90 frames?)
// 4 = Shifta + Deband (level = (IQ / 40) + 1; delayed by 90 frames)
// 5 = TODO (used internally; seems to be effect 1, but delayed by 90 frames?)
// 6 = Resta (not synced via 6x61; level = (IQ / 40) + 1; delayed by 90 frames)
// 7 = Reverser (not synced via 6x61; delayed by 90 frames)
// 8 = invincibility (duration = (((IQ + synchro) / 3) + 40) * 30 frames, but this is capped at 30 seconds, so in
// practice it's always just 30 seconds regardless of IQ and synchro)
uint8_t on_pb_full = 0;
uint8_t on_low_hp = 0;
uint8_t on_death = 0;
+4 -4
View File
@@ -131,7 +131,7 @@ void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_p
} else if ((primary_identifier & 0xFFFF0000) == 0x030C0000) { // Non-combo mag cells
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
uint8_t evolution_number = s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]);
uint8_t evolution_number = s->mag_metadata_table(c->version())->get_evolution_number(mag.data.data1[1]);
if (evolution_number < 4) {
switch (item.data.data1[2]) {
case 0x00: // Cell of MAG 502
@@ -268,7 +268,7 @@ void apply_mag_feed_result(
ItemData& mag_item,
const ItemData& fed_item,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const MagEvolutionTable> mag_evolution_table,
std::shared_ptr<const MagMetadataTable> mag_metadata_table,
uint8_t char_class,
uint8_t section_id,
bool version_has_rare_mags) {
@@ -314,7 +314,7 @@ void apply_mag_feed_result(
uint8_t mag_level = mag_item.compute_mag_level();
mag_item.data1[2] = mag_level;
uint8_t evolution_number = mag_evolution_table->get_evolution_number(mag_item.data1[1]);
uint8_t evolution_number = mag_metadata_table->get_evolution_number(mag_item.data1[1]);
uint8_t mag_number = mag_item.data1[1];
// Note: Sega really did just hardcode all these rules into the client. There is no data file describing these
@@ -489,7 +489,7 @@ void player_feed_mag(std::shared_ptr<Client> c, size_t mag_item_index, size_t fe
player->inventory.items[mag_item_index].data,
player->inventory.items[fed_item_index].data,
s->item_parameter_table(c->version()),
s->mag_evolution_table(c->version()),
s->mag_metadata_table(c->version()),
player->disp.visual.sh.char_class,
player->disp.visual.sh.section_id,
!is_v1_or_v2(c->version()));
+1 -1
View File
@@ -19,7 +19,7 @@ void apply_mag_feed_result(
ItemData& mag_item,
const ItemData& fed_item,
std::shared_ptr<const ItemParameterTable> item_parameter_table,
std::shared_ptr<const MagEvolutionTable> mag_evolution_table,
std::shared_ptr<const MagMetadataTable> mag_metadata_table,
uint8_t char_class,
uint8_t section_id,
bool version_has_rare_mags);
-306
View File
@@ -1,306 +0,0 @@
#include "MagEvolutionTable.hh"
#include "CommonFileFormats.hh"
template <bool BE>
struct MotionReferenceTables {
// It seems that there are two definition tables, but only the first is used on any version of PSO. On v3 and later,
// the two offsets point to the same table, but on v2 they don't and the second table contains different data.
// TODO: Figure out what the deal is with the different v2 tables.
U32T<BE> ref_table;
U32T<BE> unused_ref_table;
} __packed_ws_be__(MotionReferenceTables, 0x08);
template <bool BE>
struct ColorEntry {
// Colors are specified as 4 floats, each in the range [0, 1], for each color channel. The default colors are:
// alpha red green blue color (see StaticGameData.cc)
// 00 => 1.0 1.0 0.2 0.1 red
// 01 => 1.0 0.2 0.2 1.0 blue
// 02 => 1.0 1.0 0.9 0.1 yellow
// 03 => 1.0 0.1 1.0 0.1 green
// 04 => 1.0 0.8 0.1 1.0 purple
// 05 => 1.0 0.1 0.1 0.2 black
// 06 => 1.0 0.9 1.0 1.0 white
// 07 => 1.0 0.1 0.9 1.0 cyan
// 08 => 1.0 0.5 0.3 0.2 brown
// 09 => 1.0 1.0 0.4 0.0 orange (v3+)
// 0A => 1.0 0.502 0.545 0.977 light-blue (v3+)
// 0B => 1.0 0.502 0.502 0.0 olive (v3+)
// 0C => 1.0 0.0 0.941 0.714 turquoise (v3+)
// 0D => 1.0 0.8 0.098 0.392 fuchsia (v3+)
// 0E => 1.0 0.498 0.498 0.498 grey (v3+)
// 0F => 1.0 0.996 0.996 0.832 cream (v3+)
// 10 => 1.0 0.996 0.498 0.784 pink (v3+)
// 11 => 1.0 0.0 0.498 0.322 dark-green (v3+)
// If a mag's color index is invalid (>= 0x12), it is reassigned at equip time using the following logic:
// - Set base_index to player->visual.skin if player is an android, or player->visual.costume otherwise
// - If (base_index % 9) < 7 (that is, if their costume or body color is one of the colored slots on the character
// creation screen), then set the mag color to either (base_index % 9) or (base_index % 9) + 9, with equal
// probability.
// - If (base_index % 9) >= 7 (that is, if their costume or body color is one of the last two blank-colored slots
// on the character creation screen), then set the mag color to any of the available colors, chosen at random.
F32T<BE> alpha;
F32T<BE> red;
F32T<BE> green;
F32T<BE> blue;
ColorEntry(const VectorXYZTF& c) : alpha(c.t), red(c.x), green(c.y), blue(c.z) {}
operator VectorXYZTF() const {
return VectorXYZTF{this->red.load(), this->green.load(), this->blue.load(), this->alpha.load()};
}
} __packed_ws_be__(ColorEntry, 0x10);
template <bool BE>
struct UnknownA3EntryT {
uint8_t flags;
uint8_t unknown_a2;
U16T<BE> unknown_a3;
U16T<BE> unknown_a4;
U16T<BE> unknown_a5;
UnknownA3EntryT(const MagEvolutionTable::UnknownA3Entry& e)
: flags(e.flags),
unknown_a2(e.unknown_a2),
unknown_a3(e.unknown_a3),
unknown_a4(e.unknown_a4),
unknown_a5(e.unknown_a5) {}
operator MagEvolutionTable::UnknownA3Entry() const {
return MagEvolutionTable::UnknownA3Entry{
this->flags, this->unknown_a2, this->unknown_a3, this->unknown_a4, this->unknown_a5};
}
} __packed_ws_be__(UnknownA3EntryT, 0x08);
struct HeaderV1 {
parray<uint8_t, 4> unknown_a1 = {0x0F, 0xF0, 0x00, 0x00};
le_uint32_t unknown_a2 = 0x00000003;
le_uint16_t unknown_a3 = 0x00C8;
le_uint16_t unknown_a4 = 0x0078;
// unknown_a5 added in V2
le_float unknown_a6 = 0.25;
le_float unknown_a7 = 0.1;
le_uint32_t unknown_a8 = 0x00000C00;
} __packed_ws__(HeaderV1, 0x18);
template <bool BE>
struct HeaderV2V3V4 {
parray<uint8_t, 4> unknown_a1 = {0x0F, 0xF0, 0x00, 0x00};
U32T<BE> unknown_a2 = 0x00000003;
U16T<BE> unknown_a3 = 0x00C8;
U16T<BE> unknown_a4 = 0x0078;
parray<uint8_t, 4> unknown_a5 = {0xC8, 0x00, 0x00, 0x00};
F32T<BE> unknown_a6 = 0.25;
F32T<BE> unknown_a7 = 0.1;
U32T<BE> unknown_a8 = 0x00000C00;
} __packed_ws_be__(HeaderV2V3V4, 0x1C);
// Fields:
// 112K / V1 / V2 / V3 / BB R
// 0018 / 0018 / 001C / 001C / 001C motion_tables.ref_table // -> MotionReference[NumMags]
// 0228 / 0228 / 02D4 / 001C / 001C motion_tables.unused_ref_table // -> MotionReference[NumMags]
// 0438 / 0438 / 05BC / 0340 / 0400 * motion_tables; // -> MotionReferenceTables
// 0440 / 0440 / 0594 / 0348 / 0408 * unknown_a2; // -> (uint8_t[2])[NumMags] (references into unknown_a3)
// 0498 / 0498 / 0608 / 03CE / 04AE * unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1]
// 0510 / 0520 / 06B0 / 0476 / 0556 * unknown_a4; // -> uint8_t[NumMags]
// 053C / 054C / 06EC / 04BC / 05AC * color_table; // -> ColorEntry[NumColors]
// ---- / ---- / 077C / 05DC / 06CC * evolution_number_table; // -> uint8_t[NumMags]
template <bool BE>
struct RootV1 {
U32T<BE> motion_tables;
U32T<BE> unknown_a2;
U32T<BE> unknown_a3;
U32T<BE> unknown_a4;
U32T<BE> color_table;
} __packed_ws_be__(RootV1, 0x14);
template <bool BE>
struct RootV2V3V4 : RootV1<BE> {
U32T<BE> evolution_number_table;
} __packed_ws_be__(RootV2V3V4, 0x18);
static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) {
static const std::array<uint8_t, 0x2C> v1_evolution_number_table{
/* 00 */ 0, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2,
/* 10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 3, 4, 3, 3,
/* 20 */ 3, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4};
if (data1_1 >= v1_evolution_number_table.size()) {
throw std::runtime_error("invalid mag number");
}
return v1_evolution_number_table[data1_1];
}
template <typename HeaderT, typename RootT, size_t NumMags, size_t NumColors, bool BE>
class BinaryMagEvolutionTableT : public MagEvolutionTable {
public:
explicit BinaryMagEvolutionTableT(std::shared_ptr<const std::string> data)
: data(data), r(*data), root(&r.pget<RootT>(this->r.pget_u32l(this->data->size() - 0x10))) {}
virtual ~BinaryMagEvolutionTableT() = default;
template <typename RawT, typename ParsedT>
const ParsedT& add_to_vector_cache(std::vector<ParsedT>& cache, size_t base_offset, size_t index) const {
while (cache.size() <= index) {
cache.emplace_back(this->r.pget<RawT>(base_offset + sizeof(RawT) * cache.size()));
}
return cache[index];
}
virtual size_t num_mags() const {
return NumMags;
}
virtual size_t num_motion_entries(bool use_second_table) const {
const auto& tables = this->r.pget<MotionReferenceTables<BE>>(this->root->motion_tables);
return get_rel_array_count<MotionReference>(
this->all_start_offsets(), use_second_table ? tables.unused_ref_table : tables.ref_table);
}
virtual const MotionReference& get_motion_reference(bool use_second_table, size_t index) const {
if (index >= this->num_motion_entries(use_second_table)) {
throw std::logic_error("Invalid motion reference index");
}
const auto& tables = this->r.pget<MotionReferenceTables<BE>>(this->root->motion_tables);
uint32_t array_offset = use_second_table ? tables.unused_ref_table : tables.ref_table;
return this->r.pget<MotionReference>(array_offset + sizeof(MotionReference) * index);
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t index) const {
if (index >= this->num_mags()) {
throw std::logic_error("Invalid unknown_a2 index");
}
uint32_t base_offset = this->root->unknown_a2 + (index * 2);
return std::make_pair(this->r.pget_u8(base_offset), this->r.pget_u8(base_offset + 1));
}
virtual size_t num_unknown_a3_entries() const {
return get_rel_array_count<UnknownA3EntryT<BE>>(this->all_start_offsets(), this->root->unknown_a3);
}
virtual const UnknownA3Entry& get_unknown_a3(size_t index) const {
if (index >= this->num_unknown_a3_entries()) {
throw std::logic_error("Invalid unknown_a2 index");
}
return this->add_to_vector_cache<UnknownA3EntryT<BE>>(this->unknown_a3_entries, this->root->unknown_a3, index);
}
virtual uint8_t get_unknown_a4(size_t index) const {
if (index >= this->num_mags()) {
throw std::logic_error("Invalid unknown_a4 index");
}
return this->r.pget_u8(this->root->unknown_a2 + index);
}
virtual size_t num_colors() const {
return NumColors;
}
virtual const VectorXYZTF& get_color_rgba(size_t index) const {
if (index >= NumColors) {
throw std::runtime_error("invalid mag color index");
}
return this->add_to_vector_cache<ColorEntry<BE>>(this->colors, this->root->color_table, index);
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
if (data1_1 >= this->num_mags()) {
throw std::logic_error("Invalid unknown_a4 index");
}
if constexpr (requires { this->root->evolution_number_table; }) {
return this->r.pget_u8(this->root->evolution_number_table + data1_1);
} else {
return get_v1_mag_evolution_number(data1_1);
}
}
const std::set<uint32_t>& all_start_offsets() const {
if (this->start_offsets.empty()) {
this->start_offsets = all_relocation_offsets_for_rel_file<BE>(r.pgetv(0, r.size()), r.size());
}
return this->start_offsets;
}
protected:
std::shared_ptr<const std::string> data;
phosg::StringReader r;
const RootT* root;
mutable std::set<uint32_t> start_offsets;
mutable std::vector<UnknownA3Entry> unknown_a3_entries;
mutable std::vector<VectorXYZTF> colors;
};
class MagEvolutionTableDCNTE : public MagEvolutionTable {
public:
MagEvolutionTableDCNTE() = default;
virtual ~MagEvolutionTableDCNTE() = default;
virtual size_t num_mags() const {
return 0x2C;
}
virtual size_t num_motion_entries(bool) const {
return 0;
}
virtual const MotionReference& get_motion_reference(bool, size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_unknown_a3_entries() const {
return 0;
}
virtual const UnknownA3Entry& get_unknown_a3(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_unknown_a4(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_colors() const {
return 0;
}
virtual const VectorXYZTF& get_color_rgba(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
return get_v1_mag_evolution_number(data1_1);
}
};
using MagEvolutionTableDC112000 = BinaryMagEvolutionTableT<HeaderV1, RootV1<false>, 0x28, 0x09, false>;
using MagEvolutionTableV1 = BinaryMagEvolutionTableT<HeaderV1, RootV1<false>, 0x28, 0x09, false>;
using MagEvolutionTableV2 = BinaryMagEvolutionTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x3A, 0x09, false>;
using MagEvolutionTableGCNTE = BinaryMagEvolutionTableT<HeaderV2V3V4<true>, RootV2V3V4<true>, 0x3A, 0x09, true>;
using MagEvolutionTableGC = BinaryMagEvolutionTableT<HeaderV2V3V4<true>, RootV2V3V4<true>, 0x43, 0x12, true>;
using MagEvolutionTableXB = BinaryMagEvolutionTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x43, 0x12, false>;
using MagEvolutionTableV4 = BinaryMagEvolutionTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x53, 0x12, false>;
std::shared_ptr<MagEvolutionTable> MagEvolutionTable::create(
std::shared_ptr<const std::string> data, Version version) {
switch (version) {
case Version::DC_NTE:
return std::make_shared<MagEvolutionTableDCNTE>();
case Version::DC_11_2000:
return std::make_shared<MagEvolutionTableDC112000>(data);
case Version::DC_V1:
return std::make_shared<MagEvolutionTableV1>(data);
case Version::DC_V2:
case Version::PC_NTE:
case Version::PC_V2:
return std::make_shared<MagEvolutionTableV2>(data);
case Version::GC_NTE:
return std::make_shared<MagEvolutionTableGCNTE>(data);
case Version::GC_V3:
case Version::GC_EP3:
case Version::GC_EP3_NTE:
return std::make_shared<MagEvolutionTableGC>(data);
case Version::XB_V3:
return std::make_shared<MagEvolutionTableXB>(data);
case Version::BB_V4:
return std::make_shared<MagEvolutionTableV4>(data);
default:
throw std::logic_error("Cannot create mag evolution table for this version");
}
}
-58
View File
@@ -1,58 +0,0 @@
#pragma once
#include "WindowsPlatform.hh"
#include <stdint.h>
#include <memory>
#include <string>
#include "CommonFileFormats.hh"
#include "Text.hh"
#include "Types.hh"
#include "Version.hh"
class MagEvolutionTable {
public:
struct MotionReference {
struct Side {
// This specifies which entry in ItemMagMotion.dat is used. The file is just a list of 0x64-byte structures.
// 0xFF = no TItemMagSub is created
uint8_t motion_table_entry = 0xFF;
parray<uint8_t, 5> unknown_a1 = 0;
} __packed_ws__(Side, 0x06);
parray<Side, 2> sides; // [0] = right side, [1] = left side
} __packed_ws__(MotionReference, 0x0C);
struct UnknownA3Entry {
uint8_t flags;
uint8_t unknown_a2;
uint16_t unknown_a3;
uint16_t unknown_a4;
uint16_t unknown_a5;
};
virtual ~MagEvolutionTable() = default;
static std::shared_ptr<MagEvolutionTable> create(std::shared_ptr<const std::string> data, Version version);
virtual size_t num_mags() const = 0;
virtual size_t num_motion_entries(bool use_second_table) const = 0;
virtual const MotionReference& get_motion_reference(bool use_second_table, size_t index) const = 0;
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t index) const = 0;
virtual size_t num_unknown_a3_entries() const = 0;
virtual const UnknownA3Entry& get_unknown_a3(size_t index) const = 0;
virtual uint8_t get_unknown_a4(size_t index) const = 0;
virtual size_t num_colors() const = 0;
virtual const VectorXYZTF& get_color_rgba(size_t index) const = 0;
virtual uint8_t get_evolution_number(uint8_t data1_1) const = 0;
protected:
MagEvolutionTable() = default;
};
+580
View File
@@ -0,0 +1,580 @@
#include "MagMetadataTable.hh"
#include "CommonFileFormats.hh"
MagMetadataTable::MotionReferences MagMetadataTable::MotionReferences::from_json(const phosg::JSON& json) {
auto parse_side = [](Side& side, const phosg::JSON& side_json) -> void {
side.eff_1 = side_json.at("Eff1").as_int();
side.eff_2 = side_json.at("Eff2").as_int();
side.eff_3 = side_json.at("Eff3").as_int();
side.eff_4_8 = side_json.at("Eff48").as_int();
side.eff_5 = side_json.at("Eff5").as_int();
side.eff_6_7 = side_json.at("Eff67").as_int();
};
MagMetadataTable::MotionReferences ret;
parse_side(ret.sides[0], json.at("Left"));
parse_side(ret.sides[1], json.at("Right"));
return ret;
}
phosg::JSON MagMetadataTable::MotionReferences::json() const {
auto serialize_side = [](const Side& side) -> phosg::JSON {
return phosg::JSON::dict({
{"Eff1", side.eff_1},
{"Eff2", side.eff_2},
{"Eff3", side.eff_3},
{"Eff48", side.eff_4_8},
{"Eff5", side.eff_5},
{"Eff67", side.eff_6_7},
});
};
return phosg::JSON::dict({{"Left", serialize_side(this->sides[0])}, {"Right", serialize_side(this->sides[1])}});
}
MagMetadataTable::UnknownA3Entry MagMetadataTable::UnknownA3Entry::from_json(const phosg::JSON& json) {
return UnknownA3Entry{
.flags = static_cast<uint8_t>(json.get_int("Flags")),
.unknown_a2 = static_cast<uint8_t>(json.get_int("UnknownA2")),
.unknown_a3 = static_cast<int16_t>(json.get_int("UnknownA3")),
.unknown_a4 = static_cast<int16_t>(json.get_int("UnknownA4")),
.unknown_a5 = static_cast<int16_t>(json.get_int("UnknownA5")),
};
}
phosg::JSON MagMetadataTable::UnknownA3Entry::json() const {
return phosg::JSON::dict({
{"Flags", this->flags},
{"UnknownA2", this->unknown_a2},
{"UnknownA3", this->unknown_a3},
{"UnknownA4", this->unknown_a4},
{"UnknownA5", this->unknown_a5},
});
}
static VectorXYZTF color_for_json(const phosg::JSON& json) {
return VectorXYZTF{
.x = json.get_float(0),
.y = json.get_float(1),
.z = json.get_float(2),
.t = json.get_float(3),
};
}
static phosg::JSON json_for_color(const VectorXYZTF& color) {
return phosg::JSON::list({color.x.load(), color.y.load(), color.z.load(), color.t.load()});
}
template <bool BE>
struct MotionReferenceTables {
// It seems that there are two definition tables, but only the first is used on any version of PSO. On v3 and later,
// the two offsets point to the same table, but on v2 they don't and the second table contains different data.
// TODO: Figure out what the deal is with the different v2 tables.
U32T<BE> first_ref_table;
U32T<BE> second_ref_table;
} __packed_ws_be__(MotionReferenceTables, 0x08);
template <bool BE>
struct ColorEntry {
// Colors are specified as 4 floats, each in the range [0, 1], for each color channel. The default colors are:
// alpha red green blue color (see StaticGameData.cc)
// 00 => 1.0 1.0 0.2 0.1 red
// 01 => 1.0 0.2 0.2 1.0 blue
// 02 => 1.0 1.0 0.9 0.1 yellow
// 03 => 1.0 0.1 1.0 0.1 green
// 04 => 1.0 0.8 0.1 1.0 purple
// 05 => 1.0 0.1 0.1 0.2 black
// 06 => 1.0 0.9 1.0 1.0 white
// 07 => 1.0 0.1 0.9 1.0 cyan
// 08 => 1.0 0.5 0.3 0.2 brown
// 09 => 1.0 1.0 0.4 0.0 orange (v3+)
// 0A => 1.0 0.502 0.545 0.977 light-blue (v3+)
// 0B => 1.0 0.502 0.502 0.0 olive (v3+)
// 0C => 1.0 0.0 0.941 0.714 turquoise (v3+)
// 0D => 1.0 0.8 0.098 0.392 fuchsia (v3+)
// 0E => 1.0 0.498 0.498 0.498 grey (v3+)
// 0F => 1.0 0.996 0.996 0.832 cream (v3+)
// 10 => 1.0 0.996 0.498 0.784 pink (v3+)
// 11 => 1.0 0.0 0.498 0.322 dark-green (v3+)
// If a mag's color index is invalid (>= 0x12), it is reassigned at equip time using the following logic:
// - Set base_index to player->visual.sh.skin if player is an android, or player->visual.costume otherwise
// - If (base_index % 9) < 7 (that is, if their costume or body color is one of the colored slots on the character
// creation screen), then set the mag color to either (base_index % 9) or (base_index % 9) + 9, with equal
// probability.
// - If (base_index % 9) >= 7 (that is, if their costume or body color is one of the last two blank-colored slots
// on the character creation screen), then set the mag color to any of the available colors, chosen at random.
F32T<BE> alpha;
F32T<BE> red;
F32T<BE> green;
F32T<BE> blue;
ColorEntry(const VectorXYZTF& c) : alpha(c.t), red(c.x), green(c.y), blue(c.z) {}
operator VectorXYZTF() const {
return VectorXYZTF{this->red.load(), this->green.load(), this->blue.load(), this->alpha.load()};
}
} __packed_ws_be__(ColorEntry, 0x10);
template <bool BE>
struct UnknownA3EntryT {
uint8_t flags;
uint8_t unknown_a2;
S16T<BE> unknown_a3;
S16T<BE> unknown_a4;
S16T<BE> unknown_a5;
UnknownA3EntryT(const MagMetadataTable::UnknownA3Entry& e)
: flags(e.flags),
unknown_a2(e.unknown_a2),
unknown_a3(e.unknown_a3),
unknown_a4(e.unknown_a4),
unknown_a5(e.unknown_a5) {}
operator MagMetadataTable::UnknownA3Entry() const {
return MagMetadataTable::UnknownA3Entry{
this->flags, this->unknown_a2, this->unknown_a3, this->unknown_a4, this->unknown_a5};
}
} __packed_ws_be__(UnknownA3EntryT, 0x08);
class JSONMagMetadataTable : public MagMetadataTable {
public:
explicit JSONMagMetadataTable(const phosg::JSON& json) {
for (const auto& mag_json : json.at("Mags").as_list()) {
const auto& unknown_a2_json = mag_json->at("UnknownA2").as_list();
auto& mag = this->mags.emplace_back();
mag.first_motion_refs = MotionReferences::from_json(mag_json->at("MotionRefs1"));
mag.second_motion_refs = MotionReferences::from_json(mag_json->at("MotionRefs2"));
mag.unknown_a2 = std::make_pair(unknown_a2_json.at(0)->as_int(), unknown_a2_json.at(1)->as_int());
mag.render_flags = mag_json->at("RenderFlags").as_int();
mag.evolution_number = mag_json->at("EvolutionNumber").as_int();
}
for (const auto& a3_json : json.at("UnknownA3").as_list()) {
this->unknown_a3.emplace_back(UnknownA3Entry::from_json(*a3_json));
}
for (const auto& color_json : json.at("Colors").as_list()) {
this->colors.emplace_back(color_for_json(*color_json));
}
}
virtual ~JSONMagMetadataTable() = default;
virtual size_t num_mags() const {
return this->mags.size();
}
virtual size_t num_motion_entries(bool) const {
return this->mags.size();
}
virtual const MotionReferences& get_motion_references(bool use_second_table, size_t data1_1) const {
const auto& mag = this->mags.at(data1_1);
return use_second_table ? mag.second_motion_refs : mag.first_motion_refs;
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t data1_1) const {
return this->mags.at(data1_1).unknown_a2;
}
virtual size_t num_unknown_a3_entries() const {
return this->unknown_a3.size();
}
virtual const UnknownA3Entry& get_unknown_a3(size_t index) const {
return this->unknown_a3.at(index);
}
virtual uint8_t get_render_flags(size_t data1_1) const {
return this->mags.at(data1_1).render_flags;
}
virtual size_t num_colors() const {
return this->colors.size();
}
virtual const VectorXYZTF& get_color_rgba(size_t index) const {
return this->colors.at(index);
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
return this->mags.at(data1_1).evolution_number;
}
protected:
struct Mag {
MotionReferences first_motion_refs;
MotionReferences second_motion_refs;
std::pair<uint8_t, uint8_t> unknown_a2 = std::make_pair(0, 0);
uint8_t render_flags = 0;
uint8_t evolution_number = 0;
};
std::vector<Mag> mags;
std::vector<UnknownA3Entry> unknown_a3;
std::vector<VectorXYZTF> colors;
};
struct HeaderV1 {
parray<uint8_t, 4> unknown_a1 = {0x0F, 0xF0, 0x00, 0x00};
le_uint32_t unknown_a2 = 0x00000003;
le_uint16_t unknown_a3 = 0x00C8;
le_uint16_t unknown_a4 = 0x0078;
// unknown_a5 added in V2
le_float unknown_a6 = 0.25; // 3E800000
le_float unknown_a7 = 0.099999994; // 3DCCCCCC
le_uint32_t unknown_a8 = 0x00000C00;
} __packed_ws__(HeaderV1, 0x18);
template <bool BE>
struct HeaderV2V3V4 {
parray<uint8_t, 4> unknown_a1 = {0x0F, 0xF0, 0x00, 0x00};
U32T<BE> unknown_a2 = 0x00000003;
U16T<BE> unknown_a3 = 0x00C8;
U16T<BE> unknown_a4 = 0x0078;
parray<uint8_t, 4> unknown_a5 = {0xC8, 0x00, 0x00, 0x00};
F32T<BE> unknown_a6 = 0.25; // 3E800000
F32T<BE> unknown_a7 = 0.099999994; // 3DCCCCCC
U32T<BE> unknown_a8 = 0x00000C00;
} __packed_ws_be__(HeaderV2V3V4, 0x1C);
// Fields:
// 112K / V1 / V2 / V3 / BB R
// 0018 / 0018 / 001C / 001C / 001C motion_tables.first_ref_table // -> MotionReferences[NumMags]
// 0228 / 0228 / 02D4 / 001C / 001C motion_tables.second_ref_table // -> MotionReferences[NumMags]
// 0438 / 0438 / 05BC / 0340 / 0400 * motion_tables; // -> MotionReferenceTables
// 0440 / 0440 / 0594 / 0348 / 0408 * unknown_a2; // -> (uint8_t[2])[NumMags] (references into unknown_a3)
// 0498 / 0498 / 0608 / 03CE / 04AE * unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1]
// 0510 / 0520 / 06B0 / 0476 / 0556 * render_flags; // -> uint8_t[NumMags]
// 053C / 054C / 06EC / 04BC / 05AC * color_table; // -> ColorEntry[NumColors]
// ---- / ---- / 077C / 05DC / 06CC * evolution_number_table; // -> uint8_t[NumMags]
template <bool BE>
struct RootV1 {
U32T<BE> motion_tables;
U32T<BE> unknown_a2;
U32T<BE> unknown_a3;
U32T<BE> render_flags;
U32T<BE> color_table;
} __packed_ws_be__(RootV1, 0x14);
template <bool BE>
struct RootV2V3V4 : RootV1<BE> {
U32T<BE> evolution_number_table;
} __packed_ws_be__(RootV2V3V4, 0x18);
static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) {
static const std::array<uint8_t, 0x2C> v1_evolution_number_table{
/* 00 */ 0, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2,
/* 10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 3, 4, 3, 3,
/* 20 */ 3, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4};
if (data1_1 >= v1_evolution_number_table.size()) {
throw std::runtime_error("invalid mag number");
}
return v1_evolution_number_table[data1_1];
}
template <typename HeaderT, typename RootT, size_t NumMags, size_t NumColors, bool BE>
class BinaryMagMetadataTableT : public MagMetadataTable {
public:
explicit BinaryMagMetadataTableT(std::shared_ptr<const std::string> data)
: data(data), r(*data), root(&r.pget<RootT>(this->r.pget<U32T<BE>>(this->data->size() - 0x10))) {}
virtual ~BinaryMagMetadataTableT() = default;
template <typename RawT, typename ParsedT>
const ParsedT& add_to_vector_cache(std::vector<ParsedT>& cache, size_t base_offset, size_t index) const {
while (cache.size() <= index) {
cache.emplace_back(this->r.pget<RawT>(base_offset + sizeof(RawT) * cache.size()));
}
return cache[index];
}
virtual size_t num_mags() const {
return NumMags;
}
virtual size_t num_motion_entries(bool use_second_table) const {
const auto& tables = this->r.pget<MotionReferenceTables<BE>>(this->root->motion_tables);
return get_rel_array_count<MotionReferences>(
this->all_start_offsets(), use_second_table ? tables.second_ref_table : tables.first_ref_table);
}
virtual const MotionReferences& get_motion_references(bool use_second_table, size_t index) const {
if (index >= this->num_motion_entries(use_second_table)) {
throw std::logic_error("Invalid motion reference index");
}
const auto& tables = this->r.pget<MotionReferenceTables<BE>>(this->root->motion_tables);
uint32_t array_offset = use_second_table ? tables.second_ref_table : tables.first_ref_table;
return this->r.pget<MotionReferences>(array_offset + sizeof(MotionReferences) * index);
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t index) const {
if (index >= this->num_mags()) {
throw std::logic_error("Invalid unknown_a2 index");
}
uint32_t base_offset = this->root->unknown_a2 + (index * 2);
return std::make_pair(this->r.pget_u8(base_offset), this->r.pget_u8(base_offset + 1));
}
virtual size_t num_unknown_a3_entries() const {
return get_rel_array_count<UnknownA3EntryT<BE>>(this->all_start_offsets(), this->root->unknown_a3);
}
virtual const UnknownA3Entry& get_unknown_a3(size_t index) const {
if (index >= this->num_unknown_a3_entries()) {
throw std::logic_error("Invalid unknown_a2 index");
}
return this->add_to_vector_cache<UnknownA3EntryT<BE>>(this->unknown_a3_entries, this->root->unknown_a3, index);
}
virtual uint8_t get_render_flags(size_t index) const {
if (index >= this->num_mags()) {
throw std::logic_error("Invalid render_flags index");
}
return this->r.pget_u8(this->root->render_flags + index);
}
virtual size_t num_colors() const {
return NumColors;
}
virtual const VectorXYZTF& get_color_rgba(size_t index) const {
if (index >= NumColors) {
throw std::runtime_error("invalid mag color index");
}
return this->add_to_vector_cache<ColorEntry<BE>>(this->colors, this->root->color_table, index);
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
if (data1_1 >= this->num_mags()) {
throw std::logic_error("Invalid evolution_number index");
}
if constexpr (requires { this->root->evolution_number_table; }) {
return this->r.pget_u8(this->root->evolution_number_table + data1_1);
} else {
return get_v1_mag_evolution_number(data1_1);
}
}
const std::set<uint32_t>& all_start_offsets() const {
if (this->start_offsets.empty()) {
this->start_offsets = all_relocation_offsets_for_rel_file<BE>(r.pgetv(0, r.size()), r.size());
}
return this->start_offsets;
}
static std::string serialize(const MagMetadataTable& table) {
RELFileWriter<BE> rel;
RootT root;
rel.template put<HeaderT>(HeaderT());
MotionReferenceTables<BE> motion_ref_tables;
motion_ref_tables.first_ref_table = rel.w.size();
bool alias_motion_ref_tables = true;
for (size_t z = 0; z < table.num_motion_entries(false); z++) {
const auto& refs = table.get_motion_references(false, z);
rel.template put<MotionReferences>(refs);
if (refs != table.get_motion_references(true, z)) {
alias_motion_ref_tables = false;
}
}
if (alias_motion_ref_tables) {
motion_ref_tables.second_ref_table = motion_ref_tables.first_ref_table;
} else {
motion_ref_tables.second_ref_table = rel.w.size();
for (size_t z = 0; z < table.num_motion_entries(true); z++) {
rel.template put<MotionReferences>(table.get_motion_references(true, z));
}
}
root.motion_tables = rel.w.size();
rel.template put<MotionReferenceTables<BE>>(motion_ref_tables);
rel.relocations.emplace(root.motion_tables);
rel.relocations.emplace(root.motion_tables + 4);
root.unknown_a2 = rel.w.size();
for (size_t z = 0; z < table.num_mags(); z++) {
auto [left, right] = table.get_unknown_a2(z);
rel.template put<uint8_t>(left);
rel.template put<uint8_t>(right);
}
root.unknown_a3 = rel.w.size();
for (size_t z = 0; z < table.num_unknown_a3_entries(); z++) {
rel.template put<UnknownA3EntryT<BE>>(table.get_unknown_a3(z));
}
root.render_flags = rel.w.size();
for (size_t z = 0; z < table.num_mags(); z++) {
rel.template put<uint8_t>(table.get_render_flags(z));
}
rel.align(4);
root.color_table = rel.w.size();
for (size_t z = 0; z < table.num_colors(); z++) {
rel.template put<ColorEntry<BE>>(table.get_color_rgba(z));
}
if constexpr (requires { root.evolution_number_table; }) {
root.evolution_number_table = rel.w.size();
for (size_t z = 0; z < table.num_mags(); z++) {
rel.template put<uint8_t>(table.get_evolution_number(z));
}
}
rel.align(4);
uint32_t root_offset = rel.template put<RootT>(root);
for (size_t z = 1; z <= sizeof(RootT) / 4; z++) {
rel.relocations.emplace(rel.w.size() - (z * 4));
}
return rel.finalize(root_offset);
}
protected:
std::shared_ptr<const std::string> data;
phosg::StringReader r;
const RootT* root;
mutable std::set<uint32_t> start_offsets;
mutable std::vector<UnknownA3Entry> unknown_a3_entries;
mutable std::vector<VectorXYZTF> colors;
};
class MagMetadataTableDCNTE : public MagMetadataTable {
public:
MagMetadataTableDCNTE() = default;
virtual ~MagMetadataTableDCNTE() = default;
virtual size_t num_mags() const {
return 0x2C;
}
virtual size_t num_motion_entries(bool) const {
return 0;
}
virtual const MotionReferences& get_motion_references(bool, size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_unknown_a3_entries() const {
return 0;
}
virtual const UnknownA3Entry& get_unknown_a3(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_render_flags(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual size_t num_colors() const {
return 0;
}
virtual const VectorXYZTF& get_color_rgba(size_t) const {
throw std::runtime_error("Mag tables not available on DC NTE");
}
virtual uint8_t get_evolution_number(uint8_t data1_1) const {
return get_v1_mag_evolution_number(data1_1);
}
};
using MagMetadataTableDC112000 = BinaryMagMetadataTableT<HeaderV1, RootV1<false>, 0x2C, 0x09, false>;
using MagMetadataTableV1 = BinaryMagMetadataTableT<HeaderV1, RootV1<false>, 0x2C, 0x09, false>;
using MagMetadataTableV2 = BinaryMagMetadataTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x3A, 0x09, false>;
using MagMetadataTableGCNTE = BinaryMagMetadataTableT<HeaderV2V3V4<true>, RootV2V3V4<true>, 0x3A, 0x09, true>;
using MagMetadataTableGC = BinaryMagMetadataTableT<HeaderV2V3V4<true>, RootV2V3V4<true>, 0x43, 0x12, true>;
using MagMetadataTableXB = BinaryMagMetadataTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x43, 0x12, false>;
using MagMetadataTableV4 = BinaryMagMetadataTableT<HeaderV2V3V4<false>, RootV2V3V4<false>, 0x53, 0x12, false>;
std::shared_ptr<MagMetadataTable> MagMetadataTable::from_binary(
std::shared_ptr<const std::string> data, Version version) {
switch (version) {
case Version::DC_NTE:
return std::make_shared<MagMetadataTableDCNTE>();
case Version::DC_11_2000:
return std::make_shared<MagMetadataTableDC112000>(data);
case Version::DC_V1:
return std::make_shared<MagMetadataTableV1>(data);
case Version::DC_V2:
case Version::PC_NTE:
case Version::PC_V2:
return std::make_shared<MagMetadataTableV2>(data);
case Version::GC_NTE:
return std::make_shared<MagMetadataTableGCNTE>(data);
case Version::GC_V3:
case Version::GC_EP3:
case Version::GC_EP3_NTE:
return std::make_shared<MagMetadataTableGC>(data);
case Version::XB_V3:
return std::make_shared<MagMetadataTableXB>(data);
case Version::BB_V4:
return std::make_shared<MagMetadataTableV4>(data);
default:
throw std::logic_error("Cannot create mag metadata table for this version");
}
}
std::shared_ptr<MagMetadataTable> MagMetadataTable::from_json(const phosg::JSON& json) {
return std::make_shared<JSONMagMetadataTable>(json);
}
phosg::JSON MagMetadataTable::json() const {
if (this->num_motion_entries(true) != this->num_motion_entries(false)) {
throw std::runtime_error("Motion entry counts differ across tables");
}
if (this->num_motion_entries(false) != this->num_mags()) {
throw std::runtime_error(std::format("Motion entry count {} does not match mag count {}",
this->num_motion_entries(false), this->num_mags()));
}
auto mags_json = phosg::JSON::list();
for (size_t z = 0; z < this->num_mags(); z++) {
auto mag_json = phosg::JSON::dict();
mag_json.emplace("MotionRefs1", this->get_motion_references(false, z).json());
mag_json.emplace("MotionRefs2", this->get_motion_references(true, z).json());
auto unknown_a2 = this->get_unknown_a2(z);
mag_json.emplace("UnknownA2", phosg::JSON::list({unknown_a2.first, unknown_a2.second}));
mag_json.emplace("RenderFlags", this->get_render_flags(z));
mag_json.emplace("EvolutionNumber", this->get_evolution_number(z));
mags_json.emplace_back(std::move(mag_json));
}
auto unknown_a3_json = phosg::JSON::list();
for (size_t z = 0; z < this->num_unknown_a3_entries(); z++) {
unknown_a3_json.emplace_back(this->get_unknown_a3(z).json());
}
auto colors_json = phosg::JSON::list();
for (size_t z = 0; z < this->num_colors(); z++) {
colors_json.emplace_back(json_for_color(this->get_color_rgba(z)));
}
return phosg::JSON::dict({
{"Mags", std::move(mags_json)},
{"UnknownA3", std::move(unknown_a3_json)},
{"Colors", std::move(colors_json)},
});
}
std::string MagMetadataTable::serialize_binary(Version version) const {
switch (version) {
case Version::DC_NTE:
throw std::runtime_error("DC NTE does not have a an ItemMagEdit format");
case Version::DC_11_2000:
return MagMetadataTableDC112000::serialize(*this);
case Version::DC_V1:
return MagMetadataTableV1::serialize(*this);
case Version::DC_V2:
case Version::PC_NTE:
case Version::PC_V2:
return MagMetadataTableV2::serialize(*this);
case Version::GC_NTE:
return MagMetadataTableGCNTE::serialize(*this);
case Version::GC_V3:
case Version::GC_EP3:
case Version::GC_EP3_NTE:
return MagMetadataTableGC::serialize(*this);
case Version::XB_V3:
return MagMetadataTableXB::serialize(*this);
case Version::BB_V4:
return MagMetadataTableV4::serialize(*this);
default:
throw std::logic_error("Cannot create item parameter table for this version");
}
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include "WindowsPlatform.hh"
#include <stdint.h>
#include <memory>
#include <string>
#include "CommonFileFormats.hh"
#include "Text.hh"
#include "Types.hh"
#include "Version.hh"
class MagMetadataTable {
public:
struct MotionReferences {
// These entries specify which entry in ItemMagMotion.dat is used. The file is just a list of 0x64-byte structures.
// 0xFF = no TItemMagSub is created
struct Side {
uint8_t eff_1;
uint8_t eff_2;
uint8_t eff_3;
uint8_t eff_4_8;
uint8_t eff_5;
uint8_t eff_6_7;
bool operator==(const Side&) const = default;
bool operator!=(const Side&) const = default;
} __packed_ws__(Side, 6);
parray<Side, 2> sides; // [0] = right side, [1] = left side
bool operator==(const MotionReferences&) const = default;
bool operator!=(const MotionReferences&) const = default;
static MotionReferences from_json(const phosg::JSON& json);
phosg::JSON json() const;
} __packed_ws__(MotionReferences, 0x0C);
struct UnknownA3Entry {
uint8_t flags;
uint8_t unknown_a2;
int16_t unknown_a3;
int16_t unknown_a4;
int16_t unknown_a5;
static UnknownA3Entry from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
virtual ~MagMetadataTable() = default;
virtual size_t num_mags() const = 0;
virtual size_t num_motion_entries(bool use_second_table) const = 0;
virtual const MotionReferences& get_motion_references(bool use_second_table, size_t index) const = 0;
virtual std::pair<uint8_t, uint8_t> get_unknown_a2(size_t index) const = 0;
virtual size_t num_unknown_a3_entries() const = 0;
virtual const UnknownA3Entry& get_unknown_a3(size_t index) const = 0;
virtual uint8_t get_render_flags(size_t index) const = 0;
virtual size_t num_colors() const = 0;
virtual const VectorXYZTF& get_color_rgba(size_t index) const = 0;
virtual uint8_t get_evolution_number(uint8_t data1_1) const = 0;
static std::shared_ptr<MagMetadataTable> from_binary(std::shared_ptr<const std::string> data, Version version);
static std::shared_ptr<MagMetadataTable> from_json(const phosg::JSON& json);
phosg::JSON json() const;
std::string serialize_binary(Version version) const;
protected:
MagMetadataTable() = default;
};
+179 -15
View File
@@ -2396,6 +2396,136 @@ Action a_encode_item_parameter_table(
write_output_data(args, data, nullptr);
});
Action a_decode_mag_metadata_table(
"decode-mag-metadata-table", "\
decode-mag-metadata-table [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\
Converts an ItemMagEdit file into a JSON mag metadata file. A version\n\
option is required. Expects compressed input (a .prs file) by default; use\n\
--decompressed if the input is not compressed.\n",
+[](phosg::Arguments& args) {
auto input_data = read_input_data(args);
if (!args.get<bool>("decompressed")) {
input_data = prs_decompress(input_data);
}
auto data = std::make_shared<std::string>(std::move(input_data));
auto table = MagMetadataTable::from_binary(data, get_cli_version(args, Version::BB_V4));
auto json = table->json();
auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS);
write_output_data(args, serialized, nullptr);
});
Action a_encode_mag_metadata_table(
"encode-mag-metadata-table", "\
encode-mag-metadata-table [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\
Converts a JSON mag metadata file into an ItemMagEdit file compatible with\n\
the game client. A version option is required. By default the output will\n\
be compressed, as the client expects; use --decompressed to get\n\
uncompressed output.\n",
+[](phosg::Arguments& args) {
auto json = phosg::JSON::parse(read_input_data(args));
auto table = MagMetadataTable::from_json(json);
std::string data = table->serialize_binary(get_cli_version(args, Version::BB_V4));
if (!args.get<bool>("decompressed")) {
data = prs_compress_optimal(data);
}
write_output_data(args, data, nullptr);
});
Action a_decode_tekker_adjustment_set(
"decode-tekker-adjustment-set", "\
decode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a JudgeItem.rel file into a JSON tekker adjustment set. Use\n\
--big-endian if the .rel file is from PSO GC.\n",
+[](phosg::Arguments& args) {
auto input_data = read_input_data(args);
TekkerAdjustmentSet table(input_data, args.get<bool>("big-endian"));
auto json = table.json();
auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS);
write_output_data(args, serialized, nullptr);
});
Action a_encode_tekker_adjustment_set(
"encode-tekker-adjustment-set", "\
encode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a JSON tekker adjustment set into a JudgeItem.rel file compatible\n\
with the game client. Use --big-endian if the .rel file is for PSO GC.\n",
+[](phosg::Arguments& args) {
TekkerAdjustmentSet table(phosg::JSON::parse(read_input_data(args)));
write_output_data(args, table.serialize_binary(args.get<bool>("big-endian")), nullptr);
});
Action a_decode_armor_shop_random_set(
"decode-armor-shop-random-set", "\
decode-armor-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a ArmorRandom.rel file into a JSON armor shop random set. Use\n\
--big-endian if the .rel file is from PSO GC.\n",
+[](phosg::Arguments& args) {
auto input_data = read_input_data(args);
ArmorShopRandomSet table(input_data, args.get<bool>("big-endian"));
auto json = table.json();
auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS);
write_output_data(args, serialized, nullptr);
});
Action a_encode_armor_shop_random_set(
"encode-armor-shop-random-set", "\
encode-armor-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a JSON armo shop random set into an ArmorRandom.rel file\n\
compatible with the game client. Use --big-endian if the .rel file is for\n\
PSO GC.\n",
+[](phosg::Arguments& args) {
ArmorShopRandomSet table(phosg::JSON::parse(read_input_data(args)));
write_output_data(args, table.serialize_binary(args.get<bool>("big-endian")), nullptr);
});
Action a_decode_tool_shop_random_set(
"decode-tool-shop-random-set", "\
decode-tool-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a ToolRandom.rel file into a JSON tool shop random set. Use\n\
--big-endian if the .rel file is from PSO GC.\n",
+[](phosg::Arguments& args) {
auto input_data = read_input_data(args);
ToolShopRandomSet table(input_data, args.get<bool>("big-endian"));
auto json = table.json();
auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS);
write_output_data(args, serialized, nullptr);
});
Action a_encode_tool_shop_random_set(
"encode-tool-shop-random-set", "\
encode-tool-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a JSON armo shop random set into an ToolRandom.rel file\n\
compatible with the game client. Use --big-endian if the .rel file is for\n\
PSO GC.\n",
+[](phosg::Arguments& args) {
ToolShopRandomSet table(phosg::JSON::parse(read_input_data(args)));
write_output_data(args, table.serialize_binary(args.get<bool>("big-endian")), nullptr);
});
Action a_decode_weapon_shop_random_set(
"decode-weapon-shop-random-set", "\
decode-weapon-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a WeaponRandom.rel file into a JSON weapon shop random set. Use\n\
--big-endian if the .rel file is from PSO GC.\n",
+[](phosg::Arguments& args) {
auto input_data = read_input_data(args);
WeaponShopRandomSet table(input_data, args.get<bool>("big-endian"));
auto json = table.json();
auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS);
write_output_data(args, serialized, nullptr);
});
Action a_encode_weapon_shop_random_set(
"encode-weapon-shop-random-set", "\
encode-weapon-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\
Converts a JSON armo shop random set into an WeaponRandom.rel file\n\
compatible with the game client. Use --big-endian if the .rel file is for\n\
PSO GC.\n",
+[](phosg::Arguments& args) {
WeaponShopRandomSet table(phosg::JSON::parse(read_input_data(args)));
write_output_data(args, table.serialize_binary(args.get<bool>("big-endian")), nullptr);
});
Action a_decode_level_table(
"decode-level-table", nullptr,
+[](phosg::Arguments& args) {
@@ -2646,7 +2776,7 @@ Action a_name_all_items(
Action a_print_level_stats(
"show-level-tables", "\
show-level-tables\n\
Print the level tables for each version in a semi-human-reatable format.\n",
Print the level tables for each version in a semi-human-readable format.\n",
+[](phosg::Arguments& args) {
auto s = std::make_shared<ServerState>(get_config_filename(args));
s->load_config_early();
@@ -2732,7 +2862,7 @@ Action a_print_level_stats(
Action a_show_item_parameter_tables(
"show-item-parameter-tables", "\
show-item-parameter-tables\n\
Print the item parameter tables for each version in a semi-human-reatable\n\
Print the item parameter tables for each version in a semi-human-readable\n\
format.\n",
+[](phosg::Arguments& args) {
auto s = std::make_shared<ServerState>(get_config_filename(args));
@@ -2746,6 +2876,27 @@ Action a_show_item_parameter_tables(
}
});
Action a_show_shop_random_sets(
"show-shop-random-sets", "\
show-shop-random-sets\n\
Print the tekker and shop generation tables in a semi-human-readable\n\
format.\n",
+[](phosg::Arguments& args) {
auto s = std::make_shared<ServerState>(get_config_filename(args));
s->load_all(false);
s->tekker_adjustment_set->print(stdout);
s->armor_random_set->print(stdout);
s->tool_random_set->print(stdout);
phosg::fwrite_fmt(stdout, "(Normal) ");
s->weapon_random_set(Difficulty::NORMAL)->print(stdout);
phosg::fwrite_fmt(stdout, "(Hard) ");
s->weapon_random_set(Difficulty::HARD)->print(stdout);
phosg::fwrite_fmt(stdout, "(Very Hard) ");
s->weapon_random_set(Difficulty::VERY_HARD)->print(stdout);
phosg::fwrite_fmt(stdout, "(Ultimate) ");
s->weapon_random_set(Difficulty::ULTIMATE)->print(stdout);
});
Action a_show_ep3_cards(
"show-ep3-cards", "\
show-ep3-cards\n\
@@ -3775,21 +3926,34 @@ Action a_diff_executables(
bool b_is_dol = b_filename.ends_with(".dol");
bool a_is_xbe = a_filename.ends_with(".xbe");
bool b_is_xbe = b_filename.ends_with(".xbe");
std::vector<DiffEntry> result;
if (a_is_dol && b_is_dol) {
result = diff_dol_files(a_filename, b_filename);
} else if (a_is_xbe && b_is_xbe) {
result = diff_xbe_files(a_filename, b_filename);
if (a_is_dol && b_is_dol && args.get<bool>("semantic")) {
std::unordered_set<uint32_t> a_ignore_functions, b_ignore_functions;
for (const auto& addr : args.get_multi<uint32_t>("a-ignore-function", phosg::Arguments::IntFormat::HEX)) {
a_ignore_functions.emplace(addr);
}
for (const auto& addr : args.get_multi<uint32_t>("b-ignore-function", phosg::Arguments::IntFormat::HEX)) {
b_ignore_functions.emplace(addr);
}
diff_dol_files_semantic(stdout, a_filename, b_filename, a_ignore_functions, b_ignore_functions);
} else {
throw std::runtime_error("the two files are not the same type of executable, or are neither dol nor xbe");
}
for (const auto& it : result) {
std::string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
if (show_pre) {
std::string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
phosg::fwrite_fmt(stdout, "{:08X}: {} => {}\n", it.address, a_str, b_str);
std::vector<DiffEntry> result;
if (a_is_dol && b_is_dol) {
result = diff_dol_files(a_filename, b_filename);
} else if (a_is_xbe && b_is_xbe) {
result = diff_xbe_files(a_filename, b_filename);
} else {
phosg::fwrite_fmt(stdout, "{:08X} {}\n", it.address, b_str);
throw std::runtime_error("the two files are not the same type of executable, or are neither dol nor xbe");
}
for (const auto& it : result) {
std::string b_str = phosg::format_data_string(it.b_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
if (show_pre) {
std::string a_str = phosg::format_data_string(it.a_data, nullptr, phosg::FormatDataStringFlags::HEX_ONLY);
phosg::fwrite_fmt(stdout, "{:08X}: {} => {}\n", it.address, a_str, b_str);
} else {
phosg::fwrite_fmt(stdout, "{:08X} {}\n", it.address, b_str);
}
}
}
});
+9 -1
View File
@@ -5630,12 +5630,16 @@ static void on_quest_F95E_result_bb(std::shared_ptr<Client> c, SubcommandMessage
auto s = c->require_server_state();
size_t count = (cmd.type > 0x03) ? 1 : (static_cast<size_t>(l->difficulty) + 1);
c->log.info_f("Creating {} F95E result items", count);
for (size_t z = 0; z < count; z++) {
const auto& results = s->quest_F95E_results.at(cmd.type).at(static_cast<size_t>(l->difficulty));
if (results.empty()) {
throw std::runtime_error("invalid result type");
}
ItemData item = (results.size() == 1) ? results[0] : results[l->rand_crypt->next() % results.size()];
if (c->log.should_log(phosg::LogLevel::L_INFO)) {
c->log.info_f("Chose F95E result item {}", item.hex());
}
if (item.data1[0] == 0x04) { // Meseta
// TODO: What is the right amount of Meseta to use here? Presumably it should be random within a certain range,
// but it's not obvious what that range should be.
@@ -5649,6 +5653,10 @@ static void on_quest_F95E_result_bb(std::shared_ptr<Client> c, SubcommandMessage
item.id = l->generate_item_id(0xFF);
l->add_item(cmd.floor, item, cmd.pos, nullptr, nullptr, 0x100F);
if (c->log.should_log(phosg::LogLevel::L_INFO)) {
c->log.info_f("Item created as {}", item.hex());
}
send_drop_stacked_item_to_lobby(l, item, cmd.floor, cmd.pos);
}
}
@@ -5908,7 +5916,7 @@ static void on_upgrade_weapon_attribute_bb(std::shared_ptr<Client> c, Subcommand
send_destroy_item_to_lobby(c, payment_item.id, cmd.payment_count);
item.data1[attribute_index] = cmd.attribute;
item.data1[attribute_index + 1] += new_attr_value;
item.data1[attribute_index + 1] = new_attr_value;
send_destroy_item_to_lobby(c, item.id, 1);
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
+52 -47
View File
@@ -490,15 +490,19 @@ std::shared_ptr<const ItemParameterTable> ServerState::item_parameter_table_for_
return this->item_parameter_table(is_v1(version) ? Version::PC_V2 : version);
}
std::shared_ptr<const MagEvolutionTable> ServerState::mag_evolution_table(Version version) const {
if (is_v1(version)) {
return this->mag_evolution_table_v1;
std::shared_ptr<const MagMetadataTable> ServerState::mag_metadata_table(Version version) const {
if (version == Version::DC_NTE) {
return this->mag_metadata_table_dc_nte;
} else if (version == Version::DC_11_2000) {
return this->mag_metadata_table_dc_11_2000;
} else if (is_v1(version)) {
return this->mag_metadata_table_v1;
} else if (is_v2(version)) {
return this->mag_evolution_table_v2;
return this->mag_metadata_table_v2;
} else if (!is_v4(version)) {
return this->mag_evolution_table_v3;
return this->mag_metadata_table_v3;
} else {
return this->mag_evolution_table_v4;
return this->mag_metadata_table_v4;
}
}
@@ -2136,28 +2140,28 @@ void ServerState::load_drop_tables() {
}
config_log.info_f("Loading armor table");
auto armor_data = std::make_shared<std::string>(phosg::load_file("system/tables/ArmorRandom-gc-v3.rel"));
auto new_armor_random_set = std::make_shared<ArmorRandomSet>(armor_data);
auto armor_json = phosg::JSON::parse(phosg::load_file("system/tables/armor-shop-random-set.json"));
auto new_armor_random_set = std::make_shared<ArmorShopRandomSet>(armor_json);
config_log.info_f("Loading tool table");
auto tool_data = std::make_shared<std::string>(phosg::load_file("system/tables/ToolRandom-gc-v3.rel"));
auto new_tool_random_set = std::make_shared<ToolRandomSet>(tool_data);
auto tool_json = phosg::JSON::parse(phosg::load_file("system/tables/tool-shop-random-set.json"));
auto new_tool_random_set = std::make_shared<ToolShopRandomSet>(tool_json);
config_log.info_f("Loading weapon tables");
std::array<std::shared_ptr<const WeaponRandomSet>, 4> new_weapon_random_sets;
std::array<std::shared_ptr<const WeaponShopRandomSet>, 4> new_weapon_random_sets;
const char* filenames[4] = {
"system/tables/WeaponRandomNormal-gc-v3.rel",
"system/tables/WeaponRandomHard-gc-v3.rel",
"system/tables/WeaponRandomVeryHard-gc-v3.rel",
"system/tables/WeaponRandomUltimate-gc-v3.rel",
"system/tables/weapon-shop-random-set-normal.json",
"system/tables/weapon-shop-random-set-hard.json",
"system/tables/weapon-shop-random-set-very-hard.json",
"system/tables/weapon-shop-random-set-ultimate.json",
};
for (size_t z = 0; z < 4; z++) {
auto weapon_data = std::make_shared<std::string>(phosg::load_file(filenames[z]));
new_weapon_random_sets[z] = std::make_shared<WeaponRandomSet>(weapon_data);
new_weapon_random_sets[z] = std::make_shared<WeaponShopRandomSet>(
phosg::JSON::parse(phosg::load_file(filenames[z])));
}
config_log.info_f("Loading tekker adjustment table");
auto tekker_data = std::make_shared<std::string>(phosg::load_file("system/tables/JudgeItem-gc-v3.rel"));
config_log.info_f("Loading tekker adjustment set");
auto tekker_data = phosg::JSON::parse(phosg::load_file("system/tables/tekker-adjustment-set.json"));
auto new_tekker_adjustment_set = std::make_shared<TekkerAdjustmentSet>(tekker_data);
this->rare_item_sets = std::move(new_rare_item_sets);
@@ -2188,25 +2192,32 @@ void ServerState::load_item_definitions() {
auto json = phosg::JSON::parse(phosg::load_file("system/tables/translation-table.json"));
auto new_item_translation_table = std::make_shared<ItemTranslationTable>(json, new_item_parameter_tables);
config_log.info_f("Loading v1 mag evolution table");
auto mag_data_v1 = std::make_shared<std::string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-dc-v1.prs")));
auto new_table_v1 = MagEvolutionTable::create(mag_data_v1, Version::DC_V1);
config_log.info_f("Loading v2 mag evolution table");
auto mag_data_v2 = std::make_shared<std::string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-dc-v2.prs")));
auto new_table_v2 = MagEvolutionTable::create(mag_data_v2, Version::DC_V2);
config_log.info_f("Loading v3 mag evolution table");
auto mag_data_v3 = std::make_shared<std::string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-xb-v3.prs")));
auto new_table_v3 = MagEvolutionTable::create(mag_data_v3, Version::XB_V3);
config_log.info_f("Loading v4 mag evolution table");
auto mag_data_v4 = std::make_shared<std::string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-bb-v4.prs")));
auto new_table_v4 = MagEvolutionTable::create(mag_data_v4, Version::BB_V4);
config_log.info_f("Creating DC NTE mag metadata table");
auto new_table_dc_nte = MagMetadataTable::from_binary(nullptr, Version::DC_NTE);
config_log.info_f("Loading DC 11/2000 mag metadata table");
auto new_table_11_2000 = MagMetadataTable::from_json(phosg::JSON::parse(phosg::load_file(
"system/tables/mag-metadata-table-dc-11-2000.json")));
config_log.info_f("Loading v1 mag metadata table");
auto new_table_v1 = MagMetadataTable::from_json(phosg::JSON::parse(phosg::load_file(
"system/tables/mag-metadata-table-v1.json")));
config_log.info_f("Loading v2 mag metadata table");
auto new_table_v2 = MagMetadataTable::from_json(phosg::JSON::parse(phosg::load_file(
"system/tables/mag-metadata-table-v2.json")));
config_log.info_f("Loading v3 mag metadata table");
auto new_table_v3 = MagMetadataTable::from_json(phosg::JSON::parse(phosg::load_file(
"system/tables/mag-metadata-table-v3.json")));
config_log.info_f("Loading v4 mag metadata table");
auto new_table_v4 = MagMetadataTable::from_json(phosg::JSON::parse(phosg::load_file(
"system/tables/mag-metadata-table-v4.json")));
this->item_parameter_tables = std::move(new_item_parameter_tables);
this->item_translation_table = std::move(new_item_translation_table);
this->mag_evolution_table_v1 = std::move(new_table_v1);
this->mag_evolution_table_v2 = std::move(new_table_v2);
this->mag_evolution_table_v3 = std::move(new_table_v3);
this->mag_evolution_table_v4 = std::move(new_table_v4);
this->mag_metadata_table_dc_nte = std::move(new_table_dc_nte);
this->mag_metadata_table_dc_11_2000 = std::move(new_table_11_2000);
this->mag_metadata_table_v1 = std::move(new_table_v1);
this->mag_metadata_table_v2 = std::move(new_table_v2);
this->mag_metadata_table_v3 = std::move(new_table_v3);
this->mag_metadata_table_v4 = std::move(new_table_v4);
}
void ServerState::load_ep3_cards() {
@@ -2262,20 +2273,13 @@ void ServerState::generate_bb_stream_file() {
config_log.info_f("Generating BB stream file");
auto sf = std::make_shared<BBStreamFile>();
auto add_file = [&](const std::string& filename, const void* data = nullptr, size_t size = 0) -> void {
auto add_file = [&](const std::string& filename, const void* data, size_t size) -> void {
auto& e = sf->entries.emplace_back();
e.offset = sf->data.size();
e.filename = filename;
if (!data) {
std::string file_data = phosg::load_file("system/blueburst/" + filename);
e.size = file_data.size();
e.checksum = phosg::crc32(file_data.data(), file_data.size());
sf->data += file_data;
} else {
e.size = size;
e.checksum = phosg::crc32(data, size);
sf->data.append(reinterpret_cast<const char*>(data), size);
}
e.size = size;
e.checksum = phosg::crc32(data, size);
sf->data.append(reinterpret_cast<const char*>(data), size);
config_log.debug_f(
"[BBStreamFile] Added file {} at offset {:08X} ({:08X} bytes) with checksum {:08X}; total size is now {:08X}",
filename, e.offset, e.size, e.checksum, sf->data.size());
@@ -2283,6 +2287,7 @@ void ServerState::generate_bb_stream_file() {
auto level_table_data = prs_compress_optimal(this->level_table_v4->serialize_binary_v4());
auto pmt_data = prs_compress_optimal(this->item_parameter_table(Version::BB_V4)->serialize_binary(Version::BB_V4));
auto mag_data = prs_compress_optimal(this->mag_metadata_table(Version::BB_V4)->serialize_binary(Version::BB_V4));
const auto& bps = *this->battle_params;
add_file("BattleParamEntry.dat", &bps.get_table(true, Episode::EP1), sizeof(BattleParamsIndex::Table));
@@ -2292,7 +2297,7 @@ void ServerState::generate_bb_stream_file() {
add_file("BattleParamEntry_lab_on.dat", &bps.get_table(false, Episode::EP2), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_ep4_on.dat", &bps.get_table(false, Episode::EP4), sizeof(BattleParamsIndex::Table));
add_file("PlyLevelTbl.prs", level_table_data.data(), level_table_data.size());
add_file("ItemMagEdit.prs");
add_file("ItemMagEdit.prs", mag_data.data(), mag_data.size());
add_file("ItemPMT.prs", pmt_data.data(), pmt_data.size());
this->bb_stream_file = sf;
+14 -10
View File
@@ -24,10 +24,12 @@
#include "ItemTranslationTable.hh"
#include "LevelTable.hh"
#include "Lobby.hh"
#include "MagEvolutionTable.hh"
#include "MagMetadataTable.hh"
#include "Menu.hh"
#include "Quest.hh"
#include "ShopRandomSets.hh"
#include "TeamIndex.hh"
#include "TekkerAdjustmentSet.hh"
#include "WordSelectTable.hh"
// Forward declarations due to reference cycles
@@ -219,19 +221,21 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const GSLArchive> bb_data_gsl;
std::unordered_map<std::string, std::shared_ptr<const CommonItemSet>> common_item_sets;
std::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
std::shared_ptr<const ArmorRandomSet> armor_random_set;
std::shared_ptr<const ToolRandomSet> tool_random_set;
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets; // Keyed oin difficulty
std::shared_ptr<const ArmorShopRandomSet> armor_random_set;
std::shared_ptr<const ToolShopRandomSet> tool_random_set;
std::array<std::shared_ptr<const WeaponShopRandomSet>, 4> weapon_random_sets; // Keyed on difficulty
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
std::shared_ptr<const ItemTranslationTable> item_translation_table;
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
size_t bb_max_bank_items = 200;
size_t bb_max_bank_meseta = 999999;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v2;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v3;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v4;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_dc_nte;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_dc_11_2000;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_v1;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_v2;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_v3;
std::shared_ptr<const MagMetadataTable> mag_metadata_table_v4;
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;
@@ -371,7 +375,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, Difficulty difficulty) const;
inline std::shared_ptr<const WeaponRandomSet> weapon_random_set(Difficulty difficulty) const {
inline std::shared_ptr<const WeaponShopRandomSet> weapon_random_set(Difficulty difficulty) const {
return this->weapon_random_sets.at(static_cast<size_t>(difficulty));
}
inline std::shared_ptr<const MapState::RareEnemyRates> rare_enemy_rates(Difficulty difficulty) const {
@@ -381,7 +385,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const LevelTable> level_table(Version version) 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;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table(Version version) const;
std::shared_ptr<const MagMetadataTable> mag_metadata_table(Version version) const;
std::shared_ptr<const ItemData::StackLimits> item_stack_limits(Version version) const;
std::shared_ptr<const ItemNameIndex> item_name_index_opt(Version version) const; // Returns null if missing
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
+834
View File
@@ -0,0 +1,834 @@
#include "ShopRandomSets.hh"
#include "CommonFileFormats.hh"
#include "StaticGameData.hh"
#include "Types.hh"
template <bool BE>
struct TableSpecT {
U32T<BE> offset;
uint8_t row_size;
parray<uint8_t, 3> unused;
} __packed_ws_be__(TableSpecT, 8);
template <typename T>
void print_table_2d(FILE* stream, const std::vector<std::vector<T>>& table) {
for (size_t z = 0; z < table.size(); z++) {
phosg::fwrite_fmt(stream, " {:02X}:", z);
for (const auto& cell : table[z]) {
if constexpr (std::is_integral_v<T>) {
phosg::fwrite_fmt(stream, " {:02X}", cell);
} else {
// It should be ShopRandomSetBase::IntPairT<...>
phosg::fwrite_fmt(stream, " {:02X} @ {:03}", cell.first, cell.second);
}
}
phosg::fwrite_fmt(stream, "\n");
}
}
template <typename ParsedT, typename StoredT = ParsedT>
std::vector<std::vector<ParsedT>> parse_table_t(
const phosg::StringReader& r, uint32_t offset, size_t row_size, const std::set<uint32_t> start_offsets) {
auto end_offset_it = start_offsets.upper_bound(offset);
if (end_offset_it == start_offsets.end()) {
throw std::runtime_error("Cannot determine table end offset");
}
uint32_t end_offset = *end_offset_it;
size_t row_bytes = row_size * sizeof(StoredT);
size_t row_count = (end_offset - offset) / row_bytes;
auto sub_r = r.sub(offset, row_bytes * row_count);
std::vector<std::vector<ParsedT>> ret;
while (ret.size() < row_count) {
auto& row = ret.emplace_back();
while (row.size() < row_size) {
row.emplace_back(sub_r.get<StoredT>());
}
}
return ret;
}
template <typename StoredT, typename ParsedT = StoredT, bool BE>
TableSpecT<BE> serialize_table_t(RELFileWriter<BE>& rel, const std::vector<std::vector<ParsedT>>& table) {
if (table.empty()) {
throw std::runtime_error("Table is empty");
}
TableSpecT<BE> ret;
ret.offset = rel.w.size();
ret.row_size = table[0].size();
for (const auto& row : table) {
if (row.size() != ret.row_size) {
throw std::runtime_error("Table has different row sizes");
}
for (const auto& cell : row) {
rel.template put<StoredT>(cell);
}
}
return ret;
}
template <size_t RowCount, size_t RowSize, typename ParsedT, typename StoredT = ParsedT>
std::array<std::array<ParsedT, RowSize>, RowCount> parse_fixed_table_t(const phosg::StringReader& r, uint32_t offset) {
auto sub_r = r.sub(offset, RowSize * RowCount * sizeof(StoredT));
std::array<std::array<ParsedT, RowSize>, RowCount> ret;
for (size_t y = 0; y < RowCount; y++) {
for (size_t x = 0; x < RowSize; x++) {
ret[y][x] = sub_r.get<StoredT>();
}
}
return ret;
}
template <size_t RowCount, size_t RowSize, typename ParsedT, typename StoredT = ParsedT, bool BE>
uint32_t serialize_fixed_table_t(
RELFileWriter<BE>& rel, const std::array<std::array<ParsedT, RowSize>, RowCount>& table) {
uint32_t ret = rel.w.size();
for (const auto& row : table) {
for (const auto& cell : row) {
rel.template put<StoredT>(cell);
}
}
return ret;
}
template <typename T>
std::vector<std::vector<T>> table_for_json_t(const phosg::JSON& table_json) {
std::vector<std::vector<T>> ret;
for (const auto& row_json : table_json.as_list()) {
auto& row = ret.emplace_back();
for (const auto& cell_json : row_json->as_list()) {
if constexpr (std::is_integral_v<T>) {
row.emplace_back(cell_json->as_int());
} else {
row.emplace_back(*cell_json);
}
}
}
return ret;
}
template <typename T>
phosg::JSON json_for_table_t(const std::vector<std::vector<T>>& table) {
auto table_json = phosg::JSON::list();
for (const auto& row : table) {
auto row_json = phosg::JSON::list();
for (const auto& cell : row) {
if constexpr (std::is_integral_v<T>) {
row_json.emplace_back(cell);
} else {
row_json.emplace_back(cell.json());
}
}
table_json.emplace_back(std::move(row_json));
}
return table_json;
}
template <typename T, size_t Count>
std::array<T, Count> fixed_table_for_json_t(const phosg::JSON& table_json) {
std::array<T, Count> ret;
for (size_t y = 0; y < Count; y++) {
ret[y] = table_json.at(y);
}
return ret;
}
template <typename T, size_t RowCount, size_t RowSize>
std::array<std::array<T, RowSize>, RowCount> fixed_table_for_json_t(const phosg::JSON& table_json) {
std::array<std::array<T, RowSize>, RowCount> ret;
for (size_t y = 0; y < RowCount; y++) {
const auto& row_json = table_json.at(y);
for (size_t x = 0; x < RowSize; x++) {
ret[y][x] = row_json.at(x);
}
}
return ret;
}
template <typename T, size_t Count>
phosg::JSON json_for_fixed_table_t(const std::array<T, Count>& table) {
auto ret = phosg::JSON::list();
for (const auto& cell : table) {
ret.emplace_back(cell.json());
}
return ret;
}
template <typename T, size_t RowCount, size_t RowSize>
phosg::JSON json_for_fixed_table_t(const std::array<std::array<T, RowSize>, RowCount>& table) {
auto table_json = phosg::JSON::list();
for (const auto& row : table) {
auto row_json = phosg::JSON::list();
for (const auto& cell : row) {
row_json.emplace_back(cell.json());
}
table_json.emplace_back(std::move(row_json));
}
return table_json;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Armor shop set
template <bool BE>
struct ArmorSubRootT {
TableSpecT<BE> armor_table; // -> WeightTableEntry8[...][...]
TableSpecT<BE> shield_table; // -> WeightTableEntry8[...][...]
TableSpecT<BE> unit_table; // -> WeightTableEntry8[...][...]
} __packed_ws_be__(ArmorSubRootT, 0x18);
template <bool BE>
struct ArmorRootT {
U32T<BE> subroot; // -> ArmorSubRootT<BE>
} __packed_ws_be__(ArmorRootT, 4);
ArmorShopRandomSet::ArmorShopRandomSet(const void* data, size_t size, bool big_endian) {
if (big_endian) {
this->parse_t<true>(data, size);
} else {
this->parse_t<false>(data, size);
}
}
ArmorShopRandomSet::ArmorShopRandomSet(const std::string& data, bool big_endian)
: ArmorShopRandomSet(data.data(), data.size(), big_endian) {}
ArmorShopRandomSet::ArmorShopRandomSet(const phosg::JSON& json) {
this->armor_table = table_for_json_t<IntPairT<uint8_t>>(json.at("ArmorTable"));
this->shield_table = table_for_json_t<IntPairT<uint8_t>>(json.at("ShieldTable"));
this->unit_table = table_for_json_t<IntPairT<uint8_t>>(json.at("UnitTable"));
}
template <bool BE>
void ArmorShopRandomSet::parse_t(const void* data, size_t size) {
std::set<uint32_t> start_offsets;
phosg::StringReader r(data, size);
uint32_t root_offset = r.pget<U32T<BE>>(size - 0x10);
start_offsets.emplace(root_offset);
const auto& root = r.pget<ArmorRootT<BE>>(root_offset);
start_offsets.emplace(root.subroot);
const auto& subroot = r.pget<ArmorSubRootT<BE>>(root.subroot);
start_offsets.emplace(subroot.armor_table.offset);
start_offsets.emplace(subroot.shield_table.offset);
start_offsets.emplace(subroot.unit_table.offset);
this->armor_table = parse_table_t<IntPairT<uint8_t>>(
r, subroot.armor_table.offset, subroot.armor_table.row_size, start_offsets);
this->shield_table = parse_table_t<IntPairT<uint8_t>>(
r, subroot.shield_table.offset, subroot.shield_table.row_size, start_offsets);
this->unit_table = parse_table_t<IntPairT<uint8_t>>(
r, subroot.unit_table.offset, subroot.unit_table.row_size, start_offsets);
}
template <bool BE>
std::string ArmorShopRandomSet::serialize_binary_t() const {
RELFileWriter<BE> rel;
ArmorSubRootT<BE> subroot;
subroot.armor_table = serialize_table_t<IntPairT<uint8_t>>(rel, this->armor_table);
subroot.shield_table = serialize_table_t<IntPairT<uint8_t>>(rel, this->shield_table);
subroot.unit_table = serialize_table_t<IntPairT<uint8_t>>(rel, this->unit_table);
ArmorRootT<BE> root;
rel.align(4);
root.subroot = rel.put(subroot);
rel.relocations.emplace(rel.w.size() - 0x18);
rel.relocations.emplace(rel.w.size() - 0x10);
rel.relocations.emplace(rel.w.size() - 0x08);
uint32_t root_offset = rel.put(root);
rel.relocations.emplace(rel.w.size() - 4);
return rel.finalize(root_offset);
}
std::string ArmorShopRandomSet::serialize_binary(bool big_endian) const {
return big_endian ? this->serialize_binary_t<true>() : this->serialize_binary_t<false>();
}
phosg::JSON ArmorShopRandomSet::json() const {
return phosg::JSON::dict({
{"ArmorTable", json_for_table_t(this->armor_table)},
{"ShieldTable", json_for_table_t(this->shield_table)},
{"UnitTable", json_for_table_t(this->unit_table)},
});
}
void ArmorShopRandomSet::print(FILE* stream) const {
phosg::fwrite_fmt(stream, "ArmorShopRandomSet\n");
phosg::fwrite_fmt(stream, " Armor table\n");
print_table_2d(stream, this->armor_table);
phosg::fwrite_fmt(stream, " Shield table\n");
print_table_2d(stream, this->shield_table);
phosg::fwrite_fmt(stream, " Unit table\n");
print_table_2d(stream, this->unit_table);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tool shop set
ToolShopRandomSet::TechDiskLevelEntry::TechDiskLevelEntry(const phosg::JSON& json) {
if (json.contains("PlayerLevelDivisor")) {
this->mode = Mode::PLAYER_LEVEL_DIVISOR;
this->player_level_divisor_or_min_level = json.get_int("PlayerLevelDivisor");
this->max_level = 0;
} else if (json.contains("MinLevel")) {
this->mode = Mode::RANDOM_IN_RANGE;
this->player_level_divisor_or_min_level = json.get_int("MinLevel");
this->max_level = json.get_int("MaxLevel");
} else {
this->mode = Mode::LEVEL_1;
this->player_level_divisor_or_min_level = 0;
this->max_level = 0;
}
}
phosg::JSON ToolShopRandomSet::TechDiskLevelEntry::json() const {
switch (this->mode) {
case Mode::LEVEL_1:
return phosg::JSON::dict();
case Mode::PLAYER_LEVEL_DIVISOR:
return phosg::JSON::dict({{"PlayerLevelDivisor", this->player_level_divisor_or_min_level}});
case Mode::RANDOM_IN_RANGE:
return phosg::JSON::dict({{"MinLevel", this->player_level_divisor_or_min_level}, {"MaxLevel", this->max_level}});
default:
throw std::runtime_error("Invalid TechDiskLevelEntry mode");
}
}
template <bool BE>
struct ToolSubRootT {
TableSpecT<BE> rare_recovery_table; // -> WeightTableEntry8[...][...]
TableSpecT<BE> tech_disk_table; // -> WeightTableEntry8[...][...]
} __packed_ws_be__(ToolSubRootT, 0x10);
template <bool BE>
struct ToolRootT {
U32T<BE> common_recovery_table; // -> TableSpecT<BE> -> WeightTableEntry8[...][...]
U32T<BE> subroot; // -> ToolSubRootT<BE>
U32T<BE> tech_disk_level_table; // -> TableSpecT<BE> -> TechDiskLevelEntry[...][...]
} __packed_ws_be__(ToolRootT, 0x0C);
ToolShopRandomSet::ToolShopRandomSet(const void* data, size_t size, bool big_endian) {
if (big_endian) {
this->parse_t<true>(data, size);
} else {
this->parse_t<false>(data, size);
}
}
const std::vector<std::pair<uint8_t, uint8_t>> ToolShopRandomSet::item_defs{
{0x00, 0x00}, // 00 -> Monomate
{0x00, 0x01}, // 01 -> Dimate
{0x00, 0x02}, // 02 -> Trimate
{0x01, 0x00}, // 03 -> Monofluid
{0x01, 0x01}, // 04 -> Difluid
{0x01, 0x02}, // 05 -> Trifluid
{0x06, 0x00}, // 06 -> Antidote
{0x06, 0x01}, // 07 -> Antiparalysis
{0x03, 0x00}, // 08 -> Sol Atomizer
{0x04, 0x00}, // 09 -> Moon Atomizer
{0x05, 0x00}, // 0A -> Star Atomizer
{0x07, 0x00}, // 0B -> Telepipe
{0x08, 0x00}, // 0C -> Trap Vision
{0x09, 0x00}, // 0D -> Scape Doll
{0x0A, 0x00}, // 0E -> Monogrinder
{0xFF, 0xFF}, // 0F -> Nothing
};
const std::array<uint8_t, 0x13> ToolShopRandomSet::tech_num_map{
0x00, 0x03, 0x06, 0x0F, 0x10, 0x0D, 0x0A, 0x0B, 0x0C, 0x01, 0x04, 0x07, 0x0E, 0x11, 0x02, 0x05, 0x08, 0x09, 0x12};
ToolShopRandomSet::ToolShopRandomSet(const std::string& data, bool big_endian)
: ToolShopRandomSet(data.data(), data.size(), big_endian) {}
ToolShopRandomSet::ToolShopRandomSet(const phosg::JSON& json) {
this->common_recovery_table = table_for_json_t<uint8_t>(json.at("CommonRecoveryTable"));
this->rare_recovery_table = table_for_json_t<IntPairT<uint8_t>>(json.at("RareRecoveryTable"));
this->tech_disk_table = table_for_json_t<IntPairT<uint8_t>>(json.at("TechDiskTable"));
this->tech_disk_level_table = table_for_json_t<TechDiskLevelEntry>(json.at("TechDiskLevelTable"));
}
template <bool BE>
void ToolShopRandomSet::parse_t(const void* data, size_t size) {
std::set<uint32_t> start_offsets;
phosg::StringReader r(data, size);
uint32_t root_offset = r.pget<U32T<BE>>(size - 0x10);
start_offsets.emplace(root_offset);
const auto& root = r.pget<ToolRootT<BE>>(root_offset);
start_offsets.emplace(root.common_recovery_table);
start_offsets.emplace(root.subroot);
start_offsets.emplace(root.tech_disk_level_table);
const auto& common_recovery_table_spec = r.pget<TableSpecT<BE>>(root.common_recovery_table);
start_offsets.emplace(common_recovery_table_spec.offset);
const auto& subroot = r.pget<ToolSubRootT<BE>>(root.subroot);
start_offsets.emplace(subroot.rare_recovery_table.offset);
start_offsets.emplace(subroot.tech_disk_table.offset);
const auto& tech_disk_level_table_spec = r.pget<TableSpecT<BE>>(root.tech_disk_level_table);
start_offsets.emplace(tech_disk_level_table_spec.offset);
this->common_recovery_table = parse_table_t<uint8_t>(
r, common_recovery_table_spec.offset, common_recovery_table_spec.row_size, start_offsets);
this->rare_recovery_table = parse_table_t<IntPairT<uint8_t>>(
r, subroot.rare_recovery_table.offset, subroot.rare_recovery_table.row_size, start_offsets);
this->tech_disk_table = parse_table_t<IntPairT<uint8_t>>(
r, subroot.tech_disk_table.offset, subroot.tech_disk_table.row_size, start_offsets);
this->tech_disk_level_table = parse_table_t<TechDiskLevelEntry>(
r, tech_disk_level_table_spec.offset, tech_disk_level_table_spec.row_size, start_offsets);
}
template <bool BE>
std::string ToolShopRandomSet::serialize_binary_t() const {
RELFileWriter<BE> rel;
ToolSubRootT<BE> subroot;
auto common_recovery_table_spec = serialize_table_t<uint8_t>(rel, this->common_recovery_table);
subroot.rare_recovery_table = serialize_table_t<IntPairT<uint8_t>>(rel, this->rare_recovery_table);
subroot.tech_disk_table = serialize_table_t<IntPairT<uint8_t>>(rel, this->tech_disk_table);
auto tech_disk_level_table_spec = serialize_table_t<TechDiskLevelEntry>(rel, this->tech_disk_level_table);
rel.align(4);
ToolRootT<BE> root;
root.subroot = rel.put(subroot);
rel.relocations.emplace(rel.w.size() - 0x10);
rel.relocations.emplace(rel.w.size() - 0x08);
root.common_recovery_table = rel.put(common_recovery_table_spec);
rel.relocations.emplace(rel.w.size() - 0x08);
root.tech_disk_level_table = rel.put(tech_disk_level_table_spec);
rel.relocations.emplace(rel.w.size() - 0x08);
uint32_t root_offset = rel.put(root);
rel.relocations.emplace(rel.w.size() - 0x0C);
rel.relocations.emplace(rel.w.size() - 0x08);
rel.relocations.emplace(rel.w.size() - 0x04);
return rel.finalize(root_offset);
}
std::string ToolShopRandomSet::serialize_binary(bool big_endian) const {
return big_endian ? this->serialize_binary_t<true>() : this->serialize_binary_t<false>();
}
phosg::JSON ToolShopRandomSet::json() const {
return phosg::JSON::dict({
{"CommonRecoveryTable", json_for_table_t(this->common_recovery_table)},
{"RareRecoveryTable", json_for_table_t(this->rare_recovery_table)},
{"TechDiskTable", json_for_table_t(this->tech_disk_table)},
{"TechDiskLevelTable", json_for_table_t(this->tech_disk_level_table)},
});
}
void ToolShopRandomSet::print(FILE* stream) const {
phosg::fwrite_fmt(stream, "ToolShopRandomSet\n");
phosg::fwrite_fmt(stream, " Common recovery table\n");
for (size_t z = 0; z < this->common_recovery_table.size(); z++) {
phosg::fwrite_fmt(stream, " {:02X}:", z);
for (uint8_t cell : this->common_recovery_table[z]) {
if (cell == 0x0F) {
phosg::fwrite_fmt(stream, " {:02X} (------)", cell);
} else {
const auto& def = this->item_defs.at(cell);
phosg::fwrite_fmt(stream, " {:02X} (03{:02X}{:02X})", cell, def.first, def.second);
}
}
phosg::fwrite_fmt(stream, "\n");
}
phosg::fwrite_fmt(stream, " Rare recovery table\n");
for (size_t z = 0; z < this->rare_recovery_table.size(); z++) {
phosg::fwrite_fmt(stream, " {:02X}:", z);
for (const auto& cell : this->rare_recovery_table[z]) {
if (cell.value == 0x0F) {
phosg::fwrite_fmt(stream, " {:02X} (------) @ {:03}", cell.value, cell.weight);
} else {
const auto& def = this->item_defs.at(cell.value);
phosg::fwrite_fmt(stream, " {:02X} (03{:02X}{:02X}) @ {:03}", cell.value, def.first, def.second, cell.weight);
}
}
phosg::fwrite_fmt(stream, "\n");
}
phosg::fwrite_fmt(stream, " Tech disk table\n");
for (size_t z = 0; z < this->tech_disk_table.size(); z++) {
phosg::fwrite_fmt(stream, " {:02X}:", z);
for (const auto& cell : this->tech_disk_table[z]) {
phosg::fwrite_fmt(stream, " {:02X}({:>8}) @ {:03}",
cell.first, name_for_technique(this->tech_num_map.at(cell.value)), cell.second);
}
phosg::fwrite_fmt(stream, "\n");
}
phosg::fwrite_fmt(stream, " Tech disk level table\n");
phosg::fwrite_fmt(stream, " ");
for (const auto& tech_num : this->tech_num_map) {
phosg::fwrite_fmt(stream, " {:<9}", name_for_technique(tech_num));
}
phosg::fwrite_fmt(stream, "\n");
for (size_t z = 0; z < this->tech_disk_level_table.size(); z++) {
phosg::fwrite_fmt(stream, " {:02X}:", z);
for (const auto& cell : this->tech_disk_level_table[z]) {
switch (cell.mode) {
case TechDiskLevelEntry::Mode::LEVEL_1:
phosg::fwrite_fmt(stream, " LEVEL_1 ");
break;
case TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR:
phosg::fwrite_fmt(stream, " PLV / {:03}", cell.player_level_divisor_or_min_level);
break;
case TechDiskLevelEntry::Mode::RANDOM_IN_RANGE:
phosg::fwrite_fmt(stream, " {:03} - {:03}", cell.player_level_divisor_or_min_level, cell.max_level);
break;
}
}
phosg::fwrite_fmt(stream, "\n");
}
std::vector<std::vector<TechDiskLevelEntry>> tech_disk_level_table;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Weapon shop set
template <bool BE>
struct RangeTableEntryT {
U32T<BE> min;
U32T<BE> max;
} __packed_ws_be__(RangeTableEntryT, 8);
template <bool BE>
struct WeaponRootT {
U32T<BE> weapon_type_table; // {c, o -> (table)}[...(offsets)]
U32T<BE> bonus_type_table1; // {u32 value, u32 weight}[9][6]
U32T<BE> bonus_type_table2; // {u32 value, u32 weight}[9][6]
U32T<BE> bonus_range_table1; // {u32 min_index, u32 max_index}[9]
U32T<BE> bonus_range_table2; // {u32 min_index, u32 max_index}[9]
U32T<BE> special_mode_table; // {u32 value, u32 weight}[8][3]
U32T<BE> default_grind_range_table; // {u32 min, u32 max}[6]
U32T<BE> favored_grind_range_table; // {u32 min, u32 max}[6]
} __packed_ws_be__(WeaponRootT, 0x20);
const std::array<std::pair<uint8_t, uint8_t>, 0x48> WeaponShopRandomSet::type_defs({
/* 00 */ {0x01, 0x00}, // Saber
/* 01 */ {0x01, 0x01}, // Brand
/* 02 */ {0x01, 0x02}, // Buster
/* 03 */ {0x01, 0x03}, // Pallasch
/* 04 */ {0x01, 0x04}, // Gladius
/* 05 */ {0x03, 0x00}, // Dagger
/* 06 */ {0x03, 0x01}, // Knife
/* 07 */ {0x03, 0x02}, // Blade
/* 08 */ {0x03, 0x03}, // Edge
/* 09 */ {0x03, 0x04}, // Ripper
/* 0A */ {0x02, 0x00}, // Sword
/* 0B */ {0x02, 0x01}, // Gigush
/* 0C */ {0x02, 0x02}, // Breaker
/* 0D */ {0x02, 0x03}, // Claymore
/* 0E */ {0x02, 0x04}, // Calibur
/* 0F */ {0x05, 0x00}, // Slicer
/* 10 */ {0x05, 0x01}, // Spinner
/* 11 */ {0x05, 0x02}, // Cutter
/* 12 */ {0x05, 0x03}, // Sawcer
/* 13 */ {0x05, 0x04}, // Diska
/* 14 */ {0x04, 0x00}, // Partisan
/* 15 */ {0x04, 0x01}, // Halbert
/* 16 */ {0x04, 0x02}, // Glaive
/* 17 */ {0x04, 0x03}, // Berdys
/* 18 */ {0x04, 0x04}, // Gungnir
/* 19 */ {0x06, 0x00}, // Handgun
/* 1A */ {0x06, 0x01}, // Autogun
/* 1B */ {0x06, 0x02}, // Lockgun
/* 1C */ {0x06, 0x03}, // Railgun
/* 1D */ {0x06, 0x04}, // Raygun
/* 1E */ {0x07, 0x00}, // Rifle
/* 1F */ {0x07, 0x01}, // Sniper
/* 20 */ {0x07, 0x02}, // Blaster
/* 21 */ {0x07, 0x03}, // Beam
/* 22 */ {0x07, 0x04}, // Laser
/* 23 */ {0x08, 0x00}, // Mechgun
/* 24 */ {0x08, 0x01}, // Assault
/* 25 */ {0x08, 0x02}, // Repeater
/* 26 */ {0x08, 0x03}, // Gatling
/* 27 */ {0x08, 0x04}, // Vulcan
/* 28 */ {0x09, 0x00}, // Shot
/* 29 */ {0x09, 0x01}, // Spread
/* 2A */ {0x09, 0x02}, // Cannon
/* 2B */ {0x09, 0x03}, // Launcher
/* 2C */ {0x09, 0x04}, // Arms
/* 2D */ {0x0A, 0x00}, // Cane
/* 2E */ {0x0A, 0x01}, // Stick
/* 2F */ {0x0A, 0x02}, // Mace
/* 30 */ {0x0A, 0x03}, // Club
/* 31 */ {0x0B, 0x00}, // Rod
/* 32 */ {0x0B, 0x01}, // Pole
/* 33 */ {0x0B, 0x02}, // Pillar
/* 34 */ {0x0B, 0x03}, // Striker
/* 35 */ {0x0C, 0x00}, // Wand
/* 36 */ {0x0C, 0x01}, // Staff
/* 37 */ {0x0C, 0x02}, // Baton
/* 38 */ {0x0C, 0x03}, // Scepter
/* 39 */ {0xFF, 0xFF}, // Special-cased in type_defs_39 (depends on section ID)
/* 3A */ {0xFF, 0xFF}, // Special-cased in type_defs_3A (depends on section ID)
/* 3B */ {0x01, 0x05}, // DB'S SABER
/* 3C */ {0x02, 0x05}, // FLOWEN'S SWORD
/* 3D */ {0x06, 0x05}, // VARISTA
/* 3E */ {0x08, 0x05}, // M&A60 VISE
/* 3F */ {0x0A, 0x04}, // CLUB OF LACONIUM
/* 40 */ {0x0C, 0x04}, // FIRE SCEPTER:AGNI
/* 41 */ {0x0B, 0x04}, // BATTLE VERGE
/* 42 */ {0x01, 0x06}, // KALADBOLG
/* 43 */ {0x03, 0x05}, // BLADE DANCE
/* 44 */ {0x07, 0x05}, // VISK-235W
/* 45 */ {0x0A, 0x05}, // MACE OF ADAMAN
/* 46 */ {0x0C, 0x05}, // ICE STAFF:DAGON
/* 47 */ {0x0B, 0x05}, // BRAVE HAMMER
});
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_39({
// Indexed by section_id
{0x28, 0x00}, // HARISEN BATTLE FAN
{0x2A, 0x00}, // AKIKO'S WOK
{0x2B, 0x00}, // TOY HAMMER
{0x35, 0x00}, // CRAZY TUNE
{0x52, 0x00}, // FLOWER CANE
{0x48, 0x00}, // SAMBA MARACAS
{0x64, 0x00}, // CHAMELEON SCYTHE
{0x59, 0x00}, // BROOM
{0x8A, 0x00}, // SANGE
{0x99, 0x00}, // ANGEL HARP
});
const std::array<std::pair<uint8_t, uint8_t>, 10> WeaponShopRandomSet::type_defs_3A({
// Indexed by section_id
{0x99, 0x00}, // ANGEL HARP
{0x64, 0x00}, // CHAMELEON SCYTHE
{0x8A, 0x00}, // SANGE
{0x28, 0x00}, // HARISEN BATTLE FAN
{0x59, 0x00}, // BROOM
{0x2B, 0x00}, // TOY HAMMER
{0x52, 0x00}, // FLOWER CANE
{0x2A, 0x00}, // AKIKO'S WOK
{0x48, 0x00}, // SAMBA MARACAS
{0x35, 0x00}, // CRAZY TUNE
});
const std::array<int8_t, 20> WeaponShopRandomSet::bonus_values{
-50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
WeaponShopRandomSet::WeaponShopRandomSet(const void* data, size_t size, bool big_endian) {
if (big_endian) {
this->parse_t<true>(data, size);
} else {
this->parse_t<false>(data, size);
}
}
WeaponShopRandomSet::WeaponShopRandomSet(const std::string& data, bool big_endian)
: WeaponShopRandomSet(data.data(), data.size(), big_endian) {}
WeaponShopRandomSet::WeaponShopRandomSet(const phosg::JSON& json) {
for (const auto& it : json.get_list("WeaponTypeWeightTables")) {
this->weapon_type_weight_tables.emplace_back(table_for_json_t<IntPairT<uint8_t>>(*it));
}
this->bonus_type_table1 = fixed_table_for_json_t<IntPairT<uint32_t>, 9, 6>(json.at("BonusTypeTable1"));
this->bonus_type_table2 = fixed_table_for_json_t<IntPairT<uint32_t>, 9, 6>(json.at("BonusTypeTable2"));
this->bonus_range_table1 = fixed_table_for_json_t<IntPairT<uint32_t>, 9>(json.at("BonusRangeTable1"));
this->bonus_range_table2 = fixed_table_for_json_t<IntPairT<uint32_t>, 9>(json.at("BonusRangeTable2"));
this->special_mode_table = fixed_table_for_json_t<IntPairT<uint32_t>, 8, 3>(json.at("SpecialModeTable"));
this->default_grind_range_table = fixed_table_for_json_t<IntPairT<uint32_t>, 6>(json.at("DefaultDringRangeTable"));
this->favored_grind_range_table = fixed_table_for_json_t<IntPairT<uint32_t>, 6>(json.at("FavoredDringRangeTable"));
}
template <bool BE>
void WeaponShopRandomSet::parse_t(const void* data, size_t size) {
std::set<uint32_t> start_offsets;
phosg::StringReader r(data, size);
uint32_t root_offset = r.pget<U32T<BE>>(size - 0x10);
start_offsets.emplace(root_offset);
const auto& root = r.pget<WeaponRootT<BE>>(root_offset);
start_offsets.emplace(root.weapon_type_table);
start_offsets.emplace(root.bonus_type_table1);
start_offsets.emplace(root.bonus_type_table2);
start_offsets.emplace(root.bonus_range_table1);
start_offsets.emplace(root.bonus_range_table2);
start_offsets.emplace(root.special_mode_table);
start_offsets.emplace(root.default_grind_range_table);
start_offsets.emplace(root.favored_grind_range_table);
// Count the weapon types tables
if (start_offsets.upper_bound(root.weapon_type_table) == start_offsets.end()) {
throw std::runtime_error("Weapon type table is out of range");
}
for (uint32_t offset = root.weapon_type_table;
offset < *start_offsets.upper_bound(root.weapon_type_table);
offset += sizeof(TableSpecT<BE>)) {
start_offsets.emplace(r.pget<TableSpecT<BE>>(offset).offset);
}
size_t num_weapon_types_tables =
(*start_offsets.upper_bound(root.weapon_type_table) - root.weapon_type_table) / sizeof(TableSpecT<BE>);
while (this->weapon_type_weight_tables.size() < num_weapon_types_tables) {
const auto& spec = r.pget<TableSpecT<BE>>(
root.weapon_type_table + this->weapon_type_weight_tables.size() * sizeof(TableSpecT<BE>));
this->weapon_type_weight_tables.emplace_back(parse_table_t<IntPairT<uint8_t>>(
r, spec.offset, spec.row_size, start_offsets));
}
auto parse_fixed_table_into_1d = [&]<size_t Count>(std::array<IntPairT<uint32_t>, Count>& ret, uint32_t offset) -> void {
auto sub_r = r.sub(offset, sizeof(IntPairT<uint32_t>) * Count);
for (size_t z = 0; z < Count; z++) {
ret[z] = sub_r.get<IntPairT<U32T<BE>>>();
}
};
auto parse_fixed_table_into_2d = [&]<size_t RowSize, size_t RowCount>(std::array<std::array<IntPairT<uint32_t>, RowSize>, RowCount>& ret, uint32_t offset) -> void {
auto sub_r = r.sub(offset, sizeof(IntPairT<uint32_t>) * RowSize * RowCount);
for (size_t y = 0; y < RowCount; y++) {
for (size_t x = 0; x < RowSize; x++) {
ret[y][x] = sub_r.get<IntPairT<U32T<BE>>>();
}
}
};
parse_fixed_table_into_2d(this->bonus_type_table1, root.bonus_type_table1);
parse_fixed_table_into_2d(this->bonus_type_table2, root.bonus_type_table2);
parse_fixed_table_into_1d(this->bonus_range_table1, root.bonus_range_table1);
parse_fixed_table_into_1d(this->bonus_range_table2, root.bonus_range_table2);
parse_fixed_table_into_2d(this->special_mode_table, root.special_mode_table);
parse_fixed_table_into_1d(this->default_grind_range_table, root.default_grind_range_table);
parse_fixed_table_into_1d(this->favored_grind_range_table, root.favored_grind_range_table);
}
template <bool BE>
std::string WeaponShopRandomSet::serialize_binary_t() const {
RELFileWriter<BE> rel;
WeaponRootT<BE> root;
std::vector<TableSpecT<BE>> weapon_type_weight_table_specs;
weapon_type_weight_table_specs.reserve(this->weapon_type_weight_tables.size());
for (const auto& table : this->weapon_type_weight_tables) {
weapon_type_weight_table_specs.emplace_back(serialize_table_t<IntPairT<uint8_t>>(rel, table));
}
root.weapon_type_table = rel.w.size();
for (const auto& spec : weapon_type_weight_table_specs) {
rel.put(spec);
rel.relocations.emplace(rel.w.size() - 8);
}
auto serialize_fixed_table_1d = [&]<size_t Count>(const std::array<IntPairT<uint32_t>, Count>& table) -> uint32_t {
uint32_t ret = rel.w.size();
for (size_t z = 0; z < Count; z++) {
rel.template put<IntPairT<U32T<BE>>>(table[z]);
}
return ret;
};
auto serialize_fixed_table_2d = [&]<size_t RowSize, size_t RowCount>(const std::array<std::array<IntPairT<uint32_t>, RowSize>, RowCount>& table) -> uint32_t {
uint32_t ret = rel.w.size();
for (size_t y = 0; y < RowCount; y++) {
for (size_t x = 0; x < RowSize; x++) {
rel.template put<IntPairT<U32T<BE>>>(table[y][x]);
}
}
return ret;
};
rel.align(4);
root.bonus_type_table1 = serialize_fixed_table_2d(this->bonus_type_table1);
root.bonus_type_table2 = serialize_fixed_table_2d(this->bonus_type_table2);
root.special_mode_table = serialize_fixed_table_2d(this->special_mode_table);
root.bonus_range_table1 = serialize_fixed_table_1d(this->bonus_range_table1);
root.bonus_range_table2 = serialize_fixed_table_1d(this->bonus_range_table2);
root.default_grind_range_table = serialize_fixed_table_1d(this->default_grind_range_table);
root.favored_grind_range_table = serialize_fixed_table_1d(this->favored_grind_range_table);
rel.align(4);
uint32_t root_offset = rel.put(root);
for (size_t z = 1; z <= 8; z++) {
rel.relocations.emplace(rel.w.size() - (z * 4));
}
return rel.finalize(root_offset);
}
std::string WeaponShopRandomSet::serialize_binary(bool big_endian) const {
return big_endian ? this->serialize_binary_t<true>() : this->serialize_binary_t<false>();
}
phosg::JSON WeaponShopRandomSet::json() const {
auto weapon_type_weight_tables_json = phosg::JSON::list();
for (const auto& table : this->weapon_type_weight_tables) {
weapon_type_weight_tables_json.emplace_back(json_for_table_t(table));
}
return phosg::JSON::dict({
{"WeaponTypeWeightTables", std::move(weapon_type_weight_tables_json)},
{"BonusTypeTable1", json_for_fixed_table_t(this->bonus_type_table1)},
{"BonusTypeTable2", json_for_fixed_table_t(this->bonus_type_table2)},
{"BonusRangeTable1", json_for_fixed_table_t(this->bonus_range_table1)},
{"BonusRangeTable2", json_for_fixed_table_t(this->bonus_range_table2)},
{"SpecialModeTable", json_for_fixed_table_t(this->special_mode_table)},
{"DefaultDringRangeTable", json_for_fixed_table_t(this->default_grind_range_table)},
{"FavoredDringRangeTable", json_for_fixed_table_t(this->favored_grind_range_table)},
});
}
void WeaponShopRandomSet::print(FILE* stream) const {
phosg::fwrite_fmt(stream, "WeaponShopRandomSet\n");
for (size_t table_index = 0; table_index < this->weapon_type_weight_tables.size(); table_index++) {
phosg::fwrite_fmt(stream, " Weapon type weight table {}\n", table_index);
for (size_t z = 0; z < this->weapon_type_weight_tables[table_index].size(); z++) {
phosg::fwrite_fmt(stream, " {:02X} ({:<10}):", z, name_for_section_id(z));
for (const auto& cell : this->weapon_type_weight_tables[table_index][z]) {
if (cell.value == 0x39) {
phosg::fwrite_fmt(stream, " {:02X} (SECID1) @ {:03}", cell.value, cell.weight);
} else if (cell.value == 0x3A) {
phosg::fwrite_fmt(stream, " {:02X} (SECID2) @ {:03}", cell.value, cell.weight);
} else {
const auto& def = this->type_defs.at(cell.value);
phosg::fwrite_fmt(stream, " {:02X} (00{:02X}{:02X}) @ {:03}", cell.value, def.first, def.second, cell.weight);
}
}
phosg::fwrite_fmt(stream, "\n");
}
}
auto print_fixed_table_1d = [&]<size_t Count>(const std::array<IntPairT<uint32_t>, Count>& table) -> void {
for (size_t z = 0; z < Count; z++) {
const auto& cell = table[z];
phosg::fwrite_fmt(stream, " {:02}: {:03}-{:03}\n", z, cell.first, cell.second);
}
};
auto print_fixed_table_2d = [&]<size_t RowCount, size_t RowSize>(const std::array<std::array<IntPairT<uint32_t>, RowSize>, RowCount>& table) -> void {
for (size_t y = 0; y < RowCount; y++) {
phosg::fwrite_fmt(stream, " {:02X}:", y);
for (size_t x = 0; x < RowSize; x++) {
const auto& cell = table[y][x];
phosg::fwrite_fmt(stream, " {:02} @ {:03}", cell.first, cell.second);
}
phosg::fwrite_fmt(stream, "\n");
}
};
phosg::fwrite_fmt(stream, " Bonus type table 1\n");
print_fixed_table_2d(this->bonus_type_table1);
phosg::fwrite_fmt(stream, " Bonus type table 2\n");
print_fixed_table_2d(this->bonus_type_table2);
phosg::fwrite_fmt(stream, " Bonus range table 1\n");
print_fixed_table_1d(this->bonus_range_table1);
phosg::fwrite_fmt(stream, " Bonus range table 2\n");
print_fixed_table_1d(this->bonus_range_table2);
phosg::fwrite_fmt(stream, " Special mode table\n");
print_fixed_table_2d(this->special_mode_table);
phosg::fwrite_fmt(stream, " Default grind range table\n");
print_fixed_table_1d(this->default_grind_range_table);
phosg::fwrite_fmt(stream, " Favored grind range table\n");
print_fixed_table_1d(this->favored_grind_range_table);
}
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include <array>
#include <phosg/Encoding.hh>
#include <phosg/JSON.hh>
#include "StaticGameData.hh"
#include "Text.hh"
#include "Types.hh"
struct ShopRandomSetBase {
template <typename T>
struct IntPairT {
union {
T first;
T value;
T min;
} __attribute__((packed));
union {
T second;
T weight;
T max;
} __attribute__((packed));
IntPairT() = default;
template <typename OtherT>
IntPairT(const IntPairT<OtherT>& v) : first(v.first), second(v.second) {}
IntPairT(const phosg::JSON& v) : first(v.get_int(0)), second(v.get_int(1)) {}
phosg::JSON json() const {
return phosg::JSON::list({this->first, this->second});
}
} __attribute__((packed));
};
struct ArmorShopRandomSet : ShopRandomSetBase {
// This struct parses and accesses data from ArmorRandom.rel
ArmorShopRandomSet(const void* data, size_t size, bool big_endian);
ArmorShopRandomSet(const std::string& data, bool big_endian);
explicit ArmorShopRandomSet(const phosg::JSON& json);
template <bool BE>
void parse_t(const void* data, size_t size);
template <bool BE>
std::string serialize_binary_t() const;
std::string serialize_binary(bool big_endian) const;
phosg::JSON json() const;
void print(FILE* stream) const;
std::vector<std::vector<IntPairT<uint8_t>>> armor_table;
std::vector<std::vector<IntPairT<uint8_t>>> shield_table;
std::vector<std::vector<IntPairT<uint8_t>>> unit_table;
};
struct ToolShopRandomSet : ShopRandomSetBase {
// This struct parses and accesses data from ToolRandom.rel
struct TechDiskLevelEntry {
enum class Mode : uint8_t {
LEVEL_1 = 0,
PLAYER_LEVEL_DIVISOR = 1,
RANDOM_IN_RANGE = 2,
};
Mode mode;
uint8_t player_level_divisor_or_min_level;
uint8_t max_level;
TechDiskLevelEntry() = default;
explicit TechDiskLevelEntry(const phosg::JSON& json);
phosg::JSON json() const;
} __packed_ws__(TechDiskLevelEntry, 3);
ToolShopRandomSet(const void* data, size_t size, bool big_endian);
ToolShopRandomSet(const std::string& data, bool big_endian);
explicit ToolShopRandomSet(const phosg::JSON& json);
template <bool BE>
void parse_t(const void* data, size_t size);
template <bool BE>
std::string serialize_binary_t() const;
std::string serialize_binary(bool big_endian) const;
phosg::JSON json() const;
void print(FILE* stream) const;
static const std::vector<std::pair<uint8_t, uint8_t>> item_defs;
static const std::array<uint8_t, 0x13> tech_num_map;
std::vector<std::vector<uint8_t>> common_recovery_table;
std::vector<std::vector<IntPairT<uint8_t>>> rare_recovery_table;
std::vector<std::vector<IntPairT<uint8_t>>> tech_disk_table;
std::vector<std::vector<TechDiskLevelEntry>> tech_disk_level_table;
};
struct WeaponShopRandomSet : ShopRandomSetBase {
// This struct parses and accesses data from WeaponRandom*.rel
WeaponShopRandomSet(const void* data, size_t size, bool big_endian);
WeaponShopRandomSet(const std::string& data, bool big_endian);
explicit WeaponShopRandomSet(const phosg::JSON& json);
template <bool BE>
void parse_t(const void* data, size_t size);
template <bool BE>
std::string serialize_binary_t() const;
std::string serialize_binary(bool big_endian) const;
phosg::JSON json() const;
void print(FILE* stream) const;
static const std::array<std::pair<uint8_t, uint8_t>, 0x48> type_defs;
static const std::array<std::pair<uint8_t, uint8_t>, 10> type_defs_39;
static const std::array<std::pair<uint8_t, uint8_t>, 10> type_defs_3A;
static const std::array<int8_t, 20> bonus_values;
std::vector<std::vector<std::vector<IntPairT<uint8_t>>>> weapon_type_weight_tables; // [table_index][section_id][entry_index]
std::array<std::array<IntPairT<uint32_t>, 6>, 9> bonus_type_table1; // [table_index][entry_index]
std::array<std::array<IntPairT<uint32_t>, 6>, 9> bonus_type_table2; // [table_index][entry_index]
std::array<IntPairT<uint32_t>, 9> bonus_range_table1; // [table_index]
std::array<IntPairT<uint32_t>, 9> bonus_range_table2; // [table_index]
std::array<std::array<IntPairT<uint32_t>, 3>, 8> special_mode_table; // [table_index][entry_index]
std::array<IntPairT<uint32_t>, 6> default_grind_range_table; // [table_index]
std::array<IntPairT<uint32_t>, 6> favored_grind_range_table; // [table_index]
};
+390
View File
@@ -0,0 +1,390 @@
#include "TekkerAdjustmentSet.hh"
#include <algorithm>
#include "CommonFileFormats.hh"
#include "StaticGameData.hh"
#include "Types.hh"
static const std::array<int8_t, 11> delta_table = {-10, -5, -3, -2, -1, 0, 1, 2, 3, 5, 10};
static const std::unordered_map<int8_t, size_t> reverse_delta_table = {
{-10, 0}, {-5, 1}, {-3, 2}, {-2, 3}, {-1, 4}, {0, 5}, {1, 6}, {2, 7}, {3, 8}, {5, 9}, {10, 10}};
struct DeltaProbabilityEntry {
uint8_t delta_index;
uint8_t count_default;
uint8_t count_favored;
} __packed_ws__(DeltaProbabilityEntry, 3);
struct LuckTableEntry {
uint8_t delta_index;
int8_t luck;
} __packed_ws__(LuckTableEntry, 2);
template <bool BE>
struct ProbTableRefT {
U32T<BE> offset;
U32T<BE> count;
} __packed_ws_be__(ProbTableRefT, 8);
template <bool BE>
struct RootT {
// Each section ID's favored weapon class has different probabilities than those used for all other weapons. The
// tables are labeled with (D) for the default values and (F) for the favored-class values.
// Note that the favored bonuses for Redria are all zero; these values are unused because Redria does not have a
// favored weapon type. Curiously, Yellowboze also does not have a favored weapon type, but the values for Yellowboze
// are not all zero.
// This table specifies how likely a special is to be upgraded or downgraded by one level.
// In PSO V3, the special upgrade table is:
// Viridia => (D) +1=10%, 0=60%, -1=30%
// Viridia => (F) +1=25%, 0=50%, -1=25%
// Greennill => (D) +1=25%, 0=65%, -1=10%
// Greennill => (F) +1=40%, 0=55%, -1=5%
// Skyly => (D) +1=15%, 0=70%, -1=15%
// Skyly => (F) +1=30%, 0=60%, -1=10%
// Bluefull => (D) +1=10%, 0=60%, -1=30%
// Bluefull => (F) +1=25%, 0=50%, -1=25%
// Purplenum => (D) +1=25%, 0=65%, -1=10%
// Purplenum => (F) +1=40%, 0=55%, -1=5%
// Pinkal => (D) +1=15%, 0=70%, -1=15%
// Pinkal => (F) +1=30%, 0=60%, -1=10%
// Redria => (D) +1=20%, 0=60%, -1=20%
// Redria => (F) +1=0%, 0=0%, -1=0%
// Oran => (D) +1=15%, 0=70%, -1=15%
// Oran => (F) +1=30%, 0=60%, -1=10%
// Yellowboze => (D) +1=25%, 0=65%, -1=10%
// Yellowboze => (F) +1=40%, 0=55%, -1=5%
// Whitill => (D) +1=10%, 0=60%, -1=30%
// Whitill => (F) +1=25%, 0=50%, -1=25%
U32T<BE> special_delta_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// This table specifies how likely a weapon's grind is to be upgraded or downgraded, and by how much. The final grind
// value is clamped to the range between 0 and the weapon's maximum grind from ItemPMT, inclusive.
// In PSO V3, the grind delta table is:
// Viridia => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Viridia => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
// Greennill => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Greennill => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Skyly => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Skyly => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Bluefull => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Bluefull => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
// Purplenum => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Purplenum => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Pinkal => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Pinkal => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Redria => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Redria => (F) +3=0%, +2=0%, +1=0%, 0=0%, -1=0%, -2=0%, -3=0%
// Oran => (D) +3=0%, +2=7%, +1=10%, 0=60%, -1=13%, -2=7%, -3=3%
// Oran => (F) +3=3%, +2=12%, +1=20%, 0=50%, -1=10%, -2=5%, -3=0%
// Yellowboze => (D) +3=0%, +2=5%, +1=10%, 0=70%, -1=10%, -2=5%, -3=0%
// Yellowboze => (F) +3=3%, +2=7%, +1=20%, 0=60%, -1=10%, -2=0%, -3=0%
// Whitill => (D) +3=3%, +2=7%, +1=13%, 0=60%, -1=10%, -2=7%, -3=0%
// Whitill => (F) +3=5%, +2=13%, +1=25%, 0=50%, -1=7%, -2=0%, -3=0%
U32T<BE> grind_delta_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// This table specifies how likely a weapon's bonuses are to be upgraded or downgraded, and by how much. The final
// bonuses are capped above at 100, but there is no lower limit (so negative results are possible).
// In PSO V3, the bonus delta table is:
// Viridia => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Viridia => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
// Greennill => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Greennill => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Skyly => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Skyly => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Bluefull => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Bluefull => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
// Purplenum => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Purplenum => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Pinkal => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Pinkal => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Redria => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Redria => (F) +10=0%, +5=0%, 0=0%, -5=0%, -10=0%
// Oran => (D) +10=10%, +5=25%, 0=50%, -5=10%, -10=5%
// Oran => (F) +10=13%, +5=30%, 0=50%, -5=5%, -10=2%
// Yellowboze => (D) +10=5%, +5=10%, 0=50%, -5=25%, -10=10%
// Yellowboze => (F) +10=8%, +5=15%, 0=50%, -5=20%, -10=7%
// Whitill => (D) +10=5%, +5=15%, 0=60%, -5=15%, -10=5%
// Whitill => (F) +10=8%, +5=20%, 0=60%, -5=10%, -10=2%
U32T<BE> bonus_delta_table_offset; // [{c, o -> (DeltaProbabilityEntry)[10][c]})
// There is a secondary computation done during weapon adjustment that appears to determine how "good" the resulting
// weapon is compared to its original state. If the result of this computation is positive, the game plays a jingle
// when the tekker result is accepted. These tables describe how much each delta affects this value, which we call
// luck.
// In PSO V3, the special upgrade luck table is:
// +1 => +20, 0 => 0, -1 => -20
U32T<BE> special_luck_table_offset; // LuckTableEntry[...]; ending with FF FF
// In PSO V3, the grind delta luck table is:
// +3 => +10, +2 => +5, +1 => +3, 0 => 0, -1 => -3, -2 => -5, -3 => -10
U32T<BE> grind_luck_table_offset; // LuckTableEntry[...]; ending with FF FF
// In PSO V3, the bonus delta luck table is:
// +10 => +15, +5 => +8, 0 => 0, -5 => -8, -10 => -15
U32T<BE> bonus_luck_table_offset; // LuckTableEntry[...]; ending with FF FF
} __packed_ws_be__(RootT, 0x18);
uint8_t TekkerAdjustmentSet::favored_weapon_type_for_section_id(uint8_t section_id) {
// The favored weapon type table is hardcoded in the game client. The table is:
// Viridia shots
// Greennill rifles
// Skyly swords
// Bluefull partisans
// Purplenum mechguns
// Pinkal canes
// Redria (none)
// Oran daggers
// Yellowboze (none)
// Whitill slicers
static const std::array<uint8_t, 10> data{0x09, 0x07, 0x02, 0x04, 0x08, 0x0A, 0xFF, 0x03, 0xFF, 0x05};
return data.at(section_id);
}
TekkerAdjustmentSet::TekkerAdjustmentSet(const void* data, size_t size, bool big_endian) {
if (big_endian) {
this->parse_t<true>(data, size);
} else {
this->parse_t<false>(data, size);
}
}
TekkerAdjustmentSet::TekkerAdjustmentSet(const std::string& data, bool big_endian)
: TekkerAdjustmentSet(data.data(), data.size(), big_endian) {}
TekkerAdjustmentSet::TekkerAdjustmentSet(const phosg::JSON& json) {
auto parse_delta_table = [](const phosg::JSON& json) -> std::array<Table, 10> {
if (!json.is_dict() || json.size() != 10) {
throw std::runtime_error("Invalid structure for TekkerAdjustmentSet JSON delta table");
}
std::array<Table, 10> ret;
for (size_t section_id = 0; section_id < 10; section_id++) {
auto& table = ret[section_id];
for (const auto& [k, v] : json.at(name_for_section_id(section_id)).as_dict()) {
auto prob = v->as_int();
table.probs.emplace(stoll(k), prob);
table.total += prob;
}
}
return ret;
};
this->favored_special_delta_table = parse_delta_table(json.at("FavoredSpecialDeltaTable"));
this->default_special_delta_table = parse_delta_table(json.at("DefaultSpecialDeltaTable"));
this->favored_grind_delta_table = parse_delta_table(json.at("FavoredGrindDeltaTable"));
this->default_grind_delta_table = parse_delta_table(json.at("DefaultGrindDeltaTable"));
this->favored_bonus_delta_table = parse_delta_table(json.at("FavoredBonusDeltaTable"));
this->default_bonus_delta_table = parse_delta_table(json.at("DefaultBonusDeltaTable"));
auto parse_luck_table = [](const phosg::JSON& json) -> std::unordered_map<int8_t, int8_t> {
std::unordered_map<int8_t, int8_t> ret;
for (const auto& [k, v] : json.as_dict()) {
ret.emplace(stoll(k), v->as_int());
}
return ret;
};
this->special_luck_table = parse_luck_table(json.at("SpecialLuckTable"));
this->grind_luck_table = parse_luck_table(json.at("GrindLuckTable"));
this->bonus_luck_table = parse_luck_table(json.at("BonusLuckTable"));
}
template <bool BE>
void TekkerAdjustmentSet::parse_t(const void* data, size_t size) {
phosg::StringReader r(data, size);
const auto& root = r.pget<RootT<BE>>(r.pget<U32T<BE>>(size - 0x10));
auto parse_delta_table = [&r](std::array<Table, 10>& favored_tables, std::array<Table, 10>& default_tables, uint32_t ref_offset) -> void {
const auto& ref = r.pget<ProbTableRefT<BE>>(ref_offset);
auto* entries = &r.pget<DeltaProbabilityEntry>(ref.offset, sizeof(DeltaProbabilityEntry) * ref.count * 10);
for (size_t section_id = 0; section_id < 10; section_id++) {
auto& favored_table = favored_tables[section_id];
auto& default_table = default_tables[section_id];
for (size_t z = 0; z < ref.count; z++) {
const auto& entry = entries[section_id * ref.count + z];
int8_t delta = delta_table.at(entry.delta_index);
favored_table.probs.emplace(delta, entry.count_favored);
favored_table.total += entry.count_favored;
default_table.probs.emplace(delta, entry.count_default);
default_table.total += entry.count_default;
}
}
};
parse_delta_table(this->favored_special_delta_table, this->default_special_delta_table, root.special_delta_table_offset);
parse_delta_table(this->favored_grind_delta_table, this->default_grind_delta_table, root.grind_delta_table_offset);
parse_delta_table(this->favored_bonus_delta_table, this->default_bonus_delta_table, root.bonus_delta_table_offset);
auto parse_luck_table = [&r](uint32_t offset) -> std::unordered_map<int8_t, int8_t> {
auto sub_r = r.sub(offset);
std::unordered_map<int8_t, int8_t> ret;
for (;;) {
const auto& entry = sub_r.get<LuckTableEntry>();
if (entry.delta_index == 0xFF) {
break;
}
ret.emplace(delta_table.at(entry.delta_index), entry.luck);
}
return ret;
};
this->special_luck_table = parse_luck_table(root.special_luck_table_offset);
this->grind_luck_table = parse_luck_table(root.grind_luck_table_offset);
this->bonus_luck_table = parse_luck_table(root.bonus_luck_table_offset);
}
template <bool BE>
std::string TekkerAdjustmentSet::serialize_binary_t() const {
RELFileWriter<BE> rel;
auto serialize_delta_tables = [&rel](const std::array<Table, 10>& favored_tables, const std::array<Table, 10>& default_tables) -> ProbTableRefT<BE> {
std::set<int8_t> all_deltas;
for (size_t section_id = 0; section_id < 10; section_id++) {
for (const auto& [delta, _] : favored_tables[section_id].probs) {
all_deltas.emplace(delta);
}
for (const auto& [delta, _] : default_tables[section_id].probs) {
all_deltas.emplace(delta);
}
}
ProbTableRefT<BE> ret{rel.w.size(), all_deltas.size()};
for (size_t section_id = 0; section_id < 10; section_id++) {
for (auto delta_it = all_deltas.rbegin(); delta_it != all_deltas.rend(); delta_it++) {
DeltaProbabilityEntry entry;
entry.delta_index = reverse_delta_table.at(*delta_it);
try {
entry.count_favored = favored_tables[section_id].probs.at(*delta_it);
} catch (const std::out_of_range&) {
}
try {
entry.count_default = default_tables[section_id].probs.at(*delta_it);
} catch (const std::out_of_range&) {
}
rel.template put<DeltaProbabilityEntry>(entry);
}
}
return ret;
};
auto special_delta_ref = serialize_delta_tables(this->favored_special_delta_table, this->default_special_delta_table);
auto grind_delta_ref = serialize_delta_tables(this->favored_grind_delta_table, this->default_grind_delta_table);
auto bonus_delta_ref = serialize_delta_tables(this->favored_bonus_delta_table, this->default_bonus_delta_table);
auto serialize_luck_table = [&rel](const std::unordered_map<int8_t, int8_t>& table) -> uint32_t {
uint32_t ret = rel.w.size();
std::vector<std::pair<uint8_t, int8_t>> entries;
for (const auto& [delta, luck] : table) {
entries.emplace_back(std::make_pair(reverse_delta_table.at(delta), luck));
}
std::sort(entries.begin(), entries.end(), std::greater<std::pair<uint8_t, int8_t>>());
for (const auto& [delta_index, luck] : entries) {
rel.w.put_u8(delta_index);
rel.w.put_s8(luck);
}
rel.w.put_u16(0xFFFF);
return ret;
};
RootT<BE> root;
root.special_luck_table_offset = serialize_luck_table(this->special_luck_table);
root.grind_luck_table_offset = serialize_luck_table(this->grind_luck_table);
root.bonus_luck_table_offset = serialize_luck_table(this->bonus_luck_table);
rel.align(4);
rel.relocations.emplace(rel.w.size());
root.special_delta_table_offset = rel.template put<ProbTableRefT<BE>>(special_delta_ref);
rel.relocations.emplace(rel.w.size());
root.grind_delta_table_offset = rel.template put<ProbTableRefT<BE>>(grind_delta_ref);
rel.relocations.emplace(rel.w.size());
root.bonus_delta_table_offset = rel.template put<ProbTableRefT<BE>>(bonus_delta_ref);
uint32_t root_offset = rel.template put<RootT<BE>>(root);
for (size_t z = 1; z <= sizeof(RootT<BE>) / 4; z++) {
rel.relocations.emplace(rel.w.size() - (z * 4));
}
return rel.finalize(root_offset);
}
std::string TekkerAdjustmentSet::serialize_binary(bool big_endian) const {
return big_endian ? this->serialize_binary_t<true>() : this->serialize_binary_t<false>();
}
phosg::JSON TekkerAdjustmentSet::json() const {
auto ret = phosg::JSON::dict();
auto serialize_delta_table = [](const std::array<Table, 10>& table) -> phosg::JSON {
auto ret = phosg::JSON::dict();
for (size_t section_id = 0; section_id < 10; section_id++) {
auto secid_ret = phosg::JSON::dict();
for (const auto& [k, v] : table[section_id].probs) {
secid_ret.emplace(std::format("{}", k), v);
}
ret.emplace(name_for_section_id(section_id), std::move(secid_ret));
}
return ret;
};
ret.emplace("FavoredSpecialDeltaTable", serialize_delta_table(this->favored_special_delta_table));
ret.emplace("DefaultSpecialDeltaTable", serialize_delta_table(this->default_special_delta_table));
ret.emplace("FavoredGrindDeltaTable", serialize_delta_table(this->favored_grind_delta_table));
ret.emplace("DefaultGrindDeltaTable", serialize_delta_table(this->default_grind_delta_table));
ret.emplace("FavoredBonusDeltaTable", serialize_delta_table(this->favored_bonus_delta_table));
ret.emplace("DefaultBonusDeltaTable", serialize_delta_table(this->default_bonus_delta_table));
auto serialize_luck_table = [](const std::unordered_map<int8_t, int8_t>& table) -> phosg::JSON {
auto ret = phosg::JSON::dict();
for (const auto& [k, v] : table) {
ret.emplace(std::format("{}", k), v);
}
return ret;
};
ret.emplace("SpecialLuckTable", serialize_luck_table(this->special_luck_table));
ret.emplace("GrindLuckTable", serialize_luck_table(this->grind_luck_table));
ret.emplace("BonusLuckTable", serialize_luck_table(this->bonus_luck_table));
return ret;
}
void TekkerAdjustmentSet::print(FILE* stream) const {
phosg::fwrite_fmt(stream, "TekkerAdjustmentSet\n");
auto print_table = [stream](const std::array<Table, 10>& table, const std::unordered_map<int8_t, int8_t>& luck_table) -> void {
for (size_t section_id = 0; section_id < 10; section_id++) {
phosg::fwrite_fmt(stream, " {:<10}:", name_for_section_id(section_id));
std::vector<std::pair<int8_t, size_t>> sorted_probs;
for (const auto& [delta, prob] : table[section_id].probs) {
sorted_probs.emplace_back(delta, prob);
}
std::sort(sorted_probs.begin(), sorted_probs.end());
for (const auto& [delta, prob] : sorted_probs) {
int8_t luck = luck_table.at(delta);
phosg::fwrite_fmt(stream, " {:>2} @ {:>2} ({:>2})", delta, prob, luck);
}
phosg::fwrite_fmt(stream, "\n");
}
};
phosg::fwrite_fmt(stream, " Favored special deltas:\n");
print_table(this->favored_special_delta_table, this->special_luck_table);
phosg::fwrite_fmt(stream, " Default special deltas:\n");
print_table(this->default_special_delta_table, this->special_luck_table);
phosg::fwrite_fmt(stream, " Favored grind deltas:\n");
print_table(this->favored_grind_delta_table, this->grind_luck_table);
phosg::fwrite_fmt(stream, " Default grind deltas:\n");
print_table(this->default_grind_delta_table, this->grind_luck_table);
phosg::fwrite_fmt(stream, " Favored bonus deltas:\n");
print_table(this->favored_bonus_delta_table, this->bonus_luck_table);
phosg::fwrite_fmt(stream, " Default bonus deltas:\n");
print_table(this->default_bonus_delta_table, this->bonus_luck_table);
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <array>
#include <phosg/Encoding.hh>
#include <phosg/JSON.hh>
#include "EnemyType.hh"
#include "GSLArchive.hh"
#include "PSOEncryption.hh"
#include "StaticGameData.hh"
#include "Text.hh"
#include "Types.hh"
struct TekkerAdjustmentSet {
// This struct parses and accesses data from JudgeItem.rel
TekkerAdjustmentSet(const void* data, size_t size, bool big_endian);
TekkerAdjustmentSet(const std::string& data, bool big_endian);
explicit TekkerAdjustmentSet(const phosg::JSON& json);
template <bool BE>
void parse_t(const void* data, size_t size);
template <bool BE>
std::string serialize_binary_t() const;
std::string serialize_binary(bool big_endian) const;
phosg::JSON json() const;
void print(FILE* stream) const;
static uint8_t favored_weapon_type_for_section_id(uint8_t section_id);
struct Table {
std::unordered_map<int8_t, size_t> probs;
size_t total = 0;
};
std::array<Table, 10> favored_special_delta_table;
std::array<Table, 10> default_special_delta_table;
std::array<Table, 10> favored_grind_delta_table;
std::array<Table, 10> default_grind_delta_table;
std::array<Table, 10> favored_bonus_delta_table;
std::array<Table, 10> default_bonus_delta_table;
std::unordered_map<int8_t, int8_t> special_luck_table;
std::unordered_map<int8_t, int8_t> grind_luck_table;
std::unordered_map<int8_t, int8_t> bonus_luck_table;
};
-1
View File
@@ -1 +0,0 @@
../tables/ItemMagEdit-bb-v4.prs
@@ -15,7 +15,7 @@ reloc0:
start:
xor eax, eax
mov edx, esp
mov esp, <VERS 0x009EC359 0x009F61B0 0x009F81B0>
mov esp, <VERS 0x009EC35B 0x009F61B0 0x009F81B0>
mov ecx, 0x3C
again:
push 0
+280 -16
View File
@@ -622,25 +622,289 @@
// bytes); textual descriptions are not supported here.
"QuestF95EResultItems": [
[
[0x00900000, 0x00900100, 0x00900200, 0x00900300, 0x00900400, 0x00900500, 0x00900600, 0x00900700, 0x00900800, 0x00B40000, 0x01014E00, 0x01030700, 0x01034100, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00B90000, 0x00340000, 0x00090100, 0x00900200, 0x00900700, 0x002C0000, 0x002D0000, 0x01023500, 0x00010600, 0x00010500, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00B60000, 0x008A0100, 0x00100100, 0x00100200, 0x00100300, 0x00100400, 0x00100500, 0x00100600, 0x00270000, 0x00010700, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00B70000, 0x00100100, 0x00100200, 0x00100300, 0x00100400, 0x00100500, 0x00100600, 0x00290000, 0x008A0000, 0x008A0200, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
// Black Paper's Deal Dorphon route
[
// Normal
0x00900000, // DB'S SABER 3062
0x00900200, // DB'S SABER 3069 Chris
0x00900800, // DB'S SABER 3077
0x01014E00, // OFFICER UNIFORM
0x01030700, // God/Mind
0x01034100, // God/Battle
0x03030000, // Sol Atomizer
0x030F0000, // AddSlot
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Hard
0x00010500, // DB'S SABER (Ephinea)
0x00010600, // KALADBOLG (Ephinea)
0x002C0000, // ELYSION
0x00340000, // RED SWORD
0x00900000, // DB'S SABER 3062
0x00900100, // DB'S SABER 3067
0x00900200, // DB'S SABER 3069 Chris (Ephinea)
0x00900300, // DB'S SABER 3064
0x00900400, // DB'S SABER 3069 Torato
0x00900500, // DB'S SABER 3073
0x00900600, // DB'S SABER 3070
0x00900700, // DB'S SABER 3075
0x00900800, // DB'S SABER 3077 (Ephinea)
0x00B90000, // FLAMBERGE
0x01023500, // SECURE FEET
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Very Hard
0x00010700, // DURANDAL
0x00100100, // AGITO 1975 (real)
0x00100200, // AGITO 1983
0x00100300, // AGITO 2001
0x00100400, // AGITO 1991
0x00100500, // AGITO 1977
0x00100600, // AGITO 1980
0x00270000, // ANCIENT SABER
0x008A0100, // YASHA
0x00B60000, // GUREN
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Ultimate
0x00100100, // AGITO 1975 (real)
0x00100200, // AGITO 1983
0x00100300, // AGITO 2001
0x00100400, // AGITO 1991
0x00100500, // AGITO 1977
0x00100600, // AGITO 1980
0x00290000, // YAMIGARASU
0x008A0000, // SANGE
0x008A0200, // KAMUI
0x00B70000, // SHOUREN
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
],
], [
[0x01028B00, 0x01022800, 0x01013400, 0x01030300, 0x01030B00, 0x03180700, 0x00550000, 0x01032900, 0x01032F00, 0x01032C00, 0x01032300, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x01028C00, 0x01021500, 0x01028A00, 0x01014000, 0x01034400, 0x01034600, 0x01034500, 0x01034700, 0x03180700, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00CB0000, 0x003A0000, 0x008C0200, 0x01022B00, 0x00500000, 0x000B0600, 0x000A0600, 0x000A0400, 0x00550000, 0x00230000, 0x003B0000, 0x03180700, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00510000, 0x01035200, 0x01032000, 0x01033E00, 0x01022900, 0x03180700, 0x000B0400, 0x000A0600, 0x00560000, 0x003B0000, 0x00230000, 0x000A0500, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
// Black Paper's Deal Rappy route
[
// Normal
0x01013400, // SMOKING PLATE
0x01022800, // TRIPOLIC SHIELD
0x01028B00, // BUNNY EARS
0x01030300, // God/Power
0x01030B00, // God/Arm
0x01032300, // Resist/Burning
0x01032600, // Resist/Blizzard
0x01032900, // Resist/Storm (Ephinea)
0x01032C00, // Resist/Holy
0x01032F00, // Resist/Devil (Ephinea)
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Hard
0x01014000, // RED COAT
0x01021500, // INVISIBLE GUARD
0x01028A00, // YATA MIRROR
0x01028C00, // CAT EARS
0x01034200, // Cure/Poison
0x01034300, // Cure/Paralysis
0x01034400, // Cure/Slow
0x01034500, // Cure/Confuse
0x01034600, // Cure/Freeze
0x01034700, // Cure/Shock
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Very Hard
0x000A0400, // CLUB OF LACONIUM (Ephinea)
0x000A0600, // CLUB OF ZUMIURAN
0x000B0600, // ALIVE AQHU
0x00230000, // STING TIP
0x003A0000, // MADAM'S PARASOL
0x003B0000, // MADAM'S UMBRELLA
0x00500000, // WINDMILL
0x00550000, // RABBIT WAND
0x00CB0000, // TYRELL'S PARASOL
0x01022B00, // KASAMI BRACER
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030E0A00, // Book of HITOGATA (Ephinea)
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Ultimate
0x000A0500, // MACE OF ADAMAN
0x000A0600, // CLUB OF ZUMIURAN
0x000B0400, // BATTLE VERGE
0x00230000, // STING TIP (Ephinea)
0x00250000, // TECHNICAL CROZIER
0x003B0000, // MADAM'S UMBRELLA
0x00500000, // WINDMILL
0x00510000, // EVIL CURST
0x00560000, // PLANTAIN LEAF
0x01022900, // STANDSTILL SHIELD
0x01032000, // God/Ability
0x01033E00, // God/Technique
0x01035200, // DIVINE PROTECTION
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x03180700, // Rappy's Beak
0x04000000, // Meseta
],
], [
[0x01013200, 0x002F0100, 0x00B30000, 0x005E0000, 0x000E0200, 0x002E0000, 0x00950000, 0x009A0000, 0x002F0000, 0x01031B00, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00C00000, 0x00D20000, 0x008D0000, 0x01012E00, 0x008B0000, 0x00090700, 0x004E0000, 0x006D0000, 0x00150000, 0x008B0200, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00AA0000, 0x01014100, 0x01015100, 0x01022300, 0x003F0000, 0x00410000, 0x00050700, 0x00050600, 0x00050500, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00AF0000, 0x00430000, 0x01035100, 0x00CD0000, 0x00990000, 0x006C0000, 0x00450000, 0x006B0000, 0x00120000, 0x00650000, 0x01022900, 0x00130000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
// Black Paper's Deal Zu route
[
// Normal
0x000E0100, // STAG CUTLERY
0x000E0200, // TWIN BRAND
0x002E0000, // METEOR CUDGEL
0x002F0000, // MONKEY KING BAR
0x005E0000, // TWIN BLAZE
0x00950000, // PARTISAN of LIGHTNING
0x009A0000, // DEMOLITION COMET
0x00B30000, // VIVIENNE
0x01013200, // ELECTRO FRAME
0x01031B00, // God/Body
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot (Ephinea)
0x04000000, // Meseta
], [
// Hard
0x00090700, // FINAL IMPACT
0x00150000, // FLAME VISIT
0x004E0000, // PANZER FAUST
0x006D0000, // MASER BEAM
0x008B0000, // PHOTON LAUNCHER
0x008B0200, // RED SCORPIR
0x008D0000, // NUG2000-BAZOOKA
0x00C00000, // CANNON ROUGE
0x00D20000, // ANO BAZOOKA
0x01012E00, // FLAME GARMENT
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Very Hard
0x00050500, // SLICER OF ASSASSIN
0x00050600, // DISKA OF LIBERATOR
0x00050700, // DISKA OF BRAVEMAN
0x003F0000, // FLIGHT CUTTER
0x00400000, // FLIGHT FAN
0x00410000, // RED SLICER
0x00AA0000, // SLICER OF FANATIC
0x01014100, // THIRTEEN
0x01015100, // INFANTRY GEAR
0x01022300, // REGENE GEAR ADV.
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot (Ephinea)
0x04000000, // Meseta
], [
// Ultimate
0x00120000, // SPREAD NEEDLE
0x00130000, // HOLY RAY
0x00430000, // HANDGUN:MILLA
0x00450000, // FROZEN SHOOTER
0x00650000, // YASMINKOV 3000R
0x006B0000, // YASMINKOV 7000V
0x006C0000, // YASMINKOV 9000M
0x00990000, // ANGEL HARP
0x00AF0000, // OPHELIE SEIZE
0x00CD0000, // TANEGASHIMA
0x01035100, // SMARTLINK
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
],
], [], [
[0x00BA0000, 0x000D0300, 0x00430100, 0x00070800, 0x00420100, 0x00C90000, 0x03100000, 0x01029500, 0x01028F00, 0x01029100],
[0x00BB0000, 0x000D0300, 0x00B70000, 0x00420100, 0x00070800, 0x00C90000, 0x01013600, 0x01028A00, 0x01029900, 0x01035100, 0x01035B00, 0x01035200, 0x03100000, 0x03180A00],
[0x00BA0000, 0x00B40000, 0x000D0300, 0x00B60000, 0x00B30000, 0x00070800, 0x00430100, 0x00C90000, 0x01013600, 0x01028A00, 0x01029900, 0x01028500, 0x01034800, 0x01035100, 0x01035B00, 0x01035200, 0x03100000],
[0x00BA0000, 0x00B40000, 0x000D0300, 0x00B60000, 0x00B30000, 0x00070800, 0x00430100, 0x00C90000, 0x01013600, 0x01028A00, 0x01029900, 0x01028500, 0x01034800, 0x01035100, 0x01035B00, 0x01035200],
// Black Paper's Dangerous Deal 2
[
// Normal
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00420100, // MASTER RAVEN (Ephinea)
0x00430100, // LAST SWAN
0x00BA0000, // YUNCHANG
0x00C90000, // DECALOG
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x03100000, // Photon Drop
], [
// Hard
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00420100, // MASTER RAVEN
0x00B40000, // KUSANAGI
0x00B70000, // SHOUREN
0x00BA0000, // YUNCHANG
0x00BB0000, // SNAKE SPIRE
0x00BC0000, // FLAPJACK FLAPPER
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
], [
// Very Hard
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00430100, // LAST SWAN
0x00B30000, // VIVIENNE
0x00B40000, // KUSANAGI
0x00B40000, // KUSANAGI
0x00B60000, // GUREN
0x00BA0000, // YUNCHANG (Ephinea)
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028500, // GRATIA
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
], [
// Ultimate
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00430100, // LAST SWAN
0x00B30000, // VIVIENNE
0x00B40000, // KUSANAGI
0x00B60000, // GUREN
0x00B70000, // SHOUREN
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028500, // GRATIA
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
],
],
],
// Results for quest opcode F95F (used in Gallon's Plan). This list is indexed by type (which is the third argument
+1
View File
@@ -0,0 +1 @@
map_city00_00e.dat
+1
View File
@@ -0,0 +1 @@
map_city00_00o.dat
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ItemMagEdit-pc-v2.prs
-1
View File
@@ -1 +0,0 @@
ItemMagEdit-gc-v3.prs
-1
View File
@@ -1 +0,0 @@
ItemMagEdit-gc-v3.prs
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ItemMagEdit-pc-v2.prs
Binary file not shown.
Binary file not shown.
+363
View File
@@ -0,0 +1,363 @@
{
"ArmorTable": [
[
[0, 33],
[1, 33],
[2, 15],
[3, 10],
[4, 5],
[5, 0],
[6, 4],
[7, 0],
[8, 0],
[9, 0],
[10, 0],
[11, 0],
[12, 0],
[13, 0],
[14, 0],
[15, 0],
[16, 0],
[17, 0],
[18, 0],
[19, 0],
[20, 0]
],
[
[0, 5],
[1, 5],
[2, 10],
[3, 15],
[4, 19],
[5, 19],
[6, 10],
[7, 10],
[8, 5],
[9, 0],
[10, 0],
[11, 2],
[12, 0],
[13, 0],
[14, 0],
[15, 0],
[16, 0],
[17, 0],
[18, 0],
[19, 0],
[20, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 5],
[5, 5],
[6, 10],
[7, 15],
[8, 19],
[9, 19],
[10, 10],
[11, 10],
[12, 5],
[13, 0],
[14, 0],
[15, 2],
[16, 0],
[17, 0],
[18, 0],
[19, 0],
[20, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0],
[6, 2],
[7, 4],
[8, 5],
[9, 5],
[10, 10],
[11, 12],
[12, 14],
[13, 14],
[14, 10],
[15, 9],
[16, 5],
[17, 5],
[18, 3],
[19, 2],
[20, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0],
[6, 0],
[7, 2],
[8, 5],
[9, 5],
[10, 5],
[11, 6],
[12, 10],
[13, 13],
[14, 14],
[15, 14],
[16, 14],
[17, 7],
[18, 3],
[19, 1],
[20, 1]
]
],
"ShieldTable": [
[
[0, 34],
[1, 34],
[2, 15],
[3, 15],
[4, 0],
[5, 0],
[6, 2],
[7, 0],
[8, 0],
[9, 0],
[10, 0],
[11, 0],
[12, 0],
[13, 0],
[14, 0],
[15, 0],
[16, 0],
[17, 0]
],
[
[0, 5],
[1, 10],
[2, 14],
[3, 18],
[4, 18],
[5, 14],
[6, 10],
[7, 5],
[8, 2],
[9, 2],
[10, 2],
[11, 0],
[12, 0],
[13, 0],
[14, 0],
[15, 0],
[16, 0],
[17, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 6],
[4, 10],
[5, 15],
[6, 19],
[7, 18],
[8, 14],
[9, 9],
[10, 4],
[11, 3],
[12, 0],
[13, 2],
[14, 0],
[15, 0],
[16, 0],
[17, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 5],
[5, 5],
[6, 5],
[7, 10],
[8, 14],
[9, 15],
[10, 14],
[11, 11],
[12, 9],
[13, 6],
[14, 4],
[15, 0],
[16, 2],
[17, 0]
],
[
[0, 0],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 4],
[6, 5],
[7, 5],
[8, 8],
[9, 11],
[10, 13],
[11, 13],
[12, 14],
[13, 14],
[14, 9],
[15, 2],
[16, 1],
[17, 1]
]
],
"UnitTable": [
[
[0, 0],
[4, 0],
[8, 0],
[12, 0],
[16, 0],
[24, 0],
[33, 0],
[36, 0],
[39, 0],
[42, 0],
[45, 0],
[1, 0],
[5, 0],
[9, 0],
[13, 0],
[17, 0],
[25, 0],
[34, 0],
[37, 0],
[40, 0],
[43, 0],
[46, 0],
[51, 0],
[54, 0],
[57, 0],
[67, 0]
],
[
[0, 10],
[4, 10],
[8, 10],
[12, 10],
[16, 10],
[24, 10],
[33, 10],
[36, 10],
[39, 10],
[42, 5],
[45, 5],
[1, 0],
[5, 0],
[9, 0],
[13, 0],
[17, 0],
[25, 0],
[34, 0],
[37, 0],
[40, 0],
[43, 0],
[46, 0],
[51, 0],
[54, 0],
[57, 0],
[67, 0]
],
[
[0, 10],
[4, 10],
[8, 10],
[12, 10],
[16, 10],
[24, 10],
[33, 10],
[36, 10],
[39, 10],
[42, 5],
[45, 5],
[1, 0],
[5, 0],
[9, 0],
[13, 0],
[17, 0],
[25, 0],
[34, 0],
[37, 0],
[40, 0],
[43, 0],
[46, 0],
[51, 0],
[54, 0],
[57, 0],
[67, 0]
],
[
[0, 4],
[4, 4],
[8, 4],
[12, 4],
[16, 4],
[24, 4],
[33, 4],
[36, 4],
[39, 4],
[42, 5],
[45, 5],
[1, 5],
[5, 5],
[9, 5],
[13, 5],
[17, 5],
[25, 5],
[34, 5],
[37, 5],
[40, 5],
[43, 3],
[46, 3],
[51, 1],
[54, 1],
[57, 1],
[67, 0]
],
[
[0, 3],
[4, 3],
[8, 3],
[12, 3],
[16, 3],
[24, 3],
[33, 4],
[36, 4],
[39, 4],
[42, 4],
[45, 5],
[1, 5],
[5, 5],
[9, 5],
[13, 5],
[17, 5],
[25, 5],
[34, 5],
[37, 5],
[40, 5],
[43, 5],
[46, 5],
[51, 2],
[54, 2],
[57, 2],
[67, 0]
]
]
}
@@ -0,0 +1,604 @@
{
"Colors": [
[1.0, 0.19999998807907104, 0.099999994039535522, 1.0],
[0.19999998807907104, 0.19999998807907104, 1.0, 1.0],
[1.0, 0.89999997615814209, 0.099999994039535522, 1.0],
[0.099999994039535522, 1.0, 0.099999994039535522, 1.0],
[0.79999995231628418, 0.099999994039535522, 1.0, 1.0],
[0.099999994039535522, 0.099999994039535522, 0.19999998807907104, 1.0],
[0.89999997615814209, 1.0, 1.0, 1.0],
[0.099999994039535522, 0.89999997615814209, 1.0, 1.0],
[0.5, 0.29999998211860657, 0.19999998807907104, 1.0]
],
"Mags": [
{
"EvolutionNumber": 0,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 10, "Eff3": 10, "Eff48": 10, "Eff5": 10, "Eff67": 10}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 10, "Eff3": 10, "Eff48": 10, "Eff5": 10, "Eff67": 10}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 4, "Eff48": 4, "Eff5": 4, "Eff67": 4}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 4, "Eff48": 4, "Eff5": 4, "Eff67": 4}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 2, "Eff5": 2, "Eff67": 2}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 2, "Eff5": 2, "Eff67": 2}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 14, "Eff5": 14, "Eff67": 14}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 14, "Eff5": 14, "Eff67": 14}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"MotionRefs2": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 32, "Eff5": 32, "Eff67": 32},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 31, "Eff5": 31, "Eff67": 31}
},
"MotionRefs2": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 32, "Eff5": 32, "Eff67": 32},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 31, "Eff5": 31, "Eff67": 31}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 3, "Eff5": 3, "Eff67": 3}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 3, "Eff5": 3, "Eff67": 3}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 11, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"MotionRefs2": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 16, "Eff5": 16, "Eff67": 16},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 15, "Eff5": 15, "Eff67": 15}
},
"MotionRefs2": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 16, "Eff5": 16, "Eff67": 16},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 15, "Eff5": 15, "Eff67": 15}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 18, "Eff5": 18, "Eff67": 18},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 17, "Eff5": 17, "Eff67": 17}
},
"MotionRefs2": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 18, "Eff5": 18, "Eff67": 18},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 17, "Eff5": 17, "Eff67": 17}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 35, "Eff5": 35, "Eff67": 35},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 36, "Eff5": 36, "Eff67": 36}
},
"MotionRefs2": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 35, "Eff5": 35, "Eff67": 35},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 36, "Eff5": 36, "Eff67": 36}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 1,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 9, "Eff5": 9, "Eff67": 9},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 8, "Eff5": 8, "Eff67": 8}
},
"MotionRefs2": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 9, "Eff5": 9, "Eff67": 9},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 8, "Eff5": 8, "Eff67": 8}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 40, "Eff2": 40, "Eff3": 40, "Eff48": 46, "Eff5": 40, "Eff67": 46},
"Right": {"Eff1": 39, "Eff2": 39, "Eff3": 39, "Eff48": 45, "Eff5": 39, "Eff67": 45}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 20, "Eff5": 20, "Eff67": 20},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 19, "Eff5": 19, "Eff67": 19}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 20, "Eff5": 20, "Eff67": 20},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 19, "Eff5": 19, "Eff67": 19}
},
"RenderFlags": 0,
"UnknownA2": [1, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 2,
"UnknownA2": [2, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"MotionRefs2": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 21, "Eff2": 21, "Eff3": 21, "Eff48": 54, "Eff5": 21, "Eff67": 54}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 21, "Eff2": 21, "Eff3": 21, "Eff48": 21, "Eff5": 21, "Eff67": 21}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 6,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [8, 9]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [10, 11]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 38, "Eff48": 38, "Eff5": 38, "Eff67": 38}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 38, "Eff48": 38, "Eff5": 38, "Eff67": 38}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 3,
"UnknownA2": [3, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 13]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [14, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
}
],
"UnknownA3": [
{"Flags": 33, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 129, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 24},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 67, "UnknownA2": 255, "UnknownA3": 0, "UnknownA4": -45, "UnknownA5": -15},
{"Flags": 131, "UnknownA2": 255, "UnknownA3": 0, "UnknownA4": -45, "UnknownA5": -15},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 65, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -60, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 65, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -40, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 34, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 30},
{"Flags": 33, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16}
]
}
+606
View File
@@ -0,0 +1,606 @@
{
"Colors": [
[1.0, 0.19999998807907104, 0.099999994039535522, 1.0],
[0.19999998807907104, 0.19999998807907104, 1.0, 1.0],
[1.0, 0.89999997615814209, 0.099999994039535522, 1.0],
[0.099999994039535522, 1.0, 0.099999994039535522, 1.0],
[0.79999995231628418, 0.099999994039535522, 1.0, 1.0],
[0.099999994039535522, 0.099999994039535522, 0.19999998807907104, 1.0],
[0.89999997615814209, 1.0, 1.0, 1.0],
[0.099999994039535522, 0.89999997615814209, 1.0, 1.0],
[0.5, 0.29999998211860657, 0.19999998807907104, 1.0]
],
"Mags": [
{
"EvolutionNumber": 0,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 10, "Eff3": 10, "Eff48": 10, "Eff5": 10, "Eff67": 10}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 4, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 4, "Eff48": 4, "Eff5": 4, "Eff67": 4}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 72, "Eff5": 2, "Eff67": 72}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 2, "Eff5": 2, "Eff67": 2}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 67, "Eff2": 67, "Eff3": 67, "Eff48": 71, "Eff5": 67, "Eff67": 71},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 70, "Eff5": 14, "Eff67": 70}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 14, "Eff5": 14, "Eff67": 14}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"MotionRefs2": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"MotionRefs2": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 11, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"MotionRefs2": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"MotionRefs2": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"MotionRefs2": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 74, "Eff5": 35, "Eff67": 74},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 73, "Eff5": 36, "Eff67": 73}
},
"MotionRefs2": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 35, "Eff5": 35, "Eff67": 35},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 36, "Eff5": 36, "Eff67": 36}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 1,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 76, "Eff5": 9, "Eff67": 76},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 75, "Eff5": 8, "Eff67": 75}
},
"MotionRefs2": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 9, "Eff5": 9, "Eff67": 9},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 8, "Eff5": 8, "Eff67": 8}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 40, "Eff2": 40, "Eff3": 40, "Eff48": 46, "Eff5": 40, "Eff67": 46},
"Right": {"Eff1": 39, "Eff2": 39, "Eff3": 39, "Eff48": 45, "Eff5": 39, "Eff67": 45}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"RenderFlags": 0,
"UnknownA2": [1, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 2,
"UnknownA2": [2, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"MotionRefs2": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 78, "Eff2": 78, "Eff3": 78, "Eff48": 54, "Eff5": 78, "Eff67": 54}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 54, "Eff5": 3, "Eff67": 54}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 12, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 6,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [8, 9]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [10, 11]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 3,
"UnknownA2": [3, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 13]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [14, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [15, 16]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [5, 8]
}
],
"UnknownA3": [
{"Flags": 33, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 129, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 24},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -24, "UnknownA5": 0},
{"Flags": 66, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 65, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -60, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 65, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -40, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 34, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 30},
{"Flags": 33, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 129, "UnknownA2": 246, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 66, "UnknownA2": 216, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40}
]
}
+792
View File
@@ -0,0 +1,792 @@
{
"Colors": [
[1.0, 0.19999998807907104, 0.099999994039535522, 1.0],
[0.19999998807907104, 0.19999998807907104, 1.0, 1.0],
[1.0, 0.89999997615814209, 0.099999994039535522, 1.0],
[0.099999994039535522, 1.0, 0.099999994039535522, 1.0],
[0.79999995231628418, 0.099999994039535522, 1.0, 1.0],
[0.099999994039535522, 0.099999994039535522, 0.19999998807907104, 1.0],
[0.89999997615814209, 1.0, 1.0, 1.0],
[0.099999994039535522, 0.89999997615814209, 1.0, 1.0],
[0.5, 0.29999998211860657, 0.19999998807907104, 1.0]
],
"Mags": [
{
"EvolutionNumber": 0,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 10, "Eff3": 10, "Eff48": 10, "Eff5": 10, "Eff67": 10}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 4, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 4, "Eff48": 4, "Eff5": 4, "Eff67": 4}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 72, "Eff5": 2, "Eff67": 72}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 2, "Eff5": 2, "Eff67": 2}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 67, "Eff2": 67, "Eff3": 67, "Eff48": 71, "Eff5": 67, "Eff67": 71},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 70, "Eff5": 14, "Eff67": 70}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 14, "Eff5": 14, "Eff67": 14}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"MotionRefs2": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"MotionRefs2": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 11, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"MotionRefs2": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"MotionRefs2": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"MotionRefs2": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 74, "Eff5": 35, "Eff67": 74},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 73, "Eff5": 36, "Eff67": 73}
},
"MotionRefs2": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 35, "Eff5": 35, "Eff67": 35},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 36, "Eff5": 36, "Eff67": 36}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 34, "Eff5": 34, "Eff67": 34},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 33, "Eff5": 33, "Eff67": 33}
},
"RenderFlags": 1,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 7, "Eff5": 7, "Eff67": 7},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 6, "Eff5": 6, "Eff67": 6}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 76, "Eff5": 9, "Eff67": 76},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 75, "Eff5": 8, "Eff67": 75}
},
"MotionRefs2": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 9, "Eff5": 9, "Eff67": 9},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 8, "Eff5": 8, "Eff67": 8}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 40, "Eff2": 40, "Eff3": 40, "Eff48": 46, "Eff5": 40, "Eff67": 46},
"Right": {"Eff1": 39, "Eff2": 39, "Eff3": 39, "Eff48": 45, "Eff5": 39, "Eff67": 45}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 0, "Eff5": 0, "Eff67": 0},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 1, "Eff5": 1, "Eff67": 1}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"RenderFlags": 0,
"UnknownA2": [1, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 2,
"UnknownA2": [2, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"MotionRefs2": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 78, "Eff2": 78, "Eff3": 78, "Eff48": 54, "Eff5": 78, "Eff67": 54}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 54, "Eff5": 3, "Eff67": 54}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 12, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 6,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [8, 9]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [10, 11]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 3,
"UnknownA2": [3, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 13]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [14, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [15, 16]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [5, 8]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [17, 18]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [19, 20]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
}
],
"UnknownA3": [
{"Flags": 33, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 129, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 24},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -24, "UnknownA5": 0},
{"Flags": 66, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 65, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -60, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 65, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -40, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 34, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 30},
{"Flags": 33, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 129, "UnknownA2": 246, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 66, "UnknownA2": 216, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 64, "UnknownA2": 6, "UnknownA3": 0, "UnknownA4": 10, "UnknownA5": 50},
{"Flags": 64, "UnknownA2": 6, "UnknownA3": 0, "UnknownA4": -50, "UnknownA5": -10},
{"Flags": 64, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 80},
{"Flags": 64, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -80, "UnknownA5": 0}
]
}
+918
View File
@@ -0,0 +1,918 @@
{
"Colors": [
[1.0, 0.19999998807907104, 0.099999994039535522, 1.0],
[0.19999998807907104, 0.19999998807907104, 1.0, 1.0],
[1.0, 0.89999997615814209, 0.099999994039535522, 1.0],
[0.099999994039535522, 1.0, 0.099999994039535522, 1.0],
[0.79999995231628418, 0.099999994039535522, 1.0, 1.0],
[0.099999994039535522, 0.099999994039535522, 0.19999998807907104, 1.0],
[0.89999997615814209, 1.0, 1.0, 1.0],
[0.099999994039535522, 0.89999997615814209, 1.0, 1.0],
[0.5, 0.29999998211860657, 0.19999998807907104, 1.0],
[1.0, 0.39999997615814209, 0.0, 1.0],
[0.50196075439453125, 0.54509800672531128, 0.97647053003311157, 1.0],
[0.50196075439453125, 0.50196075439453125, 0.0, 1.0],
[0.0, 0.94117641448974609, 0.71372544765472412, 1.0],
[0.79999995231628418, 0.098039209842681885, 0.39215683937072754, 1.0],
[0.49803918600082397, 0.49803918600082397, 0.49803918600082397, 1.0],
[0.99607837200164795, 0.99607837200164795, 0.83529406785964966, 1.0],
[0.99607837200164795, 0.49803918600082397, 0.78431367874145508, 1.0],
[0.0, 0.49803918600082397, 0.32156860828399658, 1.0]
],
"Mags": [
{
"EvolutionNumber": 0,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 4, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 4, "Eff67": 13}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 72, "Eff5": 2, "Eff67": 72}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 2, "Eff2": 2, "Eff3": 2, "Eff48": 72, "Eff5": 2, "Eff67": 72}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 67, "Eff2": 67, "Eff3": 67, "Eff48": 71, "Eff5": 67, "Eff67": 71},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 70, "Eff5": 14, "Eff67": 70}
},
"MotionRefs2": {
"Left": {"Eff1": 67, "Eff2": 67, "Eff3": 67, "Eff48": 71, "Eff5": 67, "Eff67": 71},
"Right": {"Eff1": 14, "Eff2": 14, "Eff3": 14, "Eff48": 70, "Eff5": 14, "Eff67": 70}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"MotionRefs2": {
"Left": {"Eff1": 30, "Eff2": 30, "Eff3": 30, "Eff48": 49, "Eff5": 30, "Eff67": 49},
"Right": {"Eff1": 29, "Eff2": 29, "Eff3": 29, "Eff48": 48, "Eff5": 29, "Eff67": 48}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"MotionRefs2": {
"Left": {"Eff1": 32, "Eff2": 32, "Eff3": 32, "Eff48": 61, "Eff5": 32, "Eff67": 61},
"Right": {"Eff1": 31, "Eff2": 31, "Eff3": 31, "Eff48": 60, "Eff5": 31, "Eff67": 60}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"MotionRefs2": {
"Left": {"Eff1": 28, "Eff2": 28, "Eff3": 28, "Eff48": 51, "Eff5": 28, "Eff67": 51},
"Right": {"Eff1": 27, "Eff2": 27, "Eff3": 27, "Eff48": 50, "Eff5": 27, "Eff67": 50}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"MotionRefs2": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"MotionRefs2": {
"Left": {"Eff1": 18, "Eff2": 18, "Eff3": 18, "Eff48": 66, "Eff5": 18, "Eff67": 66},
"Right": {"Eff1": 17, "Eff2": 17, "Eff3": 17, "Eff48": 65, "Eff5": 17, "Eff67": 65}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 74, "Eff5": 35, "Eff67": 74},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 73, "Eff5": 36, "Eff67": 73}
},
"MotionRefs2": {
"Left": {"Eff1": 35, "Eff2": 35, "Eff3": 35, "Eff48": 74, "Eff5": 35, "Eff67": 74},
"Right": {"Eff1": 36, "Eff2": 36, "Eff3": 36, "Eff48": 73, "Eff5": 36, "Eff67": 73}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"RenderFlags": 0,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"MotionRefs2": {
"Left": {"Eff1": 34, "Eff2": 34, "Eff3": 34, "Eff48": 69, "Eff5": 34, "Eff67": 69},
"Right": {"Eff1": 33, "Eff2": 33, "Eff3": 33, "Eff48": 68, "Eff5": 33, "Eff67": 68}
},
"RenderFlags": 1,
"UnknownA2": [0, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"MotionRefs2": {
"Left": {"Eff1": 7, "Eff2": 7, "Eff3": 7, "Eff48": 42, "Eff5": 7, "Eff67": 42},
"Right": {"Eff1": 6, "Eff2": 6, "Eff3": 6, "Eff48": 41, "Eff5": 6, "Eff67": 41}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 76, "Eff5": 9, "Eff67": 76},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 75, "Eff5": 8, "Eff67": 75}
},
"MotionRefs2": {
"Left": {"Eff1": 9, "Eff2": 9, "Eff3": 9, "Eff48": 76, "Eff5": 9, "Eff67": 76},
"Right": {"Eff1": 8, "Eff2": 8, "Eff3": 8, "Eff48": 75, "Eff5": 8, "Eff67": 75}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 40, "Eff2": 40, "Eff3": 40, "Eff48": 46, "Eff5": 40, "Eff67": 46},
"Right": {"Eff1": 39, "Eff2": 39, "Eff3": 39, "Eff48": 45, "Eff5": 39, "Eff67": 45}
},
"MotionRefs2": {
"Left": {"Eff1": 40, "Eff2": 40, "Eff3": 40, "Eff48": 46, "Eff5": 40, "Eff67": 46},
"Right": {"Eff1": 39, "Eff2": 39, "Eff3": 39, "Eff48": 45, "Eff5": 39, "Eff67": 45}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 53, "Eff5": 20, "Eff67": 53},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 52, "Eff5": 19, "Eff67": 52}
},
"RenderFlags": 0,
"UnknownA2": [1, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"MotionRefs2": {
"Left": {"Eff1": 23, "Eff2": 23, "Eff3": 23, "Eff48": 53, "Eff5": 23, "Eff67": 53},
"Right": {"Eff1": 22, "Eff2": 22, "Eff3": 22, "Eff48": 52, "Eff5": 22, "Eff67": 52}
},
"RenderFlags": 2,
"UnknownA2": [2, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"MotionRefs2": {
"Left": {"Eff1": 25, "Eff2": 25, "Eff3": 25, "Eff48": 56, "Eff5": 25, "Eff67": 56},
"Right": {"Eff1": 24, "Eff2": 24, "Eff3": 24, "Eff48": 55, "Eff5": 24, "Eff67": 55}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"MotionRefs2": {
"Left": {"Eff1": 20, "Eff2": 20, "Eff3": 20, "Eff48": 58, "Eff5": 20, "Eff67": 58},
"Right": {"Eff1": 19, "Eff2": 19, "Eff3": 19, "Eff48": 57, "Eff5": 19, "Eff67": 57}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 78, "Eff2": 78, "Eff3": 78, "Eff48": 54, "Eff5": 78, "Eff67": 54}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 78, "Eff2": 78, "Eff3": 78, "Eff48": 54, "Eff5": 78, "Eff67": 54}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 1,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 12, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 13, "Eff48": 13, "Eff5": 12, "Eff67": 13}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 2,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 6,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [8, 9]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [10, 11]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 38, "Eff2": 38, "Eff3": 59, "Eff48": 59, "Eff5": 38, "Eff67": 59}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 3,
"UnknownA2": [3, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 13]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [14, 255]
},
{
"EvolutionNumber": 3,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [15, 16]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [2, 4]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [5, 8]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"MotionRefs2": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"RenderFlags": 255,
"UnknownA2": [17, 18]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"MotionRefs2": {
"Left": {"Eff1": 81, "Eff2": 81, "Eff3": 81, "Eff48": 81, "Eff5": 81, "Eff67": 81},
"Right": {"Eff1": 80, "Eff2": 80, "Eff3": 80, "Eff48": 80, "Eff5": 80, "Eff67": 80}
},
"RenderFlags": 255,
"UnknownA2": [19, 20]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"MotionRefs2": {
"Left": {"Eff1": 0, "Eff2": 0, "Eff3": 0, "Eff48": 43, "Eff5": 0, "Eff67": 43},
"Right": {"Eff1": 1, "Eff2": 1, "Eff3": 1, "Eff48": 44, "Eff5": 1, "Eff67": 44}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 26, "Eff2": 26, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [6, 7]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 82, "Eff2": 82, "Eff3": 82, "Eff48": 82, "Eff5": 82, "Eff67": 82}
},
"RenderFlags": 255,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"MotionRefs2": {
"Left": {"Eff1": 16, "Eff2": 16, "Eff3": 16, "Eff48": 64, "Eff5": 16, "Eff67": 64},
"Right": {"Eff1": 15, "Eff2": 15, "Eff3": 15, "Eff48": 63, "Eff5": 15, "Eff67": 63}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 4, "Eff3": 11, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 1,
"UnknownA2": [12, 13]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 3, "Eff2": 3, "Eff3": 3, "Eff48": 62, "Eff5": 3, "Eff67": 62}
},
"RenderFlags": 1,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 37, "Eff2": 37, "Eff3": 37, "Eff48": 47, "Eff5": 37, "Eff67": 47}
},
"RenderFlags": 10,
"UnknownA2": [255, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 4, "Eff2": 77, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 3,
"UnknownA2": [10, 11]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 5, "Eff2": 5, "Eff3": 77, "Eff48": 13, "Eff5": 12, "Eff67": 11}
},
"RenderFlags": 2,
"UnknownA2": [14, 255]
},
{
"EvolutionNumber": 4,
"MotionRefs1": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"MotionRefs2": {
"Left": {"Eff1": 255, "Eff2": 255, "Eff3": 255, "Eff48": 255, "Eff5": 255, "Eff67": 255},
"Right": {"Eff1": 10, "Eff2": 13, "Eff3": 10, "Eff48": 13, "Eff5": 10, "Eff67": 13}
},
"RenderFlags": 0,
"UnknownA2": [255, 255]
}
],
"UnknownA3": [
{"Flags": 33, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 129, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 24},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 20},
{"Flags": 130, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -24, "UnknownA5": 0},
{"Flags": 66, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 34, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": -20, "UnknownA5": 0},
{"Flags": 65, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -60, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 60},
{"Flags": 65, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": -40, "UnknownA5": 0},
{"Flags": 68, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 34, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 35, "UnknownA2": 1, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 30},
{"Flags": 33, "UnknownA2": 2, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 16},
{"Flags": 129, "UnknownA2": 246, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 66, "UnknownA2": 216, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 40},
{"Flags": 64, "UnknownA2": 6, "UnknownA3": 0, "UnknownA4": 10, "UnknownA5": 50},
{"Flags": 64, "UnknownA2": 6, "UnknownA3": 0, "UnknownA4": -50, "UnknownA5": -10},
{"Flags": 64, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": 0, "UnknownA5": 80},
{"Flags": 64, "UnknownA2": 15, "UnknownA3": 0, "UnknownA4": -80, "UnknownA5": 0}
]
}
File diff suppressed because it is too large Load Diff
+15 -15
View File
@@ -122,12 +122,12 @@
"000F03": "SONIC KNUCKLE",
"000F04": "LOGiN",
"001000": "OROTIAGITO",
"001001": "AGITO",
"001002": "AGITO",
"001003": "AGITO",
"001004": "AGITO",
"001005": "AGITO",
"001006": "AGITO",
"001001": "AGITO", // 1975 (real)
"001002": "AGITO", // 1983
"001003": "AGITO", // 2001
"001004": "AGITO", // 1991
"001005": "AGITO", // 1977
"001006": "AGITO", // 1980
"001007": "RAIKIRI",
"001100": "SOUL EATER",
"001101": "SOUL BANISH",
@@ -693,15 +693,15 @@
"008F06": "FLOWEN'S SWORD",
"008F07": "FLOWEN'S SWORD",
"008F08": "FLOWEN'S SWORD",
"009000": "DB'S SABER",
"009001": "DB'S SABER",
"009002": "DB'S SABER",
"009003": "DB'S SABER",
"009004": "DB'S SABER",
"009005": "DB'S SABER",
"009006": "DB'S SABER",
"009007": "DB'S SABER",
"009008": "DB'S SABER",
"009000": "DB'S SABER", // 3062
"009001": "DB'S SABER", // 3067
"009002": "DB'S SABER", // 3069 Chris
"009003": "DB'S SABER", // 3064
"009004": "DB'S SABER", // 3069 Torato
"009005": "DB'S SABER", // 3073
"009006": "DB'S SABER", // 3070
"009007": "DB'S SABER", // 3075
"009008": "DB'S SABER", // 3077
"009100": "GI GUE BAZOOKA",
"009200": "GUARDIANNA",
"009300": "VIRIDIA CARD",
+77
View File
@@ -0,0 +1,77 @@
{
"BonusLuckTable": {"-10": -15, "-5": -8, "0": 0, "10": 15, "5": 8},
"DefaultBonusDeltaTable": {
"Bluefull": {"-10": 5, "-5": 15, "0": 60, "10": 5, "5": 15},
"Greennill": {"-10": 10, "-5": 25, "0": 50, "10": 5, "5": 10},
"Oran": {"-10": 5, "-5": 10, "0": 50, "10": 10, "5": 25},
"Pinkal": {"-10": 5, "-5": 10, "0": 50, "10": 10, "5": 25},
"Purplenum": {"-10": 10, "-5": 25, "0": 50, "10": 5, "5": 10},
"Redria": {"-10": 5, "-5": 10, "0": 50, "10": 10, "5": 25},
"Skyly": {"-10": 5, "-5": 10, "0": 50, "10": 10, "5": 25},
"Viridia": {"-10": 5, "-5": 15, "0": 60, "10": 5, "5": 15},
"Whitill": {"-10": 5, "-5": 15, "0": 60, "10": 5, "5": 15},
"Yellowboze": {"-10": 10, "-5": 25, "0": 50, "10": 5, "5": 10}
},
"DefaultGrindDeltaTable": {
"Bluefull": {"-1": 10, "-2": 7, "-3": 0, "0": 60, "1": 13, "2": 7, "3": 3},
"Greennill": {"-1": 10, "-2": 5, "-3": 0, "0": 70, "1": 10, "2": 5, "3": 0},
"Oran": {"-1": 13, "-2": 7, "-3": 3, "0": 60, "1": 10, "2": 7, "3": 0},
"Pinkal": {"-1": 13, "-2": 7, "-3": 3, "0": 60, "1": 10, "2": 7, "3": 0},
"Purplenum": {"-1": 10, "-2": 5, "-3": 0, "0": 70, "1": 10, "2": 5, "3": 0},
"Redria": {"-1": 13, "-2": 7, "-3": 3, "0": 60, "1": 10, "2": 7, "3": 0},
"Skyly": {"-1": 13, "-2": 7, "-3": 3, "0": 60, "1": 10, "2": 7, "3": 0},
"Viridia": {"-1": 10, "-2": 7, "-3": 0, "0": 60, "1": 13, "2": 7, "3": 3},
"Whitill": {"-1": 10, "-2": 7, "-3": 0, "0": 60, "1": 13, "2": 7, "3": 3},
"Yellowboze": {"-1": 10, "-2": 5, "-3": 0, "0": 70, "1": 10, "2": 5, "3": 0}
},
"DefaultSpecialDeltaTable": {
"Bluefull": {"-1": 30, "0": 60, "1": 10},
"Greennill": {"-1": 10, "0": 65, "1": 25},
"Oran": {"-1": 15, "0": 70, "1": 15},
"Pinkal": {"-1": 15, "0": 70, "1": 15},
"Purplenum": {"-1": 10, "0": 65, "1": 25},
"Redria": {"-1": 20, "0": 60, "1": 20},
"Skyly": {"-1": 15, "0": 70, "1": 15},
"Viridia": {"-1": 30, "0": 60, "1": 10},
"Whitill": {"-1": 30, "0": 60, "1": 10},
"Yellowboze": {"-1": 10, "0": 65, "1": 25}
},
"FavoredBonusDeltaTable": {
"Bluefull": {"-10": 2, "-5": 10, "0": 60, "10": 8, "5": 20},
"Greennill": {"-10": 7, "-5": 20, "0": 50, "10": 8, "5": 15},
"Oran": {"-10": 2, "-5": 5, "0": 50, "10": 13, "5": 30},
"Pinkal": {"-10": 2, "-5": 5, "0": 50, "10": 13, "5": 30},
"Purplenum": {"-10": 7, "-5": 20, "0": 50, "10": 8, "5": 15},
"Redria": {"-10": 0, "-5": 0, "0": 0, "10": 0, "5": 0},
"Skyly": {"-10": 2, "-5": 5, "0": 50, "10": 13, "5": 30},
"Viridia": {"-10": 2, "-5": 10, "0": 60, "10": 8, "5": 20},
"Whitill": {"-10": 2, "-5": 10, "0": 60, "10": 8, "5": 20},
"Yellowboze": {"-10": 7, "-5": 20, "0": 50, "10": 8, "5": 15}
},
"FavoredGrindDeltaTable": {
"Bluefull": {"-1": 7, "-2": 0, "-3": 0, "0": 50, "1": 25, "2": 13, "3": 5},
"Greennill": {"-1": 10, "-2": 0, "-3": 0, "0": 60, "1": 20, "2": 7, "3": 3},
"Oran": {"-1": 10, "-2": 5, "-3": 0, "0": 50, "1": 20, "2": 12, "3": 3},
"Pinkal": {"-1": 10, "-2": 5, "-3": 0, "0": 50, "1": 20, "2": 12, "3": 3},
"Purplenum": {"-1": 10, "-2": 0, "-3": 0, "0": 60, "1": 20, "2": 7, "3": 3},
"Redria": {"-1": 0, "-2": 0, "-3": 0, "0": 0, "1": 0, "2": 0, "3": 0},
"Skyly": {"-1": 10, "-2": 5, "-3": 0, "0": 50, "1": 20, "2": 12, "3": 3},
"Viridia": {"-1": 7, "-2": 0, "-3": 0, "0": 50, "1": 25, "2": 13, "3": 5},
"Whitill": {"-1": 7, "-2": 0, "-3": 0, "0": 50, "1": 25, "2": 13, "3": 5},
"Yellowboze": {"-1": 10, "-2": 0, "-3": 0, "0": 60, "1": 20, "2": 7, "3": 3}
},
"FavoredSpecialDeltaTable": {
"Bluefull": {"-1": 25, "0": 50, "1": 25},
"Greennill": {"-1": 5, "0": 55, "1": 40},
"Oran": {"-1": 10, "0": 60, "1": 30},
"Pinkal": {"-1": 10, "0": 60, "1": 30},
"Purplenum": {"-1": 5, "0": 55, "1": 40},
"Redria": {"-1": 0, "0": 0, "1": 0},
"Skyly": {"-1": 10, "0": 60, "1": 30},
"Viridia": {"-1": 25, "0": 50, "1": 25},
"Whitill": {"-1": 25, "0": 50, "1": 25},
"Yellowboze": {"-1": 5, "0": 55, "1": 40}
},
"GrindLuckTable": {"-1": -3, "-2": -5, "-3": -10, "0": 0, "1": 3, "2": 5, "3": 10},
"SpecialLuckTable": {"-1": -20, "0": 0, "1": 20}
}
+251
View File
@@ -0,0 +1,251 @@
{
"CommonRecoveryTable": [
[0, 15, 3, 15, 6, 7, 9, 11, 12, 15, 15],
[0, 1, 3, 4, 6, 7, 9, 11, 12, 15, 15],
[0, 1, 3, 4, 6, 7, 9, 11, 12, 15, 15],
[15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15],
[15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15],
[15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15]
],
"RareRecoveryTable": [
[
[2, 0],
[5, 0],
[8, 0],
[10, 0],
[13, 0],
[14, 0],
[15, 100]
],
[
[2, 25],
[5, 25],
[8, 24],
[10, 0],
[13, 0],
[14, 1],
[15, 25]
],
[
[2, 37],
[5, 37],
[8, 10],
[10, 4],
[13, 0],
[14, 2],
[15, 10]
],
[
[2, 42],
[5, 42],
[8, 9],
[10, 4],
[13, 0],
[14, 3],
[15, 0]
],
[
[2, 41],
[5, 41],
[8, 9],
[10, 5],
[13, 0],
[14, 4],
[15, 0]
]
],
"TechDiskLevelTable": [
[
{"PlayerLevelDivisor": 3},
{"PlayerLevelDivisor": 4},
{"PlayerLevelDivisor": 6},
{"PlayerLevelDivisor": 3},
{"PlayerLevelDivisor": 35},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
{}
],
[
{"PlayerLevelDivisor": 6},
{"PlayerLevelDivisor": 7},
{"PlayerLevelDivisor": 8},
{"PlayerLevelDivisor": 6},
{"PlayerLevelDivisor": 35},
{"PlayerLevelDivisor": 9},
{"PlayerLevelDivisor": 7},
{"PlayerLevelDivisor": 6},
{"PlayerLevelDivisor": 8},
{"PlayerLevelDivisor": 9},
{"PlayerLevelDivisor": 12},
{"PlayerLevelDivisor": 8},
{},
{},
{},
{},
{}
],
[
{"MaxLevel": 10, "MinLevel": 3},
{"MaxLevel": 10, "MinLevel": 3},
{"MaxLevel": 10, "MinLevel": 3},
{"MaxLevel": 10, "MinLevel": 3},
{"PlayerLevelDivisor": 35},
{"PlayerLevelDivisor": 8},
{"PlayerLevelDivisor": 7},
{"PlayerLevelDivisor": 5},
{"PlayerLevelDivisor": 6},
{"PlayerLevelDivisor": 8},
{"PlayerLevelDivisor": 9},
{"PlayerLevelDivisor": 10},
{},
{},
{"PlayerLevelDivisor": 10},
{"PlayerLevelDivisor": 11},
{"PlayerLevelDivisor": 12}
],
[
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"PlayerLevelDivisor": 35},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 12, "MinLevel": 4},
{"MaxLevel": 10, "MinLevel": 3},
{"MaxLevel": 10, "MinLevel": 3},
{"MaxLevel": 10, "MinLevel": 3},
{},
{},
{"PlayerLevelDivisor": 12},
{"PlayerLevelDivisor": 13},
{"PlayerLevelDivisor": 11}
],
[
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"PlayerLevelDivisor": 35},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 14, "MinLevel": 5},
{"MaxLevel": 13, "MinLevel": 4},
{"MaxLevel": 13, "MinLevel": 4},
{"MaxLevel": 13, "MinLevel": 4},
{},
{},
{"MaxLevel": 13, "MinLevel": 3},
{"MaxLevel": 13, "MinLevel": 3},
{"MaxLevel": 13, "MinLevel": 3}
]
],
"TechDiskTable": [
[
[0, 18],
[1, 18],
[2, 18],
[3, 18],
[4, 18],
[5, 2],
[6, 3],
[7, 3],
[8, 2],
[9, 0],
[10, 0],
[11, 0],
[12, 0],
[13, 0],
[14, 0],
[15, 0],
[16, 0]
],
[
[0, 7],
[1, 7],
[2, 7],
[3, 7],
[4, 7],
[5, 6],
[6, 6],
[7, 6],
[8, 7],
[9, 8],
[10, 8],
[11, 8],
[12, 5],
[13, 5],
[14, 2],
[15, 2],
[16, 2]
],
[
[0, 5],
[1, 5],
[2, 5],
[3, 6],
[4, 5],
[5, 5],
[6, 5],
[7, 5],
[8, 5],
[9, 5],
[10, 5],
[11, 5],
[12, 6],
[13, 6],
[14, 9],
[15, 9],
[16, 9]
],
[
[0, 6],
[1, 6],
[2, 6],
[3, 5],
[4, 5],
[5, 6],
[6, 6],
[7, 6],
[8, 6],
[9, 6],
[10, 6],
[11, 6],
[12, 6],
[13, 6],
[14, 6],
[15, 6],
[16, 6]
],
[
[0, 6],
[1, 6],
[2, 6],
[3, 5],
[4, 5],
[5, 6],
[6, 6],
[7, 6],
[8, 6],
[9, 6],
[10, 6],
[11, 6],
[12, 6],
[13, 6],
[14, 6],
[15, 6],
[16, 6]
]
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,862 @@
{
"BonusRangeTable1": [
[10, 14],
[10, 14],
[10, 15],
[10, 15],
[10, 16],
[10, 16],
[10, 16],
[10, 17],
[10, 19]
],
"BonusRangeTable2": [
[10, 13],
[10, 14],
[10, 14],
[10, 14],
[10, 15],
[10, 15],
[10, 16],
[10, 16],
[10, 19]
],
"BonusTypeTable1": [
[
[0, 60],
[1, 20],
[2, 20],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 50],
[1, 15],
[2, 20],
[3, 15],
[4, 0],
[5, 0]
],
[
[0, 30],
[1, 10],
[2, 15],
[3, 25],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 15],
[3, 20],
[4, 30],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 25],
[3, 20],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 30],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 20],
[3, 20],
[4, 25],
[5, 0]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 20],
[4, 25],
[5, 5]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 20],
[4, 20],
[5, 10]
]
],
"BonusTypeTable2": [
[
[0, 100],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 100],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 65],
[1, 5],
[2, 10],
[3, 10],
[4, 10],
[5, 0]
],
[
[0, 45],
[1, 10],
[2, 10],
[3, 15],
[4, 20],
[5, 0]
],
[
[0, 30],
[1, 25],
[2, 20],
[3, 10],
[4, 10],
[5, 5]
],
[
[0, 15],
[1, 15],
[2, 20],
[3, 25],
[4, 20],
[5, 5]
],
[
[0, 15],
[1, 15],
[2, 20],
[3, 25],
[4, 15],
[5, 10]
],
[
[0, 10],
[1, 15],
[2, 20],
[3, 20],
[4, 20],
[5, 15]
],
[
[0, 10],
[1, 20],
[2, 15],
[3, 15],
[4, 20],
[5, 20]
]
],
"DefaultDringRangeTable": [
[0, 2],
[0, 3],
[0, 4],
[0, 6],
[0, 8],
[0, 10]
],
"FavoredDringRangeTable": [
[0, 1],
[0, 3],
[1, 5],
[2, 8],
[3, 11],
[3, 16]
],
"SpecialModeTable": [
[
[0, 85],
[1, 15],
[2, 0]
],
[
[0, 70],
[1, 30],
[2, 0]
],
[
[0, 50],
[1, 50],
[2, 0]
],
[
[0, 20],
[1, 80],
[2, 0]
],
[
[0, 20],
[1, 60],
[2, 20]
],
[
[0, 20],
[1, 40],
[2, 40]
],
[
[0, 10],
[1, 30],
[2, 60]
],
[
[0, 10],
[1, 10],
[2, 80]
]
],
"WeaponTypeWeightTables": [
[
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
],
[
[0, 20],
[5, 20],
[25, 15],
[30, 25],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 20],
[30, 20],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
],
[
[0, 20],
[5, 20],
[25, 25],
[30, 10],
[45, 25]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 5],
[45, 30]
],
[
[0, 25],
[5, 5],
[25, 25],
[30, 20],
[45, 25]
],
[
[0, 20],
[5, 25],
[25, 20],
[30, 10],
[45, 25]
],
[
[0, 20],
[5, 20],
[25, 20],
[30, 20],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
]
],
[
[
[5, 8],
[30, 8],
[1, 12],
[10, 8],
[15, 0],
[20, 16],
[26, 12],
[35, 8],
[46, 12],
[49, 8],
[53, 8]
],
[
[5, 12],
[30, 18],
[1, 10],
[10, 0],
[15, 8],
[20, 8],
[26, 10],
[35, 8],
[46, 10],
[49, 8],
[53, 8]
],
[
[5, 8],
[30, 12],
[1, 10],
[10, 18],
[15, 8],
[20, 8],
[26, 10],
[35, 0],
[46, 10],
[49, 8],
[53, 8]
],
[
[5, 8],
[30, 8],
[1, 10],
[10, 8],
[15, 8],
[20, 18],
[26, 10],
[35, 8],
[46, 10],
[49, 12],
[53, 0]
],
[
[5, 13],
[30, 8],
[1, 10],
[10, 3],
[15, 8],
[20, 3],
[26, 10],
[35, 19],
[46, 10],
[49, 8],
[53, 8]
],
[
[5, 8],
[30, 0],
[1, 10],
[10, 8],
[15, 8],
[20, 12],
[26, 10],
[35, 8],
[46, 10],
[49, 8],
[53, 18]
],
[
[5, 0],
[30, 9],
[1, 10],
[10, 9],
[15, 16],
[20, 9],
[26, 10],
[35, 9],
[46, 10],
[49, 9],
[53, 9]
],
[
[5, 16],
[30, 8],
[1, 10],
[10, 11],
[15, 8],
[20, 8],
[26, 10],
[35, 8],
[46, 10],
[49, 0],
[53, 11]
],
[
[5, 9],
[30, 9],
[1, 10],
[10, 9],
[15, 9],
[20, 9],
[26, 9],
[35, 9],
[46, 9],
[49, 9],
[53, 9]
],
[
[5, 7],
[30, 7],
[1, 10],
[10, 7],
[15, 16],
[20, 7],
[26, 10],
[35, 12],
[46, 10],
[49, 7],
[53, 7]
]
],
[
[
[1, 10],
[10, 7],
[6, 7],
[26, 10],
[35, 7],
[49, 7],
[40, 16],
[15, 0],
[20, 10],
[31, 7],
[46, 12],
[53, 7]
],
[
[1, 10],
[10, 0],
[6, 12],
[26, 10],
[35, 7],
[49, 7],
[40, 7],
[15, 7],
[20, 7],
[31, 16],
[46, 10],
[53, 7]
],
[
[1, 10],
[10, 16],
[6, 7],
[26, 10],
[35, 0],
[49, 7],
[40, 7],
[15, 7],
[20, 7],
[31, 12],
[46, 10],
[53, 7]
],
[
[1, 10],
[10, 7],
[6, 7],
[26, 10],
[35, 7],
[49, 12],
[40, 7],
[15, 7],
[20, 16],
[31, 7],
[46, 10],
[53, 0]
],
[
[1, 10],
[10, 3],
[6, 12],
[26, 10],
[35, 17],
[49, 7],
[40, 7],
[15, 7],
[20, 3],
[31, 7],
[46, 10],
[53, 7]
],
[
[1, 10],
[10, 7],
[6, 7],
[26, 10],
[35, 7],
[49, 7],
[40, 7],
[15, 7],
[20, 12],
[31, 0],
[46, 10],
[53, 16]
],
[
[1, 10],
[10, 7],
[6, 0],
[26, 10],
[35, 7],
[49, 7],
[40, 14],
[15, 14],
[20, 7],
[31, 7],
[46, 10],
[53, 7]
],
[
[1, 10],
[10, 10],
[6, 15],
[26, 10],
[35, 7],
[49, 0],
[40, 7],
[15, 7],
[20, 7],
[31, 7],
[46, 10],
[53, 10]
],
[
[1, 9],
[10, 8],
[6, 8],
[26, 9],
[35, 8],
[49, 8],
[40, 9],
[15, 8],
[20, 8],
[31, 8],
[46, 9],
[53, 8]
],
[
[1, 10],
[10, 7],
[6, 7],
[26, 10],
[35, 12],
[49, 7],
[40, 0],
[15, 16],
[20, 7],
[31, 7],
[46, 10],
[53, 7]
]
],
[
[
[2, 10],
[11, 7],
[6, 7],
[15, 0],
[20, 12],
[26, 10],
[40, 16],
[36, 7],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 0],
[6, 12],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 7],
[31, 16],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 16],
[6, 7],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 0],
[31, 12],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 7],
[20, 16],
[26, 10],
[40, 7],
[36, 7],
[31, 7],
[46, 10],
[53, 0],
[49, 12]
],
[
[2, 10],
[11, 3],
[6, 12],
[15, 7],
[20, 3],
[26, 10],
[40, 7],
[36, 17],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 7],
[20, 12],
[26, 10],
[40, 7],
[36, 7],
[31, 0],
[46, 10],
[53, 16],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 0],
[15, 14],
[20, 7],
[26, 10],
[40, 14],
[36, 7],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 10],
[6, 15],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 7],
[31, 7],
[46, 10],
[53, 10],
[49, 0]
],
[
[2, 9],
[11, 8],
[6, 8],
[15, 8],
[20, 8],
[26, 9],
[40, 9],
[36, 8],
[31, 8],
[46, 9],
[53, 8],
[49, 8]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 16],
[20, 7],
[26, 10],
[40, 0],
[36, 12],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
]
],
[
[
[2, 10],
[11, 7],
[6, 7],
[15, 0],
[20, 12],
[26, 10],
[40, 16],
[36, 7],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 0],
[6, 12],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 7],
[31, 16],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 16],
[6, 7],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 0],
[31, 12],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 7],
[20, 16],
[26, 10],
[40, 7],
[36, 7],
[31, 7],
[46, 10],
[53, 0],
[49, 12]
],
[
[2, 10],
[11, 3],
[6, 12],
[15, 7],
[20, 3],
[26, 10],
[40, 7],
[36, 17],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 7],
[20, 12],
[26, 10],
[40, 7],
[36, 7],
[31, 0],
[46, 10],
[53, 16],
[49, 7]
],
[
[2, 10],
[11, 7],
[6, 0],
[15, 14],
[20, 7],
[26, 10],
[40, 14],
[36, 7],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
],
[
[2, 10],
[11, 10],
[6, 15],
[15, 7],
[20, 7],
[26, 10],
[40, 7],
[36, 7],
[31, 7],
[46, 10],
[53, 10],
[49, 0]
],
[
[2, 9],
[11, 8],
[6, 8],
[15, 8],
[20, 8],
[26, 9],
[40, 9],
[36, 8],
[31, 8],
[46, 9],
[53, 8],
[49, 8]
],
[
[2, 10],
[11, 7],
[6, 7],
[15, 16],
[20, 7],
[26, 10],
[40, 0],
[36, 12],
[31, 7],
[46, 10],
[53, 7],
[49, 7]
]
]
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,972 @@
{
"BonusRangeTable1": [
[10, 14],
[10, 14],
[10, 15],
[10, 15],
[10, 16],
[10, 16],
[10, 16],
[10, 17],
[10, 19]
],
"BonusRangeTable2": [
[10, 13],
[10, 14],
[10, 14],
[10, 14],
[10, 15],
[10, 15],
[10, 16],
[10, 16],
[10, 19]
],
"BonusTypeTable1": [
[
[0, 60],
[1, 20],
[2, 20],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 50],
[1, 15],
[2, 20],
[3, 15],
[4, 0],
[5, 0]
],
[
[0, 30],
[1, 10],
[2, 15],
[3, 25],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 15],
[3, 20],
[4, 30],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 25],
[3, 20],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 30],
[4, 20],
[5, 0]
],
[
[0, 10],
[1, 25],
[2, 20],
[3, 20],
[4, 25],
[5, 0]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 20],
[4, 25],
[5, 5]
],
[
[0, 10],
[1, 20],
[2, 20],
[3, 20],
[4, 20],
[5, 10]
]
],
"BonusTypeTable2": [
[
[0, 100],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 100],
[1, 0],
[2, 0],
[3, 0],
[4, 0],
[5, 0]
],
[
[0, 65],
[1, 5],
[2, 10],
[3, 10],
[4, 10],
[5, 0]
],
[
[0, 45],
[1, 10],
[2, 10],
[3, 15],
[4, 20],
[5, 0]
],
[
[0, 30],
[1, 25],
[2, 20],
[3, 10],
[4, 10],
[5, 5]
],
[
[0, 15],
[1, 15],
[2, 20],
[3, 25],
[4, 20],
[5, 5]
],
[
[0, 15],
[1, 15],
[2, 20],
[3, 25],
[4, 15],
[5, 10]
],
[
[0, 10],
[1, 15],
[2, 20],
[3, 20],
[4, 20],
[5, 15]
],
[
[0, 10],
[1, 20],
[2, 15],
[3, 15],
[4, 20],
[5, 20]
]
],
"DefaultDringRangeTable": [
[0, 2],
[0, 3],
[0, 4],
[0, 6],
[0, 8],
[0, 10]
],
"FavoredDringRangeTable": [
[0, 1],
[0, 3],
[1, 5],
[2, 8],
[3, 11],
[3, 16]
],
"SpecialModeTable": [
[
[0, 85],
[1, 15],
[2, 0]
],
[
[0, 70],
[1, 30],
[2, 0]
],
[
[0, 50],
[1, 50],
[2, 0]
],
[
[0, 20],
[1, 80],
[2, 0]
],
[
[0, 20],
[1, 60],
[2, 20]
],
[
[0, 20],
[1, 40],
[2, 40]
],
[
[0, 10],
[1, 30],
[2, 60]
],
[
[0, 10],
[1, 10],
[2, 80]
]
],
"WeaponTypeWeightTables": [
[
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
],
[
[0, 20],
[5, 20],
[25, 15],
[30, 25],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 20],
[30, 20],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
],
[
[0, 20],
[5, 20],
[25, 25],
[30, 10],
[45, 25]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 5],
[45, 30]
],
[
[0, 25],
[5, 5],
[25, 25],
[30, 20],
[45, 25]
],
[
[0, 20],
[5, 25],
[25, 20],
[30, 10],
[45, 25]
],
[
[0, 20],
[5, 20],
[25, 20],
[30, 20],
[45, 20]
],
[
[0, 25],
[5, 15],
[25, 25],
[30, 15],
[45, 20]
]
],
[
[
[1, 10],
[10, 7],
[15, 0],
[20, 12],
[26, 10],
[35, 7],
[46, 10],
[49, 7],
[53, 7],
[6, 7],
[40, 16],
[31, 7]
],
[
[1, 10],
[10, 0],
[15, 7],
[20, 7],
[26, 10],
[35, 7],
[46, 10],
[49, 7],
[53, 7],
[6, 12],
[40, 7],
[31, 16]
],
[
[1, 10],
[10, 16],
[15, 7],
[20, 7],
[26, 10],
[35, 0],
[46, 10],
[49, 7],
[53, 7],
[6, 7],
[40, 7],
[31, 12]
],
[
[1, 10],
[10, 7],
[15, 7],
[20, 16],
[26, 10],
[35, 7],
[46, 10],
[49, 12],
[53, 0],
[6, 7],
[40, 7],
[31, 7]
],
[
[1, 10],
[10, 3],
[15, 7],
[20, 3],
[26, 10],
[35, 17],
[46, 10],
[49, 7],
[53, 7],
[6, 12],
[40, 7],
[31, 7]
],
[
[1, 10],
[10, 7],
[15, 7],
[20, 12],
[26, 10],
[35, 7],
[46, 10],
[49, 7],
[53, 16],
[6, 7],
[40, 7],
[31, 0]
],
[
[1, 10],
[10, 7],
[15, 14],
[20, 7],
[26, 10],
[35, 7],
[46, 10],
[49, 7],
[53, 7],
[6, 0],
[40, 14],
[31, 7]
],
[
[1, 10],
[10, 10],
[15, 7],
[20, 7],
[26, 10],
[35, 7],
[46, 10],
[49, 0],
[53, 10],
[6, 15],
[40, 7],
[31, 7]
],
[
[1, 9],
[10, 8],
[15, 8],
[20, 8],
[26, 9],
[35, 8],
[46, 9],
[49, 8],
[53, 8],
[6, 8],
[40, 9],
[31, 8]
],
[
[1, 10],
[10, 7],
[15, 16],
[20, 7],
[26, 10],
[35, 12],
[46, 10],
[49, 7],
[53, 7],
[6, 7],
[40, 0],
[31, 7]
]
],
[
[
[1, 5],
[10, 4],
[6, 4],
[26, 5],
[35, 4],
[49, 4],
[40, 8],
[15, 0],
[20, 12],
[32, 7],
[46, 10],
[53, 7],
[2, 5],
[11, 3],
[7, 3],
[27, 5],
[36, 3],
[50, 3],
[41, 8]
],
[
[1, 5],
[10, 0],
[6, 6],
[26, 5],
[35, 4],
[49, 4],
[40, 4],
[15, 7],
[20, 7],
[32, 16],
[46, 10],
[53, 7],
[2, 5],
[11, 0],
[7, 6],
[27, 5],
[36, 3],
[50, 3],
[41, 3]
],
[
[1, 5],
[10, 8],
[6, 4],
[26, 5],
[35, 0],
[49, 4],
[40, 4],
[15, 7],
[20, 7],
[32, 12],
[46, 10],
[53, 7],
[2, 5],
[11, 8],
[7, 3],
[27, 5],
[36, 0],
[50, 3],
[41, 3]
],
[
[1, 5],
[10, 4],
[6, 4],
[26, 5],
[35, 4],
[49, 6],
[40, 4],
[15, 7],
[20, 16],
[32, 7],
[46, 10],
[53, 0],
[2, 5],
[11, 3],
[7, 3],
[27, 5],
[36, 3],
[50, 6],
[41, 3]
],
[
[1, 5],
[10, 2],
[6, 6],
[26, 5],
[35, 9],
[49, 4],
[40, 4],
[15, 7],
[20, 3],
[32, 7],
[46, 10],
[53, 7],
[2, 5],
[11, 1],
[7, 6],
[27, 5],
[36, 8],
[50, 3],
[41, 3]
],
[
[1, 5],
[10, 4],
[6, 4],
[26, 5],
[35, 4],
[49, 4],
[40, 4],
[15, 7],
[20, 12],
[32, 0],
[46, 10],
[53, 16],
[2, 5],
[11, 3],
[7, 3],
[27, 5],
[36, 3],
[50, 3],
[41, 3]
],
[
[1, 5],
[10, 4],
[6, 0],
[26, 5],
[35, 4],
[49, 4],
[40, 7],
[15, 14],
[20, 7],
[32, 7],
[46, 10],
[53, 7],
[2, 5],
[11, 3],
[7, 0],
[27, 5],
[36, 3],
[50, 3],
[41, 7]
],
[
[1, 5],
[10, 5],
[6, 8],
[26, 5],
[35, 4],
[49, 0],
[40, 4],
[15, 7],
[20, 7],
[32, 7],
[46, 10],
[53, 10],
[2, 5],
[11, 5],
[7, 7],
[27, 5],
[36, 3],
[50, 0],
[41, 3]
],
[
[1, 5],
[10, 4],
[6, 4],
[26, 5],
[35, 4],
[49, 4],
[40, 4],
[15, 9],
[20, 8],
[32, 8],
[46, 9],
[53, 8],
[2, 4],
[11, 4],
[7, 4],
[27, 4],
[36, 4],
[50, 4],
[41, 4]
],
[
[1, 5],
[10, 4],
[6, 4],
[26, 5],
[35, 6],
[49, 4],
[40, 0],
[15, 16],
[20, 7],
[32, 7],
[46, 10],
[53, 7],
[2, 5],
[11, 3],
[7, 3],
[27, 5],
[36, 6],
[50, 3],
[41, 0]
]
],
[
[
[2, 9],
[12, 8],
[7, 7],
[16, 0],
[21, 9],
[27, 10],
[41, 18],
[37, 8],
[32, 7],
[47, 10],
[54, 7],
[50, 7],
[57, 0]
],
[
[2, 9],
[12, 0],
[7, 9],
[16, 7],
[21, 8],
[27, 10],
[41, 7],
[37, 7],
[32, 18],
[47, 10],
[54, 7],
[50, 8],
[57, 0]
],
[
[2, 9],
[12, 18],
[7, 7],
[16, 7],
[21, 7],
[27, 10],
[41, 8],
[37, 0],
[32, 9],
[47, 10],
[54, 8],
[50, 7],
[57, 0]
],
[
[2, 9],
[12, 7],
[7, 7],
[16, 8],
[21, 18],
[27, 10],
[41, 8],
[37, 7],
[32, 7],
[47, 10],
[54, 0],
[50, 9],
[57, 0]
],
[
[2, 10],
[12, 2],
[7, 9],
[16, 7],
[21, 2],
[27, 11],
[41, 8],
[37, 18],
[32, 7],
[47, 11],
[54, 7],
[50, 8],
[57, 0]
],
[
[2, 9],
[12, 7],
[7, 8],
[16, 7],
[21, 9],
[27, 10],
[41, 7],
[37, 7],
[32, 0],
[47, 10],
[54, 18],
[50, 8],
[57, 0]
],
[
[2, 8],
[12, 8],
[7, 0],
[16, 12],
[21, 8],
[27, 8],
[41, 12],
[37, 8],
[32, 8],
[47, 8],
[54, 8],
[50, 12],
[57, 0]
],
[
[2, 9],
[12, 8],
[7, 18],
[16, 7],
[21, 8],
[27, 10],
[41, 8],
[37, 7],
[32, 7],
[47, 10],
[54, 8],
[50, 0],
[57, 0]
],
[
[2, 8],
[12, 8],
[7, 8],
[16, 8],
[21, 8],
[27, 8],
[41, 8],
[37, 8],
[32, 8],
[47, 8],
[54, 5],
[50, 15],
[57, 0]
],
[
[2, 9],
[12, 8],
[7, 7],
[16, 18],
[21, 8],
[27, 10],
[41, 0],
[37, 9],
[32, 7],
[47, 10],
[54, 7],
[50, 7],
[57, 0]
]
],
[
[
[3, 10],
[12, 7],
[8, 7],
[17, 0],
[22, 11],
[28, 10],
[42, 17],
[37, 7],
[32, 7],
[47, 10],
[54, 7],
[50, 7],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 0],
[8, 11],
[17, 7],
[22, 7],
[28, 10],
[42, 7],
[37, 7],
[32, 17],
[47, 10],
[54, 7],
[50, 7],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 17],
[8, 7],
[17, 7],
[22, 7],
[28, 10],
[42, 7],
[37, 0],
[32, 11],
[47, 10],
[54, 7],
[50, 7],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 7],
[8, 7],
[17, 7],
[22, 17],
[28, 11],
[42, 7],
[37, 7],
[32, 7],
[47, 10],
[54, 0],
[50, 10],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 3],
[8, 11],
[17, 7],
[22, 3],
[28, 11],
[42, 7],
[37, 17],
[32, 7],
[47, 10],
[54, 7],
[50, 7],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 7],
[8, 7],
[17, 7],
[22, 11],
[28, 11],
[42, 7],
[37, 7],
[32, 0],
[47, 10],
[54, 17],
[50, 6],
[57, 0],
[58, 0]
],
[
[3, 7],
[12, 6],
[8, 0],
[17, 12],
[22, 7],
[28, 7],
[42, 12],
[37, 6],
[32, 7],
[47, 10],
[54, 13],
[50, 13],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 8],
[8, 15],
[17, 6],
[22, 6],
[28, 10],
[42, 6],
[37, 6],
[32, 6],
[47, 10],
[54, 17],
[50, 0],
[57, 0],
[58, 0]
],
[
[3, 6],
[12, 6],
[8, 6],
[17, 6],
[22, 6],
[28, 6],
[42, 6],
[37, 6],
[32, 13],
[47, 13],
[54, 13],
[50, 13],
[57, 0],
[58, 0]
],
[
[3, 10],
[12, 6],
[8, 6],
[17, 13],
[22, 6],
[28, 9],
[42, 0],
[37, 9],
[32, 14],
[47, 17],
[54, 4],
[50, 6],
[57, 0],
[58, 0]
]
]
]
}
+280 -57
View File
@@ -283,66 +283,289 @@
],
"QuestF95EResultItems": [
[
[0x00900000, 0x00900100, 0x00900200, 0x00900300, 0x00900400, 0x00900500,
0x00900600, 0x00900700, 0x00900800, 0x00B40000, 0x01014E00, 0x01030700,
0x01034100, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00B90000, 0x00340000, 0x00090100, 0x00900200, 0x00900700, 0x002C0000,
0x002D0000, 0x01023500, 0x00010600, 0x00010500, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000],
[0x00B60000, 0x008A0100, 0x00100100, 0x00100200, 0x00100300, 0x00100400,
0x00100500, 0x00100600, 0x00270000, 0x00010700, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000],
[0x00B70000, 0x00100100, 0x00100200, 0x00100300, 0x00100400, 0x00100500,
0x00100600, 0x00290000, 0x008A0000, 0x008A0200, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000],
// Black Paper's Deal Dorphon route
[
// Normal
0x00900000, // DB'S SABER 3062
0x00900200, // DB'S SABER 3069 Chris
0x00900800, // DB'S SABER 3077
0x01014E00, // OFFICER UNIFORM
0x01030700, // God/Mind
0x01034100, // God/Battle
0x03030000, // Sol Atomizer
0x030F0000, // AddSlot
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Hard
0x00010500, // DB'S SABER (Ephinea)
0x00010600, // KALADBOLG (Ephinea)
0x002C0000, // ELYSION
0x00340000, // RED SWORD
0x00900000, // DB'S SABER 3062
0x00900100, // DB'S SABER 3067
0x00900200, // DB'S SABER 3069 Chris (Ephinea)
0x00900300, // DB'S SABER 3064
0x00900400, // DB'S SABER 3069 Torato
0x00900500, // DB'S SABER 3073
0x00900600, // DB'S SABER 3070
0x00900700, // DB'S SABER 3075
0x00900800, // DB'S SABER 3077 (Ephinea)
0x00B90000, // FLAMBERGE
0x01023500, // SECURE FEET
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Very Hard
0x00010700, // DURANDAL
0x00100100, // AGITO 1975 (real)
0x00100200, // AGITO 1983
0x00100300, // AGITO 2001
0x00100400, // AGITO 1991
0x00100500, // AGITO 1977
0x00100600, // AGITO 1980
0x00270000, // ANCIENT SABER
0x008A0100, // YASHA
0x00B60000, // GUREN
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Ultimate
0x00100100, // AGITO 1975 (real)
0x00100200, // AGITO 1983
0x00100300, // AGITO 2001
0x00100400, // AGITO 1991
0x00100500, // AGITO 1977
0x00100600, // AGITO 1980
0x00290000, // YAMIGARASU
0x008A0000, // SANGE
0x008A0200, // KAMUI
0x00B70000, // SHOUREN
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
],
], [
[0x01028B00, 0x01022800, 0x01013400, 0x01030300, 0x01030B00, 0x03180700,
0x00550000, 0x01032900, 0x01032F00, 0x01032C00, 0x01032300, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000],
[0x01028C00, 0x01021500, 0x01028A00, 0x01014000, 0x01034400, 0x01034600,
0x01034500, 0x01034700, 0x03180700, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00CB0000, 0x003A0000, 0x008C0200, 0x01022B00, 0x00500000, 0x000B0600,
0x000A0600, 0x000A0400, 0x00550000, 0x00230000, 0x003B0000, 0x03180700,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000],
[0x00510000, 0x01035200, 0x01032000, 0x01033E00, 0x01022900, 0x03180700,
0x000B0400, 0x000A0600, 0x00560000, 0x003B0000, 0x00230000, 0x000A0500,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000],
// Black Paper's Deal Rappy route
[
// Normal
0x01013400, // SMOKING PLATE
0x01022800, // TRIPOLIC SHIELD
0x01028B00, // BUNNY EARS
0x01030300, // God/Power
0x01030B00, // God/Arm
0x01032300, // Resist/Burning
0x01032600, // Resist/Blizzard
0x01032900, // Resist/Storm (Ephinea)
0x01032C00, // Resist/Holy
0x01032F00, // Resist/Devil (Ephinea)
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Hard
0x01014000, // RED COAT
0x01021500, // INVISIBLE GUARD
0x01028A00, // YATA MIRROR
0x01028C00, // CAT EARS
0x01034200, // Cure/Poison
0x01034300, // Cure/Paralysis
0x01034400, // Cure/Slow
0x01034500, // Cure/Confuse
0x01034600, // Cure/Freeze
0x01034700, // Cure/Shock
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Very Hard
0x000A0400, // CLUB OF LACONIUM (Ephinea)
0x000A0600, // CLUB OF ZUMIURAN
0x000B0600, // ALIVE AQHU
0x00230000, // STING TIP
0x003A0000, // MADAM'S PARASOL
0x003B0000, // MADAM'S UMBRELLA
0x00500000, // WINDMILL
0x00550000, // RABBIT WAND
0x00CB0000, // TYRELL'S PARASOL
0x01022B00, // KASAMI BRACER
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030E0A00, // Book of HITOGATA (Ephinea)
0x03180700, // Rappy's Beak
0x04000000, // Meseta
], [
// Ultimate
0x000A0500, // MACE OF ADAMAN
0x000A0600, // CLUB OF ZUMIURAN
0x000B0400, // BATTLE VERGE
0x00230000, // STING TIP (Ephinea)
0x00250000, // TECHNICAL CROZIER
0x003B0000, // MADAM'S UMBRELLA
0x00500000, // WINDMILL
0x00510000, // EVIL CURST
0x00560000, // PLANTAIN LEAF
0x01022900, // STANDSTILL SHIELD
0x01032000, // God/Ability
0x01033E00, // God/Technique
0x01035200, // DIVINE PROTECTION
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x03180700, // Rappy's Beak
0x04000000, // Meseta
],
], [
[0x01013200, 0x002F0100, 0x00B30000, 0x005E0000, 0x000E0200, 0x002E0000,
0x00950000, 0x009A0000, 0x002F0000, 0x01031B00, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000],
[0x00C00000, 0x00D20000, 0x008D0000, 0x01012E00, 0x008B0000, 0x00090700,
0x004E0000, 0x006D0000, 0x00150000, 0x008B0200, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000],
[0x00AA0000, 0x01014100, 0x01015100, 0x01022300, 0x003F0000, 0x00410000,
0x00050700, 0x00050600, 0x00050500, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000],
[0x00AF0000, 0x00430000, 0x01035100, 0x00CD0000, 0x00990000, 0x006C0000,
0x00450000, 0x006B0000, 0x00120000, 0x00650000, 0x01022900, 0x00130000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000],
// Black Paper's Deal Zu route
[
// Normal
0x000E0100, // STAG CUTLERY
0x000E0200, // TWIN BRAND
0x002E0000, // METEOR CUDGEL
0x002F0000, // MONKEY KING BAR
0x005E0000, // TWIN BLAZE
0x00950000, // PARTISAN of LIGHTNING
0x009A0000, // DEMOLITION COMET
0x00B30000, // VIVIENNE
0x01013200, // ELECTRO FRAME
0x01031B00, // God/Body
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot (Ephinea)
0x04000000, // Meseta
], [
// Hard
0x00090700, // FINAL IMPACT
0x00150000, // FLAME VISIT
0x004E0000, // PANZER FAUST
0x006D0000, // MASER BEAM
0x008B0000, // PHOTON LAUNCHER
0x008B0200, // RED SCORPIR
0x008D0000, // NUG2000-BAZOOKA
0x00C00000, // CANNON ROUGE
0x00D20000, // ANO BAZOOKA
0x01012E00, // FLAME GARMENT
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
], [
// Very Hard
0x00050500, // SLICER OF ASSASSIN
0x00050600, // DISKA OF LIBERATOR
0x00050700, // DISKA OF BRAVEMAN
0x003F0000, // FLIGHT CUTTER
0x00400000, // FLIGHT FAN
0x00410000, // RED SLICER
0x00AA0000, // SLICER OF FANATIC
0x01014100, // THIRTEEN
0x01015100, // INFANTRY GEAR
0x01022300, // REGENE GEAR ADV.
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot (Ephinea)
0x04000000, // Meseta
], [
// Ultimate
0x00120000, // SPREAD NEEDLE
0x00130000, // HOLY RAY
0x00430000, // HANDGUN:MILLA
0x00450000, // FROZEN SHOOTER
0x00650000, // YASMINKOV 3000R
0x006B0000, // YASMINKOV 7000V
0x006C0000, // YASMINKOV 9000M
0x00990000, // ANGEL HARP
0x00AF0000, // OPHELIE SEIZE
0x00CD0000, // TANEGASHIMA
0x01035100, // SMARTLINK
0x03030000, // Sol Atomizer
0x03040000, // Moon Atomizer
0x03050000, // Star Atomizer
0x030F0000, // AddSlot
0x04000000, // Meseta
],
], [], [
[0x00BA0000, 0x000D0300, 0x00430100, 0x00070800, 0x00420100, 0x00C90000,
0x03100000, 0x01029500, 0x01028F00, 0x01029100],
[0x00BB0000, 0x000D0300, 0x00B70000, 0x00420100, 0x00070800, 0x00C90000,
0x01013600, 0x01028A00, 0x01029900, 0x01035100, 0x01035B00, 0x01035200,
0x03100000, 0x03180A00],
[0x00BA0000, 0x00B40000, 0x000D0300, 0x00B60000, 0x00B30000, 0x00070800,
0x00430100, 0x00C90000, 0x01013600, 0x01028A00, 0x01029900, 0x01028500,
0x01034800, 0x01035100, 0x01035B00, 0x01035200, 0x03100000],
[0x00BA0000, 0x00B40000, 0x000D0300, 0x00B60000, 0x00B30000, 0x00070800,
0x00430100, 0x00C90000, 0x01013600, 0x01028A00, 0x01029900, 0x01028500,
0x01034800, 0x01035100, 0x01035B00, 0x01035200],
// Black Paper's Dangerous Deal 2
[
// Normal
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00420100, // MASTER RAVEN (Ephinea)
0x00430100, // LAST SWAN
0x00BA0000, // YUNCHANG
0x00C90000, // DECALOG
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x03100000, // Photon Drop
], [
// Hard
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00420100, // MASTER RAVEN
0x00B40000, // KUSANAGI
0x00B70000, // SHOUREN
0x00BA0000, // YUNCHANG
0x00BB0000, // SNAKE SPIRE
0x00BC0000, // FLAPJACK FLAPPER
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
], [
// Very Hard
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00430100, // LAST SWAN
0x00B30000, // VIVIENNE
0x00B40000, // KUSANAGI
0x00B40000, // KUSANAGI
0x00B60000, // GUREN
0x00BA0000, // YUNCHANG (Ephinea)
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028500, // GRATIA
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
], [
// Ultimate
0x00070800, // RIANOV 303SNR
0x000D0300, // PHOENIX CLAW
0x00430100, // LAST SWAN
0x00B30000, // VIVIENNE
0x00B40000, // KUSANAGI
0x00B60000, // GUREN
0x00B70000, // SHOUREN
0x00C90000, // DECALOG
0x01013600, // BLACK HOUND CUIRASS
0x01028500, // GRATIA
0x01028A00, // YATA MIRROR
0x01029900, // STINK SHIELD
0x01034800, // YASAKANI MAGATAMA
0x01035100, // SMARTLINK
0x01035200, // DIVINE PROTECTION
0x01035B00, // Centurion/Ability
0x03100000, // Photon Drop
],
],
],
"QuestF95FResultItems": [
+91
View File
@@ -9,6 +9,42 @@ fi
DIR=tests/game-tables
PMT_PREFIX=$DIR/item-parameter-table
MMT_PREFIX=$DIR/mag-metadata-table
echo "... (armor-random-shop-set)"
$EXECUTABLE decode-armor-shop-random-set --big-endian $DIR/armor-shop-random-set.expected.bin $DIR/armor-shop-random-set.json
$EXECUTABLE encode-armor-shop-random-set --big-endian $DIR/armor-shop-random-set.json $DIR/armor-shop-random-set.encoded.bin
bindiff $DIR/armor-shop-random-set.expected.bin $DIR/armor-shop-random-set.encoded.bin
echo "... (tool-random-shop-set)"
$EXECUTABLE decode-tool-shop-random-set --big-endian $DIR/tool-shop-random-set.expected.bin $DIR/tool-shop-random-set.json
$EXECUTABLE encode-tool-shop-random-set --big-endian $DIR/tool-shop-random-set.json $DIR/tool-shop-random-set.encoded.bin
bindiff $DIR/tool-shop-random-set.expected.bin $DIR/tool-shop-random-set.encoded.bin
echo "... (weapon-random-shop-set-normal)"
$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-normal.expected.bin $DIR/weapon-shop-random-set-normal.json
$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-normal.json $DIR/weapon-shop-random-set-normal.encoded.bin
bindiff $DIR/weapon-shop-random-set-normal.expected.bin $DIR/weapon-shop-random-set-normal.encoded.bin
echo "... (weapon-random-shop-set-hard)"
$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-hard.expected.bin $DIR/weapon-shop-random-set-hard.json
$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-hard.json $DIR/weapon-shop-random-set-hard.encoded.bin
bindiff $DIR/weapon-shop-random-set-hard.expected.bin $DIR/weapon-shop-random-set-hard.encoded.bin
echo "... (weapon-random-shop-set-very-hard)"
$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-very-hard.expected.bin $DIR/weapon-shop-random-set-very-hard.json
$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-very-hard.json $DIR/weapon-shop-random-set-very-hard.encoded.bin
bindiff $DIR/weapon-shop-random-set-very-hard.expected.bin $DIR/weapon-shop-random-set-very-hard.encoded.bin
echo "... (weapon-random-shop-set-ultimate)"
$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-ultimate.expected.bin $DIR/weapon-shop-random-set-ultimate.json
$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-ultimate.json $DIR/weapon-shop-random-set-ultimate.encoded.bin
bindiff $DIR/weapon-shop-random-set-ultimate.expected.bin $DIR/weapon-shop-random-set-ultimate.encoded.bin
echo "... (tekker-adjustment-set)"
$EXECUTABLE decode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.json
$EXECUTABLE encode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.json $DIR/tekker-adjustment-set.encoded.bin
bindiff $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.encoded.bin
echo "... (battle-params)"
$EXECUTABLE decode-battle-params tests/game-tables/battle-params-ep1-on.dat tests/game-tables/battle-params-ep2-on.dat tests/game-tables/battle-params-ep4-on.dat tests/game-tables/battle-params-ep1-off.dat tests/game-tables/battle-params-ep2-off.dat tests/game-tables/battle-params-ep4-off.dat tests/game-tables/battle-params.json
@@ -85,5 +121,60 @@ $EXECUTABLE decode-item-parameter-table --bb-v4 $PMT_PREFIX-bb-v4.expected.bin -
$EXECUTABLE encode-item-parameter-table --bb-v4 $PMT_PREFIX-bb-v4.json $PMT_PREFIX-bb-v4.encoded.bin --decompressed
bindiff $PMT_PREFIX-bb-v4.expected.bin $PMT_PREFIX-bb-v4.encoded.bin
echo "... (mag-metadata-table) DC 11/2000"
$EXECUTABLE decode-mag-metadata-table --dc-11-2000 $MMT_PREFIX-dc-11-2000.expected.bin --decompressed $MMT_PREFIX-dc-11-2000.json --hex
$EXECUTABLE encode-mag-metadata-table --dc-11-2000 $MMT_PREFIX-dc-11-2000.json $MMT_PREFIX-dc-11-2000.encoded.bin --decompressed
bindiff $MMT_PREFIX-dc-11-2000.expected.bin $MMT_PREFIX-dc-11-2000.encoded.bin
echo "... (mag-metadata-table) DC V1"
$EXECUTABLE decode-mag-metadata-table --dc-v1 $MMT_PREFIX-dc-v1.expected.bin --decompressed $MMT_PREFIX-dc-v1.json --hex
$EXECUTABLE encode-mag-metadata-table --dc-v1 $MMT_PREFIX-dc-v1.json $MMT_PREFIX-dc-v1.encoded.bin --decompressed
bindiff $MMT_PREFIX-dc-v1.expected.bin $MMT_PREFIX-dc-v1.encoded.bin
echo "... (mag-metadata-table) DC V2"
$EXECUTABLE decode-mag-metadata-table --dc-v2 $MMT_PREFIX-dc-v2.expected.bin --decompressed $MMT_PREFIX-dc-v2.json --hex
$EXECUTABLE encode-mag-metadata-table --dc-v2 $MMT_PREFIX-dc-v2.json $MMT_PREFIX-dc-v2.encoded.bin --decompressed
bindiff $MMT_PREFIX-dc-v2.expected.bin $MMT_PREFIX-dc-v2.encoded.bin
echo "... (mag-metadata-table) PC NTE"
$EXECUTABLE decode-mag-metadata-table --pc-nte $MMT_PREFIX-pc-nte.expected.bin --decompressed $MMT_PREFIX-pc-nte.json --hex
$EXECUTABLE encode-mag-metadata-table --pc-nte $MMT_PREFIX-pc-nte.json $MMT_PREFIX-pc-nte.encoded.bin --decompressed
bindiff $MMT_PREFIX-pc-nte.expected.bin $MMT_PREFIX-pc-nte.encoded.bin
echo "... (mag-metadata-table) PC V2"
$EXECUTABLE decode-mag-metadata-table --pc-v2 $MMT_PREFIX-pc-v2.expected.bin --decompressed $MMT_PREFIX-pc-v2.json --hex
$EXECUTABLE encode-mag-metadata-table --pc-v2 $MMT_PREFIX-pc-v2.json $MMT_PREFIX-pc-v2.encoded.bin --decompressed
bindiff $MMT_PREFIX-pc-v2.expected.bin $MMT_PREFIX-pc-v2.encoded.bin
echo "... (mag-metadata-table) GC NTE"
$EXECUTABLE decode-mag-metadata-table --gc-nte $MMT_PREFIX-gc-nte.expected.bin --decompressed $MMT_PREFIX-gc-nte.json --hex
$EXECUTABLE encode-mag-metadata-table --gc-nte $MMT_PREFIX-gc-nte.json $MMT_PREFIX-gc-nte.encoded.bin --decompressed
bindiff $MMT_PREFIX-gc-nte.expected.bin $MMT_PREFIX-gc-nte.encoded.bin
echo "... (mag-metadata-table) GC V3"
$EXECUTABLE decode-mag-metadata-table --gc-v3 $MMT_PREFIX-gc-v3.expected.bin --decompressed $MMT_PREFIX-gc-v3.json --hex
$EXECUTABLE encode-mag-metadata-table --gc-v3 $MMT_PREFIX-gc-v3.json $MMT_PREFIX-gc-v3.encoded.bin --decompressed
bindiff $MMT_PREFIX-gc-v3.expected.bin $MMT_PREFIX-gc-v3.encoded.bin
echo "... (mag-metadata-table) GC Ep3 NTE"
$EXECUTABLE decode-mag-metadata-table --gc-ep3-nte $MMT_PREFIX-gc-ep3-nte.expected.bin --decompressed $MMT_PREFIX-gc-ep3-nte.json --hex
$EXECUTABLE encode-mag-metadata-table --gc-ep3-nte $MMT_PREFIX-gc-ep3-nte.json $MMT_PREFIX-gc-ep3-nte.encoded.bin --decompressed
bindiff $MMT_PREFIX-gc-ep3-nte.expected.bin $MMT_PREFIX-gc-ep3-nte.encoded.bin
echo "... (mag-metadata-table) GC Ep3"
$EXECUTABLE decode-mag-metadata-table --gc-ep3 $MMT_PREFIX-gc-ep3.expected.bin --decompressed $MMT_PREFIX-gc-ep3.json --hex
$EXECUTABLE encode-mag-metadata-table --gc-ep3 $MMT_PREFIX-gc-ep3.json $MMT_PREFIX-gc-ep3.encoded.bin --decompressed
bindiff $MMT_PREFIX-gc-ep3.expected.bin $MMT_PREFIX-gc-ep3.encoded.bin
echo "... (mag-metadata-table) XB"
$EXECUTABLE decode-mag-metadata-table --xb-v3 $MMT_PREFIX-xb-v3.expected.bin --decompressed $MMT_PREFIX-xb-v3.json --hex
$EXECUTABLE encode-mag-metadata-table --xb-v3 $MMT_PREFIX-xb-v3.json $MMT_PREFIX-xb-v3.encoded.bin --decompressed
bindiff $MMT_PREFIX-xb-v3.expected.bin $MMT_PREFIX-xb-v3.encoded.bin
echo "... (mag-metadata-table) BB"
$EXECUTABLE decode-mag-metadata-table --bb-v4 $MMT_PREFIX-bb-v4.expected.bin --decompressed $MMT_PREFIX-bb-v4.json --hex
$EXECUTABLE encode-mag-metadata-table --bb-v4 $MMT_PREFIX-bb-v4.json $MMT_PREFIX-bb-v4.encoded.bin --decompressed
bindiff $MMT_PREFIX-bb-v4.expected.bin $MMT_PREFIX-bb-v4.encoded.bin
echo "... clean up"
rm -f $DIR/*.encoded.bin $DIR/*.json $DIR/battle-params.json.enc* $DIR/battle-params-encoded*