OSDN Git Service

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