diff --git a/index.html b/index.html
index 60793eb..65f49fa 100644
--- a/index.html
+++ b/index.html
@@ -860,10 +860,13 @@
File:
-
+
+
+
|
Raw
Decoded
+
@@ -879,6 +882,11 @@
+
+
+
+
+
@@ -2489,7 +2497,6 @@ async function pysimRefresh() {
: 'none') + '' +
(data.current_selection
? ' [' + (data.current_selection.type || '?') + ']' +
- (data.current_selection.file_type ? ' ' + (({transparent:'TR',linear_fixed:'LF',cyclic:'CY',ber_tlv:'BT',df:'DF'})[data.current_selection.file_type] || '') : '') +
' FID=' + (data.current_selection.fid || '?') +
(data.current_selection.path ? ' path=' + data.current_selection.path : '') +
(data.current_selection.file_size ? ' size=' + data.current_selection.file_size : '') +
@@ -2785,7 +2792,7 @@ function pysimCustomInject(node) {
const pfid = node.fid.toLowerCase();
for (const c of pysimCustomFiles) {
if (c.parentFid.toLowerCase() !== pfid) continue;
- const existing = (node.children || []).find(ch => ch.fid === c.fid);
+ const existing = (node.children || []).find(ch => ch.fid?.toLowerCase() === c.fid.toLowerCase());
if (existing) {
existing.name = c.name;
existing.custom = true;
@@ -2933,6 +2940,7 @@ async function pysimFsToggleDir(name) {
}
async function pysimFsClickFile(name) {
+ pysimFsResetEdit();
pysimFsSetMode('raw');
const node = pysimFsFindNode(name, pysimFsTreeRoot);
let body = { name: name, fid: node ? node.fid : null };
@@ -3015,7 +3023,77 @@ async function pysimFsSelect() {
pysimRefresh();
}
-let pysimFsDecodedMode = false;
+let pysimFsEditMode = false;
+let pysimFsEditData = null;
+
+function pysimFsEdit() {
+ pysimFsEditMode = true;
+ document.getElementById('pysim-fs-read-btn').classList.add('hidden');
+ document.getElementById('pysim-fs-edit-btn').classList.add('hidden');
+ document.getElementById('pysim-fs-save-btn').classList.remove('hidden');
+ document.getElementById('pysim-fs-cancel-btn').classList.remove('hidden');
+ const out = document.getElementById('pysim-fs-content');
+ out.querySelectorAll('textarea, input').forEach(el => el.removeAttribute('readonly'));
+ // Store current data for save
+ pysimFsEditData = out.innerHTML;
+}
+
+function pysimFsCancel() {
+ pysimFsEditMode = false;
+ document.getElementById('pysim-fs-read-btn').classList.remove('hidden');
+ document.getElementById('pysim-fs-edit-btn').classList.remove('hidden');
+ document.getElementById('pysim-fs-save-btn').classList.add('hidden');
+ document.getElementById('pysim-fs-cancel-btn').classList.add('hidden');
+ const out = document.getElementById('pysim-fs-content');
+ if (pysimFsEditData) out.innerHTML = pysimFsEditData;
+ pysimFsEditData = null;
+}
+
+async function pysimFsSave() {
+ const rawName = document.getElementById('pysim-fs-filename').textContent.replace(/ \[.*?\]$/, '').replace(/^✗ /, '');
+ const statusEl = document.getElementById('pysim-fs-status');
+ const out = document.getElementById('pysim-fs-content');
+ const node = pysimFsFindNode(rawName, pysimFsTreeRoot);
+ statusEl.textContent = 'Saving...';
+ try {
+ const textareas = out.querySelectorAll('textarea');
+ if (textareas.length === 1) {
+ // Transparent file
+ const body = { name: rawName, fid: node ? node.fid : null, data: textareas[0].value.replace(/[^0-9a-fA-F]/g, '') };
+ const parentSel = getParentSel(node);
+ if (parentSel) body.parent_sel = parentSel;
+ const data = await pysimFetch('/api/write', body);
+ if (data.success) {
+ statusEl.textContent = 'SW: ' + data.sw + ' OK';
+ pysimFsCancel();
+ pysimFsRead();
+ } else {
+ statusEl.textContent = 'SW: ' + (data.sw || '?') + ' — ' + (data.error || 'Error');
+ }
+ } else {
+ // Record file
+ const inputs = out.querySelectorAll('input');
+ for (const inp of inputs) {
+ const num = inp.closest('div')?.querySelector('span')?.textContent;
+ if (num) {
+ const body = { name: rawName, fid: node ? node.fid : null, data: inp.value.replace(/[^0-9a-fA-F]/g, ''), record_nr: parseInt(num) };
+ const parentSel = getParentSel(node);
+ if (parentSel) body.parent_sel = parentSel;
+ const data = await pysimFetch('/api/write', body);
+ if (!data.success) {
+ statusEl.textContent = 'SW: ' + (data.sw || '?') + ' — ' + (data.error || 'Error') + ' (record ' + num + ')';
+ return;
+ }
+ }
+ }
+ statusEl.textContent = 'SW: 9000 OK';
+ pysimFsCancel();
+ pysimFsRead();
+ }
+ } catch (e) {
+ statusEl.textContent = 'Error: ' + e.message;
+ }
+}
function pysimFsSetMode(mode) {
pysimFsDecodedMode = mode === 'dec';
@@ -3030,6 +3108,15 @@ function pysimFsSetMode(mode) {
});
}
+function pysimFsResetEdit() {
+ pysimFsEditMode = false;
+ pysimFsEditData = null;
+ document.getElementById('pysim-fs-read-btn').classList.remove('hidden');
+ document.getElementById('pysim-fs-edit-btn').classList.remove('hidden');
+ document.getElementById('pysim-fs-save-btn').classList.add('hidden');
+ document.getElementById('pysim-fs-cancel-btn').classList.add('hidden');
+}
+
// ===== custom files =====
let pysimCustomFiles = [];
@@ -3048,13 +3135,17 @@ function pysimCustomAdd() {
const pathEl = document.getElementById('pysim-cf-path');
const nameEl = document.getElementById('pysim-cf-name');
const path = pathEl.value.trim().toUpperCase();
- const name = nameEl.value.trim().toUpperCase();
+ const name = nameEl.value.trim();
if (!path || !name) return;
+ if (!name.startsWith('EF.') && !name.startsWith('DF.')) {
+ alert('Alias must start with "EF." or "DF."');
+ return;
+ }
const parts = path.split('/').filter(Boolean);
if (parts.length < 2) { alert('Path must have at least 2 parts, e.g. 3F00/6F46'); return; }
const fid = parts[parts.length - 1];
const parentFid = parts[parts.length - 2];
- if (pysimCustomFiles.some(c => c.path === path)) { alert('Path already exists'); return; }
+ if (pysimCustomFiles.some(c => c.path.toUpperCase() === path)) { alert('Path already exists'); return; }
pysimCustomFiles.push({ path, name, fid, parentFid });
pysimCustomSave();
pysimCustomRender();
@@ -3082,6 +3173,42 @@ function pysimCustomRender() {
el.innerHTML = html;
}
+function pysimCustomExport() {
+ const el = document.getElementById('pysim-cf-io');
+ el.value = JSON.stringify(pysimCustomFiles, null, 2);
+ el.classList.remove('hidden');
+ el.select();
+}
+
+function pysimCustomImport() {
+ const el = document.getElementById('pysim-cf-io');
+ try {
+ const imported = JSON.parse(el.value);
+ if (!Array.isArray(imported)) throw new Error('Expected an array');
+ let added = 0;
+ for (const entry of imported) {
+ if (!entry.path || !entry.name) continue;
+ const path = entry.path.toUpperCase();
+ if (!pysimCustomFiles.some(c => c.path.toUpperCase() === path)) {
+ const parts = path.split('/').filter(Boolean);
+ pysimCustomFiles.push({
+ path: path,
+ name: entry.name.toUpperCase(),
+ fid: (entry.fid || parts[parts.length - 1]).toUpperCase(),
+ parentFid: (entry.parentFid || parts[parts.length - 2]).toUpperCase(),
+ });
+ added++;
+ }
+ }
+ pysimCustomSave();
+ pysimCustomRender();
+ el.value = 'Imported ' + added + ' files.';
+ setTimeout(() => { el.classList.add('hidden'); }, 2000);
+ } catch (e) {
+ alert('Import error: ' + e.message);
+ }
+}
+
// Init custom files
pysimCustomLoad();
diff --git a/style.css b/style.css
index 02129fb..bee4e3e 100644
--- a/style.css
+++ b/style.css
@@ -877,6 +877,11 @@ video {
background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
}
+.bg-gray-600 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));
+}
+
.bg-neutral-50 {
--tw-bg-opacity: 1;
background-color: rgb(250 250 250 / var(--tw-bg-opacity, 1));
@@ -1128,6 +1133,11 @@ video {
background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1));
}
+.hover\:bg-gray-700:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));
+}
+
.hover\:text-gray-600:hover {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity, 1));