OSDN Git Service

Merge pull request #3814 from Slimebreath6078/feature/Add_Laffey_II
[hengbandforosx/hengbandosx.git] / src / io-dump / character-dump.cpp
1 #include "io-dump/character-dump.h"
2 #include "artifact/fixed-art-types.h"
3 #include "avatar/avatar.h"
4 #include "cmd-building/cmd-building.h"
5 #include "dungeon/quest.h"
6 #include "flavor/flavor-describer.h"
7 #include "floor/floor-town.h"
8 #include "game-option/birth-options.h"
9 #include "game-option/game-play-options.h"
10 #include "inventory/inventory-slot-types.h"
11 #include "io-dump/player-status-dump.h"
12 #include "io-dump/special-class-dump.h"
13 #include "io/mutations-dump.h"
14 #include "io/write-diary.h"
15 #include "knowledge/knowledge-quests.h"
16 #include "main/angband-headers.h"
17 #include "market/arena-info-table.h"
18 #include "monster-race/monster-race.h"
19 #include "monster/monster-describer.h"
20 #include "monster/monster-description-types.h"
21 #include "monster/monster-info.h"
22 #include "monster/monster-status.h"
23 #include "monster/smart-learn-types.h"
24 #include "object/object-info.h"
25 #include "pet/pet-util.h"
26 #include "player-info/alignment.h"
27 #include "player/player-status-flags.h"
28 #include "player/player-status-table.h"
29 #include "player/race-info-table.h"
30 #include "realm/realm-names-table.h"
31 #include "store/store-util.h"
32 #include "store/store.h"
33 #include "system/angband-version.h"
34 #include "system/building-type-definition.h"
35 #include "system/dungeon-info.h"
36 #include "system/floor-type-definition.h"
37 #include "system/item-entity.h"
38 #include "system/monster-entity.h"
39 #include "system/monster-race-info.h"
40 #include "system/player-type-definition.h"
41 #include "term/gameterm.h"
42 #include "term/z-form.h"
43 #include "util/enum-converter.h"
44 #include "util/int-char-converter.h"
45 #include "util/sort.h"
46 #include "util/string-processor.h"
47 #include "view/display-messages.h"
48 #include "world/world.h"
49 #include <numeric>
50 #include <string>
51
52 /*!
53  * @brief プレイヤーのペット情報をファイルにダンプする
54  * @param player_ptr プレイヤーへの参照ポインタ
55  * @param fff ファイルポインタ
56  */
57 static void dump_aux_pet(PlayerType *player_ptr, FILE *fff)
58 {
59     bool pet = false;
60     bool pet_settings = false;
61     for (int i = player_ptr->current_floor_ptr->m_max - 1; i >= 1; i--) {
62         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
63
64         if (!m_ptr->is_valid()) {
65             continue;
66         }
67         if (!m_ptr->is_pet()) {
68             continue;
69         }
70         pet_settings = true;
71         if (!m_ptr->is_named() && (player_ptr->riding != i)) {
72             continue;
73         }
74         if (!pet) {
75             fprintf(fff, _("\n\n  [主なペット]\n\n", "\n\n  [Leading Pets]\n\n"));
76             pet = true;
77         }
78
79         const auto pet_name = monster_desc(player_ptr, m_ptr, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE);
80         fprintf(fff, "%s\n", pet_name.data());
81     }
82
83     if (!pet_settings) {
84         return;
85     }
86
87     fprintf(fff, _("\n\n  [ペットへの命令]\n", "\n\n  [Command for Pets]\n"));
88
89     fprintf(fff, _("\n ドアを開ける:                       %s", "\n Pets open doors:                    %s"),
90         (player_ptr->pet_extra_flags & PF_OPEN_DOORS) ? "ON" : "OFF");
91
92     fprintf(fff, _("\n アイテムを拾う:                     %s", "\n Pets pick up items:                 %s"),
93         (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) ? "ON" : "OFF");
94
95     fprintf(fff, _("\n テレポート系魔法を使う:             %s", "\n Allow teleport:                     %s"),
96         (player_ptr->pet_extra_flags & PF_TELEPORT) ? "ON" : "OFF");
97
98     fprintf(fff, _("\n 攻撃魔法を使う:                     %s", "\n Allow cast attack spell:            %s"),
99         (player_ptr->pet_extra_flags & PF_ATTACK_SPELL) ? "ON" : "OFF");
100
101     fprintf(fff, _("\n 召喚魔法を使う:                     %s", "\n Allow cast summon spell:            %s"),
102         (player_ptr->pet_extra_flags & PF_SUMMON_SPELL) ? "ON" : "OFF");
103
104     fprintf(fff, _("\n プレイヤーを巻き込む範囲魔法を使う: %s", "\n Allow involve player in area spell: %s"),
105         (player_ptr->pet_extra_flags & PF_BALL_SPELL) ? "ON" : "OFF");
106
107     fputc('\n', fff);
108 }
109
110 /*!
111  * @brief クエスト情報をファイルにダンプする
112  * @param player_ptr プレイヤーへの参照ポインタ
113  * @param fff ファイルポインタ
114  */
115 static void dump_aux_quest(PlayerType *player_ptr, FILE *fff)
116 {
117     fprintf(fff, _("\n\n  [クエスト情報]\n", "\n\n  [Quest Information]\n"));
118
119     const auto &quest_list = QuestList::get_instance();
120     std::vector<QuestId> quest_numbers;
121     for (const auto &[q_idx, quest] : quest_list) {
122         quest_numbers.push_back(q_idx);
123     }
124     int dummy;
125     ang_sort(player_ptr, quest_numbers.data(), &dummy, quest_numbers.size(), ang_sort_comp_quest_num, ang_sort_swap_quest_num);
126
127     fputc('\n', fff);
128     do_cmd_knowledge_quests_completed(player_ptr, fff, quest_numbers);
129     fputc('\n', fff);
130     do_cmd_knowledge_quests_failed(player_ptr, fff, quest_numbers);
131     fputc('\n', fff);
132 }
133
134 /*!
135  * @brief 死の直前メッセージ並びに遺言をファイルにダンプする
136  * @param player_ptr プレイヤーへの参照ポインタ
137  * @param fff ファイルポインタ
138  */
139 static void dump_aux_last_message(PlayerType *player_ptr, FILE *fff)
140 {
141     if (!player_ptr->is_dead) {
142         return;
143     }
144
145     if (!w_ptr->total_winner) {
146         fprintf(fff, _("\n  [死ぬ直前のメッセージ]\n\n", "\n  [Last Messages]\n\n"));
147         for (int i = std::min(message_num(), 30); i >= 0; i--) {
148             fprintf(fff, "> %s\n", message_str(i)->data());
149         }
150
151         fputc('\n', fff);
152         return;
153     }
154
155     if (player_ptr->last_message.empty()) {
156         return;
157     }
158
159     fprintf(fff, _("\n  [*勝利*メッセージ]\n\n", "\n  [*Winning* Message]\n\n"));
160     fprintf(fff, "  %s\n", player_ptr->last_message.data());
161     fputc('\n', fff);
162 }
163
164 /*!
165  * @brief 帰還場所情報をファイルにダンプする
166  * @param fff ファイルポインタ
167  */
168 static void dump_aux_recall(FILE *fff)
169 {
170     fprintf(fff, _("\n  [帰還場所]\n\n", "\n  [Recall Depth]\n\n"));
171     for (const auto &d_ref : dungeons_info) {
172         bool seiha = false;
173
174         if (d_ref.idx == 0 || !d_ref.maxdepth) {
175             continue;
176         }
177         if (!max_dlv[d_ref.idx]) {
178             continue;
179         }
180         if (MonsterRace(d_ref.final_guardian).is_valid()) {
181             if (!monraces_info[d_ref.final_guardian].max_num) {
182                 seiha = true;
183             }
184         } else if (max_dlv[d_ref.idx] == d_ref.maxdepth) {
185             seiha = true;
186         }
187
188         fprintf(fff, _("   %c%-12s: %3d 階\n", "   %c%-16s: level %3d\n"), seiha ? '!' : ' ', d_ref.name.data(), (int)max_dlv[d_ref.idx]);
189     }
190 }
191
192 /*!
193  * @brief オプション情報をファイルにダンプする
194  * @param fff ファイルポインタ
195  */
196 static void dump_aux_options(FILE *fff)
197 {
198     fprintf(fff, _("\n  [オプション設定]\n", "\n  [Option Settings]\n"));
199     if (preserve_mode) {
200         fprintf(fff, _("\n 保存モード:         ON", "\n Preserve Mode:      ON"));
201     } else {
202         fprintf(fff, _("\n 保存モード:         OFF", "\n Preserve Mode:      OFF"));
203     }
204
205     if (ironman_small_levels) {
206         fprintf(fff, _("\n 小さいダンジョン:   ALWAYS", "\n Small Levels:       ALWAYS"));
207     } else if (always_small_levels) {
208         fprintf(fff, _("\n 小さいダンジョン:   ON", "\n Small Levels:       ON"));
209     } else if (small_levels) {
210         fprintf(fff, _("\n 小さいダンジョン:   ENABLED", "\n Small Levels:       ENABLED"));
211     } else {
212         fprintf(fff, _("\n 小さいダンジョン:   OFF", "\n Small Levels:       OFF"));
213     }
214
215     if (vanilla_town) {
216         fprintf(fff, _("\n 元祖の町のみ:       ON", "\n Vanilla Town:       ON"));
217     } else if (lite_town) {
218         fprintf(fff, _("\n 小規模な町:         ON", "\n Lite Town:          ON"));
219     }
220
221     if (ironman_shops) {
222         fprintf(fff, _("\n 店なし:             ON", "\n No Shops:           ON"));
223     }
224
225     if (ironman_downward) {
226         fprintf(fff, _("\n 階段を上がれない:   ON", "\n Diving Only:        ON"));
227     }
228
229     if (ironman_rooms) {
230         fprintf(fff, _("\n 普通でない部屋:     ON", "\n Unusual Rooms:      ON"));
231     }
232
233     if (ironman_nightmare) {
234         fprintf(fff, _("\n 悪夢モード:         ON", "\n Nightmare Mode:     ON"));
235     }
236
237     if (ironman_empty_levels) {
238         fprintf(fff, _("\n アリーナ:           ALWAYS", "\n Arena Levels:       ALWAYS"));
239     } else if (empty_levels) {
240         fprintf(fff, _("\n アリーナ:           ENABLED", "\n Arena Levels:       ENABLED"));
241     } else {
242         fprintf(fff, _("\n アリーナ:           OFF", "\n Arena Levels:       OFF"));
243     }
244
245     fputc('\n', fff);
246
247     if (w_ptr->noscore) {
248         fprintf(fff, _("\n 何か不正なことをしてしまっています。\n", "\n You have done something illegal.\n"));
249     }
250
251     fputc('\n', fff);
252 }
253
254 /*!
255  * @brief 闘技場の情報をファイルにダンプする
256  * @param player_ptr プレイヤーへの参照ポインタ
257  * @param fff ファイルポインタ
258  */
259 static void dump_aux_arena(PlayerType *player_ptr, FILE *fff)
260 {
261     if (lite_town || vanilla_town) {
262         return;
263     }
264
265     const auto arena_number = player_ptr->arena_number;
266     if (arena_number < 0) {
267         if (arena_number <= ARENA_DEFEATED_OLD_VER) {
268             fprintf(fff, _("\n 闘技場: 敗北\n", "\n Arena: Defeated\n"));
269         } else {
270             constexpr auto mes = _("\n 闘技場: %d回戦で%sの前に敗北\n", "\n Arena: Defeated by %s in the %d%s fight\n");
271             const auto &arena = arena_info[-1 - arena_number];
272             const auto &arena_monrace = monraces_info[arena.r_idx];
273 #ifdef JP
274             fprintf(fff, mes, -arena_number, arena_monrace.name.data());
275 #else
276             fprintf(fff, mes, arena_monrace.name.data(), -arena_number, get_ordinal_number_suffix(-arena_number).data());
277 #endif
278         }
279
280         fprintf(fff, "\n");
281         return;
282     }
283
284     if (arena_number > MAX_ARENA_MONS + 2) {
285         fprintf(fff, _("\n 闘技場: 真のチャンピオン\n", "\n Arena: True Champion\n"));
286         fprintf(fff, "\n");
287         return;
288     }
289
290     if (arena_number > MAX_ARENA_MONS - 1) {
291         fprintf(fff, _("\n 闘技場: チャンピオン\n", "\n Arena: Champion\n"));
292         fprintf(fff, "\n");
293         return;
294     }
295
296     const auto victory_count = arena_number > MAX_ARENA_MONS ? MAX_ARENA_MONS : arena_number;
297 #ifdef JP
298     fprintf(fff, "\n 闘技場: %2d勝\n", victory_count);
299 #else
300     fprintf(fff, "\n Arena: %2d %s\n", victory_count, (arena_number > 1) ? "Victories" : "Victory");
301 #endif
302     fprintf(fff, "\n");
303 }
304
305 /*!
306  * @brief 撃破モンスターの情報をファイルにダンプする
307  * @param fff ファイルポインタ
308  */
309 static void dump_aux_monsters(PlayerType *player_ptr, FILE *fff)
310 {
311     fprintf(fff, _("\n  [倒したモンスター]\n\n", "\n  [Defeated Monsters]\n\n"));
312
313     /* Allocate the "who" array */
314     uint16_t why = 2;
315     std::vector<MonsterRaceId> who;
316
317     /* Count monster kills */
318     auto norm_total = 0;
319     for (const auto &[monrace_id, monrace] : monraces_info) {
320         /* Ignore unused index */
321         if (!MonsterRace(monrace_id).is_valid()) {
322             continue;
323         }
324
325         if (monrace.kind_flags.has(MonsterKindType::UNIQUE)) {
326             auto dead = (monrace.max_num == 0);
327             if (dead) {
328                 norm_total++;
329
330                 /* Add a unique monster to the list */
331                 who.push_back(monrace.idx);
332             }
333
334             continue;
335         }
336
337         if (monrace.r_pkills > 0) {
338             norm_total += monrace.r_pkills;
339         }
340     }
341
342     /* No monsters is defeated */
343     if (norm_total < 1) {
344         fprintf(fff, _("まだ敵を倒していません。\n", "You have defeated no enemies yet.\n"));
345         return;
346     }
347
348     auto uniq_total = static_cast<int>(who.size());
349     /* Defeated more than one normal monsters */
350     if (uniq_total == 0) {
351 #ifdef JP
352         fprintf(fff, "%d体の敵を倒しています。\n", norm_total);
353 #else
354         fprintf(fff, "You have defeated %d %s.\n", norm_total, norm_total == 1 ? "enemy" : "enemies");
355 #endif
356         return;
357     }
358
359     /* Defeated more than one unique monsters */
360 #ifdef JP
361     fprintf(fff, "%d体のユニーク・モンスターを含む、合計%d体の敵を倒しています。\n", uniq_total, norm_total);
362 #else
363     fprintf(fff, "You have defeated %d %s including %d unique monster%s in total.\n", norm_total, norm_total == 1 ? "enemy" : "enemies", uniq_total,
364         (uniq_total == 1 ? "" : "s"));
365 #endif
366
367     /* Sort the array by dungeon depth of monsters */
368     ang_sort(player_ptr, who.data(), &why, uniq_total, ang_sort_comp_hook, ang_sort_swap_hook);
369     fprintf(fff, _("\n《上位%d体のユニーク・モンスター》\n", "\n< Unique monsters top %d >\n"), std::min(uniq_total, 10));
370
371     char buf[80];
372     for (auto it = who.rbegin(); it != who.rend() && std::distance(who.rbegin(), it) < 10; it++) {
373         auto *r_ptr = &monraces_info[*it];
374         if (r_ptr->defeat_level && r_ptr->defeat_time) {
375             strnfmt(buf, sizeof(buf), _(" - レベル%2d - %d:%02d:%02d", " - level %2d - %d:%02d:%02d"), r_ptr->defeat_level, r_ptr->defeat_time / (60 * 60),
376                 (r_ptr->defeat_time / 60) % 60, r_ptr->defeat_time % 60);
377         } else {
378             buf[0] = '\0';
379         }
380
381         auto name = str_separate(r_ptr->name, 40);
382         fprintf(fff, _("  %-40s (レベル%3d)%s\n", "  %-40s (level %3d)%s\n"), name.front().data(), (int)r_ptr->level, buf);
383         for (auto i = 1U; i < name.size(); ++i) {
384             fprintf(fff, "  %s\n", name[i].data());
385         }
386     }
387 }
388
389 /*!
390  * @brief 元種族情報をファイルにダンプする
391  * @param player_ptr プレイヤーへの参照ポインタ
392  * @param fff ファイルポインタ
393  */
394 static void dump_aux_race_history(PlayerType *player_ptr, FILE *fff)
395 {
396     if (!player_ptr->old_race1 && !player_ptr->old_race2) {
397         return;
398     }
399
400     fprintf(fff, _("\n\n あなたは%sとして生まれた。", "\n\n You were born as %s."), race_info[enum2i(player_ptr->start_race)].title);
401     for (int i = 0; i < MAX_RACES; i++) {
402         if (enum2i(player_ptr->start_race) == i) {
403             continue;
404         }
405         if (i < 32) {
406             if (!(player_ptr->old_race1 & 1UL << i)) {
407                 continue;
408             }
409         } else {
410             if (!(player_ptr->old_race2 & 1UL << (i - 32))) {
411                 continue;
412             }
413         }
414
415         fprintf(fff, _("\n あなたはかつて%sだった。", "\n You were a %s before."), race_info[i].title);
416     }
417
418     fputc('\n', fff);
419 }
420
421 /*!
422  * @brief 元魔法領域情報をファイルにダンプする
423  * @param player_ptr プレイヤーへの参照ポインタ
424  * @param fff ファイルポインタ
425  */
426 static void dump_aux_realm_history(PlayerType *player_ptr, FILE *fff)
427 {
428     if (player_ptr->old_realm == 0) {
429         return;
430     }
431
432     fputc('\n', fff);
433     for (int i = 0; i < MAX_MAGIC; i++) {
434         if (!(player_ptr->old_realm & 1UL << i)) {
435             continue;
436         }
437         fprintf(fff, _("\n あなたはかつて%s魔法を使えた。", "\n You were able to use %s magic before."), realm_names[i + 1]);
438     }
439
440     fputc('\n', fff);
441 }
442
443 /*!
444  * @brief 徳の情報をファイルにダンプする
445  * @param player_ptr プレイヤーへの参照ポインタ
446  * @param fff ファイルポインタ
447  */
448 static void dump_aux_virtues(PlayerType *player_ptr, FILE *fff)
449 {
450     fprintf(fff, _("\n\n  [自分に関する情報]\n\n", "\n\n  [HP-rate & Max stat & Virtues]\n\n"));
451
452     int percent = (int)(((long)player_ptr->player_hp[PY_MAX_LEVEL - 1] * 200L) / (2 * player_ptr->hitdie + ((PY_MAX_LEVEL - 1 + 3) * (player_ptr->hitdie + 1))));
453
454 #ifdef JP
455     if (player_ptr->knowledge & KNOW_HPRATE) {
456         fprintf(fff, "現在の体力ランク : %d/100\n\n", percent);
457     } else {
458         fprintf(fff, "現在の体力ランク : ???\n\n");
459     }
460     fprintf(fff, "能力の最大値\n");
461 #else
462     if (player_ptr->knowledge & KNOW_HPRATE) {
463         fprintf(fff, "Your current Life Rating is %d/100.\n\n", percent);
464     } else {
465         fprintf(fff, "Your current Life Rating is ???.\n\n");
466     }
467     fprintf(fff, "Limits of maximum stats\n");
468 #endif
469     for (int v_nr = 0; v_nr < A_MAX; v_nr++) {
470         if ((player_ptr->knowledge & KNOW_STAT) || player_ptr->stat_max[v_nr] == player_ptr->stat_max_max[v_nr]) {
471             fprintf(fff, "%s 18/%d\n", stat_names[v_nr], player_ptr->stat_max_max[v_nr] - 18);
472         } else {
473             fprintf(fff, "%s ???\n", stat_names[v_nr]);
474         }
475     }
476
477     std::string alg = PlayerAlignment(player_ptr).get_alignment_description();
478     fprintf(fff, _("\n属性 : %s\n", "\nYour alignment : %s\n"), alg.data());
479     fprintf(fff, "\n");
480     dump_virtues(player_ptr, fff);
481 }
482
483 /*!
484  * @brief 突然変異の情報をファイルにダンプする
485  * @param player_ptr プレイヤーへの参照ポインタ
486  * @param fff ファイルポインタ
487  */
488 static void dump_aux_mutations(PlayerType *player_ptr, FILE *fff)
489 {
490     if (player_ptr->muta.any()) {
491         fprintf(fff, _("\n\n  [突然変異]\n\n", "\n\n  [Mutations]\n\n"));
492         dump_mutations(player_ptr, fff);
493     }
494 }
495
496 /*!
497  * @brief 所持品の情報をファイルにダンプする
498  * @param player_ptr プレイヤーへの参照ポインタ
499  * @param fff ファイルポインタ
500  */
501 static void dump_aux_equipment_inventory(PlayerType *player_ptr, FILE *fff)
502 {
503     if (player_ptr->equip_cnt) {
504         fprintf(fff, _("  [キャラクタの装備]\n\n", "  [Character Equipment]\n\n"));
505         for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
506             auto item_name = describe_flavor(player_ptr, &player_ptr->inventory_list[i], 0);
507             auto is_two_handed = ((i == INVEN_MAIN_HAND) && can_attack_with_sub_hand(player_ptr));
508             is_two_handed |= ((i == INVEN_SUB_HAND) && can_attack_with_main_hand(player_ptr));
509             if (is_two_handed && has_two_handed_weapons(player_ptr)) {
510                 item_name = _("(武器を両手持ち)", "(wielding with two-hands)");
511             }
512
513             fprintf(fff, "%c) %s\n", index_to_label(i), item_name.data());
514         }
515
516         fprintf(fff, "\n\n");
517     }
518
519     fprintf(fff, _("  [キャラクタの持ち物]\n\n", "  [Character Inventory]\n\n"));
520
521     for (int i = 0; i < INVEN_PACK; i++) {
522         if (!player_ptr->inventory_list[i].is_valid()) {
523             break;
524         }
525
526         const auto item_name = describe_flavor(player_ptr, &player_ptr->inventory_list[i], 0);
527         fprintf(fff, "%c) %s\n", index_to_label(i), item_name.data());
528     }
529
530     fprintf(fff, "\n\n");
531 }
532
533 /*!
534  * @brief 我が家と博物館のオブジェクト情報をファイルにダンプする
535  * @param fff ファイルポインタ
536  */
537 static void dump_aux_home_museum(PlayerType *player_ptr, FILE *fff)
538 {
539     const auto *store_ptr = &towns_info[1].stores[StoreSaleType::HOME];
540     if (store_ptr->stock_num) {
541         fprintf(fff, _("  [我が家のアイテム]\n", "  [Home Inventory]\n"));
542         auto page = 1;
543         for (auto i = 0; i < store_ptr->stock_num; i++) {
544             if ((i % 12) == 0) {
545                 fprintf(fff, _("\n ( %d ページ )\n", "\n ( page %d )\n"), page++);
546             }
547
548             const auto item_name = describe_flavor(player_ptr, &store_ptr->stock[i], 0);
549             fprintf(fff, "%c) %s\n", I2A(i % 12), item_name.data());
550         }
551
552         fprintf(fff, "\n\n");
553     }
554
555     store_ptr = &towns_info[1].stores[StoreSaleType::MUSEUM];
556
557     if (store_ptr->stock_num == 0) {
558         return;
559     }
560
561     fprintf(fff, _("  [博物館のアイテム]\n", "  [Museum]\n"));
562
563     auto page = 1;
564     for (auto i = 0; i < store_ptr->stock_num; i++) {
565         if ((i % 12) == 0) {
566             fprintf(fff, _("\n ( %d ページ )\n", "\n ( page %d )\n"), page++);
567         }
568
569         const auto item_name = describe_flavor(player_ptr, &store_ptr->stock[i], 0);
570         fprintf(fff, "%c) %s\n", I2A(i % 12), item_name.data());
571     }
572
573     fprintf(fff, "\n\n");
574 }
575
576 /*!
577  * @brief チェックサム情報を出力 / Get check sum in string form
578  * @return チェックサム情報の文字列
579  */
580 static std::string get_check_sum(void)
581 {
582     static constexpr auto headers = {
583         &artifacts_header,
584         &baseitems_header,
585         &class_magics_header,
586         &class_skills_header,
587         &dungeons_header,
588         &egos_header,
589         &monraces_header,
590         &terrains_header,
591         &vaults_header,
592     };
593
594     util::SHA256 sha256;
595     for (const auto *header : headers) {
596         sha256.update(header->digest.data(), header->digest.size());
597     }
598
599     return util::to_string(sha256.digest());
600 }
601
602 /*!
603  * @brief ダンプ出力のメインルーチン
604  * Output the character dump to a file
605  * @param player_ptr プレイヤーへの参照ポインタ
606  * @param fff ファイルポインタ
607  * @return エラーコード
608  */
609 void make_character_dump(PlayerType *player_ptr, FILE *fff)
610 {
611     TermCenteredOffsetSetter tos(MAIN_TERM_MIN_COLS, std::nullopt);
612
613     fprintf(fff, _("  [%s キャラクタ情報]\n\n", "  [%s Character Dump]\n\n"), get_version().data());
614
615     dump_aux_player_status(player_ptr, fff);
616     dump_aux_last_message(player_ptr, fff);
617     dump_aux_options(fff);
618     dump_aux_recall(fff);
619     dump_aux_quest(player_ptr, fff);
620     dump_aux_arena(player_ptr, fff);
621     dump_aux_monsters(player_ptr, fff);
622     dump_aux_virtues(player_ptr, fff);
623     dump_aux_race_history(player_ptr, fff);
624     dump_aux_realm_history(player_ptr, fff);
625     dump_aux_class_special(player_ptr, fff);
626     dump_aux_mutations(player_ptr, fff);
627     dump_aux_pet(player_ptr, fff);
628     fputs("\n\n", fff);
629     dump_aux_equipment_inventory(player_ptr, fff);
630     dump_aux_home_museum(player_ptr, fff);
631
632     // ダンプの幅をはみ出さないように48文字目以降を切り捨てる
633     const auto checksum = get_check_sum().erase(48);
634     fprintf(fff, _("  [チェックサム: \"%s\"]\n\n", "  [Check Sum: \"%s\"]\n\n"), checksum.data());
635 }