apps/ircc: refactor color-arg skipping, fix width/render desync

- Factor duplicated .skip_color from text_insert_newlines and
  text_nextline into a shared skip_mirc_color_args matching the
  renderer's dec_to_esi byte span.
- Guard text_nextline backward scans (DF check) so forward-parsing
  \x03 args doesn't corrupt esi during scroll-up.
- Document the silent jmp .line that stops .no_colors from eating
  the byte after \x03N,N.
- Document the .color_reset branch for bare \x03.
This commit is contained in:
2026-05-20 12:29:27 +03:00
parent c449fb9d71
commit eae9ab3518
+81 -56
View File
@@ -42,33 +42,7 @@ text_insert_newlines: ; esi = ASCIIZ string
je .next_byte
jmp .more
.skip_color:
lodsb ; optional fg color digit(s)
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
lodsb ; optional 2nd fg digit
cmp al, '0'
jb .skip_color_comma
cmp al, '9'
ja .skip_color_comma
lodsb ; char after 2 fg digits
.skip_color_comma:
cmp al, ',' ; optional background color?
jne .skip_color_done
lodsb ; optional bg color digit(s)
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
lodsb ; optional 2nd bg digit
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
jmp .next_byte
.skip_color_done:
dec esi
call skip_mirc_color_args ; consume the same byte span as renderer's dec_to_esi
jmp .next_byte
.newline:
inc edx
@@ -122,34 +96,20 @@ text_nextline:
.done:
ret
.skip_color:
lodsb
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
lodsb
cmp al, '0'
jb .skip_color_comma
cmp al, '9'
ja .skip_color_comma
lodsb
.skip_color_comma:
cmp al, ','
jne .skip_color_done
lodsb
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
lodsb
cmp al, '0'
jb .skip_color_done
cmp al, '9'
ja .skip_color_done
jmp .loop
.skip_color_done:
dec esi
; In backward scans (called with DF set from draw_channel_text scroll loop),
; color args precede \x03 in buffer and have already been counted as plain
; chars. Forward-parsing them here would corrupt esi, so fall back to the
; pre-PR behavior of treating \x03 as a regular 1-width character.
pushfd
pop eax ; AL is reloaded by lodsb on .loop re-entry
test eax, 1 shl 10 ; DF set = backward direction
jnz .skip_color_backward
call skip_mirc_color_args
jmp .loop
.skip_color_backward:
dec ecx
jnz .loop
ret ; ecx hit 0: same as .done fallthrough
;----------------------------------
; print string
@@ -356,7 +316,8 @@ draw_channel_text:
movzx eax, byte[edx]
sub al, '0'
cmp al, 9
ja @f
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]
@@ -428,7 +389,9 @@ draw_channel_text:
jz .line
mov edi, [irc_colors + 4*esi]
or ecx, 0x40000000
jmp .line
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
@@ -524,6 +487,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: