From 2163fb7c77d5f4995a559a3589c65b107033511e Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 20 Jan 2019 01:00:18 +0000 Subject: [PATCH] Early version of fteqccgui-qt a few other misc tweaks, eg for the debug protocol in linux fteqw. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5387 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 5 +- engine/client/cl_main.c | 2 + engine/client/m_options.c | 10 + engine/client/renderer.c | 2 +- engine/client/sys_linux.c | 37 +- engine/common/pr_bgcmd.c | 48 +- engine/gl/gl_heightmap.c | 6 +- engine/gl/gl_rlight.c | 2 +- engine/qclib/cmdlib.h | 2 +- engine/qclib/comprout.c | 10 +- engine/qclib/decomp.c | 20 +- engine/qclib/gui.h | 12 +- engine/qclib/packager.c | 3 +- engine/qclib/progsint.h | 2 +- engine/qclib/progslib.h | 6 +- engine/qclib/qcc.h | 8 +- engine/qclib/qcc_cmdlib.c | 2 +- engine/qclib/qcc_pr_comp.c | 2 +- engine/qclib/qcc_pr_lex.c | 21 +- engine/qclib/qccgui.c | 141 +-- engine/qclib/qccguiqt.cpp | 1914 ++++++++++++++++++++++++++++++++++++ engine/qclib/qccguistuff.c | 170 +++- engine/qclib/qccmain.c | 17 +- engine/qclib/qcctui.c | 2 +- engine/qclib/qcd.h | 6 +- engine/qclib/qcd_main.c | 8 +- engine/server/pr_cmds.c | 2 +- 27 files changed, 2232 insertions(+), 228 deletions(-) create mode 100644 engine/qclib/qccguiqt.cpp diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 769e6332..84c9a30c 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -29,6 +29,7 @@ extern cvar_t cl_predict_players_frac; extern cvar_t cl_predict_players_latency; extern cvar_t cl_predict_players_nudge; extern cvar_t cl_lerp_players; +extern cvar_t cl_lerp_maxinterval; extern cvar_t cl_solid_players; extern cvar_t cl_item_bobbing; @@ -3279,7 +3280,7 @@ static void CL_UpdateNetFrameLerpState(qboolean force, int curframe, int curbase frame = (fst==FST_BASE)?curbaseframe:curframe; if (force || frame != le->newframe[fst]) { - le->framelerpdeltatime[fst] = bound(0, cl.servertime - le->newframestarttime[fst], 0.1); //clamp to 10 tics per second + le->framelerpdeltatime[fst] = bound(0, cl.servertime - le->newframestarttime[fst], cl_lerp_maxinterval.value); //clamp to 10 tics per second if (!force) { @@ -3607,7 +3608,7 @@ static void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newp if (!VectorEquals(le->neworigin, snew__origin) || !VectorEquals(le->newangle, snew->angles)) { le->newsequence = snew->sequence; - le->orglerpdeltatime = bound(0, oldpack->servertime - le->orglerpstarttime, 0.11); //clamp to 10 tics per second + le->orglerpdeltatime = bound(0, oldpack->servertime - le->orglerpstarttime, cl_lerp_maxinterval.value); //clamp to 10 tics per second le->orglerpstarttime = oldpack->servertime; VectorCopy(le->neworigin, le->oldorigin); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index b43a7058..8caeceef 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -101,6 +101,7 @@ cvar_t m_yaw = CVARF("m_yaw","0.022", CVAR_ARCHIVE); cvar_t m_forward = CVARF("m_forward","1", CVAR_ARCHIVE); cvar_t m_side = CVARF("m_side","0.8", CVAR_ARCHIVE); +cvar_t cl_lerp_maxinterval = CVARD("cl_lerp_maxinterval", "0.3", "Maximum interval between keyframes, in seconds. Larger values can result in entities drifting very slowly when they move sporadically."); cvar_t cl_lerp_players = CVARD("cl_lerp_players", "1", "Set this to make other players smoother, though it may increase effective latency. Affects only QuakeWorld."); cvar_t cl_predict_players = CVARD("cl_predict_players", "1", "Clear this cvar to see ents exactly how they are on the server."); cvar_t cl_predict_players_frac = CVARD("cl_predict_players_frac", "0.9", "How much of other players to predict. Values less than 1 will help minimize overruns."); @@ -4455,6 +4456,7 @@ void CL_Init (void) Cvar_Register (&rcon_password, cl_controlgroup); Cvar_Register (&rcon_address, cl_controlgroup); + Cvar_Register (&cl_lerp_maxinterval, cl_controlgroup); Cvar_Register (&cl_lerp_players, cl_controlgroup); Cvar_Register (&cl_predict_players, cl_predictiongroup); Cvar_Register (&cl_predict_players_frac, cl_predictiongroup); diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 8852038d..eb4c3296 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -1066,6 +1066,16 @@ void FPS_Preset_f (void) return; } + if (!stricmp("dp", arg)) + { + Cbuf_InsertText( + "set sv_listen_dp 1\n" + "set sv_bigcoords 1\n" + "echo you may need to restart the map\n" + , RESTRICT_LOCAL, false); + return; + } + if (!stricmp("tenebrae", arg)) { //for the luls. combine with the tenebrae mod for maximum effect. Cbuf_InsertText( diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 77a842d5..b014b8a3 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -2996,7 +2996,7 @@ void R_SetFrustum (float projmat[16], float viewmat[16]) //do far plane //fog will logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500 - if (r_refdef.globalfog.density && r_fog_cullentities.ival) + if (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && r_fog_cullentities.ival) { float culldist; float fog; diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 79a24b93..3d0c94f2 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -850,7 +850,7 @@ char *Sys_ConsoleInput(void) { #if 1 static char text[256]; - int len; + char *nl; #ifdef SUBSERVERS if (SSV_IsSubServer()) @@ -865,11 +865,10 @@ char *Sys_ConsoleInput(void) // if (!qrenderer) { - len = read (STDIN_FILENO, text, sizeof(text)); - if (len < 1) + if (!fgets(text, sizeof(text), stdin)) return NULL; - - text[len-1] = 0; // rip off the /n and terminate + nl = strchr(text, '\n'); + *nl = 0; //Con_Printf("console input: %s\n", text); @@ -937,6 +936,22 @@ int main (int c, const char **v) } #endif + if (COM_CheckParm("-qcdebug")) + { + isPlugin = 3; + nostdout = true; //only spew debugging messages. + } + else + { + isPlugin = !!COM_CheckParm("-plugin"); + if (isPlugin) + { + printf("status Starting up!\n"); + fflush(stdout); + nostdout = true; + } + } + parms.basedir = realpath(".", NULL); memset(bindir, 0, sizeof(bindir)); //readlink does NOT null terminate, apparently. #ifdef __linux__ @@ -944,7 +959,7 @@ int main (int c, const char **v) if (readlink("/proc/self/exe", bindir, sizeof(bindir)-1) > 0) { *COM_SkipPath(bindir) = 0; - printf("Binary is located at \"%s\"\n", bindir); + Sys_Printf("Binary is located at \"%s\"\n", bindir); parms.binarydir = bindir; } /*#elif defined(__bsd__) @@ -952,21 +967,13 @@ int main (int c, const char **v) if (readlink("/proc/self/file", bindir, sizeof(bindir)-1) > 0) { *COM_SkipPath(bindir) = 0; - printf("Binary is located at "%s"\n", bindir); + Sys_Printf("Binary is located at "%s"\n", bindir); parms.binarydir = bindir; } */ #endif TL_InitLanguages(parms.binarydir); - isPlugin = !!COM_CheckParm("-plugin"); - if (isPlugin) - { - printf("status Starting up!\n"); - fflush(stdout); - nostdout = true; - } - noconinput = COM_CheckParm("-noconinput"); #ifndef __DJGPP__ diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index d4e5c931..2444e458 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -17,6 +17,7 @@ static char *cvargroup_progs = "Progs variables"; cvar_t utf8_enable = CVARD("utf8_enable", "0", "When 1, changes the qc builtins to act upon codepoints instead of bytes. Do not use unless com_parseutf8 is also set."); cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods."); cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods. It is better for mods to use FL_FINDABLE_NONSOLID instead."); +cvar_t sv_gameplayfix_droptofloorstartsolid = CVARD("sv_gameplayfix_droptofloorstartsolid", "0", "When droptofloor fails, this causes a second attemp, but with traceline instead."); cvar_t dpcompat_findradiusarealinks = CVARD("dpcompat_findradiusarealinks", "0", "Use the world collision info to accelerate findradius instead of looping through every single entity. May actually be slower for large radiuses, or fail to find entities which have not been linked properly with setorigin."); #ifndef NOLEGACY cvar_t dpcompat_strcat_limit = CVARD("dpcompat_strcat_limit", "", "When set, cripples strcat (and related function) string lengths to the value specified.\nSet to 16383 to replicate DP's limit, otherwise leave as 0 to avoid limits."); @@ -73,6 +74,7 @@ void PF_Common_RegisterCvars(void) Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); Cvar_Register (&sv_gameplayfix_nolinknonsolid, cvargroup_progs); + Cvar_Register (&sv_gameplayfix_droptofloorstartsolid, cvargroup_progs); Cvar_Register (&dpcompat_findradiusarealinks, cvargroup_progs); #ifndef NOLEGACY Cvar_Register (&dpcompat_strcat_limit, cvargroup_progs); @@ -187,7 +189,6 @@ static int debuggerstacky; int QCLibEditor(pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *error, pbool fatal); void QCLoadBreakpoints(const char *vmname, const char *progsname) { //this asks the gui to reapply any active breakpoints and waits for them so that any spawn functions can be breakpointed properly. -#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX) extern int isPlugin; if (isPlugin >= 2) { @@ -197,14 +198,20 @@ void QCLoadBreakpoints(const char *vmname, const char *progsname) Sys_SendKeyEvents(); #endif debuggerresume = -1; - printf("qcreloaded \"%s\" \"%s\"\n", vmname, progsname); + fprintf(stdout, "qcreloaded \"%s\" \"%s\"\n", vmname, progsname); fflush(stdout); +#ifdef _WIN32 #ifndef SERVERONLY INS_UpdateGrabs(false, false); +#endif #endif while(debuggerresume == -1 && !wantquit) { +#ifdef _WIN32 Sleep(10); +#else + usleep(10*1000); +#endif #ifdef SERVERONLY SV_GetConsoleCommands(); #else @@ -212,7 +219,6 @@ void QCLoadBreakpoints(const char *vmname, const char *progsname) #endif } } -#endif } extern cvar_t pr_sourcedir; pubprogfuncs_t *debuggerinstance; @@ -373,7 +379,7 @@ qboolean QCExternalDebuggerCommand(char *text) int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *reason, pbool fatal) { -#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX) +//#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX) if (isPlugin >= 2) { if (wantquit) @@ -395,8 +401,10 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int #endif debuggerresume = -1; debuggerresumeline = *line; +#ifdef _WIN32 if (debuggerwnd) SetForegroundWindow((HWND)debuggerwnd); +#endif if (reason) { char tmpbuffer[8192]; @@ -415,18 +423,28 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int } while(debuggerresume == -1 && !wantquit) { +#ifdef _WIN32 Sleep(10); +#else + usleep(10*1000); +#endif SV_GetConsoleCommands(); } #else +#ifdef _WIN32 INS_UpdateGrabs(false, false); +#endif if (reason) Con_Footerf(NULL, false, "^bDebugging: %s", reason); else Con_Footerf(NULL, false, "^bDebugging"); while(debuggerresume == -1 && !wantquit) { +#ifdef _WIN32 Sleep(10); +#else + usleep(10*1000); +#endif Sys_SendKeyEvents(); if (qrenderer) @@ -453,7 +471,7 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int return DEBUG_TRACE_ABORT; return debuggerresume; } -#endif +//#endif #ifdef TEXTEDITOR return QCLibEditor(prinst, filename, line, statement, reason, fatal); @@ -5290,7 +5308,25 @@ void QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_g VectorCopy (ent->v->origin, start); trace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent); - if (trace.fraction == 1 || trace.allsolid) + if (trace.allsolid && sv_gameplayfix_droptofloorstartsolid.ival && gravitydir[2] == -1) + { + //try again but with a traceline, something is better than nothing. + vec3_t offset; + VectorAvg(ent->v->maxs, ent->v->mins, offset); + offset[2] = ent->v->mins[2]; + VectorAdd(start, offset, start); + VectorAdd(end, offset, end); + trace = World_Move (world, start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent); + if (trace.fraction < 1) + { + VectorSubtract (trace.endpos, offset, ent->v->origin); + World_LinkEdict (world, ent, false); + ent->v->flags = (int)ent->v->flags | FL_ONGROUND; + ent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent); + G_FLOAT(OFS_RETURN) = 1; + } + } + else if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; else { diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index b50d8ae2..a301e27b 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -3030,13 +3030,13 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) Terr_Brush_Draw(hm, batches, e); - if (r_refdef.globalfog.density || r_refdef.maxdist>0) + if ((r_refdef.globalfog.density&&r_refdef.globalfog.alpha>=1) || r_refdef.maxdist>0) { float culldist; extern cvar_t r_fog_exp2; - if (r_refdef.globalfog.density) - { + if (r_refdef.globalfog.density&&r_refdef.globalfog.alpha>=1) + { //fogalpha<1 means you can always see through it, so don't cull when its invisible. //figure out the eyespace distance required to reach that fog value culldist = log(0.5/255.0f); if (r_fog_exp2.ival) diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index ebd161d6..260cadbf 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -215,7 +215,7 @@ void R_GenerateFlashblendTexture(void) pixels[y][x][3] = 255; } } - R_LoadReplacementTexture("***flashblend***", NULL, 0, pixels, 32, 32, TF_RGBA32); + R_LoadReplacementTexture("***flashblend***", NULL, IF_LINEAR, pixels, 32, 32, TF_RGBA32); } void R_InitFlashblends(void) { diff --git a/engine/qclib/cmdlib.h b/engine/qclib/cmdlib.h index 9faeb5ea..98f452ae 100644 --- a/engine/qclib/cmdlib.h +++ b/engine/qclib/cmdlib.h @@ -28,7 +28,7 @@ // set these before calling CheckParm extern int myargc; -extern char **myargv; +extern const char **myargv; //char *strupr (char *in); //char *strlower (char *in); diff --git a/engine/qclib/comprout.c b/engine/qclib/comprout.c index 57e47d57..8481c097 100644 --- a/engine/qclib/comprout.c +++ b/engine/qclib/comprout.c @@ -77,12 +77,12 @@ pbool PreCompile(void) return !!qcchunk; } -pbool QCC_main (int argc, char **argv); +pbool QCC_main (int argc, const char **argv); void QCC_FinishCompile(void); -int comp_nump;char **comp_parms; +int comp_nump;const char **comp_parms; //void Editor(char *fname, int line, int numparms, char **compileparms); -pbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, char **parms) +pbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, const char **parms) { comp_nump = nump; comp_parms = parms; @@ -113,7 +113,7 @@ pbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, char **pa return true; } -int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, char **parms) +int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms) { comp_nump = nump; comp_parms = parms; @@ -158,7 +158,7 @@ pbool CompileFile(progfuncs_t *progfuncs, const char *filename) #else char srcfile[32]; char newname[32]; - static char *p[5]; + static const char *p[5]; int parms; char *s, *s2; diff --git a/engine/qclib/decomp.c b/engine/qclib/decomp.c index 866b878d..35e6f65a 100644 --- a/engine/qclib/decomp.c +++ b/engine/qclib/decomp.c @@ -172,6 +172,13 @@ pbool IsConstant(QCC_ddef_t *def) return true; } +const char *qcstring(int str) +{ + if ((unsigned)str >= strofs) + return ""; + return strings+str; +} + char *type_name (QCC_ddef_t *def) { QCC_ddef_t *j; @@ -509,10 +516,10 @@ static struct { {108, "showpicent", NULL, {NULL}, "void(string slot, entity player)"}, {109, "hidepicent", NULL, {NULL}, "void(string slot, entity player)"}, - {110, "fopen", &type_float, {&type_string,&type_float}, "filestream(string filename, float mode, optional float mmapminsize)"}, - {111, "fclose", NULL, {&type_float}, "void(filestream fhandle)"}, - {112, "fgets", &type_string, {&type_float,&type_string}, "string(filestream fhandle)"}, - {113, "fputs", NULL, {&type_float,&type_string}, "void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)"}, + {110, "fopen", &type_float, {&type_string,&type_float}, "float(string filename, float mode, optional float mmapminsize)"}, + {111, "fclose", NULL, {&type_float}, "void(float fhandle)"}, + {112, "fgets", &type_string, {&type_float,&type_string}, "string(float fhandle)"}, + {113, "fputs", NULL, {&type_float,&type_string}, "void(float fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)"}, {114, "strlen", &type_float, {&type_string}, "float(string s)"}, {115, "strcat", &type_string, {&type_string,&type_string}, "string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)"}, {116, "substring", &type_string, {&type_string,&type_float,&type_float}, "string(string s, float start, float length)"}, @@ -1254,8 +1261,9 @@ char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t) if (def) { + const char *defname = qcstring(def->s_name); - if (!strcmp(strings + def->s_name, "IMMEDIATE") || !strcmp(strings + def->s_name, ".imm") || !def->s_name) + if (!strcmp(defname, "IMMEDIATE") || !strcmp(defname, ".imm") || !def->s_name) { etype_t ty; if (!req_t) @@ -1270,7 +1278,7 @@ char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t) } else { - if (!strings[def->s_name]) + if (!*defname) { char line[16]; char *buf; diff --git a/engine/qclib/gui.h b/engine/qclib/gui.h index 8a2a1d85..aa8734b7 100644 --- a/engine/qclib/gui.h +++ b/engine/qclib/gui.h @@ -1,23 +1,25 @@ -void GoToDefinition(char *name); -int Grep(char *filename, char *string); +void GoToDefinition(const char *name); +int Grep(const char *filename, const char *string); void EditFile(const char *name, int line, pbool setcontrol); void GUI_SetDefaultOpts(void); -int GUI_BuildParms(char *args, char **argv, pbool quick); +int GUI_BuildParms(const char *args, const char **argv, pbool quick); //unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz); int QCC_RawFileSize (const char *fname); pbool QCC_WriteFile (const char *name, void *data, int len); -void GUI_DialogPrint(char *title, char *text); +void GUI_DialogPrint(const char *title, const char *text); void *GUIReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile); int GUIFileSize(const char *fname); -int GUI_ParseCommandLine(char *args, pbool keepsrcanddir); //0=gui, 1=commandline +int GUI_ParseCommandLine(const char *args, pbool keepsrcanddir); //0=gui, 1=commandline void GUI_SaveConfig(void); void GUI_RevealOptions(void); int GUIprintf(const char *msg, ...); +pbool GenBuiltinsList(char *buffer, int buffersize); + extern char parameters[16384]; extern char progssrcname[256]; diff --git a/engine/qclib/packager.c b/engine/qclib/packager.c index 5e9c6159..e710a09f 100644 --- a/engine/qclib/packager.c +++ b/engine/qclib/packager.c @@ -1015,7 +1015,6 @@ static void PKG_WritePackageData(struct pkgctx_s *ctx, struct output_s *out, uns #define misint64(ptr,ofs,data) do{misint((ptr),(ofs),(data));misint((ptr),(ofs)+4,((quint64_t)(data))>>32);}while(0) qofs_t num=0; pbool pak = false; - int startofs = 0; struct file_s *f; char centralheader[46+sizeof(f->write.name)]; @@ -1291,6 +1290,7 @@ static void PKG_WritePackageData(struct pkgctx_s *ctx, struct output_s *out, uns fclose(outf); } +/* #include static time_t PKG_GetFileTime(const char *filename) { @@ -1298,6 +1298,7 @@ static time_t PKG_GetFileTime(const char *filename) if (stat(filename, &s) != -1) return s.st_mtime; } +*/ static void PKG_ReadPackContents(struct pkgctx_s *ctx, struct oldpack_s *old) { diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 4eff5fd9..e15149d3 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -289,7 +289,7 @@ typedef struct edictrun_s } edictrun_t; -int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, char **parms); +int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms); int PDECL Comp_Continue(pubprogfuncs_t *progfuncs); pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *key); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 5c4a0f17..6ba015ee 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -114,11 +114,11 @@ struct pubprogfuncs_s func_t (PDECL *FindFunction) (pubprogfuncs_t *prinst, const char *funcname, progsnum_t num); - int (PDECL *StartCompile) (pubprogfuncs_t *prinst, int argv, char **argc); //1 if can compile, 0 if failed to compile + int (PDECL *StartCompile) (pubprogfuncs_t *prinst, int argv, const char **argc); //1 if can compile, 0 if failed to compile int (PDECL *ContinueCompile) (pubprogfuncs_t *prinst); //2 if finished, 1 if more to go, 0 if failed - char *(PDECL *filefromprogs) (pubprogfuncs_t *prinst, progsnum_t prnum, char *fname, size_t *size, char *buffer); //reveals encoded/added files from already loaded progs - char *(PDECL *filefromnewprogs) (pubprogfuncs_t *prinst, char *prname, char *fname, size_t *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere + char *(PDECL *filefromprogs) (pubprogfuncs_t *prinst, progsnum_t prnum, const char *fname, size_t *size, char *buffer); //reveals encoded/added files from already loaded progs + char *(PDECL *filefromnewprogs) (pubprogfuncs_t *prinst, const char *prname, const char *fname, size_t *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere void (PDECL *ED_Print) (pubprogfuncs_t *prinst, struct edict_s *ed); char *(PDECL *save_ents) (pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, int mode); //dump the entire progs info into one big self allocated string diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 5b11ed59..ef5533bb 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -694,7 +694,7 @@ extern int optres_locals_overlapping; extern int optres_logicops; extern int optres_inlines; -pbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, char **parms); +pbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, const char **parms); void QCC_PR_PrintStatement (QCC_statement_t *s); @@ -711,7 +711,6 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype); QCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs); char *QCC_PR_ParseName (void); -CompilerConstant_t *QCC_PR_DefineName(char *name); struct QCC_typeparam_s *QCC_PR_FindStructMember(QCC_type_t *t, const char *membername, unsigned int *out_ofs); QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); @@ -1054,7 +1053,7 @@ void QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags); void QCC_PR_FinaliseFunctions(void); -pbool QCC_main (int argc, char **argv); //as part of the quake engine +pbool QCC_main (int argc, const char **argv); //as part of the quake engine void QCC_ContinueCompile(void); void PostCompile(void); pbool PreCompile(void); @@ -1195,6 +1194,7 @@ static void inline QCC_PR_Expect (const char *string) } #endif +CompilerConstant_t *QCC_PR_DefineName(const char *name); void editbadfile(const char *fname, int line); char *TypeName(QCC_type_t *type, char *buffer, int buffersize); void QCC_PR_AddIncludePath(const char *newinc); @@ -1210,10 +1210,12 @@ extern void (*pHash_RemoveData)(hashtable_t *table, const char *name, void *data //when originally running from a .dat, we load up all the functions and work from those rather than actual files. //(these get re-written into the resulting .dat) typedef struct qcc_cachedsourcefile_s vfile_t; +void QCC_CloseAllVFiles(void); vfile_t *QCC_FindVFile(const char *name); vfile_t *QCC_AddVFile(const char *name, void *data, size_t size); void QCC_CatVFile(vfile_t *, const char *fmt, ...); void QCC_InsertVFile(vfile_t *, size_t pos, const char *fmt, ...); +char *ReadProgsCopyright(char *buf, size_t bufsize); //void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile); diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index c1808625..c7eea877 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -14,7 +14,7 @@ extern jmp_buf qcccompileerror; // set these before calling CheckParm int myargc; -char **myargv; +const char **myargv; char qcc_token[1024]; int qcc_eof; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 0f301fae..4f532de3 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -12108,7 +12108,7 @@ static void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) { if (statements[last-1].op != OP_GOTO || (signed)statements[last-1].a.ofs > 0) { - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + QCC_PR_Warning(WARN_MISSINGRETURN, s_filen, statements[last].linenum, "%s: not all control paths return a value", pr_scope->name ); return; } } diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 7edb83bc..551ca0cf 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -8,11 +8,11 @@ #define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2)) //saves about 2-6 out of 120 - expansion of idea from fastqcc void QCC_PR_PreProcessor_Define(pbool append); -pbool QCC_PR_UndefineName(char *name); -char *QCC_PR_CheckCompConstString(char *def); -CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); +pbool QCC_PR_UndefineName(const char *name); +const char *QCC_PR_CheckCompConstString(const char *def); +CompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def); int QCC_PR_CheckCompConst(void); -pbool QCC_Include(char *filename); +pbool QCC_Include(const char *filename); void QCC_FreeDef(QCC_def_t *def); #define MAXINCLUDEDIRS 8 @@ -1579,7 +1579,8 @@ void QCC_PR_LexString (void) unsigned int c, t; int bytecount; int len = 0; - char *end, *cnst; + char *end; + const char *cnst; int raw; char rawdelim[64]; int stringtype; @@ -2833,7 +2834,7 @@ static void QCC_PR_LexGrab (void) //=========================== //compiler constants - dmw -pbool QCC_PR_UndefineName(char *name) +pbool QCC_PR_UndefineName(const char *name) { // int a; CompilerConstant_t *c; @@ -2848,7 +2849,7 @@ pbool QCC_PR_UndefineName(char *name) return true; } -CompilerConstant_t *QCC_PR_DefineName(char *name) +CompilerConstant_t *QCC_PR_DefineName(const char *name) { int i; CompilerConstant_t *cnst; @@ -3653,9 +3654,9 @@ int QCC_PR_CheckCompConst(void) return false; } -char *QCC_PR_CheckCompConstString(char *def) +const char *QCC_PR_CheckCompConstString(const char *def) { - char *s; + const char *s; CompilerConstant_t *c; @@ -3669,7 +3670,7 @@ char *QCC_PR_CheckCompConstString(char *def) return def; } -CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def) +CompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def) { CompilerConstant_t *c = pHash_Get(&compconstantstable, def); return c; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index e6b5d82e..1f9d9f9e 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -22,7 +22,6 @@ void OptionsDialog(void); static void GUI_CreateInstaller_Windows(void); static void GUI_CreateInstaller_Android(void); -pbool GenBuiltinsList(char *buffer, int buffersize); static void SetProgsSrcFileAndPath(char *filename); static void CreateOutputWindow(pbool doannoates); void AddSourceFile(const char *parentsrc, const char *filename); @@ -355,8 +354,8 @@ static pbool QCC_RegSetValue(HKEY base, char *keyname, char *valuename, int type #undef Sys_Error void Sys_Error(const char *text, ...); -pbool qcc_vfiles_changed; -static vfile_t *qcc_vfiles; +extern pbool qcc_vfiles_changed; +extern vfile_t *qcc_vfiles; HWND mainwindow; HINSTANCE ghInstance; static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) ; @@ -451,96 +450,6 @@ void QCC_SaveVFiles(void) } } } -void QCC_CloseAllVFiles(void) -{ - vfile_t *f; - - while(qcc_vfiles) - { - f = qcc_vfiles; - qcc_vfiles = f->next; - - free(f->file); - free(f); - } - qcc_vfiles_changed = false; -} -vfile_t *QCC_FindVFile(const char *name) -{ - vfile_t *f; - for (f = qcc_vfiles; f; f = f->next) - { - if (!strcmp(f->filename, name)) - return f; - } - //give it another go, for case - for (f = qcc_vfiles; f; f = f->next) - { - if (!QC_strcasecmp(f->filename, name)) - return f; - } - return NULL; -} -vfile_t *QCC_AddVFile(const char *name, void *data, size_t size) -{ - vfile_t *f = QCC_FindVFile(name); - if (!f) - { - f = malloc(sizeof(vfile_t) + strlen(name)); - f->next = qcc_vfiles; - strcpy(f->filename, name); - qcc_vfiles = f; - } - else - free(f->file); - f->file = malloc(size); - f->type = FT_CODE; - memcpy(f->file, data, size); - f->size = f->bufsize = size; - - qcc_vfiles_changed = true; - return f; -} -void QCC_CatVFile(vfile_t *f, const char *fmt, ...) -{ - va_list argptr; - char msg[8192]; - size_t n; - - va_start (argptr,fmt); - QC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr); - va_end (argptr); - - n = strlen(msg); - if (f->size+n > f->bufsize) - { - size_t msize = f->bufsize + n + 8192; - f->file = realloc(f->file, msize); - f->bufsize = msize; - } - memcpy((char*)f->file+f->size, msg, n); - f->size += n; -} -void QCC_InsertVFile(vfile_t *f, size_t pos, const char *fmt, ...) -{ - va_list argptr; - char msg[8192]; - size_t n; - va_start (argptr,fmt); - QC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr); - va_end (argptr); - - n = strlen(msg); - if (f->size+n > f->bufsize) - { - size_t msize = f->bufsize + n + 8192; - f->file = realloc(f->file, msize); - f->bufsize = msize; - } - memmove((char*)f->file+pos+n, (char*)f->file+pos, f->size-pos); - f->size += n; - memcpy((char*)f->file+pos, msg, n); -} void QCC_EnumerateFilesResult(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize) { @@ -1144,7 +1053,7 @@ enum -void GUI_DialogPrint(char *title, char *text) +void GUI_DialogPrint(const char *title, const char *text) { MessageBox(mainwindow, text, title, 0); } @@ -2407,45 +2316,6 @@ pbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize) return usedbuffer>0; } -pbool GenBuiltinsList(char *buffer, int buffersize) -{ - QCC_def_t *def; - int usedbuffer = 0; - int l; - int fno; - for (fno = 0; fno < sourcefilesnumdefs; fno++) - { - for (def = sourcefilesdefs[fno]; def; def = def->next) - { - if (def->scope) - continue; //ignore locals, because we don't know where we are, and they're probably irrelevent. - - //if its a builtin function... - if (def->type->type == ev_function && def->symboldata->function && functions[def->symboldata->function].code<0) - ; - else if (def->filen && strstr(def->filen, "extensions")) - ; - else - continue; - - //but ignore it if its one of those special things that you're not meant to know about. - if (strcmp(def->name, "IMMEDIATE") && !strchr(def->name, ':') && !strchr(def->name, '.') && !strchr(def->name, '*') && !strchr(def->name, '[')) - { - l = strlen(def->name); - if (l && usedbuffer+2+l < buffersize) - { - if (usedbuffer) - buffer[usedbuffer++] = ' '; - memcpy(buffer+usedbuffer, def->name, l); - usedbuffer += l; - } - } - } - } - buffer[usedbuffer] = 0; - return usedbuffer>0; -} - editor_t *tooltip_editor = NULL; char tooltip_variable[256]; char tooltip_type[256]; @@ -4673,6 +4543,7 @@ void GUI_CreateInstaller_Android(void) free(mandata); } +#ifdef AVAIL_PNGLIB //size info that microsoft recommends static const struct { @@ -4692,6 +4563,8 @@ static const struct // {32, 32, 1}, {256, 256, 32} //vista! }; +#endif + //dates back to 16bit windows. bah. #pragma pack(push) #pragma pack(2) @@ -6885,7 +6758,7 @@ void compilecb(void) void Sys_Error(const char *text, ...); void RunCompiler(char *args, pbool quick) { - char *argv[128]; + const char *argv[128]; int argc; progexterns_t ext; progfuncs_t funcs; diff --git a/engine/qclib/qccguiqt.cpp b/engine/qclib/qccguiqt.cpp new file mode 100644 index 00000000..6ba0dfb6 --- /dev/null +++ b/engine/qclib/qccguiqt.cpp @@ -0,0 +1,1914 @@ +/*Copyright Spoike, license is GPLv2+ */ + +/*Todo (in no particular order): + tooltips for inspecting variables/types. + variables/watch list. + calltips for argument info + autocompletion calltips, for people who can't remember function names + ctrl+f/f3 stuff + options window + initial open project prompt + shpuld's styling + decompiler output saving + right-click popup + goto-def + grep-for + toggle-breakpoint + set-next + bracket/brace highlights + autoindentation on enter, etc + different displays for non-text files? + utf-16? mneh, who gives a shit + give focus back to the engine on resume +*/ + + +#ifdef __PIC__ + #undef __PIE__ //QT is being annoying. +#endif +#include +#include +#include +#include +#include + +extern "C" +{ +#include "qcc.h" +#include "gui.h" + +extern pbool fl_nondfltopts; +extern pbool fl_hexen2; +extern pbool fl_ftetarg; +extern pbool fl_compileonstart; +extern pbool fl_showall; +extern pbool fl_log; +extern pbool fl_extramargins; +extern int fl_tabsize; + +extern char enginebinary[MAX_OSPATH]; +extern char enginebasedir[MAX_OSPATH]; +extern char enginecommandline[8192]; +}; +static char *cmdlineargs; + +#undef NULL +#define NULL nullptr + +#undef Sys_Error + +//c++ sucks and just pisses me off with its lack of support for void* +template inline T cpprealloc(T p, size_t s) {return static_cast(realloc(static_cast(p),s));}; +#define STRINGIFY2(s) #s +#define STRINGIFY(s) STRINGIFY2(s) + + +static void DebuggerStop(void); +static bool DebuggerSendCommand(const char *msg, ...); +static void DebuggerStart(void); + + +void Sys_Error(const char *text, ...) +{ + va_list argptr; + static char msg[2048]; + + va_start (argptr,text); + QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); + va_end (argptr); + + QCC_Error(ERR_INTERNAL, "%s", msg); +} + +void RunCompiler(const char *args, pbool quick); +static void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size) +{ + size_t len; + unsigned char *buffer; + + vfile_t *v = QCC_FindVFile(fname); + if (v) + { + len = v->size; + if (buf_get) + buffer = buf_get(buf_ctx, len+1); + else + buffer = static_cast(malloc(len+1)); + if (!buffer) + return NULL; + buffer[len] = 0; + if (len > v->size) + len = v->size; + memcpy(buffer, v->file, len); + if (out_size) + *out_size = len; + return buffer; + } + + auto f = fopen(fname, "rb"); + if (!f) + { + if (out_size) + *out_size = 0; + return nullptr; + } + + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + if (buf_get) + buffer = buf_get(buf_ctx, len+1); + else + buffer = static_cast(malloc(len+1)); + buffer[len] = 0; + if (len != fread(buffer, 1, len, f)) + { + if (!buf_get) + free(buffer); + buffer = nullptr; + } + fclose(f); + + if (out_size) + *out_size = len; + return buffer; +} +static int PDECL QCC_FileSize (const char *fname) +{ + vfile_t *v = QCC_FindVFile(fname); + if (v) + return v->size; + + long length; + auto f = fopen(fname, "rb"); + if (!f) + return -1; + fseek(f, 0, SEEK_END); + length = ftell(f); + fclose(f); + return length; +} +static int PDECL QCC_PopFileSize (const char *fname) +{ //populates the file list, as well as returning the size + extern int qcc_compileactive; + int len = QCC_FileSize(fname); + if (len >= 0 && qcc_compileactive) + { + AddSourceFile(compilingrootfile, fname); + } + return len; +} + +static int PDECL QCC_StatFile (const char *fname, struct stat *sbuf) +{ + vfile_t *v = QCC_FindVFile(fname); + if (v) + { + memset(sbuf, 0, sizeof(*sbuf)); + sbuf->st_size = v->size; + return 0; + } + return stat(fname, sbuf); +} + +pbool PDECL QCC_WriteFile (const char *name, void *data, int len) +{ + long length; + FILE *f; + + auto *ext = strrchr(name, '.'); + if (ext && !stricmp(ext, ".gz")) + { +#ifdef AVAIL_ZLIB + pbool okay = true; + char out[1024*8]; + + z_stream strm = { + data, + len, + 0, + + out, + sizeof(out), + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + + f = fopen(name, "wb"); + if (!f) + return false; + deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + while(okay && deflate(&strm, Z_FINISH) == Z_OK) + { + if (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f)) + okay = false; + strm.next_out = out; + strm.avail_out = sizeof(out); + } + if (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f)) + okay = false; + deflateEnd(&strm); + fclose(f); + if (!okay) + unlink(name); + return okay; +#else + return false; +#endif + } + + if (QCC_FindVFile(name)) + return !!QCC_AddVFile(name, data, len); + + f = fopen(name, "wb"); + if (!f) + return false; + length = fwrite(data, 1, len, f); + fclose(f); + + if (length != len) + return false; + + return true; +} + +//for the project's treeview to work, we need a subclass to provide the info to be displayed +class filelist : public QAbstractItemModel +{ +public: + struct filenode_s + { + filenode_s *parent = nullptr; + int numchildren; + filenode_s **children; + + char *name; + + ~filenode_s() + { + while(numchildren) + { + delete(children[--numchildren]); + } + free(children); + free(name); + } + } *root; + + filenode_s *getItem(const QModelIndex &idx) const + { + if (idx.isValid()) + return static_cast(idx.internalPointer()); + return root; + } + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const + { + const filenode_s *n = getItem(parent); + return n->numchildren; + } + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const + { + return 1; + } + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + const filenode_s *n = getItem(index); + switch(role) + { + case Qt::DisplayRole: + return QVariant(n->name); + } + return QVariant(); + } + virtual QModelIndex index(int row, int column, const QModelIndex &parent) const + { + filenode_s *n; + if (column) + return QModelIndex(); + n = getItem(parent); + if (row >= 0 && row < n->numchildren) + return createIndex(row, column, n->children[row]); + return QModelIndex(); + } + virtual QModelIndex parent(const QModelIndex &index) const + { + if (!index.isValid()) + return QModelIndex(); + + filenode_s *n = getItem(index); + filenode_s *p = n->parent; + + if (p == root) + return QModelIndex(); + else + { + int parentrow = 0; + if (n->parent) + while (n != n->parent->children[parentrow]) + parentrow++; + return createIndex(parentrow, 0, p); + } + } + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const + { + switch(role) + { + case Qt::DisplayRole: + return QVariant("Files"); + } + return QVariant(); + } + +public: + filelist() + { + root = new filenode_s(); + } + + void GrepAll(const char *search, filenode_s *n = nullptr) + { + if (!n) + { + GUIprintf(""); + GUIprintf("Grep for %s\n", search); + n = root; + } + + Grep(n->name, search); + for (int i = 0; i < n->numchildren; i++) + { + auto c = n->children[i]; + GrepAll(search, c); + } + } + + filenode_s *FindChild(filenode_s *p, const char *filename) + { + for (int i = 0; i < p->numchildren; i++) + { + auto c = p->children[i]; + if (!strcasecmp(c->name, filename)) + return c; + } + return nullptr; + } + void AddFile(const char *parentpath, const char *filename) + { + filenode_s *p = root, *c; + + if (!filename) + { + delete root; + root = new filenode_s(); + return; + } + + while (parentpath && *parentpath) + { + auto sl = strchr(parentpath, '/'); + + c = FindChild(p, parentpath); + if (!c) + break; + p = c; + + if (sl) + sl++; + parentpath = sl; + } + + while(!strncmp(filename, "./", 2)) + filename+=2; + + if (p->parent && FindChild(p->parent, filename) == p) + return; //matches its parent. probably the .src file itself + c = FindChild(p, filename); + if (c) + return; //already in there. + + c = new filenode_s(); + c->name = strdup(filename); + c->parent = p; + + p->children = cpprealloc(p->children, sizeof(*p->children)*(p->numchildren+1)); + p->children[p->numchildren] = c; + p->numchildren++; + } +}; + +class documentlist : public QAbstractListModel +{ + QsciScintilla *s; //this is the widget that we load our documents into + + int numdocuments; + struct document_s + { //these are swapped in/out of the scintilla widget + const char *fname; + const char *shortname; + time_t filemodifiedtime; + bool modified; + int cursorline; + int savefmt; //encoding to save as + QsciDocument doc; + QsciLexer *l; + } **docs, *curdoc; + + class docstacklock + { + struct document_s *oldval; + documentlist &dl; + public: + docstacklock(documentlist *ptr_, struct document_s *newval) : dl(*ptr_) + { //pick new stuff + oldval = dl.curdoc; + dl.curdoc = newval; + dl.s->setDocument(dl.curdoc->doc); + } + ~docstacklock() + { //restore state to how it used to be + if (!oldval) + return; + dl.curdoc = oldval; + dl.s->setDocument(dl.curdoc->doc); + } + }; + + document_s *getItem(const QModelIndex &idx) const + { + if (idx.isValid()) + return docs[idx.row()]; + return nullptr; + } + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const + { + return numdocuments; + } + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + const document_s *n = getItem(index); + if(n) + switch(role) + { + case Qt::DisplayRole: + if (n->modified) + return QVariant(QString::asprintf("%s*", n->fname)); + else + return QVariant(n->fname); + } + return QVariant(); + } + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const + { + switch(role) + { + case Qt::DisplayRole: + return QVariant("Files"); + } + return QVariant(); + } + void UpdateTitle(void); +public: + documentlist(QsciScintilla *editor) + { + s = editor; + numdocuments = 0; + docs = nullptr; + + connect(s, &QsciScintilla::cursorPositionChanged, + [=](int line, int index) + { + if (curdoc) + { + curdoc->cursorline = line+1; + UpdateTitle(); + } + }); + + connect(s, &QsciScintilla::modificationChanged, + [=](bool m) + { + if (curdoc) + { + curdoc->modified = m; + + for(int row = 0; row < numdocuments; row++) + { + if(docs[row] == curdoc) + { + auto i = index(row); + this->dataChanged(i, i); + } + } + } + }); + } + + void SetupScintilla(document_s *ed) + { +// ed->l = new QsciLexerCPP (s); + s->SendScintilla(QsciScintillaBase::SCI_STYLERESETDEFAULT); +// s->SendScintilla(QsciScintillaBase::SCI_STYLESETFONT, QsciScintillaBase::STYLE_DEFAULT, "Consolas"); + s->setFont(QFont(QString("Consolas"), 8)); + s->SendScintilla(QsciScintillaBase::SCI_STYLECLEARALL); + + s->SendScintilla(QsciScintillaBase::SCI_SETCODEPAGE, QsciScintillaBase::SC_CP_UTF8); + s->SendScintilla(QsciScintillaBase::SCI_SETLEXER, QsciScintillaBase::SCLEX_CPP); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Default, QColor(0x00, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLECLEARALL); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Comment, QColor(0x00, 0x80, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentLine, QColor(0x00, 0x80, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDoc, QColor(0x00, 0x80, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Number, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Keyword, QColor(0x00, 0x00, 0xFF)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::DoubleQuotedString, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::SingleQuotedString, QColor(0xA0, 0x10, 0x10)); +// s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UUID, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessor, QColor(0x00, 0x00, 0xFF)); +// s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Operator, QColor(0x00, 0x00, 0x00)); +// s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Identifier, QColor(0x00, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UnclosedString, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::VerbatimString, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Regex, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentLineDoc, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::KeywordSet2, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDocKeyword, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDocKeywordError, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::GlobalClass, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::RawString, QColor(0xA0, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::TripleQuotedVerbatimString, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::HashQuotedString, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessorComment, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessorCommentLineDoc, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UserLiteral, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::TaskMarker, QColor(0xA0, 0x10, 0x10)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::EscapeSequence, QColor(0xA0, 0x10, 0x10)); + + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciScintillaBase::STYLE_BRACELIGHT, QColor(0x00, 0x00, 0x3F)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, QsciScintillaBase::STYLE_BRACELIGHT, QColor(0xef, 0xaf, 0xaf)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETBOLD, QsciScintillaBase::STYLE_BRACELIGHT, true); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciScintillaBase::STYLE_BRACEBAD, QColor(0x3F, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, QsciScintillaBase::STYLE_BRACEBAD, QColor(0xff, 0xaf, 0xaf)); + + //SCE_C_WORD + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, static_cast(0), + "if else for do not while asm break case const continue " + "default enum enumflags extern " + "float goto __in __out __inout noref " + "nosave shared __state optional string " + "struct switch thinktime until loop " + "typedef union var " + "accessor get set inline " + "virtual nonvirtual class static nonstatic local return " + "string float vector void int integer __variant entity" + ); + + //SCE_C_WORD2 + { + char buffer[65536]; + GenBuiltinsList(buffer, sizeof(buffer)); + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 1, buffer); + } + //SCE_C_COMMENTDOCKEYWORDERROR + //SCE_C_GLOBALCLASS + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 3, + "" + ); +//preprocessor listing + { + char *deflist = QCC_PR_GetDefinesList(); + if (!deflist) + deflist = strdup(""); + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 4, deflist); + free(deflist); + } + //task markers (in comments only) + s->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS, 5, + "TODO FIXME BUG" + ); + + s->SendScintilla(QsciScintillaBase::SCI_USEPOPUP, QsciScintillaBase::SC_POPUP_NEVER); //so we can do right-click menus ourselves. + + s->SendScintilla(QsciScintillaBase::SCI_SETMOUSEDWELLTIME, 1000); + s->SendScintilla(QsciScintillaBase::SCI_AUTOCSETORDER, QsciScintillaBase::SC_ORDER_PERFORMSORT); + s->SendScintilla(QsciScintillaBase::SCI_AUTOCSETFILLUPS, nullptr, ".,[<>(*/+-=\t\n"); + + //Set up gui options. + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN, 0, fl_extramargins?40:0); //line numbers+folding + s->SendScintilla(QsciScintillaBase::SCI_SETTABWIDTH, fl_tabsize); //tab size + + //add margin for breakpoints + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINMASKN, 1, ~QsciScintillaBase::SC_MASK_FOLDERS); + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN, 1, 16); + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINSENSITIVEN, 1, true); + //give breakpoints a nice red circle. + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, 0, QsciScintillaBase::SC_MARK_CIRCLE); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, 0, QColor(0x7F, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, 0, QColor(0xFF, 0x00, 0x00)); + //give current line a yellow arrow + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, 1, QsciScintillaBase::SC_MARK_SHORTARROW); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, 1, QColor(0xFF, 0xFF, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, 1, QColor(0x7F, 0x7F, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, 2, QsciScintillaBase::SC_MARK_BACKGROUND); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, 2, QColor(0x00, 0x00, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, 2, QColor(0xFF, 0xFF, 0x00)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETALPHA, 2, 0x40); + + //add margin for folding + + s->SendScintilla(QsciScintillaBase::SCI_SETPROPERTY, "fold", "1"); + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN, 2, fl_extramargins?16:0); + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINMASKN, 2, QsciScintillaBase::SC_MASK_FOLDERS); + s->SendScintilla(QsciScintillaBase::SCI_SETMARGINSENSITIVEN, 2, true); + //stop the images from being stupid + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDEROPEN, QsciScintillaBase::SC_MARK_BOXMINUS); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDER, QsciScintillaBase::SC_MARK_BOXPLUS); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDERSUB, QsciScintillaBase::SC_MARK_VLINE); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDERTAIL, QsciScintillaBase::SC_MARK_LCORNERCURVE); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDEREND, QsciScintillaBase::SC_MARK_BOXPLUSCONNECTED); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDEROPENMID, QsciScintillaBase::SC_MARK_BOXMINUSCONNECTED); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE, QsciScintillaBase::SC_MARKNUM_FOLDERMIDTAIL, QsciScintillaBase::SC_MARK_TCORNERCURVE); + //and fuck with colours so that its visible. +#define FOLDBACK QColor(0x50, 0x50, 0x50) + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, QsciScintillaBase::SC_MARKNUM_FOLDER, QColor(0xFF, 0xFF, 0xFF)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDER, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, QsciScintillaBase::SC_MARKNUM_FOLDEROPEN, QColor(0xFF, 0xFF, 0xFF)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDEROPEN, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, QsciScintillaBase::SC_MARKNUM_FOLDEROPENMID, QColor(0xFF, 0xFF, 0xFF)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDEROPENMID, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDERSUB, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE, QsciScintillaBase::SC_MARKNUM_FOLDEREND, QColor(0xFF, 0xFF, 0xFF)); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDEREND, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDERTAIL, FOLDBACK); + s->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK, QsciScintillaBase::SC_MARKNUM_FOLDERMIDTAIL, FOLDBACK); + + //disable preprocessor tracking, because QC preprocessor is not specific to an individual file, and even if it was, includes would be messy. +// s->SendScintilla(QsciScintillaBase::SCI_SETPROPERTY, (WPARAM)"lexer.cpp.track.preprocessor", (LPARAM)"0"); + + for (int i = 0; i < 0x100; i++) + { + const char *lowtab[32] = {"QNUL",NULL,NULL,NULL,NULL,".",NULL,NULL,NULL,NULL,NULL,"#",NULL,">",".",".", + "[","]","0","1","2","3","4","5","6","7","8","9",".","<-","-","->"}; + const char *hightab[32] = {"(=","=","=)","=#=","White",".","Green","Red","Yellow","Blue",NULL,"Purple",NULL,">",".",".", + "[","]","0","1","2","3","4","5","6","7","8","9",".","<-","-","->"}; + char foo[4]; + char bar[4]; + unsigned char c = i&0xff; + foo[0] = i; //these are invalid encodings or control chars. + foo[1] = 0; + + if (c < 32) + { + if (lowtab[c]) + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, lowtab[c]); + } + else if (c >= (128|0) && c < (128|32)) + { + if (hightab[c-128]) + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, hightab[c-128]); + } + else if (c < 128) + continue; //don't do anything weird for ascii (other than control chars) + else + { + int b = 0; + bar[b++] = c&0x7f; + bar[b++] = 0; + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, bar); + } + } + + for (int i = 0xe000; i < 0xe100; i++) + { + const char *lowtab[32] = {"QNUL",NULL,NULL,NULL,NULL,".",NULL,NULL,NULL,NULL,NULL,"#",NULL,">",".",".", + "[","]","0","1","2","3","4","5","6","7","8","9",".","<-","-","->"}; + const char *hightab[32] = {"(=","=","=)","=#=","White",".","Green","Red","Yellow","Blue",NULL,"Purple",NULL,">",".",".", + "[","]","^0","^1","^2","^3","^4","^5","^6","^7","^8","^9",".","^<-","^-","^->"}; + char foo[4]; + char bar[4]; + unsigned char c = i&0xff; + foo[0] = ((i>>12) & 0xf) | 0xe0; + foo[1] = ((i>>6) & 0x3f) | 0x80; + foo[2] = ((i>>0) & 0x3f) | 0x80; + foo[3] = 0; + + if (c < 32) + { + if (lowtab[c]) + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, lowtab[c]); + } + else if (c >= (128|0) && c < (128|32)) + { + if (hightab[c-128]) + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, hightab[c-128]); + } + else + { + int b = 0; + if (c >= 128) + bar[b++] = '^'; + bar[b++] = c&0x7f; + bar[b++] = 0; + s->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION, foo, bar); + } + } + +/* auto f = fopen("scintilla.cfg", "rt"); + if (f) + { + char buf[256]; + while(fgets(buf, sizeof(buf)-1, f)) + { + int msg; + long lparam; + long wparam; + char *c; + buf[sizeof(buf)-1] = 0; + c = buf; + while(*c == ' ' || *c == '\t') + c++; + if (c[0] == '#') + continue; + if (c[0] == '/' && c[1] == '/') + continue; + if (c[0] == '\r' || c[0] == '\n' || !c[0]) + continue; + msg = strtoul(c, &c, 0); + while(*c == ' ' || *c == '\t') + c++; + if (*c == '\"') + { + c++; + wparam = c; + c = strrchr(c, '\"'); + if (c) + *c++ = 0; + } + else + wparam = strtoul(c, &c, 0); + while(*c == ' ' || *c == '\t') + c++; + if (*c == '\"') + { + c++; + lparam = c; + c = strrchr(c, '\"'); + if (c) + *c++ = 0; + } + else + lparam = strtoul(c, &c, 0); + + s->SendScintilla(QsciScintillaBase::msg, wparam, lparam); + } + if (!ftell(f)) + { + fclose(f); + f = fopen("scintilla.cfg", "wt"); + if (f) + { + int i; + int val; + for (i = 0; i < STYLE_LASTPREDEFINED; i++) + { + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETFORE, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETFORE, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETBACK, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETBACK, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETBOLD, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETBOLD, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETITALIC, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETITALIC, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETSIZE, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETSIZE, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETFONT, i, (LPARAM)buf); + fprintf(f, "%i\t%i\t\"%s\"\n", SCI_STYLESETFONT, i, buf); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETUNDERLINE, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETUNDERLINE, i, val); + val = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETCASE, i, 0); + fprintf(f, "%i\t%i\t%#x\n", SCI_STYLESETCASE, i, val); + } + fclose(f); + } + } + else + fclose(f); + } +*/ } + + bool CreateDocument(document_s *ed) + { + size_t flensz; + auto rawfile = QCC_ReadFile(ed->fname, nullptr, nullptr, &flensz); + if (!rawfile) + return false; + auto flen = flensz; + pbool dofree; + auto file = QCC_SanitizeCharSet(static_cast(rawfile), &flen, &dofree, &ed->savefmt); + struct stat sbuf; + QCC_StatFile(ed->fname, &sbuf); + ed->filemodifiedtime = sbuf.st_mtime; + + int endings = 0; + char *e, *stop; + for (e = file, stop=file+flen; e < stop; ) + { + if (*e == '\r') + { + e++; + if (*e == '\n') + { + e++; + endings |= 4; + } + else + endings |= 2; + } + else if (*e == '\n') + { + e++; + endings |= 1; + } + else + e++; + } + + curdoc = ed; + s->setDocument(ed->doc); + + switch(endings) + { + case 0: //new file with no endings, default to windows on windows. + case 4: //windows + s->setEolMode(QsciScintilla::EolMode::EolWindows); + s->setEolVisibility(false); + break; + case 1: //unix + s->setEolMode(QsciScintilla::EolMode::EolUnix); + s->setEolVisibility(false); + break; + case 2: //mac. traditionally qccs have never supported this. one of the mission packs has a \r in the middle of some single-line comment. + s->setEolMode(QsciScintilla::EolMode::EolMac); + s->setEolVisibility(false); + break; + default: //panic! everyone panic! + s->setEolMode(QsciScintilla::EolMode::EolUnix); + s->setEolVisibility(true); + break; + } + + s->setUtf8(ed->savefmt != UTF_ANSI); + s->setText(QString(file)); + + SetupScintilla(ed); + + s->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT); + ed->modified = false; + return true; + } + + void SwitchToDocument(document_s *ed) + { + struct stat sbuf; + QCC_StatFile(ed->fname, &sbuf); + if (ed->filemodifiedtime < sbuf.st_mtime) + { + CreateDocument(ed); + return; + } + + curdoc = ed; + s->setDocument(ed->doc); + } + + document_s *FindFile(const char *filename) + { + for (int i = 0; i < numdocuments; i++) + { + if (!strcasecmp(filename, docs[i]->fname)) + return docs[i]; + } + return nullptr; + } + + void *getFileData(document_s *d, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size) + { + docstacklock lock(this, d); + unsigned char *ret = NULL; + auto text = s->text().toUtf8(); + *out_size = text.length(); + if (buf_get) + ret = buf_get(buf_ctx, *out_size+1); + else + ret = static_cast(malloc(*out_size+1)); + memcpy(ret, text.data(), *out_size); + ret[*out_size] = 0; + return ret; + } + int getFileSize(document_s *d) + { + docstacklock lock(this,d); + int ret = -1; + auto text = s->text().toUtf8(); + ret = text.length(); + return ret; + } + + + bool saveDocument(document_s *d) + { + struct stat sbuf; + bool saved = false; + + //wordpad will corrupt any embedded quake chars if we force a bom, because it'll re-save using the wrong char encoding by default. + int bomlen = 0; + const char *bom = ""; + if (!d) + d = curdoc; + if (!d) + return false; + + if (d->savefmt == UTF32BE || d->savefmt == UTF32LE || d->savefmt == UTF16BE) + d->savefmt = UTF16LE; + + if (d->savefmt == UTF8_BOM) + { + bomlen = 3; + bom = "\xEF\xBB\xBF"; + } + else if (d->savefmt == UTF16BE) + { + bomlen = 2; + bom = "\xFE\xFF"; + } + else if (d->savefmt == UTF16LE) + { + bomlen = 2; + bom = "\xFF\xFE"; + } + else if (d->savefmt == UTF32BE) + { + bomlen = 4; + bom = "\x00\x00\xFE\xFF"; + } + else if (d->savefmt == UTF32LE) + { + bomlen = 4; + bom = "\xFF\xFE\x00\x00"; + } + + docstacklock lock(this, d); + + auto text = s->text().toUtf8(); + text.prepend(bom, bomlen); + + //because wordpad saves in ansi by default instead of the format the file was originally saved in, we HAVE to use ansi without + if (d->savefmt != UTF8_BOM && d->savefmt != UTF8_RAW) + { + /*int mchars; + char *mc; + int wchars = MultiByteToWideChar(CP_UTF8, 0, text.data(), text.length(), NULL, 0); + if (wchars) + { + wchar_t *wc = malloc(wchars * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text.data(), text.length(), wc, wchars); + + if (d->savefmt == UTF_ANSI) + { + mchars = WideCharToMultiByte(CP_ACP, 0, wc, wchars, NULL, 0, "", &failed); + if (mchars) + { + mc = malloc(mchars); + WideCharToMultiByte(CP_ACP, 0, wc, wchars, mc, mchars, "", &failed); + if (!failed) + { + saved = QCC_WriteFile(d->filename, mc, mchars)) + } + free(mc); + } + } + else + saved = QCC_WriteFile(d->filename, wc, wchars); + free(wc); + }*/ + } + else + saved = QCC_WriteFile(d->fname, text.data(), bomlen+text.length()); + if (!saved) + { + QMessageBox::critical(NULL, "Failure", "Save failed\nCheck path and ReadOnly flags"); + return false; + } + else + { + s->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT); + + /*now whatever is on disk should have the current time*/ + //d->modified = false; + QCC_StatFile(d->fname, &sbuf); + d->filemodifiedtime = sbuf.st_mtime; + + //remove the * in a silly way. + //d->oldline=~0; + //UpdateEditorTitle(d); + } + + return true; + } + + bool saveAll(void) + { + document_s *d; + struct stat sbuf; + for (int i = 0; i < numdocuments; i++) + { + d = docs[i]; + QCC_StatFile(d->fname, &sbuf); + if (d->modified) + { + if (d->filemodifiedtime != sbuf.st_mtime) + { + switch(QMessageBox::question(nullptr, "Modification conflict", QString::asprintf("%s is modified in both memory and on disk. Overwrite external modification? (saying no will reload from disk)", d->fname), QMessageBox::Save|QMessageBox::Reset|QMessageBox::Ignore, QMessageBox::Ignore)) + { + case QMessageBox::Save: + if (!saveDocument(d)) + QMessageBox::critical(nullptr, "Error", QString::asprintf("Unable to write %s, file was not saved", d->fname)); + break; + case QMessageBox::Reset: + CreateDocument(d); + break; + default: + case QMessageBox::Ignore: + break; /*compiling will use whatever is in memory*/ + } + } + else + { + /*not modified on disk, but modified in memory? try and save it, cos we might as well*/ + if (!saveDocument(d)) + QMessageBox::critical(nullptr, "Error", QString::asprintf("Unable to write %s, file was not saved", d->fname)); + } + } + else + { + /*modified on disk but not in memory? just reload it off disk*/ + if (d->filemodifiedtime != sbuf.st_mtime) + CreateDocument(d); + } + } + return true; + } + + void reapplyAllBreakpoints(void) + { + for (int i = 0; i < numdocuments; i++) + { + document_s *d = docs[i]; + int line = -1; + s->setDocument(d->doc); + for (;;) + { + line = s->SendScintilla(QsciScintillaBase::SCI_MARKERNEXT, line, 1); + if (line == -1) + break; //no more. + line++; + + DebuggerSendCommand("qcbreakpoint 1 \"%s\" %i\n", d->fname, line); + } + } + if (curdoc) + s->setDocument(curdoc->doc); + } + void toggleBreak(void) + { + if (!curdoc) + return; + int mode = !(s->SendScintilla(QsciScintillaBase::SCI_MARKERGET, curdoc->cursorline-1) & 1); + s->SendScintilla(mode?QsciScintillaBase::SCI_MARKERADD:QsciScintillaBase::SCI_MARKERDELETE, curdoc->cursorline-1); + + DebuggerSendCommand("qcbreakpoint %i \"%s\" %i\n", mode, curdoc->fname, curdoc->cursorline); + } + void setNext(void) + { + if (!curdoc) + return; + DebuggerSendCommand("qcjump \"%s\" %i\n", curdoc->fname, curdoc->cursorline); + } + + void EditFile(const char *filename, int linenum=-1, bool setcontrol=false); +}; + +template +class keyEnterReceiver : public QObject +{ + void(*cb)(T &ctx); + T ctx; +public: + keyEnterReceiver(void(*cb_)(T &ctx), T &ctx_) : cb(cb_), ctx(T(ctx_)) + { + } +protected: + bool eventFilter(QObject* obj, QEvent* event) + { + if (event->type()==QEvent::KeyPress) + { + QKeyEvent* key = static_cast(event); + if ( (key->key()==Qt::Key_Enter) || (key->key()==Qt::Key_Return) ) + cb(ctx); + else + return QObject::eventFilter(obj, event); + return true; + } + else + return QObject::eventFilter(obj, event); + return false; + } +}; + +class optionswindow : public QDialog +{ + QWidget *newlineedit(const char *initial, void(*changed)(const QString&)) + { + auto w = new QLineEdit(initial); + connect(w, &QLineEdit::textEdited, changed); + return w; + } + void SetOptsEnabled(QGridLayout *layout, int lev = -1) + { + for(int i = 0; i < layout->count(); i++) + { + auto w = static_cast(layout->itemAt(i)->widget()); + if (!w) + break; + auto id = w->property("optnum").toInt(); + if (lev >= 0 && lev <= 3) + w->setCheckState((lev >= optimisations[id].optimisationlevel)?Qt::Checked:Qt::Unchecked); + else if (lev == 4) + { + if (optimisations[id].flags & FLAG_KILLSDEBUGGERS) + w->setCheckState(Qt::Unchecked); + } + else if (lev == 5) + w->setCheckState((optimisations[id].flags&FLAG_ASDEFAULT)?Qt::Checked:Qt::Unchecked); + w->setEnabled(fl_nondfltopts); + } + } + +public: + optionswindow() + { + setModal(true); + + auto toplayout = new QHBoxLayout; + auto leftlayout = new QVBoxLayout; + auto rightlayout = new QVBoxLayout; + + { + auto layout = new QGridLayout; + for (int i = 0,n=0; optimisations[i].fullname; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + auto w = new QCheckBox(optimisations[i].fullname); + w->setProperty("optnum", i); + w->setCheckState((optimisations[i].flags & FLAG_SETINGUI)?Qt::Checked:Qt::Unchecked); + w->setEnabled(!fl_nondfltopts); + connect(w, &QCheckBox::stateChanged, [=](int v){if (v)optimisations[i].flags |= FLAG_SETINGUI;else optimisations[i].flags &= ~FLAG_SETINGUI;}); + layout->addWidget(w, n>>1, n&1); + n++; + } + SetOptsEnabled(layout, -1); + auto gb = new QGroupBox("Optimisations"); + auto opts = new QVBoxLayout; + opts->addLayout(layout); + auto optlevels = new QHBoxLayout; + for (int i=0; i<6; i++) + { + const char *buttons[] = {"O0", "O1", "O2", "O3", "Debug", "Default"}; + auto w = new QPushButton(buttons[i]); + if (i == 5) + { + w->setCheckable(true); + w->setChecked(fl_nondfltopts); + } + connect(w, &QPushButton::clicked, [=](bool checked) + { + if (i == 5) + fl_nondfltopts = !checked; + else + fl_nondfltopts = true; + SetOptsEnabled(layout, i); + }); + optlevels->addWidget(w); + } + opts->addLayout(optlevels); + gb->setLayout(opts); + leftlayout->addWidget(gb); + } + { + auto layout = new QFormLayout; + layout->addRow("Engine:", newlineedit(enginebinary, [](const QString&str){QC_strlcpy(enginebinary, str.toUtf8().data(), sizeof(enginebinary));})); + layout->addRow("Basedir:", newlineedit(enginebasedir, [](const QString&str){QC_strlcpy(enginebasedir, str.toUtf8().data(), sizeof(enginebasedir));})); + layout->addRow("Cmdline:", newlineedit(enginecommandline, [](const QString&str){QC_strlcpy(enginecommandline, str.toUtf8().data(), sizeof(enginecommandline));})); + leftlayout->addLayout(layout); + } + + { + auto layout = new QGridLayout; + int n = 0; + + auto w = new QCheckBox("HexenC"); + w->setStatusTip("Compile for hexen2.\nThis changes the opcodes slightly, the progs crc, and enables some additional keywords."); + w->setCheckState((fl_hexen2)?Qt::Checked:Qt::Unchecked); + connect(w, &QCheckBox::stateChanged, [=](int v){fl_hexen2=!!v;}); + layout->addWidget(w, n/3, n%3); + n++; + + w = new QCheckBox("Extended Instructions"); + w->setStatusTip("Enables the use of additional opcodes, which only FTE supports at this time.\nThis gives both smaller and faster code, as well as allowing pointers, ints, and other extensions not possible with the vanilla QCVM"); + w->setCheckState((fl_ftetarg)?Qt::Checked:Qt::Unchecked); + connect(w, &QCheckBox::stateChanged, [=](int v){fl_ftetarg=!!v;}); + layout->addWidget(w, n/3, n%3); + n++; + + for (int i = 0; compiler_flag[i].fullname; i++) + { + if (compiler_flag[i].flags & FLAG_HIDDENINGUI) + continue; + auto w = new QCheckBox(compiler_flag[i].fullname); + w->setCheckState((compiler_flag[i].flags & FLAG_SETINGUI)?Qt::Checked:Qt::Unchecked); + connect(w, &QCheckBox::stateChanged, [=](int v){if (v)compiler_flag[i].flags |= FLAG_SETINGUI;else compiler_flag[i].flags &= ~FLAG_SETINGUI;}); + layout->addWidget(w, n/3, n%3); + n++; + } + + auto gb = new QGroupBox("Compiler Flags"); + gb->setLayout(layout); + rightlayout->addWidget(gb); + + rightlayout->addWidget(new QTextEdit()); + } + toplayout->addLayout(leftlayout); + toplayout->addLayout(rightlayout); + + setLayout(toplayout); + } +}; + +static class guimainwindow : public QMainWindow +{ +public: + QsciScintilla s; + QSplitter leftrightsplit; + QSplitter logsplit; + QSplitter leftsplit; + QTreeView files_w; + QListView docs_w; + + QTextEdit log; + filelist files; + documentlist docs; + +private: + void CreateMenus(void) + { + auto prefs = new QAction(tr("&Preferences"), this); + prefs->setShortcuts(QKeySequence::Preferences); + prefs->setStatusTip(tr("Reconfigure stuff")); + connect(prefs, &QAction::triggered, [](){(new optionswindow())->show();}); + + auto grep = new QAction(tr("&Grep"), this); +// grep->setShortcuts(QKeySequence::Preferences); + grep->setStatusTip(tr("Search through all project files")); + connect(grep, &QAction::triggered, [=]() + { + struct grepargs_s + { + guimainwindow *mw; + QDialog *d; + QLineEdit *t; + } args = {this, new QDialog()}; + args.t = new QLineEdit(QString("")); + auto l = new QVBoxLayout; + l->addWidget(args.t); + args.d->setLayout(l); + args.d->setWindowTitle("FTEQCC Grep"); + args.t->installEventFilter(new keyEnterReceiver([](grepargs_s &ctx) + { + ctx.mw->files.GrepAll(ctx.t->text().toUtf8().data()); + ctx.d->done(0); + }, args)); + args.d->show(); + }); + + auto fileMenu = menuBar()->addMenu(tr("&File")); + fileMenu->addAction(prefs); + fileMenu->addAction(grep); + } + +public: + ~guimainwindow() + { //if we're dying, make sure there's no engine waiting for us + DebuggerStop(); + } + guimainwindow() : s(this), leftrightsplit(Qt::Horizontal, this), logsplit(Qt::Vertical, this), leftsplit(Qt::Vertical, this), files_w(this), docs_w(this), docs(&s) + { + setWindowTitle(QString("FTEQCC Gui")); + + s.setReadOnly(true); + + files_w.setModel(&files); + connect(&files_w, &QTreeView::clicked, + [=](const QModelIndex &index) + { + docs.EditFile(files.getItem(index)->name); + }); + + leftrightsplit.addWidget(&leftsplit); + leftrightsplit.addWidget(&logsplit); + leftsplit.addWidget(&files_w); + leftsplit.addWidget(&docs_w); + docs_w.setModel(&docs); + logsplit.addWidget(&s); + logsplit.addWidget(&log); + QList sizes; + sizes.append(64); + sizes.append(256); + leftrightsplit.setSizes(sizes); + QList sizes2; + sizes.append(1); + sizes.append(0); + logsplit.setSizes(sizes2); + setCentralWidget(&leftrightsplit); + + log.setReadOnly(true); + log.clear(); + + connect(&log, &QTextEdit::selectionChanged, + [=]() + { + auto foo = log.textCursor(); + foo.select(QTextCursor::LineUnderCursor); + auto txt = foo.selectedText(); + auto colon = txt.indexOf(':'); + if (colon>0) + { + auto colon2 = txt.indexOf(':', colon+1); + if (colon2>0) + { + auto line = txt.mid(colon+1, colon2-colon-1).toInt(); + EditFile(txt.mid(0, colon).toUtf8().data(), line, true); + } + else + EditFile(txt.mid(0, colon).toUtf8().data(), -1, true); + } + }); + + //editor UI things + connect(new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")), this), &QShortcut::activated, + [=]() + { + GUIprintf("Ctrl+O hit\n"); + }); + connect(new QShortcut(QKeySequence(tr("Ctrl+S", "File|Save")), this), &QShortcut::activated, + [=]() + { + GUIprintf("Ctrl+S hit\n"); + }); + + //compiler things + connect(new QShortcut(QKeySequence(tr("F7", "File|Compile")), this), &QShortcut::activated, + [=]() + { + RunCompiler("", false); + }); + + //debug things. + connect(new QShortcut(QKeySequence(tr("F5", "File|Debug")), this), &QShortcut::activated, + [=]() + { + if (!DebuggerSendCommand("qcresume\n")) + DebuggerStart(); //unable to send? assume its not running. + }); + connect(new QShortcut(QKeySequence(tr("F10", "File|DebugOver")), this), &QShortcut::activated, + [=]() + { + if (!DebuggerSendCommand("qcstep over\n")) + DebuggerStart(); + }); + connect(new QShortcut(QKeySequence(tr("F11", "File|DebugInto")), this), &QShortcut::activated, + [=]() + { + if (!DebuggerSendCommand("qcstep into\n")) + DebuggerStart(); + }); + connect(new QShortcut(QKeySequence(tr("Shift+F1", "File|DebugOut")), this), &QShortcut::activated, + [=]() + { + if (!DebuggerSendCommand("qcstep out\n")) + DebuggerStart(); + }); + connect(new QShortcut(QKeySequence(tr("F9", "File|DebugToggle")), this), &QShortcut::activated, + [=]() + { + docs.toggleBreak(); + }); + + CreateMenus(); + } +} *mainwnd; + +//called when progssrcname has changed. +//progssrcname should already have been set. +void UpdateFileList(void) +{ + char *buffer; + + AddSourceFile(nullptr, progssrcname); + + size_t size; + buffer = static_cast(QCC_ReadFile(progssrcname, nullptr, nullptr, &size)); + + pr_file_p = QCC_COM_Parse(buffer); + if (*qcc_token == '#') + { + //aaaahhh! newstyle! + } + else + { + pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat + while(pr_file_p) + { + if (*qcc_token == '#') //panic if there's preprocessor in there. + break; + + AddSourceFile(progssrcname, qcc_token); + pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat + } + } + free(buffer); + + //handle any #includes in there + RunCompiler(parameters, true); + + //expand everything, so the user doesn't get annoyed. + mainwnd->files_w.expandAll(); +} +void AddSourceFile(const char *parentpath, const char *filename) +{ + mainwnd->files.AddFile(parentpath, filename); +} + +static int Dummyprintf(const char *msg, ...){return 0;} + +void RunCompiler(const char *args, pbool quick) +{ + static FILE *logfile; + const char *argv[128]; + int argc; + progexterns_t ext; + progfuncs_t funcs; + + mainwnd->docs.saveAll(); + + memset(&funcs, 0, sizeof(funcs)); + funcs.funcs.parms = &ext; + memset(&ext, 0, sizeof(ext)); + ext.ReadFile = GUIReadFile; + ext.FileSize = GUIFileSize; + ext.WriteFile = QCC_WriteFile; + ext.Sys_Error = Sys_Error; + + if (quick) + ext.Printf = Dummyprintf; + else + { + ext.Printf = GUIprintf; + GUIprintf(""); + } + ext.DPrintf = ext.Printf; + + if (logfile) + fclose(logfile); + if (fl_log && !quick) + logfile = fopen("fteqcc.log", "wb"); + else + logfile = NULL; + + argc = GUI_BuildParms(args, argv, quick); + + if (CompileParams(&funcs, NULL, argc, argv)) + { + if (!quick) + { + //DebuggerGiveFocus(); + DebuggerSendCommand("qcresume\nqcreload\n"); + } + } + + if (logfile) + { + fclose(logfile); + logfile = NULL; + } +} + +void GUI_DoDecompile(void *buf, size_t size) +{ + const char *c = ReadProgsCopyright((char*)buf, size); + if (!c || !*c) + c = "COPYRIGHT OWNER NOT KNOWN"; //all work is AUTOMATICALLY copyrighted under the terms of the Berne Convention in all major nations. It _IS_ copyrighted, even if there's no license etc included. Good luck guessing what rights you have. + if (QMessageBox::Open == QMessageBox::question(mainwnd, "Copyright", QString::asprintf("The copyright message from this progs is\n%s\n\nPlease respect the wishes and legal rights of the person who created this.", c), QMessageBox::Open|QMessageBox::Cancel, QMessageBox::Cancel)) + { + GUIprintf(""); + + DecompileProgsDat(progssrcname, buf, size); + +// QCC_SaveVFiles(); + } +} + +static void QCC_EnumerateFilesResult(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize) +{ + auto buffer = new char[plainsize]; + if (QC_decode(nullptr, compsize, plainsize, method, compdata, buffer)) + QCC_AddVFile(name, buffer, plainsize); + + delete [] buffer; +} + +static void SetMainSrcFile(const char *src) +{ + //if its a path, chdir to it instead + const char *sl = strrchr(src, '/'); + if (sl) + { + sl++; + auto gah = static_cast(malloc(sl-src+1)); + memcpy(gah, src, sl-src); + gah[sl-src] = 0; + chdir(gah); + free(gah); + src = sl; + } + + strcpy(progssrcname, src); + + QCC_CloseAllVFiles(); + + GUI_SetDefaultOpts(); + GUI_ParseCommandLine(cmdlineargs, true); + GUI_RevealOptions(); + + //if the project is a .dat or .zip then decompile it now (so we can access the 'source') + { + char *ext = strrchr(progssrcname, '.'); + if (ext && (!QC_strcasecmp(ext, ".dat") || !QC_strcasecmp(ext, ".pak") || !QC_strcasecmp(ext, ".zip") || !QC_strcasecmp(ext, ".pk3"))) + { + FILE *f = fopen(progssrcname, "rb"); + if (f) + { + size_t size; + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + auto buf = new char[size]; + fread(buf, 1, size, f); + fclose(f); + if (!QC_EnumerateFilesFromBlob(buf, size, QCC_EnumerateFilesResult) && !QC_strcasecmp(ext, ".dat")) + { //its a .dat and contains no .src files + GUI_DoDecompile(buf, size); + } + else if (!QCC_FindVFile("progs.src")) + { + vfile_t *f; + char *archivename = progssrcname; + while(strchr(archivename, '\\')) + archivename = strchr(archivename, '\\')+1; + AddSourceFile(NULL, archivename); +// for (f = qcc_vfiles; f; f = f->next) +// AddSourceFile(archivename, f->filename); + + f = QCC_FindVFile("progs.dat"); + if (f) + GUI_DoDecompile(f->file, f->size); + } + delete [] buf; + strcpy(progssrcname, "progs.src"); + } + else + strcpy(progssrcname, "progs.src"); + + for (int i = 0; ; i++) + { + if (!strcmp("embedsrc", compiler_flag[i].abbrev)) + { + compiler_flag[i].flags |= FLAG_SETINGUI; + break; + } + } + } + } + + //then populate the file list. + UpdateFileList(); + EditFile(progssrcname, -1, false); +} + +int main(int argc, char* argv[]) +{ + //handle initial commandline args + GUI_SetDefaultOpts(); + { + size_t argl = 1; + for (int i = 1; i < argc; i++) + argl += strlen(argv[i])+1; + cmdlineargs = (char*)malloc(argl); + argl = 0; + for (int i = 1; i < argc; i++) + { + memcpy(cmdlineargs+argl, argv[i], strlen(argv[i])); + argl += strlen(argv[i]); + cmdlineargs[argl++] = ' '; + } + cmdlineargs[argl] = 0; + int mode = GUI_ParseCommandLine(cmdlineargs, false); + if (mode == 1) + { //compile-only + RunCompiler(cmdlineargs, false); + return EXIT_SUCCESS; + } + } + + //start up the gui parts + QCoreApplication *app = new QApplication(argc, argv); + mainwnd = new guimainwindow(); + mainwnd->show(); + GUIprintf("Welcome to FTEQCC!\n(QT edition)\n"); +#ifdef SVNREVISION + if (strcmp(STRINGIFY(SVNREVISION), "-")) + GUIprintf("FTE SVN Revision: %s\n",STRINGIFY(SVNREVISION)); +#endif + + //and now set up our project... + if (*progssrcname) + SetMainSrcFile(progssrcname); + else + SetMainSrcFile("progs.src"); + + //done, run the gui's main loop. + return app->exec(); +} +void compilecb(void) +{ +} + +int GUIprintf(const char *msg, ...) +{ + static QString l; + va_list va; + + if (!*msg) + { //starting a compile or something. + //clear the text and make sure the log is visible. + mainwnd->log.setText(QString("")); + if (!mainwnd->logsplit.sizes()[1]) + { //force it visible + QList sizes; + sizes.append(2); + sizes.append(1); + mainwnd->logsplit.setSizes(sizes); + } + return 0; + } + + va_start(va, msg); + l.append(QString::vasprintf(msg, va)); + va_end(va); + for (;;) + { + auto idx = l.indexOf('\n'); + if (idx >= 0) + { + QString s = l.mid(0, idx); + l = l.mid(idx+1); + + if (s.contains(": error") || s.contains(": werror") || !s.mid(0,5).compare("error", Qt::CaseInsensitive)) + mainwnd->log.setTextColor(QColor(255, 0, 0)); + else if (s.contains(": warning")) + mainwnd->log.setTextColor(QColor(128, 128, 0)); + else + mainwnd->log.setTextColor(QColor(0, 0, 0)); + mainwnd->log.append(s); + } + else + break; + } + return 0; +} +void documentlist::UpdateTitle(void) +{ + if (curdoc) + mainwnd->setWindowTitle(QString::asprintf("%s%s:%i", curdoc->fname, curdoc->modified?"*":"", curdoc->cursorline)); + else + mainwnd->setWindowTitle("FTEQCC"); +} +void documentlist::EditFile(const char *filename, int linenum, bool setcontrol) +{ + if (setcontrol) + { + for (int i = 0; i < numdocuments; i++) + { + docstacklock lock(this, docs[i]); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 1); + s->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 2); + } + } + + auto c = FindFile(filename); + if (!c) + { + c = new document_s(); + c->fname = strdup(filename); + + docs = cpprealloc(docs, sizeof(*docs)*(numdocuments+1)); + + if (!CreateDocument(c)) + { + delete(c); + numdocuments--; + return; + } + + beginInsertRows(QModelIndex(), numdocuments, numdocuments); + docs[numdocuments] = c; + numdocuments++; + endInsertRows(); + } + else + { + SwitchToDocument(c); + } + + UpdateTitle(); + + if (linenum >= 1) + { + linenum--; //scintilla is 0-based, apparently. + s->ensureLineVisible(max(1, linenum - 3)); + s->ensureLineVisible(linenum + 3); + s->setCursorPosition(linenum, 0); + s->ensureCursorVisible(); + s->setFocus(); + + if (setcontrol) + { + s->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum, 1); + s->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum, 2); + } + } +} +void EditFile(const char *name, int line, pbool setcontrol) +{ + mainwnd->docs.EditFile(name, line, setcontrol); +} +void GUI_DialogPrint(const char *title, const char *text) +{ + QMessageBox::information(mainwnd, title, text); +} +void *GUIReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile) +{ + auto d = mainwnd->docs.FindFile(fname); + if (d) + return mainwnd->docs.getFileData(d, buf_get, buf_ctx, out_size); + + if (issourcefile) + AddSourceFile(compilingrootfile, fname); + + return QCC_ReadFile(fname, buf_get, buf_ctx, out_size); +} +int GUIFileSize(const char *fname) +{ + auto d = mainwnd->docs.FindFile(fname); + if (d) + return mainwnd->docs.getFileSize(d); + + return QCC_PopFileSize(fname); +} + + + + + + +static QProcess *qcdebugger; +static void DebuggerStop(void) +{ + if (qcdebugger) + { + //GUIprintf("Detatching from debuggee\n"); + qcdebugger->closeWriteChannel(); + qcdebugger->closeReadChannel(QProcess::StandardOutput); + qcdebugger->closeReadChannel(QProcess::StandardError); + qcdebugger->waitForFinished(); + delete qcdebugger; + qcdebugger = NULL; + } +} +static bool DebuggerSendCommand(const char *msg, ...) +{ + va_list va; + //qcresume + //qcstep out|over|into + //qcjump "file" line + //debuggerwnd windowid + //qcinspect "variable" + //qcreload + //qcbreakpoint off=0|on=1|toggle=2 "file" line + + if (!qcdebugger || qcdebugger->state() == QProcess::NotRunning) + return false; //not running, can't send. + + va_start(va, msg); + qcdebugger->write(QString::vasprintf(msg, va).toUtf8().data()); + va_end(va); + return true; +} +static void DebuggerStart(void) +{ + DebuggerStop(); + qcdebugger = new QProcess(mainwnd); + qcdebugger->setProgram(enginebinary); + qcdebugger->setWorkingDirectory(enginebasedir); + qcdebugger->setArguments(QStringList(enginecommandline)); + + QObject::connect(qcdebugger, static_cast(&QProcess::finished), + [=](int exitcode) + { +// GUIprintf("Debuggee finished\n"); +// DebuggerStop(); //can't kill it here, there's still code running inside it + mainwnd->activateWindow(); //try and grab the user's attention + }); + QObject::connect(qcdebugger, &QProcess::readyReadStandardOutput, + [=]() + { + while(qcdebugger->canReadLine()) + { + auto l = qcdebugger->readLine(); + const char *line = l.data(); + if (!strncmp(line, "qcstep ", 7) || !strncmp(line, "qcfault ", 8)) + { //engine hit a breakpoint or some such. + //file, linenum, message + line = QCC_COM_Parse (line+7); + QString s(qcc_token); + if (*line == ':') + line++; //grr + line = QCC_COM_Parse (line); + EditFile(s.toUtf8().data(), atoi(qcc_token), true); + line = QCC_COM_Parse (line); + mainwnd->activateWindow(); + if (*qcc_token) + QMessageBox::critical(mainwnd, "Debugger Fault", qcc_token); + } + else if (!strncmp(line, "qcreloaded ", 10)) + { //vmname, progsname + mainwnd->docs.reapplyAllBreakpoints(); //send all breakpoints... + DebuggerSendCommand("qcresume\n"); //and let it run + } + //status + //curserver + //qcstack + //qcvalue + //refocuswindow + else + GUIprintf(line); + } + }); + + qcdebugger->start(QProcess::ReadWrite|QProcess::Unbuffered); + qcdebugger->waitForStarted(); + switch (qcdebugger->state()) + { + case QProcess::NotRunning: + GUIprintf("Child process not running\n"); + break; + case QProcess::Starting: + GUIprintf("Child starting up\n"); //still forking? + break; + case QProcess::Running: +// GUIprintf("Child is running\n"); + break; + } +} \ No newline at end of file diff --git a/engine/qclib/qccguistuff.c b/engine/qclib/qccguistuff.c index c8f99e27..3dd5b39d 100644 --- a/engine/qclib/qccguistuff.c +++ b/engine/qclib/qccguistuff.c @@ -1,9 +1,7 @@ #include "qcc.h" #include "gui.h" -#ifdef _WIN32 -#define alloca _alloca -#else +#ifndef _WIN32 #include #endif @@ -25,8 +23,15 @@ char enginebinary[MAX_OSPATH]; char enginebasedir[MAX_OSPATH]; char enginecommandline[8192]; +pbool qcc_vfiles_changed; +vfile_t *qcc_vfiles; + +//for finding symbol keywords +extern QCC_def_t *sourcefilesdefs[]; +extern int sourcefilesnumdefs; + int qccpersisthunk = 1; -int Grep(char *filename, char *string) +int Grep(const char *filename, const char *string) { int foundcount = 0; char *last, *found, *linestart; @@ -74,7 +79,7 @@ int Grep(char *filename, char *string) return foundcount; } -void GoToDefinition(char *name) +void GoToDefinition(const char *name) { #define MAXSOURCEFILESLIST 8 extern QCC_def_t *sourcefilesdefs[MAXSOURCEFILESLIST]; @@ -84,16 +89,23 @@ void GoToDefinition(char *name) QCC_def_t *def, *guess; QCC_function_t *fnc; - char *strip; //trim whitespace (for convieniance). + const char *strip; //trim whitespace (for convieniance). while (*name <= ' ' && *name) name++; - for (strip = name + strlen(name)-1; *strip; strip++) + for (strip = name + strlen(name)-1; strip > name; strip--) { if (*strip <= ' ') - *strip = '\0'; + continue; else //got some part of a word break; } + if (*strip <= ' ') + { + char *t = alloca(strip-name+1); + memcpy(t, name, strip-t); + t[strip-t] = 0; + name = t; + } if (!globalstable.numbuckets) { @@ -449,11 +461,11 @@ void GUI_LoadConfig(void) //this function takes the windows specified commandline and strips out all the options menu items. -int GUI_ParseCommandLine(char *args, pbool keepsrcanddir) +int GUI_ParseCommandLine(const char *args, pbool keepsrcanddir) { int paramlen=0; int l, p; - char *next; + const char *next; int mode = 0; if (!*args) @@ -469,8 +481,8 @@ int GUI_ParseCommandLine(char *args, pbool keepsrcanddir) len = ftell(f); fseek(f, 0, SEEK_SET); args = alloca(len+1); - fread(args, 1, len, f); - args[len] = '\0'; + fread((char*)args, 1, len, f); + ((char*)args)[len] = '\0'; fclose(f); while((s = strchr(args, '\r'))) @@ -835,12 +847,12 @@ void GUI_RevealOptions(void) -int GUI_BuildParms(char *args, char **argv, pbool quick)//, char *forceoutputfile) +int GUI_BuildParms(const char *args, const char **argv, pbool quick)//, char *forceoutputfile) { static char param[2048]; int paramlen = 0; int argc; - char *next; + const char *next; int i; int targ; char *targs[] = {"", "-Th2", "-Tfte", "-Tfteh2"}; @@ -938,3 +950,133 @@ int GUI_BuildParms(char *args, char **argv, pbool quick)//, char *forceoutputfil return argc; } + +pbool GenBuiltinsList(char *buffer, int buffersize) +{ + QCC_def_t *def; + int usedbuffer = 0; + int l; + int fno; + for (fno = 0; fno < sourcefilesnumdefs; fno++) + { + for (def = sourcefilesdefs[fno]; def; def = def->next) + { + if (def->scope) + continue; //ignore locals, because we don't know where we are, and they're probably irrelevent. + + //if its a builtin function... + if (def->type->type == ev_function && def->symboldata->function && functions[def->symboldata->function].code<0) + ; + else if (def->filen && strstr(def->filen, "extensions")) + ; + else + continue; + + //but ignore it if its one of those special things that you're not meant to know about. + if (strcmp(def->name, "IMMEDIATE") && !strchr(def->name, ':') && !strchr(def->name, '.') && !strchr(def->name, '*') && !strchr(def->name, '[')) + { + l = strlen(def->name); + if (l && usedbuffer+2+l < buffersize) + { + if (usedbuffer) + buffer[usedbuffer++] = ' '; + memcpy(buffer+usedbuffer, def->name, l); + usedbuffer += l; + } + } + } + } + buffer[usedbuffer] = 0; + return usedbuffer>0; +} + +void QCC_CloseAllVFiles(void) +{ + vfile_t *f; + + while(qcc_vfiles) + { + f = qcc_vfiles; + qcc_vfiles = f->next; + + free(f->file); + free(f); + } + qcc_vfiles_changed = false; +} +vfile_t *QCC_FindVFile(const char *name) +{ + vfile_t *f; + for (f = qcc_vfiles; f; f = f->next) + { + if (!strcmp(f->filename, name)) + return f; + } + //give it another go, for case + for (f = qcc_vfiles; f; f = f->next) + { + if (!QC_strcasecmp(f->filename, name)) + return f; + } + return NULL; +} +vfile_t *QCC_AddVFile(const char *name, void *data, size_t size) +{ + vfile_t *f = QCC_FindVFile(name); + if (!f) + { + f = malloc(sizeof(vfile_t) + strlen(name)); + f->next = qcc_vfiles; + strcpy(f->filename, name); + qcc_vfiles = f; + } + else + free(f->file); + f->file = malloc(size); + f->type = FT_CODE; + memcpy(f->file, data, size); + f->size = f->bufsize = size; + + qcc_vfiles_changed = true; + return f; +} +void QCC_CatVFile(vfile_t *f, const char *fmt, ...) +{ + va_list argptr; + char msg[8192]; + size_t n; + + va_start (argptr,fmt); + QC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr); + va_end (argptr); + + n = strlen(msg); + if (f->size+n > f->bufsize) + { + size_t msize = f->bufsize + n + 8192; + f->file = realloc(f->file, msize); + f->bufsize = msize; + } + memcpy((char*)f->file+f->size, msg, n); + f->size += n; +} +void QCC_InsertVFile(vfile_t *f, size_t pos, const char *fmt, ...) +{ + va_list argptr; + char msg[8192]; + size_t n; + va_start (argptr,fmt); + QC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr); + va_end (argptr); + + n = strlen(msg); + if (f->size+n > f->bufsize) + { + size_t msize = f->bufsize + n + 8192; + f->file = realloc(f->file, msize); + f->bufsize = msize; + } + memmove((char*)f->file+pos+n, (char*)f->file+pos, f->size-pos); + f->size += n; + memcpy((char*)f->file+pos, msg, n); +} diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 672a3b50..c4eac98a 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -3973,7 +3973,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) { CompilerConstant_t *cnst; int i, j, p; - char *name, *val; + const char *name, *val; pbool werror = false; qcc_nopragmaoptimise = false; @@ -4036,10 +4036,15 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) val = strchr(name, '='); if (val) { - *val = '\0'; + char *t = malloc(val-name+1); + memcpy(t, name, val-name); + t[val-name] = 0; + cnst = QCC_PR_DefineName(t); + free(t); val++; } - cnst = QCC_PR_DefineName(name); + else + cnst = QCC_PR_DefineName(name); if (val) { cnst->value = qccHunkAlloc(strlen(val)+1); @@ -4065,7 +4070,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) } else { - char *a = myargv[i]+2; + const char *a = myargv[i]+2; pbool state = true; if (!strnicmp(a, "no-", 3)) { @@ -4284,7 +4289,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) else if ( !strnicmp(myargv[i], "-W", 2) || WINDOWSARG(!strnicmp(myargv[i], "/W", 2)) ) { - char *a = myargv[i]+2; + const char *a = myargv[i]+2; if (!stricmp(a, "all")) { for (j = 0; j < ERR_PARSEERRORS; j++) @@ -4662,7 +4667,7 @@ const char *qcccol[COL_MAX]; int qcc_compileactive = false; extern int accglobalsblock; char *originalqccmsrc; //for autoprototype. -pbool QCC_main (int argc, char **argv) //as part of the quake engine +pbool QCC_main (int argc, const char **argv) //as part of the quake engine { extern int pr_bracelevel; time_t long_time; diff --git a/engine/qclib/qcctui.c b/engine/qclib/qcctui.c index 19cf6df4..4acf6e06 100644 --- a/engine/qclib/qcctui.c +++ b/engine/qclib/qcctui.c @@ -115,7 +115,7 @@ static int logprintf(const char *format, ...) return 0; } -int main (int argc, char **argv) +int main (int argc, const char **argv) { unsigned int i; pbool sucess; diff --git a/engine/qclib/qcd.h b/engine/qclib/qcd.h index 6ce02153..eac1bff2 100644 --- a/engine/qclib/qcd.h +++ b/engine/qclib/qcd.h @@ -1,11 +1,11 @@ pbool QC_decodeMethodSupported(int method); -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const char *info, char *buffer); +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer); int QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int handle); int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)); int QC_encodecrc(int len, char *in); -char *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, char *fname, size_t *size, char *buffer); -char *filefromnewprogs(pubprogfuncs_t *progfuncs, char *prname, char *fname, size_t *size, char *buffer);//fixme - remove parm 1 +char *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, const char *fname, size_t *size, char *buffer); +char *filefromnewprogs(pubprogfuncs_t *progfuncs, const char *prname, const char *fname, size_t *size, char *buffer);//fixme - remove parm 1 void DecompileProgsDat(char *name, void *buf, size_t bufsize); char *ReadProgsCopyright(char *buf, size_t bufsize); diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c index f08d0d9c..be5fa6df 100644 --- a/engine/qclib/qcd_main.c +++ b/engine/qclib/qcd_main.c @@ -41,7 +41,7 @@ pbool QC_decodeMethodSupported(int method) return false; } -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const char *info, char *buffer) +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer) { int i; if (method == 0) //copy @@ -53,7 +53,7 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const { if (complen != len) Sys_Error("lengths do not match"); for (i = 0; i < len; i++) - buffer[i] = info[i] ^ 0xA5; + buffer[i] = ((const char*)info)[i] ^ 0xA5; } #ifdef AVAIL_ZLIB else if (method == 2 || method == 8) //compression (ZLIB) @@ -272,7 +272,7 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons return ret; } -char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, char *fname, size_t *size, char *buffer) +char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, const char *fname, size_t *size, char *buffer) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int num; @@ -310,7 +310,7 @@ char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, char *fname, si } /* -char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer) +char *filefromnewprogs(progfuncs_t *progfuncs, const char *prname, const char *fname, int *size, char *buffer) { int num; includeddatafile_t *s; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 2828dcf6..1b78a6dc 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1313,7 +1313,7 @@ static void PR_Compile_f(void) qboolean killondone = false; int argc=3; double time = Sys_DoubleTime(); - char *argv[64] = {"", "-src", pr_sourcedir.string, "-srcfile", "progs.src"}; + const char *argv[64] = {"", "-src", pr_sourcedir.string, "-srcfile", "progs.src"}; if (Cmd_Argc()>2) {