#pragma once12#include <atomic>3#include <mutex>4#include <string>5#include <thread>6#include <unordered_map>7#include <vector>89// Represents symbol from a Ghidra's program.10// A symbol can be for example a data label, instruction label or a function.11struct GhidraSymbol {12u32 address = 0;13std::string name;14bool label;15bool userDefined;16std::string dataTypePathName;17};1819// Possible kinds of data types, such as enum, structures or built-ins (Ghidra's primitive types).20enum GhidraTypeKind {21ENUM,22TYPEDEF,23POINTER,24ARRAY,25STRUCTURE,26UNION,27FUNCTION_DEFINITION,28BUILT_IN,29UNKNOWN,30};3132// Describes single member of an enum type.33struct GhidraEnumMember {34std::string name;35u64 value = 0;36std::string comment;37};3839// Describes single member of a composite (structure or union) type.40struct GhidraCompositeMember {41std::string fieldName;42u32 ordinal = 0;43u32 offset = 0;44int length = 0;45std::string typePathName;46std::string comment;47};4849// Describes data type from Ghidra. Note that some fields of this structure will only be populated depending on the50// type's kind. Each type has a display name that is suitable for displaying to the user and a path name that51// unambiguously identifies this type.52struct GhidraType {53GhidraTypeKind kind;54std::string displayName;55std::string pathName;56int length = 0;57int alignedLength = 0;58bool zeroLength = false;59std::string description;6061std::vector<GhidraCompositeMember> compositeMembers;62std::vector<GhidraEnumMember> enumMembers;63bool enumBitfield = false; // Determined from a simple heuristic check64std::string pointerTypePathName;65std::string typedefTypePathName;66std::string typedefBaseTypePathName;67std::string arrayTypePathName;68int arrayElementLength = 0;69u32 arrayElementCount = 0;70std::string functionPrototypeString;71std::string builtInGroup;72};7374// GhidraClient implements fetching data (such as symbols or types) from a remote Ghidra project.75//76// This client uses unofficial API provided by the third party "ghidra-rest-api" extension. The extension is77// available at https://github.com/kotcrab/ghidra-rest-api.78//79// This class doesn't fetch data from every possible endpoint, only those that are actually used by PPSSPP.80//81// How to use:82// 1. The client is created in the Idle status.83// 2. To start fetching data call the FetchAll() method. The client goes to Pending status and the data is fetched84// in a background thread so your code remains unblocked.85// 3. Periodically check with the Ready() method is the operation has completed. (i.e. check if the client86// is in the Ready status)87// 4. If the client is ready call UpdateResult() to update result field with new data.88// 5. The client is now back to Idle status, and you can call FetchAll() again later if needed.89class GhidraClient {90enum class Status {91Idle,92Pending,93Ready,94};9596public:97struct Result {98std::vector<GhidraSymbol> symbols;99std::unordered_map<std::string, GhidraType> types;100std::string error;101};102103// Current result of the client. Your thread is safe to access this regardless of client status.104Result result;105106GhidraClient() : status_(Status::Idle) {}107~GhidraClient();108109// If client is idle then asynchronously starts fetching data from Ghidra.110void FetchAll(const std::string &host, int port);111112// If client is ready then updates the result field with newly fetched data.113// This must be called from the thread using the result.114void UpdateResult();115116bool Idle() const { return status_ == Status::Idle; }117118bool Ready() const { return status_ == Status::Ready; }119120bool Failed() const { return !result.error.empty(); }121122private:123std::thread thread_;124std::mutex mutex_;125std::atomic<Status> status_;126Result pendingResult_;127std::string host_;128int port_ = 0;129130bool FetchAllDo(const std::string &host, int port);131132bool FetchSymbols();133134bool FetchTypes();135136bool FetchResource(const std::string &path, std::string &outResult);137};138139140