Update readme and rename config java

This commit is contained in:
Guus Waals 2025-05-29 16:31:55 +08:00
parent d7de3deb59
commit 647e3668a0
28 changed files with 562 additions and 13162 deletions

View File

@ -1,7 +1,6 @@
function(setup_target TARGET)
function(setup_target TARGET DBG_MODE)
add_executable(${TARGET}
r3/main.cpp
r3/binders/static_mem.cxx
r3/main.cxx
gh_global.cxx
)
@ -63,9 +62,18 @@ function(setup_target TARGET)
target_precompile_headers(${TARGET} PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/r3/binders/auto_pch.cxx>"
)
if(DBG_MODE)
target_sources(${TARGET} PRIVATE
r3/binders/dbg_mem.cxx
)
target_compile_definitions(game_dbg PRIVATE RE_DBG_INJECTED=1)
else()
target_sources(${TARGET} PRIVATE
r3/binders/static_mem.cxx
)
endif()
endfunction()
setup_target(game_re)
setup_target(game_dbg)
target_compile_definitions(game_dbg PRIVATE RE_DBG_INJECTED=1)
setup_target(game_re OFF)
setup_target(game_dbg ON)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,12 @@
#ifndef B8D59B54_1674_4C0F_AA2C_611385FF5D03
#define B8D59B54_1674_4C0F_AA2C_611385FF5D03
#if RE_DBG_INJECTED
#include "dbg_mem.h"
#else
#include "static_mem.h"
#endif
#include "base.h"
#include <gh_types.h>
@ -108,6 +113,6 @@ inline longlong r3_ftol(float a) { return (longlong)a; }
constexpr byte R3ModId_not_initialized = 0xff;
#include "stubexcept.h"
#include "stub.h"
#endif /* B8D59B54_1674_4C0F_AA2C_611385FF5D03 */

View File

@ -0,0 +1,4 @@
#include "r3/config/static.hpp"
void gh_init_dbg_loader() {
}

View File

@ -0,0 +1,15 @@
#ifndef BD364AE6_AD96_4DEA_9D6B_B237BC1E2C6A
#define BD364AE6_AD96_4DEA_9D6B_B237BC1E2C6A
#include <gh_datasegment.h>
template <size_t addr> inline constexpr void checkMappedMemory() {
static_assert(addr >= GH_DATA_START, "Address outside lower bound");
static_assert(addr < GH_DATA_END, "Address outside upper bound");
}
void gh_init_dbg_loader();
#define GH_MEM(addr) (checkMappedMemory<addr>(), *memoryMapSafe(addr))
#endif /* BD364AE6_AD96_4DEA_9D6B_B237BC1E2C6A */

View File

@ -4,7 +4,11 @@
#define EDBE48FC_B879_4985_9274_B7ACF24AD024
#include "base.h"
#if RE_DBG_INJECTED
#include "dbg_mem.h"
#else
#include "static_mem.h"
#endif
#include <gh_types.h>
#endif /* EDBE48FC_B879_4985_9274_B7ACF24AD024 */

View File

@ -0,0 +1,5 @@
#include <spdlog/spdlog.h>
GHStubException::GHStubException(const char *msg) : std::exception(msg) {
SPDLOG_ERROR("{}", msg);
}

34
game_re/r3/binders/stub.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef AE625BF8_B0F9_452E_8772_8819F311CB57
#define AE625BF8_B0F9_452E_8772_8819F311CB57
#include <stdexcept>
struct GHStubException : public std::exception {
GHStubException(const char *msg);
};
void *gh_stub_impl_ptr(void *ptr);
template <typename T, typename... Args>
T gh_stub_impl_cdecl(void *ptr, Args... args) {
#if RE_DBG_INJECTED
using Callable = __cdecl T (*)(Args...);
static Callable *ptr = (Callable *)gh_stub_impl_ptr(ptr);
return ptr(args...);
#else
throw GHStubException("Function not implemented");
#endif
}
template <typename T, typename... Args>
T gh_stub_impl_stdcall(void *ptr, Args... args) {
#if RE_DBG_INJECTED
using Callable = __stdcall T (*)(Args...);
static Callable *ptr = (Callable *)gh_stub_impl_ptr(ptr);
return ptr(args...);
#else
throw GHStubException("Function not implemented");
#endif
}
#endif /* AE625BF8_B0F9_452E_8772_8819F311CB57 */

View File

@ -1,10 +0,0 @@
#ifndef AE625BF8_B0F9_452E_8772_8819F311CB57
#define AE625BF8_B0F9_452E_8772_8819F311CB57
#include <stdexcept>
struct GHStubException : public std::exception {
GHStubException(const char *msg);
};
#endif /* AE625BF8_B0F9_452E_8772_8819F311CB57 */

View File

@ -2,22 +2,27 @@
#include <spdlog/spdlog.h>
#include <stdexcept>
#include <stdio.h>
#include <r3/binders/stub.h>
#if RE_DBG_INJECTED
#include <r3/binders/dbg_mem.h>
#else
#include <r3/binders/static_mem.h>
#include <r3/binders/stubexcept.h>
#endif
// Error reporting and such
extern "C" void r3_noop(void*, void*) {}
extern "C" void r3_noop(void *, void *) {}
extern "C" int r3_main(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR *cmdline, int showCmd);
GHStubException::GHStubException(const char *msg) : std::exception(msg) {
SPDLOG_ERROR("{}", msg);
}
int main(int argc, char **argv) {
try {
#if RE_DBG_INJECTED
gh_init_dbg_loader();
#else
gh_init_data_segment();
#endif
r3_main(GetModuleHandle(NULL), NULL, argv, SW_SHOW);
} catch (const std::exception &e) {

View File

@ -150,16 +150,16 @@ public class Decompile extends GhidraScript {
return;
}
RecompileConfig.INSTANCE = new RecompileConfig(this);
RemanConfig.INSTANCE = new RemanConfig(this);
if (!new File(RecompileConfig.INSTANCE.outputDir).exists()) {
throw new Exception("Output directory does not exist: " + RecompileConfig.INSTANCE.outputDir);
if (!new File(RemanConfig.INSTANCE.outputDir).exists()) {
throw new Exception("Output directory does not exist: " + RemanConfig.INSTANCE.outputDir);
}
// Make sure to create output folders
RecompileConfig.INSTANCE.dirDecompFix.mkdirs();
RecompileConfig.INSTANCE.dirDecompAuto.mkdirs();
RecompileConfig.INSTANCE.dirDecompRef.mkdirs();
RemanConfig.INSTANCE.dirDecompFix.mkdirs();
RemanConfig.INSTANCE.dirDecompAuto.mkdirs();
RemanConfig.INSTANCE.dirDecompRef.mkdirs();
// buildFunctionBlacklist();

View File

@ -6,13 +6,13 @@ import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.RecompileConfig;
import re3lib.RemanConfig;
public class DumpCurrentFunction extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
@ -26,7 +26,7 @@ public class DumpCurrentFunction extends GhidraScript {
}
if (functionDumper.createdFile)
RecompileConfig.INSTANCE.touchCMakeTimestamp();
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();

View File

@ -12,7 +12,7 @@ import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
import re3lib.RecompileConfig;
import re3lib.RemanConfig;
import re3lib.TypeDumper;
public class DumpCurrentFunctionN extends GhidraScript {
@ -43,8 +43,8 @@ public class DumpCurrentFunctionN extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();

View File

@ -10,14 +10,14 @@ import ghidra.program.model.listing.Function;
import re3lib.FunctionDumper;
import re3lib.GlobalDumper;
import re3lib.PCallTracer;
import re3lib.RecompileConfig;
import re3lib.RemanConfig;
import re3lib.TypeDumper;
public class DumpCurrentFunctionRecursive extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();
@ -65,7 +65,7 @@ public class DumpCurrentFunctionRecursive extends GhidraScript {
}
if (functionDumper.createdFile)
RecompileConfig.INSTANCE.touchCMakeTimestamp();
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();

View File

@ -7,7 +7,7 @@ import java.io.FileOutputStream;
import java.io.PrintWriter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import re3lib.RecompileConfig;
import re3lib.RemanConfig;
public class ExportData extends GhidraScript {
@ -16,16 +16,16 @@ public class ExportData extends GhidraScript {
if (currentProgram == null) {
return;
}
RecompileConfig.INSTANCE = new RecompileConfig(this);
RemanConfig.INSTANCE = new RemanConfig(this);
String dataFile = new File(RecompileConfig.INSTANCE.outputDir, "gh_datasegment.bin").toString();
String headerFile = new File(RecompileConfig.INSTANCE.outputDir, "gh_datasegment.h").toString();
String dataFile = new File(RemanConfig.INSTANCE.outputDir, "gh_datasegment.bin").toString();
String headerFile = new File(RemanConfig.INSTANCE.outputDir, "gh_datasegment.h").toString();
FileOutputStream dataOutputStream = new FileOutputStream(dataFile);
PrintWriter headerWriter = new PrintWriter(headerFile, "UTF-8");
Address startAddr = RecompileConfig.INSTANCE.staticMemoryBlockStart;
Address endAddr = RecompileConfig.INSTANCE.staticMemoryBlockEnd;
Address startAddr = RemanConfig.INSTANCE.staticMemoryBlockStart;
Address endAddr = RemanConfig.INSTANCE.staticMemoryBlockEnd;
// Dump all the memory to the bin file
int numBytes = (int) endAddr.subtract(startAddr);

View File

@ -4,13 +4,13 @@
import ghidra.app.script.GhidraScript;
import re3lib.GlobalDumper;
import re3lib.RecompileConfig;
import re3lib.RemanConfig;
public class SanitizeGlobalSymbols extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
RecompileConfig.INSTANCE.createDirectories();
RemanConfig.INSTANCE = new RemanConfig(this);
RemanConfig.INSTANCE.createDirectories();
GlobalDumper globalDumper = new GlobalDumper(this);
globalDumper.loadGlobalManifest();

View File

@ -2,83 +2,22 @@
// @menupath Reman3.Test
// @importpackage org.sqlite
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.StandAloneDataTypeManager;
import re3lib.RecompileConfig;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// SQLite imports
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.sqlite.JDBC;
import ghidra.app.script.GhidraScript;
import re3lib.FunctionDatabase;
import re3lib.RemanConfig;
public class Test extends GhidraScript {
@Override
public void run() throws Exception {
RecompileConfig.INSTANCE = new RecompileConfig(this);
java.sql.DriverManager.registerDriver(new JDBC());
RemanConfig.INSTANCE = new RemanConfig(this);
// Example SQLite usage
testSQLite();
}
private void testSQLite() throws Exception {
String dbPath = "jdbc:sqlite:" + RecompileConfig.INSTANCE.outputDir + "/functions.db";
try (Connection conn = DriverManager.getConnection(dbPath)) {
println("Connected to SQLite database: " + dbPath);
// Create a simple table
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS functions (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"address TEXT NOT NULL, " +
"name TEXT NOT NULL, " +
"file_path TEXT)");
println("Functions table created/verified");
}
// Insert example data
String insertSQL = "INSERT INTO functions (address, name, file_path) VALUES (?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
pstmt.setString(1, "0x00401000");
pstmt.setString(2, "main");
pstmt.setString(3, "/path/to/main.cxx");
pstmt.executeUpdate();
println("Inserted example function");
}
// Query data
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM functions")) {
while (rs.next()) {
println("Function: " + rs.getString("name") +
" at " + rs.getString("address") +
" in " + rs.getString("file_path"));
}
}
} catch (SQLException e) {
println("SQLite error: " + e.getMessage());
throw e;
FunctionDatabase db = new FunctionDatabase(this);
List<FunctionDatabase.Entry> entries = db.loadAllEntries();
for (FunctionDatabase.Entry entry : entries) {
println("entry.name: " + entry.name + " entry.address: " + entry.address + " entry.type: " + entry.type);
}
}
}

View File

@ -1,6 +1,11 @@
package re3lib;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -8,106 +13,349 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.sqlite.JDBC;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class FunctionDatabase {
public enum Type {
Auto,
Fix,
Stub,
Ref
}
Auto(0),
Fix(1),
Stub(2),
Ref(3);
public class Dependency implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public Address address;
public String name;
private final int value;
public Dependency(Address address, String name) {
this.address = address;
this.name = name;
Type(int value) {
this.value = value;
}
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.writeObject(address != null ? address.toString() : null);
out.writeObject(name);
public int getValue() {
return value;
}
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
String addressString = (String) in.readObject();
if (addressString != null) {
address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString);
public static Type fromValue(int value) {
for (Type type : Type.values()) {
if (type.value == value) {
return type;
}
}
name = (String) in.readObject();
throw new IllegalArgumentException("Unknown type value: " + value);
}
}
public class Entry implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public class Entry {
public Address address;
public String name;
public File file;
public Type type;
public List<Dependency> dependencies = new ArrayList<>();
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.writeObject(address != null ? address.toString() : null);
out.writeObject(name);
out.writeObject(file != null ? file.toString() : null);
out.writeObject(type);
out.writeObject(dependencies);
}
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
String addressString = (String) in.readObject();
if (addressString != null) {
address = RecompileConfig.INSTANCE.script.getCurrentProgram().getAddressFactory().getAddress(addressString);
}
name = (String) in.readObject();
String fileString = (String) in.readObject();
if (fileString != null) {
file = new File(fileString);
}
type = (Type) in.readObject();
dependencies = (List<Dependency>) in.readObject();
public Entry(Address address, String name, File file, Type type) {
this.address = address;
this.name = name;
this.file = file;
this.type = type;
}
}
public List<Entry> entries = new ArrayList<>();
private File file;
private File dbFile;
private transient GhidraScript script;
private Connection connection;
// Prepared statements for better performance
private PreparedStatement findByNameFunctions;
private PreparedStatement findByNameImports;
private PreparedStatement findByAddressFunctions;
private PreparedStatement findByAddressImports;
private PreparedStatement insertOrReplaceFunctions;
private PreparedStatement deleteByFilepathFunctions;
private PreparedStatement deleteByFilepathImports;
private PreparedStatement loadAllFunctions;
private PreparedStatement loadAllImports;
public FunctionDatabase(GhidraScript script) {
this.script = script;
file = new File(RecompileConfig.INSTANCE.outputDir, "functions.dat");
}
public void load() throws Exception {
if (!file.exists()) {
return;
}
try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new java.io.FileInputStream(file))) {
entries = (List<Entry>) ois.readObject();
script.println("Loaded " + entries.size() + " function entries from " + file);
} catch (java.io.IOException | ClassNotFoundException e) {
script.println("Error loading function database: " + e.getMessage());
dbFile = RemanConfig.INSTANCE.databasePath;
try {
java.sql.DriverManager.registerDriver(new JDBC());
} catch (SQLException e) {
script.printerr("Error registering JDBC driver: " + e.getMessage());
}
}
public void save() throws Exception {
try (java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(new java.io.FileOutputStream(file))) {
oos.writeObject(entries);
script.println("Saved " + entries.size() + " function entries to " + file);
} catch (java.io.IOException e) {
script.println("Error saving function database: " + e.getMessage());
public void connect() throws Exception {
if (connection != null && !connection.isClosed()) {
return; // Already connected
}
if (!dbFile.exists()) {
script.println("Database file not found: " + dbFile);
// Create parent directories if they don't exist
dbFile.getParentFile().mkdirs();
}
try {
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
createTablesIfNotExist();
prepareCachedStatements();
script.println("Connected to database: " + dbFile);
} catch (SQLException e) {
script.println("Error connecting to database: " + e.getMessage());
throw new Exception("Failed to connect to database", e);
}
}
public void add(Entry entry) {
entries.add(entry);
public void disconnect() throws Exception {
if (connection != null && !connection.isClosed()) {
try {
// Close prepared statements
closePreparedStatements();
connection.close();
script.println("Disconnected from database");
} catch (SQLException e) {
script.println("Error disconnecting from database: " + e.getMessage());
throw new Exception("Failed to disconnect from database", e);
}
}
}
private void ensureConnection() throws Exception {
if (connection == null || connection.isClosed()) {
connect();
}
}
private void prepareCachedStatements() throws SQLException {
// Find by name
findByNameFunctions = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Functions WHERE name = ?");
findByNameImports = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Imports WHERE name = ?");
// Find by address
findByAddressFunctions = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Functions WHERE address = ?");
findByAddressImports = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Imports WHERE address = ?");
// Insert or replace
insertOrReplaceFunctions = connection.prepareStatement(
"INSERT OR REPLACE INTO Functions (filepath, name, address, type) VALUES (?, ?, ?, ?)");
// Delete by filepath
deleteByFilepathFunctions = connection.prepareStatement(
"DELETE FROM Functions WHERE filepath = ?");
deleteByFilepathImports = connection.prepareStatement(
"DELETE FROM Imports WHERE filepath = ?");
// Load all entries
loadAllFunctions = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Functions");
loadAllImports = connection.prepareStatement(
"SELECT filepath, name, address, type FROM Imports");
}
private void closePreparedStatements() throws SQLException {
if (findByNameFunctions != null)
findByNameFunctions.close();
if (findByNameImports != null)
findByNameImports.close();
if (findByAddressFunctions != null)
findByAddressFunctions.close();
if (findByAddressImports != null)
findByAddressImports.close();
if (insertOrReplaceFunctions != null)
insertOrReplaceFunctions.close();
if (deleteByFilepathFunctions != null)
deleteByFilepathFunctions.close();
if (deleteByFilepathImports != null)
deleteByFilepathImports.close();
if (loadAllFunctions != null)
loadAllFunctions.close();
if (loadAllImports != null)
loadAllImports.close();
}
public List<Entry> loadAllEntries() throws Exception {
ensureConnection();
List<Entry> entries = new ArrayList<>();
try {
// Load from Functions table
try (ResultSet rs = loadAllFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
if (entry != null) {
entries.add(entry);
}
}
}
script.println("Loaded " + entries.size() + " function entries from database");
return entries;
} catch (SQLException e) {
script.println("Error loading entries: " + e.getMessage());
throw new Exception("Failed to load entries", e);
}
}
private Entry createEntryFromResultSet(ResultSet rs) throws SQLException {
String filepath = rs.getString("filepath");
String name = rs.getString("name");
String addressStr = rs.getString("address");
int typeValue = rs.getInt("type");
if (addressStr != null && !addressStr.isEmpty()) {
Address address = script.getCurrentProgram().getAddressFactory().getAddress(addressStr);
File file = new File(RemanConfig.INSTANCE.outputDir, filepath);
Type type = Type.fromValue(typeValue);
return new Entry(address, name, file, type);
}
return null;
}
private void createTablesIfNotExist() throws SQLException {
String createFunctions = """
CREATE TABLE IF NOT EXISTS Functions (
filepath TEXT,
name TEXT,
address TEXT,
type INTEGER,
PRIMARY KEY (name, filepath)
)""";
String createImports = """
CREATE TABLE IF NOT EXISTS Imports (
filepath TEXT,
name TEXT,
address TEXT,
type INTEGER,
PRIMARY KEY (name, filepath)
)""";
connection.prepareStatement(createFunctions).executeUpdate();
connection.prepareStatement(createImports).executeUpdate();
}
// Helper method to find entries by name
public List<Entry> findEntriesByName(String name) throws Exception {
ensureConnection();
List<Entry> results = new ArrayList<>();
try {
// Search Functions table
findByNameFunctions.setString(1, name);
try (ResultSet rs = findByNameFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
// Search Imports table
findByNameImports.setString(1, name);
try (ResultSet rs = findByNameImports.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
return results;
} catch (SQLException e) {
script.println("Error finding entries by name: " + e.getMessage());
throw new Exception("Failed to find entries by name", e);
}
}
// Helper method to find entries by address
public List<Entry> findEntriesByAddress(Address address) throws Exception {
ensureConnection();
List<Entry> results = new ArrayList<>();
String addressStr = address.toString();
try {
// Search Functions table
findByAddressFunctions.setString(1, addressStr);
try (ResultSet rs = findByAddressFunctions.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
// Search Imports table
findByAddressImports.setString(1, addressStr);
try (ResultSet rs = findByAddressImports.executeQuery()) {
while (rs.next()) {
Entry entry = createEntryFromResultSet(rs);
if (entry != null) {
results.add(entry);
}
}
}
return results;
} catch (SQLException e) {
script.println("Error finding entries by address: " + e.getMessage());
throw new Exception("Failed to find entries by address", e);
}
}
// Helper method to add/update entry (insert or replace based on filename)
public void addEntryAt(Entry entry) throws Exception {
ensureConnection();
String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath()
.relativize(entry.file.toPath()).toString().replace('\\', '/');
try {
insertOrReplaceFunctions.setString(1, relativePath);
insertOrReplaceFunctions.setString(2, entry.name);
insertOrReplaceFunctions.setString(3, entry.address.toString());
insertOrReplaceFunctions.setInt(4, entry.type.getValue());
insertOrReplaceFunctions.executeUpdate();
script.println("Added/updated entry: " + entry.name + " at " + entry.address + " in " + relativePath);
} catch (SQLException e) {
script.println("Error adding entry: " + e.getMessage());
throw new Exception("Failed to add entry", e);
}
}
// Helper method to remove entry by file path
public void removeEntryAt(String filePath) throws Exception {
ensureConnection();
String relativePath = new File(RemanConfig.INSTANCE.outputDir).toPath()
.relativize(new File(filePath).toPath()).toString().replace('\\', '/');
try {
deleteByFilepathFunctions.setString(1, relativePath);
int deletedCount = deleteByFilepathFunctions.executeUpdate();
deleteByFilepathImports.setString(1, relativePath);
deletedCount += deleteByFilepathImports.executeUpdate();
script.println("Removed " + deletedCount + " entries for file: " + relativePath);
} catch (SQLException e) {
script.println("Error removing entries: " + e.getMessage());
throw new Exception("Failed to remove entries", e);
}
}
public void add(Entry entry) throws Exception {
// Add entry directly to database
addEntryAt(entry);
}
public void applyDefaultFilters(boolean rebuildAllGlobals) throws Exception {
@ -120,13 +368,15 @@ public class FunctionDatabase {
boolean madeAnyChanges = false;
// Load all entries from database
List<Entry> entries = loadAllEntries();
// Create a hash map to store symbol names
Map<Address, String> symbolNames = new HashMap<>();
Map<String, File> exportedFunctionNames = new HashMap<>();
for (Entry entry : entries) {
Function function = script.getFunctionAt(entry.address);
if (function != null) {
String dirComponent = entry.file.getParent().toString();
boolean isAuto = entry.type == Type.Auto;
boolean isFix = entry.type == Type.Fix;
// Get the actual symbol name and store it in the hash map
@ -197,21 +447,6 @@ public class FunctionDatabase {
}
}
// Check if dependencies are valid
for (Dependency dependency : entry.dependencies) {
Function depFunction = script.getFunctionAt(dependency.address);
if (depFunction == null) {
script.println(
"Dependency not found: " + dependency.name + " at " + dependency.address + " in " + entry.file);
pendingRegenerate = true;
} else if (!dumper.isValidFunction(depFunction) || !depFunction.getName().equals(dependency.name)) {
script
.println("Invalid dependency: " + dependency.name + " at " + dependency.address + " in " + entry.file
+ " should be " + dependency.name);
pendingRegenerate = true;
}
}
entry.name = actualSymbolName; // Update the entry name to match the actual symbol
madeAnyChanges = true;
}
@ -220,11 +455,15 @@ public class FunctionDatabase {
iterator.remove();
if (!dryMode) {
entry.file.delete();
// Remove from database
removeEntryAt(entry.file.getAbsolutePath());
madeAnyChanges = true;
}
} else if (pendingRegenerate && entry.type != Type.Stub) {
if (!dryMode) {
functionsToRegenerate.add(function);
// Update entry in database with corrected name
addEntryAt(entry);
madeAnyChanges = true;
}
}
@ -237,7 +476,7 @@ public class FunctionDatabase {
if (madeAnyChanges) {
// Update CMake timestamp
RecompileConfig.INSTANCE.touchCMakeTimestamp();
RemanConfig.INSTANCE.touchCMakeTimestamp();
globalDumper.dumpGlobals();
globalDumper.saveGlobalManifest();

View File

@ -28,6 +28,7 @@ import re3lib.GlobalDumper.GlobalRec;
public class FunctionDumper {
GhidraScript script;
GlobalDumper globalDumper;
FunctionDatabase functionDatabase;
public HashSet<Address> functionAddrBlackList = new HashSet<>();
@ -37,8 +38,9 @@ public class FunctionDumper {
static final Pattern fieldAccessRegex = Pattern.compile("^_([0-9]+)_([0-9]+)_$");
public FunctionDumper(GhidraScript script, GlobalDumper globalDumper) {
public FunctionDumper(GhidraScript script, FunctionDatabase functionDatabase, GlobalDumper globalDumper) {
this.script = script;
this.functionDatabase = functionDatabase;
this.globalDumper = globalDumper;
initFunctionBlacklist();
}
@ -58,7 +60,7 @@ public class FunctionDumper {
}
void initFunctionBlacklist() {
functionAddrBlackList = Utils.loadFunctionBlacklist(RecompileConfig.INSTANCE.functionBlacklistPath);
functionAddrBlackList = Utils.loadFunctionBlacklist(RemanConfig.INSTANCE.functionBlacklistPath);
// Build blacklist if not loaded
if (functionAddrBlackList == null) {
@ -99,7 +101,7 @@ public class FunctionDumper {
}
if (modified) {
Utils.saveFunctionBlacklist(functionAddrBlackList, RecompileConfig.INSTANCE.functionBlacklistPath);
Utils.saveFunctionBlacklist(functionAddrBlackList, RemanConfig.INSTANCE.functionBlacklistPath);
}
}
}
@ -107,14 +109,14 @@ public class FunctionDumper {
public static boolean isDumpedFix(Function function) {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx";
File f0 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
File f0 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName);
return f0.exists();
}
public static boolean isDumpedAuto(Function function) {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx";
File f0 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
File f0 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName);
return f0.exists();
}
@ -123,20 +125,21 @@ public class FunctionDumper {
String sanitizedFunctionName = Utils.sanitizeIdentifier(function.getName());
String fileName = sanitizedFunctionName + ".cxx";
// Remove the stub file, since we now use the decompiled file
File stubFile = new File(RecompileConfig.INSTANCE.dirDecompStub, fileName);
File stubFile = new File(RemanConfig.INSTANCE.dirDecompStub, fileName);
if (stubFile.exists()) {
script.println("Removing function stub " + stubFile);
stubFile.delete();
createdFile = true;
}
File f0 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
File f0 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName);
if (f0.exists()) {
script.println("Func " + function.getName() + " skipped (gh_fix)");
f0 = new File(RecompileConfig.INSTANCE.dirDecompRef, fileName);
f0 = new File(RemanConfig.INSTANCE.dirDecompRef, fileName);
} else {
f0 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
f0 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName);
if (f0.exists()) {
f0.delete();
} else {
@ -148,7 +151,7 @@ public class FunctionDumper {
List<Function> externalFunctionCalls = new ArrayList<>();
DecompileResults decompRes = RecompileConfig.INSTANCE.decompCache.getOrInsert(function);
DecompileResults decompRes = RemanConfig.INSTANCE.decompCache.getOrInsert(function);
try (PrintWriter writer2 = new PrintWriter(f0, "UTF-8")) {
writer2.println("// AUTO-GENERATED FILE, MOVE TO 'gh_fix' FOLDER PREVENT OVERWRITING!!!!! ");
writer2.println();
@ -291,15 +294,15 @@ public class FunctionDumper {
for (Function externalFunction : externalFunctionCalls) {
String sanitizedExtFunctionName = Utils.sanitizeIdentifier(externalFunction.getName());
fileName = sanitizedExtFunctionName + ".cxx";
File f2 = new File(RecompileConfig.INSTANCE.dirDecompFix, fileName);
File f3 = new File(RecompileConfig.INSTANCE.dirDecompAuto, fileName);
File f2 = new File(RemanConfig.INSTANCE.dirDecompFix, fileName);
File f3 = new File(RemanConfig.INSTANCE.dirDecompAuto, fileName);
if (f2.exists() || f3.exists()) {
// script.println("Skipping external function: " + externalFunction.getName() +
// " - " + externalFunction.getEntryPoint());
continue;
}
File f4 = new File(RecompileConfig.INSTANCE.dirDecompStub, fileName);
File f4 = new File(RemanConfig.INSTANCE.dirDecompStub, fileName);
script.println("Generating function stub for " + externalFunction.getName() + " => " + f4.toString());
try (PrintWriter writer2 = new PrintWriter(f4, "UTF-8")) {

View File

@ -25,7 +25,6 @@ import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.DataTypePath;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Undefined;
import ghidra.program.model.listing.Data;
import ghidra.program.model.pcode.HighSymbol;
import ghidra.program.model.symbol.SourceType;
@ -49,11 +48,13 @@ public class GlobalDumper {
GhidraScript script;
File manifestFile;
FunctionDatabase functionDatabase;
HashMap<Address, GlobalRec> globalAddrs = new HashMap<>();
public GlobalDumper(GhidraScript script) {
public GlobalDumper(GhidraScript script, FunctionDatabase functionDatabase) {
this.script = script;
manifestFile = new File(RecompileConfig.INSTANCE.outputDir, "globals.txt");
this.functionDatabase = functionDatabase;
manifestFile = new File(RemanConfig.INSTANCE.outputDir, "globals.txt");
}
public void removeGlobalManifest() {
@ -108,8 +109,8 @@ public class GlobalDumper {
}
public void addGlobal(Address addr, HighSymbol sym) throws Exception {
if (addr.compareTo(RecompileConfig.INSTANCE.staticMemoryBlockStart) < 0
|| addr.compareTo(RecompileConfig.INSTANCE.staticMemoryBlockEnd) > 0) {
if (addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockStart) < 0
|| addr.compareTo(RemanConfig.INSTANCE.staticMemoryBlockEnd) > 0) {
throw new Exception("Global address out of range: " + addr);
}
@ -162,14 +163,14 @@ public class GlobalDumper {
}
public void dumpGlobals() throws Exception {
File globalSymbolsListH = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.h");
File globalSymbolsListH = new File(RemanConfig.INSTANCE.outputDir, "gh_global.h");
PrintWriter hwriter = new PrintWriter(globalSymbolsListH, "UTF-8");
hwriter.println("// AUTO-GENERATED FILE ");
Utils.headerGuardPre(hwriter, "GLOBALS");
hwriter.println("#include <r3/binders/global.h>");
hwriter.println();
File globalSymbolsListC = new File(RecompileConfig.INSTANCE.outputDir, "gh_global.cxx");
File globalSymbolsListC = new File(RemanConfig.INSTANCE.outputDir, "gh_global.cxx");
PrintWriter cwriter = new PrintWriter(globalSymbolsListC, "UTF-8");
cwriter.println("// AUTO-GENERATED FILE ");
cwriter.println("#include <r3/binders/global.h>");

View File

@ -42,7 +42,7 @@ public class PCallTracer {
Program program;
public PCallTracer() {
this.script = RecompileConfig.INSTANCE.script;
this.script = RemanConfig.INSTANCE.script;
this.program = this.script.getCurrentProgram();
// this.decomp = RecompileConfig.INSTANCE.decompCache;
}

View File

@ -10,7 +10,9 @@ import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
public class RecompileConfig {
// A general configuration for the reman project
// including all paths, specific addresses and stuff
public class RemanConfig {
private static final String RECOMPILE_PREFIX = "game_re";
// Version control project root
@ -24,6 +26,9 @@ public class RecompileConfig {
public final Address staticMemoryBlockStart;
public final Address staticMemoryBlockEnd;
// The path to the database file
public final File databasePath;
// The automatically decompiled files
public final File dirDecompAuto;
// The manually decompiled files (will not be overwritten by the auto
@ -48,9 +53,9 @@ public class RecompileConfig {
public final GhidraScript script;
public static RecompileConfig INSTANCE;
public static RemanConfig INSTANCE;
public RecompileConfig(GhidraScript script) {
public RemanConfig(GhidraScript script) {
staticMemoryBlockStart = script.getCurrentProgram().getAddressFactory().getAddress("00597000");
staticMemoryBlockEnd = script.getCurrentProgram().getAddressFactory().getAddress("00843fff");
@ -64,6 +69,8 @@ public class RecompileConfig {
categoryPathBlacklistPath = new File(outputDir, "type_path_blacklist.txt").toString();
functionBlacklistPath = new File(outputDir, "function_blacklist.txt").toString();
databasePath = new File(outputDir, "gh.db");
dirDecompAuto = new File(outputDir, "gh_auto");
dirDecompFix = new File(outputDir, "gh_fix");
dirDecompRef = new File(outputDir, "gh_ref");

View File

@ -25,15 +25,15 @@ public class TypeDumper {
public TypeDumper(GhidraScript script) {
this.script = script;
currentProgram = script.getCurrentProgram();
RecompileConfig.INSTANCE = new RecompileConfig(script);
RemanConfig.INSTANCE = new RemanConfig(script);
}
public void run() throws Exception {
ProgramBasedDataTypeManager dtm = currentProgram.getDataTypeManager();
HashSet<String> typeBlacklist = Utils.loadSimpleBlacklist(RecompileConfig.INSTANCE.typeBlacklistPath);
HashSet<String> typeBlacklist = Utils.loadSimpleBlacklist(RemanConfig.INSTANCE.typeBlacklistPath);
HashSet<String> categoryPathBlacklist = Utils
.loadSimpleBlacklist(RecompileConfig.INSTANCE.categoryPathBlacklistPath);
.loadSimpleBlacklist(RemanConfig.INSTANCE.categoryPathBlacklistPath);
if (typeBlacklist == null) {
script.println("Building struct blacklist from existing data types");
@ -45,7 +45,7 @@ public class TypeDumper {
typeBlacklist.add(dt.getDisplayName());
}
}
Utils.saveStructBlacklist(typeBlacklist, RecompileConfig.INSTANCE.typeBlacklistPath);
Utils.saveStructBlacklist(typeBlacklist, RemanConfig.INSTANCE.typeBlacklistPath);
}
List<DataType> filteredTypes = new ArrayList<>();
@ -92,7 +92,7 @@ public class TypeDumper {
}
}
try (PrintWriter writer = new PrintWriter(new File(RecompileConfig.INSTANCE.outputDir, "gh_types.h"),
try (PrintWriter writer = new PrintWriter(new File(RemanConfig.INSTANCE.outputDir, "gh_types.h"),
"UTF-8")) {
Utils.headerGuardPre(writer, "STRUCTS");
writer.println("// AUTO-GENERATED FILE ");

View File

@ -51,7 +51,7 @@ public class Utils {
}
public static HashSet<Address> loadFunctionBlacklist(String path) {
GhidraScript script = RecompileConfig.INSTANCE.script;
GhidraScript script = RemanConfig.INSTANCE.script;
HashSet<Address> fnBlacklist = new HashSet<>();
File blacklistFile = new File(path);
try (Scanner scanner = new Scanner(blacklistFile)) {
@ -60,7 +60,7 @@ public class Utils {
// Strip comment
String line1 = line.split("//")[0].trim();
// Deserialize address
Address addr = RecompileConfig.INSTANCE.currentProgram.getAddressFactory().getAddress(line1);
Address addr = RemanConfig.INSTANCE.currentProgram.getAddressFactory().getAddress(line1);
fnBlacklist.add(addr);
}
script.println("Loaded blacklist with " + fnBlacklist.size() + " entries");
@ -71,7 +71,7 @@ public class Utils {
}
public static void saveFunctionBlacklist(HashSet<Address> fnBlacklist, String path) {
GhidraScript script = RecompileConfig.INSTANCE.script;
GhidraScript script = RemanConfig.INSTANCE.script;
File blacklistFile = new File(path);
try (PrintWriter writer = new PrintWriter(blacklistFile)) {
for (Address addr : fnBlacklist) {

View File

@ -11,3 +11,63 @@ The decompile database is a sqlite database that contains a list of all files th
To generate the database from the current set of files, run the scan_sources script in the /game_re folder.
Make sure you have set up the tooling by running the /tooling/setup script.
## IDE Notes
This should work with the redhat java plugin for vscode, you however need to manually add the referenced libraries to the settings, like so:
```json
{
"java.project.referencedLibraries": [
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Base\\lib\\Base.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Decompiler\\lib\\Decompiler.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\BSim\\lib\\BSim.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\BSimFeatureVisualizer\\lib\\BSimFeatureVisualizer.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Base\\lib\\Base.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\BytePatterns\\lib\\BytePatterns.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\ByteViewer\\lib\\ByteViewer.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\CodeCompare\\lib\\CodeCompare.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\DebugUtils\\lib\\DebugUtils.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Decompiler\\lib\\Decompiler.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\DecompilerDependent\\lib\\DecompilerDependent.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\FileFormats\\lib\\FileFormats.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\FunctionGraph\\lib\\FunctionGraph.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\FunctionGraphDecompilerExtension\\lib\\FunctionGraphDecompilerExtension.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\FunctionID\\lib\\FunctionID.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\GhidraGo\\lib\\GhidraGo.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\GhidraServer\\lib\\GhidraServer.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\GnuDemangler\\lib\\GnuDemangler.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\GraphFunctionCalls\\lib\\GraphFunctionCalls.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\GraphServices\\lib\\GraphServices.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\MicrosoftCodeAnalyzer\\lib\\MicrosoftCodeAnalyzer.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\MicrosoftDemangler\\lib\\MicrosoftDemangler.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\MicrosoftDmang\\lib\\MicrosoftDmang.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\PDB\\lib\\PDB.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\ProgramDiff\\lib\\ProgramDiff.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\ProgramGraph\\lib\\ProgramGraph.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Python\\lib\\Python.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Recognizers\\lib\\Recognizers.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\Sarif\\lib\\Sarif.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\SourceCodeLookup\\lib\\SourceCodeLookup.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\SwiftDemangler\\lib\\SwiftDemangler.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\SystemEmulation\\lib\\SystemEmulation.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\VersionTracking\\lib\\VersionTracking.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\VersionTrackingBSim\\lib\\VersionTrackingBSim.jar",
"C:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Features\\WildcardAssembler\\lib\\WildcardAssembler.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\DB\\lib\\DB.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Docking\\lib\\Docking.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Emulation\\lib\\Emulation.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\FileSystem\\lib\\FileSystem.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Generic\\lib\\Generic.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Graph\\lib\\Graph.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Gui\\lib\\Gui.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Help\\lib\\Help.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Project\\lib\\Project.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Pty\\lib\\Pty.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\SoftwareModeling\\lib\\SoftwareModeling.jar",
"c:\\Projects\\ghidra_11.3.2_PUBLIC\\Ghidra\\Framework\\Utility\\lib\\Utility.jar",
"c:\\Projects\\R3\\java\\ghidra\\sqlite-jdbc-3.49.1.0.jar"
]
}
```

View File

@ -13,10 +13,18 @@ CREATE TABLE Functions (
filepath TEXT,
name TEXT,
address TEXT,
type INTEGER,
PRIMARY KEY (name, filepath)
);
```
Where type is one of the following:
- 0: Auto
- 1: Fix
- 2: Stub
- 3: Ref
**Purpose**: Stores function definitions that have function bodies (actual implementations)
- `filepath`: Source file path where the function is defined
- `name`: Function name (identifier)
@ -29,6 +37,7 @@ CREATE TABLE Imports (
filepath TEXT,
name TEXT,
address TEXT,
type INTEGER,
PRIMARY KEY (name, filepath)
);
```