Merge pull request #11 from starsong-consulting/feat/struct-management
feat: add struct data type management API
This commit is contained in:
commit
8268e55a08
@ -404,6 +404,201 @@ Provides access to string data in the binary.
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Structs
|
||||
|
||||
Provides functionality for creating and managing struct (composite) data types.
|
||||
|
||||
- **`GET /structs`**: List all struct data types in the program. Supports pagination and filtering.
|
||||
- Query Parameters:
|
||||
- `?offset=[int]`: Number of structs to skip (default: 0).
|
||||
- `?limit=[int]`: Maximum number of structs to return (default: 100).
|
||||
- `?category=[string]`: Filter by category path (e.g. "/winapi").
|
||||
```json
|
||||
// Example Response
|
||||
"result": [
|
||||
{
|
||||
"name": "MyStruct",
|
||||
"path": "/custom/MyStruct",
|
||||
"size": 16,
|
||||
"numFields": 4,
|
||||
"category": "/custom",
|
||||
"description": "Custom data structure"
|
||||
},
|
||||
{
|
||||
"name": "FileHeader",
|
||||
"path": "/FileHeader",
|
||||
"size": 32,
|
||||
"numFields": 8,
|
||||
"category": "/",
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"_links": {
|
||||
"self": { "href": "/structs?offset=0&limit=100" },
|
||||
"program": { "href": "/program" }
|
||||
}
|
||||
```
|
||||
|
||||
- **`GET /structs?name={struct_name}`**: Get detailed information about a specific struct including all fields.
|
||||
```json
|
||||
// Example Response for GET /structs?name=MyStruct
|
||||
"result": {
|
||||
"name": "MyStruct",
|
||||
"path": "/custom/MyStruct",
|
||||
"size": 16,
|
||||
"category": "/custom",
|
||||
"description": "Custom data structure",
|
||||
"numFields": 4,
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"offset": 0,
|
||||
"length": 4,
|
||||
"type": "int",
|
||||
"typePath": "/int",
|
||||
"comment": "Unique identifier"
|
||||
},
|
||||
{
|
||||
"name": "flags",
|
||||
"offset": 4,
|
||||
"length": 4,
|
||||
"type": "dword",
|
||||
"typePath": "/dword",
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "data_ptr",
|
||||
"offset": 8,
|
||||
"length": 4,
|
||||
"type": "pointer",
|
||||
"typePath": "/pointer",
|
||||
"comment": "Pointer to data"
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"offset": 12,
|
||||
"length": 4,
|
||||
"type": "uint",
|
||||
"typePath": "/uint",
|
||||
"comment": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"_links": {
|
||||
"self": { "href": "/structs?name=MyStruct" },
|
||||
"structs": { "href": "/structs" },
|
||||
"program": { "href": "/program" }
|
||||
}
|
||||
```
|
||||
|
||||
- **`POST /structs/create`**: Create a new struct data type.
|
||||
- Request Payload:
|
||||
- `name`: Name for the new struct (required).
|
||||
- `category`: Category path (optional, defaults to root).
|
||||
- `description`: Description for the struct (optional).
|
||||
```json
|
||||
// Example Request Payload
|
||||
{
|
||||
"name": "NetworkPacket",
|
||||
"category": "/network",
|
||||
"description": "Network packet structure"
|
||||
}
|
||||
|
||||
// Example Response
|
||||
"result": {
|
||||
"name": "NetworkPacket",
|
||||
"path": "/network/NetworkPacket",
|
||||
"category": "/network",
|
||||
"size": 0,
|
||||
"message": "Struct created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
- **`POST /structs/addfield`**: Add a field to an existing struct.
|
||||
- Request Payload:
|
||||
- `struct`: Name of the struct to modify (required).
|
||||
- `fieldName`: Name for the new field (required).
|
||||
- `fieldType`: Data type for the field (required, e.g. "int", "char", "pointer").
|
||||
- `offset`: Specific offset to insert field (optional, appends to end if not specified).
|
||||
- `comment`: Comment for the field (optional).
|
||||
```json
|
||||
// Example Request Payload
|
||||
{
|
||||
"struct": "NetworkPacket",
|
||||
"fieldName": "header",
|
||||
"fieldType": "dword",
|
||||
"comment": "Packet header"
|
||||
}
|
||||
|
||||
// Example Response
|
||||
"result": {
|
||||
"struct": "NetworkPacket",
|
||||
"fieldName": "header",
|
||||
"fieldType": "dword",
|
||||
"offset": 0,
|
||||
"length": 4,
|
||||
"structSize": 4,
|
||||
"message": "Field added successfully"
|
||||
}
|
||||
```
|
||||
|
||||
- **`POST /structs/updatefield`**: Update an existing field in a struct (rename, change type, or modify comment).
|
||||
- Request Payload:
|
||||
- `struct`: Name of the struct to modify (required).
|
||||
- `fieldOffset` OR `fieldName`: Identify the field to update (one required).
|
||||
- `newName`: New name for the field (optional).
|
||||
- `newType`: New data type for the field (optional).
|
||||
- `newComment`: New comment for the field (optional).
|
||||
- At least one of `newName`, `newType`, or `newComment` must be provided.
|
||||
```json
|
||||
// Example Request Payload - rename a field
|
||||
{
|
||||
"struct": "NetworkPacket",
|
||||
"fieldName": "header",
|
||||
"newName": "packet_header",
|
||||
"newComment": "Updated packet header field"
|
||||
}
|
||||
|
||||
// Example Request Payload - change type by offset
|
||||
{
|
||||
"struct": "NetworkPacket",
|
||||
"fieldOffset": 0,
|
||||
"newType": "qword"
|
||||
}
|
||||
|
||||
// Example Response
|
||||
"result": {
|
||||
"struct": "NetworkPacket",
|
||||
"offset": 0,
|
||||
"originalName": "header",
|
||||
"originalType": "dword",
|
||||
"originalComment": "Packet header",
|
||||
"newName": "packet_header",
|
||||
"newType": "dword",
|
||||
"newComment": "Updated packet header field",
|
||||
"length": 4,
|
||||
"message": "Field updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
- **`POST /structs/delete`**: Delete a struct data type.
|
||||
- Request Payload:
|
||||
- `name`: Name of the struct to delete (required).
|
||||
```json
|
||||
// Example Request Payload
|
||||
{
|
||||
"name": "NetworkPacket"
|
||||
}
|
||||
|
||||
// Example Response
|
||||
"result": {
|
||||
"name": "NetworkPacket",
|
||||
"path": "/network/NetworkPacket",
|
||||
"category": "/network",
|
||||
"message": "Struct deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Memory Segments
|
||||
|
||||
Represents memory blocks/sections defined in the program.
|
||||
|
||||
@ -48,6 +48,7 @@ The API is organized into namespaces for different types of operations:
|
||||
- instances_* : For managing Ghidra instances
|
||||
- functions_* : For working with functions
|
||||
- data_* : For working with data items
|
||||
- structs_* : For creating and managing struct data types
|
||||
- memory_* : For memory access
|
||||
- xrefs_* : For cross-references
|
||||
- analysis_* : For program analysis
|
||||
@ -1924,6 +1925,234 @@ def data_set_type(address: str, data_type: str, port: int = None) -> dict:
|
||||
response = safe_post(port, "data/type", payload)
|
||||
return simplify_response(response)
|
||||
|
||||
# Struct tools
|
||||
@mcp.tool()
|
||||
def structs_list(offset: int = 0, limit: int = 100, category: str = None, port: int = None) -> dict:
|
||||
"""List all struct data types in the program
|
||||
|
||||
Args:
|
||||
offset: Pagination offset (default: 0)
|
||||
limit: Maximum items to return (default: 100)
|
||||
category: Filter by category path (e.g. "/winapi")
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: List of structs with name, size, and field count
|
||||
"""
|
||||
port = _get_instance_port(port)
|
||||
|
||||
params = {
|
||||
"offset": offset,
|
||||
"limit": limit
|
||||
}
|
||||
if category:
|
||||
params["category"] = category
|
||||
|
||||
response = safe_get(port, "structs", params)
|
||||
simplified = simplify_response(response)
|
||||
|
||||
# Ensure we maintain pagination metadata
|
||||
if isinstance(simplified, dict) and "error" not in simplified:
|
||||
simplified.setdefault("size", len(simplified.get("result", [])))
|
||||
simplified.setdefault("offset", offset)
|
||||
simplified.setdefault("limit", limit)
|
||||
|
||||
return simplified
|
||||
|
||||
@mcp.tool()
|
||||
def structs_get(name: str, port: int = None) -> dict:
|
||||
"""Get detailed information about a specific struct including all fields
|
||||
|
||||
Args:
|
||||
name: Struct name
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: Struct details including all fields with their names, types, and offsets
|
||||
"""
|
||||
if not name:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "Struct name parameter is required"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
port = _get_instance_port(port)
|
||||
|
||||
params = {"name": name}
|
||||
response = safe_get(port, "structs", params)
|
||||
return simplify_response(response)
|
||||
|
||||
@mcp.tool()
|
||||
def structs_create(name: str, category: str = None, description: str = None, port: int = None) -> dict:
|
||||
"""Create a new struct data type
|
||||
|
||||
Args:
|
||||
name: Name for the new struct
|
||||
category: Category path for the struct (e.g. "/custom")
|
||||
description: Optional description for the struct
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: Created struct information
|
||||
"""
|
||||
if not name:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "Struct name parameter is required"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
port = _get_instance_port(port)
|
||||
|
||||
payload = {"name": name}
|
||||
if category:
|
||||
payload["category"] = category
|
||||
if description:
|
||||
payload["description"] = description
|
||||
|
||||
response = safe_post(port, "structs/create", payload)
|
||||
return simplify_response(response)
|
||||
|
||||
@mcp.tool()
|
||||
def structs_add_field(struct_name: str, field_name: str, field_type: str,
|
||||
offset: int = None, comment: str = None, port: int = None) -> dict:
|
||||
"""Add a field to an existing struct
|
||||
|
||||
Args:
|
||||
struct_name: Name of the struct to modify
|
||||
field_name: Name for the new field
|
||||
field_type: Data type for the field (e.g. "int", "char", "pointer")
|
||||
offset: Specific offset to insert field (optional, appends to end if not specified)
|
||||
comment: Optional comment for the field
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: Operation result with updated struct size and field information
|
||||
"""
|
||||
if not struct_name or not field_name or not field_type:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "struct_name, field_name, and field_type parameters are required"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
port = _get_instance_port(port)
|
||||
|
||||
payload = {
|
||||
"struct": struct_name,
|
||||
"fieldName": field_name,
|
||||
"fieldType": field_type
|
||||
}
|
||||
if offset is not None:
|
||||
payload["offset"] = offset
|
||||
if comment:
|
||||
payload["comment"] = comment
|
||||
|
||||
response = safe_post(port, "structs/addfield", payload)
|
||||
return simplify_response(response)
|
||||
|
||||
@mcp.tool()
|
||||
def structs_update_field(struct_name: str, field_name: str = None, field_offset: int = None,
|
||||
new_name: str = None, new_type: str = None, new_comment: str = None,
|
||||
port: int = None) -> dict:
|
||||
"""Update an existing field in a struct (change name, type, or comment)
|
||||
|
||||
Args:
|
||||
struct_name: Name of the struct to modify
|
||||
field_name: Name of the field to update (use this OR field_offset)
|
||||
field_offset: Offset of the field to update (use this OR field_name)
|
||||
new_name: New name for the field (optional)
|
||||
new_type: New data type for the field (optional, e.g. "int", "pointer")
|
||||
new_comment: New comment for the field (optional)
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: Operation result with old and new field values
|
||||
"""
|
||||
if not struct_name:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "struct_name parameter is required"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
if not field_name and field_offset is None:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "Either field_name or field_offset must be provided"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
if not new_name and not new_type and new_comment is None:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "At least one of new_name, new_type, or new_comment must be provided"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
port = _get_instance_port(port)
|
||||
|
||||
payload = {"struct": struct_name}
|
||||
if field_name:
|
||||
payload["fieldName"] = field_name
|
||||
if field_offset is not None:
|
||||
payload["fieldOffset"] = field_offset
|
||||
if new_name:
|
||||
payload["newName"] = new_name
|
||||
if new_type:
|
||||
payload["newType"] = new_type
|
||||
if new_comment is not None:
|
||||
payload["newComment"] = new_comment
|
||||
|
||||
response = safe_post(port, "structs/updatefield", payload)
|
||||
return simplify_response(response)
|
||||
|
||||
@mcp.tool()
|
||||
def structs_delete(name: str, port: int = None) -> dict:
|
||||
"""Delete a struct data type
|
||||
|
||||
Args:
|
||||
name: Name of the struct to delete
|
||||
port: Specific Ghidra instance port (optional)
|
||||
|
||||
Returns:
|
||||
dict: Operation result confirming deletion
|
||||
"""
|
||||
if not name:
|
||||
return {
|
||||
"success": False,
|
||||
"error": {
|
||||
"code": "MISSING_PARAMETER",
|
||||
"message": "Struct name parameter is required"
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}
|
||||
|
||||
port = _get_instance_port(port)
|
||||
|
||||
payload = {"name": name}
|
||||
response = safe_post(port, "structs/delete", payload)
|
||||
return simplify_response(response)
|
||||
|
||||
# Analysis tools
|
||||
@mcp.tool()
|
||||
def analysis_run(port: int = None, analysis_options: dict = None) -> dict:
|
||||
|
||||
@ -139,6 +139,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
new SymbolEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new NamespaceEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new DataEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new StructEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new MemoryEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new XrefsEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
new AnalysisEndpoints(currentProgram, port, tool).registerEndpoints(server);
|
||||
@ -376,6 +377,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
.addLink("data", "/data")
|
||||
.addLink("strings", "/strings")
|
||||
.addLink("segments", "/segments")
|
||||
.addLink("structs", "/structs")
|
||||
.addLink("memory", "/memory")
|
||||
.addLink("xrefs", "/xrefs")
|
||||
.addLink("analysis", "/analysis")
|
||||
|
||||
776
src/main/java/eu/starsong/ghidra/endpoints/StructEndpoints.java
Normal file
776
src/main/java/eu/starsong/ghidra/endpoints/StructEndpoints.java
Normal file
@ -0,0 +1,776 @@
|
||||
package eu.starsong.ghidra.endpoints;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import eu.starsong.ghidra.api.ResponseBuilder;
|
||||
import eu.starsong.ghidra.util.TransactionHelper;
|
||||
import eu.starsong.ghidra.util.TransactionHelper.TransactionException;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Endpoints for managing struct (composite) data types in Ghidra.
|
||||
* Provides REST API for creating, listing, modifying, and deleting structs.
|
||||
*/
|
||||
public class StructEndpoints extends AbstractEndpoint {
|
||||
|
||||
private PluginTool tool;
|
||||
|
||||
public StructEndpoints(Program program, int port) {
|
||||
super(program, port);
|
||||
}
|
||||
|
||||
public StructEndpoints(Program program, int port, PluginTool tool) {
|
||||
super(program, port);
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerEndpoints(HttpServer server) {
|
||||
server.createContext("/structs", this::handleStructs);
|
||||
server.createContext("/structs/create", exchange -> {
|
||||
try {
|
||||
if ("POST".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parseJsonPostParams(exchange);
|
||||
handleCreateStruct(exchange, params);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /structs/create endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
server.createContext("/structs/delete", exchange -> {
|
||||
try {
|
||||
if ("POST".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parseJsonPostParams(exchange);
|
||||
handleDeleteStruct(exchange, params);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /structs/delete endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
server.createContext("/structs/addfield", exchange -> {
|
||||
try {
|
||||
if ("POST".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parseJsonPostParams(exchange);
|
||||
handleAddField(exchange, params);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /structs/addfield endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
server.createContext("/structs/updatefield", exchange -> {
|
||||
try {
|
||||
if ("POST".equals(exchange.getRequestMethod()) || "PATCH".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> params = parseJsonPostParams(exchange);
|
||||
handleUpdateField(exchange, params);
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /structs/updatefield endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle GET /structs - list all structs, or GET /structs?name=X - get specific struct details
|
||||
*/
|
||||
private void handleStructs(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
if ("GET".equals(exchange.getRequestMethod())) {
|
||||
Map<String, String> qparams = parseQueryParams(exchange);
|
||||
String structName = qparams.get("name");
|
||||
|
||||
if (structName != null && !structName.isEmpty()) {
|
||||
handleGetStruct(exchange, structName);
|
||||
} else {
|
||||
handleListStructs(exchange);
|
||||
}
|
||||
} else {
|
||||
sendErrorResponse(exchange, 405, "Method Not Allowed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error in /structs endpoint", e);
|
||||
sendErrorResponse(exchange, 500, "Internal server error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all struct data types in the program
|
||||
*/
|
||||
private void handleListStructs(HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
Map<String, String> qparams = parseQueryParams(exchange);
|
||||
int offset = parseIntOrDefault(qparams.get("offset"), 0);
|
||||
int limit = parseIntOrDefault(qparams.get("limit"), 100);
|
||||
String categoryFilter = qparams.get("category");
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
List<Map<String, Object>> structList = new ArrayList<>();
|
||||
|
||||
// Iterate through all data types and filter for structures
|
||||
dtm.getAllDataTypes().forEachRemaining(dataType -> {
|
||||
if (dataType instanceof Structure) {
|
||||
Structure struct = (Structure) dataType;
|
||||
|
||||
// Apply category filter if specified
|
||||
if (categoryFilter != null && !categoryFilter.isEmpty()) {
|
||||
CategoryPath catPath = struct.getCategoryPath();
|
||||
if (!catPath.getPath().contains(categoryFilter)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> structInfo = new HashMap<>();
|
||||
structInfo.put("name", struct.getName());
|
||||
structInfo.put("path", struct.getPathName());
|
||||
structInfo.put("size", struct.getLength());
|
||||
structInfo.put("numFields", struct.getNumComponents());
|
||||
structInfo.put("category", struct.getCategoryPath().getPath());
|
||||
structInfo.put("description", struct.getDescription() != null ? struct.getDescription() : "");
|
||||
|
||||
// Add HATEOAS links
|
||||
Map<String, Object> links = new HashMap<>();
|
||||
Map<String, String> selfLink = new HashMap<>();
|
||||
selfLink.put("href", "/structs?name=" + struct.getName());
|
||||
links.put("self", selfLink);
|
||||
structInfo.put("_links", links);
|
||||
|
||||
structList.add(structInfo);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort by name for consistency
|
||||
structList.sort(Comparator.comparing(s -> (String) s.get("name")));
|
||||
|
||||
// Build response with pagination
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port).success(true);
|
||||
List<Map<String, Object>> paginated = applyPagination(structList, offset, limit, builder, "/structs");
|
||||
builder.result(paginated);
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error listing structs", e);
|
||||
sendErrorResponse(exchange, 500, "Error listing structs: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get details of a specific struct including all fields
|
||||
*/
|
||||
private void handleGetStruct(HttpExchange exchange, String structName) throws IOException {
|
||||
try {
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Try to find the struct - support both full paths and simple names
|
||||
DataType dataType = null;
|
||||
|
||||
// If it looks like a full path (starts with /), try direct lookup
|
||||
if (structName.startsWith("/")) {
|
||||
dataType = dtm.getDataType(structName);
|
||||
if (dataType == null) {
|
||||
dataType = dtm.findDataType(structName);
|
||||
}
|
||||
} else {
|
||||
// Search by simple name using the helper method
|
||||
dataType = findStructByName(dtm, structName);
|
||||
}
|
||||
|
||||
if (dataType == null || !(dataType instanceof Structure)) {
|
||||
sendErrorResponse(exchange, 404, "Struct not found: " + structName, "STRUCT_NOT_FOUND");
|
||||
return;
|
||||
}
|
||||
|
||||
Structure struct = (Structure) dataType;
|
||||
Map<String, Object> structInfo = buildStructInfo(struct);
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(structInfo);
|
||||
|
||||
builder.addLink("self", "/structs?name=" + struct.getName());
|
||||
builder.addLink("structs", "/structs");
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error getting struct details", e);
|
||||
sendErrorResponse(exchange, 500, "Error getting struct: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new struct data type
|
||||
* POST /structs/create
|
||||
* Required params: name
|
||||
* Optional params: category, size, description
|
||||
*/
|
||||
private void handleCreateStruct(HttpExchange exchange, Map<String, String> params) throws IOException {
|
||||
try {
|
||||
String structName = params.get("name");
|
||||
String category = params.get("category");
|
||||
String sizeStr = params.get("size");
|
||||
String description = params.get("description");
|
||||
|
||||
if (structName == null || structName.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: name", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("name", structName);
|
||||
|
||||
try {
|
||||
TransactionHelper.executeInTransaction(program, "Create Struct", () -> {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Check if struct already exists
|
||||
DataType existing = dtm.getDataType("/" + structName);
|
||||
if (existing != null) {
|
||||
throw new Exception("Struct already exists: " + structName);
|
||||
}
|
||||
|
||||
// Determine category path
|
||||
CategoryPath catPath;
|
||||
if (category != null && !category.isEmpty()) {
|
||||
catPath = new CategoryPath(category);
|
||||
} else {
|
||||
catPath = CategoryPath.ROOT;
|
||||
}
|
||||
|
||||
// Create the structure
|
||||
StructureDataType struct = new StructureDataType(catPath, structName, 0);
|
||||
|
||||
if (description != null && !description.isEmpty()) {
|
||||
struct.setDescription(description);
|
||||
}
|
||||
|
||||
// Add to data type manager
|
||||
Structure addedStruct = (Structure) dtm.addDataType(struct, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
|
||||
resultMap.put("path", addedStruct.getPathName());
|
||||
resultMap.put("category", addedStruct.getCategoryPath().getPath());
|
||||
resultMap.put("size", addedStruct.getLength());
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
resultMap.put("message", "Struct created successfully");
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(resultMap);
|
||||
|
||||
builder.addLink("self", "/structs?name=" + structName);
|
||||
builder.addLink("structs", "/structs");
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 201);
|
||||
} catch (TransactionException e) {
|
||||
Msg.error(this, "Transaction failed: Create Struct", e);
|
||||
sendErrorResponse(exchange, 500, "Failed to create struct: " + e.getMessage(), "TRANSACTION_ERROR");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error creating struct", e);
|
||||
sendErrorResponse(exchange, 400, "Error creating struct: " + e.getMessage(), "INVALID_PARAMETER");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Unexpected error creating struct", e);
|
||||
sendErrorResponse(exchange, 500, "Error creating struct: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field to an existing struct
|
||||
* POST /structs/addfield
|
||||
* Required params: struct, fieldName, fieldType
|
||||
* Optional params: offset, comment
|
||||
*/
|
||||
private void handleAddField(HttpExchange exchange, Map<String, String> params) throws IOException {
|
||||
try {
|
||||
String structName = params.get("struct");
|
||||
String fieldName = params.get("fieldName");
|
||||
String fieldType = params.get("fieldType");
|
||||
String offsetStr = params.get("offset");
|
||||
String comment = params.get("comment");
|
||||
|
||||
if (structName == null || structName.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: struct", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
if (fieldName == null || fieldName.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: fieldName", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
if (fieldType == null || fieldType.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: fieldType", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer offset = null;
|
||||
if (offsetStr != null && !offsetStr.isEmpty()) {
|
||||
try {
|
||||
offset = Integer.parseInt(offsetStr);
|
||||
} catch (NumberFormatException e) {
|
||||
sendErrorResponse(exchange, 400, "Invalid offset parameter: must be an integer", "INVALID_PARAMETER");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("struct", structName);
|
||||
resultMap.put("fieldName", fieldName);
|
||||
resultMap.put("fieldType", fieldType);
|
||||
|
||||
final Integer finalOffset = offset;
|
||||
|
||||
try {
|
||||
TransactionHelper.executeInTransaction(program, "Add Struct Field", () -> {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Find the struct - handle both full paths and simple names
|
||||
DataType dataType = null;
|
||||
if (structName.startsWith("/")) {
|
||||
dataType = dtm.getDataType(structName);
|
||||
if (dataType == null) {
|
||||
dataType = dtm.findDataType(structName);
|
||||
}
|
||||
} else {
|
||||
dataType = findStructByName(dtm, structName);
|
||||
}
|
||||
|
||||
if (dataType == null || !(dataType instanceof Structure)) {
|
||||
throw new Exception("Struct not found: " + structName);
|
||||
}
|
||||
|
||||
Structure struct = (Structure) dataType;
|
||||
|
||||
// Find the field type
|
||||
DataType fieldDataType = findDataType(dtm, fieldType);
|
||||
if (fieldDataType == null) {
|
||||
throw new Exception("Field type not found: " + fieldType);
|
||||
}
|
||||
|
||||
// Add the field
|
||||
DataTypeComponent component;
|
||||
if (finalOffset != null) {
|
||||
// Insert at specific offset
|
||||
component = struct.insertAtOffset(finalOffset, fieldDataType,
|
||||
fieldDataType.getLength(), fieldName, comment);
|
||||
} else {
|
||||
// Append to end
|
||||
component = struct.add(fieldDataType, fieldName, comment);
|
||||
}
|
||||
|
||||
resultMap.put("offset", component.getOffset());
|
||||
resultMap.put("length", component.getLength());
|
||||
resultMap.put("structSize", struct.getLength());
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
resultMap.put("message", "Field added successfully");
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(resultMap);
|
||||
|
||||
builder.addLink("struct", "/structs?name=" + structName);
|
||||
builder.addLink("structs", "/structs");
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (TransactionException e) {
|
||||
Msg.error(this, "Transaction failed: Add Struct Field", e);
|
||||
sendErrorResponse(exchange, 500, "Failed to add field: " + e.getMessage(), "TRANSACTION_ERROR");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error adding field", e);
|
||||
sendErrorResponse(exchange, 400, "Error adding field: " + e.getMessage(), "INVALID_PARAMETER");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Unexpected error adding field", e);
|
||||
sendErrorResponse(exchange, 500, "Error adding field: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing field in a struct
|
||||
* POST/PATCH /structs/updatefield
|
||||
* Required params: struct, fieldOffset (or fieldName)
|
||||
* Optional params: newName, newType, newComment
|
||||
*/
|
||||
private void handleUpdateField(HttpExchange exchange, Map<String, String> params) throws IOException {
|
||||
try {
|
||||
String structName = params.get("struct");
|
||||
String fieldOffsetStr = params.get("fieldOffset");
|
||||
String fieldName = params.get("fieldName");
|
||||
String newName = params.get("newName");
|
||||
String newType = params.get("newType");
|
||||
String newComment = params.get("newComment");
|
||||
|
||||
if (structName == null || structName.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: struct", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
// Must have either fieldOffset or fieldName to identify the field
|
||||
if ((fieldOffsetStr == null || fieldOffsetStr.isEmpty()) && (fieldName == null || fieldName.isEmpty())) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: either fieldOffset or fieldName must be provided", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
// Must have at least one update parameter
|
||||
if ((newName == null || newName.isEmpty()) &&
|
||||
(newType == null || newType.isEmpty()) &&
|
||||
(newComment == null || newComment.isEmpty())) {
|
||||
sendErrorResponse(exchange, 400, "At least one of newName, newType, or newComment must be provided", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer fieldOffset = null;
|
||||
if (fieldOffsetStr != null && !fieldOffsetStr.isEmpty()) {
|
||||
try {
|
||||
fieldOffset = Integer.parseInt(fieldOffsetStr);
|
||||
} catch (NumberFormatException e) {
|
||||
sendErrorResponse(exchange, 400, "Invalid fieldOffset parameter: must be an integer", "INVALID_PARAMETER");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("struct", structName);
|
||||
|
||||
final Integer finalFieldOffset = fieldOffset;
|
||||
final String finalFieldName = fieldName;
|
||||
|
||||
try {
|
||||
TransactionHelper.executeInTransaction(program, "Update Struct Field", () -> {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Find the struct
|
||||
DataType dataType = null;
|
||||
if (structName.startsWith("/")) {
|
||||
dataType = dtm.getDataType(structName);
|
||||
if (dataType == null) {
|
||||
dataType = dtm.findDataType(structName);
|
||||
}
|
||||
} else {
|
||||
dataType = findStructByName(dtm, structName);
|
||||
}
|
||||
|
||||
if (dataType == null || !(dataType instanceof Structure)) {
|
||||
throw new Exception("Struct not found: " + structName);
|
||||
}
|
||||
|
||||
Structure struct = (Structure) dataType;
|
||||
|
||||
// Find the field to update
|
||||
DataTypeComponent component = null;
|
||||
if (finalFieldOffset != null) {
|
||||
component = struct.getComponentAt(finalFieldOffset);
|
||||
} else {
|
||||
// Search by field name
|
||||
for (DataTypeComponent comp : struct.getComponents()) {
|
||||
if (finalFieldName.equals(comp.getFieldName())) {
|
||||
component = comp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (component == null) {
|
||||
throw new Exception("Field not found in struct: " + (finalFieldOffset != null ? "offset " + finalFieldOffset : finalFieldName));
|
||||
}
|
||||
|
||||
int componentOffset = component.getOffset();
|
||||
int componentLength = component.getLength();
|
||||
DataType originalType = component.getDataType();
|
||||
String originalName = component.getFieldName();
|
||||
String originalComment = component.getComment();
|
||||
|
||||
// Store original values
|
||||
resultMap.put("originalName", originalName);
|
||||
resultMap.put("originalType", originalType.getName());
|
||||
resultMap.put("originalComment", originalComment != null ? originalComment : "");
|
||||
resultMap.put("offset", componentOffset);
|
||||
|
||||
// Determine new values
|
||||
String updatedName = (newName != null && !newName.isEmpty()) ? newName : originalName;
|
||||
String updatedComment = (newComment != null) ? newComment : originalComment;
|
||||
DataType updatedType = originalType;
|
||||
|
||||
if (newType != null && !newType.isEmpty()) {
|
||||
updatedType = findDataType(dtm, newType);
|
||||
if (updatedType == null) {
|
||||
throw new Exception("Field type not found: " + newType);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the field by replacing it
|
||||
// Ghidra doesn't have a direct "update" - we need to delete and re-add
|
||||
struct.deleteAtOffset(componentOffset);
|
||||
DataTypeComponent newComponent = struct.insertAtOffset(componentOffset, updatedType,
|
||||
updatedType.getLength(),
|
||||
updatedName, updatedComment);
|
||||
|
||||
resultMap.put("newName", newComponent.getFieldName());
|
||||
resultMap.put("newType", newComponent.getDataType().getName());
|
||||
resultMap.put("newComment", newComponent.getComment() != null ? newComponent.getComment() : "");
|
||||
resultMap.put("length", newComponent.getLength());
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
resultMap.put("message", "Field updated successfully");
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(resultMap);
|
||||
|
||||
builder.addLink("struct", "/structs?name=" + structName);
|
||||
builder.addLink("structs", "/structs");
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (TransactionException e) {
|
||||
Msg.error(this, "Transaction failed: Update Struct Field", e);
|
||||
sendErrorResponse(exchange, 500, "Failed to update field: " + e.getMessage(), "TRANSACTION_ERROR");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error updating field", e);
|
||||
sendErrorResponse(exchange, 400, "Error updating field: " + e.getMessage(), "INVALID_PARAMETER");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Unexpected error updating field", e);
|
||||
sendErrorResponse(exchange, 500, "Error updating field: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a struct data type
|
||||
* POST /structs/delete
|
||||
* Required params: name
|
||||
*/
|
||||
private void handleDeleteStruct(HttpExchange exchange, Map<String, String> params) throws IOException {
|
||||
try {
|
||||
String structName = params.get("name");
|
||||
|
||||
if (structName == null || structName.isEmpty()) {
|
||||
sendErrorResponse(exchange, 400, "Missing required parameter: name", "MISSING_PARAMETERS");
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = getCurrentProgram();
|
||||
if (program == null) {
|
||||
sendErrorResponse(exchange, 400, "No program loaded", "NO_PROGRAM_LOADED");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("name", structName);
|
||||
|
||||
try {
|
||||
TransactionHelper.executeInTransaction(program, "Delete Struct", () -> {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Find the struct - handle both full paths and simple names
|
||||
DataType dataType = null;
|
||||
if (structName.startsWith("/")) {
|
||||
dataType = dtm.getDataType(structName);
|
||||
if (dataType == null) {
|
||||
dataType = dtm.findDataType(structName);
|
||||
}
|
||||
} else {
|
||||
dataType = findStructByName(dtm, structName);
|
||||
}
|
||||
|
||||
if (dataType == null) {
|
||||
throw new Exception("Struct not found: " + structName);
|
||||
}
|
||||
|
||||
if (!(dataType instanceof Structure)) {
|
||||
throw new Exception("Data type is not a struct: " + structName);
|
||||
}
|
||||
|
||||
// Store info before deletion
|
||||
resultMap.put("path", dataType.getPathName());
|
||||
resultMap.put("category", dataType.getCategoryPath().getPath());
|
||||
|
||||
// Remove the struct
|
||||
dtm.remove(dataType, null);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
resultMap.put("message", "Struct deleted successfully");
|
||||
|
||||
ResponseBuilder builder = new ResponseBuilder(exchange, port)
|
||||
.success(true)
|
||||
.result(resultMap);
|
||||
|
||||
builder.addLink("structs", "/structs");
|
||||
builder.addLink("program", "/program");
|
||||
|
||||
sendJsonResponse(exchange, builder.build(), 200);
|
||||
} catch (TransactionException e) {
|
||||
Msg.error(this, "Transaction failed: Delete Struct", e);
|
||||
sendErrorResponse(exchange, 500, "Failed to delete struct: " + e.getMessage(), "TRANSACTION_ERROR");
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Error deleting struct", e);
|
||||
sendErrorResponse(exchange, 400, "Error deleting struct: " + e.getMessage(), "INVALID_PARAMETER");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Msg.error(this, "Unexpected error deleting struct", e);
|
||||
sendErrorResponse(exchange, 500, "Error deleting struct: " + e.getMessage(), "INTERNAL_ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a detailed information map for a struct including all fields
|
||||
*/
|
||||
private Map<String, Object> buildStructInfo(Structure struct) {
|
||||
Map<String, Object> structInfo = new HashMap<>();
|
||||
structInfo.put("name", struct.getName());
|
||||
structInfo.put("path", struct.getPathName());
|
||||
structInfo.put("size", struct.getLength());
|
||||
structInfo.put("category", struct.getCategoryPath().getPath());
|
||||
structInfo.put("description", struct.getDescription() != null ? struct.getDescription() : "");
|
||||
structInfo.put("numFields", struct.getNumComponents());
|
||||
|
||||
// Add field details
|
||||
List<Map<String, Object>> fields = new ArrayList<>();
|
||||
for (DataTypeComponent component : struct.getComponents()) {
|
||||
Map<String, Object> fieldInfo = new HashMap<>();
|
||||
fieldInfo.put("name", component.getFieldName() != null ? component.getFieldName() : "");
|
||||
fieldInfo.put("offset", component.getOffset());
|
||||
fieldInfo.put("length", component.getLength());
|
||||
fieldInfo.put("type", component.getDataType().getName());
|
||||
fieldInfo.put("typePath", component.getDataType().getPathName());
|
||||
fieldInfo.put("comment", component.getComment() != null ? component.getComment() : "");
|
||||
fields.add(fieldInfo);
|
||||
}
|
||||
structInfo.put("fields", fields);
|
||||
|
||||
return structInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a struct by name, searching through all data types
|
||||
*/
|
||||
private DataType findStructByName(DataTypeManager dtm, String structName) {
|
||||
final DataType[] result = new DataType[1];
|
||||
|
||||
dtm.getAllDataTypes().forEachRemaining(dt -> {
|
||||
if (dt instanceof Structure && dt.getName().equals(structName)) {
|
||||
if (result[0] == null) {
|
||||
result[0] = dt;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a data type by name, trying multiple lookup methods
|
||||
*/
|
||||
private DataType findDataType(DataTypeManager dtm, String typeName) {
|
||||
// Try direct lookup with path
|
||||
DataType dataType = dtm.getDataType("/" + typeName);
|
||||
|
||||
// Try without path
|
||||
if (dataType == null) {
|
||||
dataType = dtm.findDataType("/" + typeName);
|
||||
}
|
||||
|
||||
// Try built-in primitive types
|
||||
if (dataType == null) {
|
||||
switch(typeName.toLowerCase()) {
|
||||
case "byte":
|
||||
dataType = new ByteDataType();
|
||||
break;
|
||||
case "char":
|
||||
dataType = new CharDataType();
|
||||
break;
|
||||
case "word":
|
||||
dataType = new WordDataType();
|
||||
break;
|
||||
case "dword":
|
||||
dataType = new DWordDataType();
|
||||
break;
|
||||
case "qword":
|
||||
dataType = new QWordDataType();
|
||||
break;
|
||||
case "float":
|
||||
dataType = new FloatDataType();
|
||||
break;
|
||||
case "double":
|
||||
dataType = new DoubleDataType();
|
||||
break;
|
||||
case "int":
|
||||
dataType = new IntegerDataType();
|
||||
break;
|
||||
case "long":
|
||||
dataType = new LongDataType();
|
||||
break;
|
||||
case "pointer":
|
||||
dataType = new PointerDataType();
|
||||
break;
|
||||
case "string":
|
||||
dataType = new StringDataType();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dataType;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user