Skip to content

Commit d321fd2

Browse files
committed
feat(bytesto4t): add new tool Reference finder
1 parent e74eb26 commit d321fd2

File tree

5 files changed

+221
-1
lines changed

5 files changed

+221
-1
lines changed

bytesto4t/src-tauri/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ struct HistoryItem {
3333
timestamp: String,
3434
}
3535

36+
#[derive(Debug)]
37+
struct Reference {
38+
element_index: usize,
39+
references: Vec<String>
40+
}
41+
3642
struct AppData {
3743
target_file_path: String,
3844
bytecode: Option<Bytecode>,
@@ -41,6 +47,7 @@ struct AppData {
4147
selected_item: Option<AppItem>,
4248
function_addresses: Option<Vec<String>>,
4349
history_items: Mutex<Vec<HistoryItem>>,
50+
references: Option<Reference>,
4451
}
4552

4653
struct Storage {
@@ -510,6 +517,40 @@ fn get_inspector_info(app_data: State<Storage>) -> Result<String, String> {
510517
Ok(info)
511518
}
512519

520+
#[tauri::command]
521+
fn clear_references(app_data: State<Storage>) -> Result<(), String> {
522+
let mut app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
523+
app_data.references = None;
524+
Ok(())
525+
}
526+
527+
#[tauri::command]
528+
fn get_all_references(elem_idx: usize, app_data: State<Storage>) -> Result<Vec<String>, String> {
529+
let mut app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
530+
let bytecode = app_data.bytecode.as_ref().ok_or("bytecode not loaded")?;
531+
532+
let references = bytecode.functions
533+
.iter()
534+
.enumerate()
535+
.flat_map(|(i, f)| {
536+
f.find_elem_refs(elem_idx)
537+
.map(move |(pos, op)| format!("{}{}@{}###{}###{}",
538+
f.name(&bytecode),
539+
f.findex,
540+
i,
541+
pos,
542+
op.name()))
543+
})
544+
.collect();
545+
546+
app_data.references = Some(Reference {
547+
element_index: elem_idx,
548+
references
549+
});
550+
551+
Ok(app_data.references.as_ref().unwrap().references.clone())
552+
}
553+
513554
#[tauri::command]
514555
fn get_disassembler_info(app_data: State<Storage>) -> Result<String, String> {
515556
let app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
@@ -820,6 +861,12 @@ async fn get_history_items(
820861
Ok(history.clone())
821862
}
822863

864+
#[tauri::command]
865+
fn get_saved_references(app_data: State<Storage>) -> Result<Option<(usize, Vec<String>)>, String> {
866+
let app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
867+
Ok(app_data.references.as_ref().map(|r| (r.element_index, r.references.clone())))
868+
}
869+
823870
#[cfg_attr(mobile, tauri::mobile_entry_point)]
824871
pub fn run() {
825872
tauri::Builder::default()
@@ -839,6 +886,7 @@ pub fn run() {
839886
selected_item: None,
840887
function_addresses: None,
841888
history_items: Mutex::new(Vec::new()),
889+
references: None,
842890
}),
843891
})
844892
.plugin(tauri_plugin_shell::init())
@@ -858,6 +906,8 @@ pub fn run() {
858906
set_selected_item,
859907
get_selected_item_foffset,
860908
get_inspector_info,
909+
clear_references,
910+
get_all_references,
861911
get_disassembler_info,
862912
read_binary_file,
863913
load_function_addresses_from_file,
@@ -877,6 +927,7 @@ pub fn run() {
877927
get_target_file_path,
878928
add_history_item,
879929
get_history_items,
930+
get_saved_references,
880931
])
881932
.run(tauri::generate_context!())
882933
.expect("error while running tauri application");

bytesto4t/src/lib/PanelMain.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script lang="ts">
22
import { invoke } from "@tauri-apps/api/core";
33
import { onMount, onDestroy } from "svelte";
4+
import { setContext } from 'svelte';
45
import { TabGroup, Tab } from '@skeletonlabs/skeleton';
56
import ViewDashboard from "./ViewDashboard.svelte";
67
import ViewInspector from "./ViewInspector.svelte";
@@ -68,6 +69,7 @@
6869
});
6970
7071
onMount(() => {
72+
setContext('tools', { elementIndex: null, references: [] });
7173
window.addEventListener("bytecode-item-selected", bytecodeItemSelectedHandler);
7274
loadFile();
7375
});

bytesto4t/src/lib/ViewFunctions.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
funcIndex = functionIndexElement.textContent.substring(1);
3737
}
3838
39+
console.log("findex: `" + funcIndex + "`");
3940
await invoke("set_selected_item", {
4041
appItem: {
4142
index: funcIndex,
4243
typ: "function"
4344
}
4445
});
4546
47+
console.log("fullName: `" + funcName + functionIndexElement?.textContent + "`");
4648
await invoke("add_history_item", {
4749
item: {
4850
name: funcName + functionIndexElement?.textContent,
@@ -51,6 +53,7 @@
5153
}
5254
});
5355
56+
console.log("fullName: `" + funcName + functionIndexElement?.textContent + "`");
5457
const ev = new CustomEvent("bytecode-item-selected", {
5558
detail: {
5659
name: funcName + functionIndexElement?.textContent,

bytesto4t/src/lib/ViewTools.svelte

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { open, message } from "@tauri-apps/plugin-dialog";
55
import { onMount } from "svelte";
66
import { BaseDirectory, readTextFile, writeFile } from "@tauri-apps/plugin-fs";
7+
import VirtualList from 'svelte-tiny-virtual-list';
78
89
interface FileStatus {
910
name: string;
@@ -13,6 +14,8 @@
1314
let addressesStatus: FileStatus | null = $state(null);
1415
let filteredStatus: FileStatus | null = $state(null);
1516
let recognizedPreview: string = $state("");
17+
let elementIndex: number | null = $state(null);
18+
let references: string[] = $state([]);
1619
1720
async function updateRecognizedPreview() {
1821
if (!loadedContent) return;
@@ -244,9 +247,119 @@
244247
}
245248
}
246249
250+
async function onClickFindReferencesHandler() {
251+
if (elementIndex === null) {
252+
elementIndex = null;
253+
references = [];
254+
await invoke("clear_references");
255+
return;
256+
}
257+
258+
try {
259+
references = await invoke("get_all_references", { elemIdx: elementIndex });
260+
261+
if (references.length === 0) {
262+
await message(`No references found for element ${elementIndex}`, { title: "Info", kind: "info" });
263+
}
264+
} catch (error) {
265+
await message(
266+
`Failed to find references: ${error}`,
267+
{ title: "Error", kind: "error" }
268+
);
269+
}
270+
}
271+
272+
async function onClickReference(ref: string) {
273+
const [funcPart] = ref.split('###');
274+
const [name, id, findex] = funcPart.split('@');
275+
const fullName = `${name}@${id}@${findex}`;
276+
277+
console.log("findex: `" + findex + "`");
278+
await invoke("set_selected_item", {
279+
appItem: {
280+
index: findex,
281+
typ: "function"
282+
}
283+
});
284+
285+
console.log("fullName: `" + fullName + "`");
286+
await invoke("add_history_item", {
287+
item: {
288+
name: fullName,
289+
typ: "function",
290+
timestamp: new Date().toISOString()
291+
}
292+
});
293+
294+
console.log("fullName: `" + fullName + "`");
295+
const ev = new CustomEvent("bytecode-item-selected", {
296+
detail: {
297+
name: fullName,
298+
type: "function"
299+
}
300+
});
301+
302+
window.dispatchEvent(ev);
303+
}
304+
305+
function parseReference(ref: string) {
306+
const [funcPart, pos, op] = ref.split('###');
307+
return { funcPart, pos, op };
308+
}
309+
247310
let loadedContent: string | null = null;
248311
312+
async function loadSavedReferences() {
313+
try {
314+
const saved = await invoke<[number, string[]] | null>("get_saved_references");
315+
if (saved) {
316+
const [idx, refs] = saved;
317+
elementIndex = idx;
318+
references = refs;
319+
}
320+
} catch (error) {
321+
console.error("Failed to load saved references:", error);
322+
}
323+
}
324+
325+
async function onClickSaveReferencesHandler() {
326+
try {
327+
if (references.length === 0) {
328+
await message("No references to save", { title: "Error", kind: "error" });
329+
return;
330+
}
331+
332+
const result = await save({
333+
defaultPath: `references_${elementIndex}.csv`,
334+
title: "Save references",
335+
filters: [{
336+
name: "CSV Files",
337+
extensions: ["csv"]
338+
},
339+
{
340+
name: "All Files",
341+
extensions: ["*"]
342+
}]
343+
});
344+
345+
if (result) {
346+
const csvContent = references.map(ref => {
347+
const { funcPart, pos, op } = parseReference(ref);
348+
return `${funcPart},${pos},${op}`;
349+
}).join('\n');
350+
351+
await writeFile(result, new TextEncoder().encode(csvContent));
352+
}
353+
} catch (error) {
354+
await message(
355+
`Failed to save references: ${error}`,
356+
{ title: "Error", kind: "error" }
357+
);
358+
}
359+
}
360+
249361
onMount(() => {
362+
loadSavedReferences();
250363
});
251364
</script>
252365

@@ -315,5 +428,56 @@
315428
{/if}
316429
</div>
317430
</section>
431+
<section class="card p-4 variant-soft-secondary space-y-2">
432+
<div class="flex justify-between items-center">
433+
<h4 class="h4">Reference finder</h4>
434+
{#if references.length > 0}
435+
<button
436+
type="button"
437+
class="btn variant-soft-secondary"
438+
onclick={onClickSaveReferencesHandler}
439+
>
440+
Save to csv
441+
</button>
442+
{/if}
443+
</div>
444+
<div class="flex flex-row space-x-2">
445+
<input
446+
type="number"
447+
class="input variant-form-material"
448+
placeholder="Element index"
449+
bind:value={elementIndex}
450+
/>
451+
<button
452+
type="button"
453+
class="btn variant-soft-secondary"
454+
onclick={onClickFindReferencesHandler}
455+
>
456+
Find
457+
</button>
458+
</div>
459+
{#if references.length > 0}
460+
<div class="card p-2 variant-soft-secondary">
461+
<VirtualList
462+
itemCount={references.length}
463+
itemSize={35}
464+
height={400}
465+
width="100%"
466+
>
467+
<div slot="item" let:index let:style {style}>
468+
{@const { funcPart, pos, op } = parseReference(references[index])}
469+
<button
470+
class="grid grid-cols-3 gap-4 p-2 hover:bg-secondary-700/20 w-full text-left"
471+
onclick={() => onClickReference(references[index])}
472+
>
473+
<div class="truncate">{funcPart}</div>
474+
<div>{pos}</div>
475+
<div>{op}</div>
476+
</button>
477+
</div>
478+
</VirtualList>
479+
</div>
480+
{/if}
481+
</section>
318482
</div>
319483
</div>

external/hlbc

0 commit comments

Comments
 (0)