diff --git a/ghidra/ImportMarkersAsBookmarks.java b/ghidra/ImportMarkersAsBookmarks.java new file mode 100644 index 0000000..4fbd9e8 --- /dev/null +++ b/ghidra/ImportMarkersAsBookmarks.java @@ -0,0 +1,121 @@ +// CSV format per line: +// 0x401000, +// +// Creates NOTE bookmarks in category: FAIL_MARKER + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.BookmarkManager; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + +public class ImportMarkersAsBookmarks extends GhidraScript { + + private static final String BOOKMARK_TYPE = "NOTE"; + private static final String BOOKMARK_CATEGORY = "FAIL_MARKER"; + + @Override + public void run() throws Exception { + File input = askFile("Select CSV file", "Import"); + BookmarkManager bm = currentProgram.getBookmarkManager(); + + int imported = 0; + int skipped = 0; + int lineNo = 0; + + try (BufferedReader br = new BufferedReader(new FileReader(input))) { + String line; + while ((line = br.readLine()) != null) { + lineNo++; + + if (monitor.isCancelled()) { + println("Cancelled."); + break; + } + + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + String[] parts = splitCsvLine(line); + if (parts.length < 1) { + skipped++; + printerr("Line " + lineNo + ": missing address"); + continue; + } + + String addrText = parts[0].trim(); + String description = parts.length >= 2 ? parts[1].trim() : ""; + + if (addrText.isEmpty()) { + skipped++; + printerr("Line " + lineNo + ": empty address"); + continue; + } + + Address addr = parseAddress(addrText); + if (addr == null) { + skipped++; + printerr("Line " + lineNo + ": invalid address: " + addrText); + continue; + } + + println("Adding bookmark at " + addr + " with description " + description); + bm.setBookmark(addr, BOOKMARK_TYPE, BOOKMARK_CATEGORY, description); + imported++; + } + } + + println("Imported " + imported + " bookmarks."); + if (skipped > 0) { + println("Skipped " + skipped + " lines."); + } + } + + public Address parseAddress(String text) { + text = text.trim(); + + if (text.startsWith("\"") && text.endsWith("\"") && text.length() >= 2) { + text = text.substring(1, text.length() - 1).trim(); + } + + if (text.startsWith("0x") || text.startsWith("0X")) { + text = text.substring(2); + } + + try { + return toAddr(text); + } catch (Exception e) { + return null; + } + } + + private String[] splitCsvLine(String line) { + java.util.ArrayList fields = new java.util.ArrayList<>(); + StringBuilder current = new StringBuilder(); + boolean inQuotes = false; + + for (int i = 0; i < line.length(); i++) { + char c = line.charAt(i); + + if (c == '"') { + if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == '"') { + current.append('"'); + i++; + } else { + inQuotes = !inQuotes; + } + } else if (c == ',' && !inQuotes) { + fields.add(current.toString()); + current.setLength(0); + } else { + current.append(c); + } + } + + fields.add(current.toString()); + return fields.toArray(new String[0]); + } +}