From e5866d6ba4f2d5d12f54e3ac8d53ff9cacd569bc Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Sat, 4 Jul 2026 22:00:24 +0300 Subject: [PATCH] =?UTF-8?q?mdview2:=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=B0=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B7=D0=B8=D1=86=D0=B8=D1=8F=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D0=BB=D1=8E=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=D1=85=20F2=20(MD=E2=86=94RAW)=20=D0=B8=20F8?= =?UTF-8?q?=20(8bit=E2=86=94UTF-8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Валюта позиции — байт-offset активного буфера: - line_at_off(off): обратный перевод offset → MD-строка (бинарный поиск по seg_off, оффсеты сегментов монотонны); - map_off(off, from, to): пропорциональный перенос позиции между буферами разного размера — один цикл restoring-деления, без __mullong/__divulong, точность from/65536; - raw_pos()/raw_reanchor(off) в RAW-модуле; raw_seed_from через reanchor, raw_home стал приватным (только клавиша Home). F2 RAW→MD: top_line = line_at_off(raw_pos()) — точное позиционирование. F8 между готовыми наборами: view_pos → map_off → view_reanchor вместо восстановления сохранённой позиции набора. Ленивая сборка — по-прежнему с начала (в RAW с raw_reanchor(0) и откатом при неудаче). Попутно: F8 в RAW-режиме больше не рисует MD-вид поверх RAW — перерисовка по g_view. exe 25100 → 25725 (+625). Проверено в MAME. Co-Authored-By: Claude Fable 5 --- examples/mdview2/mdview2.c | 72 +++++++++++++++++++++++++++++--- examples/mdview2/mdview2.h | 6 ++- examples/mdview2/mdview2_index.c | 14 +++++++ examples/mdview2/mdview2_raw.c | 22 +++++++--- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/examples/mdview2/mdview2.c b/examples/mdview2/mdview2.c index f4cd485..85793e4 100644 --- a/examples/mdview2/mdview2.c +++ b/examples/mdview2/mdview2.c @@ -826,6 +826,43 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible) return 0; } +/* Пропорциональный перенос байт-позиции между буферами разного размера + * (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один + * цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k) + * сразу добавляет to/2^k в результат — сумма to/2^k по установленным + * битам и есть off*to/from. Точность from/65536 (≤4 байта на максимальном + * файле); ляжет на границу строки через line_at_off / raw_reanchor. */ +static uint32_t map_off(uint32_t off, uint32_t from, uint32_t to) +{ + if (from == to || from == 0) return off; + uint32_t res = 0; + for (uint8_t i = 0; i < 16; i++) { + from >>= 1; + to >>= 1; + if (off >= from) { off -= from; res += to; } + } + return res; +} + +/* Байт-позиция текущего вида (верх экрана) — единая «валюта» для переноса + * позиции между видами/наборами. */ +static uint32_t view_pos(void) +{ +#if WITH_RAW + if (g_view != VIEW_MD) return raw_pos(); +#endif + return seg_off(top_line); +} + +/* Поставить текущий вид на байт-позицию off (после doc_switch). */ +static void view_reanchor(uint32_t off) +{ +#if WITH_RAW + if (g_view != VIEW_MD) { raw_reanchor(off); return; } +#endif + top_line = line_at_off(off); +} + /* Переключает кодировку на `next` (вызывается из главного цикла по F8). * 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор * — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту @@ -842,14 +879,23 @@ static void switch_encoding(uint8_t next) set_encoding(next); g_doc[target].enc = next; } else if (g_doc[target].built) { /* набор готов — мгновенный свап */ - doc_switch(target); + /* Позицию переносим пропорцией байтов (буферы наборов разного + * размера), а не из сохранённой в g_doc копии — иначе каждый набор + * скачет по своей старой позиции. */ + uint32_t old_off = view_pos(); + uint32_t old_size = file_size; + doc_switch(target); g_disp_doc = target; - set_encoding(next); + set_encoding(next); g_doc[target].enc = next; + view_reanchor(map_off(old_off, old_size, file_size)); } else { /* строим набор лениво */ doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */ #if WITH_RAW - g_ready = 0; + uint32_t raw_save = raw_pos(); + if (g_view != VIEW_MD) + raw_reanchor(0); /* прогрессивный показ нового буфера — с начала */ + g_ready = 0; render_menu(); /* идёт индексация — прячем F2 */ #endif spinner_show(1); @@ -861,9 +907,13 @@ static void switch_encoding(uint8_t next) render_menu(); #endif if (rc < 0) { /* нет EMM — откат на текущий набор */ - if (target == DOC_UTF8) + if (target == DOC_UTF8) g_utf_failed = 1; doc_load_live(g_active_doc); +#if WITH_RAW + if (g_view != VIEW_MD) + raw_reanchor(raw_save); /* буфер прежний — вернуть позицию RAW */ +#endif return; } g_disp_doc = target; /* build_doc уже выставил active/enc */ @@ -1036,6 +1086,7 @@ int main(int argc, char **argv) } else { g_raw_last = g_view; /* запомнить под-режим RAW */ g_view = VIEW_MD; + top_line = line_at_off(raw_pos()); /* позиция из RAW */ clamp_top(); render_full_status(); render_menu(); draw_viewport_from_cache(); @@ -1050,9 +1101,18 @@ int main(int argc, char **argv) if (next == ENC_UTF8 && g_utf_failed) next = ENC_CP866; /* UTF-8 недоступен */ switch_encoding(next); - if (g_abort) + if (g_abort) exit(0); /* Esc/F10 во время ленивой досборки */ - render_full_status(); /* перерисовка — в MD-режиме */ + /* Перерисовка — по текущему виду (switch_encoding сам не рисует). */ +#if WITH_RAW + if (g_view != VIEW_MD) { + status_encoding(); + render_raw_status(); + raw_draw(); + continue; + } +#endif + render_full_status(); draw_viewport_from_cache(); continue; } diff --git a/examples/mdview2/mdview2.h b/examples/mdview2/mdview2.h index b40cec7..ece41f9 100644 --- a/examples/mdview2/mdview2.h +++ b/examples/mdview2/mdview2.h @@ -139,12 +139,14 @@ void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, void fill_row(uint8_t y, uint8_t attr); uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */ uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */ +uint16_t line_at_off(uint32_t off); /* обратно: последняя строка с seg_off <= off */ /* ================================================================== * * API модуля RAW (mdview2_raw.c). * ================================================================== */ -void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */ -void raw_home(void); /* сброс в начало (смена буфера при F8 в/из UTF-8) */ +void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */ +uint32_t raw_pos(void); /* текущий байт-offset верха экрана RAW */ +void raw_reanchor(uint32_t off); /* поставить RAW на строку с байтом off */ void raw_draw(void); /* перерисовать область документа в RAW */ uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */ uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */ diff --git a/examples/mdview2/mdview2_index.c b/examples/mdview2/mdview2_index.c index c79d0bc..96db5a3 100644 --- a/examples/mdview2/mdview2_index.c +++ b/examples/mdview2/mdview2_index.c @@ -98,6 +98,20 @@ uint32_t seg_off(uint16_t idx) return r.off; } +/* Последняя строка индекса с исходным offset <= off — обратный перевод + * байт-позиции в MD-строку (позиционирование при F2/F8). Оффсеты сегментов + * монотонны, поэтому бинарный поиск (~15 bank-чтений). */ +uint16_t line_at_off(uint32_t off) +{ + uint16_t lo = 0, hi = n_lines; + while (lo < hi) { + uint16_t mid = (uint16_t)((lo + hi) >> 1); + if (seg_off(mid) <= off) lo = (uint16_t)(mid + 1); + else hi = mid; + } + return (lo > 0) ? (uint16_t)(lo - 1) : 0; +} + /* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1). * Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); } diff --git a/examples/mdview2/mdview2_raw.c b/examples/mdview2/mdview2_raw.c index 2fdd1b6..db2be7b 100644 --- a/examples/mdview2/mdview2_raw.c +++ b/examples/mdview2/mdview2_raw.c @@ -173,17 +173,27 @@ uint8_t calc_raw_pct(void) /* ---- посев позиции / пересев при смене активного буфера ----------- */ -void raw_seed_from(uint16_t md_top_line) +uint32_t raw_pos(void) { - uint32_t off = seg_off(md_top_line); - if (off > file_size) off = 0; + return raw_top; +} + +/* Поставить RAW на начало строки, содержащей байт off (позиционирование + * при F2 MD→RAW и F8-переносе позиции между наборами). */ +void raw_reanchor(uint32_t off) +{ + if (off > file_size) off = file_size; raw_top = raw_line_start(off); raw_hpan = 0; } -/* Сброс в начало — при смене активного буфера (F8 в/из UTF-8: размеры/offset'ы - * разные, старый raw_top невалиден). Без percent-математики. */ -void raw_home(void) +void raw_seed_from(uint16_t md_top_line) +{ + raw_reanchor(seg_off(md_top_line)); +} + +/* Сброс в начало (клавиша Home). */ +static void raw_home(void) { raw_top = 0; raw_hpan = 0;