diff --git a/modules/NIGHTDLL/nt_msg.c b/modules/NIGHTDLL/nt_msg.c index 9209789..aeec152 100644 --- a/modules/NIGHTDLL/nt_msg.c +++ b/modules/NIGHTDLL/nt_msg.c @@ -104,7 +104,7 @@ void ntmsg_init() { // 主人公名前 - nt_sstr_regist_replace(utf2sjis(SNAME_RYO), utf2sjis(SNAME_RYO_DEF)); + nt_sstr_regist_replace(fromUTF8(SNAME_RYO), fromUTF8(SNAME_RYO_DEF)); night.msg.cbmove = cb_mousemove; night.msg.cbrelease = cb_keyrelease; diff --git a/modules/SACT/sactlog.c b/modules/SACT/sactlog.c index f2dfec1..4372234 100644 --- a/modules/SACT/sactlog.c +++ b/modules/SACT/sactlog.c @@ -43,7 +43,7 @@ #define LOGMSG_LINES 6 -static const char *logmsg[LOGMSG_LINES] = { +static const char *logmsg_utf8[LOGMSG_LINES] = { "\n", "※バックログ操作方法※", "[ESC]またはマウス右クリックでゲームに戻る", @@ -110,15 +110,15 @@ static void draw_log() { int sblog_start(void) { - static char *logmsg_sjis[6]; - if (!logmsg_sjis[0]) { + static char *logmsg[LOGMSG_LINES]; + if (!logmsg[0]) { for (int i = 0; i < LOGMSG_LINES; i++) - logmsg_sjis[i] = utf2sjis(logmsg[i]); + logmsg[i] = fromUTF8(logmsg_utf8[i]); } // 説明文章を追加 for (int i = 0; i < LOGMSG_LINES; i++) - sact.log = list_append(sact.log, logmsg_sjis[i]); + sact.log = list_append(sact.log, logmsg[i]); back = sf_dup(sf0); chr = sf_create_surface(sf0->width, sf0->height, 8); diff --git a/src/ags.c b/src/ags.c index 144f9fa..cb25442 100644 --- a/src/ags.c +++ b/src/ags.c @@ -186,16 +186,16 @@ void ags_setViewArea(int x, int y, int width, int height) { void ags_setWindowTitle(const char *src) { #define TITLEHEAD "XSystem35 Version "VERSION":" - BYTE *dst, *d; + BYTE *utf, *d; - dst = sjis2utf(src); - if (NULL == (d = malloc(strlen(dst) + strlen(TITLEHEAD) + 1))) { + utf = toUTF8(src); + if (NULL == (d = malloc(strlen(utf) + strlen(TITLEHEAD) + 1))) { NOMEMERR(); } strcpy(d, TITLEHEAD); - strcat(d, dst); + strcat(d, utf); SetWindowTitle(d); - free(dst); + free(utf); free(d); } @@ -402,7 +402,7 @@ int ags_drawString(int x, int y, const char *src, int col) { if (!check_param_xy(&x, &y)) return 0; DspDeviceSync(); - char *utf8 = sjis2utf(src); + char *utf8 = toUTF8(src); w = DrawString(x, y, utf8, col); free(utf8); @@ -410,7 +410,7 @@ int ags_drawString(int x, int y, const char *src, int col) { } agsurface_t *ags_drawStringToSurface(const char *str) { - char *utf8 = sjis2utf(str); + char *utf8 = toUTF8(str); agsurface_t *sf = nact->ags.font->get_glyph(utf8); free(utf8); return sf; diff --git a/src/cmd2F.c b/src/cmd2F.c index e33ede4..579af93 100644 --- a/src/cmd2F.c +++ b/src/cmd2F.c @@ -367,7 +367,7 @@ void commands2F23() { int y = getCaliValue(); char *vFileName = sys_getString(0); - char *fname_utf8 = sjis2utf(vFileName); + char *fname_utf8 = toUTF8(vFileName); sysVar[0] = cg_load_with_filename(fname_utf8, x, y); free(fname_utf8); @@ -380,7 +380,7 @@ void commands2F24() { int *var = NULL, _var = 0; int num = 0; - char *fname_utf8 = sjis2utf(file_name); + char *fname_utf8 = toUTF8(file_name); switch(type) { case 0: var = getCaliVariable(); @@ -412,8 +412,8 @@ void commands2F26() { char *title = sys_getString(0); char *t1, *t2, *t3; - t1 = sjis2utf(title); - t2 = sjis2utf(v_str(dst_no -1)); + t1 = toUTF8(title); + t2 = toUTF8(v_str(dst_no -1)); mi_param.title = t1; mi_param.oldstring = t2; @@ -426,10 +426,10 @@ void commands2F26() { return; } - t3 = utf2sjis(mi_param.newstring); + t3 = fromUTF8(mi_param.newstring); /* 全角文字以外は不可 */ - if (!sjis_has_hankaku(t3)) { + if (nact->encoding != SHIFT_JIS || !sjis_has_hankaku(t3)) { v_strcpy(dst_no -1, t3); } free(t1); @@ -457,7 +457,7 @@ void commands2F28() { if (nact->game_title_utf8) free(nact->game_title_utf8); - nact->game_title_utf8 = sjis2utf(title); + nact->game_title_utf8 = toUTF8(title); ags_setWindowTitle(title); @@ -478,7 +478,7 @@ void commands2F29() { if (ni_param.title != NULL) { free(ni_param.title); } - t = sjis2utf(title); + t = toUTF8(title); ni_param.title = t; DEBUG_COMMAND("NT(new) %s:\n", title); @@ -489,7 +489,7 @@ void commands2F2A() { char *file_name = sys_getString(0); int *var, _var = 0, cnt; - char *fname_utf8 = sjis2utf(file_name); + char *fname_utf8 = toUTF8(file_name); switch(type) { case 0: var = getCaliVariable(); @@ -924,8 +924,8 @@ void commands2F57() { char *t1, *t2, *t3; INPUTSTRING_PARAM p; - t1 = sjis2utf(sTitle); - t2 = sjis2utf(v_str(eStrVar -1)); + t1 = toUTF8(sTitle); + t2 = toUTF8(v_str(eStrVar -1)); p.title = t1; p.oldstring = t2; p.max = eLength; @@ -934,10 +934,10 @@ void commands2F57() { if (p.newstring == NULL) { *vResult = 65535; } else { - t3 = utf2sjis(p.newstring); - if (!sjis_has_hankaku(t3)) { + t3 = fromUTF8(p.newstring); + if (nact->encoding != SHIFT_JIS || !sjis_has_hankaku(t3)) { v_strcpy(eStrVar -1, t3); - *vResult = sjis_count_char(t3); + *vResult = v_strlen(eStrVar -1); } else { *vResult = 65535; } @@ -971,7 +971,7 @@ void commands2F5A() { char *sText = sys_getString(0); char *t1; - t1 = sjis2utf(sText); + t1 = toUTF8(sText); menu_msgbox_open(t1); free(t1); @@ -982,7 +982,7 @@ void commands2F5B() { int eNum = getCaliValue(); char *t1; - t1 = sjis2utf(v_str(eNum) - 1); + t1 = toUTF8(v_str(eNum) - 1); menu_msgbox_open(t1); free(t1); diff --git a/src/cmd_check.c b/src/cmd_check.c index 3e7df3c..38a3448 100644 --- a/src/cmd_check.c +++ b/src/cmd_check.c @@ -1098,6 +1098,8 @@ void check_command(int c0) { undeferr(); } break; + case 'U': + commandZU(); break; case 'Z': switch(sl_getc()) { case 0: diff --git a/src/cmd_check.h b/src/cmd_check.h index 4a1c5d6..04c3cb7 100644 --- a/src/cmd_check.h +++ b/src/cmd_check.h @@ -304,6 +304,7 @@ extern void commandZI(); extern void commandZA(); extern void commandZK(); extern void commandZR(); +extern void commandZU(); /* define in cmd2F.c */ extern void commands2F00(); diff --git a/src/cmdh.c b/src/cmdh.c index e4aac78..faf0910 100644 --- a/src/cmdh.c +++ b/src/cmdh.c @@ -22,9 +22,11 @@ /* $Id: cmdh.c,v 1.5 2000/09/10 10:39:48 chikama Exp $ */ #include +#include #include #include "portab.h" #include "xsystem35.h" +#include "utfsjis.h" #include "hankaku.h" void commandH() { @@ -32,7 +34,9 @@ void commandH() { int num = getCaliValue(); char buf[512]; - sys_addMsg(format_number_zenkaku(num, fig, buf)); + char *s = fromSJIS(format_number_zenkaku(num, fig, buf)); + sys_addMsg(s); + free(s); DEBUG_COMMAND("H %d,%d:\n",fig,num); } diff --git a/src/cmdl.c b/src/cmdl.c index a218868..11c0bf5 100644 --- a/src/cmdl.c +++ b/src/cmdl.c @@ -112,7 +112,7 @@ void commandLE() { int *var, _var = 0; int num; - char *fname_utf8 = sjis2utf(filename); + char *fname_utf8 = toUTF8(filename); switch(type) { case 0: /* T2 */ var = getCaliVariable(); @@ -246,7 +246,7 @@ void commandLC() { int y = getCaliValue(); char *filename = sys_getString(':'); - char *fname_utf8 = sjis2utf(filename); + char *fname_utf8 = toUTF8(filename); sysVar[0] = cg_load_with_filename(fname_utf8, x, y); free(fname_utf8); diff --git a/src/cmdm.c b/src/cmdm.c index e45d630..b5d6efb 100644 --- a/src/cmdm.c +++ b/src/cmdm.c @@ -62,34 +62,37 @@ void commandMP() { /* 指定の文字列を指定文字数だけ表示する(Xコマンドの桁数指定) */ int num1 = getCaliValue(); int num2 = getCaliValue(); - const char bstr[3] = "\x81\x40"; // full-width whitespace + const char *fullwidth_blank[CHARACTER_ENCODING_MAX + 1] = { + [SHIFT_JIS] = "\x81\x40", + [UTF8] = " ", + }; const char *src = v_str(num1 - 1); int chars = num2; char *str; /* Patched English executable appends num2 spaces instead of truncating */ if (have_eng_mp_patch) { - str = calloc(strlen(src) + num2 * 2 + 1, 1); + str = calloc(strlen(src) + num2 * strlen(fullwidth_blank[nact->encoding]) + 1, 1); if (NULL == str) { NOMEMERR(); } strcpy(str, src); } else { - str = calloc(num2 * 2 + 1, 1); + str = calloc(num2 * MAX_BYTES_PAR_CHAR(nact->encoding) + 1, 1); if (NULL == str) { NOMEMERR(); } const char *p = src; while (*p && chars > 0) { - p += CHECKSJIS1BYTE(*p) ? 2 : 1; + p = advance_char(p, nact->encoding); chars--; } strncpy(str, src, p - src); } while (chars-- > 0) { - strcat(str, bstr); + strcat(str, fullwidth_blank[nact->encoding]); } sys_addMsg(str); @@ -105,8 +108,8 @@ void commandMI() { /* T2 */ char *title = sys_getString(':'); char *t1, *t2, *t3; - t1 = sjis2utf(title); - t2 = sjis2utf(v_str(dst_no -1)); + t1 = toUTF8(title); + t2 = toUTF8(v_str(dst_no -1)); mi_param.title = t1; mi_param.oldstring = t2; @@ -119,10 +122,10 @@ void commandMI() { /* T2 */ return; } - t3 = utf2sjis(mi_param.newstring); + t3 = fromUTF8(mi_param.newstring); /* 全角文字以外は不可 */ - if (!sjis_has_hankaku(t3)) { + if (nact->encoding != SHIFT_JIS ||!sjis_has_hankaku(t3)) { v_strcpy(dst_no -1, t3); } free(t1); @@ -163,7 +166,7 @@ void commandMT() { if (nact->game_title_utf8) free(nact->game_title_utf8); - nact->game_title_utf8 = sjis2utf(str); + nact->game_title_utf8 = toUTF8(str); ags_setWindowTitle(str); /* 闘神都市II 対策 */ @@ -197,7 +200,9 @@ void commandMH() { int num2 = getCaliValue(); char buf[512]; - v_strcpy(num1 - 1, format_number_zenkaku(num2, fig, buf)); + char *s = fromSJIS(format_number_zenkaku(num2, fig, buf)); + v_strcpy(num1 - 1, s); + free(s); DEBUG_COMMAND("MH %d,%d,%d:\n",num1,fig,num2); } @@ -215,7 +220,7 @@ void commandML() { int *var = getCaliVariable(); int str_no = getCaliValue(); - *var = sjis_count_char(v_str(str_no -1)); + *var = v_strlen(str_no -1); DEBUG_COMMAND("ML %p,%d:\n",var, str_no); } @@ -338,7 +343,7 @@ void commandMJ() { INPUTSTRING_PARAM mj_param; char *t1, *t2; - t1 = sjis2utf(v_str(num -1)); + t1 = toUTF8(v_str(num -1)); mj_param.max = max_len; mj_param.x = x; mj_param.y = y; @@ -349,8 +354,8 @@ void commandMJ() { menu_inputstring2(&mj_param); if (mj_param.newstring == NULL) return; - t2 = utf2sjis(mj_param.newstring); - if (!sjis_has_hankaku(t2)) { + t2 = fromUTF8(mj_param.newstring); + if (nact->encoding != SHIFT_JIS || !sjis_has_hankaku(t2)) { v_strcpy(num -1, t2); } free(t1); diff --git a/src/cmdn.c b/src/cmdn.c index 6abff7d..59d35b7 100644 --- a/src/cmdn.c +++ b/src/cmdn.c @@ -375,13 +375,9 @@ void commandNI() { /* From Panyo */ void commandNT() { /* From Panyo */ /* NIコマンドで表示するタイトルを設定する。*/ char *str = sys_getString(':'); - char *t; - if (ni_param.title != NULL) { - free(ni_param.title); - } - t = sjis2utf(str); - ni_param.title = t; + free(ni_param.title); + ni_param.title = toUTF8(str); DEBUG_COMMAND("NT %p:\n", str); } diff --git a/src/cmdq.c b/src/cmdq.c index aaa30e6..587396a 100644 --- a/src/cmdq.c +++ b/src/cmdq.c @@ -87,7 +87,7 @@ void commandQE() { char *filename = sys_getString(':'); int *var, _var = 0, cnt; - char *fname_utf8 = sjis2utf(filename); + char *fname_utf8 = toUTF8(filename); switch(type) { case 0: var = getCaliVariable(); diff --git a/src/cmdz.c b/src/cmdz.c index 697af2e..d2f0ad4 100644 --- a/src/cmdz.c +++ b/src/cmdz.c @@ -525,3 +525,13 @@ void commandZR() { DEBUG_COMMAND("ZR %d,%d:\n", num, *var); } + +void commandZU() { + /* xsystem35 extension: sets unicode mode */ + int sw = getCaliValue(); + + if (sw <= CHARACTER_ENCODING_MAX) + sys_setCharacterEncoding(sw); + + DEBUG_COMMAND("ZU %d:\n",sw); +} diff --git a/src/font_freetype2.c b/src/font_freetype2.c index 39f30a5..8dd43cf 100644 --- a/src/font_freetype2.c +++ b/src/font_freetype2.c @@ -34,6 +34,7 @@ #include "system.h" #include "font.h" #include "ags.h" +#include "utfsjis.h" extern const unsigned short *s2u[]; @@ -59,35 +60,6 @@ static FONT *this; static void pixmap2comimg(BYTE *src, int x, int y, int w, int h, int src_bpl); static void pixmapmono2comimg(BYTE *src, int x, int y, int w, int h, int src_bpl); -static int utf8_next_codepoint(const char **msg) { - int code; - const unsigned char *s = (const unsigned char *)*msg; - - if (*s <= 0x7f) { - code = *s++; - } else if (*s <= 0xbf) { - WARNING("Invalid UTF-8 sequence %02x\n", *s); - code = '?'; - s++; - } else if (*s <= 0xdf) { - code = (s[0] & 0x1f) << 6 | (s[1] & 0x3f); - s += 2; - } else if (*s <= 0xef) { - code = (s[0] & 0xf) << 12 | (s[1] & 0x3f) << 6 | (s[2] & 0x3f); - s += 3; - } else if (*s <= 0xf7) { - code = (s[0] & 0x7) << 18 | (s[1] & 0x3f) << 12 | (s[2] & 0x3f) << 6 | (s[3] & 0x3f); - s += 4; - } else { - code = 0xfffd; // REPLACEMENT CHARACTER - s++; - while (0x80 <= *s && *s <= 0xbf) - s++; - } - *msg = (const char *)s; - return code; -} - static boolean select_charmap(FT_Face f, int type) { int i; diff --git a/src/hankaku.c b/src/hankaku.c index 6ea5b23..87a2e53 100644 --- a/src/hankaku.c +++ b/src/hankaku.c @@ -140,7 +140,7 @@ static const char zenkaku_digits[][3] = { 0x81,0x40,0x00, /* space */ }; -BYTE *zen2han(const BYTE *src) { +static BYTE *zen2han_sjis(const BYTE *src) { BYTE c0, c1; char *dst, *_dst; @@ -173,7 +173,201 @@ BYTE *zen2han(const BYTE *src) { return _dst; } -BYTE *han2zen(const BYTE *src) { +static char *zen2han_utf8(const char *src) { + char *dst, *_dst; + dst = _dst = malloc(strlen(src) + 1); + if (dst == NULL) { + fprintf(stderr, "zen2han(): Out of Memory (size %zu)", strlen(src) + 1); + return NULL; + } + + const char *p = src; + while (*p) { + const char *prev = p; + switch (utf8_next_codepoint(&p)) { + case L'’': *dst++ = '\''; break; + case L'”': *dst++ = '"'; break; + case L' ': *dst++ = ' '; break; + case L'、': strcpy(dst, "、"); dst += 3; break; + case L'。': strcpy(dst, "。"); dst += 3; break; + case L'「': strcpy(dst, "「"); dst += 3; break; + case L'」': strcpy(dst, "」"); dst += 3; break; + case L'゛': strcpy(dst, "゙"); dst += 3; break; + case L'゜': strcpy(dst, "゚"); dst += 3; break; + case L'ァ': strcpy(dst, "ァ"); dst += 3; break; + case L'ア': strcpy(dst, "ア"); dst += 3; break; + case L'ィ': strcpy(dst, "ィ"); dst += 3; break; + case L'イ': strcpy(dst, "イ"); dst += 3; break; + case L'ゥ': strcpy(dst, "ゥ"); dst += 3; break; + case L'ウ': strcpy(dst, "ウ"); dst += 3; break; + case L'ェ': strcpy(dst, "ェ"); dst += 3; break; + case L'エ': strcpy(dst, "エ"); dst += 3; break; + case L'ォ': strcpy(dst, "ォ"); dst += 3; break; + case L'オ': strcpy(dst, "オ"); dst += 3; break; + case L'カ': strcpy(dst, "カ"); dst += 3; break; + case L'キ': strcpy(dst, "キ"); dst += 3; break; + case L'ク': strcpy(dst, "ク"); dst += 3; break; + case L'ケ': strcpy(dst, "ケ"); dst += 3; break; + case L'コ': strcpy(dst, "コ"); dst += 3; break; + case L'サ': strcpy(dst, "サ"); dst += 3; break; + case L'シ': strcpy(dst, "シ"); dst += 3; break; + case L'ス': strcpy(dst, "ス"); dst += 3; break; + case L'セ': strcpy(dst, "セ"); dst += 3; break; + case L'ソ': strcpy(dst, "ソ"); dst += 3; break; + case L'タ': strcpy(dst, "タ"); dst += 3; break; + case L'チ': strcpy(dst, "チ"); dst += 3; break; + case L'ッ': strcpy(dst, "ッ"); dst += 3; break; + case L'ツ': strcpy(dst, "ツ"); dst += 3; break; + case L'テ': strcpy(dst, "テ"); dst += 3; break; + case L'ト': strcpy(dst, "ト"); dst += 3; break; + case L'ナ': strcpy(dst, "ナ"); dst += 3; break; + case L'ニ': strcpy(dst, "ニ"); dst += 3; break; + case L'ヌ': strcpy(dst, "ヌ"); dst += 3; break; + case L'ネ': strcpy(dst, "ネ"); dst += 3; break; + case L'ノ': strcpy(dst, "ノ"); dst += 3; break; + case L'ハ': strcpy(dst, "ハ"); dst += 3; break; + case L'ヒ': strcpy(dst, "ヒ"); dst += 3; break; + case L'フ': strcpy(dst, "フ"); dst += 3; break; + case L'ヘ': strcpy(dst, "ヘ"); dst += 3; break; + case L'ホ': strcpy(dst, "ホ"); dst += 3; break; + case L'マ': strcpy(dst, "マ"); dst += 3; break; + case L'ミ': strcpy(dst, "ミ"); dst += 3; break; + case L'ム': strcpy(dst, "ム"); dst += 3; break; + case L'メ': strcpy(dst, "メ"); dst += 3; break; + case L'モ': strcpy(dst, "モ"); dst += 3; break; + case L'ャ': strcpy(dst, "ャ"); dst += 3; break; + case L'ヤ': strcpy(dst, "ヤ"); dst += 3; break; + case L'ュ': strcpy(dst, "ュ"); dst += 3; break; + case L'ユ': strcpy(dst, "ユ"); dst += 3; break; + case L'ョ': strcpy(dst, "ョ"); dst += 3; break; + case L'ヨ': strcpy(dst, "ヨ"); dst += 3; break; + case L'ラ': strcpy(dst, "ラ"); dst += 3; break; + case L'リ': strcpy(dst, "リ"); dst += 3; break; + case L'ル': strcpy(dst, "ル"); dst += 3; break; + case L'レ': strcpy(dst, "レ"); dst += 3; break; + case L'ロ': strcpy(dst, "ロ"); dst += 3; break; + case L'ワ': strcpy(dst, "ワ"); dst += 3; break; + case L'ヲ': strcpy(dst, "ヲ"); dst += 3; break; + case L'ン': strcpy(dst, "ン"); dst += 3; break; + case L'・': strcpy(dst, "・"); dst += 3; break; + case L'ー': strcpy(dst, "ー"); dst += 3; break; + case L'!': *dst++ = '!'; break; + case L'#': *dst++ = '#'; break; + case L'$': *dst++ = '$'; break; + case L'%': *dst++ = '%'; break; + case L'&': *dst++ = '&'; break; + case L'(': *dst++ = '('; break; + case L')': *dst++ = ')'; break; + case L'*': *dst++ = '*'; break; + case L'+': *dst++ = '+'; break; + case L',': *dst++ = ','; break; + case L'-': *dst++ = '-'; break; + case L'.': *dst++ = '.'; break; + case L'/': *dst++ = '/'; break; + case L'0': *dst++ = '0'; break; + case L'1': *dst++ = '1'; break; + case L'2': *dst++ = '2'; break; + case L'3': *dst++ = '3'; break; + case L'4': *dst++ = '4'; break; + case L'5': *dst++ = '5'; break; + case L'6': *dst++ = '6'; break; + case L'7': *dst++ = '7'; break; + case L'8': *dst++ = '8'; break; + case L'9': *dst++ = '9'; break; + case L':': *dst++ = ':'; break; + case L';': *dst++ = ';'; break; + case L'<': *dst++ = '<'; break; + case L'=': *dst++ = '='; break; + case L'>': *dst++ = '>'; break; + case L'?': *dst++ = '?'; break; + case L'@': *dst++ = '@'; break; + case L'A': *dst++ = 'A'; break; + case L'B': *dst++ = 'B'; break; + case L'C': *dst++ = 'C'; break; + case L'D': *dst++ = 'D'; break; + case L'E': *dst++ = 'E'; break; + case L'F': *dst++ = 'F'; break; + case L'G': *dst++ = 'G'; break; + case L'H': *dst++ = 'H'; break; + case L'I': *dst++ = 'I'; break; + case L'J': *dst++ = 'J'; break; + case L'K': *dst++ = 'K'; break; + case L'L': *dst++ = 'L'; break; + case L'M': *dst++ = 'M'; break; + case L'N': *dst++ = 'N'; break; + case L'O': *dst++ = 'O'; break; + case L'P': *dst++ = 'P'; break; + case L'Q': *dst++ = 'Q'; break; + case L'R': *dst++ = 'R'; break; + case L'S': *dst++ = 'S'; break; + case L'T': *dst++ = 'T'; break; + case L'U': *dst++ = 'U'; break; + case L'V': *dst++ = 'V'; break; + case L'W': *dst++ = 'W'; break; + case L'X': *dst++ = 'X'; break; + case L'Y': *dst++ = 'Y'; break; + case L'Z': *dst++ = 'Z'; break; + case L'[': *dst++ = '['; break; + case L']': *dst++ = ']'; break; + case L'^': *dst++ = '^'; break; + case L'_': *dst++ = '_'; break; + case L'`': *dst++ = '`'; break; + case L'a': *dst++ = 'a'; break; + case L'b': *dst++ = 'b'; break; + case L'c': *dst++ = 'c'; break; + case L'd': *dst++ = 'd'; break; + case L'e': *dst++ = 'e'; break; + case L'f': *dst++ = 'f'; break; + case L'g': *dst++ = 'g'; break; + case L'h': *dst++ = 'h'; break; + case L'i': *dst++ = 'i'; break; + case L'j': *dst++ = 'j'; break; + case L'k': *dst++ = 'k'; break; + case L'l': *dst++ = 'l'; break; + case L'm': *dst++ = 'm'; break; + case L'n': *dst++ = 'n'; break; + case L'o': *dst++ = 'o'; break; + case L'p': *dst++ = 'p'; break; + case L'q': *dst++ = 'q'; break; + case L'r': *dst++ = 'r'; break; + case L's': *dst++ = 's'; break; + case L't': *dst++ = 't'; break; + case L'u': *dst++ = 'u'; break; + case L'v': *dst++ = 'v'; break; + case L'w': *dst++ = 'w'; break; + case L'x': *dst++ = 'x'; break; + case L'y': *dst++ = 'y'; break; + case L'z': *dst++ = 'z'; break; + case L'{': *dst++ = '{'; break; + case L'|': *dst++ = '|'; break; + case L'}': *dst++ = '}'; break; + case L' ̄': *dst++ = '~'; break; + case L'¥': *dst++ = '\\'; break; + default: + while (prev < p) + *dst++ = *prev++; + break; + } + } + *dst = 0; + return _dst; +} + +BYTE *zen2han(const BYTE *src, CharacterEncoding enc) { + switch (enc) { + case SHIFT_JIS: + return zen2han_sjis(src); + case UTF8: + return zen2han_utf8(src); + default: + return (BYTE *)src; + } +} + +BYTE *han2zen(const BYTE *src, CharacterEncoding enc) { + if (enc != SHIFT_JIS) + return (BYTE *)src; // Not implemented + BYTE c0; BYTE *dst, *_dst; dst = _dst = malloc(strlen(src) * 2 + 1); diff --git a/src/hankaku.h b/src/hankaku.h index 5b92cda..9d74f79 100644 --- a/src/hankaku.h +++ b/src/hankaku.h @@ -24,9 +24,10 @@ #define __HANKAKU_H__ #include "portab.h" +#include "utfsjis.h" -extern BYTE *zen2han(const BYTE *src); -extern BYTE *han2zen(const BYTE *src); +extern BYTE *zen2han(const BYTE *src, CharacterEncoding enc); +extern BYTE *han2zen(const BYTE *src, CharacterEncoding enc); extern char *format_number(int n, int width, char *buf); extern char *format_number_zenkaku(int n, int width, char *buf); diff --git a/src/hankaku_test.c b/src/hankaku_test.c index 9feef71..a528cfc 100644 --- a/src/hankaku_test.c +++ b/src/hankaku_test.c @@ -52,9 +52,11 @@ static void zen2han_test(void) { "ベペボポヮヰヱヴヵヶΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩα" "βγδεζηθικλμνξοπρστυφχψω"; for (const Zen2hanTestCase *tc = testcases; tc->zen; tc++) { - ASSERT_STRCMP(sjis2utf(zen2han(utf2sjis(tc->zen))), tc->han); + ASSERT_STRCMP(sjis2utf(zen2han(utf2sjis(tc->zen), SHIFT_JIS)), tc->han); + ASSERT_STRCMP(zen2han(tc->zen, UTF8), tc->han); } - ASSERT_STRCMP(sjis2utf(zen2han(utf2sjis(unconvertable))), unconvertable); + ASSERT_STRCMP(sjis2utf(zen2han(utf2sjis(unconvertable), SHIFT_JIS)), unconvertable); + ASSERT_STRCMP(zen2han(unconvertable, UTF8), unconvertable); } static void format_number_test(void) { diff --git a/src/message.c b/src/message.c index cd83fc2..4579b56 100644 --- a/src/message.c +++ b/src/message.c @@ -326,11 +326,14 @@ void msg_getMessageLocation(MyPoint *loc) { void msg_hitAnyKey() { int w; - static BYTE hak[] = {0x81, 0xa5, 0x00}; /* ▼ */ + const char *prompt[CHARACTER_ENCODING_MAX + 1] = { + [SHIFT_JIS] = "\x81\xa5", + [UTF8] = "▼", + }; w = ags_drawString(msg.win->x + msg.win->width - msg.MsgFontSize, msg.win->y + msg.win->height - msg.MsgFontSize, - hak, msg.HitAnyKeyMsgColor); + prompt[nact->encoding], msg.HitAnyKeyMsgColor); ags_updateArea(msg.win->x + msg.win->width - msg.MsgFontSize, msg.win->y + msg.win->height - msg.MsgFontSize, w, msg.MsgFontSize); diff --git a/src/nact.c b/src/nact.c index be1743f..50da3fe 100644 --- a/src/nact.c +++ b/src/nact.c @@ -70,6 +70,10 @@ void sys_setHankakuMode(int mode) { msg_msgHankakuMode = mode; } +void sys_setCharacterEncoding(CharacterEncoding encoding) { + nact->encoding = encoding; +} + /* 文字列の取り出し */ char *sys_getString(char term) { int c0; @@ -103,9 +107,9 @@ void sys_addMsg(const char *str) { switch(msg_msgHankakuMode) { case 0: - msg = han2zen(str); break; + msg = han2zen(str, nact->encoding); break; case 1: - msg = zen2han(str); break; + msg = zen2han(str, nact->encoding); break; case 2: msg = str; break; default: @@ -126,7 +130,7 @@ void sys_addMsg(const char *str) { } } - if (msg && msg_msgHankakuMode < 2) { + if (msg != str) { free((char *)msg); } } @@ -136,8 +140,10 @@ static int checkMessage() { char *index = msgbuf; int c0 = sl_getc(); - while (c0 == 0x20 || c0 > 0x80) { - if (c0 == 0x20) { + while (c0 == 0x20 || c0 >= 0x80) { + if (nact->encoding == UTF8) { + *index++ = (char)c0; + } else if (c0 == 0x20) { *index++ = (char)c0; } else if (c0 >= 0xe0) { *index++ = (char)c0; *index++ = (char)sl_getc(); diff --git a/src/nact.h b/src/nact.h index 08bdece..77d37f9 100644 --- a/src/nact.h +++ b/src/nact.h @@ -32,6 +32,12 @@ #include "selection.h" #include "message.h" #include "ags.h" +#include "utfsjis.h" + +#define toUTF8(s) codeconv(UTF8, nact->encoding, s) +#define toSJIS(s) codeconv(SHIFT_JIS, nact->encoding, s) +#define fromUTF8(s) codeconv(nact->encoding, UTF8, s) +#define fromSJIS(s) codeconv(nact->encoding, SHIFT_JIS, s) /* コマンド解析時に参照する */ #define sys_getc sl_getc @@ -46,6 +52,7 @@ extern int *getVariable(); extern char *sys_getString(char term); extern void sys_addMsg(const char *str); extern void sys_setHankakuMode(int mode); +extern void sys_setCharacterEncoding(CharacterEncoding encoding); extern char *sys_getConstString(); // extern boolean sys_nact_engine(); @@ -61,6 +68,7 @@ typedef struct { boolean is_message_locked; /* pointer 等の event handler を呼び出さない */ boolean popupmenu_opened; /* popup menu が 開いているか */ boolean mmx_is_ok; /* MMX が有効かどうか */ + CharacterEncoding encoding; char *game_title_utf8; int scenario_version; diff --git a/src/utfsjis.c b/src/utfsjis.c index 1e1f5ee..8f0e52c 100644 --- a/src/utfsjis.c +++ b/src/utfsjis.c @@ -33,6 +33,18 @@ #include "utfsjis.h" #include "s2utbl.h" +char* codeconv(CharacterEncoding tocode, + CharacterEncoding fromcode, + const char *str) { + if (tocode == fromcode) + return strdup(str); + if (tocode == UTF8 && fromcode == SHIFT_JIS) + return (char *)sjis2utf((char *)str); + if (tocode == SHIFT_JIS && fromcode == UTF8) + return (char *)utf2sjis((char *)str); + return NULL; +} + BYTE *sjis2utf(const BYTE *src) { BYTE* dst = malloc(strlen(src) * 3 + 1); BYTE* dstp = dst; @@ -147,15 +159,43 @@ boolean sjis_has_zenkaku(const BYTE *src) { return FALSE; } -/* src 中の文字数を数える 全角文字も1文字 */ -int sjis_count_char(const BYTE *src) { - int c = 0; - - while(*src) { - if (CHECKSJIS1BYTE(*src)) { - src++; - } - c++; src++; +int utf8_next_codepoint(const char **msg) { + int code; + const unsigned char *s = (const unsigned char *)*msg; + + if (*s <= 0x7f) { + code = *s++; + } else if (*s <= 0xbf) { + // Invalid UTF-8 sequence + code = '?'; + s++; + } else if (*s <= 0xdf) { + code = (s[0] & 0x1f) << 6 | (s[1] & 0x3f); + s += 2; + } else if (*s <= 0xef) { + code = (s[0] & 0xf) << 12 | (s[1] & 0x3f) << 6 | (s[2] & 0x3f); + s += 3; + } else if (*s <= 0xf7) { + code = (s[0] & 0x7) << 18 | (s[1] & 0x3f) << 12 | (s[2] & 0x3f) << 6 | (s[3] & 0x3f); + s += 4; + } else { + code = 0xfffd; // REPLACEMENT CHARACTER + s++; + while (0x80 <= *s && *s <= 0xbf) + s++; } - return c; + *msg = (const char *)s; + return code; +} + +char *advance_char(const char *s, CharacterEncoding e) { + switch (e) { + case SHIFT_JIS: + return (char *)s + ((CHECKSJIS1BYTE(*s) && *(s + 1)) ? 2 : 1); + case UTF8: + while (UTF8_TRAIL_BYTE(*++s)) + ; + return (char *)s; + } + return NULL; } diff --git a/src/utfsjis.h b/src/utfsjis.h index 67a1baf..c771d54 100644 --- a/src/utfsjis.h +++ b/src/utfsjis.h @@ -26,12 +26,30 @@ #include "portab.h" +typedef enum { + SHIFT_JIS, + UTF8, + CHARACTER_ENCODING_MAX = UTF8 +} CharacterEncoding; + +#define MAX_SJIS_BYTES_PAR_CHAR 2 +#define MAX_UTF8_BYTES_PAR_CHAR 4 +#define MAX_BYTES_PAR_CHAR(encoding) \ + ((encoding) == SHIFT_JIS ? MAX_SJIS_BYTES_PAR_CHAR : MAX_UTF8_BYTES_PAR_CHAR) + +#define UTF8_TRAIL_BYTE(b) ((signed char)(b) < -0x40) #define CHECKSJIS1BYTE(b) ( ((b) & 0xe0) == 0x80 || ((b) & 0xe0) == 0xe0 ) +extern char* codeconv(CharacterEncoding tocode, + CharacterEncoding fromcode, + const char *str); + extern BYTE* sjis2utf(const BYTE *src); extern BYTE* utf2sjis(const BYTE *src); extern boolean sjis_has_hankaku(const BYTE *src); extern boolean sjis_has_zenkaku(const BYTE *src); -extern int sjis_count_char(const BYTE *src); +extern int utf8_next_codepoint(const char **msg); + +extern char *advance_char(const char *s, CharacterEncoding e); #endif /* __UTFSJIS__ */ diff --git a/src/variable.c b/src/variable.c index 57f24ea..2a43614 100644 --- a/src/variable.c +++ b/src/variable.c @@ -45,7 +45,7 @@ int strvar_len = STRVAR_LEN; static char *advance(const char *s, int n) { while (*s && n > 0) { - s += (CHECKSJIS1BYTE(*s) && *(s + 1)) ? 2 : 1; + s = advance_char(s, nact->encoding); n--; } return (char *)s; @@ -175,7 +175,14 @@ void v_strcat(int no, const char *str) { /* 文字変数の長さ */ size_t v_strlen(int no) { - return sjis_count_char(v_str(no)); + const char *s = v_str(no); + + int c = 0; + while (*s) { + s = advance_char(s, nact->encoding); + c++; + } + return c; } /* Width of a string (2 for full-width characters, 1 for half-width) */ @@ -207,7 +214,7 @@ int v_strstr(int no, int start, const char *str) { return -1; int n = start; while (p < found) { - p += CHECKSJIS1BYTE(*p) ? 2 : 1; + p = advance_char(p, nact->encoding); n++; } return n;