diff --git a/programs/network/ircc/serverparser.inc b/programs/network/ircc/serverparser.inc index 587b8bfec..b3182acb3 100644 --- a/programs/network/ircc/serverparser.inc +++ b/programs/network/ircc/serverparser.inc @@ -158,12 +158,8 @@ skip_parameter: cmp al, ':' jne .part1 -; Skip all trailing spaces +; esi already points to first char after ':', preserve leading spaces .part3: - lodsb - cmp al, ' ' - je .part3 - dec esi ret ; Now, skip all trailing spaces and first semicolon diff --git a/programs/network/ircc/textbox.inc b/programs/network/ircc/textbox.inc index bedaec6fe..540f9a314 100644 --- a/programs/network/ircc/textbox.inc +++ b/programs/network/ircc/textbox.inc @@ -33,10 +33,17 @@ text_insert_newlines: ; esi = ASCIIZ string je .soft_nl cmp al, 10 ; it's a newline, continue onto the next line je .newline + cmp al, 3 ; mIRC color escape - skip args without counting width + je .skip_color + cmp al, 0x20 ; other control char (bold, italic, reset, etc.) - skip without counting + jb .next_byte and al, 0xc0 ; Is it a multi byte UTF8 char? cmp al, 0x80 je .next_byte jmp .more + .skip_color: + call skip_mirc_color_args ; consume the same byte span as renderer's dec_to_esi + jmp .next_byte .newline: inc edx jmp .next_line @@ -279,11 +286,19 @@ draw_channel_text: je @f cmp al, 10 ; hard newline je @f + cmp al, 15 ; 0x0F reset char: default color applies from here forward + je @f cmp al, 3 ; mIRC escape code jne @b cld lea edx, [esi+2] + ; bare \x03 (no digit) = color reset, use default + movzx eax, byte[edx] + sub al, '0' + cmp al, 9 + ja @f ; ecx already holds default here (set above), + ; no explicit reset needed in this branch call dec_to_esi jz @f mov ecx, [irc_colors + 4*esi] @@ -328,9 +343,21 @@ draw_channel_text: cmp byte[edx], 10 je .newline_hard + cmp byte[edx], 15 ; 0x0F: reset all formatting to default + jne .check_colors + inc edx + mov ecx, [colors.work_text] + or ecx, 0x30000000 + jmp .line + .check_colors: cmp byte[edx], 3 ; escape code for mIRC colors jne .no_colors inc edx + ; bare \x03 with no digit = reset color to default + movzx eax, byte[edx] + sub al, '0' + cmp al, 9 + ja .color_reset call dec_to_esi jz .line mov ecx, [irc_colors + 4*esi] @@ -343,6 +370,13 @@ draw_channel_text: jz .line mov edi, [irc_colors + 4*esi] or ecx, 0x40000000 + jmp .line ; skip .no_colors fallthrough; pre-PR fell + ; through and inc'd edx, eating first char + ; of the message after \x03N,N + .color_reset: + mov ecx, [colors.work_text] + or ecx, 0x30000000 + jmp .line .no_colors: ; Some non-printable, just skip it @@ -434,6 +468,68 @@ dec_to_esi: +;---------------------------------- +; Skip mIRC color args after \x03 (forward direction only). +; +; Consumes the exact same byte span as the dec_to_esi-based parsing used by +; draw_channel_text, so width calculation in text_insert_newlines/text_nextline +; stays in sync with what the renderer actually draws: +; +; no digit after \x03 : consume 0 bytes (renderer takes .color_reset) +; N fg digits, value 0..15 : consume N, then optionally +1+M for ',bg' +; (fg=0 is mIRC color 0 = white, fully valid) +; N fg digits, value >= 16 : consume N (renderer's .fail path resets esi +; to 0 and trips jz; ',bg' left untouched) +; +; "No digits" is distinguished from "value 0" by remembering the entry esi on +; the stack and comparing after the fg loop. This is what dec_to_esi cannot do +; (it reuses esi=0 for both fail and value-0), but the renderer side gets away +; with it because the recent pre-check ('cmp al, 9; ja .color_reset') already +; filtered out the no-digit case before the call. +; +; IN: esi = ptr to first byte after \x03 +; OUT: esi = ptr to first byte past the consumed color args +; ebx preserved (callers track state in it); eax clobbered (matches +; dec_to_esi convention; callers reload AL via lodsb on return) +;---------------------------------- +skip_mirc_color_args: + + push ebx + push esi ; remember entry esi to detect "no digits" + xor ebx, ebx ; fg value accumulator (mirrors dec_to_esi's esi) + .fg_loop: + movzx eax, byte[esi] + sub al, '0' + jb .fg_done + cmp al, 9 + ja .fg_done + inc esi + lea ebx, [ebx*4 + ebx] ; ebx * 5 + lea ebx, [ebx*2 + eax] ; ebx * 10 + eax + jmp .fg_loop + .fg_done: + cmp [esp], esi ; esi unchanged? → no fg digits, bare \x03 + je .done ; (renderer took .color_reset, no ',bg') + cmp ebx, 16 ; fg out of range → renderer's .fail path, + jae .done ; ',bg' also left unconsumed + cmp byte[esi], ',' + jne .done + inc esi ; consume ',' + .bg_loop: + movzx eax, byte[esi] + sub al, '0' + jb .done + cmp al, 9 + ja .done + inc esi ; bg digits always consumed regardless of value + jmp .bg_loop + .done: + pop eax ; discard the saved entry esi + pop ebx + ret + + + if TIMESTAMP print_timestamp: diff --git a/programs/network/ircc/window.inc b/programs/network/ircc/window.inc index 14f6a7573..209eb2a7a 100644 --- a/programs/network/ircc/window.inc +++ b/programs/network/ircc/window.inc @@ -226,11 +226,11 @@ window_open: .got_it: lodsb cmp al, ' ' - je .got_it + je .got_it ; skip spaces before the colon cmp al, ':' - je .got_it - dec esi - + je .set_window ; found colon, stop here - preserve leading spaces of message + dec esi ; not space or colon, put it back + .set_window: mov [window_print], ebx ret