OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / locale / japanese.cpp
1 /*!
2  *  @file japanese.cpp
3  *  @brief 日本語処理関数
4  *  @date 2014/07/07
5  */
6
7 #include "locale/japanese.h"
8 #include "locale/utf-8.h"
9 #include "util/enum-converter.h"
10 #include "util/string-processor.h"
11 #include "view/display-messages.h"
12 #include <set>
13 #include <sstream>
14
15 #ifdef JP
16
17 struct convert_key;
18
19 struct convert_key {
20     concptr key1;
21     concptr key2;
22 };
23
24 static const convert_key s2j_table[] = { { "mb", "nb" }, { "mp", "np" }, { "mv", "nv" }, { "mm", "nm" }, { "x", "ks" },
25     /* sindar:シンダール  parantir:パランティア  feanor:フェアノール */
26     { "ar$", "a-ru$" }, { "ir$", "ia$" }, { "or$", "o-ru$" }, { "ra", "ラ" }, { "ri", "リ" }, { "ru", "ル" }, { "re", "レ" }, { "ro", "ロ" }, { "ir", "ia" },
27     { "ur", "ua" }, { "er", "ea" }, { "ar", "aル" }, { "sha", "シャ" }, { "shi", "シ" }, { "shu", "シュ" }, { "she", "シェ" }, { "sho", "ショ" },
28     { "tha", "サ" }, { "thi", "シ" }, { "thu", "ス" }, { "the", "セ" }, { "tho", "ソ" }, { "cha", "ハ" }, { "chi", "ヒ" }, { "chu", "フ" }, { "che", "ヘ" },
29     { "cho", "ホ" }, { "dha", "ザ" }, { "dhi", "ジ" }, { "dhu", "ズ" }, { "dhe", "ゼ" }, { "dho", "ゾ" }, { "ba", "バ" }, { "bi", "ビ" }, { "bu", "ブ" },
30     { "be", "ベ" }, { "bo", "ボ" }, { "ca", "カ" }, { "ci", "キ" }, { "cu", "ク" }, { "ce", "ケ" }, { "co", "コ" }, { "da", "ダ" }, { "di", "ディ" },
31     { "du", "ドゥ" }, { "de", "デ" }, { "do", "ド" }, { "fa", "ファ" }, { "fi", "フィ" }, { "fu", "フ" }, { "fe", "フェ" }, { "fo", "フォ" }, { "ga", "ガ" },
32     { "gi", "ギ" }, { "gu", "グ" }, { "ge", "ゲ" }, { "go", "ゴ" }, { "ha", "ハ" }, { "hi", "ヒ" }, { "hu", "フ" }, { "he", "ヘ" }, { "ho", "ホ" },
33     { "ja", "ジャ" }, { "ji", "ジ" }, { "ju", "ジュ" }, { "je", "ジェ" }, { "jo", "ジョ" }, { "ka", "カ" }, { "ki", "キ" }, { "ku", "ク" }, { "ke", "ケ" },
34     { "ko", "コ" }, { "la", "ラ" }, { "li", "リ" }, { "lu", "ル" }, { "le", "レ" }, { "lo", "ロ" }, { "ma", "マ" }, { "mi", "ミ" }, { "mu", "ム" },
35     { "me", "メ" }, { "mo", "モ" }, { "na", "ナ" }, { "ni", "ニ" }, { "nu", "ヌ" }, { "ne", "ネ" }, { "no", "ノ" }, { "pa", "パ" }, { "pi", "ピ" },
36     { "pu", "プ" }, { "pe", "ペ" }, { "po", "ポ" }, { "qu", "ク" }, { "sa", "サ" }, { "si", "シ" }, { "su", "ス" }, { "se", "セ" }, { "so", "ソ" },
37     { "ta", "タ" }, { "ti", "ティ" }, { "tu", "トゥ" }, { "te", "テ" }, { "to", "ト" }, { "va", "ヴァ" }, { "vi", "ヴィ" }, { "vu", "ヴ" }, { "ve", "ヴェ" },
38     { "vo", "ヴォ" }, { "wa", "ワ" }, { "wi", "ウィ" }, { "wu", "ウ" }, { "we", "ウェ" }, { "wo", "ウォ" }, { "ya", "ヤ" }, { "yu", "ユ" }, { "yo", "ヨ" },
39     { "za", "ザ" }, { "zi", "ジ" }, { "zu", "ズ" }, { "ze", "ゼ" }, { "zo", "ゾ" }, { "dh", "ズ" }, { "ch", "フ" }, { "th", "ス" }, { "b", "ブ" },
40     { "c", "ク" }, { "d", "ド" }, { "f", "フ" }, { "g", "グ" }, { "h", "フ" }, { "j", "ジュ" }, { "k", "ク" }, { "l", "ル" }, { "m", "ム" }, { "n", "ン" },
41     { "p", "プ" }, { "q", "ク" }, { "r", "ル" }, { "s", "ス" }, { "t", "ト" }, { "v", "ヴ" }, { "w", "ウ" }, { "y", "イ" }, { "a", "ア" }, { "i", "イ" },
42     { "u", "ウ" }, { "e", "エ" }, { "o", "オ" }, { "-", "ー" }, { nullptr, nullptr } };
43
44 /*!
45  * @brief シンダリンを日本語の読みに変換する
46  * @param sindarin 変換前のシンダリン文字列
47  * @return std::string 変換後のシンダリン文字列
48  * @details
49  */
50 std::string sindarin_to_kana(std::string_view sindarin)
51 {
52     std::string kana;
53
54     for (const auto &ch : sindarin) {
55         kana.push_back(isupper(ch) ? static_cast<char>(tolower(ch)) : ch);
56     }
57     kana.append("$");
58
59     for (auto idx = 0; s2j_table[idx].key1 != nullptr; idx++) {
60         concptr pat1 = s2j_table[idx].key1;
61         size_t len = strlen(pat1);
62         std::string::size_type i = 0;
63
64         while (i < kana.length()) {
65             if (strncmp(kana.data() + i, pat1, len) == 0) {
66                 concptr pat2 = s2j_table[idx].key2;
67
68                 kana.replace(i, len, pat2);
69                 i += strlen(pat2);
70             } else {
71                 if (iskanji(kana[i])) {
72                     ++i;
73                 }
74                 ++i;
75             }
76         }
77     }
78
79     kana.erase(kana.find('$'));
80     return kana;
81 }
82
83 /*!
84  * 日本語動詞活用 (打つ>打って,打ち etc)
85  * AND : 殴る,蹴る > 殴り,蹴る
86  * TO  : 殴る,蹴る > 殴って蹴る
87  * OR  : 殴る,蹴る > 殴ったり蹴ったり
88  */
89 static constexpr struct jverb_table_t {
90     std::string_view from;
91     std::string_view to_list[3];
92 } jverb_table[] = {
93     { "する", { "し", "して", "した" } },
94     { "いる", { "いて", "いて", "いた" } },
95
96     { "える", { "え", "えて", "えた" } },
97     { "ける", { "け", "けて", "けた" } },
98     { "げる", { "げ", "えて", "げた" } },
99     { "せる", { "せ", "せて", "せた" } },
100     { "ぜる", { "ぜ", "ぜて", "ぜた" } },
101     { "てる", { "て", "てって", "てった" } },
102     { "でる", { "で", "でて", "でた" } },
103     { "ねる", { "ね", "ねて", "ねた" } },
104     { "へる", { "へ", "へて", "へた" } },
105     { "べる", { "べ", "べて", "べた" } },
106     { "める", { "め", "めて", "めた" } },
107     { "れる", { "れ", "れて", "れた" } },
108
109     { "う", { "い", "って", "った" } },
110     { "く", { "き", "いて", "いた" } },
111     { "ぐ", { "ぎ", "いで", "いだ" } },
112     { "す", { "し", "して", "した" } },
113     { "ず", { "じ", "じて", "じた" } },
114     { "つ", { "ち", "って", "った" } },
115     { "づ", { "ぢ", "って", "った" } },
116     { "ぬ", { "に", "ねて", "ねた" } },
117     { "ふ", { "ひ", "へて", "へた" } },
118     { "ぶ", { "び", "んで", "んだ" } },
119     { "む", { "み", "んで", "んだ" } },
120     { "る", { "り", "って", "った" } },
121 };
122
123 /*!
124  * @brief jverb_table_tに従って動詞を活用する
125  * @param in 変換元となる原形動詞
126  * @param type 変換種類を指定(AND/TO/OR)
127  * @return 活用形の動詞
128  */
129 std::string conjugate_jverb(std::string_view in, JVerbConjugationType type)
130 {
131     std::stringstream ss;
132
133     for (const auto &[from, to_list] : jverb_table) {
134         const auto stem_length = in.length() - from.length();
135         if (in.substr(stem_length) == from) {
136             ss << in.substr(0, stem_length) << to_list[enum2i(type)];
137             return ss.str();
138         }
139     }
140
141     constexpr std::string_view conjuctions[3] = {
142         "そして",
143         "ことにより",
144         "ことや",
145     };
146
147     ss << in << conjuctions[enum2i(type)];
148     return ss.str();
149 }
150
151 static const std::set<std::string_view> kinsoku_list{
152     // clang-format off
153     "、", "。", ",", ".", "?", "!",
154     "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ",
155     "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "ゃ", "ゅ", "ょ", "っ",
156     "ー", "~",
157     "」", "』", ")", "}", "]", "》", "】",
158     // clang-format on
159 };
160
161 /*!
162  * @brief 引数で与えられた文字が行頭禁則文字であるかどうか調べる
163  *
164  * @param ch 調べる文字
165  * @return 行頭禁則文字であるなら true、そうでないなら false
166  */
167 bool is_kinsoku(std::string_view ch)
168 {
169     return (ch.length() >= 2) && kinsoku_list.contains(ch);
170 }
171
172 /*!
173  * @brief 文字コードをSJISからEUCに変換する / Convert SJIS string to EUC string
174  * @param str 変換する文字列のポインタ
175  * @details
176  */
177 void sjis2euc(char *str)
178 {
179     int i;
180     unsigned char c1, c2;
181
182     int len = strlen(str);
183
184     std::vector<char> tmp(len + 1);
185
186     for (i = 0; i < len; i++) {
187         c1 = str[i];
188         if (c1 & 0x80) {
189             i++;
190             c2 = str[i];
191             if (c2 >= 0x9f) {
192                 c1 = c1 * 2 - (c1 >= 0xe0 ? 0xe0 : 0x60);
193                 c2 += 2;
194             } else {
195                 c1 = c1 * 2 - (c1 >= 0xe0 ? 0xe1 : 0x61);
196                 c2 += 0x60 + (c2 < 0x7f);
197             }
198             tmp[i - 1] = c1;
199             tmp[i] = c2;
200         } else {
201             tmp[i] = c1;
202         }
203     }
204     tmp[len] = 0;
205     strcpy(str, tmp.data());
206 }
207
208 /*!
209  * @brief 文字コードをEUCからSJISに変換する / Convert EUC string to SJIS string
210  * @param str 変換する文字列のポインタ
211  * @details
212  */
213 void euc2sjis(char *str)
214 {
215     int i;
216     unsigned char c1, c2;
217
218     int len = strlen(str);
219
220     std::vector<char> tmp(len + 1);
221
222     for (i = 0; i < len; i++) {
223         c1 = str[i];
224         if (c1 & 0x80) {
225             i++;
226             c2 = str[i];
227             if (c1 % 2) {
228                 c1 = (c1 >> 1) + (c1 < 0xdf ? 0x31 : 0x71);
229                 c2 -= 0x60 + (c2 < 0xe0);
230             } else {
231                 c1 = (c1 >> 1) + (c1 < 0xdf ? 0x30 : 0x70);
232                 c2 -= 2;
233             }
234
235             tmp[i - 1] = c1;
236             tmp[i] = c2;
237         } else {
238             tmp[i] = c1;
239         }
240     }
241     tmp[len] = 0;
242     strcpy(str, tmp.data());
243 }
244
245 /*!
246  * @brief strを環境に合った文字コードに変換し、変換前の文字コードを返す。strの長さに制限はない。
247  * @param str 変換する文字列のポインタ
248  * @return
249  * 0: Unknown<br>
250  * 1: ASCII (Never known to be ASCII in this function.)<br>
251  * 2: EUC<br>
252  * 3: SJIS<br>
253  */
254 byte codeconv(char *str)
255 {
256     byte code = 0;
257     for (auto i = 0; str[i]; i++) {
258         unsigned char c1;
259         unsigned char c2;
260
261         /* First byte */
262         c1 = str[i];
263
264         /* ASCII? */
265         if (!(c1 & 0x80)) {
266             continue;
267         }
268
269         /* Second byte */
270         i++;
271         c2 = str[i];
272
273         if (((0xa1 <= c1 && c1 <= 0xdf) || (0xfd <= c1 && c1 <= 0xfe)) && (0xa1 <= c2 && c2 <= 0xfe)) {
274             /* Only EUC is allowed */
275             if (!code) {
276                 /* EUC */
277                 code = 2;
278             }
279
280             /* Broken string? */
281             else if (code != 2) {
282                 /* No conversion */
283                 return 0;
284             }
285         } else {
286             auto is_cp932 = (0x81 <= c1 && c1 <= 0x9f) && ((0x40 <= c2 && c2 <= 0x7e) || (0x80 <= c2 && c2 <= 0xfc));
287             is_cp932 |= (0xe0 <= c1 && c1 <= 0xfc) && (0x40 <= c2 && c2 <= 0x7e);
288             if (!is_cp932) {
289                 continue;
290             }
291
292             /* Only SJIS is allowed */
293             if (!code) {
294                 /* SJIS */
295                 code = 3;
296             }
297
298             /* Broken string? */
299             else if (code != 3) {
300                 /* No conversion */
301                 return 0;
302             }
303         }
304     }
305
306     switch (code) {
307 #ifdef EUC
308     case 3:
309         /* SJIS -> EUC */
310         sjis2euc(str);
311         break;
312 #endif
313
314 #ifdef SJIS
315     case 2:
316         /* EUC -> SJIS */
317         euc2sjis(str);
318
319         break;
320 #endif
321     }
322
323     /* Return kanji code */
324     return code;
325 }
326
327 /*!
328  * @brief 文字列sのxバイト目が漢字の1バイト目かどうか判定する
329  * @param s 判定する文字列のポインタ
330  * @param x 判定する位置(バイト)
331  * @return 漢字の1バイト目ならばTRUE
332  */
333 bool iskanji2(concptr s, int x)
334 {
335     int i;
336
337     for (i = 0; i < x; i++) {
338         if (iskanji(s[i])) {
339             i++;
340         }
341     }
342     if ((x == i) && iskanji(s[x])) {
343         return true;
344     }
345
346     return false;
347 }
348
349 /*!
350  * @brief 文字列の文字コードがASCIIかどうかを判定する
351  * @param str 判定する文字列へのポインタ
352  * @return 文字列の文字コードがASCIIならTRUE、そうでなければFALSE
353  */
354 static bool is_ascii_str(concptr str)
355 {
356     for (; *str; str++) {
357         int ch = *str;
358         if (!(0x00 < ch && ch <= 0x7f)) {
359             return false;
360         }
361     }
362     return true;
363 }
364
365 #if defined(EUC)
366 #include <algorithm>
367 #include <iconv.h>
368 #include <initializer_list>
369 #include <vector>
370
371 // UTF-8 の文字列長は必ずしも3バイトとは限らないが、変愚蛮怒の仕様範囲では3固定.
372 constexpr auto ENCODING_LENGTH = 3;
373 class EncodingConverter {
374 public:
375     EncodingConverter(const std::initializer_list<unsigned char> from, const std::initializer_list<unsigned char> to)
376         : from(from)
377         , to(to)
378     {
379     }
380
381     bool equals(const unsigned char *p) const
382     {
383         return std::equal(from.begin(), from.end(), p);
384     }
385
386     void replace(unsigned char *p) const
387     {
388         std::copy_n(to.begin(), ENCODING_LENGTH, p);
389     }
390
391 private:
392     std::vector<unsigned char> from;
393     std::vector<unsigned char> to;
394 };
395
396 const std::vector<EncodingConverter> encoding_characters = {
397     { { 0xef, 0xbd, 0x9e }, { 0xe3, 0x80, 0x9c } }, /* FULLWIDTH TILDE -> WAVE DASH (全角チルダ → 波ダッシュ) */
398     { { 0xef, 0xbc, 0x8d }, { 0xe2, 0x88, 0x92 } }, /* FULLWIDTH HYPHEN-MINUS -> MINUS SIGN (全角ハイフン → マイナス記号) */
399 };
400
401 /*!
402  * @brief 受け取ったUTF-8文字列を調べ、特定のコードポイントの文字の置き換えを行う
403  *
404  * 受け取ったUTF-8の文字列に含まれる文字を1つ1つ調べて、encoding_characters で
405  * 定義されている特定のコードポイントの文字の変換を行う。
406  *
407  * '~'と'-'は、Windows環境(CP932)とLinux/UNIX環境(EUC-JP)でUTF-8に対応する
408  * 文字としてそれぞれ別のコードポイントが割り当てられており、別の環境の
409  * UTF-8からシステムの文字コードに変換した時に、これらの文字は変換できず
410  * 文字化けが起きてしまう。
411  *
412  * これを避けるため、Linux/UNIX環境(EUC-JP)ではUTF-8→EUC-JPの変換を行う前に
413  * 該当するコードポイントの文字をLinux/UNIX環境のものに置き換えてから
414  * 変換を行うようにするためのルーチン。
415  *
416  * @param str コードポイントの置き換えを行う文字列へのポインタ
417  */
418 static void ms_to_jis_unicode(char *str)
419 {
420     for (auto *p = (unsigned char *)str; *p; p++) {
421         auto subseq_num = 0;
422         if (0x00 < *p && *p <= 0x7f) {
423             continue;
424         }
425
426         if ((*p & 0xe0) == 0xc0) {
427             subseq_num = 1;
428         }
429
430         if ((*p & 0xf0) == 0xe0) {
431             for (const auto &converter : encoding_characters) {
432                 if (converter.equals(p)) {
433                     converter.replace(p);
434                 }
435             }
436
437             subseq_num = 2;
438         }
439
440         if ((*p & 0xf8) == 0xf0) {
441             subseq_num = 3;
442         }
443
444         p += subseq_num;
445     }
446 }
447
448 #elif defined(SJIS) && defined(WINDOWS)
449 #include <Windows.h>
450 #endif
451
452 #ifdef EUC
453 /*!
454  * @brief 文字列の文字コードをUTF-8からEUC-JPに変換する
455  * @param utf8_str 変換元の文字列へのポインタ
456  * @param utf8_str_len 変換元の文字列の長さ(文字数ではなくバイト数)
457  * @param euc_buf 変換した文字列を格納するバッファへのポインタ
458  * @param euc_buf_len 変換した文字列を格納するバッファのサイズ
459  * @return 変換に成功した場合変換後の文字列の長さを返す
460  *         変換に失敗した場合-1を返す
461  */
462 int utf8_to_euc(char *utf8_str, size_t utf8_str_len, char *euc_buf, size_t euc_buf_len)
463 {
464     static iconv_t cd = nullptr;
465     if (!cd) {
466         cd = iconv_open("EUC-JP", "UTF-8");
467     }
468
469     ms_to_jis_unicode(utf8_str);
470
471     size_t inlen_left = utf8_str_len;
472     size_t outlen_left = euc_buf_len;
473     char *in = utf8_str;
474     char *out = euc_buf;
475
476     if (iconv(cd, &in, &inlen_left, &out, &outlen_left) == (size_t)-1) {
477         return -1;
478     }
479
480     return euc_buf_len - outlen_left;
481 }
482
483 /*!
484  * @brief 文字列の文字コードをEUC-JPからUTF-8に変換する
485  * @param euc_str 変換元の文字列へのポインタ
486  * @param euc_str_len 変換元の文字列の長さ(文字数ではなくバイト数)
487  * @param utf8_buf 変換した文字列を格納するバッファへのポインタ
488  * @param utf8_buf_len 変換した文字列を格納するバッファのサイズ
489  * @return 変換に成功した場合変換後の文字列の長さを返す
490  *         変換に失敗した場合-1を返す
491  */
492 int euc_to_utf8(const char *euc_str, size_t euc_str_len, char *utf8_buf, size_t utf8_buf_len)
493 {
494     static iconv_t cd = nullptr;
495     if (!cd) {
496         cd = iconv_open("UTF-8", "EUC-JP");
497     }
498
499     size_t inlen_left = euc_str_len;
500     size_t outlen_left = utf8_buf_len;
501     const char *in = euc_str;
502     char *out = utf8_buf;
503
504     // iconv は入力バッファを書き換えないのでキャストで const を外してよい
505     if (iconv(cd, (char **)&in, &inlen_left, &out, &outlen_left) == (size_t)-1) {
506         return -1;
507     }
508
509     return utf8_buf_len - outlen_left;
510 }
511 #endif
512
513 /*!
514  * @brief 文字コードがUTF-8の文字列をシステムの文字コードに変換する
515  * @param utf8_str 変換するUTF-8の文字列へのポインタ
516  * @param sys_str_buffer 変換したシステムの文字コードの文字列を格納するバッファへのポインタ
517  * @param sys_str_buflen 変換したシステムの文字コードの文字列を格納するバッファの長さ
518  * @return 変換に成功した場合TRUE、失敗した場合FALSEを返す
519  */
520 static bool utf8_to_sys(char *utf8_str, char *sys_str_buffer, size_t sys_str_buflen)
521 {
522 #if defined(EUC)
523
524     /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
525     return utf8_to_euc(utf8_str, strlen(utf8_str) + 1, sys_str_buffer, sys_str_buflen) >= 0;
526
527 #elif defined(SJIS) && defined(WINDOWS)
528
529     int input_len = strlen(utf8_str) + 1; /* include termination character */
530
531     std::vector<WCHAR> utf16buf(input_len);
532
533     /* UTF-8 -> UTF-16 */
534     if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, input_len, utf16buf.data(), input_len) == 0) {
535         return false;
536     }
537
538     /* UTF-8 -> SJIS(CP932) */
539     if (WideCharToMultiByte(932, 0, utf16buf.data(), -1, sys_str_buffer, sys_str_buflen, nullptr, nullptr) == 0) {
540         return false;
541     }
542
543     return true;
544
545 #else
546     return false;
547 #endif
548 }
549
550 /*!
551  * @brief システムの文字コードからUTF-8に変換する
552  * @param str システムの文字コードの文字列
553  * @return UTF-8に変換した文字列
554  *         変換に失敗した場合はstd::nullopt
555  */
556 std::optional<std::string> sys_to_utf8(std::string_view str)
557 {
558 #if defined(EUC)
559     std::string utf8str(str.length() * 2 + 1, '\0');
560     const auto len = euc_to_utf8(str.data(), str.length(), utf8str.data(), utf8str.size());
561
562     return (len >= 0) ? std::make_optional(std::move(utf8str.erase(len))) : std::nullopt;
563 #elif defined(SJIS) && defined(WINDOWS)
564     /* SJIS(CP932) -> UTF-16 */
565     std::vector<WCHAR> utf16buf(str.length());
566     const auto utf16_len = MultiByteToWideChar(932, 0, str.data(), str.size(), utf16buf.data(), utf16buf.size());
567     if (utf16_len == 0) {
568         return std::nullopt;
569     }
570
571     /* UTF-16 -> UTF-8 */
572     std::vector<char> utf8buf(str.length() * 2 + 1);
573     const auto utf8_len = WideCharToMultiByte(CP_UTF8, 0, utf16buf.data(), utf16_len, utf8buf.data(), utf8buf.size(), nullptr, nullptr);
574     if (utf8_len == 0) {
575         return std::nullopt;
576     }
577
578     return std::make_optional<std::string>(utf8buf.data(), utf8_len);
579 #else
580     return std::nullopt;
581 #endif
582 }
583
584 /*!
585  * @brief 受け取った文字列の文字コードを推定し、システムの文字コードへ変換する
586  * @param strbuf 変換する文字列を格納したバッファへのポインタ。
587  *               バッファは変換した文字列で上書きされる。
588  *               UTF-8からSJISもしくはEUCへの変換を想定しているのでバッファの長さが足りなくなることはない。
589  * @param buflen バッファの長さ。
590  */
591 void guess_convert_to_system_encoding(char *strbuf, int buflen)
592 {
593     if (is_ascii_str(strbuf)) {
594         return;
595     }
596
597     if (is_utf8_str(strbuf)) {
598         std::vector<char> work(buflen);
599         angband_strcpy(work.data(), strbuf, buflen);
600         if (!utf8_to_sys(work.data(), strbuf, buflen)) {
601             msg_print("警告:文字コードの変換に失敗しました");
602             msg_print(nullptr);
603         }
604     }
605 }
606
607 /*!
608  * @brief 変愚蛮怒基準のポンド→キログラム変換定義(全体)
609  * @param x ポンド値
610  * @return キログラム値
611  * @details 帝国ポンドとは完全にずれているが、気にするな!
612  */
613 static int lb_to_kg_all(int x)
614 {
615     return x * 5;
616 }
617
618 /*!
619  * @brief 変愚蛮怒基準のポンド→キログラム変換定義(整数部)
620  * @param x ポンド値
621  * @return キログラム値の整数部
622  */
623 int lb_to_kg_integer(int x)
624 {
625     return lb_to_kg_all(x) / 100;
626 }
627
628 /*!
629  * 変愚蛮怒基準のポンド→キログラム変換定義(小数部)
630  * @param x ポンド値
631  * @return キログラム値の小数部
632  */
633 int lb_to_kg_fraction(int x)
634 {
635     return (lb_to_kg_all(x) % 100) / 10;
636 }
637
638 #endif /* JP */