//DMW /* F1 will return to progs.src F2 will try to open a file with the name of which is on that line. (excluding comments/tabs). Needs conditions. F3 will give a prompt for typing in a value name to see the value. F4 will save F5 will run (unbreak). F6 will list the stack. F7 will compile. F8 will move execution F9 will set a break point. F10 will step over. F11 will step into. */ #include "quakedef.h" #ifdef TEXTEDITOR #include "pr_common.h" #ifdef _WIN32 #define editaddcr_default "1" #else #define editaddcr_default "0" #endif //#if defined(ANDROID) || defined(SERVERONLY) #define debugger_default "0" //#else //#define debugger_default "1" //#endif static cvar_t editstripcr = CVARD("edit_stripcr", "1", "remove \\r from eols (on load)"); static cvar_t editaddcr = CVARD("edit_addcr", editaddcr_default, "make sure that each line ends with a \\r (on save)"); static cvar_t edittabspacing = CVARD("edit_tabsize", "4", "How wide tab alignment is"); cvar_t pr_debugger = CVARAFD("pr_debugger", debugger_default, "debugger", CVAR_SAVE, "When enabled, QC errors and debug events will enable step-by-step tracing."); extern cvar_t pr_sourcedir; static pubprogfuncs_t *editprogfuncs; typedef struct fileblock_s { struct fileblock_s *next; struct fileblock_s *prev; int allocatedlength; int datalength; int flags; char *data; } fileblock_t; #define FB_BREAK 1 static fileblock_t *cursorblock, *firstblock, *executionblock, *viewportystartblock; static void *E_Malloc(int size) { char *mem; mem = Z_Malloc(size); if (!mem) Sys_Error("Failed to allocate enough mem for editor\n"); return mem; } static void E_Free(void *mem) { Z_Free(mem); } #define GETBLOCK(s, ret) ret = (void *)E_Malloc(sizeof(fileblock_t) + s);ret->allocatedlength = s;ret->data = (char *)ret + sizeof(fileblock_t) fileblock_t *GenAsm(int statement) { char linebuf[256]; fileblock_t *b; int l; if (!editprogfuncs) return NULL; editprogfuncs->GenerateStatementString(editprogfuncs, statement, linebuf, sizeof(linebuf)); l = strlen(linebuf); b = E_Malloc(sizeof(fileblock_t) + l); b->allocatedlength = l; b->datalength = l; b->data = (char *)b + sizeof(fileblock_t); memcpy(b->data, linebuf, l); return b; } static char OpenEditorFile[256]; qboolean editoractive; //(export) qboolean editormodal; //doesn't return. (export) int editorstep; static qboolean madechanges; static qboolean editenabled; static qboolean insertkeyhit=true; static qboolean useeval; static qboolean stepasm; static char evalstring[256]; static int executionlinenum; //step by step debugger static int cursorlinenum, cursorx; static int viewportx; static int viewporty; static int VFS_GETC(vfsfile_t *fp) { unsigned char c; VFS_READ(fp, &c, 1); return c; } //newsize = number of chars, EXCLUDING terminator. static void MakeNewSize(fileblock_t *block, int newsize) //this is used to resize a block. It allocates a new one, copys the data frees the old one and links it into the right place //it is called when the user is typing { fileblock_t *newblock; newsize = (newsize + 4)&~3; //We allocate a bit extra, so we don't need to keep finding a new block for each and every character. if (block->allocatedlength >= newsize) return; //Ignore. This block is too large already. Don't bother resizeing, cos that's pretty much pointless. GETBLOCK(newsize, newblock); memcpy(newblock->data, block->data, block->datalength); newblock->prev = block->prev; newblock->next = block->next; if (newblock->prev) newblock->prev->next = newblock; if (newblock->next) newblock->next->prev = newblock; newblock->datalength = block->datalength; newblock->flags = block->flags; E_Free(block); if (firstblock == block) firstblock = newblock; if (cursorblock == block) cursorblock = newblock; } static int positionacross; static void GetCursorpos(void) { int a; char *s; int ts = edittabspacing.value; if (ts < 1) ts = 4; for (a=0,positionacross=0,s=cursorblock->data;a < cursorx && *s;s++,a++) { if (*s == '\t') { positionacross += ts; positionacross -= cursorx%ts; } else positionacross++; } // positionacross = cursorofs; } static void SetCursorpos(void) { int a=0; char *s; int ts = edittabspacing.value; if (ts < 1) ts = 4; for (cursorx=0,s=cursorblock->data;cursorx < positionacross && *s;s++,a++) { if (*s == '\t') { cursorx += ts; cursorx -= cursorx%ts; } else cursorx++; } cursorx=a; //just in case if (cursorx > cursorblock->datalength) cursorx = cursorblock->datalength; } static void CloseEditor(void) { fileblock_t *b; Key_Dest_Remove(kdm_editor); editoractive = false; editprogfuncs = NULL; cursorblock = NULL; if (!firstblock) return; OpenEditorFile[0] = '\0'; while(firstblock) { b = firstblock; firstblock=firstblock->next; E_Free(b); } madechanges = false; editormodal = false; editorstep = DEBUG_TRACE_OFF; firstblock = NULL; executionlinenum = 0; } static qboolean EditorSaveFile(char *s) //returns true if succesful { // FILE *F; fileblock_t *b; int len=0; int pos=0; char *data; char native[MAX_OSPATH]; if (!FS_NativePath(s, FS_GAMEONLY, native, sizeof(native))) Con_Printf("Not saving.\n"); Con_Printf("Saving to \"%s\"\n", native); for (b = firstblock; b; b = b->next) //find total length required. { len += b->datalength; len+=2; //extra for \n } data = Hunk_TempAlloc(len); for (b = firstblock; b; b = b->next) //find total length required. { memcpy(data + pos, b->data, b->datalength); pos += b->datalength; if (editaddcr.ival) { data[pos]='\r'; pos++; } data[pos]='\n'; pos++; } COM_WriteFile(s, FS_GAMEONLY, data, len); madechanges = false; editenabled = true; executionlinenum = 0; return true; } static void EditorNewFile(void) { fileblock_t *b; while(firstblock) { b = firstblock; firstblock=firstblock->next; E_Free(b); } GETBLOCK(64, firstblock); GETBLOCK(64, firstblock->next); firstblock->next->prev = firstblock; cursorblock = firstblock; cursorlinenum = 0; cursorx = 0; viewportystartblock = NULL; madechanges = true; executionlinenum = 0; Key_Dest_Add(kdm_editor); editoractive = true; editenabled = true; } static void EditorOpenFile(const char *name, qboolean readonly) { int i; char line[8192]; int len, flen, pos=0; vfsfile_t *F; fileblock_t *b; char *prname; pubprogfuncs_t *epf = editprogfuncs; CloseEditor(); strcpy(OpenEditorFile, name); if (!(F = FS_OpenVFS(OpenEditorFile, "rb", FS_GAME))) { Q_snprintfz(OpenEditorFile, sizeof(OpenEditorFile), "%s/%s", pr_sourcedir.string, name); if (!(F = FS_OpenVFS(OpenEditorFile, "rb", FS_GAME))) { Con_Printf("Couldn't open file \"%s\"\nA new file will be created\n", name); strcpy(OpenEditorFile, name); Key_Dest_Add(kdm_console); EditorNewFile(); return; } } i=1; prname = OpenEditorFile; if (!strncmp(prname, "src/", 4)) prname += 4; if (!strncmp(prname, "source/", 7)) prname += 7; flen = VFS_GETLEN(F); while(pos < flen) { len = 0; for(;;) { if (pos+len >= flen || len > sizeof(line) - 16) break; line[len] = VFS_GETC(F); if (line[len] == '\n') break; len++; } pos+=len; pos++; //and a \n if (editstripcr.ival) { if (line[len-1] == '\r') len--; } b = firstblock; GETBLOCK(len+1, firstblock); firstblock->prev = b; if (b) b->next = firstblock; firstblock->datalength = len; memcpy(firstblock->data, line, len); if (epf) { if (epf->ToggleBreak(epf, prname, i, 3)) { firstblock->flags |= FB_BREAK; } } #ifndef CLIENTONLY else { if (svprogfuncs) { if (svprogfuncs->ToggleBreak(svprogfuncs, prname, i, 3)) { firstblock->flags |= FB_BREAK; } } } #endif i++; } if (firstblock == NULL) { GETBLOCK(10, firstblock); } else for (; firstblock->prev; firstblock=firstblock->prev); VFS_CLOSE(F); cursorblock = firstblock; cursorx = 0; viewportystartblock = NULL; madechanges = false; executionlinenum = 0; editenabled = !readonly; Key_Dest_Add(kdm_editor); editoractive = true; editprogfuncs = epf; } extern qboolean keydown[K_MAX]; void Editor_Key(int key, int unicode) { int i; fileblock_t *nb; if (keybindings[key][0]) if (!strcmp(keybindings[key][0], "toggleconsole")) { Key_Dest_Add(kdm_console); return; } /* if (CmdAfterSave) { switch (key) { case 'Y': case 'y': if (!EditorSaveFile(OpenEditorFile)) { Con_Printf("Couldn't save file \"%s\"\n", OpenEditorFile); key_dest = key_console; } else if (!CmdAfterSaveCalled) { CmdAfterSaveCalled=true; (*CmdAfterSave)(); CmdAfterSaveCalled=false; } CmdAfterSave = NULL; break; case 'N': case 'n': (*CmdAfterSave)(); CmdAfterSave = NULL; break; case 'C': case 'c': CmdAfterSave = NULL; break; } return; } */ if (key == K_SHIFT) return; if (useeval) { switch(key) { case K_ESCAPE: if (editprogfuncs) editprogfuncs->debug_trace = DEBUG_TRACE_OFF; useeval = false; return; case K_F3: useeval = false; return; case K_DEL: evalstring[0] = '\0'; return; case K_BACKSPACE: i = strlen(evalstring); if (i < 1) return; evalstring[i-1] = '\0'; return; default: if (unicode) { i = strlen(evalstring); evalstring[i] = unicode; evalstring[i+1] = '\0'; } return; case K_F5: case K_F9: case K_F11: case K_MWHEELUP: case K_UPARROW: case K_PGUP: case K_MWHEELDOWN: case K_DOWNARROW: case K_PGDN: case K_LEFTARROW: case K_RIGHTARROW: break; } } /* if (ctrl_down && (key == 'c' || key == K_INS)) key = K_F9; if ((ctrl_down && key == 'v') || (shift_down && key == K_INS)) key = K_F10; */ switch (key) { case K_LSHIFT: case K_RSHIFT: break; case K_LALT: case K_RALT: break; case K_LCTRL: case K_RCTRL: break; case K_MWHEELUP: case K_UPARROW: case K_PGUP: if (!cursorblock) break; GetCursorpos(); { int a; if (key == K_PGUP) a =(vid.height/8)/2; else if (key == K_MWHEELUP) a = 5; else a = 1; while(a) { a--; if (cursorblock->prev) { cursorblock = cursorblock->prev; cursorlinenum--; } else if (cursorlinenum>1) { cursorlinenum--; nb = GenAsm(cursorlinenum); nb->next = cursorblock; cursorblock->prev = nb; firstblock = cursorblock = nb; } } } SetCursorpos(); break; case K_MWHEELDOWN: case K_DOWNARROW: case K_PGDN: GetCursorpos(); { int a; if (key == K_PGDN) a =(vid.height/8)/2; else if (key == K_MWHEELDOWN) a = 5; else a = 1; while(a) { a--; if (cursorblock->next) { cursorblock = cursorblock->next; cursorlinenum++; } else { nb = GenAsm(cursorlinenum+1); if (nb) { cursorlinenum++; nb->prev = cursorblock; cursorblock->next = nb; cursorblock = nb; } } } } SetCursorpos(); break; // case K_BACK: case K_F1: Con_Printf( "Editor help:\n" "F1: Show help\n" "F2: Open file named on cursor line\n" "F3: Toggle expression evaluator\n" "F4: Save file\n" "F5: Stop tracing (resume)\n" "F6: Print stack trace\n" "F7: Save file and recompile\n" "F8: Change current point of execution\n" "F9: Set breakpoint\n" "F10: Save file, recompile, reload vm\n" "F11: Single step\n" "F12: \n" "Escape: Abort call, close editor\n" ); Cbuf_AddText("toggleconsole\n", RESTRICT_LOCAL); break; // case K_FORWARD: case K_F2: { char file[1024]; char *s; Q_strncpyz(file, cursorblock->data, sizeof(file)); s = file; while (*s) { if ((*s == '/' && s[1] == '/') || (*s == '\t')) { *s = '\0'; break; } s++; } if (*file) EditorOpenFile(file, false); } break; case K_F3: if (editprogfuncs) useeval = true; break; case K_F4: EditorSaveFile(OpenEditorFile); break; case K_F5: /*stop debugging*/ editormodal = false; editorstep = DEBUG_TRACE_OFF; break; case K_F6: if (editprogfuncs) PR_StackTrace(editprogfuncs, 2); break; case K_F7: /*save+recompile*/ EditorSaveFile(OpenEditorFile); if (!editprogfuncs) Cbuf_AddText("compile; toggleconsole\n", RESTRICT_LOCAL); break; case K_F8: /*move execution point to here - I hope you move to the same function!*/ executionlinenum = cursorlinenum; executionblock = cursorblock; break; case K_F9: /*set breakpoint*/ { int f = 0; char *fname = OpenEditorFile; if (!strncmp(fname, "src/", 4)) fname += 4; if (!strncmp(fname, "source/", 7)) fname += 7; if (editprogfuncs) { if (editprogfuncs->ToggleBreak(editprogfuncs, fname, cursorlinenum, 2)) f |= 1; else f |= 2; } #ifndef CLIENTONLY else if (svprogfuncs) { if (svprogfuncs->ToggleBreak(svprogfuncs, fname, cursorlinenum, 2)) f |= 1; else f |= 2; } #endif if (f & 1) cursorblock->flags |= FB_BREAK; else cursorblock->flags &= ~FB_BREAK; } break; case K_F10: editormodal = false; editorstep = DEBUG_TRACE_OVER; break; case K_F11: //single step editormodal = false; editorstep = DEBUG_TRACE_INTO; break; case K_F12: //save+apply changes, supposedly EditorSaveFile(OpenEditorFile); Cbuf_AddText("applycompile\n", RESTRICT_LOCAL); break; // case K_STOP: case K_ESCAPE: if (editprogfuncs && editormodal) { editprogfuncs->AbortStack(editprogfuncs); CloseEditor(); executionlinenum = 0; stepasm = true; } else CloseEditor(); editormodal = false; editorstep = DEBUG_TRACE_ABORT; break; case K_HOME: cursorx = 0; break; case K_END: cursorx = cursorblock->datalength; break; case K_LEFTARROW: cursorx--; if (keydown[K_LCTRL] || keydown[K_RCTRL]) { //skip additional whitespace while(cursorx > 0 && (cursorblock->data[cursorx-1] == ' ' || cursorblock->data[cursorx-1] <= '\t')) cursorx--; //skip over the word, to the start of it while(cursorx > 0 && ((cursorblock->data[cursorx-1] >= 'a' && cursorblock->data[cursorx-1] <= 'z') || (cursorblock->data[cursorx-1] >= 'A' && cursorblock->data[cursorx-1] <= 'Z') || (cursorblock->data[cursorx-1] >= '0' && cursorblock->data[cursorx-1] <= '9'))) cursorx--; } if (cursorx < 0) cursorx = 0; break; case K_RIGHTARROW: if (keydown[K_LCTRL] || keydown[K_RCTRL]) { while(cursorx+1 < cursorblock->datalength && ((cursorblock->data[cursorx] >= 'a' && cursorblock->data[cursorx] <= 'z') || (cursorblock->data[cursorx] >= 'A' && cursorblock->data[cursorx] <= 'Z') || (cursorblock->data[cursorx] >= '0' && cursorblock->data[cursorx] <= '9'))) cursorx++; cursorx++; while(cursorx+1 < cursorblock->datalength && (cursorblock->data[cursorx] == ' ' || cursorblock->data[cursorx] <= '\t')) cursorx++; } else { cursorx++; if (cursorx > cursorblock->datalength) cursorx = cursorblock->datalength; } break; case K_BACKSPACE: cursorx--; if (cursorx < 0) { fileblock_t *b = cursorblock; if (b == firstblock) //no line above to remove to { cursorx=0; break; } if (editenabled) { cursorlinenum-=1; madechanges = true; cursorblock = b->prev; MakeNewSize(cursorblock, b->datalength + cursorblock->datalength+5); cursorx = cursorblock->datalength; memcpy(cursorblock->data + cursorblock->datalength, b->data, b->datalength); cursorblock->datalength += b->datalength; cursorblock->next = b->next; if (b->next) b->next->prev = cursorblock; // cursorblock->prev->next = cursorblock->next; // cursorblock->next->prev = cursorblock->prev; E_Free(b); // cursorblock = b; } else { cursorblock = cursorblock->prev; cursorx = cursorblock->datalength; } break; } case K_DEL: //bksp falls through. if (editenabled) { int a; fileblock_t *b; cursorlinenum=-1; madechanges = true; //FIXME: does this work right? if (!cursorblock->datalength && cursorblock->next && cursorblock->prev) //blank line { b = cursorblock; if (b->next) b->next->prev = b->prev; if (b->prev) b->prev->next = b->next; if (cursorblock->next) cursorblock = cursorblock->next; else cursorblock = cursorblock->prev; E_Free(b); } else { for (a = cursorx; a < cursorblock->datalength;a++) cursorblock->data[a] = cursorblock->data[a+1]; cursorblock->datalength--; } if (cursorx > cursorblock->datalength) cursorx = cursorblock->datalength; } break; case K_ENTER: case K_KP_ENTER: if (editenabled) { fileblock_t *b = cursorblock; cursorlinenum=-1; madechanges = true; GETBLOCK(b->datalength - cursorx, cursorblock); cursorblock->next = b->next; cursorblock->prev = b; b->next = cursorblock; if (cursorblock->next) cursorblock->next->prev = cursorblock; if (cursorblock->prev) cursorblock->prev->next = cursorblock; cursorblock->datalength = b->datalength - cursorx; memcpy(cursorblock->data, b->data+cursorx, cursorblock->datalength); b->datalength = cursorx; cursorx = 0; } else if (cursorblock->next) { cursorblock = cursorblock->next; cursorlinenum++; cursorx = 0; } break; case K_INS: insertkeyhit = insertkeyhit?false:true; break; default: if (!editenabled) break; if (unicode < ' ' && unicode != '\t') //we deem these as unprintable break; if (insertkeyhit) //insert a char { char *s; madechanges = true; MakeNewSize(cursorblock, cursorblock->datalength+5); //allocate a bit extra, so we don't need to keep resizing it and shifting loads a data about s = cursorblock->data + cursorblock->datalength; while (s >= cursorblock->data + cursorx) { s[1] = s[0]; s--; } cursorx++; cursorblock->datalength++; *(s+1) = unicode; } else //over write a char { MakeNewSize(cursorblock, cursorblock->datalength+5); //not really needed cursorblock->data[cursorx] = unicode; cursorx++; if (cursorx > cursorblock->datalength) cursorblock->datalength = cursorx; } break; } } static void Draw_Line(int vy, fileblock_t *b, int cursorx) { int nx = 0; int y; char *tooltip = NULL, *t; int nnx; qbyte *d = b->data; qbyte *c; int i; int smx = (mousecursor_x * vid.pixelwidth) / vid.width, smy = (mousecursor_y * vid.pixelheight) / vid.height; unsigned int colour; int ts = edittabspacing.value; char linebuf[128]; if (cursorx >= 0) c = d + cursorx; else c = NULL; Font_BeginString(font_default, nx, vy, &nx, &y); if (ts < 1) ts = 4; ts*=8; //figure out the colour if (b->flags & (FB_BREAK)) { if (executionblock == b) colour = COLOR_MAGENTA<= y && smy < y + Font_CharHeight()) { int e, s; nx = -viewportx; for (i = 0; i < b->datalength; i++) { if (d[i] == '\t') { nnx=nx+ts; nnx-=(nnx - -viewportx)%ts; } else nnx = Font_CharEndCoord(font_default, nx, (int)d[i] | (colour)); if (smx >= nx && smx <= nnx) { for(s = i; s > 0; ) { if ((d[s-1] >= 'a' && d[s-1] <= 'z') || (d[s-1] >= 'A' && d[s-1] <= 'Z') || (d[s-1] >= '0' && d[s-1] <= '9') || d[s-1] == '.' || d[s-1] == '_') s--; else break; } for (e = i; e < b->datalength; ) { if ((d[e] >= 'a' && d[e] <= 'z') || (d[e] >= 'A' && d[e] <= 'Z') || (d[e] >= '0' && d[e] <= '9') || /*d[e] == '.' ||*/ d[e] == '_') e++; else break; } if (e >= s+sizeof(linebuf)) e = s+sizeof(linebuf) - 1; memcpy(linebuf, d+s, e - s); linebuf[e-s] = 0; if (*linebuf) tooltip = editprogfuncs->EvaluateDebugString(editprogfuncs, linebuf); break; } nx = nnx; } } nx = -viewportx; for (i = 0; i < b->datalength; i++) { if (*d == '\t') { if (d == c) { int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) { viewportx -= -nx; if (viewportx < 0) viewportx = 0; } } nx+=ts; nx-=(nx - -viewportx)%ts; d++; continue; } if (nx <= (int)vid.pixelwidth || cursorx>=0) nnx = Font_DrawChar(nx, y, (int)*d | (colour)); else nnx = vid.pixelwidth; if (d == c) { int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) { viewportx -= -nx; if (viewportx < 0) viewportx = 0; } } nx = nnx; d++; } /*we didn't do the cursor! stick it at the end*/ if (c && c >= d) { int e = Font_DrawChar(nx, y, (int)0xe00b | (CON_WHITEMASK|CON_BLINKTEXT)); if (e >= vid.pixelwidth) viewportx += e - vid.pixelwidth; if (nx < 0) { viewportx -= -nx; if (viewportx < 0) viewportx = 0; } } if (tooltip) { nx = ((mousecursor_x+16) * vid.pixelwidth) / vid.width; while(*tooltip) { for (t = tooltip, smx = nx; *tooltip; tooltip++) { if (*tooltip == '\n') break; smx = Font_CharEndCoord(font_default, smx, *tooltip); } y = Font_CharHeight(); Font_EndString(font_default); R2D_ImageColours(0, 0, 0, 1); R2D_FillBlock(((nx)*vid.width) / vid.pixelwidth, ((smy)*vid.height) / vid.pixelheight, ((smx - nx)*vid.width) / vid.pixelwidth, (y*vid.height) / vid.pixelheight); R2D_ImageColours(1, 1, 1, 1); Font_BeginString(font_default, nx, vy, &y, &y); for(smx = nx; t < tooltip; t++) { smx = Font_DrawChar(smx, smy, (COLOR_CYAN<prev) return b; b = b->prev; lines--; if (lines <= 0) return b; } return NULL; } void Editor_Draw(void) { int y; int c; fileblock_t *b; if ((editoractive && cls.state == ca_disconnected) || editormodal) R2D_EditorBackground(); if (cursorlinenum < 0) //look for the cursor line num { cursorlinenum = 0; for (b = firstblock; b; b=b->next) { cursorlinenum++; if (b == cursorblock) break; } } if (!viewportystartblock) //look for the cursor line num { y = 0; for (viewportystartblock = firstblock; viewportystartblock; viewportystartblock=viewportystartblock->prev) { y++; if (y == viewporty) break; } } if (madechanges) Draw_FunString (vid.width - 8, 0, "!"); if (!insertkeyhit) Draw_FunString (vid.width - 16, 0, "O"); if (!editenabled) Draw_FunString (vid.width - 24, 0, "R"); Draw_FunString(0, 0, va("%6i:%4i:%s", cursorlinenum, cursorx+1, OpenEditorFile)); if (useeval) { if (!editprogfuncs) useeval = false; else { char *eq, *term; Draw_FunString(0, 8, evalstring); eq = strchr(evalstring, '='); if (eq) { term = strchr(eq, ';'); if (!term) term = strchr(eq, '\n'); if (!term) term = strchr(eq, '\r'); if (term) { *term = '\0'; eq = NULL; } else *eq = '\0'; } Draw_FunString(vid.width/2, 8, editprogfuncs->EvaluateDebugString(editprogfuncs, evalstring)); if (eq) *eq = '='; } y = 16; } else y=8; b = firstline(); for (; b; b=b->next) { c = -1; if (b == cursorblock) c = cursorx; Draw_Line(y, b, c); y+=8; if (y > vid.height) break; } /* if (CmdAfterSave) { if (madechanges) { M_DrawTextBox (0, 0, 36, 5); M_PrintWhite (16, 12, OpenEditorFile); M_PrintWhite (16, 28, "Do you want to save the open file?"); M_PrintWhite (16, 36, "[Y]es/[N]o/[C]ancel"); } else { if (!CmdAfterSaveCalled) { CmdAfterSaveCalled = true; (*CmdAfterSave) (); CmdAfterSaveCalled = false; } CmdAfterSave = NULL; } } */ } int QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *statement, int nump, char **parms) { const char *f1, *f2; if (!pr_debugger.ival) { Con_Printf("Set %s to trace\n", pr_debugger.name); return DEBUG_TRACE_OFF; //get lost } //we can cope with no line info by displaying asm if (editormodal || !statement) return DEBUG_TRACE_OFF; //whoops if (qrenderer == QR_NONE) { int i; char buffer[8192]; char *r; vfsfile_t *f; f = FS_OpenVFS(filename, "rb", FS_GAME); if (!f) Con_Printf("%s - %i\n", filename, *line); else { for (i = 0; i < *line; i++) { VFS_GETS(f, buffer, sizeof(buffer)); } if ((r = strchr(buffer, '\r'))) { r[0] = '\n';r[1]='\0';} Con_Printf("%s", buffer); VFS_CLOSE(f); } //PF_break(NULL); return DEBUG_TRACE_OUT; } editprogfuncs = prfncs; f1 = OpenEditorFile; f2 = filename; if (!strncmp(f1, "src/", 4)) f1 += 4; if (!strncmp(f2, "src/", 4)) f2 += 4; if (!strncmp(f1, "source/", 7)) f1 += 7; if (!strncmp(f2, "source/", 7)) f2 += 7; stepasm = !line; if (stepasm) { fileblock_t *nb, *lb; int i; EditorNewFile(); E_Free(firstblock->next); E_Free(firstblock); cursorlinenum = *statement; firstblock = GenAsm(cursorlinenum); cursorblock = firstblock; for (i = cursorlinenum; i > 0 && i > cursorlinenum - 20; ) { i--; firstblock->prev = GenAsm(i); firstblock->prev->next = firstblock; firstblock = firstblock->prev; } lb = cursorblock; for (i = cursorlinenum; i < cursorlinenum+20; ) { i++; nb = GenAsm(i); lb->next = nb; nb->prev = lb; lb = nb; } } else { if (!editoractive || strcmp(f1, f2)) { if (editoractive && madechanges) EditorSaveFile(OpenEditorFile); EditorOpenFile(filename, true); } for (cursorlinenum = 1, cursorblock = firstblock; cursorlinenum < *line && cursorblock->next; cursorlinenum++) cursorblock=cursorblock->next; } executionlinenum = cursorlinenum; executionblock = cursorblock; if (!parms) { double oldrealtime = realtime; editormodal = true; editorstep = DEBUG_TRACE_OFF; while(editormodal && editoractive && editprogfuncs) { realtime = Sys_DoubleTime(); // key_dest = key_editor; scr_disabled_for_loading=false; SCR_UpdateScreen(); Sys_SendKeyEvents(); IN_Commands (); S_ExtraUpdate(); NET_Sleep(20/1000.0, false); //any os. } realtime = oldrealtime; editormodal = false; } if (stepasm) { if (line) *line = 0; *statement = executionlinenum; } else if (line) *line = executionlinenum; return editorstep; } void Editor_ProgsKilled(pubprogfuncs_t *dead) { if (editprogfuncs == dead) { editprogfuncs = NULL; editormodal = false; editorstep = DEBUG_TRACE_OFF; } } static void Editor_f(void) { int argc = Cmd_Argc(); if (argc != 2 && argc != 3) { Con_Printf("edit [line]\n"); return; } editprogfuncs = NULL; useeval = false; if (editoractive && madechanges) EditorSaveFile(OpenEditorFile); EditorOpenFile(Cmd_Argv(1), false); // EditorNewFile(); if (argc == 3) { int line = atoi(Cmd_Argv(2)); for (cursorlinenum = 1, cursorblock = firstblock; cursorlinenum < line && cursorblock->next; cursorlinenum++) cursorblock=cursorblock->next; } } void Editor_Init(void) { Cmd_AddCommand("edit", Editor_f); Cvar_Register(&editstripcr, "Text editor"); Cvar_Register(&editaddcr, "Text editor"); Cvar_Register(&edittabspacing, "Text editor"); Cvar_Register(&pr_debugger, "Text editor"); } #endif