ui: descriptive check-results headers with the check type first
- new profilerResultsHeaderText() builds the header from a structured
{kind, from, to}: profile checks read 'Profile verification results
for: <profile> -> <card ICCID | snapshot name>', snapshot comparison
reads 'Snapshot comparison results: <master> -> <checked>' (the arrow
matches the one already used in the compare header)
- profilerRunProfile() stores the header and profilerRenderResultsView()
writes it, so the header follows a language switch and the ICCID stays
current; RU keys added ('Результаты проверки профиля:',
'Результаты сравнения снимков:')
- tests: header builder variants + render wiring; flow tests now expect
header objects; help EN/RU and READMEs updated; SW cache v129 -> v130.
This commit is contained in:
@@ -514,7 +514,7 @@ A filesystem rule is defined by:
|
|||||||
- **File attributes** — file type, size, record length and record count, taken from the FCP template (any may be left unset).
|
- **File attributes** — file type, size, record length and record count, taken from the FCP template (any may be left unset).
|
||||||
- **Check contents** (optional) — **Exact** hex equality, or **Mask** where `?` is a per-nibble wildcard (a mask with no `?` is a prefix match, e.g. `0891` for the IMSI MCC/MNC). Record files store a per-record list.
|
- **Check contents** (optional) — **Exact** hex equality, or **Mask** where `?` is a per-nibble wildcard (a mask with no `?` is a prefix match, e.g. `0891` for the IMSI MCC/MNC). Record files store a per-record list.
|
||||||
|
|
||||||
The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results title shows `profile — ICCID` / `profile — snapshot`. **Only mismatches** in the results header hides all passing files and keeps failures and errors only.
|
The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results header reads `Profile verification results for: <profile> → <card ICCID>` (or `… → <snapshot name>`; snapshot comparison: `Snapshot comparison results: <master> → <checked>`). **Only mismatches** in the results header hides all passing files and keeps failures and errors only.
|
||||||
|
|
||||||
#### “Profile from card” scan options
|
#### “Profile from card” scan options
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -488,7 +488,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на
|
|||||||
- **Атрибуты файла** — тип, размер, длина и число записей из шаблона FCP (любое можно не задавать).
|
- **Атрибуты файла** — тип, размер, длина и число записей из шаблона FCP (любое можно не задавать).
|
||||||
- **Проверка содержимого** (опционально) — **Exact** (точное равенство hex) или **Mask**, где `?` — пониббловый джокер (маска без `?` — префиксное совпадение, напр. `0891` для MCC/MNC IMSI). Для record-файлов хранится список записей.
|
- **Проверка содержимого** (опционально) — **Exact** (точное равенство hex) или **Mask**, где `?` — пониббловый джокер (маска без `?` — префиксное совпадение, напр. `0891` для MCC/MNC IMSI). Для record-файлов хранится список записей.
|
||||||
|
|
||||||
Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `профиль — ICCID` / `профиль — снимок`. Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки.
|
Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `Результаты проверки профиля: <профиль> → <ICCID карты>` (или `… → <имя снимка>`; для сравнения снимков — `Результаты сравнения снимков: <эталон> → <проверяемый>`). Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки.
|
||||||
|
|
||||||
#### Опции сканирования «Профиль с карты»
|
#### Опции сканирования «Профиль с карты»
|
||||||
|
|
||||||
|
|||||||
@@ -387,7 +387,7 @@
|
|||||||
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
|
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
|
||||||
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
|
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта показываются имя профиля и ICCID карты. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
|
<p class="text-sm mb-3"><strong>Проверить карту</strong> выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, <em>тип файла и размер, содержимое</em> или <em>полный FCI</em>); если часть проверок прошла, а часть нет — каждый аспект помечается (<em>тип файла ✓, размер ✗, содержимое ✓</em>), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка <em>совпадающие записи: 1-5, 7-10</em> со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны <em>ожидалось (имя профиля)</em> и <em>фактически (ICCID карты)</em>, а в заголовке отчёта выводится <em>Результаты проверки профиля: <профиль> → <ICCID карты></em>. Опция <strong>«Только расхождения»</strong> в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.</p>
|
||||||
<h4 class="font-medium mb-1">Опции сканирования «Профиль с карты»</h4>
|
<h4 class="font-medium mb-1">Опции сканирования «Профиль с карты»</h4>
|
||||||
<p class="text-sm mb-2">Диалог сканирования запрашивает имя профиля и предлагает селектор <strong>«Проверка FCP/FCI»</strong> (те же три режима, по умолчанию <strong>Тип файла + размер (FCP)</strong>), применяемый ко всем создаваемым правилам, а также список <strong>«Игнорировать содержимое файлов»</strong> (все отмечены по умолчанию, кроме <code class="font-mono text-sm">EF.ARR</code>; флажок в заголовке отмечает или снимает весь список) часто перезаписываемых файлов, содержимое которых пропускается: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Ещё две отмеченные по умолчанию опции <strong>«Сравнивать первые 4 байта для»</strong> <code class="font-mono text-sm">EF.IMSI</code> и <code class="font-mono text-sm">EF.ICCID</code> захватывают содержимое этих файлов как маску только первых 4 байт (снимите для точного сравнения). Строка прогресса показывает <em>N / всего файлов</em> с текущим путём файла во время сканирования; при сканировании опции скрываются, а кнопки блокируются. Правила создаются только для файлов, которые реально существуют на карте (возвращён FCP-шаблон); отсутствующие файлы пропускаются. Пользовательские файлы из подвкладки <strong>«Пользовательские файлы»</strong> включаются с той же проверкой существования.</p>
|
<p class="text-sm mb-2">Диалог сканирования запрашивает имя профиля и предлагает селектор <strong>«Проверка FCP/FCI»</strong> (те же три режима, по умолчанию <strong>Тип файла + размер (FCP)</strong>), применяемый ко всем создаваемым правилам, а также список <strong>«Игнорировать содержимое файлов»</strong> (все отмечены по умолчанию, кроме <code class="font-mono text-sm">EF.ARR</code>; флажок в заголовке отмечает или снимает весь список) часто перезаписываемых файлов, содержимое которых пропускается: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Ещё две отмеченные по умолчанию опции <strong>«Сравнивать первые 4 байта для»</strong> <code class="font-mono text-sm">EF.IMSI</code> и <code class="font-mono text-sm">EF.ICCID</code> захватывают содержимое этих файлов как маску только первых 4 байт (снимите для точного сравнения). Строка прогресса показывает <em>N / всего файлов</em> с текущим путём файла во время сканирования; при сканировании опции скрываются, а кнопки блокируются. Правила создаются только для файлов, которые реально существуют на карте (возвращён FCP-шаблон); отсутствующие файлы пропускаются. Пользовательские файлы из подвкладки <strong>«Пользовательские файлы»</strong> включаются с той же проверкой существования.</p>
|
||||||
|
|
||||||
@@ -397,8 +397,8 @@
|
|||||||
<li><strong>Новый снимок</strong> — запрашивает имя и сканирует карту, затем возвращает к списку.</li>
|
<li><strong>Новый снимок</strong> — запрашивает имя и сканирует карту, затем возвращает к списку.</li>
|
||||||
<li><strong>Импорт снимка</strong> — загружает снимок из JSON-файла.</li>
|
<li><strong>Импорт снимка</strong> — загружает снимок из JSON-файла.</li>
|
||||||
<li>В каждой строке снимка — <strong>Открыть</strong>, <strong>Экспорт</strong> и <strong>Удалить</strong>. <strong>Открыть</strong> показывает все захваченные данные только для чтения (сырой FCI с декодированным FCI, содержимое); редактируется только имя снимка.</li>
|
<li>В каждой строке снимка — <strong>Открыть</strong>, <strong>Экспорт</strong> и <strong>Удалить</strong>. <strong>Открыть</strong> показывает все захваченные данные только для чтения (сырой FCI с декодированным FCI, содержимое); редактируется только имя снимка.</li>
|
||||||
<li><strong>Проверить снимок карты</strong> в строке профиля выполняет правила профиля на выбранном из списка снимке, без картридера. Отчёт такой же, как при проверке карты, но фактическая сторона подписана именем снимка (<em>фактически (имя снимка)</em>); файлы, содержимое которых не было захвачено при сканировании, помечаются как непроверяемые ошибки.</li>
|
<li><strong>Проверить снимок карты</strong> в строке профиля выполняет правила профиля на выбранном из списка снимке, без картридера. Отчёт такой же, как при проверке карты: фактическая сторона подписана именем снимка (<em>фактически (имя снимка)</em>), а в заголовке — <em>Результаты проверки профиля: <профиль> → <имя снимка></em>; файлы, содержимое которых не было захвачено при сканировании, помечаются как непроверяемые ошибки.</li>
|
||||||
<li><strong>Сравнить снимки</strong> сравнивает два снимка без картридера так же, как проверка профиля: выберите <em>эталонный</em> снимок и <em>снимок для проверки</em>, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт; в этом отчёте поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».</li>
|
<li><strong>Сравнить снимки</strong> сравнивает два снимка без картридера так же, как проверка профиля: выберите <em>эталонный</em> снимок и <em>снимок для проверки</em>, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт; в этом отчёте заголовок — <em>Результаты сравнения снимков: <эталон> → <проверяемый></em>, а поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+3
-3
@@ -387,7 +387,7 @@
|
|||||||
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
||||||
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results title shows the profile name and the card ICCID. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
|
<p class="text-sm mb-3"><strong>Check card</strong> runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. <em>filetype and size, contents</em> or <em>exact FCI</em>); when some checks pass and others fail, each aspect is marked (<em>filetype ✓, size ✗, contents ✓</em>) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a <em>matching records: 1-5, 7-10</em> note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled <em>expected (profile name)</em> and <em>actual (card ICCID)</em>, and the results header reads <em>Profile verification results for: <profile> → <card ICCID></em>. An <strong>Only mismatches</strong> option in the results header hides all passing files and keeps only failures and errors.</p>
|
||||||
<h4 class="font-medium mb-1">“Profile from card” scan options</h4>
|
<h4 class="font-medium mb-1">“Profile from card” scan options</h4>
|
||||||
<p class="text-sm mb-2">The scan dialog asks for a profile name and offers a <strong>“FCP/FCI check”</strong> selector (the same three modes above, default <strong>Filetype + size</strong>) applied to every generated rule, plus an <strong>“Ignore contents of files”</strong> checklist (all checked by default except <code class="font-mono text-sm">EF.ARR</code>; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Two further checked-by-default options <strong>“Match first 4 bytes for”</strong> <code class="font-mono text-sm">EF.IMSI</code> and <code class="font-mono text-sm">EF.ICCID</code> capture those files’ contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows <em>N / total files</em> with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the <strong>Custom files</strong> sub-tab are included under the same existence check.</p>
|
<p class="text-sm mb-2">The scan dialog asks for a profile name and offers a <strong>“FCP/FCI check”</strong> selector (the same three modes above, default <strong>Filetype + size</strong>) applied to every generated rule, plus an <strong>“Ignore contents of files”</strong> checklist (all checked by default except <code class="font-mono text-sm">EF.ARR</code>; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: <code class="font-mono text-sm">EF.LOCI</code>, <code class="font-mono text-sm">EF.PSLOCI</code>, <code class="font-mono text-sm">EF.EPSLOCI</code>, <code class="font-mono text-sm">EF.5GS3GPPLOCI</code>, <code class="font-mono text-sm">EF.Keys</code>, <code class="font-mono text-sm">EF.KeysPS</code>, <code class="font-mono text-sm">EF.SMS</code>, <code class="font-mono text-sm">EF.Kc</code>, <code class="font-mono text-sm">EF.KcGPRS</code>, <code class="font-mono text-sm">EF.LOCIGPRS</code>, <code class="font-mono text-sm">EF.CBMID</code>, <code class="font-mono text-sm">EF.SMSS</code>, <code class="font-mono text-sm">EF.ACC</code>, <code class="font-mono text-sm">EF.EPSNSC</code>, <code class="font-mono text-sm">EF.START-HFN</code>, <code class="font-mono text-sm">EF.ARR</code>. Two further checked-by-default options <strong>“Match first 4 bytes for”</strong> <code class="font-mono text-sm">EF.IMSI</code> and <code class="font-mono text-sm">EF.ICCID</code> capture those files’ contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows <em>N / total files</em> with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the <strong>Custom files</strong> sub-tab are included under the same existence check.</p>
|
||||||
|
|
||||||
@@ -397,8 +397,8 @@
|
|||||||
<li><strong>New snapshot</strong> — asks for a name and scans the card, then returns to the list.</li>
|
<li><strong>New snapshot</strong> — asks for a name and scans the card, then returns to the list.</li>
|
||||||
<li><strong>Import snapshot</strong> — loads a snapshot from a JSON file.</li>
|
<li><strong>Import snapshot</strong> — loads a snapshot from a JSON file.</li>
|
||||||
<li>Each snapshot row has <strong>Open</strong>, <strong>Export</strong>, and <strong>Delete</strong>. <strong>Open</strong> shows all captured data read-only (raw FCI with the decoded FCI, contents); only the snapshot name is editable.</li>
|
<li>Each snapshot row has <strong>Open</strong>, <strong>Export</strong>, and <strong>Delete</strong>. <strong>Open</strong> shows all captured data read-only (raw FCI with the decoded FCI, contents); only the snapshot name is editable.</li>
|
||||||
<li><strong>Check card snapshot</strong> on a profile row runs the profile rules against a snapshot you pick from the list, without a card reader. The report is the same as a live check, but the actual side is labelled with the snapshot name (<em>actual (snapshot name)</em>); files whose contents were not captured during the scan are reported as unverifiable errors.</li>
|
<li><strong>Check card snapshot</strong> on a profile row runs the profile rules against a snapshot you pick from the list, without a card reader. The report is the same as a live check: the actual side is labelled with the snapshot name (<em>actual (snapshot name)</em>) and the header reads <em>Profile verification results for: <profile> → <snapshot name></em>; files whose contents were not captured during the scan are reported as unverifiable errors.</li>
|
||||||
<li><strong>Compare snapshots</strong> compares two snapshots offline, exactly like a profile check: pick the <em>master</em> snapshot and the <em>snapshot to check</em>, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report; in that report the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.</li>
|
<li><strong>Compare snapshots</strong> compares two snapshots offline, exactly like a profile check: pick the <em>master</em> snapshot and the <em>snapshot to check</em>, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report; in that report the header reads <em>Snapshot comparison results: <master> → <checked></em> and the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+15
-5
@@ -7404,6 +7404,7 @@ let profilerEditId = null;
|
|||||||
let profilerDraft = null;
|
let profilerDraft = null;
|
||||||
let profilerResults = null;
|
let profilerResults = null;
|
||||||
let profilerResultsLabels = null;
|
let profilerResultsLabels = null;
|
||||||
|
let profilerResultsHeader = null;
|
||||||
let profilerMismatchOnly = false;
|
let profilerMismatchOnly = false;
|
||||||
let snapshots = [];
|
let snapshots = [];
|
||||||
let snapshotViewId = null;
|
let snapshotViewId = null;
|
||||||
@@ -8440,13 +8441,13 @@ async function profilerCardIccid() {
|
|||||||
async function profilerCheck(i) {
|
async function profilerCheck(i) {
|
||||||
const p = profiles[i];
|
const p = profiles[i];
|
||||||
const iccid = await profilerCardIccid();
|
const iccid = await profilerCardIccid();
|
||||||
await profilerRunProfile(p, null, iccid ? p.name + ' — ' + iccid : p.name, null,
|
await profilerRunProfile(p, null, { kind: 'profile', from: p.name, to: iccid }, null,
|
||||||
{ expected: p.name, actual: iccid, prefix: true });
|
{ expected: p.name, actual: iccid, prefix: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function profilerRunProfile(p, source, title, extraResults, labels) {
|
async function profilerRunProfile(p, source, header, extraResults, labels) {
|
||||||
profilerSetView('results');
|
profilerSetView('results');
|
||||||
document.getElementById('profiler-results-title').textContent = title;
|
profilerResultsHeader = header || null;
|
||||||
const prog = document.getElementById('profiler-progress');
|
const prog = document.getElementById('profiler-progress');
|
||||||
document.getElementById('profiler-summary').innerHTML = '';
|
document.getElementById('profiler-summary').innerHTML = '';
|
||||||
document.getElementById('profiler-report').innerHTML = '';
|
document.getElementById('profiler-report').innerHTML = '';
|
||||||
@@ -8483,7 +8484,7 @@ async function profilerCheckSnapshot(i, si) {
|
|||||||
document.getElementById('snapshot-pick-modal').classList.add('hidden');
|
document.getElementById('snapshot-pick-modal').classList.add('hidden');
|
||||||
const p = profiles[i];
|
const p = profiles[i];
|
||||||
const s = snapshots[si];
|
const s = snapshots[si];
|
||||||
await profilerRunProfile(p, profilerSnapshotSource(s), p.name + ' — ' + s.name, null,
|
await profilerRunProfile(p, profilerSnapshotSource(s), { kind: 'profile', from: p.name, to: s.name }, null,
|
||||||
{ expected: p.name, actual: s.name, prefix: true });
|
{ expected: p.name, actual: s.name, prefix: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8571,7 +8572,7 @@ async function snapshotCompareRun() {
|
|||||||
});
|
});
|
||||||
document.getElementById('snapshot-compare-modal').classList.add('hidden');
|
document.getElementById('snapshot-compare-modal').classList.add('hidden');
|
||||||
const pseudo = { name: master.name + ' → ' + check.name, rules: profilerRulesFromSnapshot(master, maskFids) };
|
const pseudo = { name: master.name + ' → ' + check.name, rules: profilerRulesFromSnapshot(master, maskFids) };
|
||||||
await profilerRunProfile(pseudo, profilerSnapshotSource(check), pseudo.name, profilerExtraFileResults(master, check),
|
await profilerRunProfile(pseudo, profilerSnapshotSource(check), { kind: 'snapshot', from: master.name, to: check.name }, profilerExtraFileResults(master, check),
|
||||||
{ expected: master.name, actual: check.name });
|
{ expected: master.name, actual: check.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8584,8 +8585,15 @@ function profilerVisibleResults(results, mismatchOnly) {
|
|||||||
return mismatchOnly ? results.filter(r => r.status !== 'pass') : results;
|
return mismatchOnly ? results.filter(r => r.status !== 'pass') : results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function profilerResultsHeaderText(header) {
|
||||||
|
if (!header || !header.from) return '';
|
||||||
|
const base = header.kind === 'snapshot' ? t('Snapshot comparison results:') : t('Profile verification results for:');
|
||||||
|
return base + ' ' + header.from + (header.to ? ' \u2192 ' + header.to : '');
|
||||||
|
}
|
||||||
|
|
||||||
function profilerRenderResultsView() {
|
function profilerRenderResultsView() {
|
||||||
if (!profilerResults) return;
|
if (!profilerResults) return;
|
||||||
|
document.getElementById('profiler-results-title').textContent = profilerResultsHeaderText(profilerResultsHeader);
|
||||||
let passed = 0, failed = 0, errors = 0;
|
let passed = 0, failed = 0, errors = 0;
|
||||||
for (const res of profilerResults) {
|
for (const res of profilerResults) {
|
||||||
if (res.status === 'pass') passed++;
|
if (res.status === 'pass') passed++;
|
||||||
@@ -9276,6 +9284,8 @@ const LANG_RU = {
|
|||||||
'Step': 'Шаг',
|
'Step': 'Шаг',
|
||||||
'Install OK': 'Установка OK',
|
'Install OK': 'Установка OK',
|
||||||
'Install FAILED at step:': 'Установка не удалась на шаге:',
|
'Install FAILED at step:': 'Установка не удалась на шаге:',
|
||||||
|
'Profile verification results for:': 'Результаты проверки профиля:',
|
||||||
|
'Snapshot comparison results:': 'Результаты сравнения снимков:',
|
||||||
'RAM operations perform atomic GlobalPlatform commands over SCP80. Card keys are taken from the saved preset.': 'RAM-операции выполняют атомарные команды GlobalPlatform через SCP80. Ключи карты берутся из сохранённого пресета.',
|
'RAM operations perform atomic GlobalPlatform commands over SCP80. Card keys are taken from the saved preset.': 'RAM-операции выполняют атомарные команды GlobalPlatform через SCP80. Ключи карты берутся из сохранённого пресета.',
|
||||||
'No cards defined.': 'Карты не заданы.',
|
'No cards defined.': 'Карты не заданы.',
|
||||||
'Card presets': 'Выбор карты',
|
'Card presets': 'Выбор карты',
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'otaman-v129';
|
const CACHE = 'otaman-v130';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ function extractFunc(src, name, asyncFn) {
|
|||||||
return (asyncFn ? 'async ' : '') + src.slice(m.index, i + 1);
|
return (asyncFn ? 'async ' : '') + src.slice(m.index, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerMaskFidForFile', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText'];
|
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerMaskFidForFile', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView'];
|
||||||
let code = '';
|
let code = '';
|
||||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||||
code += extractFunc(html, 'profilerBuildFileRule', true) + '\n';
|
code += extractFunc(html, 'profilerBuildFileRule', true) + '\n';
|
||||||
@@ -31,7 +31,7 @@ code += extractFunc(html, 'profilerBuildSnapshotFile', true) + '\n';
|
|||||||
code += extractFunc(html, 'profilerCardIccid', true) + '\n';
|
code += extractFunc(html, 'profilerCardIccid', true) + '\n';
|
||||||
code += extractFunc(html, 'profilerCheck', true) + '\n';
|
code += extractFunc(html, 'profilerCheck', true) + '\n';
|
||||||
code += extractFunc(html, 'profilerCheckSnapshot', true) + '\n';
|
code += extractFunc(html, 'profilerCheckSnapshot', true) + '\n';
|
||||||
code += "var _scanTarget = 'profile';\n";
|
code += "var _scanTarget = 'profile';\nvar profilerResults = null;\nvar profilerResultsHeader = null;\nvar profilerMismatchOnly = false;\n";
|
||||||
code += html.match(/const PROFILER_MASK_PREFIX4_FIDS = \{[\s\S]*?\n\};/)[0] + '\n';
|
code += html.match(/const PROFILER_MASK_PREFIX4_FIDS = \{[\s\S]*?\n\};/)[0] + '\n';
|
||||||
eval(code);
|
eval(code);
|
||||||
|
|
||||||
@@ -1359,7 +1359,7 @@ test('profilerCheck titles with the card ICCID and passes prefixed labels', asyn
|
|||||||
let captured = null;
|
let captured = null;
|
||||||
global.profilerRunProfile = async (...args) => { captured = args; };
|
global.profilerRunProfile = async (...args) => { captured = args; };
|
||||||
await profilerCheck(0);
|
await profilerCheck(0);
|
||||||
assert.strictEqual(captured[2], 'MyProfile — 89012345678901234567');
|
assert.deepStrictEqual(captured[2], { kind: 'profile', from: 'MyProfile', to: '89012345678901234567' });
|
||||||
assert.deepStrictEqual(captured[4], { expected: 'MyProfile', actual: '89012345678901234567', prefix: true });
|
assert.deepStrictEqual(captured[4], { expected: 'MyProfile', actual: '89012345678901234567', prefix: true });
|
||||||
delete global.profiles; delete global.pysimFetch; delete global.profilerRunProfile; delete global.t;
|
delete global.profiles; delete global.pysimFetch; delete global.profilerRunProfile; delete global.t;
|
||||||
});
|
});
|
||||||
@@ -1372,7 +1372,33 @@ test('profilerCheckSnapshot passes the snapshot name as the actual label', async
|
|||||||
let captured = null;
|
let captured = null;
|
||||||
global.profilerRunProfile = async (...args) => { captured = args; };
|
global.profilerRunProfile = async (...args) => { captured = args; };
|
||||||
await profilerCheckSnapshot(0, 0);
|
await profilerCheckSnapshot(0, 0);
|
||||||
assert.strictEqual(captured[2], 'Prof — SnapX');
|
assert.deepStrictEqual(captured[2], { kind: 'profile', from: 'Prof', to: 'SnapX' });
|
||||||
assert.deepStrictEqual(captured[4], { expected: 'Prof', actual: 'SnapX', prefix: true });
|
assert.deepStrictEqual(captured[4], { expected: 'Prof', actual: 'SnapX', prefix: true });
|
||||||
delete global.profiles; delete global.snapshots; delete global.profilerRunProfile; delete global.t;
|
delete global.profiles; delete global.snapshots; delete global.profilerRunProfile; delete global.t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('profilerResultsHeaderText builds profile and snapshot headers', () => {
|
||||||
|
global.t = s => 't:' + s;
|
||||||
|
assert.strictEqual(profilerResultsHeaderText({ kind: 'profile', from: 'Prof', to: '8901' }), 't:Profile verification results for: Prof \u2192 8901');
|
||||||
|
assert.strictEqual(profilerResultsHeaderText({ kind: 'snapshot', from: 'Snap1', to: 'Snap2' }), 't:Snapshot comparison results: Snap1 \u2192 Snap2');
|
||||||
|
assert.strictEqual(profilerResultsHeaderText({ kind: 'profile', from: 'Prof', to: null }), 't:Profile verification results for: Prof');
|
||||||
|
assert.strictEqual(profilerResultsHeaderText(null), '');
|
||||||
|
delete global.t;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('profilerRenderResultsView writes the built header into the title', () => {
|
||||||
|
global.t = s => s;
|
||||||
|
profilerResults = [];
|
||||||
|
profilerResultsHeader = { kind: 'profile', from: 'Prof', to: 'Snap' };
|
||||||
|
const els = {};
|
||||||
|
for (const id of ['profiler-summary', 'profiler-report', 'profiler-results-title']) els[id] = { innerHTML: '', textContent: '' };
|
||||||
|
global.document = { getElementById: id => els[id] || null };
|
||||||
|
profilerRenderResultsView();
|
||||||
|
assert.strictEqual(els['profiler-results-title'].textContent, 'Profile verification results for: Prof \u2192 Snap');
|
||||||
|
profilerResultsHeader = { kind: 'snapshot', from: 'A', to: 'B' };
|
||||||
|
profilerRenderResultsView();
|
||||||
|
assert.strictEqual(els['profiler-results-title'].textContent, 'Snapshot comparison results: A \u2192 B');
|
||||||
|
profilerResults = null;
|
||||||
|
profilerResultsHeader = null;
|
||||||
|
delete global.t;
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user