From d4e06db8e2b6d0cc1e3f8bd726b5c8d491308186 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Sat, 29 Apr 2023 10:32:50 -0700 Subject: [PATCH] Initial DAP implementation So far only basic debugging functionality is implemented (breakpoints, stepping, stack traces/variables). This is not tested against an established DAP client. My intention is to create a custom GUI frontend and extend the protocol to support xsystem4-specific features, such as inspecting the scene. (This work is underway.) Instruction references are hex-encoded address strings. Clients do not need to obtain them from DAP requests. This is outside the spec, but it is needed for a binary-only debugger. Functions that write directly to stdout (e.g. printf) should generally not be used any more. Instead, use either sys_message (which respects sys_silent) or log_message (which will send the message to the debugger if DAP is enabled). stderr can be used as normal. --- include/debugger.h | 17 + include/msgqueue.h | 45 +++ include/xsystem4.h | 4 +- src/3d/debug.c | 76 ++-- src/debug.c | 36 +- src/debugger_dap.c | 756 ++++++++++++++++++++++++++++++++++++ src/hll/CrayfishLogViewer.c | 2 +- src/hll/OutputLog.c | 19 +- src/input.c | 2 + src/meson.build | 2 + src/msgqueue.c | 95 +++++ src/parts/debug.c | 264 ++++++------- src/scene.c | 16 +- src/sprite.c | 78 ++-- src/system4.c | 13 +- src/text.c | 24 +- src/util.c | 17 +- src/vm.c | 4 +- subprojects/libsys4 | 2 +- 19 files changed, 1217 insertions(+), 255 deletions(-) create mode 100644 include/msgqueue.h create mode 100644 src/debugger_dap.c create mode 100644 src/msgqueue.c diff --git a/include/debugger.h b/include/debugger.h index bcdfe44..96c3155 100644 --- a/include/debugger.h +++ b/include/debugger.h @@ -18,6 +18,7 @@ #define SYSTEM4_DEBUGGER_H #ifdef DEBUGGER_ENABLED +#include #include #include #include "system4/instructions.h" @@ -46,6 +47,7 @@ struct dbg_cmd { void (*run)(unsigned nr_args, char **args); }; +extern bool dbg_dap; extern bool dbg_enabled; extern bool dbg_start_in_debugger; extern unsigned dbg_current_frame; @@ -54,6 +56,8 @@ void dbg_init(void); void dbg_fini(void); void dbg_repl(void); +void dbg_log(const char *log, const char *fmt, va_list ap); + void dbg_continue(void); void dbg_quit(void); void dbg_start(void(*fun)(void*), void *data); @@ -61,6 +65,7 @@ void dbg_cmd_init(void); void dbg_cmd_repl(void); void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands); void dbg_handle_breakpoint(void); +bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*)); bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data); bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), void *data); bool dbg_set_step_over_breakpoint(void); @@ -73,10 +78,22 @@ void dbg_print_vm_state(void); union vm_value dbg_eval_string(const char *str, struct ain_type *type_out); struct string *dbg_value_to_string(struct ain_type *type, union vm_value value, int recursive); +void dbg_dap_init(void); +void dbg_dap_quit(void); +void dbg_dap_repl(void); +void dbg_dap_handle_messages(void); +void dbg_dap_log(const char *log, const char *fmt, va_list ap); + #ifdef HAVE_SCHEME void dbg_scm_init(void); void dbg_scm_fini(void); void dbg_scm_repl(void); #endif /* HAVE_SCHEME */ +#else /* DEBUGGER ENABLED */ +#define dbg_init() +#define dbg_repl() +#define dbg_dap 0 +#define dbg_dap_handle_messages() +#define dbg_dap_log(log, fmt, ap) #endif /* DEBUGGER_ENABLED */ #endif /* SYSTEM4_DEBUGGER_H */ diff --git a/include/msgqueue.h b/include/msgqueue.h new file mode 100644 index 0000000..062430d --- /dev/null +++ b/include/msgqueue.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 kichikuou + * + * 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 + * + */ + +#ifndef __MSGQUEUE_H__ +#define __MSGQUEUE_H__ + +#include +#include + +struct msgq_elem; + +struct msgq { + SDL_mutex *mutex; + SDL_cond *cond_nonempty; + struct msgq_elem *head; + struct msgq_elem *last; +}; + +static inline bool msgq_isempty(struct msgq *q) { + return !q->head; +} + +struct msgq *msgq_new(void); +void msgq_free(struct msgq *q); +void msgq_enqueue(struct msgq *q, void *msg); +void *msgq_dequeue(struct msgq *q); +void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms); + +#endif // __MSGQUEUE_H__ diff --git a/include/xsystem4.h b/include/xsystem4.h index 78f29ba..bcfc9a9 100644 --- a/include/xsystem4.h +++ b/include/xsystem4.h @@ -56,7 +56,9 @@ const char *display_utf0(const char *utf); const char *display_utf1(const char *utf); const char *display_utf2(const char *utf); -void indent_printf(int indent, const char *fmt, ...); +void indent_message(int indent, const char *fmt, ...); + +void log_message(const char *log, const char *fmt, ...); char *unix_path(const char *path); char *gamedir_path(const char *path); diff --git a/src/3d/debug.c b/src/3d/debug.c index a7b9bfc..5958373 100644 --- a/src/3d/debug.c +++ b/src/3d/debug.c @@ -60,11 +60,11 @@ static const char *fog_type_name(enum RE_fog_type type) static void print_motion(const char *name, struct motion *m, int indent) { if (m->instance->type == RE_ITYPE_BILLBOARD) { - indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n", + indent_message(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n", name, motion_state_name(m->state), m->current_frame, (int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end); } else if (m->mot) { - indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n", + indent_message(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n", name, m->mot->name, motion_state_name(m->state), m->current_frame); } } @@ -73,85 +73,85 @@ static void print_instance(struct RE_instance *inst, int index, int indent) { if (!inst) return; - indent_printf(indent, "instance[%d] = {\n", index); + indent_message(indent, "instance[%d] = {\n", index); indent++; - indent_printf(indent, "type = %s,\n", instance_type_name(inst->type)); + indent_message(indent, "type = %s,\n", instance_type_name(inst->type)); if (inst->type == RE_ITYPE_DIRECTIONAL_LIGHT || inst->type == RE_ITYPE_SPECULAR_LIGHT) { - indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec)); - indent_printf(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse)); - indent_printf(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse)); + indent_message(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec)); + indent_message(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse)); + indent_message(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse)); } else { - indent_printf(indent, "draw = %d,\n", inst->draw); - indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos)); - indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale)); - indent_printf(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient)); + indent_message(indent, "draw = %d,\n", inst->draw); + indent_message(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos)); + indent_message(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale)); + indent_message(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient)); } if (inst->model) { - indent_printf(indent, "path = \"%s\",\n", inst->model->path); - indent_printf(indent, "aabb = {min = {x=%f, y=%f, z=%f}, max = {x=%f, y=%f, z=%f}},\n", + indent_message(indent, "path = \"%s\",\n", inst->model->path); + indent_message(indent, "aabb = {min = {x=%f, y=%f, z=%f}, max = {x=%f, y=%f, z=%f}},\n", SPREAD_VEC3(inst->model->aabb[0]), SPREAD_VEC3(inst->model->aabb[1])); } if (inst->motion) { - indent_printf(indent, "fps = %f,\n", inst->fps); + indent_message(indent, "fps = %f,\n", inst->fps); print_motion("motion", inst->motion, indent); } if (inst->next_motion) print_motion("next_motion", inst->next_motion, indent); if (inst->motion_blend) - indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate); + indent_message(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate); if (inst->effect) - indent_printf(indent, "path = \"%s\",\n", inst->effect->pae->path); + indent_message(indent, "path = \"%s\",\n", inst->effect->pae->path); indent--; - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } static void print_back_cg(struct RE_back_cg *bcg, int index, int indent) { if (!bcg->name && !bcg->no) return; - indent_printf(indent, "backCG[%d] = {\n", index); + indent_message(indent, "backCG[%d] = {\n", index); indent++; if (bcg->no) - indent_printf(indent, "no = %d,\n", bcg->no); + indent_message(indent, "no = %d,\n", bcg->no); if (bcg->name) - indent_printf(indent, "name = \"%s\",\n", bcg->name ? bcg->name->text : ""); - indent_printf(indent, "pos = {x=%f, y=%f},\n", bcg->x, bcg->y); - indent_printf(indent, "blend_rate = %f,\n", bcg->blend_rate); - indent_printf(indent, "mag = %f,\n", bcg->mag); - indent_printf(indent, "show = %d,\n", bcg->show); + indent_message(indent, "name = \"%s\",\n", bcg->name ? bcg->name->text : ""); + indent_message(indent, "pos = {x=%f, y=%f},\n", bcg->x, bcg->y); + indent_message(indent, "blend_rate = %f,\n", bcg->blend_rate); + indent_message(indent, "mag = %f,\n", bcg->mag); + indent_message(indent, "show = %d,\n", bcg->show); indent--; - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } void RE_debug_print(struct sact_sprite *sp, int indent) { struct RE_plugin *p = (struct RE_plugin *)sp->plugin; - indent_printf(indent, "name = \"%s\",\n", p->plugin.name); - indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n", + indent_message(indent, "name = \"%s\",\n", p->plugin.name); + indent_message(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n", SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw); - indent_printf(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir)); - indent_printf(indent, "shadow_bias = %f,\n", p->shadow_bias); + indent_message(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir)); + indent_message(indent, "shadow_bias = %f,\n", p->shadow_bias); - indent_printf(indent, "fog_type = %s,\n", fog_type_name(p->fog_type)); + indent_message(indent, "fog_type = %s,\n", fog_type_name(p->fog_type)); switch (p->fog_type) { case RE_FOG_NONE: break; case RE_FOG_LINEAR: - indent_printf(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far); - indent_printf(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color)); + indent_message(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far); + indent_message(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color)); break; case RE_FOG_LIGHT_SCATTERING: - indent_printf(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m); - indent_printf(indent, "ls_g = %f,\n", p->ls_g); - indent_printf(indent, "ls_distance = %f,\n", p->ls_distance); - indent_printf(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir)); - indent_printf(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color)); - indent_printf(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color)); + indent_message(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m); + indent_message(indent, "ls_g = %f,\n", p->ls_g); + indent_message(indent, "ls_distance = %f,\n", p->ls_distance); + indent_message(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir)); + indent_message(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color)); + indent_message(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color)); break; } diff --git a/src/debug.c b/src/debug.c index 3e292ca..f285070 100644 --- a/src/debug.c +++ b/src/debug.c @@ -36,6 +36,7 @@ #include "little_endian.h" #include "xsystem4.h" +bool dbg_dap = false; bool dbg_enabled = true; bool dbg_start_in_debugger = false; unsigned dbg_current_frame = 0; @@ -74,7 +75,10 @@ void dbg_start(void(*fun)(void*), void *data) static void _dbg_repl(void *_) { - dbg_cmd_repl(); + if (dbg_dap) + dbg_dap_repl(); + else + dbg_cmd_repl(); } void dbg_repl(void) @@ -86,10 +90,15 @@ void dbg_repl(void) void dbg_init(void) { - dbg_cmd_init(); + if (dbg_dap) + dbg_dap_init(); + else + dbg_cmd_init(); #ifdef HAVE_SCHEME dbg_scm_init(); #endif + if (!dbg_dap && dbg_start_in_debugger) + dbg_repl(); } void dbg_fini(void) @@ -135,6 +144,17 @@ static struct breakpoint *get_breakpoint(uint32_t addr) return ht_get_int(bp_table, addr, NULL); } +bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*)) +{ + struct breakpoint *bp = get_breakpoint(addr); + if (!bp) + return false; + if (free_data) + free_data(bp->data); + delete_breakpoint(addr, bp); + return true; +} + bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data) { char *name = utf2sjis(_name, 0); @@ -157,7 +177,7 @@ bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint* LittleEndian_putW(ain->code, f->address, BREAKPOINT | bp->restore_op); add_breakpoint(f->address, bp); - printf("Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address); + log_message("debug", "Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address); return true; } @@ -185,7 +205,7 @@ bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), LittleEndian_putW(ain->code, address, BREAKPOINT | bp->restore_op); add_breakpoint(address, bp); - printf("Set breakpoint at 0x%08x\n", address); + log_message("debug", "Set breakpoint at 0x%08x\n", address); return true; } @@ -196,7 +216,7 @@ static void dbg_step_breakpoint_cb(struct breakpoint *bp) if ((intptr_t)bp->data != call_stack_ptr) return; delete_breakpoint(instr_ptr, bp); - dbg_cmd_repl(); + _dbg_repl(NULL); } static void dbg_set_step_breakpoint(int32_t address, int call_index) @@ -384,8 +404,8 @@ static void _dbg_handle_breakpoint(void *data) if (bp->cb) { bp->cb(bp); } else { - printf("%s\n", bp->message); - dbg_cmd_repl(); + log_message("debug", "%s\n", bp->message); + _dbg_repl(NULL); } } @@ -408,7 +428,7 @@ void dbg_print_frame(unsigned no) unsigned cs_no = call_stack_ptr - (1 + no); struct ain_function *f = &ain->functions[call_stack[cs_no].fno]; uint32_t addr = no ? call_stack[cs_no+1].call_address : instr_ptr; - printf("%c #%d 0x%08x in %s\n", no == dbg_current_frame ? '*' : ' ', + sys_message("%c #%d 0x%08x in %s\n", no == dbg_current_frame ? '*' : ' ', no, addr, display_sjis0(f->name)); } diff --git a/src/debugger_dap.c b/src/debugger_dap.c new file mode 100644 index 0000000..48e6480 --- /dev/null +++ b/src/debugger_dap.c @@ -0,0 +1,756 @@ +/* Copyright (C) 2023 Nunuhara Cabbage + * + * Based largely on DAP implementation from xsystem35-sdl2 + * Copyright (C) 2021 kichikuou + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +#define VM_PRIVATE + +#include "system4.h" +#include "system4/string.h" +#include "system4/utfsjis.h" +#include "xsystem4.h" +#include "cJSON.h" +#include "debugger.h" +#include "msgqueue.h" +#include "vm.h" +#include "vm/heap.h" +#include "vm/page.h" + +#define THREAD_ID 1 + +static enum { + DAP_UNINITIALIZED, + DAP_INITIALIZED, + DAP_RUNNING, + DAP_STOPPED +} dap_state = DAP_UNINITIALIZED; + +static struct msgq *queue; + +static void json_add_sjis_to_object(cJSON *obj, const char *name, const char *sjis) +{ + char *utf = sjis2utf(sjis, 0); + cJSON_AddStringToObject(obj, name, utf); + free(utf); +} + +static void send_json(cJSON *json) +{ + static int seq = 1; + + 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 send_response(cJSON *response, bool success) +{ + cJSON_AddBoolToObject(response, "success", success); + send_json(response); +} + +static void emit_event(const char *name, struct cJSON *body) +{ + cJSON *event = cJSON_CreateObject(); + cJSON_AddStringToObject(event, "type", "event"); + cJSON_AddStringToObject(event, "event", name); + if (body) { + cJSON_AddItemToObjectCS(event, "body", body); + } + send_json(event); +} + +static void emit_initialized_event(void) +{ + emit_event("initialized", NULL); +} + +static void emit_terminated_event(void) +{ + emit_event("terminated", NULL); +} + +static void emit_stopped_event(void) +{ + cJSON *body = cJSON_CreateObject(); + // TODO: specify correct reason + cJSON_AddStringToObject(body, "reason", "pause"); + emit_event("stopped", body); +} + +static void emit_output_event(const char *category, const char *output) +{ + cJSON *body = cJSON_CreateObject(); + cJSON_AddStringToObject(body, "category", category); + cJSON_AddStringToObject(body, "output", output); + emit_event("output", body); +} + +static void cmd_initialize(cJSON *args, cJSON *resp) +{ + cJSON *body; + + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + cJSON_AddBoolToObject(body, "supportsConfigurationDoneRequest", true); + //cJSON_AddBoolToObject(body, "supportsFunctionBreakpoints", true); + //cJSON_AddBoolToObject(body, "supportsConditionalBreakpoints", true); + //cJSON_AddBoolToObject(body, "supportsHitConditionalBreakpoints", true); + cJSON_AddBoolToObject(body, "supportsEvaluateForHovers", true); + //cJSON_AddBoolToObject(body, "supportsSetVariable", true); + //cJSON_AddBoolToObject(body, "supportsSetExpression", true); + //cJSON_AddBoolToObject(body, "supportsTerminateRequest", true); + cJSON_AddBoolToObject(body, "supportsInstructionBreakpoints", true); + send_response(resp, true); + + emit_initialized_event(); + dap_state = DAP_INITIALIZED; +} + +static void cmd_launch(cJSON *args, cJSON *resp) +{ + if (dap_state != DAP_INITIALIZED) { + send_response(resp, false); + return; + } + + // TODO: handle noDebug option + send_response(resp, true); + dap_state = DAP_RUNNING; +} + +static void cmd_configurationDone(cJSON *args, cJSON *resp) +{ + send_response(resp, true); +} + +static void cmd_stackTrace(cJSON *args, cJSON *resp) +{ + cJSON *body, *stack_frames, *frame; + // FIXME: we ignore startFrame/levels arguments + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + cJSON_AddNumberToObject(body, "totalFrames", call_stack_ptr); + cJSON_AddItemToObjectCS(body, "stackFrames", stack_frames = cJSON_CreateArray()); + for (int i = 0; i < call_stack_ptr; i++) { + cJSON_AddItemToArray(stack_frames, frame = cJSON_CreateObject()); + cJSON_AddNumberToObject(frame, "id", i); + // this *shouldn't* happen, but since we're in the debugger... + int fno = call_stack[i].fno; + if (fno < 0 || fno >= ain->nr_functions) { + cJSON_AddStringToObject(frame, "name", ""); + } else { + json_add_sjis_to_object(frame, "name", ain->functions[fno].name); + } + char ip[9]; + if (i == call_stack_ptr - 1) { + snprintf(ip, 9, "%x", (unsigned)instr_ptr); + } else { + snprintf(ip, 9, "%x", call_stack[i].call_address); + } + cJSON_AddStringToObject(frame, "instructionPointerReference", ip); + cJSON_AddNumberToObject(frame, "line", 0); + cJSON_AddNumberToObject(frame, "column", 0); + } + send_response(resp, true); +} + +enum var_ref_type { + VAR_REF_ARGUMENTS = 0, + VAR_REF_LOCALS = 1, + VAR_REF_MEMBERS = 2, + VAR_REF_GENERIC = 3, +}; + +static inline unsigned var_ref(unsigned slot, enum var_ref_type type) +{ + return (slot << 4) | type; +} + +static inline unsigned var_ref_slot(unsigned var_ref) +{ + return var_ref >> 4; +} + +static inline enum var_ref_type var_ref_type(unsigned var_ref) +{ + return var_ref & 0xF; +} + +static void cmd_scopes(cJSON *args, cJSON *resp) +{ + if (!args) { + send_response(resp, false); + return; + } + + cJSON *j_id = cJSON_GetObjectItemCaseSensitive(args, "frameId"); + if (!j_id || !cJSON_IsNumber(j_id)) { + send_response(resp, false); + return; + } + + int id = j_id->valueint; + if (id < 0 || id >= call_stack_ptr) { + send_response(resp, false); + return; + } + + if (call_stack[id].fno < 0 || call_stack[id].fno >= ain->nr_functions) { + send_response(resp, false); + return; + } + + cJSON *body, *scopes, *arg_scope, *loc_scope, *mem_scope; + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + cJSON_AddItemToObjectCS(body, "scopes", scopes = cJSON_CreateArray()); + + int l_page = call_stack[id].page_slot; + if (page_index_valid(l_page) && heap_get_page(l_page)->type == LOCAL_PAGE) { + int arg_ref = var_ref(l_page, VAR_REF_ARGUMENTS); + int loc_ref = var_ref(l_page, VAR_REF_LOCALS); + cJSON_AddItemToArray(scopes, arg_scope = cJSON_CreateObject()); + cJSON_AddStringToObject(arg_scope, "name", "Arguments"); + cJSON_AddStringToObject(arg_scope, "presentationHint", "arguments"); + cJSON_AddNumberToObject(arg_scope, "variablesReference", arg_ref); + cJSON_AddItemToArray(scopes, loc_scope = cJSON_CreateObject()); + cJSON_AddStringToObject(loc_scope, "name", "Locals"); + cJSON_AddStringToObject(loc_scope, "presentationHint", "locals"); + cJSON_AddNumberToObject(loc_scope, "variablesReference", loc_ref); + } + + int s_page = call_stack[id].struct_page; + if (page_index_valid(s_page) && heap_get_page(s_page)->type == STRUCT_PAGE) { + int mem_ref = var_ref(s_page, VAR_REF_MEMBERS); + cJSON_AddItemToArray(scopes, mem_scope = cJSON_CreateObject()); + cJSON_AddStringToObject(mem_scope, "name", "Members"); + cJSON_AddStringToObject(mem_scope, "presentationHint", "locals"); + cJSON_AddNumberToObject(mem_scope, "variablesReference", mem_ref); + } + + // TODO: stack values? + + send_response(resp, true); +} + +static bool type_is_structured(struct ain_type *type) +{ + switch (type->data) { + case AIN_STRUCT: + case AIN_REF_STRUCT: + case AIN_ARRAY_TYPE: + case AIN_REF_ARRAY_TYPE: + return true; + default: + return false; + } +} + +static int make_var_ref(struct ain_type *type, union vm_value value) +{ + if (type_is_structured(type) && page_index_valid(value.i)) + return var_ref(value.i, VAR_REF_GENERIC); + return 0; +} + +static cJSON *value_to_json(const char *name, struct ain_type *type, union vm_value value) +{ + char *s_type = ain_strtype_d(ain, type); + struct string *s_val = dbg_value_to_string(type, value, 0); + + cJSON *json = cJSON_CreateObject(); + json_add_sjis_to_object(json, "name", name); + json_add_sjis_to_object(json, "value", s_val->text); + json_add_sjis_to_object(json, "type", s_type); + cJSON_AddNumberToObject(json, "variablesReference", make_var_ref(type, value)); + + free_string(s_val); + free(s_type); + return json; +} + +static cJSON *var_to_json(struct ain_variable *var, union vm_value value) +{ + return value_to_json(var->name, &var->type, value); +} + +static void add_arguments_to_array(cJSON *array, struct page *page) +{ + struct ain_function *f = &ain->functions[page->index]; + for (int i = 0; i < f->nr_args; i++) { + cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i])); + } +} + +static void add_locals_to_array(cJSON *array, struct page *page) +{ + struct ain_function *f = &ain->functions[page->index]; + for (int i = f->nr_args; i < f->nr_vars; i++) { + cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i])); + } +} + +static void add_variables_to_array(cJSON *array, struct page *page) +{ + struct ain_function *f = &ain->functions[page->index]; + for (int i = 0; i < f->nr_vars; i++) { + cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i])); + } +} + +static void add_members_to_array(cJSON *array, struct page *page) +{ + struct ain_struct *s = &ain->structures[page->index]; + for (int i = 0; i < s->nr_members; i++) { + cJSON_AddItemToArray(array, var_to_json(&s->members[i], page->values[i])); + } +} + +static void add_globals_to_array(cJSON *array, struct page *page) +{ + for (int i = 0; i < ain->nr_globals; i++) { + cJSON_AddItemToArray(array, var_to_json(&ain->globals[i], page->values[i])); + } +} + +static void add_array_elements_to_array(cJSON *array, struct page *page) +{ + if (!page) + return; + + for (int i = 0; i < page->nr_vars; i++) { + char name[512]; + snprintf(name, 512, "[%d]", i); + struct ain_type type; + type.data = variable_type(page, i, &type.struc, &type.rank); + cJSON_AddItemToArray(array, value_to_json(name, &type, page->values[i])); + } +} + +static void add_page_to_array(cJSON *array, struct page *page) +{ + switch (page->type) { + case GLOBAL_PAGE: + add_globals_to_array(array, page); + break; + case LOCAL_PAGE: + add_variables_to_array(array, page); + break; + case STRUCT_PAGE: + add_members_to_array(array, page); + break; + case ARRAY_PAGE: + add_array_elements_to_array(array, page); + break; + default: + break; + } +} + +static cJSON *var_ref_to_json(struct page *page, enum var_ref_type type) +{ + cJSON *vars = cJSON_CreateArray(); + if (type == VAR_REF_ARGUMENTS) { + add_arguments_to_array(vars, page); + } else if (type == VAR_REF_LOCALS) { + add_locals_to_array(vars, page); + } else if (type == VAR_REF_MEMBERS) { + add_members_to_array(vars, page); + } else if (type == VAR_REF_GENERIC) { + add_page_to_array(vars, page); + } else { + cJSON_Delete(vars); + return NULL; + } + return vars; +} + +static void cmd_variables(cJSON *args, cJSON *resp) +{ + if (!args) { + send_response(resp, false); + return; + } + + cJSON *j_ref = cJSON_GetObjectItemCaseSensitive(args, "variablesReference"); + if (!j_ref || !cJSON_IsNumber(j_ref)) { + send_response(resp, false); + return; + } + + int slot = var_ref_slot(j_ref->valueint); + enum var_ref_type type = var_ref_type(j_ref->valueint); + if (!page_index_valid(slot)) { + send_response(resp, false); + return; + } + + struct page *page = heap_get_page(slot); + + // FIXME: filter/start/count are ignored + + cJSON *vars = var_ref_to_json(page, type); + if (!vars) { + send_response(resp, false); + return; + } + + cJSON *body = cJSON_CreateObject(); + cJSON_AddItemToObjectCS(body, "variables", vars); + cJSON_AddItemToObjectCS(resp, "body", body); + send_response(resp, true); +} + +static void cmd_setInstructionBreakpoints(cJSON *args, cJSON *resp) +{ + static uint32_t *old_breakpoints = NULL; + static int nr_old_breakpoints = 0; + + uint32_t *addresses = NULL; + if (!args) { + goto fail; + } + + cJSON *j_bp = cJSON_GetObjectItemCaseSensitive(args, "breakpoints"); + if (!j_bp || !cJSON_IsArray(j_bp)) { + goto fail; + } + + int nr_breakpoints = cJSON_GetArraySize(j_bp); + addresses = xcalloc(nr_breakpoints, sizeof(uint32_t)); + + int i; + cJSON *e; + cJSON_ArrayForEachIndex(i, e, j_bp) { + if (!cJSON_IsObject(e)) { + goto fail; + } + cJSON *ref = cJSON_GetObjectItemCaseSensitive(e, "instructionReference"); + if (!ref || !cJSON_IsString(ref)) { + goto fail; + } + char *endptr; + addresses[i] = strtol(ref->valuestring, &endptr, 16); + if (ref->valuestring[0] == '\0' || *endptr != '\0') { + goto fail; + } + // FIXME: offset is ignored + // TODO: condition + // TODO: hitCondition + } + + // clear old breakpoints + for (int i = 0; i < nr_old_breakpoints; i++) { + dbg_clear_breakpoint(old_breakpoints[i], NULL); + } + + cJSON *body, *breakpoints, *bp; + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + cJSON_AddItemToObjectCS(body, "breakpoints", breakpoints = cJSON_CreateArray()); + for (int i = 0; i < nr_breakpoints; i++) { + char s_addr[9]; + snprintf(s_addr, 9, "%x", addresses[i]); + cJSON_AddItemToArray(breakpoints, bp = cJSON_CreateObject()); + cJSON_AddStringToObject(breakpoints, "instructionReference", s_addr); + bool verified = dbg_set_address_breakpoint(addresses[i], NULL, NULL); + cJSON_AddBoolToObject(bp, "verified", verified); + // TODO: id, message, offset + } + + // save new breakpoints list + free(old_breakpoints); + old_breakpoints = addresses; + nr_old_breakpoints = nr_breakpoints; + + send_response(resp, true); + return; +fail: + free(addresses); + send_response(resp, false); +} + +static void cmd_evaluate(cJSON *args, cJSON *resp) +{ + if (!args) { + send_response(resp, false); + return; + } + + cJSON *j_expr = cJSON_GetObjectItemCaseSensitive(args, "expression"); + if (!j_expr || !cJSON_IsString(j_expr)) { + send_response(resp, false); + return; + } + + // TODO: frameId + + struct ain_type type; + union vm_value val = dbg_eval_string(j_expr->valuestring, &type); + struct string *s_val = dbg_value_to_string(&type, val, 0); + char *s_type = ain_strtype_d(ain, &type); + + cJSON *body; + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + json_add_sjis_to_object(body, "result", s_val->text); + json_add_sjis_to_object(body, "type", s_type); + cJSON_AddNumberToObject(body, "variablesReference", make_var_ref(&type, val)); + // TODO: presentationHint + + send_response(resp, true); +} + +static void cmd_continue(cJSON *args, cJSON *resp) +{ + send_response(resp, true); + + dap_state = DAP_RUNNING; + dbg_continue(); +} + +static void cmd_pause(cJSON *args, cJSON *resp) +{ + if (dap_state < DAP_RUNNING) { + cJSON_AddBoolToObject(resp, "success", false); + return; + } + + cJSON_AddBoolToObject(resp, "success", true); + send_json(resp); + + if (dap_state != DAP_STOPPED) { + dbg_repl(); + } +} + +static void cmd_stepIn(cJSON *args, cJSON *resp) +{ + dbg_set_step_into_breakpoint(); + send_response(resp, true); + + dap_state = DAP_RUNNING; + dbg_continue(); +} + +static void cmd_stepOut(cJSON *args, cJSON *resp) +{ + dbg_set_finish_breakpoint(); + send_response(resp, true); + + dap_state = DAP_RUNNING; + dbg_continue(); +} + +static void cmd_next(cJSON *args, cJSON *resp) +{ + dbg_set_step_over_breakpoint(); + send_response(resp, true); + + dap_state = DAP_RUNNING; + dbg_continue(); +} + +static void cmd_threads(cJSON *args, cJSON *resp) +{ + cJSON *body, *threads, *thread; + cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject()); + cJSON_AddItemToObjectCS(body, "threads", threads = cJSON_CreateArray()); + cJSON_AddItemToArray(threads, thread = cJSON_CreateObject()); + cJSON_AddNumberToObject(thread, "id", 0); + cJSON_AddStringToObject(thread, "name", "main_thread"); + send_response(resp, true); +} + +static void cmd_setVariable(cJSON *args, cJSON *resp) +{ + // TODO + send_response(resp, false); +} + +static void cmd_disconnect(cJSON *args, cJSON *resp) +{ + // TODO + send_response(resp, false); +} + +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)) { + WARNING("protocol error: command is not a string"); + 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, "setInstructionBreakpoints")) { + cmd_setInstructionBreakpoints(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 { + WARNING("unknown command \"%s\"", command->valuestring); + } + 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) { + WARNING("Debug Adapter Protocol error: no Content-Length header"); + continue; + } + char *buf = malloc(content_length); + if (fread(buf, content_length, 1, stdin) != 1) { + WARNING("fread(stdin): %s", strerror(errno)); + free(buf); + continue; + } + msgq_enqueue(queue, buf); + content_length = -1; + } else { + WARNING("Unknown Debug Adapter Protocol header: %s", header); + } + } + msgq_enqueue(queue, NULL); // EOF + return 0; +} + +void dbg_dap_init(void) +{ + queue = msgq_new(); + +#ifdef _WIN32 + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + + SDL_CreateThread(read_command_thread, "Debugger", NULL); + + while (dap_state < DAP_RUNNING) { + char *msg = msgq_dequeue(queue); + if (!msg) + break; + handle_message(msg); + } +} + +void dbg_dap_quit(void) +{ + emit_terminated_event(); +} + +void dbg_dap_repl(void) +{ + emit_stopped_event(); + dap_state = DAP_STOPPED; + + bool continue_repl = true; + while (continue_repl) { + char *msg = msgq_dequeue(queue); + if (!msg) + break; + continue_repl = handle_message(msg); + } +} + +void dbg_dap_handle_messages(void) +{ + while (!msgq_isempty(queue)) { + char *msg = msgq_dequeue(queue); + if (!msg) + break; + handle_message(msg); + } +} + +void dbg_dap_log(const char *log, const char *fmt, va_list ap) +{ + size_t len = max(1024, strlen(fmt) * 2); + char *buf = xmalloc(len); + vsnprintf(buf, len, fmt, ap); + emit_output_event(log, buf); + free(buf); +} diff --git a/src/hll/CrayfishLogViewer.c b/src/hll/CrayfishLogViewer.c index f8bff4d..d156bb7 100644 --- a/src/hll/CrayfishLogViewer.c +++ b/src/hll/CrayfishLogViewer.c @@ -45,7 +45,7 @@ HLL_WARN_UNIMPLEMENTED( , void, CrayfishLogViewer, SetWindowTitleName, static bool CrayfishLogViewer_AddText(struct string *text) { - sys_message("%s", display_sjis0(text->text)); + log_message("Crayfish", "%s", display_sjis0(text->text)); return true; } diff --git a/src/hll/OutputLog.c b/src/hll/OutputLog.c index 6ce06bc..2cc7ee1 100644 --- a/src/hll/OutputLog.c +++ b/src/hll/OutputLog.c @@ -23,12 +23,25 @@ #include "hll.h" #include "xsystem4.h" -static void OutputLog_Output(int handle, struct string *s) +static struct string **logs = NULL; +static int nr_logs = 0; + +int OutputLog_Create(struct string *name) { - sys_message("%s", display_sjis0(s->text)); + logs = xrealloc_array(logs, nr_logs, nr_logs + 1, sizeof(struct string*)); + logs[nr_logs++] = string_dup(name); + return nr_logs - 1; +} + +static void OutputLog_Output(int handle, struct string *s) +{ + if (handle < 0 || handle >= nr_logs) { + log_message("OutputLog", "%s", display_sjis0(s->text)); + } else { + log_message(display_sjis0(logs[handle]->text), "%s", display_sjis1(s->text)); + } } -HLL_WARN_UNIMPLEMENTED(0, int, OutputLog, Create, struct string *name); HLL_WARN_UNIMPLEMENTED( , void, OutputLog, Clear, int handle); HLL_WARN_UNIMPLEMENTED(0, int, OutputLog, Save, int handle, struct string *filename); HLL_WARN_UNIMPLEMENTED(0, bool, OutputLog, EnableAutoSave, int handle, struct string *filename); diff --git a/src/input.c b/src/input.c index dbe31d1..e3db35a 100644 --- a/src/input.c +++ b/src/input.c @@ -544,5 +544,7 @@ void handle_events(void) break; } } + if (dbg_dap) + dbg_dap_handle_messages(); } diff --git a/src/meson.build b/src/meson.build index 0cb8b8f..b8a4c53 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,6 +20,7 @@ xsystem4 = [version_h, 'heap.c', 'id_pool.c', 'input.c', + 'msgqueue.c', 'page.c', 'resume.c', 'savedata.c', @@ -137,6 +138,7 @@ if get_option('debugger').allowed() add_project_arguments('-DDEBUGGER_ENABLED', language : 'c') xsystem4 += 'debug.c' xsystem4 += 'debugger_cmd.c' + xsystem4 += 'debugger_dap.c' if chibi.found() xsystem4_deps += chibi xsystem4 += 'debugger_scm.c' diff --git a/src/msgqueue.c b/src/msgqueue.c new file mode 100644 index 0000000..5c57668 --- /dev/null +++ b/src/msgqueue.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 kichikuou + * + * 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 +#include "msgqueue.h" + +struct msgq_elem { + void *msg; + struct msgq_elem *next; +}; + +struct msgq *msgq_new(void) { + struct msgq *q = calloc(1, sizeof(struct msgq)); + q->mutex = SDL_CreateMutex(); + q->cond_nonempty = SDL_CreateCond(); + return q; +} + +void msgq_free(struct msgq *q) { + SDL_DestroyMutex(q->mutex); + SDL_DestroyCond(q->cond_nonempty); + free(q); +} + +void msgq_enqueue(struct msgq *q, void *msg) { + struct msgq_elem *e = malloc(sizeof(struct msgq_elem)); + e->msg = msg; + e->next = NULL; + + SDL_LockMutex(q->mutex); + if (!q->head) { + q->head = q->last = e; + } else { + q->last->next = e; + q->last = e; + } + SDL_UnlockMutex(q->mutex); + SDL_CondSignal(q->cond_nonempty); +} + +void *msgq_dequeue(struct msgq *q) { + SDL_LockMutex(q->mutex); + while (!q->head) + SDL_CondWait(q->cond_nonempty, q->mutex); + + struct msgq_elem *e = q->head; + q->head = e->next; + if (!e->next) + q->last = NULL; + + SDL_UnlockMutex(q->mutex); + + void *msg = e->msg; + free(e); + return msg; +} + +void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms) { + SDL_LockMutex(q->mutex); + + while (!q->head && SDL_CondWaitTimeout(q->cond_nonempty, q->mutex, timeout_ms) == 0) + ; + + if (!q->head) { // timed out + SDL_UnlockMutex(q->mutex); + return NULL; + } + + struct msgq_elem *e = q->head; + q->head = e->next; + if (!e->next) + q->last = NULL; + + SDL_UnlockMutex(q->mutex); + + void *msg = e->msg; + free(e); + return msg; +} diff --git a/src/parts/debug.c b/src/parts/debug.c index 41b3cc7..5990271 100644 --- a/src/parts/debug.c +++ b/src/parts/debug.c @@ -23,64 +23,64 @@ static void parts_cg_print(struct parts_cg *cg, int indent) { - indent_printf(indent, "cg.no = %d,\n", cg->no); + indent_message(indent, "cg.no = %d,\n", cg->no); } static void parts_text_print(struct parts_text *text, int indent) { - indent_printf(indent, "text.lines = {\n"); + indent_message(indent, "text.lines = {\n"); for (unsigned i = 0; i < text->nr_lines; i++) { struct string *s = parts_text_line_get(&text->lines[i]); - indent_printf(indent+1, "contents = \"%s\",\n", display_sjis0(s->text)); - indent_printf(indent+1, "width = %u,\n", text->lines[i].width); - indent_printf(indent+1, "height = %u,\n", text->lines[i].height); + indent_message(indent+1, "contents = \"%s\",\n", display_sjis0(s->text)); + indent_message(indent+1, "width = %u,\n", text->lines[i].width); + indent_message(indent+1, "height = %u,\n", text->lines[i].height); free_string(s); } - indent_printf(indent, "},\n"); - indent_printf(indent, "text.line_space = %u,\n", text->line_space); - indent_printf(indent, "text.cursor = "); + indent_message(indent, "},\n"); + indent_message(indent, "text.line_space = %u,\n", text->line_space); + indent_message(indent, "text.cursor = "); gfx_print_point(&text->cursor); - printf(",\n"); - indent_printf(indent, "text.ts = "); + sys_message(",\n"); + indent_message(indent, "text.ts = "); gfx_print_text_style(&text->ts, indent); - printf("\n"); + sys_message("\n"); } static void parts_animation_print(struct parts_animation *anim, int indent) { - indent_printf(indent, "anim.start_no = %u,\n", anim->start_no); - indent_printf(indent, "anim.frame_time = %u,\n", anim->frame_time); - indent_printf(indent, "anim.elapsed = %u,\n", anim->elapsed); - indent_printf(indent, "anim.current_frame = %u,\n", anim->current_frame); - indent_printf(indent, "anim.frames = {\n"); + indent_message(indent, "anim.start_no = %u,\n", anim->start_no); + indent_message(indent, "anim.frame_time = %u,\n", anim->frame_time); + indent_message(indent, "anim.elapsed = %u,\n", anim->elapsed); + indent_message(indent, "anim.current_frame = %u,\n", anim->current_frame); + indent_message(indent, "anim.frames = {\n"); for (unsigned i = 0; i < anim->nr_frames; i++) { - indent_printf(indent+1, "[%u] = ", i); + indent_message(indent+1, "[%u] = ", i); gfx_print_texture(&anim->frames[i], indent+1); - indent_printf(indent+1, ",\n"); + indent_message(indent+1, ",\n"); } - indent_printf(indent, "}\n"); + indent_message(indent, "}\n"); } static void parts_numeral_print(struct parts_numeral *num, int indent) { - indent_printf(indent, "num.have_num = %s,\n", num->have_num ? "true" : "false"); - indent_printf(indent, "num.num = %d,\n", num->num); - indent_printf(indent, "num.space = %d,\n", num->space); - indent_printf(indent, "num.show_comma = %d,\n", num->show_comma); - indent_printf(indent, "num.length = %d,\n", num->length); - indent_printf(indent, "num.cg_no = %d,\n", num->cg_no); - indent_printf(indent, "num.cg = {\n"); + indent_message(indent, "num.have_num = %s,\n", num->have_num ? "true" : "false"); + indent_message(indent, "num.num = %d,\n", num->num); + indent_message(indent, "num.space = %d,\n", num->space); + indent_message(indent, "num.show_comma = %d,\n", num->show_comma); + indent_message(indent, "num.length = %d,\n", num->length); + indent_message(indent, "num.cg_no = %d,\n", num->cg_no); + indent_message(indent, "num.cg = {\n"); for (int i = 0; i < 12; i++) { - indent_printf(indent+1, "[%d] = ", i); + indent_message(indent+1, "[%d] = ", i); gfx_print_texture(&num->cg[i], indent+1); - indent_printf(indent+1, ",\n"); + indent_message(indent+1, ",\n"); } - indent_printf(indent, "}\n"); + indent_message(indent, "}\n"); } static void parts_gauge_print(struct parts_gauge *gauge, int indent) { - indent_printf(indent, "gauge.cg = "); + indent_message(indent, "gauge.cg = "); gfx_print_texture(&gauge->cg, indent); } @@ -99,78 +99,78 @@ static void parts_construction_process_print(struct parts_construction_process * [PARTS_CP_COPY_TEXT] = "PARTS_CP_COPY_TEXT", }; - indent_printf(indent, "cproc.ops = {\n"); + indent_message(indent, "cproc.ops = {\n"); indent++; int i = 0; struct parts_cp_op *op; TAILQ_FOREACH(op, &cproc->ops, entry) { - indent_printf(indent, "[%d] = {\n", i++); + indent_message(indent, "[%d] = {\n", i++); indent++; const char *type = "INVALID_CONSTRUCTION_PROCERSS_TYPE"; if (op->type >= 0 && op->type < PARTS_NR_CP_TYPES) type = type_names[op->type]; - indent_printf(indent, "type = %s,\n", type); + indent_message(indent, "type = %s,\n", type); switch (op->type) { case PARTS_CP_CREATE: case PARTS_CP_CREATE_PIXEL_ONLY: - indent_printf(indent, "create = {w=%d,h=%d}\n", op->create.w, op->create.h); + indent_message(indent, "create = {w=%d,h=%d}\n", op->create.w, op->create.h); break; case PARTS_CP_CG: - indent_printf(indent, "cg.no = %d\n", op->cg.no); + indent_message(indent, "cg.no = %d\n", op->cg.no); break; case PARTS_CP_FILL: case PARTS_CP_FILL_ALPHA_COLOR: case PARTS_CP_FILL_AMAP: - indent_printf(indent, "fill.rect = {x=%d,y=%d,w=%d,h=%d},\n", + indent_message(indent, "fill.rect = {x=%d,y=%d,w=%d,h=%d},\n", op->fill.x, op->fill.y, op->fill.w, op->fill.h); - indent_printf(indent, "fill.color = (%d,%d,%d,%d)\n", + indent_message(indent, "fill.color = (%d,%d,%d,%d)\n", op->fill.r, op->fill.g, op->fill.b, op->fill.a); break; case PARTS_CP_DRAW_CUT_CG: case PARTS_CP_COPY_CUT_CG: - indent_printf(indent, "cut_cg.cg_no = %d,\n", op->cut_cg.cg_no); - indent_printf(indent, "cut_cg.dst = {x=%d,y=%d,w=%d,h=%d},\n", + indent_message(indent, "cut_cg.cg_no = %d,\n", op->cut_cg.cg_no); + indent_message(indent, "cut_cg.dst = {x=%d,y=%d,w=%d,h=%d},\n", op->cut_cg.dx, op->cut_cg.dy, op->cut_cg.dw, op->cut_cg.dh); - indent_printf(indent, "cut_cg.src = {x=%d,y=%d,w=%d,h=%d},\n", + indent_message(indent, "cut_cg.src = {x=%d,y=%d,w=%d,h=%d},\n", op->cut_cg.sx, op->cut_cg.sy, op->cut_cg.sw, op->cut_cg.sh); - indent_printf(indent, "cut_cg.interp_type = %d\n", op->cut_cg.interp_type); + indent_message(indent, "cut_cg.interp_type = %d\n", op->cut_cg.interp_type); break; case PARTS_CP_DRAW_TEXT: case PARTS_CP_COPY_TEXT: - indent_printf(indent, "text.text = \"%s\",\n", display_sjis0(op->text.text->text)); - indent_printf(indent, "text.pos = {x=%d,y=%d},\n", op->text.x, op->text.y); - indent_printf(indent, "text.line_space = %d,\n", op->text.line_space); - indent_printf(indent, "text.style = "); + indent_message(indent, "text.text = \"%s\",\n", display_sjis0(op->text.text->text)); + indent_message(indent, "text.pos = {x=%d,y=%d},\n", op->text.x, op->text.y); + indent_message(indent, "text.line_space = %d,\n", op->text.line_space); + indent_message(indent, "text.style = "); gfx_print_text_style(&op->text.style, indent); - printf("\n"); + sys_message("\n"); break; } indent--; - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } indent--; - indent_printf(indent, "}\n"); + indent_message(indent, "}\n"); } static void parts_print_state(struct parts_state *state, int indent) { if (state->type == PARTS_UNINITIALIZED) { - printf("UNINITIALIZED"); + sys_message("UNINITIALIZED"); return; } - printf("{\n"); + sys_message("{\n"); indent++; struct parts_common *com = &state->common; - indent_printf(indent, "dims = {w=%d,h=%d},\n", state->common.w, state->common.h); - indent_printf(indent, "origin_offset = "); gfx_print_point(&com->origin_offset); printf(",\n"); - indent_printf(indent, "hitbox = "); gfx_print_rectangle(&com->hitbox); printf(",\n"); - indent_printf(indent, "surface_area = "); gfx_print_rectangle(&com->surface_area); printf(",\n"); + indent_message(indent, "dims = {w=%d,h=%d},\n", state->common.w, state->common.h); + indent_message(indent, "origin_offset = "); gfx_print_point(&com->origin_offset); sys_message(",\n"); + indent_message(indent, "hitbox = "); gfx_print_rectangle(&com->hitbox); sys_message(",\n"); + indent_message(indent, "surface_area = "); gfx_print_rectangle(&com->surface_area); sys_message(",\n"); switch (state->type) { case PARTS_UNINITIALIZED: break; @@ -196,22 +196,22 @@ static void parts_print_state(struct parts_state *state, int indent) } indent--; - indent_printf(indent, "}"); + indent_message(indent, "}"); } static void parts_print_motion_param(union parts_motion_param param, enum parts_motion_type type) { switch (type) { case PARTS_MOTION_POS: - printf("{x=%d,y=%d}", param.x, param.y); + sys_message("{x=%d,y=%d}", param.x, param.y); break; case PARTS_MOTION_VIBRATION_SIZE: - printf("{w=%d,h=%d}", param.x, param.y); + sys_message("{w=%d,h=%d}", param.x, param.y); break; case PARTS_MOTION_ALPHA: case PARTS_MOTION_CG: case PARTS_MOTION_NUMERAL_NUMBER: - printf("%d", param.i); + sys_message("%d", param.i); break; case PARTS_MOTION_HGAUGE_RATE: case PARTS_MOTION_VGAUGE_RATE: @@ -220,7 +220,7 @@ static void parts_print_motion_param(union parts_motion_param param, enum parts_ case PARTS_MOTION_ROTATE_X: case PARTS_MOTION_ROTATE_Y: case PARTS_MOTION_ROTATE_Z: - printf("%f", param.f); + sys_message("%f", param.f); break; } } @@ -246,55 +246,55 @@ static void parts_print_motion(struct parts_motion *motion, int indent) if (motion->type >= 0 && motion->type < PARTS_NR_MOTION_TYPES) type = type_names[motion->type]; - printf("{\n"); + sys_message("{\n"); indent++; - indent_printf(indent, "type = %s,\n", type); - indent_printf(indent, "begin = "); + indent_message(indent, "type = %s,\n", type); + indent_message(indent, "begin = "); parts_print_motion_param(motion->begin, motion->type); - printf(",\n"); - indent_printf(indent, "end = "); + sys_message(",\n"); + indent_message(indent, "end = "); parts_print_motion_param(motion->end, motion->type); - printf(",\n"); - indent_printf(indent, "begin_time = %d,\n", motion->begin_time); - indent_printf(indent, "end_time = %d,\n", motion->end_time); + sys_message(",\n"); + indent_message(indent, "begin_time = %d,\n", motion->begin_time); + indent_message(indent, "end_time = %d,\n", motion->end_time); indent--; - indent_printf(indent, "}"); + indent_message(indent, "}"); } static void parts_print_params(struct parts_params *global, struct parts_params *local, int indent) { - printf("{\n"); + sys_message("{\n"); indent++; - indent_printf(indent, "z = %d (%d),\n", global->z, local->z); - indent_printf(indent, "pos = "); + indent_message(indent, "z = %d (%d),\n", global->z, local->z); + indent_message(indent, "pos = "); gfx_print_point(&global->pos); - printf(" ("); + sys_message(" ("); gfx_print_point(&local->pos); - printf("),\n"); - indent_printf(indent, "show = %s (%s),\n", global->show ? "true" : "false", + sys_message("),\n"); + indent_message(indent, "show = %s (%s),\n", global->show ? "true" : "false", local->show ? "true" : "false"); - indent_printf(indent, "alpha = %u (%u),\n", (unsigned)global->alpha, (unsigned)local->alpha); - indent_printf(indent, "scale = {x=%f,y=%f} ({x=%f,y=%f}),\n", global->scale.x, global->scale.y, + indent_message(indent, "alpha = %u (%u),\n", (unsigned)global->alpha, (unsigned)local->alpha); + indent_message(indent, "scale = {x=%f,y=%f} ({x=%f,y=%f}),\n", global->scale.x, global->scale.y, local->scale.x, local->scale.y); - indent_printf(indent, "rotation = {x=%f,y=%f,z=%f} ({x=%f,y=%f,z=%f}),\n", + indent_message(indent, "rotation = {x=%f,y=%f,z=%f} ({x=%f,y=%f,z=%f}),\n", global->rotation.x, global->rotation.y, global->rotation.z, local->rotation.x, local->rotation.y, local->rotation.z); - indent_printf(indent, "add_color = "); + indent_message(indent, "add_color = "); gfx_print_color(&global->add_color); - printf(" ("); + sys_message(" ("); gfx_print_color(&local->add_color); - printf("),\n"); - indent_printf(indent, "multiply_color = "); + sys_message("),\n"); + indent_message(indent, "multiply_color = "); gfx_print_color(&global->multiply_color); - printf(" ("); + sys_message(" ("); gfx_print_color(&local->multiply_color); - printf("),\n"); + sys_message("),\n"); indent--; - indent_printf(indent, "}"); + indent_message(indent, "}"); } static void _parts_print(struct parts *parts, int indent) @@ -308,71 +308,71 @@ static void _parts_print(struct parts *parts, int indent) if (parts->state >= 0 && parts->state < PARTS_NR_STATES) state = state_names[parts->state]; - indent_printf(indent, "parts %d = {\n", parts->no); + indent_message(indent, "parts %d = {\n", parts->no); indent++; // print states - indent_printf(indent, "state = %s,\n", state); + indent_message(indent, "state = %s,\n", state); for (int i = 0; i < PARTS_NR_STATES; i++) { if (parts->states[i].type == PARTS_UNINITIALIZED && i != parts->state) continue; - indent_printf(indent, "state[%s] = ", state_names[i]); + indent_message(indent, "state[%s] = ", state_names[i]); parts_print_state(&parts->states[i], indent); - printf(",\n"); + sys_message(",\n"); } // print params - indent_printf(indent, "local = "); + indent_message(indent, "local = "); parts_print_params(&parts->global, &parts->local, indent); - printf(",\n"); + sys_message(",\n"); // print misc. data if (parts->delegate_index >= 0) - indent_printf(indent, "delegate_index = %d,\n", parts->delegate_index); - indent_printf(indent, "sprite_deform = %d,\n", parts->sprite_deform); - indent_printf(indent, "clickable = %s,\n", parts->clickable ? "true" : "false"); + indent_message(indent, "delegate_index = %d,\n", parts->delegate_index); + indent_message(indent, "sprite_deform = %d,\n", parts->sprite_deform); + indent_message(indent, "clickable = %s,\n", parts->clickable ? "true" : "false"); if (parts->on_cursor_sound >= 0) - indent_printf(indent, "on_cursor_sound = %d,\n", parts->on_cursor_sound); + indent_message(indent, "on_cursor_sound = %d,\n", parts->on_cursor_sound); if (parts->on_click_sound >= 0) - indent_printf(indent, "on_click_sound = %d,\n", parts->on_click_sound); - indent_printf(indent, "origin_mode = %d,\n", parts->origin_mode); - indent_printf(indent, "parent = %d,\n", parts->parent ? parts->parent->no : -1); + indent_message(indent, "on_click_sound = %d,\n", parts->on_click_sound); + indent_message(indent, "origin_mode = %d,\n", parts->origin_mode); + indent_message(indent, "parent = %d,\n", parts->parent ? parts->parent->no : -1); if (parts->linked_to >= 0) - indent_printf(indent, "linked_to = %d,\n", parts->linked_to); + indent_message(indent, "linked_to = %d,\n", parts->linked_to); if (parts->linked_from >= 0) - indent_printf(indent, "linked_from = %d,\n", parts->linked_from); - indent_printf(indent, "draw_filter = %d,\n", parts->draw_filter); + indent_message(indent, "linked_from = %d,\n", parts->linked_from); + indent_message(indent, "draw_filter = %d,\n", parts->draw_filter); // print motion data if (TAILQ_EMPTY(&parts->motion)) { - indent_printf(indent, "motion = {},\n"); + indent_message(indent, "motion = {},\n"); } else { - indent_printf(indent, "motion = {\n"); + indent_message(indent, "motion = {\n"); int i = 0; struct parts_motion *motion; TAILQ_FOREACH(motion, &parts->motion, entry) { - indent_printf(indent+1, "[%d] = ", i++); + indent_message(indent+1, "[%d] = ", i++); parts_print_motion(motion, indent+1); - printf(",\n"); + sys_message(",\n"); } - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } // print children if (TAILQ_EMPTY(&parts->children)) { } else { - indent_printf(indent, "children = {\n"); + indent_message(indent, "children = {\n"); struct parts *child; PARTS_FOREACH_CHILD(child, parts) { _parts_print(child, indent+1); - printf(",\n"); + sys_message(",\n"); } - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } indent--; - indent_printf(indent, "}"); + indent_message(indent, "}"); } void parts_print(struct parts *parts) @@ -413,77 +413,77 @@ static void parts_cmd_parts(unsigned nr_args, char **args) static void parts_list_print(struct parts *parts, int indent) { - indent_printf(indent, parts->local.show ? "+ " : "- "); - printf("parts %d ", parts->no); + indent_message(indent, parts->local.show ? "+ " : "- "); + sys_message("parts %d ", parts->no); struct parts_state *state = &parts->states[parts->state]; switch (state->type) { case PARTS_UNINITIALIZED: - printf("(uninitialized)"); + sys_message("(uninitialized)"); break; case PARTS_CG: if (state->cg.name) - printf("(cg %s)", display_sjis0(state->cg.name->text)); + sys_message("(cg %s)", display_sjis0(state->cg.name->text)); else - printf("(cg %d)", state->cg.no); + sys_message("(cg %d)", state->cg.no); break; case PARTS_TEXT: - printf("(text)"); // TODO? store actual text and print it here + sys_message("(text)"); // TODO? store actual text and print it here break; case PARTS_ANIMATION: - printf("(animation %d+%d)", state->anim.start_no, state->anim.nr_frames); + sys_message("(animation %d+%d)", state->anim.start_no, state->anim.nr_frames); break; case PARTS_NUMERAL: - printf("(numeral %d)", state->num.cg_no); + sys_message("(numeral %d)", state->num.cg_no); break; case PARTS_HGAUGE: - printf("(hgauge)"); // TODO? store rate and cg and print them here + sys_message("(hgauge)"); // TODO? store rate and cg and print them here break; case PARTS_VGAUGE: - printf("(vgauge)"); + sys_message("(vgauge)"); break; case PARTS_CONSTRUCTION_PROCESS: { - printf("(construction process:"); + sys_message("(construction process:"); struct parts_cp_op *op; TAILQ_FOREACH(op, &state->cproc.ops, entry) { switch (op->type) { case PARTS_CP_CREATE: - printf(" create"); + sys_message(" create"); break; case PARTS_CP_CREATE_PIXEL_ONLY: - printf(" create-pixel-only"); + sys_message(" create-pixel-only"); break; case PARTS_CP_CG: - printf(" cg"); + sys_message(" cg"); break; case PARTS_CP_FILL: - printf(" fill"); + sys_message(" fill"); break; case PARTS_CP_FILL_ALPHA_COLOR: - printf(" fill-alpha-color"); + sys_message(" fill-alpha-color"); break; case PARTS_CP_FILL_AMAP: - printf(" fill-amap"); + sys_message(" fill-amap"); break; case PARTS_CP_DRAW_CUT_CG: - printf(" draw-cut-cg"); + sys_message(" draw-cut-cg"); break; case PARTS_CP_COPY_CUT_CG: - printf(" copy-cut-cg"); + sys_message(" copy-cut-cg"); break; case PARTS_CP_DRAW_TEXT: - printf(" draw-text"); + sys_message(" draw-text"); break; case PARTS_CP_COPY_TEXT: - printf(" copy-text"); + sys_message(" copy-text"); break; } } - printf(")"); + sys_message(")"); break; } } - printf(" @ z=%d (%d)\n", parts->global.z, parts->local.z); + sys_message(" @ z=%d (%d)\n", parts->global.z, parts->local.z); struct parts *child; PARTS_FOREACH_CHILD(child, parts) { diff --git a/src/scene.c b/src/scene.c index d23e45a..98f4e77 100644 --- a/src/scene.c +++ b/src/scene.c @@ -139,15 +139,15 @@ void scene_set_sprite_z2(struct sprite *sp, int z, int z2) void scene_print_sprite(struct sprite *sp, int indent) { - printf("{\n"); + sys_message("{\n"); indent++; - indent_printf(indent, "z = (%d,%d),\n", sp->z, sp->z2); - indent_printf(indent, "has_pixel = %s,\n", sp->has_pixel ? "true" : "false"); - indent_printf(indent, "has_alpha = %s,\n", sp->has_alpha ? "true" : "false"); - indent_printf(indent, "hidden = %s,\n", sp->hidden ? "true" : "false"); - indent_printf(indent, "in_scene = %s,\n", sp->in_scene ? "true" : "false"); + indent_message(indent, "z = (%d,%d),\n", sp->z, sp->z2); + indent_message(indent, "has_pixel = %s,\n", sp->has_pixel ? "true" : "false"); + indent_message(indent, "has_alpha = %s,\n", sp->has_alpha ? "true" : "false"); + indent_message(indent, "hidden = %s,\n", sp->hidden ? "true" : "false"); + indent_message(indent, "in_scene = %s,\n", sp->in_scene ? "true" : "false"); indent--; - printf("}"); + sys_message("}"); } @@ -158,7 +158,7 @@ void scene_print(void) if (p->debug_print) { p->debug_print(p); } else { - printf("unknown_scene_entity "); + sys_message("unknown_scene_entity "); scene_print_sprite(p, 0); putchar('\n'); } diff --git a/src/sprite.c b/src/sprite.c index 0fb3088..400c5cc 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -468,79 +468,79 @@ void sprite_call_plugins(void) void gfx_print_color(SDL_Color *c) { - printf("(%d,%d,%d,%d)", c->r, c->g, c->b, c->a); + sys_message("(%d,%d,%d,%d)", c->r, c->g, c->b, c->a); } void gfx_print_rectangle(Rectangle *r) { - printf("{x=%d,y=%d,w=%d,h=%d}", r->x, r->y, r->w, r->h); + sys_message("{x=%d,y=%d,w=%d,h=%d}", r->x, r->y, r->w, r->h); } void gfx_print_point(Point *p) { - printf("{x=%d,y=%d}", p->x, p->y); + sys_message("{x=%d,y=%d}", p->x, p->y); } void gfx_print_texture(struct texture *t, int indent) { - printf("{\n"); - indent_printf(indent+1, "initialized = %s,\n", t->handle ? "true" : "false"); - indent_printf(indent+1, "size = (%d,%d),\n", t->w, t->h); - indent_printf(indent+1, "has_alpha = %s,\n", t->has_alpha ? "true" : "false"); - indent_printf(indent, "}"); + sys_message("{\n"); + indent_message(indent+1, "initialized = %s,\n", t->handle ? "true" : "false"); + indent_message(indent+1, "size = (%d,%d),\n", t->w, t->h); + indent_message(indent+1, "has_alpha = %s,\n", t->has_alpha ? "true" : "false"); + indent_message(indent, "}"); } void sprite_print(struct sact_sprite *sp) { int indent = 0; - indent_printf(indent, "sprite %d = {\n", sp->no); + indent_message(indent, "sprite %d = {\n", sp->no); indent++; - indent_printf(indent, "sp = {\n"); + indent_message(indent, "sp = {\n"); indent++; - indent_printf(indent, "z = (%d,%d),\n", sp->sp.z, sp->sp.z2); - indent_printf(indent, "has_pixel = %s,\n", sp->sp.has_pixel ? "true" : "false"); - indent_printf(indent, "has_alpha = %s,\n", sp->sp.has_alpha ? "true" : "false"); - indent_printf(indent, "hidden = %s,\n", sp->sp.hidden ? "true" : "false"); - indent_printf(indent, "in_scene = %s,\n", sp->sp.in_scene ? "true" : "false"); + indent_message(indent, "z = (%d,%d),\n", sp->sp.z, sp->sp.z2); + indent_message(indent, "has_pixel = %s,\n", sp->sp.has_pixel ? "true" : "false"); + indent_message(indent, "has_alpha = %s,\n", sp->sp.has_alpha ? "true" : "false"); + indent_message(indent, "hidden = %s,\n", sp->sp.hidden ? "true" : "false"); + indent_message(indent, "in_scene = %s,\n", sp->sp.in_scene ? "true" : "false"); indent--; - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); - indent_printf(indent, "texture = "); gfx_print_texture(&sp->texture, 1); printf(",\n"); - indent_printf(indent, "color = "); gfx_print_color(&sp->color); printf(",\n"); - indent_printf(indent, "blend_rate = %d,\n", sp->blend_rate); - indent_printf(indent, "multiply_color = "); gfx_print_color(&sp->multiply_color); printf(",\n"); - indent_printf(indent, "add_color = "); gfx_print_color(&sp->add_color); printf(",\n"); + indent_message(indent, "texture = "); gfx_print_texture(&sp->texture, 1); sys_message(",\n"); + indent_message(indent, "color = "); gfx_print_color(&sp->color); sys_message(",\n"); + indent_message(indent, "blend_rate = %d,\n", sp->blend_rate); + indent_message(indent, "multiply_color = "); gfx_print_color(&sp->multiply_color); sys_message(",\n"); + indent_message(indent, "add_color = "); gfx_print_color(&sp->add_color); sys_message(",\n"); switch (sp->draw_method) { - case DRAW_METHOD_NORMAL: indent_printf(indent, "draw_method = normal,\n"); break; - case DRAW_METHOD_SCREEN: indent_printf(indent, "draw_method = screen,\n"); break; - case DRAW_METHOD_MULTIPLY: indent_printf(indent, "draw_method = multiply,\n"); break; - case DRAW_METHOD_ADDITIVE: indent_printf(indent, "draw_method = additive,\n"); break; - default: indent_printf(indent, "draw_method = unknown,\n"); break; + case DRAW_METHOD_NORMAL: indent_message(indent, "draw_method = normal,\n"); break; + case DRAW_METHOD_SCREEN: indent_message(indent, "draw_method = screen,\n"); break; + case DRAW_METHOD_MULTIPLY: indent_message(indent, "draw_method = multiply,\n"); break; + case DRAW_METHOD_ADDITIVE: indent_message(indent, "draw_method = additive,\n"); break; + default: indent_message(indent, "draw_method = unknown,\n"); break; } - indent_printf(indent, "rect = "); gfx_print_rectangle(&sp->rect); printf(",\n"); + indent_message(indent, "rect = "); gfx_print_rectangle(&sp->rect); sys_message(",\n"); - indent_printf(indent, "text = {\n"); + indent_message(indent, "text = {\n"); indent++; - indent_printf(indent, "texture = "); gfx_print_texture(&sp->text.texture, 2); printf(",\n"); - indent_printf(indent, "home = "); gfx_print_point(&sp->text.home); printf(",\n"); - indent_printf(indent, "pos = "); gfx_print_point(&sp->text.pos); printf(",\n"); - indent_printf(indent, "char_space = %d,\n", sp->text.char_space); - indent_printf(indent, "line_space = %d,\n", sp->text.line_space); + indent_message(indent, "texture = "); gfx_print_texture(&sp->text.texture, 2); sys_message(",\n"); + indent_message(indent, "home = "); gfx_print_point(&sp->text.home); sys_message(",\n"); + indent_message(indent, "pos = "); gfx_print_point(&sp->text.pos); sys_message(",\n"); + indent_message(indent, "char_space = %d,\n", sp->text.char_space); + indent_message(indent, "line_space = %d,\n", sp->text.line_space); if (sp->plugin) { if (sp->plugin->debug_print) { - indent_printf(indent, "plugin = {\n"); + indent_message(indent, "plugin = {\n"); sp->plugin->debug_print(sp, indent + 1); - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); } else { - indent_printf(indent, "plugin = %s,\n", sp->plugin->name); + indent_message(indent, "plugin = %s,\n", sp->plugin->name); } } indent--; - indent_printf(indent, "},\n"); + indent_message(indent, "},\n"); - indent_printf(indent, "cg_no = %d\n", sp->cg_no); + indent_message(indent, "cg_no = %d\n", sp->cg_no); indent--; - indent_printf(indent, "}\n"); + indent_message(indent, "}\n"); } diff --git a/src/system4.c b/src/system4.c index b608634..cab5696 100644 --- a/src/system4.c +++ b/src/system4.c @@ -376,6 +376,7 @@ enum { #ifdef DEBUGGER_ENABLED LOPT_NODEBUG, LOPT_DEBUG, + LOPT_DEBUG_API, #endif }; @@ -420,6 +421,7 @@ int main(int argc, char *argv[]) #ifdef DEBUGGER_ENABLED { "nodebug", no_argument, 0, LOPT_NODEBUG }, { "debug", no_argument, 0, LOPT_DEBUG }, + { "debug-api", no_argument, 0, LOPT_DEBUG_API }, #endif { 0 } }; @@ -477,6 +479,10 @@ int main(int argc, char *argv[]) case LOPT_DEBUG: dbg_start_in_debugger = true; break; + case LOPT_DEBUG_API: + dbg_dap = true; + sys_silent = true; + break; #endif } } @@ -536,14 +542,7 @@ int main(int argc, char *argv[]) } apply_game_specific_hacks(ain); - asset_manager_init(); - -#ifdef DEBUGGER_ENABLED dbg_init(); - if (dbg_start_in_debugger) - dbg_repl(); -#endif - sys_exit(vm_execute_ain(ain)); } diff --git a/src/text.c b/src/text.c index 7e9dc02..aa55f23 100644 --- a/src/text.c +++ b/src/text.c @@ -455,21 +455,21 @@ void gfx_set_font_name(const char *name) void gfx_print_text_style(struct text_style *style, int indent) { - printf("{\n"); + sys_message("{\n"); indent++; - indent_printf(indent, "face = %u,\n", style->face); - indent_printf(indent, "size = %f,\n", style->size); - indent_printf(indent, "bold_width = %f,\n", style->bold_width); - indent_printf(indent, "weight = %u,\n", style->weight); - indent_printf(indent, "edge_weight = {l=%f,u=%f,r=%f,d=%f},\n", + indent_message(indent, "face = %u,\n", style->face); + indent_message(indent, "size = %f,\n", style->size); + indent_message(indent, "bold_width = %f,\n", style->bold_width); + indent_message(indent, "weight = %u,\n", style->weight); + indent_message(indent, "edge_weight = {l=%f,u=%f,r=%f,d=%f},\n", style->edge_left, style->edge_up, style->edge_right, style->edge_down); - indent_printf(indent, "color = "); gfx_print_color(&style->color); printf(",\n"); - indent_printf(indent, "edge_color = "); gfx_print_color(&style->edge_color); printf(",\n"); - indent_printf(indent, "scale_x = %f,\n", style->scale_x); - indent_printf(indent, "space_scale_x = %f,\n", style->space_scale_x); - indent_printf(indent, "font_spacing = %f,\n", style->font_spacing); + indent_message(indent, "color = "); gfx_print_color(&style->color); sys_message(",\n"); + indent_message(indent, "edge_color = "); gfx_print_color(&style->edge_color); sys_message(",\n"); + indent_message(indent, "scale_x = %f,\n", style->scale_x); + indent_message(indent, "space_scale_x = %f,\n", style->space_scale_x); + indent_message(indent, "font_spacing = %f,\n", style->font_spacing); indent--; - indent_printf(indent, "}"); + indent_message(indent, "}"); } diff --git a/src/util.c b/src/util.c index f2d6e26..695be0e 100644 --- a/src/util.c +++ b/src/util.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ #include "system4/utfsjis.h" #include "xsystem4.h" +#include "debugger.h" // In a Windows environment, console output should be SJIS-encoded // (assuming the user is using Japanese non-unicode locale). @@ -173,13 +175,24 @@ void get_time(int *hour, int *min, int *sec, int *ms) *ms = ts.tv_nsec / 1000000; } -void indent_printf(int indent, const char *fmt, ...) +void indent_message(int indent, const char *fmt, ...) { for (int i = 0; i < indent; i++) putchar('\t'); va_list ap; va_start(ap, fmt); - vprintf(fmt, ap); + sys_vmessage(fmt, ap); + va_end(ap); +} + +void log_message(const char *log, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (dbg_dap) + dbg_dap_log(log, fmt, ap); + else + sys_vmessage(fmt, ap); va_end(ap); } diff --git a/src/vm.c b/src/vm.c index 49e1167..54ad21b 100644 --- a/src/vm.c +++ b/src/vm.c @@ -482,7 +482,7 @@ static void system_call(enum syscall_code code) } case SYS_OUTPUT: {// system.Output(string szText) struct string *str = stack_peek_string(0); - sys_message("%s", display_sjis0(str->text)); + log_message("stdout", "%s", display_sjis0(str->text)); // XXX: caller S_POPs break; } @@ -2380,9 +2380,7 @@ _Noreturn void _vm_error(const char *fmt, ...) va_end(ap); sys_warning("at %s (0x%X) in:\n", current_instruction_name(), instr_ptr); vm_stack_trace(); -#ifdef DEBUGGER_ENABLED dbg_repl(); -#endif sys_exit(1); } diff --git a/subprojects/libsys4 b/subprojects/libsys4 index 22fdbf0..05b4cec 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit 22fdbf0b4b63704818abdee3c8af7574ddc0e10e +Subproject commit 05b4cec8a6085e8339d138fbde186873c849757b