mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-28 09:48:05 +03:00
Add the compile-time SDL major version option and propagate it through interface targets. Route SDL includes through C99 compatibility headers while keeping SDL2 as the desktop and Emscripten default.
685 lines
23 KiB
C
685 lines
23 KiB
C
/*
|
|
* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "sdl_compat.h"
|
|
#ifdef _WIN32
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include "cjson/cJSON.h"
|
|
#include "debugger.h"
|
|
#include "debugger_private.h"
|
|
#include "debug_symbol.h"
|
|
#include "msgqueue.h"
|
|
#include "gfx.h"
|
|
#include "event.h"
|
|
#include "system.h"
|
|
#include "nact.h"
|
|
#include "variable.h"
|
|
|
|
#define THREAD_ID 1
|
|
|
|
enum VariablesReference {
|
|
VREF_GLOBALS = 1,
|
|
VREF_STRINGS,
|
|
};
|
|
|
|
// Exception Breakpoint Filters.
|
|
static const char ebf_warnings[] = "warnings";
|
|
|
|
static bool initialized;
|
|
static char *symbols_path;
|
|
static char *src_dir;
|
|
static struct msgq *queue;
|
|
static bool break_on_warnings;
|
|
static uint32_t palette_version;
|
|
|
|
cJSON *create_source(const char *name) {
|
|
cJSON *source = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(source, "name", name);
|
|
if (src_dir) {
|
|
char *buf = alloca(strlen(src_dir) + strlen(name) + 2);
|
|
sprintf(buf, "%s/%s", src_dir, name);
|
|
cJSON_AddStringToObject(source, "path", buf);
|
|
}
|
|
cJSON_AddNumberToObject(source, "sourceReference", 0);
|
|
return source;
|
|
}
|
|
|
|
static void send_json(cJSON *json) {
|
|
static int seq = 0;
|
|
|
|
cJSON_AddNumberToObject(json, "seq", ++seq);
|
|
char *str = cJSON_PrintUnformatted(json);
|
|
printf("Content-Length: %zu\r\n\r\n%s", strlen(str), str);
|
|
fflush(stdout);
|
|
free(str);
|
|
cJSON_Delete(json);
|
|
}
|
|
|
|
static void emit_initialized_event(void) {
|
|
cJSON *event = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(event, "type", "event");
|
|
cJSON_AddStringToObject(event, "event", "initialized");
|
|
send_json(event);
|
|
}
|
|
|
|
static void emit_terminated_event(void) {
|
|
cJSON *event = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(event, "type", "event");
|
|
cJSON_AddStringToObject(event, "event", "terminated");
|
|
send_json(event);
|
|
}
|
|
|
|
static void emit_stopped_event(void) {
|
|
const char *reason;
|
|
switch (dbg_state) {
|
|
case DBG_STOPPED_ENTRY: reason = "entry"; break;
|
|
case DBG_STOPPED_STEP: reason = "step"; break;
|
|
case DBG_STOPPED_NEXT: reason = "step"; break;
|
|
case DBG_STOPPED_BREAKPOINT: reason = "breakpoint"; break;
|
|
case DBG_STOPPED_INTERRUPT: reason = "pause"; break;
|
|
case DBG_STOPPED_EXCEPTION: reason = "exception"; break;
|
|
default: reason = "unknown"; break;
|
|
}
|
|
|
|
cJSON *event = cJSON_CreateObject(), *body;
|
|
cJSON_AddStringToObject(event, "type", "event");
|
|
cJSON_AddStringToObject(event, "event", "stopped");
|
|
cJSON_AddItemToObjectCS(event, "body", body = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(body, "reason", reason);
|
|
cJSON_AddNumberToObject(body, "threadId", THREAD_ID); // needed?
|
|
send_json(event);
|
|
}
|
|
|
|
static void emit_output_event(int lv, const char *output) {
|
|
cJSON *event = cJSON_CreateObject(), *body;
|
|
cJSON_AddStringToObject(event, "type", "event");
|
|
cJSON_AddStringToObject(event, "event", "output");
|
|
cJSON_AddItemToObjectCS(event, "body", body = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(body, "output", output);
|
|
|
|
const char *src = dsym_page2src(symbols, nact->current_page);
|
|
if (src)
|
|
cJSON_AddItemToObjectCS(body, "source", create_source(src));
|
|
int line = dsym_addr2line(symbols, nact->current_page, nact->current_addr);
|
|
if (line >= 0)
|
|
cJSON_AddNumberToObject(body, "line", line);
|
|
|
|
send_json(event);
|
|
|
|
if (break_on_warnings && lv <= 1)
|
|
dbg_state = DBG_STOPPED_EXCEPTION;
|
|
}
|
|
|
|
static void cmd_initialize(cJSON *args, cJSON *resp) {
|
|
cJSON *body, *filters, *filter;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddBoolToObject(body, "supportsConditionalBreakpoints", true);
|
|
cJSON_AddBoolToObject(body, "supportsConfigurationDoneRequest", true);
|
|
cJSON_AddBoolToObject(body, "supportsEvaluateForHovers", true);
|
|
cJSON_AddBoolToObject(body, "supportsSetVariable", true);
|
|
cJSON_AddItemToObjectCS(body, "exceptionBreakpointFilters", filters = cJSON_CreateArray());
|
|
cJSON_AddItemToArray(filters, filter = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(filter, "filter", ebf_warnings);
|
|
cJSON_AddStringToObject(filter, "label", "Stop on warnings");
|
|
}
|
|
|
|
static void cmd_launch(cJSON *args, cJSON *resp) {
|
|
cJSON *noDebug = cJSON_GetObjectItemCaseSensitive(args, "noDebug");
|
|
if (cJSON_IsTrue(noDebug)) {
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
initialized = true;
|
|
return;
|
|
}
|
|
|
|
symbols = dsym_load(symbols_path);
|
|
if (!symbols) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "xsystem35: Cannot load debug symbols");
|
|
return;
|
|
}
|
|
|
|
cJSON *srcDir = cJSON_GetObjectItemCaseSensitive(args, "srcDir");
|
|
if (cJSON_IsString(srcDir))
|
|
src_dir = strdup(srcDir->valuestring);
|
|
if (cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(args, "stopOnEntry")))
|
|
dbg_state = DBG_STOPPED_ENTRY;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
|
|
emit_initialized_event();
|
|
}
|
|
|
|
static void cmd_configurationDone(cJSON *args, cJSON *resp) {
|
|
initialized = true;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_stackTrace(cJSON *args, cJSON *resp) {
|
|
StackTrace *trace = dbg_stack_trace();
|
|
|
|
cJSON *body, *stackFrames;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "stackFrames", stackFrames = cJSON_CreateArray());
|
|
for (int i = 0; i < trace->nr_frame; i++) {
|
|
StackFrame *frame = &trace->frames[i];
|
|
cJSON *item = cJSON_CreateObject();
|
|
cJSON_AddItemToArray(stackFrames, item);
|
|
cJSON_AddNumberToObject(item, "id", i);
|
|
cJSON_AddStringToObject(item, "name", frame->name);
|
|
cJSON_AddItemToObjectCS(item, "source", create_source(frame->src));
|
|
cJSON_AddNumberToObject(item, "line", frame->line);
|
|
cJSON_AddNumberToObject(item, "column", 0);
|
|
}
|
|
cJSON_AddNumberToObject(body, "totalFrames", trace->nr_frame);
|
|
|
|
free(trace);
|
|
}
|
|
|
|
static void cmd_setBreakpoints(cJSON *args, cJSON *resp) {
|
|
cJSON *source = cJSON_GetObjectItemCaseSensitive(args, "source");
|
|
cJSON *source_name = cJSON_GetObjectItemCaseSensitive(source, "name");
|
|
cJSON *in_bps = cJSON_GetObjectItemCaseSensitive(args, "breakpoints");
|
|
if (!cJSON_IsString(source_name) || !cJSON_IsArray(in_bps)) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid arguments");
|
|
return;
|
|
}
|
|
const char *filename = source_name->valuestring;
|
|
int page = dsym_src2page(symbols, filename);
|
|
|
|
dbg_delete_breakpoints_in_page(page);
|
|
|
|
cJSON *body, *out_bps;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "breakpoints", out_bps = cJSON_CreateArray());
|
|
|
|
char message[256];
|
|
cJSON *srcbp;
|
|
cJSON_ArrayForEach(srcbp, in_bps) {
|
|
cJSON *item = cJSON_CreateObject();
|
|
cJSON_AddItemToArray(out_bps, item);
|
|
|
|
cJSON *line = cJSON_GetObjectItemCaseSensitive(srcbp, "line");
|
|
int line_no = line->valueint;
|
|
int addr = dsym_line2addr(symbols, page, line_no);
|
|
if (page < 0) {
|
|
snprintf(message, sizeof(message), "no source file named %s", filename);
|
|
cJSON_AddBoolToObject(item, "verified", false);
|
|
cJSON_AddStringToObject(item, "message", message);
|
|
continue;
|
|
}
|
|
if (addr < 0) {
|
|
snprintf(message, sizeof(message), "no line %d in file %s", line_no, filename);
|
|
cJSON_AddBoolToObject(item, "verified", false);
|
|
cJSON_AddStringToObject(item, "message", message);
|
|
continue;
|
|
}
|
|
Breakpoint *bp = dbg_set_breakpoint(page, addr, false);
|
|
if (!bp) {
|
|
snprintf(message, sizeof(message), "failed to set breakpoint at %d:0x%x", page, addr);
|
|
cJSON_AddBoolToObject(item, "verified", false);
|
|
cJSON_AddStringToObject(item, "message", message);
|
|
continue;
|
|
}
|
|
|
|
cJSON *condition = cJSON_GetObjectItemCaseSensitive(srcbp, "condition");
|
|
if (condition && cJSON_IsString(condition)) {
|
|
if (!dbg_set_breakpoint_condition(bp, condition->valuestring, message, sizeof(message))) {
|
|
dbg_delete_breakpoint(bp->no);
|
|
cJSON_AddBoolToObject(item, "verified", false);
|
|
cJSON_AddStringToObject(item, "message", message);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
line_no = dsym_addr2line(symbols, page, addr);
|
|
cJSON_AddNumberToObject(item, "id", bp->no);
|
|
cJSON_AddBoolToObject(item, "verified", true);
|
|
cJSON_AddItemToObjectCS(item, "source", create_source(filename));
|
|
cJSON_AddNumberToObject(item, "line", line_no);
|
|
}
|
|
}
|
|
|
|
static void cmd_setExceptionBreakpoints(cJSON *args, cJSON *resp) {
|
|
cJSON *filters = cJSON_GetObjectItemCaseSensitive(args, "filters");
|
|
break_on_warnings = false;
|
|
cJSON *filter;
|
|
cJSON_ArrayForEach(filter, filters) {
|
|
if (cJSON_IsString(filter) && !strcmp(filter->valuestring, ebf_warnings))
|
|
break_on_warnings = true;
|
|
}
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_evaluate(cJSON *args, cJSON *resp) {
|
|
cJSON *expression = cJSON_GetObjectItemCaseSensitive(args, "expression");
|
|
if (!cJSON_IsString(expression)) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid arguments");
|
|
return;
|
|
}
|
|
char buf[256];
|
|
if (!dbg_evaluate(expression->valuestring, buf, sizeof(buf))) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", buf);
|
|
return;
|
|
}
|
|
|
|
cJSON *body;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(body, "result", buf);
|
|
cJSON_AddNumberToObject(body, "variablesReference", 0);
|
|
}
|
|
|
|
static void cmd_continue(cJSON *args, cJSON *resp) {
|
|
gfx_raiseWindow();
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_pause(cJSON *args, cJSON *resp) {
|
|
dbg_state = DBG_STOPPED_INTERRUPT;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_stepIn(cJSON *args, cJSON *resp) {
|
|
dbg_stepin();
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_stepOut(cJSON *args, cJSON *resp) {
|
|
dbg_stepout();
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_next(cJSON *args, cJSON *resp) {
|
|
dbg_next();
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
}
|
|
|
|
static void cmd_threads(cJSON *args, cJSON *resp) {
|
|
cJSON *body, *threads, *thread;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "threads", threads = cJSON_CreateArray());
|
|
cJSON_AddItemToArray(threads, thread = cJSON_CreateObject());
|
|
cJSON_AddNumberToObject(thread, "id", THREAD_ID);
|
|
cJSON_AddStringToObject(thread, "name", "main thread");
|
|
}
|
|
|
|
static void cmd_scopes(cJSON *args, cJSON *resp) {
|
|
cJSON *body, *scopes, *globals, *strings;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "scopes", scopes = cJSON_CreateArray());
|
|
cJSON_AddItemToArray(scopes, globals = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(globals, "name", "All Variables");
|
|
cJSON_AddNumberToObject(globals, "variablesReference", VREF_GLOBALS);
|
|
cJSON_AddNumberToObject(globals, "namedVariables", dsym_num_variables(symbols));
|
|
cJSON_AddBoolToObject(globals, "expensive", false);
|
|
cJSON_AddItemToArray(scopes, strings = cJSON_CreateObject());
|
|
cJSON_AddStringToObject(strings, "name", "Strings");
|
|
cJSON_AddNumberToObject(strings, "variablesReference", VREF_STRINGS);
|
|
cJSON_AddNumberToObject(strings, "indexedVariables", svar_maxindex() + 1);
|
|
cJSON_AddBoolToObject(strings, "expensive", false);
|
|
}
|
|
|
|
char *format_string_value(const char *str) {
|
|
char *utf = toUTF8(str);
|
|
char *new_value = malloc(strlen(utf) + 3);
|
|
sprintf(new_value, "\"%s\"", utf);
|
|
free(utf);
|
|
return new_value;
|
|
}
|
|
|
|
static void cmd_variables(cJSON *args, cJSON *resp) {
|
|
cJSON *start_ = cJSON_GetObjectItemCaseSensitive(args, "start");
|
|
cJSON *count_ = cJSON_GetObjectItemCaseSensitive(args, "count");
|
|
int start = cJSON_IsNumber(start_) ? start_->valueint : 0;
|
|
int count = cJSON_IsNumber(count_) ? count_->valueint : 65536;
|
|
|
|
cJSON *vref = cJSON_GetObjectItemCaseSensitive(args, "variablesReference");
|
|
if (cJSON_IsNumber(vref) && vref->valueint == VREF_GLOBALS) {
|
|
cJSON *body, *variables;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "variables", variables = cJSON_CreateArray());
|
|
int end = dsym_num_variables(symbols);
|
|
if (end > start + count)
|
|
end = start + count;
|
|
for (int i = start; i < end; i++) {
|
|
cJSON *var = cJSON_CreateObject();
|
|
cJSON_AddItemToArray(variables, var);
|
|
cJSON_AddStringToObject(var, "name", dsym_variable_name(symbols, i));
|
|
char value[20];
|
|
vmvar_t *store = v_ref(i, NULL);
|
|
if (store)
|
|
sprintf(value, "%d", *store);
|
|
else
|
|
strcpy(value, "(Out of bounds)");
|
|
cJSON_AddStringToObject(var, "value", value);
|
|
cJSON_AddNumberToObject(var, "variablesReference", 0);
|
|
}
|
|
} else if (cJSON_IsNumber(vref) && vref->valueint == VREF_STRINGS) {
|
|
cJSON *body, *variables;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddItemToObjectCS(body, "variables", variables = cJSON_CreateArray());
|
|
int end = svar_maxindex() + 1;
|
|
if (end > start + count)
|
|
end = start + count;
|
|
for (int i = start; i < end; i++) {
|
|
char name[20];
|
|
cJSON *var = cJSON_CreateObject();
|
|
cJSON_AddItemToArray(variables, var);
|
|
sprintf(name, "[%d]", i);
|
|
cJSON_AddStringToObject(var, "name", name);
|
|
char *value = format_string_value(svar_get(i));
|
|
cJSON_AddStringToObject(var, "value", value);
|
|
free(value);
|
|
cJSON_AddNumberToObject(var, "variablesReference", 0);
|
|
}
|
|
} else {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid variablesReference");
|
|
}
|
|
}
|
|
|
|
static void cmd_setVariable(cJSON *args, cJSON *resp) {
|
|
cJSON *name = cJSON_GetObjectItemCaseSensitive(args, "name");
|
|
cJSON *value = cJSON_GetObjectItemCaseSensitive(args, "value");
|
|
if (!cJSON_IsString(name) || !cJSON_IsString(value)) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid arguments");
|
|
return;
|
|
}
|
|
|
|
cJSON *vref = cJSON_GetObjectItemCaseSensitive(args, "variablesReference");
|
|
if (cJSON_IsNumber(vref) && vref->valueint == VREF_GLOBALS) {
|
|
int var = dbg_lookup_var(name->valuestring);
|
|
if (var < 0) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid variable name");
|
|
return;
|
|
}
|
|
int parsed_value;
|
|
if (sscanf(value->valuestring, "%i", &parsed_value) != 1) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "syntax error");
|
|
return;
|
|
}
|
|
vmvar_t *store = v_ref(var, NULL);
|
|
if (!store) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "out of bounds array access");
|
|
return;
|
|
}
|
|
*store = parsed_value & 0xffff;
|
|
|
|
cJSON *body;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
char new_value[20];
|
|
sprintf(new_value, "%d", *store);
|
|
cJSON_AddStringToObject(body, "value", new_value);
|
|
} else if (cJSON_IsNumber(vref) && vref->valueint == VREF_STRINGS) {
|
|
int idx;
|
|
if (sscanf(name->valuestring, "[%d]", &idx) != 1 || (unsigned)idx > svar_maxindex()) {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid string index");
|
|
return;
|
|
}
|
|
int len = strlen(value->valuestring);
|
|
if (len < 2 || value->valuestring[0] != '"' || value->valuestring[len-1] != '"') {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "syntax error");
|
|
return;
|
|
}
|
|
value->valuestring[len-1] = '\0';
|
|
char *str = fromUTF8(value->valuestring + 1);
|
|
svar_set(idx, str);
|
|
free(str);
|
|
|
|
cJSON *body;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
char *new_value = format_string_value(svar_get(idx));
|
|
cJSON_AddStringToObject(body, "value", new_value);
|
|
free(new_value);
|
|
} else {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
cJSON_AddStringToObject(resp, "message", "invalid variablesReference");
|
|
}
|
|
}
|
|
|
|
static void cmd_disconnect(cJSON *args, cJSON *resp) {
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
send_json(resp);
|
|
sys_exit(0);
|
|
}
|
|
|
|
static void cmd_palette(cJSON *args, cJSON *resp) {
|
|
cJSON *body, *palette;
|
|
cJSON_AddBoolToObject(resp, "success", true);
|
|
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
|
cJSON_AddNumberToObject(body, "version", palette_version);
|
|
cJSON_AddItemToObjectCS(body, "palette", palette = cJSON_CreateArray());
|
|
for (int i = 0; i < 256; i++) {
|
|
int val = nact->ags.pal[i].r << 16 |
|
|
nact->ags.pal[i].g << 8 |
|
|
nact->ags.pal[i].b;
|
|
cJSON_AddItemToArray(palette, cJSON_CreateNumber(val));
|
|
}
|
|
}
|
|
|
|
static bool handle_request(cJSON *request) {
|
|
bool continue_repl = true;
|
|
|
|
cJSON *resp = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(resp, "type", "response");
|
|
cJSON *request_seq = cJSON_DetachItemFromObjectCaseSensitive(request, "seq");
|
|
cJSON_AddItemToObjectCS(resp, "request_seq", request_seq);
|
|
cJSON *command = cJSON_DetachItemFromObjectCaseSensitive(request, "command");
|
|
cJSON_AddItemToObjectCS(resp, "command", command);
|
|
cJSON *args = cJSON_GetObjectItemCaseSensitive(request, "arguments");
|
|
|
|
if (!cJSON_IsString(command)) {
|
|
fprintf(stderr, "protocol error: command is not a string\n");
|
|
// FIXME: return an error response
|
|
cJSON_Delete(resp);
|
|
return continue_repl;
|
|
}
|
|
|
|
if (!strcmp(command->valuestring, "initialize")) {
|
|
cmd_initialize(args, resp);
|
|
} else if (!strcmp(command->valuestring, "launch")) {
|
|
cmd_launch(args, resp);
|
|
} else if (!strcmp(command->valuestring, "configurationDone")) {
|
|
cmd_configurationDone(args, resp);
|
|
} else if (!strcmp(command->valuestring, "continue")) {
|
|
cmd_continue(args, resp);
|
|
continue_repl = false;
|
|
} else if (!strcmp(command->valuestring, "stackTrace")) {
|
|
cmd_stackTrace(args, resp);
|
|
} else if (!strcmp(command->valuestring, "stepIn")) {
|
|
cmd_stepIn(args, resp);
|
|
continue_repl = false;
|
|
} else if (!strcmp(command->valuestring, "stepOut")) {
|
|
cmd_stepOut(args, resp);
|
|
continue_repl = false;
|
|
} else if (!strcmp(command->valuestring, "next")) {
|
|
cmd_next(args, resp);
|
|
continue_repl = false;
|
|
} else if (!strcmp(command->valuestring, "pause")) {
|
|
cmd_pause(args, resp);
|
|
} else if (!strcmp(command->valuestring, "evaluate")) {
|
|
cmd_evaluate(args, resp);
|
|
} else if (!strcmp(command->valuestring, "setBreakpoints")) {
|
|
cmd_setBreakpoints(args, resp);
|
|
} else if (!strcmp(command->valuestring, "setExceptionBreakpoints")) {
|
|
cmd_setExceptionBreakpoints(args, resp);
|
|
} else if (!strcmp(command->valuestring, "threads")) {
|
|
cmd_threads(args, resp);
|
|
} else if (!strcmp(command->valuestring, "scopes")) {
|
|
cmd_scopes(args, resp);
|
|
} else if (!strcmp(command->valuestring, "variables")) {
|
|
cmd_variables(args, resp);
|
|
} else if (!strcmp(command->valuestring, "setVariable")) {
|
|
cmd_setVariable(args, resp);
|
|
} else if (!strcmp(command->valuestring, "disconnect")) {
|
|
cmd_disconnect(args, resp);
|
|
} else if (!strcmp(command->valuestring, "xsystem35.palette")) {
|
|
cmd_palette(args, resp);
|
|
} else {
|
|
cJSON_AddBoolToObject(resp, "success", false);
|
|
char *buf = malloc(strlen(command->valuestring) + 30);
|
|
sprintf(buf, "unknown request \"%s\"", command->valuestring);
|
|
cJSON_AddStringToObject(resp, "message", buf);
|
|
fprintf(stderr, "%s\n", buf);
|
|
free(buf);
|
|
}
|
|
send_json(resp);
|
|
return continue_repl;
|
|
}
|
|
|
|
static bool handle_message(char *msg) {
|
|
cJSON *json = cJSON_Parse(msg);
|
|
cJSON *type = cJSON_GetObjectItemCaseSensitive(json, "type");
|
|
bool continue_repl = true;
|
|
if (cJSON_IsString(type) && !strcmp(type->valuestring, "request"))
|
|
continue_repl = handle_request(json);
|
|
cJSON_Delete(json);
|
|
free(msg);
|
|
return continue_repl;
|
|
}
|
|
|
|
static int read_command_thread(void *data) {
|
|
int content_length = -1;
|
|
char header[512];
|
|
while (fgets(header, sizeof(header), stdin)) {
|
|
if (sscanf(header, "Content-Length: %d", &content_length) == 1) {
|
|
continue;
|
|
} else if ((header[0] == '\r' && header[1] == '\n') || header[0] == '\n') {
|
|
if (content_length < 0) {
|
|
fprintf(stderr, "Debug Adapter Protocol error: no Content-Length header\n");
|
|
continue;
|
|
}
|
|
char *buf = malloc(content_length);
|
|
if (fread(buf, content_length, 1, stdin) != 1) {
|
|
fprintf(stderr, "Failed to read message\n");
|
|
free(buf);
|
|
break;
|
|
}
|
|
event_post_debugger_command(buf);
|
|
content_length = -1;
|
|
} else {
|
|
fprintf(stderr, "Unknown Debug Adapter Protocol header: %s", header);
|
|
}
|
|
}
|
|
event_post_debugger_command(NULL); // end of messages
|
|
return 0;
|
|
}
|
|
|
|
static void dbg_dap_init(const char *path) {
|
|
symbols_path = strdup(path);
|
|
queue = msgq_new();
|
|
|
|
#ifdef _WIN32
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
#endif
|
|
|
|
SDL_CreateThread(read_command_thread, "Debugger", NULL);
|
|
|
|
while (!initialized && !nact->is_quit) {
|
|
SDL_Event e;
|
|
SDL_WaitEvent(&e);
|
|
event_handle_event(&e);
|
|
while (!msgq_isempty(queue)) {
|
|
char *msg = msgq_dequeue(queue);
|
|
if (!msg)
|
|
return;
|
|
handle_message(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dbg_dap_quit(void) {
|
|
emit_terminated_event();
|
|
}
|
|
|
|
static void dbg_dap_repl(int bp_no) {
|
|
emit_stopped_event();
|
|
dbg_state = DBG_RUNNING;
|
|
|
|
bool continue_repl = true;
|
|
while (continue_repl && !nact->is_quit) {
|
|
SDL_Event e;
|
|
SDL_WaitEvent(&e);
|
|
event_handle_event(&e);
|
|
while (!msgq_isempty(queue)) {
|
|
char *msg = msgq_dequeue(queue);
|
|
if (!msg)
|
|
return;
|
|
continue_repl = handle_message(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dbg_dap_onsleep(void) {
|
|
while (!msgq_isempty(queue)) {
|
|
char *msg = msgq_dequeue(queue);
|
|
if (!msg)
|
|
break;
|
|
handle_message(msg);
|
|
}
|
|
if (dbg_state == DBG_STOPPED_INTERRUPT || dbg_state == DBG_STOPPED_EXCEPTION)
|
|
dbg_main(0);
|
|
}
|
|
|
|
static void dbg_dap_on_palette_change(void) {
|
|
cJSON *event = cJSON_CreateObject(), *body;
|
|
cJSON_AddStringToObject(event, "type", "event");
|
|
cJSON_AddStringToObject(event, "event", "xsystem35.paletteChanged");
|
|
cJSON_AddItemToObjectCS(event, "body", body = cJSON_CreateObject());
|
|
cJSON_AddNumberToObject(body, "version", ++palette_version);
|
|
send_json(event);
|
|
}
|
|
|
|
void dbg_post_command(void *data) {
|
|
msgq_enqueue(queue, data);
|
|
}
|
|
|
|
DebuggerImpl dbg_dap_impl = {
|
|
.init = dbg_dap_init,
|
|
.quit = dbg_dap_quit,
|
|
.repl = dbg_dap_repl,
|
|
.onsleep = dbg_dap_onsleep,
|
|
.on_palette_change = dbg_dap_on_palette_change,
|
|
.console_output = emit_output_event,
|
|
};
|