Skip to content

Commit

Permalink
feat(bytesto4t): add new tool Reference finder
Browse files Browse the repository at this point in the history
  • Loading branch information
FirowMD committed Jan 4, 2025
1 parent e74eb26 commit d321fd2
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 1 deletion.
51 changes: 51 additions & 0 deletions bytesto4t/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ struct HistoryItem {
timestamp: String,
}

#[derive(Debug)]
struct Reference {
element_index: usize,
references: Vec<String>
}

struct AppData {
target_file_path: String,
bytecode: Option<Bytecode>,
Expand All @@ -41,6 +47,7 @@ struct AppData {
selected_item: Option<AppItem>,
function_addresses: Option<Vec<String>>,
history_items: Mutex<Vec<HistoryItem>>,
references: Option<Reference>,
}

struct Storage {
Expand Down Expand Up @@ -510,6 +517,40 @@ fn get_inspector_info(app_data: State<Storage>) -> Result<String, String> {
Ok(info)
}

#[tauri::command]
fn clear_references(app_data: State<Storage>) -> Result<(), String> {
let mut app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
app_data.references = None;
Ok(())
}

#[tauri::command]
fn get_all_references(elem_idx: usize, app_data: State<Storage>) -> Result<Vec<String>, String> {
let mut app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
let bytecode = app_data.bytecode.as_ref().ok_or("bytecode not loaded")?;

let references = bytecode.functions
.iter()
.enumerate()
.flat_map(|(i, f)| {
f.find_elem_refs(elem_idx)
.map(move |(pos, op)| format!("{}{}@{}###{}###{}",
f.name(&bytecode),
f.findex,
i,
pos,
op.name()))
})
.collect();

app_data.references = Some(Reference {
element_index: elem_idx,
references
});

Ok(app_data.references.as_ref().unwrap().references.clone())
}

#[tauri::command]
fn get_disassembler_info(app_data: State<Storage>) -> Result<String, String> {
let app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
Expand Down Expand Up @@ -820,6 +861,12 @@ async fn get_history_items(
Ok(history.clone())
}

#[tauri::command]
fn get_saved_references(app_data: State<Storage>) -> Result<Option<(usize, Vec<String>)>, String> {
let app_data = app_data.app_data.lock().map_err(|e| e.to_string())?;
Ok(app_data.references.as_ref().map(|r| (r.element_index, r.references.clone())))
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
Expand All @@ -839,6 +886,7 @@ pub fn run() {
selected_item: None,
function_addresses: None,
history_items: Mutex::new(Vec::new()),
references: None,
}),
})
.plugin(tauri_plugin_shell::init())
Expand All @@ -858,6 +906,8 @@ pub fn run() {
set_selected_item,
get_selected_item_foffset,
get_inspector_info,
clear_references,
get_all_references,
get_disassembler_info,
read_binary_file,
load_function_addresses_from_file,
Expand All @@ -877,6 +927,7 @@ pub fn run() {
get_target_file_path,
add_history_item,
get_history_items,
get_saved_references,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
2 changes: 2 additions & 0 deletions bytesto4t/src/lib/PanelMain.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
import { onMount, onDestroy } from "svelte";
import { setContext } from 'svelte';
import { TabGroup, Tab } from '@skeletonlabs/skeleton';
import ViewDashboard from "./ViewDashboard.svelte";
import ViewInspector from "./ViewInspector.svelte";
Expand Down Expand Up @@ -68,6 +69,7 @@
});
onMount(() => {
setContext('tools', { elementIndex: null, references: [] });
window.addEventListener("bytecode-item-selected", bytecodeItemSelectedHandler);
loadFile();
});
Expand Down
3 changes: 3 additions & 0 deletions bytesto4t/src/lib/ViewFunctions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@
funcIndex = functionIndexElement.textContent.substring(1);
}
console.log("findex: `" + funcIndex + "`");
await invoke("set_selected_item", {
appItem: {
index: funcIndex,
typ: "function"
}
});
console.log("fullName: `" + funcName + functionIndexElement?.textContent + "`");
await invoke("add_history_item", {
item: {
name: funcName + functionIndexElement?.textContent,
Expand All @@ -51,6 +53,7 @@
}
});
console.log("fullName: `" + funcName + functionIndexElement?.textContent + "`");
const ev = new CustomEvent("bytecode-item-selected", {
detail: {
name: funcName + functionIndexElement?.textContent,
Expand Down
164 changes: 164 additions & 0 deletions bytesto4t/src/lib/ViewTools.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { open, message } from "@tauri-apps/plugin-dialog";
import { onMount } from "svelte";
import { BaseDirectory, readTextFile, writeFile } from "@tauri-apps/plugin-fs";
import VirtualList from 'svelte-tiny-virtual-list';
interface FileStatus {
name: string;
Expand All @@ -13,6 +14,8 @@
let addressesStatus: FileStatus | null = $state(null);
let filteredStatus: FileStatus | null = $state(null);
let recognizedPreview: string = $state("");
let elementIndex: number | null = $state(null);
let references: string[] = $state([]);
async function updateRecognizedPreview() {
if (!loadedContent) return;
Expand Down Expand Up @@ -244,9 +247,119 @@
}
}
async function onClickFindReferencesHandler() {
if (elementIndex === null) {
elementIndex = null;
references = [];
await invoke("clear_references");
return;
}
try {
references = await invoke("get_all_references", { elemIdx: elementIndex });
if (references.length === 0) {
await message(`No references found for element ${elementIndex}`, { title: "Info", kind: "info" });
}
} catch (error) {
await message(
`Failed to find references: ${error}`,
{ title: "Error", kind: "error" }
);
}
}
async function onClickReference(ref: string) {
const [funcPart] = ref.split('###');
const [name, id, findex] = funcPart.split('@');
const fullName = `${name}@${id}@${findex}`;
console.log("findex: `" + findex + "`");
await invoke("set_selected_item", {
appItem: {
index: findex,
typ: "function"
}
});
console.log("fullName: `" + fullName + "`");
await invoke("add_history_item", {
item: {
name: fullName,
typ: "function",
timestamp: new Date().toISOString()
}
});
console.log("fullName: `" + fullName + "`");
const ev = new CustomEvent("bytecode-item-selected", {
detail: {
name: fullName,
type: "function"
}
});
window.dispatchEvent(ev);
}
function parseReference(ref: string) {
const [funcPart, pos, op] = ref.split('###');
return { funcPart, pos, op };
}
let loadedContent: string | null = null;
async function loadSavedReferences() {
try {
const saved = await invoke<[number, string[]] | null>("get_saved_references");
if (saved) {
const [idx, refs] = saved;
elementIndex = idx;
references = refs;
}
} catch (error) {
console.error("Failed to load saved references:", error);
}
}
async function onClickSaveReferencesHandler() {
try {
if (references.length === 0) {
await message("No references to save", { title: "Error", kind: "error" });
return;
}
const result = await save({
defaultPath: `references_${elementIndex}.csv`,
title: "Save references",
filters: [{
name: "CSV Files",
extensions: ["csv"]
},
{
name: "All Files",
extensions: ["*"]
}]
});
if (result) {
const csvContent = references.map(ref => {
const { funcPart, pos, op } = parseReference(ref);
return `${funcPart},${pos},${op}`;
}).join('\n');
await writeFile(result, new TextEncoder().encode(csvContent));
}
} catch (error) {
await message(
`Failed to save references: ${error}`,
{ title: "Error", kind: "error" }
);
}
}
onMount(() => {
loadSavedReferences();
});
</script>

Expand Down Expand Up @@ -315,5 +428,56 @@
{/if}
</div>
</section>
<section class="card p-4 variant-soft-secondary space-y-2">
<div class="flex justify-between items-center">
<h4 class="h4">Reference finder</h4>
{#if references.length > 0}
<button
type="button"
class="btn variant-soft-secondary"
onclick={onClickSaveReferencesHandler}
>
Save to csv
</button>
{/if}
</div>
<div class="flex flex-row space-x-2">
<input
type="number"
class="input variant-form-material"
placeholder="Element index"
bind:value={elementIndex}
/>
<button
type="button"
class="btn variant-soft-secondary"
onclick={onClickFindReferencesHandler}
>
Find
</button>
</div>
{#if references.length > 0}
<div class="card p-2 variant-soft-secondary">
<VirtualList
itemCount={references.length}
itemSize={35}
height={400}
width="100%"
>
<div slot="item" let:index let:style {style}>
{@const { funcPart, pos, op } = parseReference(references[index])}
<button
class="grid grid-cols-3 gap-4 p-2 hover:bg-secondary-700/20 w-full text-left"
onclick={() => onClickReference(references[index])}
>
<div class="truncate">{funcPart}</div>
<div>{pos}</div>
<div>{op}</div>
</button>
</div>
</VirtualList>
</div>
{/if}
</section>
</div>
</div>
2 changes: 1 addition & 1 deletion external/hlbc

0 comments on commit d321fd2

Please sign in to comment.