diff --git a/frontend/help-ru.html b/frontend/help-ru.html
index 40b9541..968e829 100644
--- a/frontend/help-ru.html
+++ b/frontend/help-ru.html
@@ -319,7 +319,7 @@
| Операция | Описание |
| Обзор карты (все данные GP) | Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками Удалить для каждого элемента. |
- | Установка пакета (.cap файл) | Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера размер блока LOAD (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой .cap просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается оценка требований к памяти: требование NVRAM (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же перечислены библиотеки, с которыми слинкован CAP (его компонент Import, в виде имя ≥ версия — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам. Повреждённый файл не проходит анализ, и Execute остаётся недоступной до успешного анализа. |
+ | Установка пакета (.cap файл) | Отправка .cap файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера размер блока LOAD (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой .cap просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается оценка требований к памяти: требование NVRAM (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны библиотеки, с которыми слинкован CAP (его компонент Import, в виде имя ≥ версия — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам Package, Requires, Memory и Components. Повреждённый файл не проходит анализ, и Execute остаётся недоступной до успешного анализа. |
@@ -376,7 +376,7 @@
- Empty — начать с пустого списка.
- Explore ISD — эталонная административная последовательность (
GET DATA FF21, листинги GET STATUS ISD/ELF/приложений, GET DATA 0085); длинные листинги автоматически продолжаются страницами SW 6310/CAFE.
- - Install from .cap — выберите
.cap, при необходимости SD AID, параметры install/STK и Make selectable; кнопка Сгенерировать строит INSTALL [for load] → LOAD ×N → INSTALL [for install]. Файл используется только для генерации APDU — он не сохраняется, как и его имя. При выборе файл проверяется и показывается оценка требований к памяти (требование NVRAM как сумма образа кода и постоянных данных, плюс RAM); также перечисляются требуемые библиотеки, идентификаторы пакета и разбивка по компонентам. Сгенерировать недоступна до успешного анализа.
+ - Install from .cap — выберите
.cap, при необходимости SD AID, параметры install/STK и Make selectable; кнопка Сгенерировать строит INSTALL [for load] → LOAD ×N → INSTALL [for install]. Файл используется только для генерации APDU — он не сохраняется, как и его имя. При выборе файл проверяется и показывается оценка требований к памяти (требование NVRAM как сумма образа кода и постоянных данных, плюс RAM); также перечисляются требуемые библиотеки, идентификаторы пакета и разбивка по компонентам (сгруппировано по разделам Package / Requires / Memory / Components). Сгенерировать недоступна до успешного анализа.
- Delete AID — список AID (по одному в строке) и P2 (object only / object and related objects) → APDU DELETE.
Таблица показывает имя, тип, число APDU и время создания каждого скрипта, а также кнопки Редактировать и Удалить; в редакторе есть поле имени и текстовая область APDU. В ходе сессии сервер отдаёт по одному C-APDU на каждый POST карты и отслеживает выполнение: карта сообщает статус в следующем POST (X-Admin-Script-Status), оборвавшаяся сессия досылает только невыполненные APDU (X-Admin-Resume продолжает, новый диалог начинает заново), а завершённый скрипт закрывается ответом 204 No Content. Конструктор RAM/GP вкладки Remote APDU может отправить цепочку команд прямо в прогон кнопкой «В очередь SCP81».
diff --git a/frontend/help.html b/frontend/help.html
index 4117bb8..add1da8 100644
--- a/frontend/help.html
+++ b/frontend/help.html
@@ -318,7 +318,7 @@
| Operation | Description |
| Explore Card (all GP data) | Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item Delete buttons. |
- | Install Package (.cap file) | Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the LOAD block size (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large .cap simply takes several SMS. As soon as the file is selected it is validated and its memory requirements are estimated in the form: the NVRAM requirement (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box lists the libraries the CAP is linked against (its Import component, shown as name ≥ version with standard library names resolved and a Java Card SDK hint where known) plus the package/applet identity and the component breakdown. A corrupt or wrong-format file fails the analysis, and Execute stays disabled until a successful analysis. |
+ | Install Package (.cap file) | Sends a .cap file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the LOAD block size (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large .cap simply takes several SMS. As soon as the file is selected it is validated and its memory requirements are estimated in the form: the NVRAM requirement (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the libraries the CAP is linked against (its Import component, shown as name ≥ version with standard library names resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the Package, Requires, Memory and Components headings. A corrupt or wrong-format file fails the analysis, and Execute stays disabled until a successful analysis. |
@@ -375,7 +375,7 @@
- Empty — start from scratch.
- Explore ISD — the reference administration sequence (
GET DATA FF21, GET STATUS ISD/ELF/application listings, GET DATA 0085); long listings auto-continue through the SW 6310/CAFE pages.
- - Install from .cap — pick a
.cap plus optional SD AID, install/STK parameters and make selectable; Generate builds INSTALL [for load] → LOAD ×N → INSTALL [for install]. The file is only used to generate the APDUs — it is not stored, not even its name. On selection it is validated and its memory requirements are estimated (the NVRAM requirement as the code-image + persistent-data total, plus RAM); the required libraries, package identity and component breakdown are listed too. Generate stays disabled until the analysis succeeds.
+ - Install from .cap — pick a
.cap plus optional SD AID, install/STK parameters and make selectable; Generate builds INSTALL [for load] → LOAD ×N → INSTALL [for install]. The file is only used to generate the APDUs — it is not stored, not even its name. On selection it is validated and its memory requirements are estimated (the NVRAM requirement as the code-image + persistent-data total, plus RAM); the required libraries, package identity and component breakdown are listed too (grouped under Package / Requires / Memory / Components headings). Generate stays disabled until the analysis succeeds.
- Delete AID — AID list (one per line) and P2 (object only / object and related objects) → DELETE APDUs.
The table lists each script with its kind, APDU count and creation time, plus Edit and Delete; the editor has a name field and the APDU textarea. Over a session the server serves one C-APDU per card POST and tracks execution: the card reports status in its next POST (X-Admin-Script-Status), a session that dies resends only the unexecuted APDUs (X-Admin-Resume continues, a fresh dialog restarts), and a completed script is closed with 204 No Content. The Remote APDU tab's RAM/GP builder can feed a command chain straight into the run with Queue in SCP81.
diff --git a/frontend/index.html b/frontend/index.html
index 2cbe277..6f85d8d 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1616,7 +1616,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
-const SIMPLE_VERSION = '3.5.16';
+const SIMPLE_VERSION = '3.5.17';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -8032,83 +8032,116 @@ function capMemHtml(mem) {
const requirement = Number(nv.requirement) || (loadFile + nvramData);
const otherComponents = Math.max(0, loadFile - methodBytes);
const imports = mem.imports || [];
+ const applets = mem.applets || [];
+ const comps = mem.components || [];
+ const bytes = v => esc(capMemBytes(v));
+ // the report is a set of one-heading tables: labels and column heads are
+ // muted, numbers line up right-aligned in a monospace column
+ const cell = (content, cls, span) => '' + content + ' | ';
+ const head = (content, cls) => '' + content + ' | ';
+ const table = rows => '';
+ const row = cells => '' + cells + '
';
+ const label = (content, cls) => cell(content, 'whitespace-nowrap' + (cls ? ' ' + cls : ''));
+ const num = content => cell(content, 'pl-4 text-right whitespace-nowrap font-mono');
+ const part = content => cell(content, 'pl-4 text-gray-400 dark:text-slate-500');
+ const section = content => '' + esc(content) + '
';
+ const disclosure = content => '' + esc(content) + '
';
let html = '' + esc(t('CAP requirements (estimate)')) + '
';
- // the libraries the CAP is linked against (JC VM spec 6.6), shown as
- // ">=" requirements: same major, minor >= the export version (4.5.2)
- if (imports.length) {
- const req = imports.map(im => {
- const name = jcAidName(im.aid) || im.aid;
- return name + ' \u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0);
- }).join(' \u00b7 ');
- html += '' + esc(t('Requires:')) + ' ' + esc(req) + '
';
+
+ // ── package identity: AIDs, header flags (Table 6-4), applets and the
+ // compiled-against hint (javacard.framework version + CAP format) ──
+ let pkgRows = '';
+ if (mem.package_aid) {
+ let val = esc(mem.package_aid);
+ if (mem.package_name) val += '' + esc(mem.package_name) + '
';
+ pkgRows += row(label(esc(t('Package'))) + cell(val, 'pl-4 font-mono break-all'));
}
- // the NVRAM requirement is a C6+C8-style total (GP Card Spec v2.3.1
- // Table 11-48): the package image (load file) plus the persistent data
- html += '' + esc(t('NVRAM requirement ≈')) + ' ' + esc(capMemBytes(requirement)) +
- ' (' + esc(t('code image')) + ' ' + esc(capMemBytes(loadFile)) +
- ' + ' + esc(t('data')) + ' ' + esc(capMemBytes(nvramData)) + ')' +
- ' · ' + esc(t('RAM (volatile):')) + ' ' + esc(capMemBytes(ram.total)) + '
';
- html += '' + esc(t('Details')) + '
';
- // compiled-against hint from the javacard.framework version + CAP format
- const fw = imports.find(im => (im.aid || '').toUpperCase() === 'A0000000620101');
- const sdk = fw ? JC_FRAMEWORK_SDK[(Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0)] : '';
- let against = sdk ? 'Java Card ' + sdk : '';
- if (mem.cap_version) against += (against ? ' · ' : '') + t('CAP format') + ' ' + mem.cap_version;
- if (against) html += '' + esc(t('Compiled against:')) + ' ' + esc(against) + '
';
- if (imports.length) {
- imports.forEach(im => {
- const name = jcAidName(im.aid);
- const fam = jcAidFamily(im.aid);
- let line = (name || im.aid) + ' ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0);
- if (fam) line += ' — ' + fam;
- line += ' — ' + (Number(im.refs) || 0) + ' ' + t('refs');
- if (name) line += ' — ' + im.aid;
- html += '' + esc(line) + '
';
- });
+ if (mem.package_version) {
+ pkgRows += row(label(esc(t('Version'))) + cell(esc('v' + mem.package_version), 'pl-4 font-mono'));
}
- // package identity + header flags (Table 6-4) + applets
- let pkg = esc(mem.package_aid || '?') + (mem.package_version ? ' v' + esc(mem.package_version) : '');
const fl = mem.flags || {};
const fls = [];
if (fl.applet) fls.push(t('applet package'));
if (fl.export) fls.push(t('exports an API'));
if (fl.int) fls.push(t('uses int'));
- if (fls.length) pkg += ' (' + fls.join(', ') + ')';
- if (mem.package_name) pkg += ' — ' + esc(mem.package_name);
- html += '' + esc(t('Package:')) + ' ' + pkg + '
';
- if ((mem.applets || []).length) {
- html += '' + esc(t('Applets:')) + ' ' + esc((mem.applets || []).map(a => {
+ if (fls.length) pkgRows += row(label(esc(t('Type'))) + cell(esc(fls.join(' \u00b7 ')), 'pl-4'));
+ if (applets.length) {
+ pkgRows += row(label(esc(t('Applets'))) + cell(applets.map(a => {
const n = jcAidName(a);
- return n ? a + ' (' + n + ')' : a;
- }).join(', ')) + '
';
+ return esc(a) + (n ? ' (' + esc(n) + ')' : '');
+ }).join('
'), 'pl-4 font-mono break-all'));
}
- const comps = mem.components || [];
+ const fw = imports.find(im => (im.aid || '').toUpperCase() === 'A0000000620101');
+ const sdk = fw ? JC_FRAMEWORK_SDK[(Number(fw.major) || 0) + '.' + (Number(fw.minor) || 0)] : '';
+ let against = sdk ? 'Java Card ' + sdk : '';
+ if (mem.cap_version) against += (against ? ' \u00b7 ' : '') + t('CAP format') + ' ' + mem.cap_version;
+ if (against) pkgRows += row(label(esc(t('Compiled'))) + cell(esc(against), 'pl-4'));
+ if (pkgRows) html += section(t('Package')) + table(pkgRows);
+
+ // ── the libraries the CAP is linked against (JC VM spec 6.6), shown as
+ // ">=" requirements: same major, minor >= the export version (4.5.2);
+ // refs = distinct constant-pool references to the package (6.7) ──
+ if (imports.length) {
+ let reqRows = row(head('', 'text-left') + head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
+ head(esc(t('Family')), 'pl-4 text-left') + head(esc(t('Refs')), 'pl-4 text-right whitespace-nowrap'));
+ imports.forEach(im => {
+ const name = jcAidName(im.aid);
+ const fam = jcAidFamily(im.aid);
+ reqRows += row(cell(name ? esc(name) : '' + esc(im.aid) + '', 'whitespace-nowrap') +
+ num('\u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0)) +
+ part(esc(fam)) + num(String(Number(im.refs) || 0)));
+ });
+ html += section(t('Requires') + ' (' + imports.length + ')') + table(reqRows);
+ }
+
+ // ── memory: the NVRAM requirement is the C6+C8-style total (GP Card
+ // Spec v2.3.1 Table 11-48) of the code image (load file) and the
+ // persistent data; children are indented under their total ──
+ const mrows = [];
+ mrows.push(row(label(esc(t('NVRAM requirement'))) + num(bytes(requirement)) +
+ part(esc(t('code image') + ' + ' + t('data')))));
+ mrows.push(row(label(esc(t('code image (load file)')), 'pl-4') + num(bytes(loadFile)) +
+ part(esc('Method.cap ' + capMemBytes(methodBytes) + ' \u00b7 ' + t('other components') + ' ' + capMemBytes(otherComponents)))));
+ mrows.push(row(label(esc(t('persistent data')), 'pl-4') + num(bytes(nvramData)) + part(esc([
+ t('static image') + ' ' + capMemBytes(nv.static_image),
+ t('static array init') + ' ' + capMemBytes(nv.array_init),
+ t('install-time objects') + ' ' + capMemBytes(nv.install_objects),
+ t('object headers') + ' ' + capMemBytes(nv.header_overhead),
+ t('reference storage') + ' ' + capMemBytes(nv.ref_storage),
+ ].join(' \u00b7 ')))));
+ mrows.push(row(label(esc(t('RAM (volatile)'))) + num(bytes(ram.total)) + part(esc([
+ t('transient arrays') + ' ' + capMemBytes(ram.transient_arrays),
+ t('runtime transient') + ' ' + capMemBytes(ram.runtime_transient),
+ t('peak method frame') + ' ' + capMemBytes(ram.peak_frame),
+ ].join(' \u00b7 ')))));
+ mrows.push(row(label(esc(t('Suggested quotas (informational)'))) + cell(
+ 'C6=' + esc(String(Number(sug.c6) || 0)) + ' C7=0x' + esc(hex2(sug.c7)) + ' C8=0x' + esc(hex2(sug.c8)),
+ 'pl-4 font-mono', 2)));
+ html += section(t('Memory')) + table(mrows.join(''));
+
+ // ── component sizes in load-file order (incl. the Directory/Export
+ // entries capmem does not parse) with each entry's share of the load file
if (comps.length) {
const ctotal = loadFile || comps.reduce((sum, c) => sum + (Number(c.size) || 0), 0);
- html += '' + esc(t('Components:')) + ' ' + comps.map(c => {
+ const tcls = 'border-t border-gray-200 dark:border-slate-700';
+ let crows = row(head(esc(t('Component')), 'text-left') + head(esc(t('Size')), 'pl-4 text-right whitespace-nowrap') +
+ head(esc(t('Share')), 'pl-4 text-right whitespace-nowrap'));
+ comps.forEach(c => {
const sz = Number(c.size) || 0;
- const pct = ctotal ? ' (' + Math.round(sz * 100 / ctotal) + '%)' : '';
- return esc(c.name + ' ' + capMemBytes(sz) + pct);
- }).join(' · ') + '
';
+ crows += row(label(esc(c.name)) + num(bytes(sz)) +
+ num(ctotal ? Math.round(sz * 100 / ctotal) + '%' : '\u2014'));
+ });
+ crows += row(cell(esc(t('Load file (total)')), tcls) +
+ cell(bytes(loadFile), 'pl-4 text-right whitespace-nowrap font-mono ' + tcls) +
+ cell('100%', 'pl-4 text-right whitespace-nowrap font-mono ' + tcls));
+ html += disclosure(t('Components') + ' (' + comps.length + ')') + table(crows) + ' ';
}
- html += '' + esc(t('Code image (load file):')) + ' ' + esc(capMemBytes(loadFile)) +
- ' · ' + esc(t('bytecode (Method.cap):')) + ' ' + esc(capMemBytes(methodBytes)) +
- ' · ' + esc(t('other components:')) + ' ' + esc(capMemBytes(otherComponents)) + '
';
- html += '' + esc(t('Persistent data (NVRAM):')) + ' ' + esc(capMemBytes(nvramData)) +
- ' · ' + esc(t('Static image:')) + ' ' + esc(capMemBytes(nv.static_image)) +
- ' · ' + esc(t('Static array init:')) + ' ' + esc(capMemBytes(nv.array_init)) +
- ' · ' + esc(t('Install-time objects:')) + ' ' + esc(capMemBytes(nv.install_objects)) +
- ' · ' + esc(t('Object headers:')) + ' ' + esc(capMemBytes(nv.header_overhead)) +
- ' · ' + esc(t('Reference storage:')) + ' ' + esc(capMemBytes(nv.ref_storage)) + '
';
- html += '' + esc(t('RAM (volatile):')) + ' ' + esc(capMemBytes(ram.total)) +
- ' · ' + esc(t('Transient arrays:')) + ' ' + esc(capMemBytes(ram.transient_arrays)) +
- ' · ' + esc(t('Runtime transient:')) + ' ' + esc(capMemBytes(ram.runtime_transient)) +
- ' · ' + esc(t('Peak method frame:')) + ' ' + esc(capMemBytes(ram.peak_frame)) + '
';
- html += '' + esc(t('Suggested install quotas (informational):')) +
- ' C6=' + esc(String(Number(sug.c6) || 0)) + ' C7=0x' + esc(hex2(sug.c7)) + ' C8=0x' + esc(hex2(sug.c8)) + '
';
- html += '' + esc(t('NVRAM = code image + persistent data (GP Card Spec v2.3.1 Table 11-48, C6+C8 style); card memory management, rounding and the registry entry are not included, and the static field image appears in both parts.')) + '
';
- html += '' + esc(t('Estimate only — memory values are not used for installation')) + '
';
- html += ' ';
+
+ html += disclosure(t('Notes')) +
+ '' + esc(t('NVRAM = code image + persistent data (GP Card Spec v2.3.1 Table 11-48, C6+C8 style); card memory management, rounding and the registry entry are not included, and the static field image appears in both parts.')) + '
' +
+ '' + esc(t('Estimate only — memory values are not used for installation')) + '
' +
+ '';
(mem.warnings || []).forEach(w => {
html += '\u26a0 ' + esc(w) + '
';
});
@@ -16140,6 +16173,33 @@ const LANG_RU = {
'Peak method frame:': 'Пиковый кадр метода:',
'Suggested install quotas (informational):': 'Рекомендуемые квоты установки (справочно):',
'Estimate only — memory values are not used for installation': 'Только оценка — значения памяти не используются при установке',
+ 'Package': 'Пакет',
+ 'Version': 'Версия',
+ 'Type': 'Тип',
+ 'Applets': 'Апплеты',
+ 'Compiled': 'Собрано',
+ 'Requires': 'Требуются',
+ 'Family': 'Семейство',
+ 'Refs': 'Ссылки',
+ 'NVRAM requirement': 'Требование NVRAM',
+ 'code image (load file)': 'образ кода (load-файл)',
+ 'persistent data': 'постоянные данные',
+ 'RAM (volatile)': 'ОЗУ (volatile)',
+ 'other components': 'прочие компоненты',
+ 'static image': 'статический образ',
+ 'static array init': 'инициализация массивов',
+ 'install-time objects': 'объекты установки',
+ 'object headers': 'заголовки объектов',
+ 'reference storage': 'хранение ссылок',
+ 'transient arrays': 'transient-массивы',
+ 'runtime transient': 'transient во время работы',
+ 'peak method frame': 'пиковый кадр',
+ 'Suggested quotas (informational)': 'Рекомендуемые квоты (справочно)',
+ 'Components': 'Компоненты',
+ 'Component': 'Компонент',
+ 'Share': 'Доля',
+ 'Load file (total)': 'Load-файл (итого)',
+ 'Notes': 'Примечания',
'Step': 'Шаг',
'Install OK': 'Установка OK',
'Install FAILED at step:': 'Установка не удалась на шаге:',
diff --git a/frontend/sw.js b/frontend/sw.js
index 0683fd5..7ca1e35 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v269';
+const CACHE = 'simple-v270';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/capmem.test.js b/frontend/tests/capmem.test.js
index 02139c0..aa8b01c 100644
--- a/frontend/tests/capmem.test.js
+++ b/frontend/tests/capmem.test.js
@@ -79,33 +79,54 @@ test('capMemBytes formats bytes and kilobytes', () => {
assert.strictEqual(capMemBytes(null), '0 B');
});
-test('capMemHtml renders the NVRAM requirement, breakdown and warnings', () => {
+test('capMemHtml renders the grouped report: package, requires, memory, components, warnings', () => {
const mem = memFixture();
mem.warnings = ['method class[0].token[1]: unknown opcode 0xAA, scan stopped'];
const out = capMemHtml(mem);
assert.ok(out.includes('CAP requirements (estimate)'), out);
- // C6+C8-style total: load file + persistent data
- assert.ok(out.includes('NVRAM requirement ≈ 3.1 kB (code image 2.9 kB + data 136 B)'), out);
- assert.ok(out.includes('RAM (volatile): 24 B'), out);
- assert.ok(out.includes('Code image (load file): 2.9 kB'), out);
- assert.ok(out.includes('bytecode (Method.cap): 2.0 kB'), out);
- assert.ok(out.includes('other components: 952 B'), out);
- assert.ok(out.includes('Persistent data (NVRAM): 136 B'), out);
- assert.ok(out.includes('Static image: 12 B'), out);
- assert.ok(out.includes('Reference storage: 8 B'), out);
- assert.ok(out.includes('Peak method frame: 8 B'), out);
- assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
+ // grouped, labelled sections (always visible except components/notes)
+ assert.ok(out.includes('>Package'), out);
+ assert.ok(out.includes('>Requires (1)'), out);
+ assert.ok(out.includes('>Memory'), out);
+ assert.ok(out.includes('>Components (3)'), out);
+ assert.ok(out.includes('>Notes'), out);
assert.ok(out.includes('Table 11-48'), out);
assert.ok(out.includes('Estimate only'), out);
assert.ok(out.includes('unknown opcode 0xAA'), out);
- // the required libraries with the family, reference count and the
- // compiled-against hint derived from the framework version
- assert.ok(out.includes('Requires: javacard.framework \u2265 1.0'), out);
- assert.ok(out.includes('Compiled against: Java Card 2.1.1/2.1.2 \u00b7 CAP format 2.1'), out);
- assert.ok(out.includes('javacard.framework 1.0 — Oracle JavaCard API — 6 refs — A0000000620101'), out);
- assert.ok(out.includes('Package: A0000000620101 v1.0 (applet package)'), out);
- assert.ok(out.includes('Applets: A0000000620101 (javacard.framework)'), out);
- assert.ok(out.includes('Components: Header 20 B (1%) \u00b7 Method 2.7 kB (93%) \u00b7 ConstantPool 180 B (6%)'), out);
+ // memory: the C6+C8-style total once, with indented children
+ assert.ok(out.includes('NVRAM requirement'), out);
+ assert.ok(out.includes('>3.1 kB'), out);
+ assert.ok(out.includes('code image + data'), out);
+ assert.ok(out.includes('>2.9 kB'), out);
+ assert.ok(out.includes('Method.cap 2.0 kB'), out);
+ assert.ok(out.includes('other components 952 B'), out);
+ assert.ok(out.includes('>136 B'), out);
+ assert.ok(out.includes('static image 12 B'), out);
+ assert.ok(out.includes('reference storage 8 B'), out);
+ assert.ok(out.includes('>24 B'), out);
+ assert.ok(out.includes('peak method frame 8 B'), out);
+ assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
+ // requires: library name, version, family and refs; the AID is not
+ // repeated when the name resolves (it adds nothing), and the section
+ // carries no package AIDs
+ assert.ok(out.includes('>javacard.framework'), out);
+ assert.ok(out.includes('>\u2265 1.0'), out);
+ assert.ok(out.includes('>Oracle JavaCard API'), out);
+ assert.ok(out.includes('>6'), out);
+ const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory'));
+ assert.ok(!req.includes('A0000000620101'), req);
+ // package identity + compiled-against hint derived from the framework version
+ assert.ok(out.includes('A0000000620101') && out.includes('>v1.0'), out);
+ assert.ok(out.includes('applet package'), out);
+ assert.ok(out.includes('(javacard.framework)'), out);
+ assert.ok(out.includes('Java Card 2.1.1/2.1.2 \u00b7 CAP format 2.1'), out);
+ // components in load-file order with sizes, shares and the total row
+ assert.ok(out.includes('>Header') && out.includes('>20 B') && out.includes('>1%'), out);
+ assert.ok(out.includes('>Method') && out.includes('>2.7 kB') && out.includes('>93%'), out);
+ assert.ok(out.includes('>ConstantPool') && out.includes('>180 B') && out.includes('>6%'), out);
+ assert.ok(out.includes('Load file (total)') && out.includes('>100%'), out);
+ const c1 = out.indexOf('Header'), c2 = out.indexOf('Method'), c3 = out.indexOf('ConstantPool');
+ assert.ok(c1 >= 0 && c1 < c2 && c2 < c3, 'components keep the load-file order');
assert.strictEqual(capMemHtml(null), '');
});
@@ -115,17 +136,23 @@ test('capMemHtml skips the import section on a response without imports', () =>
mem.code = { method_component: 2048 };
delete mem.nvram.requirement;
const out = capMemHtml(mem);
- assert.ok(!out.includes('Requires:'), out);
- assert.ok(out.includes('NVRAM requirement \u2248 2.1 kB'), out);
+ assert.ok(!out.includes('>Requires'), out);
+ assert.ok(!out.includes('Java Card'), out); // no framework import -> no SDK hint
+ assert.ok(out.includes('CAP format 2.1'), out);
+ assert.ok(out.includes('NVRAM requirement'), out);
+ assert.ok(out.includes('>2.1 kB'), out);
});
test('capMemHtml keeps unknown import AIDs bare and labels the family only when known', () => {
const mem = memFixture();
mem.imports = [{ aid: 'A1130001180001', minor: 0, major: 1, refs: 2 }];
const out = capMemHtml(mem);
- assert.ok(out.includes('Requires: A1130001180001 \u2265 1.0'), out);
- assert.ok(out.includes('A1130001180001 1.0 — 2 refs'), out);
- assert.ok(!out.includes('Compiled against: Java Card'), out); // no framework import
+ const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory'));
+ assert.ok(req.includes('A1130001180001'), req); // unknown AID: shown as the name
+ assert.ok(req.includes('>\u2265 1.0'), req);
+ assert.ok(req.includes('>2'), req); // refs
+ assert.ok(!out.includes('Java Card'), out); // no framework import
+ assert.ok(out.includes('CAP format 2.1'), out);
});
test('capMemHtml falls back to the bytecode size on older server responses', () => {
@@ -133,7 +160,16 @@ test('capMemHtml falls back to the bytecode size on older server responses', ()
delete mem.code.load_file;
delete mem.nvram.requirement;
const out = capMemHtml(mem);
- assert.ok(out.includes('NVRAM requirement ≈ 2.1 kB (code image 2.0 kB + data 136 B)'), out);
+ assert.ok(out.includes('NVRAM requirement'), out);
+ assert.ok(out.includes('>2.1 kB'), out);
+});
+
+test('capMemHtml drops the components section without a component list', () => {
+ const mem = memFixture();
+ delete mem.components;
+ const out = capMemHtml(mem);
+ assert.ok(!out.includes('>Components'), out);
+ assert.ok(out.includes('>Notes'), out);
});
test('capGateOk gates the RAM install op and the scripts form', () => {
diff --git a/pyproject.toml b/pyproject.toml
index 7b4175c..83b25e7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-simple-server"
-version = "3.5.16"
+version = "3.5.17"
description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py
index fad44bc..c9a0250 100644
--- a/pysim_simple_server/server.py
+++ b/pysim_simple_server/server.py
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
-VERSION = '3.5.16'
+VERSION = '3.5.17'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE