Reproduced under Win7:
- move 2 squares left until Ibun gets in LOS
- reduce Term-1 width (monster list window) until the name becomes "[U] Ibun, Son of Mî (asleep) 2 S 6 W"
- game crashes
I compiled the source using my BC++5 environment and launched the exe in debug mode. Result is as follows: project angband.exe crashes with EAccessViolation at address 0051551B, reading address 11111119.
Here's the list of function calls at the moment of the crash:
- update_monlist_subwindow(EVENT_MONSTERLIST)
- monster_list_show_subwindow(16, 39)
- monster_list_format_textblock(list, textblock, 16, 39, ...)
- monster_list_format_section(list, textblock, MONSTER_LIST_SECTION_LOS, 1, 39, ...)
- textblock_append_c(tb, line_attr, "%-*s%s\n", full_width = 57, line_buffer = "[U] Ibun, Son of MÃ (asleep)", location = " 2 S 6 W") [line 419]
- textblock_vappend_c(tb, attr, fmt, vp)
At this point, a malloc-ed string temp_space is created from the va_args: temp_space = "[U] Ibun, Son of MÃ (asleep) 2 S 6 W\n".
Next call is Term_mbstowcs(NULL, temp_space, 0) to get the new length, but this call fails. The hook in Term_mbstowcs calls Term_mbstowcs_win from main-win.c: return (size_t)(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0) - 1);
Here the MultiByteToWideChar function fails (error code = 1113, ERROR_NO_UNICODE_TRANSLATION, "No mapping for the Unicode character exists in the target multi-byte code page") and returns 0. In this case, Term_mbstowcs returns -1, which is used as the new length. The following call tries to resize the textblock to an invalid size (-1 as a u32 value!) and fails with a crash...
Testing a little more in debug mode, it's easy to trace down the problem. Putting a breakpoint in update_monlist_subwindow, I was able to test the faulty code with term width = 40 then term width = 39. And here it is:
Code:
/* Clip the monster name to fit, and append the sleep tag. */
name_width = MIN(full_width - strlen(asleep), sizeof(line_buffer));
get_mon_name(line_buffer, name_width + 1, list->entries[entry_index].race, list->entries[entry_index].count[section]);
my_strcat(line_buffer, asleep, sizeof(line_buffer));
When term width = 40, everything works fine:
- name_width = 20
- line_buffer = "[U] Ibun, Son of Mî"
When term width = 39, the name is truncated!
- name_width = 19
- line_buffer = "[U] Ibun, Son of MÃ"
Then of course, the last character is not a valid unicode character anymore...
Fix: in get_mon_name(), ensure that the last character, if it's an unicode character, is not truncated.