add Ep3 NTE side-by-side card defs comparison
This commit is contained in:
@@ -975,9 +975,25 @@ string CardDefinition::str(bool single_line, const TextSet* text_archive) const
|
||||
}
|
||||
}
|
||||
}
|
||||
string jp_name_s = this->jp_name.decode();
|
||||
string en_name_short_s = this->en_short_name.decode();
|
||||
string jp_name_short_s = this->jp_short_name.decode();
|
||||
string names_str;
|
||||
if (!en_name_s.empty()) {
|
||||
names_str += phosg::string_printf(" EN: \"%s\"", en_name_s.c_str());
|
||||
if (!en_name_short_s.empty() && en_name_short_s != en_name_s) {
|
||||
names_str += phosg::string_printf(" (Abr. \"%s\")", en_name_short_s.c_str());
|
||||
}
|
||||
}
|
||||
if (!jp_name_s.empty()) {
|
||||
names_str += phosg::string_printf(" JP: \"%s\"", jp_name_s.c_str());
|
||||
if (!jp_name_short_s.empty() && jp_name_short_s != jp_name_s) {
|
||||
names_str += phosg::string_printf(" (Abr. \"%s\")", jp_name_short_s.c_str());
|
||||
}
|
||||
}
|
||||
return phosg::string_printf(
|
||||
"\
|
||||
Card: %04" PRIX32 " \"%s\"\n\
|
||||
Card: %04" PRIX32 "%s\n\
|
||||
Type: %s, class: %s\n\
|
||||
Usability condition: %s\n\
|
||||
Rank: %s\n\
|
||||
@@ -998,7 +1014,7 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
%s\n\
|
||||
Effects:%s",
|
||||
this->card_id.load(),
|
||||
en_name_s.c_str(),
|
||||
names_str.c_str(),
|
||||
type_str.c_str(),
|
||||
card_class_str.c_str(),
|
||||
criterion_str.c_str(),
|
||||
@@ -2590,12 +2606,15 @@ MapIndex::VersionedMap::VersionedMap(std::string&& compressed_data, uint8_t lang
|
||||
: language(language),
|
||||
compressed_data(std::move(compressed_data)) {
|
||||
string decompressed = prs_decompress(this->compressed_data);
|
||||
if (decompressed.size() != sizeof(MapDefinition)) {
|
||||
if (decompressed.size() == sizeof(MapDefinitionTrial)) {
|
||||
this->map = make_shared<MapDefinition>(*reinterpret_cast<const MapDefinitionTrial*>(decompressed.data()));
|
||||
} else if (decompressed.size() == sizeof(MapDefinition)) {
|
||||
this->map = make_shared<MapDefinition>(*reinterpret_cast<const MapDefinition*>(decompressed.data()));
|
||||
} else {
|
||||
throw runtime_error(phosg::string_printf(
|
||||
"decompressed data size is incorrect (expected %zu bytes, read %zu bytes)",
|
||||
sizeof(MapDefinition), decompressed.size()));
|
||||
}
|
||||
this->map = make_shared<MapDefinition>(*reinterpret_cast<const MapDefinition*>(decompressed.data()));
|
||||
}
|
||||
|
||||
shared_ptr<const MapDefinitionTrial> MapIndex::VersionedMap::trial() const {
|
||||
|
||||
@@ -1413,7 +1413,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 59DC */ uint8_t map_category;
|
||||
|
||||
// This field determines block graphics to be used in the Cyber environment.
|
||||
// There are 10 block types (0-9); if this value is > 9, type 0 is used.
|
||||
// There are 10 block types (0-9); if this value is > 9, type 0 is used. This
|
||||
// field has no effect in Ep3 NTE, even though there are 6 different block
|
||||
// texture files on the NTE disc.
|
||||
/* 59DD */ uint8_t cyber_block_type;
|
||||
|
||||
/* 59DE */ be_uint16_t unknown_a11;
|
||||
|
||||
+203
-125
@@ -2013,17 +2013,22 @@ Action a_show_ep3_cards(
|
||||
|
||||
Action a_generate_ep3_cards_html(
|
||||
"generate-ep3-cards-html", "\
|
||||
generate-ep3-cards-html [--ep3-nte] [--threads=N] [--no-images]\n\
|
||||
generate-ep3-cards-html [--ep3-nte] [--compare] [--threads=N] [--no-images]\n\
|
||||
[--no-disassembly]\n\
|
||||
Generate an HTML file describing all Episode 3 card definitions from the\n\
|
||||
system/ep3 directory. If --ep3-nte is given, use the Trial Edition card\n\
|
||||
definitions instead. If --no-images is given, omit the card images.\n",
|
||||
+[](phosg::Arguments& args) {
|
||||
size_t num_threads = args.get<size_t>("threads", 0);
|
||||
|
||||
bool is_nte = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3_NTE);
|
||||
bool include_nte = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3_NTE) || args.get<bool>("compare");
|
||||
bool include_final = (get_cli_version(args, Version::GC_EP3) == Version::GC_EP3) || args.get<bool>("compare");
|
||||
bool no_images = args.get<bool>("no-images");
|
||||
bool no_large_images = args.get<bool>("no-large-images");
|
||||
bool no_disassembly = args.get<bool>("no-disassembly");
|
||||
|
||||
auto s = make_shared<ServerState>(get_config_filename(args));
|
||||
s->clear_file_caches(false);
|
||||
s->load_patch_indexes(false);
|
||||
s->load_text_index(false);
|
||||
s->load_ep3_cards(false);
|
||||
@@ -2034,148 +2039,221 @@ Action a_generate_ep3_cards_html(
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
struct CardInfo {
|
||||
shared_ptr<const Episode3::CardIndex::CardEntry> ce;
|
||||
string small_filename;
|
||||
string medium_filename;
|
||||
string large_filename;
|
||||
string small_data_url;
|
||||
string medium_data_url;
|
||||
string large_data_url;
|
||||
struct VersionInfo {
|
||||
struct CardInfo {
|
||||
shared_ptr<const Episode3::CardIndex::CardEntry> ce;
|
||||
string small_filename;
|
||||
string medium_filename;
|
||||
string large_filename;
|
||||
string small_data_url;
|
||||
string medium_data_url;
|
||||
string large_data_url;
|
||||
|
||||
bool is_empty() const {
|
||||
return (this->ce == nullptr) && this->small_data_url.empty() && this->medium_data_url.empty() && this->large_data_url.empty();
|
||||
bool is_empty() const {
|
||||
return (this->ce == nullptr) && this->small_data_url.empty() && this->medium_data_url.empty() && this->large_data_url.empty();
|
||||
}
|
||||
};
|
||||
|
||||
const char* name;
|
||||
vector<CardInfo> card_infos;
|
||||
bool show_large_column = false;
|
||||
bool show_medium_column = false;
|
||||
bool show_small_column = false;
|
||||
size_t num_output_columns = 2;
|
||||
|
||||
VersionInfo(
|
||||
const char* name,
|
||||
shared_ptr<const Episode3::CardIndex> card_index,
|
||||
const char* cardtex_directory,
|
||||
bool no_large_images,
|
||||
size_t num_threads,
|
||||
bool no_disassembly)
|
||||
: name(name) {
|
||||
for (uint32_t card_id : card_index->all_ids()) {
|
||||
if (this->card_infos.size() <= card_id) {
|
||||
this->card_infos.resize(card_id + 1);
|
||||
}
|
||||
this->card_infos[card_id].ce = card_index->definition_for_id(card_id);
|
||||
}
|
||||
|
||||
if (cardtex_directory) {
|
||||
for (const auto& filename : phosg::list_directory_sorted(cardtex_directory)) {
|
||||
if ((filename[0] == 'C' || filename[0] == 'M' || filename[0] == 'L') && (filename[1] == '_')) {
|
||||
size_t card_id = stoull(filename.substr(2, 3), nullptr, 10);
|
||||
if (this->card_infos.size() <= card_id) {
|
||||
this->card_infos.resize(card_id + 1);
|
||||
}
|
||||
auto& info = this->card_infos[card_id];
|
||||
if (filename[0] == 'C' && !no_large_images) {
|
||||
info.large_filename = string(cardtex_directory) + "/" + filename;
|
||||
this->show_large_column = true;
|
||||
} else if (filename[0] == 'L') {
|
||||
info.medium_filename = string(cardtex_directory) + "/" + filename;
|
||||
this->show_medium_column = true;
|
||||
} else if (filename[0] == 'M') {
|
||||
info.small_filename = string(cardtex_directory) + "/" + filename;
|
||||
this->show_small_column = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phosg::parallel_range<uint32_t>([&](uint32_t index, size_t) -> bool {
|
||||
auto& info = this->card_infos[index];
|
||||
if (!info.large_filename.empty()) {
|
||||
phosg::Image img(info.large_filename);
|
||||
phosg::Image cropped(512, 399);
|
||||
cropped.blit(img, 0, 0, 512, 399, 0, 0);
|
||||
info.large_data_url = cropped.png_data_url();
|
||||
}
|
||||
if (!info.medium_filename.empty()) {
|
||||
phosg::Image img(info.medium_filename);
|
||||
phosg::Image cropped(184, 144);
|
||||
cropped.blit(img, 0, 0, 184, 144, 0, 0);
|
||||
info.medium_data_url = cropped.png_data_url();
|
||||
}
|
||||
if (!info.small_filename.empty()) {
|
||||
phosg::Image img(info.small_filename);
|
||||
phosg::Image cropped(58, 43);
|
||||
cropped.blit(img, 0, 0, 58, 43, 0, 0);
|
||||
info.small_data_url = cropped.png_data_url();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
0, this->card_infos.size(), num_threads);
|
||||
}
|
||||
|
||||
this->num_output_columns = 1 + (!no_disassembly) + this->show_small_column + this->show_medium_column + this->show_large_column;
|
||||
}
|
||||
|
||||
const CardInfo* get_entry(size_t card_id) const {
|
||||
if (card_id >= this->card_infos.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto* entry = &this->card_infos[card_id];
|
||||
return entry->is_empty() ? nullptr : entry;
|
||||
}
|
||||
};
|
||||
auto card_index = is_nte ? s->ep3_card_index_trial : s->ep3_card_index;
|
||||
vector<CardInfo> infos;
|
||||
for (uint32_t card_id : card_index->all_ids()) {
|
||||
if (infos.size() <= card_id) {
|
||||
infos.resize(card_id + 1);
|
||||
}
|
||||
infos[card_id].ce = card_index->definition_for_id(card_id);
|
||||
}
|
||||
bool show_large_column = false;
|
||||
bool show_medium_column = false;
|
||||
bool show_small_column = false;
|
||||
if (!no_images) {
|
||||
for (const auto& filename : phosg::list_directory_sorted("system/ep3/cardtex")) {
|
||||
if ((filename[0] == 'C' || filename[0] == 'M' || filename[0] == 'L') && (filename[1] == '_')) {
|
||||
size_t card_id = stoull(filename.substr(2, 3), nullptr, 10);
|
||||
if (infos.size() <= card_id) {
|
||||
infos.resize(card_id + 1);
|
||||
}
|
||||
auto& info = infos[card_id];
|
||||
if (filename[0] == 'C') {
|
||||
info.large_filename = "system/ep3/cardtex/" + filename;
|
||||
show_large_column = true;
|
||||
} else if (filename[0] == 'L') {
|
||||
info.medium_filename = "system/ep3/cardtex/" + filename;
|
||||
show_medium_column = true;
|
||||
} else if (filename[0] == 'M') {
|
||||
info.small_filename = "system/ep3/cardtex/" + filename;
|
||||
show_small_column = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phosg::parallel_range<uint32_t>([&](uint32_t index, size_t) -> bool {
|
||||
auto& info = infos[index];
|
||||
if (!info.large_filename.empty()) {
|
||||
phosg::Image img(info.large_filename);
|
||||
phosg::Image cropped(512, 399);
|
||||
cropped.blit(img, 0, 0, 512, 399, 0, 0);
|
||||
info.large_data_url = cropped.png_data_url();
|
||||
}
|
||||
if (!info.medium_filename.empty()) {
|
||||
phosg::Image img(info.medium_filename);
|
||||
phosg::Image cropped(184, 144);
|
||||
cropped.blit(img, 0, 0, 184, 144, 0, 0);
|
||||
info.medium_data_url = cropped.png_data_url();
|
||||
}
|
||||
if (!info.small_filename.empty()) {
|
||||
phosg::Image img(info.small_filename);
|
||||
phosg::Image cropped(58, 43);
|
||||
cropped.blit(img, 0, 0, 58, 43, 0, 0);
|
||||
info.small_data_url = cropped.png_data_url();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
0, infos.size(), num_threads);
|
||||
vector<VersionInfo> version_infos;
|
||||
if (include_nte) {
|
||||
version_infos.emplace_back("NTE", s->ep3_card_index_trial, no_images ? nullptr : "system/ep3/cardtex-trial", no_large_images, num_threads, no_disassembly);
|
||||
}
|
||||
if (include_final) {
|
||||
version_infos.emplace_back("Final", s->ep3_card_index, no_images ? nullptr : "system/ep3/cardtex", no_large_images, num_threads, no_disassembly);
|
||||
}
|
||||
|
||||
deque<string> blocks;
|
||||
blocks.emplace_back("<html><head><title>Phantasy Star Online Episode III cards</title></head><body style=\"background-color:#222222; color: #EEEEEE\">");
|
||||
blocks.emplace_back("<table><tr><th style=\"text-align: left\">Legend:</th></tr><tr style=\"background-color: #663333\"><td>Card has no definition and is obviously incomplete</td></tr><tr style=\"background-color: #336633\"><td>Card is unobtainable in random draws but may be a quest or event reward</td></tr><tr style=\"background-color: #333333\"><td>Card is obtainable in random draws</td></tr></table><br /><br />");
|
||||
blocks.emplace_back("<table><tr><th style=\"text-align: left; padding: 4px\">ID</th>");
|
||||
if (show_small_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Small</th>");
|
||||
blocks.emplace_back("<table><tr><th rowspan=\"2\" style=\"text-align: left; padding: 4px\">ID</th>");
|
||||
|
||||
for (const auto& vi : version_infos) {
|
||||
blocks.emplace_back(phosg::string_printf("<th colspan=\"%zu\" style=\"text-align: left; padding: 4px\">%s</th>",
|
||||
vi.num_output_columns, vi.name));
|
||||
}
|
||||
if (show_medium_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Medium</th>");
|
||||
blocks.emplace_back("</tr><tr>");
|
||||
for (const auto& vi : version_infos) {
|
||||
if (vi.show_small_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Small</th>");
|
||||
}
|
||||
if (vi.show_medium_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Medium</th>");
|
||||
}
|
||||
if (vi.show_large_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Large</th>");
|
||||
}
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Text</th><th style=\"text-align: left; padding: 4px\">Disassembly</th>");
|
||||
}
|
||||
if (show_large_column) {
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Large</th>");
|
||||
blocks.emplace_back("</tr>");
|
||||
|
||||
size_t num_infos = 0;
|
||||
for (const auto& vi : version_infos) {
|
||||
num_infos = std::max<size_t>(num_infos, vi.card_infos.size());
|
||||
}
|
||||
blocks.emplace_back("<th style=\"text-align: left; padding: 4px\">Text</th><th style=\"text-align: left; padding: 4px\">Disassembly</th></tr>");
|
||||
for (size_t card_id = 0; card_id < infos.size(); card_id++) {
|
||||
const auto& entry = infos[card_id];
|
||||
if (entry.is_empty()) {
|
||||
|
||||
for (size_t card_id = 0; card_id < num_infos; card_id++) {
|
||||
bool any_vi_has_entry = false;
|
||||
for (const auto& vi : version_infos) {
|
||||
if (vi.get_entry(card_id)) {
|
||||
any_vi_has_entry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!any_vi_has_entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* background_color;
|
||||
if (!entry.ce) {
|
||||
background_color = "#663333";
|
||||
} else if (entry.ce->def.cannot_drop ||
|
||||
((entry.ce->def.rank == Episode3::CardRank::D1) || (entry.ce->def.rank == Episode3::CardRank::D2) || (entry.ce->def.rank == Episode3::CardRank::D3)) ||
|
||||
((entry.ce->def.card_class() == Episode3::CardClass::BOSS_ATTACK_ACTION) || (entry.ce->def.card_class() == Episode3::CardClass::BOSS_TECH)) ||
|
||||
((entry.ce->def.drop_rates[0] == 6) && (entry.ce->def.drop_rates[1] == 6))) {
|
||||
background_color = "#336633";
|
||||
} else {
|
||||
background_color = "#333333";
|
||||
}
|
||||
blocks.emplace_back(phosg::string_printf("<tr><td style=\"padding: 4px; vertical-align: top\"><pre>%04zX</pre></td>", card_id));
|
||||
|
||||
blocks.emplace_back(phosg::string_printf("<tr style=\"background-color: %s\">", background_color));
|
||||
blocks.emplace_back(phosg::string_printf("<td style=\"padding: 4px; vertical-align: top\"><pre>%04zX</pre></td>", card_id));
|
||||
if (show_small_column) {
|
||||
blocks.emplace_back("<td style=\"padding: 4px; vertical-align: top\">");
|
||||
if (!entry.small_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry.small_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
for (const auto& vi : version_infos) {
|
||||
const VersionInfo::CardInfo* entry = vi.get_entry(card_id);
|
||||
if (!entry) {
|
||||
blocks.emplace_back(phosg::string_printf("<td colspan=\"%zu\" style=\"padding: 4px; vertical-align: top\"><pre>No entry</pre></td>",
|
||||
vi.num_output_columns));
|
||||
continue;
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
if (show_medium_column) {
|
||||
blocks.emplace_back("<td style=\"padding: 4px; vertical-align: top\">");
|
||||
if (!entry.medium_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry.medium_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
|
||||
const char* background_color;
|
||||
if (!entry->ce) {
|
||||
background_color = "#663333";
|
||||
} else if (entry->ce->def.cannot_drop ||
|
||||
((entry->ce->def.rank == Episode3::CardRank::D1) || (entry->ce->def.rank == Episode3::CardRank::D2) || (entry->ce->def.rank == Episode3::CardRank::D3)) ||
|
||||
((entry->ce->def.card_class() == Episode3::CardClass::BOSS_ATTACK_ACTION) || (entry->ce->def.card_class() == Episode3::CardClass::BOSS_TECH)) ||
|
||||
((entry->ce->def.drop_rates[0] == 6) && (entry->ce->def.drop_rates[1] == 6))) {
|
||||
background_color = "#336633";
|
||||
} else {
|
||||
background_color = "#333333";
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
if (show_large_column) {
|
||||
blocks.emplace_back("<td style=\"padding: 4px; vertical-align: top\">");
|
||||
if (!entry.large_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry.large_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
|
||||
string td_tag = phosg::string_printf("<td style=\"padding: 4px; vertical-align: top; background-color: %s\">", background_color);
|
||||
if (vi.show_small_column) {
|
||||
blocks.emplace_back(td_tag);
|
||||
if (!entry->small_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry->small_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
if (vi.show_medium_column) {
|
||||
blocks.emplace_back(td_tag);
|
||||
if (!entry->medium_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry->medium_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
if (vi.show_large_column) {
|
||||
blocks.emplace_back(td_tag);
|
||||
if (!entry->large_data_url.empty()) {
|
||||
blocks.emplace_back("<img src=\"");
|
||||
blocks.emplace_back(std::move(entry->large_data_url));
|
||||
blocks.emplace_back("\" />");
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
blocks.emplace_back(td_tag);
|
||||
if (entry->ce) {
|
||||
blocks.emplace_back("<pre>");
|
||||
blocks.emplace_back(entry->ce->text);
|
||||
blocks.emplace_back("</pre></td>");
|
||||
if (!no_disassembly) {
|
||||
blocks.emplace_back(td_tag);
|
||||
blocks.emplace_back("<pre>");
|
||||
blocks.emplace_back(entry->ce->def.str(false, text_english.get()));
|
||||
blocks.emplace_back("</pre></td>");
|
||||
}
|
||||
} else {
|
||||
blocks.emplace_back("</td>");
|
||||
if (!no_disassembly) {
|
||||
blocks.emplace_back(td_tag);
|
||||
blocks.emplace_back("<pre>Definition is missing</pre>");
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
}
|
||||
blocks.emplace_back("</td>");
|
||||
}
|
||||
blocks.emplace_back("<td style=\"padding: 4px; vertical-align: top\">");
|
||||
if (entry.ce) {
|
||||
blocks.emplace_back("<pre>");
|
||||
blocks.emplace_back(entry.ce->text);
|
||||
blocks.emplace_back("</pre></td><td style=\"padding: 4px; vertical-align: top\"><pre>");
|
||||
blocks.emplace_back(entry.ce->def.str(false, text_english.get()));
|
||||
blocks.emplace_back("</pre>");
|
||||
} else {
|
||||
blocks.emplace_back("</td><td style=\"padding: 4px; vertical-align: top\"><pre>Definition is missing</pre>");
|
||||
}
|
||||
blocks.emplace_back("</td></tr>");
|
||||
blocks.emplace_back("</tr>");
|
||||
}
|
||||
blocks.emplace_back("</table></body></html>");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user