diff --git a/README.md b/README.md index bee0ccc..8afe121 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,5 @@ Dirty pages: 0x100004000, 0x100005000 Then paste it into the input area of this tool to visualize the memory regions. -![docs/images/screen.png](docs/images/screen.png) \ No newline at end of file +![docs/images/screen.png](docs/images/screen.png) +![docs/images/screen2.png](docs/images/screen2.png) \ No newline at end of file diff --git a/docs/images/screen2.png b/docs/images/screen2.png new file mode 100644 index 0000000..9432db1 Binary files /dev/null and b/docs/images/screen2.png differ diff --git a/index.html b/index.html index cdf5421..36c7f52 100644 --- a/index.html +++ b/index.html @@ -64,7 +64,7 @@ class="w-full h-48 p-4 font-mono text-xs bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all resize-y text-slate-800 dark:text-slate-200" placeholder="[0x0000000100000000-0x0000000100004000) r-x __TEXT..."> -
+
+
+ + +
+ - +
+ + @@ -181,7 +221,7 @@ class="bg-white dark:bg-slate-800 divide-y divide-slate-200 dark:divide-slate-700">
Waiting for input...Waiting for input...
diff --git a/src.js b/src.js index 93c8d57..e940172 100644 --- a/src.js +++ b/src.js @@ -1,5 +1,6 @@ let allRegions = []; let currentSort = { column: null, direction: "asc" }; +let selectedRegions = new Set(); // Dark Mode Toggle function toggleDarkMode() { @@ -95,6 +96,7 @@ function parseMemory() { } allRegions = regions; + selectedRegions.clear(); updateStats(regions); document.getElementById("stats").classList.remove("hidden"); document.getElementById("stats").classList.add("grid"); @@ -189,7 +191,10 @@ function renderTable(regions) { : "hover:bg-slate-50 dark:hover:bg-slate-700"; return ` - + + + + ${r.startStr} ${r.endStr} ${formatBytes(r.size)} @@ -214,6 +219,9 @@ function renderTable(regions) { remaining > 0 ? `${dataset.length} rows shown (${remaining} hidden for performance)` : `${dataset.length} rows`; + + // Update select-all checkbox state + updateSelectAllCheckbox(); } // --- Helpers & Utilities --- @@ -249,7 +257,7 @@ function sortBy(column) { // Update active header const headers = document.querySelectorAll("th"); headers.forEach((th) => { - if (th.onclick.toString().includes(column)) { + if (th.onclick && th.onclick.toString().includes(column)) { const icon = th.querySelector(".sort-icon"); icon.textContent = currentSort.direction === "asc" ? "▲" : "▼"; icon.classList.add("text-indigo-600"); @@ -260,6 +268,136 @@ function sortBy(column) { applyFilters(); } +// --- Selection and Command Generation --- + +function toggleSelectAll() { + const selectAllCheckbox = document.getElementById("select-all"); + const checkboxes = document.querySelectorAll(".row-checkbox"); + + checkboxes.forEach((checkbox) => { + checkbox.checked = selectAllCheckbox.checked; + const start = checkbox.dataset.start; + const end = checkbox.dataset.end; + + if (selectAllCheckbox.checked) { + selectedRegions.add(start); + } else { + selectedRegions.delete(start); + } + }); + + updateGenerateButton(); + updateSelectedCount(); +} + +function updateSelection() { + const checkboxes = document.querySelectorAll(".row-checkbox"); + + checkboxes.forEach((checkbox) => { + const start = checkbox.dataset.start; + if (checkbox.checked) { + selectedRegions.add(start); + } else { + selectedRegions.delete(start); + } + }); + + updateSelectAllCheckbox(); + updateGenerateButton(); + updateSelectedCount(); +} + +function updateSelectAllCheckbox() { + const selectAllCheckbox = document.getElementById("select-all"); + const checkboxes = document.querySelectorAll(".row-checkbox"); + + if (checkboxes.length === 0) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + return; + } + + const checkedCount = Array.from(checkboxes).filter((cb) => cb.checked).length; + + if (checkedCount === 0) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + } else if (checkedCount === checkboxes.length) { + selectAllCheckbox.checked = true; + selectAllCheckbox.indeterminate = false; + } else { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = true; + } +} + +function updateGenerateButton() { + const generateBtn = document.getElementById("generate-btn"); + generateBtn.disabled = selectedRegions.size === 0; +} + +function updateSelectedCount() { + const countSpan = document.getElementById("selected-count"); + if (countSpan) { + countSpan.textContent = selectedRegions.size; + } +} + +function generateCommands() { + if (selectedRegions.size === 0) return; + + const commands = []; + + // Get selected regions in order + const selectedRegionObjs = allRegions.filter((r) => + selectedRegions.has(r.startStr), + ); + + selectedRegionObjs.forEach((region) => { + const startAddr = region.startStr; + const endAddr = region.endStr; + const command = `memory read --outfile /tmp/dump_${startAddr}_${endAddr}.bin --binary ${startAddr} ${endAddr}`; + commands.push(command); + }); + + const commandsText = document.getElementById("commands-text"); + commandsText.textContent = commands.join("\n"); + + document.getElementById("command-output").classList.remove("hidden"); + + // Scroll to commands + document + .getElementById("command-output") + .scrollIntoView({ behavior: "smooth", block: "nearest" }); +} + +function copyCommands(event) { + const commandsText = document.getElementById("commands-text").textContent; + + navigator.clipboard + .writeText(commandsText) + .then(() => { + // Visual feedback + const copyBtn = event.target.closest("button"); + const originalText = copyBtn.innerHTML; + + copyBtn.innerHTML = ` + + + + Copied! + `; + + setTimeout(() => { + copyBtn.innerHTML = originalText; + }, 2000); + }) + .catch((err) => { + console.error("Failed to copy:", err); + alert("Failed to copy to clipboard"); + }); +} + function applyFilters() { const addrFilter = document .getElementById("addressFilter") @@ -343,11 +481,12 @@ function applyFilters() { function clearAll() { document.getElementById("input").value = ""; document.getElementById("table-body").innerHTML = - `Waiting for input...`; + `Waiting for input...`; document.getElementById("stats").classList.add("hidden"); document.getElementById("stats").classList.remove("grid"); document.getElementById("filter-section").classList.add("hidden"); document.getElementById("filter-section").classList.remove("flex"); + document.getElementById("command-output").classList.add("hidden"); document.getElementById("footer-count").innerText = "0 rows"; // Reset filters @@ -357,4 +496,6 @@ function clearAll() { allRegions = []; currentSort = { column: null, direction: "asc" }; + selectedRegions.clear(); + updateGenerateButton(); }