rewrite text encoding to handle non-English properly
This commit is contained in:
@@ -354,7 +354,7 @@ void BattleRecordPlayer::schedule_events() {
|
||||
send_command(l, 0xC9, 0x00, ev.data);
|
||||
break;
|
||||
case BattleRecord::Event::Type::CHAT_MESSAGE:
|
||||
send_chat_message(l, ev.guild_card_number, decode_sjis(ev.data));
|
||||
send_prepared_chat_message(l, ev.guild_card_number, ev.data);
|
||||
break;
|
||||
}
|
||||
this->event_it++;
|
||||
|
||||
@@ -166,10 +166,12 @@ ssize_t Card::apply_abnormal_condition(
|
||||
cond.value8 = value + existing_cond_value;
|
||||
cond.random_percent = random_percent;
|
||||
|
||||
switch (eff.arg1[0]) {
|
||||
case 'a':
|
||||
cond.a_arg_value = atoi(&eff.arg1[1]);
|
||||
switch (eff.arg1.at(0)) {
|
||||
case 'a': {
|
||||
string s = eff.arg1.decode();
|
||||
cond.a_arg_value = atoi(s.c_str() + 1);
|
||||
break;
|
||||
}
|
||||
case 'e':
|
||||
cond.remaining_turns = 99;
|
||||
break;
|
||||
@@ -179,8 +181,10 @@ ssize_t Card::apply_abnormal_condition(
|
||||
case 'r':
|
||||
cond.remaining_turns = 102;
|
||||
break;
|
||||
case 't':
|
||||
cond.remaining_turns = atoi(&eff.arg1[1]);
|
||||
case 't': {
|
||||
string s = eff.arg1.decode();
|
||||
cond.remaining_turns = atoi(s.c_str() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
string cond_str = cond.str();
|
||||
|
||||
+19
-10
@@ -358,7 +358,7 @@ bool CardSpecial::apply_defense_condition(
|
||||
defense_state, defender_card, dice_roll, defender_cond->card_ref,
|
||||
defender_cond->condition_giver_card_ref);
|
||||
|
||||
string expr = orig_eff->expr;
|
||||
string expr = orig_eff->expr.decode();
|
||||
int16_t expr_value = this->evaluate_effect_expr(astats, expr.c_str(), dice_roll);
|
||||
this->execute_effect(
|
||||
*defender_cond, defender_card, expr_value, defender_cond->value,
|
||||
@@ -1397,9 +1397,9 @@ bool CardSpecial::evaluate_effect_arg2_condition(
|
||||
if (ce->def.effects[cond_index].type == ConditionType::NONE) {
|
||||
break;
|
||||
}
|
||||
uint8_t arg2_command = ce->def.effects[cond_index].arg2[0];
|
||||
uint8_t arg2_command = ce->def.effects[cond_index].arg2.at(0);
|
||||
if ((arg2_command == 'c') || (arg2_command == 'C')) {
|
||||
uint8_t other_ch1 = ce->def.effects[cond_index].arg2[1] - 0x30;
|
||||
uint8_t other_ch1 = ce->def.effects[cond_index].arg2.at(1) - 0x30;
|
||||
if ((other_ch1 > 9)) {
|
||||
return false;
|
||||
}
|
||||
@@ -2007,8 +2007,9 @@ bool CardSpecial::execute_effect(
|
||||
|
||||
case ConditionType::BONUS_FROM_LEADER:
|
||||
if (unknown_p7 & 1) {
|
||||
clamped_unknown_p5 = this->count_cards_with_card_id_except_card_ref(expr_value, 0xFFFF) + (card->action_chain).chain.ap_effect_bonus;
|
||||
(card->action_chain).chain.ap_effect_bonus = clamp<int16_t>(clamped_unknown_p5, -99, 99);
|
||||
size_t leader_count = this->count_cards_with_card_id_except_card_ref(expr_value, 0xFFFF);
|
||||
card->action_chain.chain.ap_effect_bonus = clamp<int16_t>(
|
||||
leader_count + card->action_chain.chain.ap_effect_bonus, -99, 99);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -3192,12 +3193,16 @@ bool CardSpecial::is_card_targeted_by_condition(
|
||||
}
|
||||
if (cond.remaining_turns == 102) {
|
||||
if (sc_card && ((sc_card == card) || !(sc_card->card_flags & 2))) {
|
||||
string arg3_s = ce->def.effects[cond.card_definition_effect_index].arg3.decode();
|
||||
if (arg3_s.size() < 1) {
|
||||
throw runtime_error("card definition arg3 is missing");
|
||||
}
|
||||
auto target_cards = this->get_targeted_cards_for_condition(
|
||||
cond.card_ref,
|
||||
cond.card_definition_effect_index,
|
||||
cond.condition_giver_card_ref,
|
||||
as,
|
||||
atoi(&ce->def.effects[cond.card_definition_effect_index].arg3[1]),
|
||||
atoi(arg3_s.c_str() + 1),
|
||||
0);
|
||||
for (auto c : target_cards) {
|
||||
if (c == card) {
|
||||
@@ -3686,7 +3691,11 @@ void CardSpecial::evaluate_and_apply_effects(
|
||||
continue;
|
||||
}
|
||||
|
||||
int16_t arg3_value = atoi(&card_effect.arg3[1]);
|
||||
string arg3_s = card_effect.arg3.decode();
|
||||
if (arg3_s.size() < 1) {
|
||||
throw runtime_error("card effect arg3 is missing");
|
||||
}
|
||||
int16_t arg3_value = atoi(arg3_s.c_str() + 1);
|
||||
effect_log.debug("arg3_value=%hd", arg3_value);
|
||||
auto targeted_cards = this->get_targeted_cards_for_condition(
|
||||
set_card_ref, def_effect_index, sc_card_ref, as, arg3_value, 1);
|
||||
@@ -3701,7 +3710,7 @@ void CardSpecial::evaluate_and_apply_effects(
|
||||
size_t count = 0;
|
||||
for (size_t z = 0; z < targeted_cards.size(); z++) {
|
||||
dice_roll.value_used_in_expr = false;
|
||||
string arg2_text = card_effect.arg2;
|
||||
string arg2_text = card_effect.arg2.decode();
|
||||
if (this->evaluate_effect_arg2_condition(
|
||||
as, targeted_cards[z], arg2_text.c_str(), dice_roll,
|
||||
set_card_ref, sc_card_ref, random_percent, when)) {
|
||||
@@ -3731,7 +3740,7 @@ void CardSpecial::evaluate_and_apply_effects(
|
||||
for (size_t z = 0; z < targeted_cards.size(); z++) {
|
||||
auto target_log = effect_log.sub(string_printf("(target:@%04hX) ", targeted_cards[z]->get_card_ref()));
|
||||
dice_roll.value_used_in_expr = false;
|
||||
string arg2_str = card_effect.arg2;
|
||||
string arg2_str = card_effect.arg2.decode();
|
||||
target_log.debug("arg2_str = %s", arg2_str.c_str());
|
||||
if (all_targets_matched ||
|
||||
this->evaluate_effect_arg2_condition(
|
||||
@@ -3739,7 +3748,7 @@ void CardSpecial::evaluate_and_apply_effects(
|
||||
target_log.debug("arg2 condition passed");
|
||||
auto env_stats = this->compute_attack_env_stats(
|
||||
as, targeted_cards[z], dice_roll, set_card_ref, sc_card_ref);
|
||||
string expr_str = card_effect.expr;
|
||||
string expr_str = card_effect.expr.decode();
|
||||
int16_t value = this->evaluate_effect_expr(env_stats, expr_str.c_str(), dice_roll);
|
||||
target_log.debug("expr = %s, value = %hd", expr_str.c_str(), value);
|
||||
|
||||
|
||||
+37
-36
@@ -702,11 +702,11 @@ string CardDefinition::Stat::str() const {
|
||||
bool CardDefinition::Effect::is_empty() const {
|
||||
return (this->effect_num == 0 &&
|
||||
this->type == ConditionType::NONE &&
|
||||
this->expr.is_filled_with(0) &&
|
||||
this->expr.empty() &&
|
||||
this->when == 0 &&
|
||||
this->arg1.is_filled_with(0) &&
|
||||
this->arg2.is_filled_with(0) &&
|
||||
this->arg3.is_filled_with(0) &&
|
||||
this->arg1.empty() &&
|
||||
this->arg2.empty() &&
|
||||
this->arg3.empty() &&
|
||||
this->apply_criterion == CriterionCode::NONE &&
|
||||
this->name_index == 0);
|
||||
}
|
||||
@@ -781,12 +781,12 @@ string CardDefinition::Effect::str(const char* separator, const TextArchive* tex
|
||||
tokens.emplace_back(std::move(cmd_str));
|
||||
}
|
||||
if (!this->expr.empty()) {
|
||||
tokens.emplace_back("expr=" + string(this->expr));
|
||||
tokens.emplace_back("expr=" + this->expr.decode());
|
||||
}
|
||||
tokens.emplace_back(string_printf("when=%02hhX", this->when));
|
||||
tokens.emplace_back("arg1=" + this->str_for_arg(this->arg1));
|
||||
tokens.emplace_back("arg2=" + this->str_for_arg(this->arg2));
|
||||
tokens.emplace_back("arg3=" + this->str_for_arg(this->arg3));
|
||||
tokens.emplace_back("arg1=" + this->str_for_arg(this->arg1.decode()));
|
||||
tokens.emplace_back("arg2=" + this->str_for_arg(this->arg2.decode()));
|
||||
tokens.emplace_back("arg3=" + this->str_for_arg(this->arg3.decode()));
|
||||
{
|
||||
uint8_t type = static_cast<uint8_t>(this->apply_criterion);
|
||||
string cond_str = string_printf("cond=%02hhX", type);
|
||||
@@ -1118,6 +1118,7 @@ string CardDefinition::str(bool single_line, const TextArchive* text_archive) co
|
||||
}
|
||||
}
|
||||
|
||||
string en_name_s = this->en_name.decode();
|
||||
if (single_line) {
|
||||
string range_str = string_for_range(this->range);
|
||||
return string_printf(
|
||||
@@ -1126,7 +1127,7 @@ string CardDefinition::str(bool single_line, const TextArchive* text_archive) co
|
||||
"cannot_attack=%s cannot_drop=%s hp=%s ap=%s tp=%s mv=%s left=%s right=%s "
|
||||
"top=%s class=%s assist_ai_params=[target=%s priority=%hhu effect=%hhu] drop_rates=[%s, %s] effects=[%s]]",
|
||||
this->card_id.load(),
|
||||
this->en_name.data(),
|
||||
en_name_s.c_str(),
|
||||
type_str.c_str(),
|
||||
criterion_str.c_str(),
|
||||
rank_str.c_str(),
|
||||
@@ -1192,7 +1193,7 @@ Card: %04" PRIX32 " \"%s\"\n\
|
||||
%s\n\
|
||||
Effects:%s",
|
||||
this->card_id.load(),
|
||||
this->en_name.data(),
|
||||
en_name_s.c_str(),
|
||||
type_str.c_str(),
|
||||
card_class_str.c_str(),
|
||||
criterion_str.c_str(),
|
||||
@@ -1635,7 +1636,7 @@ string MapDefinition::CameraSpec::str() const {
|
||||
this->unknown_a2[1].load(), this->unknown_a2[2].load());
|
||||
}
|
||||
|
||||
string MapDefinition::str(const CardIndex* card_index) const {
|
||||
string MapDefinition::str(const CardIndex* card_index, uint8_t language) const {
|
||||
deque<string> lines;
|
||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||
for (size_t y = 0; y < this->height; y++) {
|
||||
@@ -1691,14 +1692,14 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
" a5[0x70:0x74]: %02hhX %02hhX %02hhX %02hhX",
|
||||
this->unknown_a5[0x70], this->unknown_a5[0x71], this->unknown_a5[0x72], this->unknown_a5[0x73]));
|
||||
lines.emplace_back(" default_rules: " + this->default_rules.str());
|
||||
lines.emplace_back(" name: " + format_data_string(this->name));
|
||||
lines.emplace_back(" location_name: " + format_data_string(this->location_name));
|
||||
lines.emplace_back(" quest_name: " + format_data_string(this->quest_name));
|
||||
lines.emplace_back(" description: " + format_data_string(this->description));
|
||||
lines.emplace_back(" name: " + format_data_string(this->name.decode(language)));
|
||||
lines.emplace_back(" location_name: " + format_data_string(this->location_name.decode(language)));
|
||||
lines.emplace_back(" quest_name: " + format_data_string(this->quest_name.decode(language)));
|
||||
lines.emplace_back(" description: " + format_data_string(this->description.decode(language)));
|
||||
lines.emplace_back(string_printf(" map_xy: %hu %hu", this->map_x.load(), this->map_y.load()));
|
||||
for (size_t z = 0; z < 3; z++) {
|
||||
lines.emplace_back(string_printf(" npc_chars[%zu]:", z));
|
||||
lines.emplace_back(" name: " + format_data_string(this->npc_ai_params[z].name));
|
||||
lines.emplace_back(" name: " + format_data_string(this->npc_ai_params[z].name.decode(language)));
|
||||
lines.emplace_back(string_printf(
|
||||
" ai_params: (a1: %04hX %04hX, is_arkz: %02hhX, a2: %02hX %02hX %02hX)",
|
||||
this->npc_ai_params[z].unknown_a1[0].load(), this->npc_ai_params[z].unknown_a1[1].load(),
|
||||
@@ -1719,7 +1720,7 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
this->npc_ai_params[z].params[0x7A].load(), this->npc_ai_params[z].params[0x7B].load(),
|
||||
this->npc_ai_params[z].params[0x7C].load(), this->npc_ai_params[z].params[0x7D].load()));
|
||||
lines.emplace_back(string_printf(" npc_decks[%zu]:", z));
|
||||
lines.emplace_back(" name: " + format_data_string(this->npc_decks[z].name));
|
||||
lines.emplace_back(" name: " + format_data_string(this->npc_decks[z].name.decode(language)));
|
||||
for (size_t w = 0; w < 0x20; w++) {
|
||||
uint16_t card_id = this->npc_decks[z].card_ids[w];
|
||||
shared_ptr<const CardIndex::CardEntry> entry;
|
||||
@@ -1730,7 +1731,7 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
}
|
||||
}
|
||||
if (entry) {
|
||||
string name = entry->def.en_name;
|
||||
string name = entry->def.en_name.decode(language);
|
||||
lines.emplace_back(string_printf(" cards[%02zu]: %04hX (%s)", w, card_id, name.c_str()));
|
||||
} else {
|
||||
lines.emplace_back(string_printf(" cards[%02zu]: %04hX", w, card_id));
|
||||
@@ -1744,8 +1745,8 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
lines.emplace_back(string_printf(" npc_dialogue[%zu][%zu] (when: %04hX, chance: %hu%%):",
|
||||
z, x, set.when.load(), set.percent_chance.load()));
|
||||
for (size_t w = 0; w < 4; w++) {
|
||||
if (set.strings[w][0] != 0 && static_cast<uint8_t>(set.strings[w][0]) != 0xFF) {
|
||||
string escaped = format_data_string(set.strings[w]);
|
||||
if (!set.strings[w].empty() && set.strings[w].at(0) != 0xFF) {
|
||||
string escaped = format_data_string(set.strings[w].decode(language));
|
||||
lines.emplace_back(string_printf(" strings[%zu]: %s", w, escaped.c_str()));
|
||||
}
|
||||
}
|
||||
@@ -1754,14 +1755,14 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
lines.emplace_back(" a7: " + format_data_string(this->unknown_a7.data(), this->unknown_a7.bytes()));
|
||||
lines.emplace_back(string_printf(" npc_ai_params_entry_index: [%08" PRIX32 ", %08" PRIX32 ", %08" PRIX32 "]",
|
||||
this->npc_ai_params_entry_index[0].load(), this->npc_ai_params_entry_index[1].load(), this->npc_ai_params_entry_index[2].load()));
|
||||
if (this->before_message[0]) {
|
||||
lines.emplace_back(" before_message: " + format_data_string(this->before_message));
|
||||
if (!this->before_message.empty()) {
|
||||
lines.emplace_back(" before_message: " + format_data_string(this->before_message.decode(language)));
|
||||
}
|
||||
if (this->after_message[0]) {
|
||||
lines.emplace_back(" after_message: " + format_data_string(this->after_message));
|
||||
if (!this->after_message.empty()) {
|
||||
lines.emplace_back(" after_message: " + format_data_string(this->after_message.decode(language)));
|
||||
}
|
||||
if (this->dispatch_message[0]) {
|
||||
lines.emplace_back(" dispatch_message: " + format_data_string(this->dispatch_message));
|
||||
if (!this->dispatch_message.empty()) {
|
||||
lines.emplace_back(" dispatch_message: " + format_data_string(this->dispatch_message.decode(language)));
|
||||
}
|
||||
for (size_t z = 0; z < 0x10; z++) {
|
||||
uint16_t card_id = this->reward_card_ids[z];
|
||||
@@ -1773,7 +1774,7 @@ string MapDefinition::str(const CardIndex* card_index) const {
|
||||
}
|
||||
}
|
||||
if (entry) {
|
||||
string name = entry->def.en_name;
|
||||
string name = entry->def.en_name.decode(language);
|
||||
lines.emplace_back(string_printf(" reward_cards[%02zu]: %04hX (%s)", z, card_id, name.c_str()));
|
||||
} else {
|
||||
lines.emplace_back(string_printf(" reward_cards[%02zu]: %04hX", z, card_id));
|
||||
@@ -1955,7 +1956,7 @@ MapDefinitionTrial::operator MapDefinition() const {
|
||||
ret.dialogue_sets[z][x].percent_chance = 0xFFFF;
|
||||
for (size_t w = 0; w < 4; w++) {
|
||||
ret.dialogue_sets[z][x].strings[w].clear(0xFF);
|
||||
ret.dialogue_sets[z][x].strings[w][0] = 0x00;
|
||||
ret.dialogue_sets[z][x].strings[w].set_byte(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1972,7 +1973,7 @@ MapDefinitionTrial::operator MapDefinition() const {
|
||||
// guess and fill in the field appropriately here.
|
||||
size_t num_npc_decks = 0;
|
||||
for (size_t z = 0; z < ret.npc_decks.size(); z++) {
|
||||
if (ret.npc_decks[z].name[0]) {
|
||||
if (!ret.npc_decks[z].name.empty()) {
|
||||
num_npc_decks++;
|
||||
}
|
||||
}
|
||||
@@ -2270,7 +2271,7 @@ CardIndex::CardIndex(
|
||||
|
||||
// Some cards intentionally have the same name, so we just leave them
|
||||
// unindexed (they can still be looked up by ID, of course)
|
||||
string name = entry->def.en_name;
|
||||
string name = entry->def.en_name.decode(1);
|
||||
this->card_definitions_by_name.emplace(name, entry);
|
||||
this->card_definitions_by_name_normalized.emplace(this->normalize_card_name(name), entry);
|
||||
|
||||
@@ -2510,7 +2511,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
throw runtime_error("unknown map file format");
|
||||
}
|
||||
|
||||
string name = format_data_string(vm->map->name);
|
||||
string name = format_data_string(vm->map->name.decode(vm->language));
|
||||
auto map_it = this->maps.find(vm->map->map_number);
|
||||
if (map_it == this->maps.end()) {
|
||||
map_it = this->maps.emplace(vm->map->map_number, new Map(vm)).first;
|
||||
@@ -2529,7 +2530,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
vm->map->is_quest() ? "quest" : "free",
|
||||
name.c_str());
|
||||
}
|
||||
this->maps_by_name.emplace(vm->map->name, map_it->second);
|
||||
this->maps_by_name.emplace(vm->map->name.decode(vm->language), map_it->second);
|
||||
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning("Failed to index Episode 3 map %s: %s",
|
||||
@@ -2579,16 +2580,16 @@ const string& MapIndex::get_compressed_list(size_t num_players, uint8_t language
|
||||
e.modification_tiles = vm->map->modification_tiles;
|
||||
|
||||
e.name_offset = strings_w.size();
|
||||
strings_w.write(vm->map->name.data(), vm->map->name.len());
|
||||
strings_w.write(vm->map->name.data, vm->map->name.used_bytes_8());
|
||||
strings_w.put_u8(0);
|
||||
e.location_name_offset = strings_w.size();
|
||||
strings_w.write(vm->map->location_name.data(), vm->map->location_name.len());
|
||||
strings_w.write(vm->map->location_name.data, vm->map->location_name.used_bytes_8());
|
||||
strings_w.put_u8(0);
|
||||
e.quest_name_offset = strings_w.size();
|
||||
strings_w.write(vm->map->quest_name.data(), vm->map->quest_name.len());
|
||||
strings_w.write(vm->map->quest_name.data, vm->map->quest_name.used_bytes_8());
|
||||
strings_w.put_u8(0);
|
||||
e.description_offset = strings_w.size();
|
||||
strings_w.write(vm->map->description.data(), vm->map->description.len());
|
||||
strings_w.write(vm->map->description.data, vm->map->description.used_bytes_8());
|
||||
strings_w.put_u8(0);
|
||||
e.map_category = vm->map->map_category;
|
||||
|
||||
|
||||
+28
-28
@@ -492,16 +492,16 @@ struct CardDefinition {
|
||||
// operators to perform basic computations on them. Operators are evaluated
|
||||
// left-to-right in the expression, and there are no operator precedence
|
||||
// rules; for example, the expression "4+4//2" results in 4, not 6.
|
||||
/* 02 */ ptext<char, 0x0F> expr;
|
||||
/* 02 */ pstring<TextEncoding::ASCII, 0x0F> expr;
|
||||
// when specifies in which phase the effect should activate.
|
||||
// TODO: There are many values that can be used here; document them.
|
||||
/* 11 */ uint8_t when;
|
||||
// arg1 generally specifies how long the effect activates for.
|
||||
/* 12 */ ptext<char, 4> arg1;
|
||||
/* 12 */ pstring<TextEncoding::ASCII, 4> arg1;
|
||||
// arg2 generally specifies a condition for when the effect activates.
|
||||
/* 16 */ ptext<char, 4> arg2;
|
||||
/* 16 */ pstring<TextEncoding::ASCII, 4> arg2;
|
||||
// arg3 generally specifies who is targeted by the effect.
|
||||
/* 1A */ ptext<char, 4> arg3;
|
||||
/* 1A */ pstring<TextEncoding::ASCII, 4> arg3;
|
||||
// apply_criterion can be used to apply an additional condition for when the
|
||||
// effect should activate. For example, it can be used to make the effect
|
||||
// only activate if the target is not a Story Character.
|
||||
@@ -764,9 +764,9 @@ struct CardDefinition {
|
||||
// enormous comment? That's what this array stores.
|
||||
/* 009C */ parray<be_uint16_t, 2> drop_rates;
|
||||
|
||||
/* 00A0 */ ptext<char, 0x14> en_name;
|
||||
/* 00B4 */ ptext<char, 0x0B> jp_short_name;
|
||||
/* 00BF */ ptext<char, 0x08> en_short_name;
|
||||
/* 00A0 */ pstring<TextEncoding::SJIS, 0x14> en_name;
|
||||
/* 00B4 */ pstring<TextEncoding::SJIS, 0x0B> jp_short_name;
|
||||
/* 00BF */ pstring<TextEncoding::SJIS, 0x08> en_short_name;
|
||||
// These effects modify the card's behavior in various situations. Only
|
||||
// effects for which effect_num is not zero are used.
|
||||
/* 00C7 */ parray<Effect, 3> effects;
|
||||
@@ -800,7 +800,7 @@ struct CardDefinitionsFooter {
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DeckDefinition {
|
||||
/* 00 */ ptext<char, 0x10> name;
|
||||
/* 00 */ pstring<TextEncoding::SJIS, 0x10> name;
|
||||
/* 10 */ be_uint32_t client_id; // 0-3
|
||||
// List of card IDs. The card count is the number of nonzero entries here
|
||||
// before a zero entry (or 50 if no entries are nonzero). The first card ID is
|
||||
@@ -823,7 +823,7 @@ struct PlayerConfig {
|
||||
// The game splits this internally into two structures. The first column of
|
||||
// offsets is relative to the start of the first structure; the second column
|
||||
// is relative to the start of the second structure.
|
||||
/* 0000:---- */ ptext<char, 12> rank_text; // From B7 command
|
||||
/* 0000:---- */ pstring<TextEncoding::SJIS, 12> rank_text; // From B7 command
|
||||
/* 000C:---- */ parray<uint8_t, 0x1C> unknown_a1;
|
||||
/* 0028:---- */ parray<be_uint16_t, 20> tech_menu_shortcut_entries;
|
||||
/* 0050:---- */ parray<be_uint32_t, 10> choice_search_config;
|
||||
@@ -885,7 +885,7 @@ struct PlayerConfig {
|
||||
/* 2124:1FD0 */ be_uint32_t online_clv_exp; // CLvOn = this / 100
|
||||
struct PlayerReference {
|
||||
/* 00 */ be_uint32_t guild_card_number;
|
||||
/* 04 */ ptext<char, 0x18> player_name;
|
||||
/* 04 */ pstring<TextEncoding::SJIS, 0x18> player_name;
|
||||
} __attribute__((packed));
|
||||
// This array is updated when a battle is started (via a 6xB4x05 command). The
|
||||
// client adds the opposing players' info to ths first two entries here if the
|
||||
@@ -1198,10 +1198,10 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 1D68 */ parray<uint8_t, 0x74> unknown_a5;
|
||||
/* 1DDC */ Rules default_rules;
|
||||
|
||||
/* 1DF0 */ ptext<char, 0x14> name;
|
||||
/* 1E04 */ ptext<char, 0x14> location_name;
|
||||
/* 1E18 */ ptext<char, 0x3C> quest_name; // == location_name if not a quest
|
||||
/* 1E54 */ ptext<char, 0x190> description;
|
||||
/* 1DF0 */ pstring<TextEncoding::SJIS, 0x14> name;
|
||||
/* 1E04 */ pstring<TextEncoding::SJIS, 0x14> location_name;
|
||||
/* 1E18 */ pstring<TextEncoding::SJIS, 0x3C> quest_name; // == location_name if not a quest
|
||||
/* 1E54 */ pstring<TextEncoding::SJIS, 0x190> description;
|
||||
|
||||
// These fields describe where the map cursor on the preview screen should
|
||||
// scroll to
|
||||
@@ -1209,7 +1209,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 1FE6 */ be_uint16_t map_y;
|
||||
|
||||
struct NPCDeck {
|
||||
/* 00 */ ptext<char, 0x18> name;
|
||||
/* 00 */ pstring<TextEncoding::SJIS, 0x18> name;
|
||||
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
|
||||
/* 58 */
|
||||
} __attribute__((packed));
|
||||
@@ -1223,7 +1223,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 0000 */ parray<be_uint16_t, 2> unknown_a1;
|
||||
/* 0004 */ uint8_t is_arkz;
|
||||
/* 0005 */ parray<uint8_t, 3> unknown_a2;
|
||||
/* 0008 */ ptext<char, 0x10> name;
|
||||
/* 0008 */ pstring<TextEncoding::SJIS, 0x10> name;
|
||||
// TODO: Figure out exactly how these are used and document here.
|
||||
/* 0018 */ parray<be_uint16_t, 0x7E> params;
|
||||
/* 0114 */
|
||||
@@ -1257,9 +1257,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// appears after the battle if it's not blank. dispatch_message appears right
|
||||
// before the player chooses a deck if it's not blank; usually it says
|
||||
// something like "You can only dispatch <character>".
|
||||
/* 2440 */ ptext<char, 0x190> before_message;
|
||||
/* 25D0 */ ptext<char, 0x190> after_message;
|
||||
/* 2760 */ ptext<char, 0x190> dispatch_message;
|
||||
/* 2440 */ pstring<TextEncoding::SJIS, 0x190> before_message;
|
||||
/* 25D0 */ pstring<TextEncoding::SJIS, 0x190> after_message;
|
||||
/* 2760 */ pstring<TextEncoding::SJIS, 0x190> dispatch_message;
|
||||
|
||||
struct DialogueSet {
|
||||
// Dialogue sets specify lines that COMs can say at certain points during
|
||||
@@ -1277,7 +1277,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 0002 */ be_uint16_t percent_chance; // 0-100, or FFFF if unused
|
||||
// If the dialogue set activates, the game randomly chooses one of these
|
||||
// strings, excluding any that are empty or begin with the character '^'.
|
||||
/* 0004 */ parray<ptext<char, 0x40>, 4> strings;
|
||||
/* 0004 */ parray<pstring<TextEncoding::SJIS, 0x40>, 4> strings;
|
||||
/* 0104 */
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -1375,7 +1375,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// text may differ.
|
||||
void assert_semantically_equivalent(const MapDefinition& other) const;
|
||||
|
||||
std::string str(const CardIndex* card_index = nullptr) const;
|
||||
std::string str(const CardIndex* card_index, uint8_t language) const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MapDefinitionTrial {
|
||||
@@ -1396,19 +1396,19 @@ struct MapDefinitionTrial {
|
||||
/* 1C68 */ parray<parray<uint8_t, 0x10>, 0x10> modification_tiles;
|
||||
/* 1D68 */ parray<uint8_t, 0x74> unknown_a5;
|
||||
/* 1DD4 */ RulesTrial default_rules;
|
||||
/* 1DE8 */ ptext<char, 0x14> name;
|
||||
/* 1DFC */ ptext<char, 0x14> location_name;
|
||||
/* 1E10 */ ptext<char, 0x3C> quest_name;
|
||||
/* 1E4C */ ptext<char, 0x190> description;
|
||||
/* 1DE8 */ pstring<TextEncoding::SJIS, 0x14> name;
|
||||
/* 1DFC */ pstring<TextEncoding::SJIS, 0x14> location_name;
|
||||
/* 1E10 */ pstring<TextEncoding::SJIS, 0x3C> quest_name;
|
||||
/* 1E4C */ pstring<TextEncoding::SJIS, 0x190> description;
|
||||
/* 1FDC */ be_uint16_t map_x;
|
||||
/* 1FDE */ be_uint16_t map_y;
|
||||
/* 1FE0 */ parray<MapDefinition::NPCDeck, 3> npc_decks;
|
||||
/* 20E8 */ parray<MapDefinition::AIParams, 3> npc_ai_params;
|
||||
/* 2424 */ parray<uint8_t, 8> unknown_a7;
|
||||
/* 242C */ parray<be_int32_t, 3> npc_ai_params_entry_index;
|
||||
/* 2438 */ ptext<char, 0x190> before_message;
|
||||
/* 25C8 */ ptext<char, 0x190> after_message;
|
||||
/* 2758 */ ptext<char, 0x190> dispatch_message;
|
||||
/* 2438 */ pstring<TextEncoding::SJIS, 0x190> before_message;
|
||||
/* 25C8 */ pstring<TextEncoding::SJIS, 0x190> after_message;
|
||||
/* 2758 */ pstring<TextEncoding::SJIS, 0x190> dispatch_message;
|
||||
/* 28E8 */ parray<parray<MapDefinition::DialogueSet, 8>, 3> dialogue_sets;
|
||||
/* 4148 */ parray<be_uint16_t, 0x10> reward_card_ids;
|
||||
/* 4168 */ be_int32_t win_level_override;
|
||||
|
||||
@@ -305,7 +305,7 @@ void DeckState::print(FILE* stream, std::shared_ptr<const CardIndex> card_index)
|
||||
}
|
||||
}
|
||||
if (ce) {
|
||||
string name = ce->def.en_name;
|
||||
string name = ce->def.en_name.decode(1);
|
||||
fprintf(stream, " (%02zu) index=%02hhX ref=@%04hX card_id=#%04hX \"%s\" %s\n",
|
||||
z, e.deck_index, this->card_refs[z], e.card_id, name.c_str(), name_for_card_state(e.state));
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@ struct NameEntry {
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DeckEntry {
|
||||
ptext<char, 0x10> name;
|
||||
pstring<TextEncoding::SJIS, 0x10> name;
|
||||
le_uint32_t team_id;
|
||||
parray<le_uint16_t, 31> card_ids;
|
||||
// If the following flag is not set to 3, then the God Whim assist effect can
|
||||
|
||||
+25
-28
@@ -228,8 +228,8 @@ void Server::send_6xB4x46() const {
|
||||
}
|
||||
|
||||
G_ServerVersionStrings_GC_Ep3_6xB4x46 cmd46;
|
||||
cmd46.version_signature = VERSION_SIGNATURE;
|
||||
cmd46.date_str1 = format_time(this->options.card_index->definitions_mtime() * 1000000);
|
||||
cmd46.version_signature.encode(VERSION_SIGNATURE, 1);
|
||||
cmd46.date_str1.encode(format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
|
||||
string date_str2 = string_printf(
|
||||
"Lobby:%08" PRIX32 " Random:%08" PRIX32 "+%08" PRIX32,
|
||||
l->lobby_id,
|
||||
@@ -238,7 +238,7 @@ void Server::send_6xB4x46() const {
|
||||
if (this->last_chosen_map) {
|
||||
date_str2 += string_printf(" Map:%08" PRIX32, this->last_chosen_map->map_number);
|
||||
}
|
||||
cmd46.date_str2 = date_str2;
|
||||
cmd46.date_str2.encode(date_str2, 1);
|
||||
this->send(cmd46);
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ string Server::prepare_6xB6x41_map_definition(
|
||||
return std::move(w.str());
|
||||
}
|
||||
|
||||
void Server::send_commands_for_joining_spectator(Channel& c, uint8_t language, bool is_trial) const {
|
||||
void Server::send_commands_for_joining_spectator(Channel& ch, bool is_trial) const {
|
||||
bool should_send_state = true;
|
||||
if (this->setup_phase == SetupPhase::REGISTRATION) {
|
||||
// If registration is still in progress, we only need to send the map data
|
||||
@@ -267,38 +267,38 @@ void Server::send_commands_for_joining_spectator(Channel& c, uint8_t language, b
|
||||
}
|
||||
|
||||
if (this->last_chosen_map) {
|
||||
string data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, language, is_trial);
|
||||
this->log().info("Sending %c version of map %08" PRIX32, char_for_language_code(language), this->last_chosen_map->map_number);
|
||||
c.send(0x6C, 0x00, data);
|
||||
string data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, ch.language, is_trial);
|
||||
this->log().info("Sending %c version of map %08" PRIX32, char_for_language_code(ch.language), this->last_chosen_map->map_number);
|
||||
ch.send(0x6C, 0x00, data);
|
||||
}
|
||||
|
||||
if (should_send_state) {
|
||||
c.send(0xC9, 0x00, this->prepare_6xB4x03());
|
||||
ch.send(0xC9, 0x00, this->prepare_6xB4x03());
|
||||
for (uint8_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto ps = this->player_states[client_id];
|
||||
if (ps) {
|
||||
c.send(0xC9, 0x00, ps->prepare_6xB4x02());
|
||||
c.send(0xC9, 0x00, ps->prepare_6xB4x04());
|
||||
ch.send(0xC9, 0x00, ps->prepare_6xB4x02());
|
||||
ch.send(0xC9, 0x00, ps->prepare_6xB4x04());
|
||||
}
|
||||
}
|
||||
{
|
||||
G_UpdateMap_GC_Ep3_6xB4x05 cmd_05;
|
||||
cmd_05.state = *this->map_and_rules;
|
||||
this->send(cmd_05);
|
||||
ch.send(0xC9, 0x00, cmd_05);
|
||||
}
|
||||
// TODO: Sega does something like this; do we have to do this too?
|
||||
// for (uint8_t client_id = 0; client_id < 4; client_id++) {
|
||||
// (send 6xB4x4E, 6xB4x4C, 6xB4x4D for each set card)
|
||||
// (send 6xB4x4F for client_id)
|
||||
// }
|
||||
c.send(0xC9, 0x00, this->prepare_6xB4x07_decks_update());
|
||||
ch.send(0xC9, 0x00, this->prepare_6xB4x07_decks_update());
|
||||
// TODO: Sega sends 6xB4x05 here again; why? Is that necessary? They also
|
||||
// send 6xB4x02 again for each player after that (but not 6xB4x04)
|
||||
c.send(0xC9, 0x00, this->prepare_6xB4x1C_names_update());
|
||||
c.send(0xC9, 0x00, this->prepare_6xB4x50_trap_tile_locations());
|
||||
ch.send(0xC9, 0x00, this->prepare_6xB4x1C_names_update());
|
||||
ch.send(0xC9, 0x00, this->prepare_6xB4x50_trap_tile_locations());
|
||||
{
|
||||
G_LoadCurrentEnvironment_GC_Ep3_6xB4x3B cmd_3B;
|
||||
c.send(0xC9, 0x00, &cmd_3B, sizeof(cmd_3B));
|
||||
ch.send(0xC9, 0x00, &cmd_3B, sizeof(cmd_3B));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,8 +310,7 @@ __attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(con
|
||||
va_start(va, fmt);
|
||||
std::string buf = string_vprintf(fmt, va);
|
||||
va_end(va);
|
||||
std::u16string decoded = decode_sjis(buf);
|
||||
send_text_message(l, decoded.c_str());
|
||||
send_text_message(l, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,8 +321,7 @@ __attribute__((format(printf, 2, 3))) void Server::send_info_message_printf(cons
|
||||
va_start(va, fmt);
|
||||
std::string buf = string_vprintf(fmt, va);
|
||||
va_end(va);
|
||||
std::u16string decoded = decode_sjis(buf);
|
||||
send_text_message(l, decoded.c_str());
|
||||
send_text_message(l, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2317,8 +2315,7 @@ void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const st
|
||||
throw runtime_error("lobby is deleted");
|
||||
}
|
||||
|
||||
const auto& list_data = this->options.map_index->get_compressed_list(
|
||||
l->count_clients(), sender_c->language);
|
||||
const auto& list_data = this->options.map_index->get_compressed_list(l->count_clients(), sender_c->language());
|
||||
|
||||
StringWriter w;
|
||||
uint32_t subcommand_size = (list_data.size() + sizeof(G_MapList_GC_Ep3_6xB6x40) + 3) & (~3);
|
||||
@@ -2347,15 +2344,15 @@ void Server::send_6xB6x41_to_all_clients() const {
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
if (map_commands_by_language.size() <= c->language) {
|
||||
map_commands_by_language.resize(c->language + 1);
|
||||
if (map_commands_by_language.size() <= c->language()) {
|
||||
map_commands_by_language.resize(c->language() + 1);
|
||||
}
|
||||
if (map_commands_by_language[c->language].empty()) {
|
||||
map_commands_by_language[c->language] = this->prepare_6xB6x41_map_definition(
|
||||
this->last_chosen_map, c->language, l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
if (map_commands_by_language[c->language()].empty()) {
|
||||
map_commands_by_language[c->language()] = this->prepare_6xB6x41_map_definition(
|
||||
this->last_chosen_map, c->language(), l->flags & Lobby::Flag::IS_EP3_TRIAL);
|
||||
}
|
||||
this->log().info("Sending %c version of map %08" PRIX32, char_for_language_code(c->language), this->last_chosen_map->map_number);
|
||||
send_command(c, 0x6C, 0x00, map_commands_by_language[c->language]);
|
||||
this->log().info("Sending %c version of map %08" PRIX32, char_for_language_code(c->language()), this->last_chosen_map->map_number);
|
||||
send_command(c, 0x6C, 0x00, map_commands_by_language[c->language()]);
|
||||
};
|
||||
for (const auto& c : l->clients) {
|
||||
send_to_client(c);
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
this->send(&cmd, cmd.header.size * 4);
|
||||
}
|
||||
void send(const void* data, size_t size) const;
|
||||
void send_commands_for_joining_spectator(Channel& ch, uint8_t language, bool is_trial) const;
|
||||
void send_commands_for_joining_spectator(Channel& ch, bool is_trial) const;
|
||||
|
||||
void force_battle_result(uint8_t surrendered_client_id, bool set_winner);
|
||||
void force_destroy_field_character(uint8_t client_id, size_t set_index);
|
||||
|
||||
@@ -16,7 +16,7 @@ Tournament::PlayerEntry::PlayerEntry(uint32_t serial_number, const string& playe
|
||||
Tournament::PlayerEntry::PlayerEntry(shared_ptr<Client> c)
|
||||
: serial_number(c->license->serial_number),
|
||||
client(c),
|
||||
player_name(encode_sjis(c->game_data.player()->disp.name)) {}
|
||||
player_name(c->game_data.player()->disp.name.decode(c->language())) {}
|
||||
|
||||
Tournament::PlayerEntry::PlayerEntry(
|
||||
shared_ptr<const COMDeckDefinition> com_deck)
|
||||
@@ -743,7 +743,7 @@ void Tournament::print_bracket(FILE* stream) const {
|
||||
fprintf(stream, "Tournament \"%s\"\n", this->name.c_str());
|
||||
auto en_vm = this->map->version(1);
|
||||
if (en_vm) {
|
||||
string map_name = en_vm->map->name;
|
||||
string map_name = en_vm->map->name.decode(en_vm->language);
|
||||
fprintf(stream, " Map: %08" PRIX32 " (%s)\n", this->map->map_number, map_name.c_str());
|
||||
} else {
|
||||
fprintf(stream, " Map: %08" PRIX32 "\n", this->map->map_number);
|
||||
|
||||
Reference in New Issue
Block a user