+ PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
+}
+
+/*
+======================
+Break- and Watchpoints
+======================
+*/
+typedef struct
+{
+ char break_statement[256];
+ char watch_global[256];
+ int watch_edict;
+ char watch_field[256];
+}
+debug_data_t;
+static debug_data_t debug_data[PRVM_PROG_MAX];
+
+void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
+{
+ char vabuf[1024];
+ Con_Printf("PRVM_Breakpoint: %s\n", text);
+ PRVM_PrintState(prog, stack_index);
+ if (prvm_breakpointdump.integer)
+ Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
+}
+
+void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
+{
+ size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
+ if (memcmp(o, n, sz))
+ {
+ char buf[1024];
+ char valuebuf_o[128];
+ char valuebuf_n[128];
+ PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
+ PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
+ dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
+ PRVM_Breakpoint(prog, stack_index, buf);
+ memcpy(o, n, sz);
+ }
+}
+
+static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
+{
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ if (!prog->loaded)
+ return;
+ if (debug->break_statement[0])
+ {
+ if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
+ {
+ prog->break_statement = atoi(debug->break_statement);
+ prog->break_stack_index = 0;
+ }
+ else
+ {
+ mfunction_t *func;
+ func = PRVM_ED_FindFunction (prog, debug->break_statement);
+ if (!func)
+ {
+ Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
+ prog->break_statement = -1;
+ }
+ else
+ {
+ prog->break_statement = func->first_statement;
+ prog->break_stack_index = 1;
+ }
+ }
+ if (prog->break_statement >= -1)
+ Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
+ }
+ else
+ prog->break_statement = -1;
+
+ if (debug->watch_global[0])
+ {
+ ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
+ if( !global )
+ {
+ Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
+ prog->watch_global_type = ev_void;
+ }
+ else
+ {
+ size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
+ prog->watch_global = global->ofs;
+ prog->watch_global_type = (etype_t)global->type;
+ memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
+ }
+ if (prog->watch_global_type != ev_void)
+ Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
+ }
+ else
+ prog->watch_global_type = ev_void;
+
+ if (debug->watch_field[0])
+ {
+ ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
+ if( !field )
+ {
+ Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
+ prog->watch_field_type = ev_void;
+ }
+ else
+ {
+ size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
+ prog->watch_edict = debug->watch_edict;
+ prog->watch_field = field->ofs;
+ prog->watch_field_type = (etype_t)field->type;
+ if (prog->watch_edict < prog->num_edicts)
+ memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
+ else
+ memset(&prog->watch_edictfield_value, 0, sz);
+ }
+ if (prog->watch_edict != ev_void)
+ Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
+ }
+ else
+ prog->watch_field_type = ev_void;
+}
+
+static void PRVM_Breakpoint_f(void)
+{
+ prvm_prog_t *prog;
+
+ if( Cmd_Argc() == 2 ) {
+ if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+ return;
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ debug->break_statement[0] = 0;
+ }
+ PRVM_UpdateBreakpoints(prog);
+ return;
+ }
+ if( Cmd_Argc() != 3 ) {
+ Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
+ return;
+ }
+
+ if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
+ return;
+
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
+ }
+ PRVM_UpdateBreakpoints(prog);
+}
+
+static void PRVM_GlobalWatchpoint_f(void)
+{
+ prvm_prog_t *prog;
+
+ if( Cmd_Argc() == 2 ) {
+ if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+ return;
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ debug->watch_global[0] = 0;
+ }
+ PRVM_UpdateBreakpoints(prog);
+ return;
+ }
+ if( Cmd_Argc() != 3 ) {
+ Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
+ return;
+ }
+
+ if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
+ return;
+
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
+ }
+ PRVM_UpdateBreakpoints(prog);
+}
+
+static void PRVM_EdictWatchpoint_f(void)
+{
+ prvm_prog_t *prog;
+
+ if( Cmd_Argc() == 2 ) {
+ if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
+ return;
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ debug->watch_field[0] = 0;
+ }
+ PRVM_UpdateBreakpoints(prog);
+ return;
+ }
+ if( Cmd_Argc() != 4 ) {
+ Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
+ return;
+ }
+
+ if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
+ return;
+
+ {
+ debug_data_t *debug = &debug_data[prog - prvm_prog_list];
+ debug->watch_edict = atoi(Cmd_Argv(2));
+ strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
+ }
+ PRVM_UpdateBreakpoints(prog);